首页
社区
课程
招聘
[原创]一个简单的CrackMe代码分析(入门级别)
发表于: 2009-7-17 22:43 4385

[原创]一个简单的CrackMe代码分析(入门级别)

2009-7-17 22:43
4385
【文章标题】: Haunted的crack#1的破解
【文章作者】: 烏鴉(DarkByte)
【软件名称】: Haunted的crack#1
【下载地址】:http://bbs.pediy.com/showthread.php?t=12136&page=12
初入樵新手 crackme 博物馆168楼isgod发布的
【加壳方式】: 无
【编写语言】: MASM32 / TASM32
【使用工具】: OD
【操作平台】: WINXP
【软件介绍】: 无
【作者声明】: 偶是菜鸟,愿意与大家一起交流!第一次写破文,写的不好还请见谅
--------------------------------------------------------------------------------
【详细过程】
   程序很简单,进行的明码比较,主要分析下代码。
   运行程序,输入用户名,密码运行,弹出对话框提示,PEID查下,无壳,OD载入
   因为弹出对话框提示,所以对MessageBoxA下断点,在Command窗口输入bp MessageBoxA回车
   F9运行程序,输入用户名密码,停在系统领空,Alt+F9我们返回程序领空
004012D3   .  56            push    esi
004012D4   .  FF15 08204000 call    dword ptr [402008]               ;  USER32.MessageBoxA
004012DA   .  B9 C8000000   mov     ecx, 0C8                               <---我们停在这里
004012DF   .  B8 00000000   mov     eax, 0                          
004012E4   .  BF 0C204000   mov     edi, 0040200C                    ;  ASCII "Wrong Code",CR,LF,"-weradiyi-"
004012E9   .  F2:AE         repne   scas byte ptr es:[edi]
004012EB   .  4F            dec     edi
004012EC   .  C607 DC       mov     byte ptr [edi], 0DC
004012EF   .  5E            pop     esi
004012F0   .  C3            retn

这时候已经对注册码比较过了所以关键Call在上面,我们网上找
0040123E   .  B8 7C204000   mov     eax, 0040207C                    ;  ASCII "ssssssssssssssssss"
00401243   .  E8 F0000000   call    00401338                             ;  这个Call是计算注册码长度的,别当关键Call喽^_^
00401248   .  39D0          cmp     eax, edx
0040124A   .  0F85 5F000000 jnz     004012AF                         ;  跳向失败                    
00401250   .  BE B8204000   mov     esi, 004020B8                    ;  ASCII "HNT-3288-0304-7532-KU"
00401255   .  BF 7C204000   mov     edi, 0040207C                    ;  ASCII "ssssssssssssssssss"   
0040125A   .  89C1          mov     ecx, eax
0040125C   >  8A06          mov     al, byte ptr [esi]
0040125E   .  8A27          mov     ah, byte ptr [edi]                     ;这一段是把用户的注册码和计算得出的注册码对位相减
00401260   .  46            inc     esi
00401261   .  47            inc     edi
00401262   .  30C4          xor     ah, al                           ;  ah==al则成功
00401264   .  0F85 45000000 jnz     004012AF                         ;  跳向失败
0040126A   .  49            dec     ecx
0040126B   .^ 75 EF         jnz     short 0040125C

由上面我们知道当计算出来注册码后,先比较长度与输入的是否相等,然后再比较每一位是否相等,有一个不相等都讲导致失败
我们继续往上找

004010C9   .  40            inc     eax
004010CA   .  C3            retn
004010CB   >  B9 1E000000   mov     ecx, 1E                             ;这里就是算法函数开始的地方了,为什么呢?看到上面的retn了吧
004010D0   .  31C0          xor     eax, eax                             ;说明这是一个函数的开始
004010D2   .  BF B8204000   mov     edi, 004020B8                    ;  ASCII "HNT-3288-0304-7532-KU"
004010D7   .  F3:AA         rep     stos byte ptr es:[edi]
004010D9   .  31FF          xor     edi, edi
004010DB   .  47            inc     edi
004010DC   .  C1E7 0B       shl     edi, 0B
004010DF   .  6A 2D         push    2D
004010E1   .  68 4F204000   push    0040204F                         ;  ASCII "DarkByte"
004010E6   .  57            push    edi

我们可以看到
004010CB   >  B9 1E000000   mov     ecx, 1E        这句是由上面跳转下来的,我们继续往上走看到下面的代码

这里就是窗口的回调函数啦
0040106E   .  89D8          mov     eax, ebx
00401070   .  C1E0 10       shl     eax, 10
00401073   .  C1E8 10       shr     eax, 10
00401076   .  31FF          xor     edi, edi
00401078   .  47            inc     edi
00401079   .  83F8 02       cmp     eax, 2
0040107C   .  0F84 83020000 je      00401305
00401082   .  C1E7 07       shl     edi, 7
00401085   .  39F8          cmp     eax, edi
00401087   .  0F84 3E000000 je      004010CB                是由这里跳转下去的
0040108D   .  C1E7 02       shl     edi, 2
00401090   .  39C7          cmp     edi, eax
00401092   .  0F84 59020000 je      004012F1

再往上的这段代码就是程序的入口了
0040102D >/$  6A 00         push    0                                ; /(initial cpu selection)
0040102F  |.  E8 E44F0000   call    <jmp.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
00401034  |.  A3 98214000   mov     dword ptr [402198], eax
00401039  |.  68 6C214000   push    0040216C                         ; /user32.dllgetdlgitemtexta
0040103E  |.  E8 DB4F0000   call    <jmp.&KERNEL32.LoadLibraryA>     ; \LoadLibraryA
00401043  |.  A3 08204000   mov     dword ptr [402008], eax
00401048  |.  E8 C8020000   call    00401315
0040104D  |.  A3 08204000   mov     dword ptr [402008], eax
00401052  |.  6A 00         push    0                                ; /lParam = NULL
00401054  |.  68 00104000   push    00401000                         ; |DlgProc = Crackme.00401000
00401059  |.  6A 00         push    0                                ; |hOwner = NULL
0040105B  |.  68 E8030000   push    3E8                              ; |pTemplate = 3E8
00401060  |.  FF35 98214000 push    dword ptr [402198]               ; |hInst = 00400000
00401066  |.  E8 954F0000   call    <jmp.&USER32.DialogBoxParamA>    ; \DialogBoxParamA
0040106B  |.  31C0          xor     eax, eax
0040106D  \.  C3            retn

这个原来的代码就类似这样:

start:
                call        _WinMain
                invoke        ExitProcess,NULL

                end        start

再向上的代码就是消息循环了....
- -!额,跑题了,我们回来继续分析注册码的计算
两个GetDlgItemTextA后,我们的注册信息被传入,如果不输入会直接retn掉
我们来到这里
00401111   > \B8 4F204000   mov     eax, 0040204F                    ;  ASCII "DarkByte"
00401116   .  E8 1D020000   call    00401338                             ;还是刚才的那个计算字符串长度的函数
0040111B   .  A3 A0214000   mov     dword ptr [4021A0], eax          ;  保存用户名长度
00401120   .  BB 4F204000   mov     ebx, 0040204F                    ;  ASCII "DarkByte"
00401125   .  0FB613        movzx   edx, byte ptr [ebx]              ;  取出用户名的第一位
00401128   .  0FB64C03 FF   movzx   ecx, byte ptr [ebx+eax-1]        ;  取出用户名最后一位
0040112D   .  0FAFD1        imul    edx, ecx                         ;  用户名第一位乘以最后一位
00401130   .  50            push    eax
00401131   .  B9 02000000   mov     ecx, 2
00401136   .  52            push    edx                              ;  保存乘积
00401137   .  31D2          xor     edx, edx
00401139   .  F7F9          idiv    ecx                              ;  用户名长度/2
0040113B   .  5A            pop     edx                              ;  取出乘积
0040113C   .  0FB60C03      movzx   ecx, byte ptr [ebx+eax]          ;  
00401140   .  0FAFD1        imul    edx, ecx                             ;用刚才乘出来的那个数再乘以用户名中间的那位
00401143   .  BB B8204000   mov     ebx, 004020B8
00401148   .  C703 484E542D mov     dword ptr [ebx], 2D544E48        ;  正确注册码前三位(固定的)
0040114E   .  89D0          mov     eax, edx                             ;把刚才的乘积给eax为后面的Call做准备
00401150   .  83C3 04       add     ebx, 4                           ;
00401153   .  E8 F7010000   call    0040134F
00401158   .  C705 00204000>mov     dword ptr [402000], 1E

写成C就类似
num = username[0]*username[namelen-1]*username[namelen/2]

我们跟进这个Call

这个函数是一个求余函数,而被除数正好是刚才我们的乘积
0040134F  /$  51            push    ecx
00401350  |.  56            push    esi
00401351  |.  4B            dec     ebx
00401352  |.  B9 04000000   mov     ecx, 4                            ;循环四次
00401357  |.  BE 0A000000   mov     esi, 0A
0040135C  |.  52            push    edx
0040135D  |>  31D2          xor     edx, edx
0040135F  |.  F7FE          idiv    esi                              ;  求余
00401361  |.  80C2 30       add     dl, 30                           ;  余数加30h
00401364  |.  88140B        mov     byte ptr [ebx+ecx], dl             ;把求出的数保存起来,这里就是注册码
00401367  |.^ E2 F4         loopd   short 0040135D
00401369  |.  5A            pop     edx
0040136A  |.  5E            pop     esi
0040136B  |.  59            pop     ecx
0040136C  \.  C3            retn

上面的代码转换成C就类似
这里产生了注册码的第一个四位数组
        Rcode[i] = num3 %0x0A+0x30;
        num3/=0x0A;
继续向下
00401158   .  C705 00204000>mov     dword ptr [402000], 1E
00401162   .  68 00204000   push    00402000                         ; /pBufferSize = Crackme.00402000
00401167   .  68 9A204000   push    0040209A                         ; |Buffer = Crackme.0040209A
0040116C   .  E8 B34E0000   call    <jmp.&KERNEL32.GetComputerNameA> ; \GetComputerNameA
00401171   .  B8 9A204000   mov     eax, 0040209A
00401176   .  E8 F2010000   call    0040136D                         ;  将计算机名累加,我们注意这里的返回值也就是累加和在eax中,
                                                                        而eax在下一个Call中被ebx覆盖了,而中间并没有保存,可见这个累加值没用                                                                     
0040117B   .  BB 4F204000   mov     ebx, 0040204F                    ;  ASCII "DarkByte"
00401180   .  E8 00020000   call    00401385                             ;很麻烦的一个Call,写注册机的时候在这里卡了下
00401185   .  50            push    eax

我们看看上面的那个Call
00401385  /$  51            push    ecx
00401386  |.  52            push    edx
00401387  |.  89D8          mov     eax, ebx
00401389  |.  E8 AAFFFFFF   call    00401338                         ;  字符长度
0040138E  |.  89C1          mov     ecx, eax                            ;ecx为用户名长度
00401390  |.  4B            dec     ebx
00401391  |.  31D2          xor     edx, edx
00401393  |>  8A140B        /mov     dl, byte ptr [ebx+ecx]             ;注意这里,这里只是把edx的最低字节擦除了,而乘积保存在高位的并未擦除
00401396  |.  0FAFD1        |imul    edx, ecx                        ;  用户名对应位乘以对应字符
00401399  |.  01D0          |add     eax, edx
0040139B  |.  49            |dec     ecx
0040139C  |.^ 75 F5         \jnz     short 00401393
0040139E  |.  5A            pop     edx
0040139F  |.  59            pop     ecx
004013A0  \.  C3            retn
这段码循环类似C语言的
        for(i=namelen;i>0;i--)
        {
                temp= i*((temp&0xFFFFFF00)|(int(*(username+i-1))));
                num +=temp;
        }
继续向下
00401180   .  E8 00020000   call    00401385
00401185   .  50            push    eax                                     ;保存刚才算出来的那个数
00401186   .  31C0          xor     eax, eax                         ;  eax=0参数这是cpuid的一个参数,将返回Cpu的信息到寄存器中
00401188   .  0FA2          cpuid                                    ;  获取cpu信息
0040118A   .  58            pop     eax                              ;  取出了上面Call的那个数
0040118B   .  51            push    ecx                                     ;保存Cpu的返回信息
0040118C   .  52            push    edx                                     ;保存Cpu的返回信息
0040118D   .  21D8          and     eax, ebx                         ;  刚才那个Call计算出来的值与Cpu信息的EbX
0040118F   .  BB B8204000   mov     ebx, 004020B8                    ;  ASCII "HNT-3288"
00401194   .  89DF          mov     edi, ebx
00401196   .  50            push    eax
00401197   .  66:31C0       xor     ax, ax
0040119A   .  F2:AE         repne   scas byte ptr es:[edi]
0040119C   .  89FB          mov     ebx, edi
0040119E   .  58            pop     eax                                     ;这里是与出来的那个值
0040119F   .  4B            dec     ebx
004011A0   .  C603 2D       mov     byte ptr [ebx], 2D               ;  加-
004011A3   .  43            inc     ebx
004011A4   .  E8 A6010000   call    0040134F                            ;这个Call前面分析过了,这时候被除数就上面的eax
                                                                    ;在这里就产生了注册码的第二个四位,同上就不分析啦
再往下
004011A9   .  B8 4F204000   mov     eax, 0040204F                    ;  ASCII "DarkByte"
004011AE   .  E8 BA010000   call    0040136D
004011B3   .  59            pop     ecx                                     ;这里注意下与前面的push的对应关系
004011B4   .  31C8          xor     eax, ecx                         ;  与cpuid得到的edx异或
004011B6   .  59            pop     ecx
004011B7   .  09C8          or      eax, ecx                         ;  与cpu得到的ecx或
004011B9   .  50            push    eax
004011BA   .  BB 4F204000   mov     ebx, 0040204F                    ;  ASCII "DarkByte"
004011BF   .  0FB603        movzx   eax, byte ptr [ebx]                     ;用户名的第一位
004011C2   .  0FB64B 01     movzx   ecx, byte ptr [ebx+1]       
004011C6   .  0FAFC1        imul    eax, ecx                         ;  用户名第一位乘第二位
004011C9   .  59            pop     ecx
004011CA   .  0FAFC1        imul    eax, ecx                             ;第一位和第二位的成绩乘上上面或出来的那个数

这段就是把用户名的长度与刚才cpuid出来的信息的edx异或再或上ecx再和第一,第二位相乘
C语言这样
        num2 = ((Count(username)^_EDX)|_ECX)*username[0]*username[1];

004011CD   .  BF B8204000   mov     edi, 004020B8                    ;  ASCII "HNT-3288-0304"
004011D2   .  50            push    eax
004011D3   .  30C0          xor     al, al
004011D5   .  F2:AE         repne   scas byte ptr es:[edi]
004011D7   .  58            pop     eax
004011D8   .  89FB          mov     ebx, edi
004011DA   .  4B            dec     ebx
004011DB   .  C603 2D       mov     byte ptr [ebx], 2D
004011DE   .  43            inc     ebx
004011DF   .  E8 6B010000   call    0040134F                              ;又是刚才那个函数,到这第三个4位数就诞生了

- -!我们继续,忽忽..
004011E4   .  B8 4F204000   mov     eax, 0040204F                    ;  ASCII "DarkByte"
004011E9   .  E8 7F010000   call    0040136D                         ;  计算用户名累加值
004011EE   .  B9 1A000000   mov     ecx, 1A
004011F3   .  31D2          xor     edx, edx
004011F5   .  F7F9          idiv    ecx                              ;  用户名累加值%1A
004011F7   .  B8 41000000   mov     eax, 41                             ;  
004011FC   .  01D0          add     eax, edx                             ;余数+0x41
004011FE   .  89C7          mov     edi, eax                             ;保存在edi里
00401200   .  BB 4F204000   mov     ebx, 0040204F                    ;  ASCII "DarkByte"
00401205   .  E8 7B010000   call    00401385                             ;这里还是刚才那个奇怪乘法
0040120A   .  31D2          xor     edx, edx
0040120C   .  F7F9          idiv    ecx                                      ;eax的值,也就是刚才那个call的返回值求余1A,
0040120E   .  83C2 41       add     edx, 41                                ;加上0x41,这里保存在edx

00401211   .  BB B8204000   mov     ebx, 004020B8                    ;  ASCII "HNT-3288-0304-7532"
00401216   .  50            push    eax
00401217   .  31C0          xor     eax, eax
00401219   .  57            push    edi                              ;  保存第一次计算出来的数
0040121A   .  89DF          mov     edi, ebx
0040121C   .  F2:AE         repne   scas byte ptr es:[edi]
0040121E   .  58            pop     eax                              ;  这里的值就是上面保存在edi里面的值
0040121F   .  89FB          mov     ebx, edi                         ;  Crackme.004020CB
00401221   .  5F            pop     edi
00401222   .  4B            dec     ebx
00401223   .  C603 2D       mov     byte ptr [ebx], 2D
00401226   .  8843 01       mov     byte ptr [ebx+1], al             ;把第一次计算出来的树存放
00401229   .  8853 02       mov     byte ptr [ebx+2], dl             ;第二个数存放,edx在计算完第二个数后一直未使用
0040122C   .  C643 03 00    mov     byte ptr [ebx+3], 0

这段类似
        Rcode[19] = Count(username)%0x1A+0x41;
        Rcode[20] =char(unsigned long(num5) %0x1A +0x41);

好了,所有的注册码都出来了...再往下就是注册码的比较了,文章开始已经说了就不继续了。。

貌似有点啰嗦了

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 2575
活跃值: (502)
能力值: ( LV2,RANK:85 )
在线值:
发帖
回帖
粉丝
2
新人就这么厉害,学习一下
2009-7-17 23:07
0
雪    币: 289
活跃值: (103)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
确实比较厉害了
2009-7-20 23:09
0
游客
登录 | 注册 方可回帖
返回
//