〇〇木一 发表于 2014-5-29 22:19

[算法分析] C Program KenGenMe

本帖最后由 〇〇木一 于 2014-5-30 09:04 编辑

【文章作者】: 〇〇木一
【软件名称】: C Program KenGenMe
【下载地址】: http://www.52pojie.cn/thread-261639-1-1.html
【使用工具】: IDA , OD , VC6

分析不出,难受。分析出来了,不发表一下思路也难受。 所以一天时间就没了。
找实习,让我感到未来一片渺茫。大学以来我感觉我都没认真干过什么事,上不上课看心情,总喜欢自己搞一套,到现在绩点也就刚刚毕毕业,还有重修科目在身。找实习也受了不少挫,最后找到了个windows驱动开发的实习岗位,总的来说还算和我胃口。现在就是天天泡图书馆,想想以后要买房子,养老婆...不勤奋不行了。
IDA的使用也是图书馆看的,为了熟悉,我就一直找东西来试试。这个KenGenMe是昨天Blackk大大发的,今天在图书馆试试,结果发现点思路就停不下来了。
下面是正文:

IDA分析KenGenMe的具体流程是:
1.获取输入key,判断key的长度是否为24,不是则失败。2.对得到的key进行第一步处理,并把处理结果储存在一个全局数组中。3.对第二步全局数组中的数据进行2次处理,获取数据进行比较验证。
下面是主函数部分:.text:00410240 ; Attributes: bp-based frame
.text:00410240
.text:00410240 _main_0         proc near               ; CODE XREF: _mainj
.text:00410240
.text:00410240 anonymous_0   = byte ptr -7Ch
.text:00410240 var_70          = byte ptr -70h
.text:00410240 var_30          = dword ptr -30h
.text:00410240 var_2C          = dword ptr -2Ch
.text:00410240 var_28          = dword ptr -28h
.text:00410240 var_24          = dword ptr -24h
.text:00410240 _key_len      = dword ptr -20h
.text:00410240 char_table      = dword ptr -1Ch
.text:00410240 _key_str      = byte ptr -18h
.text:00410240
.text:00410240               push    ebp
.text:00410241               mov   ebp, esp
.text:00410243               sub   esp, 70h
.text:00410246               push    ebx
.text:00410247               push    esi
.text:00410248               push    edi
.text:00410249               lea   edi,
.text:0041024C               mov   ecx, 1Ch
.text:00410251               mov   eax, 0CCCCCCCCh
.text:00410256               rep stosd               ; ↓获取字符表
.text:00410258               mov   , offset a123456789abcde ; "123456789abcdefghijklmnpqrstuvwxyz"
.text:0041025F               push    offset asc_426120 ; "***************************************"...
.text:00410264               call    _printf
.text:00410269               add   esp, 4
.text:0041026C               push    offset asc_425A68 ; "\n\n"
.text:00410271               call    _printf
.text:00410276               add   esp, 4
.text:00410279               push    offset aPleaseKeygenMe ; "********                Please KeyGen M"...
.text:0041027E               call    _printf
.text:00410283               add   esp, 4
.text:00410286               push    offset asc_425A68 ; "\n\n"
.text:0041028B               call    _printf
.text:00410290               add   esp, 4
.text:00410293               push    offset aHaveFun_byBlac ; "********             Have Fun.By Blackk"...
.text:00410298               call    _printf
.text:0041029D               add   esp, 4
.text:004102A0               push    offset asc_425A68 ; "\n\n"
.text:004102A5               call    _printf
.text:004102AA               add   esp, 4
.text:004102AD               push    offset asc_426120 ; "***************************************"...
.text:004102B2               call    _printf
.text:004102B7               add   esp, 4
.text:004102BA               push    offset asc_42592C ; "\n"
.text:004102BF               call    _printf
.text:004102C4               add   esp, 4
.text:004102C7               push    offset aPleaseEnterPas ; "Please Enter Password:"
.text:004102CC               call    _printf
.text:004102D1               add   esp, 4
.text:004102D4               lea   eax,
.text:004102D7               push    eax             ; ↓获取key
.text:004102D8               push    offset aS       ; "%s"
.text:004102DD               call    _scanf
.text:004102E2               add   esp, 8          ; ↓获取key串长度
.text:004102E5               lea   ecx,
.text:004102E8               push    ecx             ; Str
.text:004102E9               call    _strlen
.text:004102EE               add   esp, 4
.text:004102F1               mov   , eax
.text:004102F4               cmp   , 18h
.text:004102F8               jz      loc_4103A6      ; 长度不为24就失败↓
.text:004102FE               push    offset aBadWork_please ; "Bad Work.Please try again!"
.text:00410303               call    _printf
.text:00410308               add   esp, 4
.text:0041030B               mov   edx, File._cnt
.text:00410311               sub   edx, 1
.text:00410314               mov   File._cnt, edx
.text:0041031A               cmp   File._cnt, 0
.text:00410321               jl      short loc_410345
.text:00410323               mov   eax, File._ptr
.text:00410328               movsx   ecx, byte ptr
.text:0041032B               and   ecx, 0FFh
.text:00410331               mov   , ecx
.text:00410334               mov   edx, File._ptr
.text:0041033A               add   edx, 1
.text:0041033D               mov   File._ptr, edx
.text:00410343               jmp   short loc_410355
.text:00410345 ; ---------------------------------------------------------------------------
.text:00410345
                        .
                        .
                        .
.text:0041038C ; ---------------------------------------------------------------------------
.text:0041038C
.text:0041038C loc_41038C:                           ; CODE XREF: _main_0+129j
.text:0041038C               push    offset File   ; File
.text:00410391               call    __filbuf
.text:00410396               add   esp, 4
.text:00410399               mov   , eax
.text:0041039C
.text:0041039C loc_41039C:                           ; CODE XREF: _main_0+14Aj
.text:0041039C               push    0               ; Code
.text:0041039E               call    _exit
.text:004103A3 ; ---------------------------------------------------------------------------
.text:004103A3               add   esp, 4
.text:004103A6
.text:004103A6 loc_4103A6:                           ; CODE XREF: _main_0+B8j
.text:004103A6               lea   ecx,
.text:004103A9               push    ecx             ; key_str
.text:004103AA               mov   edx,
.text:004103AD               push    edx             ; char_table
.text:004103AE               mov   eax,
.text:004103B1               push    eax             ; key_len
.text:004103B2               call    _DealKey      ; 首先处理key
.text:004103B7               add   esp, 0Ch
.text:004103BA               push    offset _GlobalArray_0
.text:004103BF               movsx   ecx, _GlobalArray_1
.text:004103C6               push    ecx
.text:004103C7               call    _VerifyKey      ; 再验证
.text:004103CC               add   esp, 8
.text:004103CF               mov   edx, File._cnt
.text:004103D5               sub   edx, 1
.text:004103D8               mov   File._cnt, edx
.text:004103DE               cmp   File._cnt, 0
.text:004103E5               jl      short loc_410409
.text:004103E7               mov   eax, File._ptr
.text:004103EC               movsx   ecx, byte ptr
.text:004103EF               and   ecx, 0FFh
.text:004103F5               mov   , ecx
.text:004103F8               mov   edx, File._ptr
.text:004103FE               add   edx, 1
.text:00410401               mov   File._ptr, edx
.text:00410407               jmp   short loc_410419
.text:00410409 ; ---------------------------------------------------------------------------
.text:00410409
                        .
                        .
                        .
.text:00410450 ; ---------------------------------------------------------------------------
.text:00410450
.text:00410450 loc_410450:                           ; CODE XREF: _main_0+1EDj
.text:00410450               push    offset File   ; File
.text:00410455               call    __filbuf
.text:0041045A               add   esp, 4
.text:0041045D               mov   , eax
.text:00410460
.text:00410460 loc_410460:                           ; CODE XREF: _main_0+20Ej
.text:00410460               pop   edi
.text:00410461               pop   esi
.text:00410462               pop   ebx
.text:00410463               add   esp, 70h
.text:00410466               cmp   ebp, esp
.text:00410468               call    __chkesp
.text:0041046D               mov   esp, ebp
.text:0041046F               pop   ebp
.text:00410470               retn
.text:00410470 _main_0         endp


搞清楚各种变量并修改后,使用IDA的f5功能可以一目了然(几乎和源代码一样)

int __cdecl main_0()
{
int v0; // eax@13
char v2; // @0
char v3; // @1
int v4; // @13
int v5; // @10
int v6; // @6
int v7; // @3
size_t _key_len; // @1
char *char_table; // @1
char _key_str; // @1


char_table = "123456789abcdefghijklmnpqrstuvwxyz";// 字符表
printf("**************************************************************");
printf("\n\n");
printf("********                Please KeyGen Me.             ********");
printf("\n\n");
printf("********             Have Fun.By Blackk          ********");
printf("\n\n");
printf("**************************************************************");
printf("\n");
printf("Please Enter Password:");
scanf("%s", _key_str);
_key_len = strlen(_key_str);
if ( _key_len != 24 )                         // 判断长度
{
    printf("Bad Work.Please try again!");
      ...
    exit(0);
}
DealKey(24, char_table, _key_str);            // 处理函数(key长度,字符表,key)
VerifyKey(GlobalArray_1, GlobalArray_0);   // 验证函数(全局变量1首字符,全局变量2)
      ...
return _chkesp(1, v0, v2);
}



这里有部分的代码省略了,都是些关于调试或者控制台有关的代码,无关紧要。
要搞清楚算法既验证原理就要逆行而上,先来看看验证部分代码


int __cdecl VerifyKey0(int gArr1Char0, int globalArray0)
.text:004017A0 _VerifyKey0   proc near               ; CODE XREF: _VerifyKeyj
.text:004017A0
.text:004017A0 anonymous_0   = byte ptr -5Ch
.text:004017A0 var_50          = byte ptr -50h
.text:004017A0 Dst             = byte ptr -0Ch
.text:004017A0 changeG1      = word ptr -8
.text:004017A0 var_5         = byte ptr -5
.text:004017A0 var_4         = byte ptr -4
.text:004017A0 gArr1Char0      = dword ptr8
.text:004017A0 globalArray0    = dword ptr0Ch
.text:004017A0
.text:004017A0               push    ebp
.text:004017A1               mov   ebp, esp
.text:004017A3               sub   esp, 50h
.text:004017A6               push    ebx
.text:004017A7               push    esi
.text:004017A8               push    edi
.text:004017A9               lea   edi,
.text:004017AC               mov   ecx, 14h
.text:004017B1               mov   eax, 0CCCCCCCCh
.text:004017B6               rep stosd
.text:004017B8               mov   , 1
.text:004017BC               mov   eax, ; 取首字符运算后(changeG1)判断是否符合要求↓
.text:004017BF               and   eax, 0F0h
.text:004017C4               sar   eax, 5
.text:004017C7               mov   , ax
.text:004017CB               movsx   ecx,
.text:004017CF               cmp   ecx, 1
.text:004017D2               jnz   short loc_4017EB
.text:004017D4               push    1               ; int
.text:004017D6               push    0Eh             ; int
.text:004017D8               mov   edx,
.text:004017DB               push    edx             ; keyStr
.text:004017DC               push    (offset _GlobalArray_1+1) ; globalArr1pchar1
.text:004017E1               call    j__DealKey1   ; changeG1为1时,第二次处理函数程序崩溃
.text:004017E6               add   esp, 10h
.text:004017E9               jmp   short loc_4017F8
.text:004017EB ; ---------------------------------------------------------------------------
.text:004017EB
.text:004017EB loc_4017EB:                           ; CODE XREF: _VerifyKey0+32j
.text:004017EB               movsx   eax,
.text:004017EF               cmp   eax, 2
.text:004017F2               jz      short loc_4017F8
.text:004017F4               mov   , 0; changeG1不为2时失败
.text:004017F8
.text:004017F8 loc_4017F8:                           ; CODE XREF: _VerifyKey0+49j
.text:004017F8                                       ; _VerifyKey0+52j
.text:004017F8               movsx   ecx,
.text:004017FC               test    ecx, ecx
.text:004017FE               jz      short loc_401873 ; 只用changeG1为2时,才能真正运行到验证
.text:00401800               push    1               ; int
.text:00401802               push    0Eh             ; int
.text:00401804               push    offset keyStr   ; "9QX6K882ISS5M"
.text:00401809               push    (offset _GlobalArray_1+1) ; globalArr1pchar1
.text:0040180E               call    j__DealKey1   ; 处理函数2
.text:00401813               add   esp, 10h
.text:00401816               push    3               ; Size
.text:00401818               push    (offset _GlobalArray_1+1) ; Src
.text:0040181D               lea   edx,
.text:00401820               push    edx             ; Dst
.text:00401821               call    _memcpy         ; 最后取值1
.text:00401826               add   esp, 0Ch
.text:00401829               push    4               ; Size
.text:0040182B               push    (offset _GlobalArray_1+0Ah) ; Src
.text:00401830               lea   eax, ; 最后取值2
.text:00401833               push    eax             ; Dst
.text:00401834               call    _memcpy
.text:00401839               add   esp, 0Ch
.text:0040183C               mov   , 0
.text:00401840               push    offset Str2   ; "FB40"
.text:00401845               lea   ecx,
.text:00401848               push    ecx             ; Str1
.text:00401849               call    _strcmp         ; 比较
.text:0040184E               add   esp, 8
.text:00401851               test    eax, eax
.text:00401853               jnz   short loc_401864
.text:00401855               push    offset Format   ; "Good Job!"
.text:0040185A               call    _printf
.text:0040185F               add   esp, 4
.text:00401862               jmp   short loc_401871
.text:00401864 ; ---------------------------------------------------------------------------
.text:00401864
.text:00401864 loc_401864:                           ; CODE XREF: _VerifyKey0+B3j
.text:00401864               push    offset aBadWork_please ; "Bad Work.Please try again!"
.text:00401869               call    _printf
.text:0040186E               add   esp, 4
.text:00401871
.text:00401871 loc_401871:                           ; CODE XREF: _VerifyKey0+C2j
.text:00401871               jmp   short loc_401880
.text:00401873 ; ---------------------------------------------------------------------------
.text:00401873
.text:00401873 loc_401873:                           ; CODE XREF: _VerifyKey0+5Ej
.text:00401873               push    offset aBadWork_please ; "Bad Work.Please try again!"
.text:00401878               call    _printf
.text:0040187D               add   esp, 4
.text:00401880
.text:00401880 loc_401880:                           ; CODE XREF: _VerifyKey0:loc_401871j
.text:00401880               pop   edi
.text:00401881               pop   esi
.text:00401882               pop   ebx
.text:00401883               add   esp, 50h
.text:00401886               cmp   ebp, esp
.text:00401888               call    __chkesp
.text:0040188D               mov   esp, ebp
.text:0040188F               pop   ebp
.text:00401890               retn
.text:00401890 _VerifyKey0   endp

F5之后
int __cdecl VerifyKey0(int gArr1Char0, int globalArray0)
{
int v2; // eax@7
char v4; // @0
char v5; // @1
char Dst; // @6
__int16 changeG1; // @1
char v8; // @6
char v9; // @1

memset(&v5, -858993460, 0x50u);
v9 = 1;
changeG1 = (gArr1Char0 & 0xF0) >> 5;          // changeG1的值必为2,为1时DealKey引入空值报错
if ( (signed __int16)((gArr1Char0 & 0xF0) >> 5) == 1 )// gArr1Char0的大小必须在0x40-0x5F
{
    j__DealKey1(&GlobalArray_1, (char *)globalArray0, 14, 1);// 陷阱程序崩溃
}
else
{
    if ( changeG1 != 2 )                        // changeG1不为2时失败
      v9 = 0;
}
if ( v9 )
{
    j__DealKey1(&GlobalArray_1, "9QX6K882ISS5M", 14, 1);// 第2次处理
    memcpy(Dst, &GlobalArray_1, 3u);         // 从全局数组中第2位开始取3个
    memcpy(&Dst, &GlobalArray_1, 4u);    // 再从第10位开始取4个
    v8 = 0;
    if ( strcmp(Dst, "FB40") )                  // 最后判断是否字串与"FB40"相等
      v2 = printf("Bad Work.Please try again!");
    else
      v2 = printf("Good Job!");
}
else
{
    v2 = printf("Bad Work.Please try again!");
}
return _chkesp(1, v2, v4);
}

从上述代码中可以看出GloabalArray_1用来判断是否真正进入验证:changeG1=(GloabalArray_1&0xF0)>>5;而changeG1必须等于2才能进入真正的验证,所以GloabalArray_1的值的二进制形式必须是,其中问号表示未知(1 or 0),也就是GloabalArray_1的范围必须在0x40~0x5F之间。最后的验证为取GloabalArray_1开始的3个字符,和GloabalArray_1开始的4个字符。但是其实GloabalArray_1之后取得只有前两个字符有用,因为验证最后是与字符串”FB40”比较是否相等所以要使验证通过最后肯定是这样的:GloabalArray_1==’F’;GloabalArray_1==’B’;GloabalArray_1==’4’;GloabalArray_1==’0’;GloabalArray_1==’\0’;
再来看一下第二个处理函数,篇幅问题就直接看下F5的结果吧int __cdecl DealKey1(char *globalArr1pchar1, char *keyStr, int gaLen, int status)
{
char v5; // @0
char v6; // @1
int keyStrLen; // @1
int i; // @2
int v9; // @6
int v10; // @6
unsigned int j; // @6
int kslTMP; // @1

memset(&v6, -858993460, 0x58u);
kslTMP = strlen(keyStr);
keyStrLen = kslTMP;
if ( status == 1 )                            // 传进来的status均为1,仅此if范围内代码有用
{
    kslTMP = 0;
    for ( i = 0; i < gaLen; ++i )
    {
      kslTMP = i;
      globalArr1pchar1 ^= keyStr;// 与传进的keyStr对应下标位置值异或
    }
}
if ( status == 2 )                            // 无用
{
    v9 = 0;
    v10 = 0;
    for ( j = 0; ; ++j )
    {
      kslTMP = j__max0(gaLen, keyStrLen);
      if ( kslTMP <= j )
      break;
      globalArr1pchar1 ^= keyStr;
      ++v9;
      if ( v9 == gaLen )
      v9 = 0;
      if ( v10 == keyStrLen )
      v10 = 0;
    }
}
strcmp(GlobalArray_0, globalArr1pchar1);
return _chkesp(1, kslTMP, v5);
}

对此函数调用的地方仅仅有两处:可以看出两处调用第4个参数status均为1
第一处是个陷阱会导致程序崩溃j__DealKey1(&GlobalArray_1, (char *)globalArray0, 14, 1);// 陷阱程序崩溃他的第二个参数是调用某个全局字串,通过分析可以发现,这个全局数组其实一直会是什么都没有,这就导致处理函数在获取字串长为0,globalArr1pchar1 ^= keyStr;// 与传进的keyStr对应下标位置值异或keyStrLen为0,已0为除数(取余会有除操作)导致系统错误,程序崩溃。
第2处的调用传入keyStr="9QX6K882ISS5M",j__DealKey1(&GlobalArray_1, "9QX6K882ISS5M", 14, 1);// 第2次处理
传进的GlobalArray_1数组中的值是与keyStr对应下标的值进行异或,这样就可以得出:我把第一次处理key后的全局变量GloabalArray_1[?] 写成<?>(为了方便).<1> ^ ’9’==’F’;<2> ^ ‘Q’==’B’;<3> ^ ‘X’==’4’;<10> ^ ‘S’==’0’;<11> ^ ‘S’==’\0’;计算后再加上方面对GloabalArray_1的分析可以得出:<0> = 0x40~0x5F (010?????B)<1> = 0x7F (01111111B)<2> = 0x13 (00010011B)<3> = 0x6C (01101100B)<10> = 0x63 (01100011B)<11> = 0x53 (01010011B)
有了这些数据接下来就去看一下第一次处理也是最主要的处理key的部分:标注一些变量后F5能够很清晰的了解算法int __cdecl DealKey0(int key_len, char *char_table, char *key_str)
{
char v4; // @0
char v5; // @1
int res; // @14
char vResult; // @11
int vCmp5; // @9
int vCmp8; // @11
int minC58; // @14
int _index; // @9
int _gArrIndex; // @9
int v13; // @14
int v14; // @14
char c; // @3
int i; // @1
unsigned int j; // @3
char _arr; // @6
size_t char_table_len; // @4

memset(&v5, 0xCCCCCCCCu, 0x174u);
for ( i = 0; i < key_len; ++i )               // 将key转为字符表对应下标
{
    c = key_str;
    for ( j = 0; ; ++j )
    {
      char_table_len = strlen(char_table);
      if ( char_table_len <= j )
      break;
      if ( char_table == c )
      {
      _arr = j;
      break;
      }
    }
}
vCmp5 = 0;
_index = 0;
_gArrIndex = 0;
while ( _index < key_len )
{
    vCmp8 = 0;
    vResult = 0;
    while ( vCmp8 < 8 && _index < key_len )
    {
      LOBYTE(res) = _arr;
      v14 = 8 - vCmp8;
      v13 = 5 - vCmp5;
      minC58 = min(5 - vCmp5, 8 - vCmp8);
      if ( vCmp5 <= vCmp8 )
      LOBYTE(res) = (_BYTE)res << vCmp8;
      else
      LOBYTE(res) = (signed int)(unsigned __int8)res >> vCmp5;
      vResult |= res;
      vCmp8 += minC58;
      vCmp5 += minC58;
      if ( vCmp5 >= 5 )
      {
      ++_index;
      vCmp5 = 0;
      }
    }
    GlobalArray_1 = vResult;
}
GlobalArray_1 = 0;
return _chkesp(1, 5 * key_len / 8, v4);
}

上面就是关键算法的实现部分了,首先它根据输入key将每个字符在字符表中的下标存入_arr[]中(为了方便我将把_arr[?]直接写成[?],与上面<?>对应),然后再去[?]中的值进行运算,将每个运算结果写入到对应的<?>中。[?]的值只能是在字符表的范围内,在这里字符表为”123456789abcdefghiklmnpqrstuvwxyz”共34个字符(它里面竟然不包含’o’,简直坑爹,害的我很苦),所以[?]值得范围是0~33(00000000B~00100001B).这个算法的分析没什么好讲的,因为这已经是源代码了。下面主要看看每个<?>对应的生成方式,每个<?>会用多个[?]来生成。改变一下这段代码就能获取每个<?>的生成方式。改变后的c代码:
#include <stdio.h>
#include <stdlib.h>
#include <string>
using namespace std;

#define min(x,y) (((x)<(y)) ? (x):(y))

string toStr(int a)
{
      char b;
      itoa(a,b,10);
      return b;
}

int main()
{
      printf("\n_arr:<?>\nGloabalArray_1:[?]\n\n");
      int key_len=24;
      int vCmp5 = 0 , vCmp8 = 0;
      int _index = 0;
      int _gArrIndex = 0;
      string vResult;
      int minC58;
      string res;
      while ( _index < key_len )
      {
                vCmp8 = 0;
                vResult = "<";
                vResult += toStr(_gArrIndex);
                vResult += "> = ";
                bool p=false;
                while ( vCmp8 < 8 && _index < key_len )
                {
                        res = "([";
                        res += toStr(_index);
                        res += "]";
                        minC58 = min(5 - vCmp5, 8 - vCmp8);
                        if ( vCmp5 <= vCmp8 )
                              res = res + "<<" + toStr(vCmp8);
                        else
                              res = res + ">>" + toStr(vCmp5);
                        res+=")";
                        if(p)
                              vResult +="|";
                        vResult +=res;
                        p=true;
                        vCmp8 += minC58;
                        vCmp5 += minC58;
                        if ( vCmp5 >= 5 )
                        {
                              ++_index;
                              vCmp5 = 0;
                        }
                }
                _gArrIndex++;
                vResult +="\n";
                printf(vResult.c_str());
      }
      printf("\n");
      system("pause");
      return 0;
}

运行后就能看到每个<?>的生成方式了。
其中只有<0>,<1>,<2>,<3>,<10>,<11>是我们需要的
下面就是最主要关键的部分了,根据已知的东西来分析获取key:第一个有用的[?]的范围:[?] = (00000000B~00100001B);先定义[?]为未知Def[?] = 00??????B(黄底的部分说明原本是在[?]中的部分,下同)一步步来①:<0> = (<<0) | (<<5) )= 010?????B↓00??????B| ???00000B = 010?????B↓000?????B| 01000000B = 010?????B所以①可以得出: = 000?????B , = 00???010B②<1> = (>>3) | (<<2) | (<<7) = 01111111B↓00000???B | ??????00B | ?0000000B = 01111111B↓00000?11B | 01111?00B | 00000000B = 01111111B = 00?11010B 要小于00100001B所以 由②可得: = 00011010B , = 00011111B , = 00?????0B③<2> = (>>1) | (<<4) = 00010011B↓000?????B | ????0000B = 00010011B↓000?00111B | 000?0000B = 00010011B = 00?001110B , 要小于00100001B所以 由③可得: = 00000110B , = 00??0001B④<3> = (>>4) | (<<1) | (<<6) = 01101100B↓000000??B | 0??????0B | ??000000B = 01101100B↓00000000B | 0?101100B | 0?000000B = 01101100B = 00?10110B,要小于00100001B所以 有④可得: = 00000001B , = 00010110B , = 00????01B⑤<10> = (<<0) | (<<5) = 01100011B↓00??????B | ???00000B = 01100011B↓00000011B |01100000B = 01100011B所以 由⑤可得: = 00000011B , = 00???011B⑥<11> = ( >> 3) | (<<2) | (<<7) =01010011B↓00000???B | ??????00B | ?0000000B = 01010011B↓00000011B | 0101000B | 00000000B = 01010011B所以 由⑥可得: = 0001101?B , = 00010100B , = 00?????0B最后综上可得:
= 000?????B = (0~31) = 00011010B= 26 = 00011111B =31 = 00000110B = 6 = 00000001B = 1 = 00010110B = 22 = 00????01B = {1,5,9,13,17,21,25,29,33} = 00000011B = 3 = 00011011B = 27 = 00010100B = 20 = 00?????0B = {2,4,6,8...30,32}
最后通过对应字符表可得最后key字符串的24位字符的范围:
charTable = '123456789abcdefghijklnpqrstuvwxyz';

key = {'1' ~ 'w'};
key = 's';
key = 'x';
key = '7';
key = '2';
key = 'n';
key = {'2','6','a','e','i','m','r','v','z'};

key ~ key = 任意;

key = '4';
key = 't';
key = 'l';
key = {'1','3','5','7','9','b','d','f','h','j','l','n','q','s','u','w','y'};

key ~ key = 任意;


//这里的任意是charTable中的任意字符(很坑爹的不包含字母'o');


随便测试几个key:

分析完了,明天继续图书馆。{:301_1003:}

fzx118 发表于 2014-5-30 08:30

谢谢分享好精的汇编语言呀

轩轩1018 发表于 2014-8-1 17:13

看看楼主的大作,支持原创,支持吾爱

OneTime 发表于 2014-7-29 09:07

不错不错~{:1_902:}

ricroon 发表于 2014-7-23 20:48

最近也在写一个keygen,代码好多

zzfonline 发表于 2014-7-13 18:30

学这些就要靠大神你的教程了!!大神我失业中求安慰{:1_918:}

骑乌龟的帅蜗牛 发表于 2014-7-12 13:48

因为精华来的,可惜小女子看不懂

清少 发表于 2014-7-11 22:18

学习了!

cdTabEnter 发表于 2014-7-8 10:32

思路相当的清楚,学习了

fah 发表于 2014-7-8 01:13

好厉害,谢谢分享。。。

star0angel 发表于 2014-5-29 22:25

这膜拜大牛啊虽然看不懂还是觉得非常厉害

a070458 发表于 2014-5-29 22:49

{:1_931:}学习了!
膜拜

阿富汗2013 发表于 2014-5-29 22:53

      表示在围观

含蕊 发表于 2014-5-29 23:16

支持一下

海盗小K 发表于 2014-5-29 23:22

学习了,非常感谢!

H2o 发表于 2014-5-30 00:47

膜拜会算法的大牛   !!手置了!

bansjs 发表于 2014-5-30 01:03

兄弟呀,说的太高深了,看不明白呀

beyond_dongdong 发表于 2014-5-30 01:57

哎 这简直是在看天书什么时候自己可以这样

zyhewsd 发表于 2014-5-30 06:59

好复杂,初学看不懂
页: [1] 2 3 4 5 6 7 8
查看完整版本: [算法分析] C Program KenGenMe