飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 4072|回复: 3

[原创] 一个crackme的算法分析

[复制链接]

该用户从未签到

发表于 2009-1-24 09:26:26 | 显示全部楼层 |阅读模式
一个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;
}
????附件发不了???
PYG19周年生日快乐!

该用户从未签到

发表于 2009-1-24 15:01:43 | 显示全部楼层
学习算法。
PYG19周年生日快乐!

该用户从未签到

发表于 2009-1-24 16:08:08 | 显示全部楼层
/:good 分析得很好。
PYG19周年生日快乐!

该用户从未签到

发表于 2009-1-24 18:05:18 | 显示全部楼层
好文章,学习下~
PYG19周年生日快乐!
您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

快速回复 返回顶部 返回列表