• 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 }
  • 相关阅读:
    使用 Nginx 内置 $http_user_agent 来区分( 电脑 pc、手机 mobile、平板 pad )端的内容访问
    原创《开源一个用 vue 写的树层级组件 vue-ztree》
    原创《weex面向未来的架构》
    原创《如何用vue来轻松的驾驭 html5 webapp的页面体验》
    Pdf Convert Image 的解决方案
    原创《分享(Angular 和 Vue)按需加载的项目实践优化方案》
    .npmrc 实用小技巧
    使用Tampermonkey,实现Gitlab禁用自我Merge的功能
    vue 之 render 函数不能渲染非全局自定义函数-方案
    反射、注解和动态代理
  • 原文地址:https://www.cnblogs.com/mavaL/p/2676776.html
Copyright © 2020-2023  润新知