CrackMe练习之KGM1Tal

CrakeMe下载

 

先运行下这个CrackMe,没啥特别的:

                

用PEID侦测下,看到它是用汇编写的:

            

OD打开之,Ctrl+N看到熟悉的GetDlgItemTextA,轻松下个断点准备动工,但出乎意料的是输入用户名序列号并点击Check后,并没有断下来,而是直接弹出了验证失败对话框.为什么会这样呢?其实我当时也不知道咋回事....看了文档的解说后原来该程序中存在简单的反调试(anti-debug)手段,即运行时检测关键处是否有0xCC软断点,我们可以通过硬件断点来破除它.于是,我们在GetDlgItemTextA处下个硬件访问断点,再重来就断下来了,如下:

 1 00401296   $  55            push    ebp
 2 00401297   .  8BEC          mov     ebp, esp
 3 00401299   .  60            pushad
 4 0040129A   .  BE FE124000   mov     esi, 004012FE
 5 0040129F   .  56            push    esi
 6 004012A0   .  64:FF35 00000>push    dword ptr fs:[0]
 7 004012A7   .  64:8925 00000>mov     dword ptr fs:[0], esp
 8 004012AE   .  FF35 3C304000 push    dword ptr [40303C]               ; /Count = 1E (30.)
 9 004012B4   .  68 00304000   push    00403000                         ; |Buffer = KGM1Tal.00403000
10 004012B9   .  68 EC030000   push    3EC                              ; |ControlID = 3EC (1004.)
11 004012BE   .  FF75 08       push    dword ptr [ebp+8]                ; |hWnd
12 004012C1   .  E8 D6020000   call    <jmp.&user32.GetDlgItemTextA>    ; \GetDlgItemTextA
13 004012C6   .  FF35 40304000 push    dword ptr [403040]               ; /Count = 14 (20.)
14 004012CC   .  68 23304000   push    00403023                         ; |Buffer = KGM1Tal.00403023
15 004012D1   .  68 ED030000   push    3ED                              ; |ControlID = 3ED (1005.)
16 004012D6   .  FF75 08       push    dword ptr [ebp+8]                ; |hWnd
17 004012D9   .  E8 BE020000   call    <jmp.&user32.GetDlgItemTextA>    ; \GetDlgItemTextA
18 004012DE   .  E8 4F000000   call    00401332                         ;  检测过程
19 004012E3   .  68 53304000   push    00403053                         ;  zwatrqlcghpsxyenvbjdfkmu
20 004012E8   .  E8 C9000000   call    004013B6                         ;  进一步检测
21 004012ED   .  E8 DC010000   call    004014CE                         ;  最后一步检测

在该区域开头处查看信息面板看到调用此处的地址:

1 Local call from 00401279

来到调用处:

 1 00401230  /.  55            push    ebp
 2 00401231  |.  8BEC          mov     ebp, esp
 3 00401233  |.  817D 0C 10010>cmp     [arg.2], 110                     ;  WM_INITDLALOG
 4 0040123A  |.  75 1E         jnz     short 0040125A
 5 0040123C  |.  68 057F0000   push    7F05                             ; /RsrcName = IDI_WINLOGO
 6 00401241  |.  6A 00         push    0                                ; |hInst = NULL
 7 00401243  |.  E8 5A030000   call    <jmp.&user32.LoadIconA>          ; \LoadIconA
 8 00401248  |.  50            push    eax                              ; /lParam
 9 00401249  |.  6A 01         push    1                                ; |wParam = 1
10 0040124B  |.  68 80000000   push    80                               ; |Message = WM_SETICON
11 00401250  |.  FF75 08       push    [arg.1]                          ; |hWnd
12 00401253  |.  E8 56030000   call    <jmp.&user32.SendMessageA>       ; \SendMessageA
13 00401258  |.  EB 36         jmp     short 00401290
14 0040125A  |>  817D 0C 11010>cmp     [arg.2], 111                     ;  WM_COMMAND
15 00401261  |.  75 1D         jnz     short 00401280
16 00401263  |.  817D 10 E9030>cmp     [arg.3], 3E9
17 0040126A  |.  75 24         jnz     short 00401290
18 0040126C  |.  E8 A8020000   call    00401519                         ;  anti-debug
19 00401271  |.  E8 33020000   call    004014A9                         ;  anti-debug
20 00401276  |.  FF75 08       push    [arg.1]
21 00401279  |.  E8 18000000   call    00401296                         ;  检测序列号过程

其中的00401519和004014A9即是两个反调试检测函数:

 1 00401519   $  BF 96124000   mov     edi, 00401296                    ;  Entry address
 2 0040151E   .  B9 00010000   mov     ecx, 100
 3 00401523   .  B0 99         mov     al, 99
 4 00401525   .  34 55         xor     al, 55                           ;  99H^55H=CCH
 5 00401527   .  F2:AE         repne   scas byte ptr es:[edi]           ;  扫描从00401296开始的100H字节是否有0xCC
 6 00401529   .  85C9          test    ecx, ecx
 7 0040152B   .  74 06         je      short 00401533                   ;  检测正常
 8 0040152D   .  5E            pop     esi
 9 0040152E   .  33F6          xor     esi, esi
10 00401530   .  57            push    edi
11 00401531   .^ EB C2         jmp     short 004014F5                   ;  检测到跟踪,完蛋
12 00401533   >  C3            retn

可以看到,00401519函数进行了软断点检测,其中的00401296就是点击Check按钮的控件响应函数地址.而004012A9做了个补充,它检测系统领空User.dll的GetDlgItemTextA实现处的开始6个字节是否下了软断点,因为破解者一般都习惯在开头处下断.这样,就形成了一个比较粗糙的反调试机制.

 1 004014A9   $  BE 9C154000   mov     esi, <jmp.&user32.GetDlgItemText>;  Entry address
 2 004014AE   .  8B7E 02       mov     edi, dword ptr [esi+2]
 3 004014B1   .  8B3F          mov     edi, dword ptr [edi]
 4 004014B3   .  B9 06000000   mov     ecx, 6
 5 004014B8   .  B0 CC         mov     al, 0CC
 6 004014BA   .  F2:AE         repne   scas byte ptr es:[edi]
 7 004014BC   .  85C9          test    ecx, ecx
 8 004014BE   .  74 06         je      short 004014C6
 9 004014C0   .  5E            pop     esi
10 004014C1   .  33F6          xor     esi, esi
11 004014C3   .  57            push    edi
12 004014C4   .  EB 2F         jmp     short 004014F5
13 004014C6   >  C3            retn

当然了,目前的各种反汇编反调试手段相当丰富刺激,根据我看的<<黑客反汇编解密>>(翻译水平实在叫人头痛)的介绍(笔记在公司,带不回来呀...),以后接触到了商业级软件的保护机制,会像速降一样刺激的....
接下来的工作就没啥特别的了,边调试边翻译汇编码边做记录就行了,熟悉和熟练了就会越来越快...何况这还不是C++写的...何况这个还不是完全优化版本...

最后写出KeyGen:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 
 5 void Keygen(const char* user, char* code)
 6 {
 7     //求序列号用的密匙
 8     const char szBuffer[] = { "ZWATRQLCGHPSXYENVBJDFKMU" };
 9 
10     unsigned char a = 0;
11     for(int i=0; i<strlen(user); ++i) a += user[i];
12     a %= 0x18;
13     //序列号第2位必为E
14     code[1] = 'E';
15     //序列号第1位
16     code[0] = szBuffer[a];
17     int b = a;
18     if(b > 0x18) b -= 0x18;
19     b += a;
20     if(b > 0x18) b -= 0x18;
21     //序列号第3位
22     code[2] = szBuffer[b];
23     //序列号第4-9位
24     for (int i=3; i<9; ++i)
25     {
26         b = b + code[i-1] - 'A';
27         if(b > 0x18) b -= 0x18;
28         code[i] = szBuffer[b];
29     }
30     //序列号第10位
31     int c = 0;
32     for(int i=0; i<9; ++i) c += code[i];
33     c /= 9;
34     code[9] = c;
35     code[10] = '\0';
36 }
37 
38 int main()
39 {
40     char szUser[50];
41     char szCode[50];
42     printf("请输入用户名:");
43     scanf("%s", szUser);
44 
45     Keygen(szUser, szCode);
46     printf("序列号是: %s\n", szCode);
47     system("pause");
48 
49     return 0;
50 }

 

 

posted @ 2012-09-08 16:35  mavaL  阅读(506)  评论(0编辑  收藏  举报