- UID
 - 57733
 
 注册时间2009-1-23
阅读权限10
最后登录1970-1-1
周游历练 
  
 
 
 
该用户从未签到  
 | 
 
一个crackme的算法分析 
o(∩_∩)o...首先声明一下,这是本人第一个破解文章。o(∩_∩)o... 
 
本人属于菜鸟级的,这个crackme也没什么技术含量。这个crackme是一个chm破解教程中的附件,虽然教程已经分析了这个程序,但是可能嫌麻烦(也可能是教程的目的仅仅是练习爆破),没有分析出具体的注册算法,仅仅给出了爆破点。本人耐心地跟踪了这个程序,才有了这个破解的文章,其中给出了注册算法。 
 
废话说完了,下面转入正题。 
 
peid检查,无壳,Microsoft Visual C++ 6.0程序。 
 
运行程序,随便输入注册信息,比如asdf,12345,点击注册,弹出出错对话框Registration fail。有出错提示,真是太好了。(没有提示的话,断点不好下啊) 
 
OD载入,选择 插件->字符串参考->Find ASCII,再找到Registration fail。双击就到达了弹出出错提示的位置。向上找到关键call和关键跳。 
 
00401064   .  E8 C7010000   call    00401230                         ;  关键call。在这里下断点。 
00401069   .  85C0          test    eax, eax                         ;  检验eax是否等于0。等于0则注册成功。 
0040106B   .  6A 00         push    0                                ; /Style = MB_OK|MB_APPLMODAL 
0040106D   .  68 80504000   push    00405080                         ; |ncrackme 
00401072   .  75 1B         jnz     short 0040108F                   ; |关键跳。要爆破就在这里动手了。 
00401074   .  A1 B8564000   mov     eax, dword ptr [4056B8]          ; | 
00401079   .  68 64504000   push    00405064                         ; |registration successful. 
0040107E   .  50            push    eax                              ; |hOwner => NULL 
0040107F   .  FF15 C0404000 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA 
00401085   .  E8 A6020000   call    00401330 
0040108A   .  33C0          xor     eax, eax 
0040108C   .  C2 1000       retn    10 
0040108F   >  8B0D B8564000 mov     ecx, dword ptr [4056B8]          ; | 
00401095   .  68 50504000   push    00405050                         ; |registration fail. 
0040109A   .  51            push    ecx                              ; |hOwner => NULL 
0040109B   .  FF15 C0404000 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA 
 
在00401064处按下F2下断点。 
F9运行程序,00401064处程序暂停,按F7跟进去。然后F8单步执行, 
 
00401245  |.  6A 10         push    10                               ; /Count = 10 (16.) 
00401247  |.  50            push    eax                              ; |Buffer 
00401248  |.  68 E8030000   push    3E8                              ; |ControlID = 3E8 (1000.) 
0040124D  |.  51            push    ecx                              ; |hWnd => NULL 
0040124E  |.  33DB          xor     ebx, ebx                         ; | 
00401250  |.  FFD6          call    esi                              ; \GetDlgItemTextA 
00401252  |.  83F8 03       cmp     eax, 3                           ;  eax=用户名长度(字符数) 
00401255  |.  73 0B         jnb     short 00401262                   ;  eax>=3,否则eax=1,然后返回,注册失败了。 
00401257  |.  5E            pop     esi 
00401258  |.  B8 01000000   mov     eax, 1 
0040125D  |.  5B            pop     ebx 
0040125E  |.  83C4 30       add     esp, 30 
00401261  |.  C3            retn 
00401262  |>  A1 BC564000   mov     eax, dword ptr [4056BC] 
 
继续跟踪, 
00401276  |.  0FBE4424 08   movsx   eax, byte ptr [esp+8]            ;  字节name[0] 
0040127B  |.  0FBE4C24 09   movsx   ecx, byte ptr [esp+9]            ;  字节name[1] 
00401280  |.  99            cdq 
00401281  |.  F7F9          idiv    ecx 
00401283  |.  8BCA          mov     ecx, edx                         ; ecx=name[1]%name[0] 
00401285  |.  83C8 FF       or      eax, FFFFFFFF 
00401288  |.  0FBE5424 0A   movsx   edx, byte ptr [esp+A]            ;  字节name[2] 
0040128D  |.  0FAFCA        imul    ecx, edx                         ; ecx=(name[1]%name[0])*name[2] 
00401290  |.  41            inc     ecx 
00401291  |.  33D2          xor     edx, edx 
00401293  |.  F7F1          div     ecx                 ; eax=0xffffffff/((name[1]%name[0])*name[2]+1) 
00401295  |.  50            push    eax 
00401296  |.  E8 A5000000   call    00401340            ;  将eax存入地址4050ac,实际上设置了rand函数的种子。 
 
继续, 
0040129E  |.  33F6          xor     esi, esi 
004012A0  |> /E8 A5000000   /call    0040134A                       ;跟进去,可以知道此处调用的就是rand函数。 
004012A5  |. |99            |cdq 
004012A6  |. |B9 1A000000   |mov     ecx, 1A 
004012AB  |. |F7F9          |idiv    ecx 
004012AD  |. |80C2 41       |add     dl, 41 
004012B0  |. |885434 18     |mov     byte ptr [esp+esi+18], dl       ;  存入(eax % 26) + 'A' 
004012B4  |. |46            |inc     esi 
004012B5  |. |83FE 0F       |cmp     esi, 0F                         ;  循环15次 
004012B8  |.^\72 E6         \jb      short 004012A0 
 
快到核心的地方了。继续努力。 
004012CD  |> /8A4434 0C     /mov     al, byte ptr [esp+esi+C]        ;  用户名的某个字节 
004012D1  |. |C0F8 05       |sar     al, 5                           ;  eax=name>>5 
004012D4  |. |0FBEC0        |movsx   eax, al 
004012D7  |. |8D1480        |lea     edx, dword ptr [eax+eax*4] 
004012DA  |. |8D04D0        |lea     eax, dword ptr [eax+edx*8] 
004012DD  |. |8D0440        |lea     eax, dword ptr [eax+eax*2]      ;  eax=eax*123(Dec) 
004012E0  |. |85C0          |test    eax, eax 
004012E2  |. |7E 0A         |jle     short 004012EE 
004012E4  |. |8BF8          |mov     edi, eax                        ;  edi=(name>>5)*123 
004012E6  |> |E8 5F000000   |/call    0040134A                       ;  调用rand函数 
004012EB  |. |4F            ||dec     edi 
004012EC  |.^|75 F8         |\jnz     short 004012E6                 ;  循环edi次 
004012EE  |> |E8 57000000   |call    0040134A                        ;  调用rand函数 
004012F3  |. |99            |cdq 
004012F4  |. |B9 1A000000   |mov     ecx, 1A 
004012F9  |. |8D7C24 0C     |lea     edi, dword ptr [esp+C]          ;  edi指向用户名 
004012FD  |. |F7F9          |idiv    ecx 
004012FF  |. |0FBE4C34 2C   |movsx   ecx, byte ptr [esp+esi+2C]      ;  序列号的某个字节 
00401304  |. |80C2 41       |add     dl, 41                          ;  dl=(eax % 26) + 'A' 
00401307  |. |0FBEC2        |movsx   eax, dl                         ;  eax=(eax % 26) + 'A' 
0040130A  |. |2BC1          |sub     eax, ecx                        ;  只有eax=ecx,才可能注册成功。 
0040130C  |. |885434 1C     |mov     byte ptr [esp+esi+1C], dl 
00401310  |. |99            |cdq                                     ;  edx=0 
00401311  |. |33C2          |xor     eax, edx 
00401313  |. |83C9 FF       |or      ecx, FFFFFFFF 
00401316  |. |2BC2          |sub     eax, edx 
00401318  |. |03D8          |add     ebx, eax                        ;  ebx 累加 
0040131A  |. |33C0          |xor     eax, eax 
0040131C  |. |46            |inc     esi 
0040131D  |. |F2:AE         |repne   scas byte ptr es:[edi] 
0040131F  |. |F7D1          |not     ecx 
00401321  |. |49            |dec     ecx 
00401322  |. |3BF1          |cmp     esi, ecx 
00401324  |.^\72 A7         \jb      short 004012CD 
00401326  |> \5F            pop     edi 
00401327  |.  8BC3          mov     eax, ebx 
00401329  |.  5E            pop     esi 
0040132A  |.  5B            pop     ebx 
0040132B  |.  83C4 30       add     esp, 30 
0040132E  \.  C3            retn 
 
这里解释一下“只有eax=ecx,才可能注册成功”:关键call返回后,只有eax=0才会注册成功。而 
00401327  |.  8BC3          mov     eax, ebx 
ebx是累加而得。因此每次累加值都应该是0。继续分析就会得到“只有eax=ecx,才可能注册成功”。 
 
最后,总结一下: 
name至少3个字符。key的长度不小于name的长度,且前面长度为name长度的部分必须是大写英文字母,后面部分对注册无影响。rand函数的种子设定为0xffffffff/((name[1]%name[0])*name[2]+1)。 
然后调用15次rand函数。 
然后调用rand函数 (name[0]>>5)*123 次, 
满足条件key[0] = (char)(rand()%26 + 'A') 时才可能注册成功。(注意,这里又调用了一次rand函数) 
然后调用rand函数 (name[1]>>5)*123 次, 
满足条件key[1] = (char)(rand()%26 + 'A') 时才可能注册成功。 
。。。。。。。 
直至取完name的所有字节。 
这里给一个正确的注册码:asdf,YNMK. 
 
另外,给出产生随机数的rand函数代码: 
static long holdrand = 1; 
void srand(unsigned seed) 
{ 
    holdrand = seed; 
} 
int rand() 
{ 
    return (holdrand = holdrand * 214013 + 2531011) >> 16 & 0x7fff; 
} 
????附件发不了??? |   
 
 
 
 |