• 热门专题

从破解实例到探讨反调试

作者:中二病也要写代码!  发布日期:2016-08-08 21:07:28
Tag标签:反调  实例  
  • 系统 : Windows xp

    程序 : KeyGenMe 1 by Taliesin

    程序下载地址 :http://pan.baidu.com/s/1c2HTuqk

    要求 : 注册机编写 

    使用工具 : OD

    可在看雪论坛中查看关于此程序的讨论:传送门

    老规矩,先查看下字符串,定位关键代码:

    004014DF  |.  B8 6C304000   mov     eax, 0040306C                    ;  great job!
    004014E4  |.  8BD8          mov     ebx, eax
    004014E6  |.  83C3 0B       add     ebx, 0B
    004014E9  |.  6A 00         push    0                                ; /Style = MB_OK|MB_APPLMODAL
    004014EB  |.  50            push    eax                              ; |Title => 'Great Job!'
    004014EC  |.  53            push    ebx                              ; |Text => 'You have completed Key Gen Me #1.'
    004014ED  |.  6A 00         push    0                                ; |hOwner = NULL
    004014EF  |.  E8 B4000000   call    <jmp.&user32.MessageBoxA>        ; MessageBoxA
    004014F4  |.  C3            retn
    004014F5  |>  6A 00         push    0                                ; /Style = MB_OK|MB_APPLMODAL
    004014F7  |.  68 C2304000   push    004030C2                         ; |not this time.
    004014FC  |.  68 99304000   push    00403099                         ; |try again, something did not work right.
    00401501  |.  6A 00         push    0                                ; |hOwner = NULL
    00401503  |.  E8 A0000000   call    <jmp.&user32.MessageBoxA>        ; MessageBoxA
    00401508  |.  6A 00         push    0                                ; /ExitCode = 0
    0040150A  .  E8 A5000000   call    <jmp.&kernel32.ExitProcess>      ; ExitProcess

    大概就是简单的一个判断逻辑,现在我们下断点看看程序的流程。

    输入密码之后发现程序没有断在API上,反而显示:'Try Again, something did not work right.'

    看来是遇到反调试了,反复查验发现有一处检测内存是否中断指令的代码:

    00401519   $  BF 96124000   mov     edi, 00401296                    ;  Entry address
    0040151E   .  B9 00010000   mov     ecx, 100
    00401523   .  B0 99         mov     al, 99
    00401525   .  34 55         xor     al, 55                           ;  al = cc
    00401527   .  F2:AE         repne   scas byte ptr es:[edi]           ;  扫描
    00401529   .  85C9          test    ecx, ecx
    0040152B   .  74 06         je      short 00401533
    0040152D   .  5E            pop     esi
    0040152E   .  33F6          xor     esi, esi
    00401530   .  57            push    edi
    00401531   .^ EB C2         jmp     short 004014F5
    00401533   >  C3            retn

    可以粗暴点直接爆破,也可以将ZF Set为1覆盖判断结果。

    接下来还有一处反调试代码:

    00401485   .  33DB          xor     ebx, ebx
    00401487   .  BF 80144000   mov     edi, 00401480
    0040148C   .  83EF 60       sub     edi, 60
    0040148F   .  B8 DE000000   mov     eax, 0DE
    00401494   .  83F0 12       xor     eax, 12
    00401497   .  B9 59000000   mov     ecx, 59
    0040149C   .  F2:AE         repne   scas byte ptr es:[edi]           ;  内存检查
    0040149E   .  85C9          test    ecx, ecx
    004014A0   .  74 06         je      short 004014A8
    004014A2   .  5E            pop     esi
    004014A3   .  33F6          xor     esi, esi
    004014A5   .  57            push    edi
    004014A6   .  EB 4D         jmp     short 004014F5
    004014A8   >  C3            retn
    004014A9   $  BE 9C154000   mov     esi, <jmp.&user32.GetDlgItemText>;  Entry address
    004014AE   .  8B7E 02       mov     edi, dword ptr [esi+2]
    004014B1   .  8B3F          mov     edi, dword ptr [edi]             ;  API地址
    004014B3   .  B9 06000000   mov     ecx, 6
    004014B8   .  B0 CC         mov     al, 0CC
    004014BA   .  F2:AE         repne   scas byte ptr es:[edi]           ;  扫描
    004014BC   .  85C9          test    ecx, ecx
    004014BE   .  74 06         je      short 004014C6
    004014C0   .  5E            pop     esi
    004014C1   .  33F6          xor     esi, esi
    004014C3   .  57            push    edi
    004014C4   .  EB 2F         jmp     short 004014F5
    004014C6   >  C3            retn

    小心点绕过即可,当然啦,如果会执行太多次还是直接爆破为好。

    接下来是算法部分:

    00401332   $  33C0          xor     eax, eax
    00401334   .  B9 00000000   mov     ecx, 0
    00401339   .  BE 23304000   mov     esi, 00403023                    ;  ASCII 'TELBJTMPWM'
    0040133E   .  8A06          mov     al, byte ptr [esi]
    00401340   .  EB 10         jmp     short 00401352
    00401342   >  0FB6C0        movzx   eax, al                          ;  遍历序列号
    00401345   .  80B8 50314000>cmp     byte ptr [eax+403150], 2         ;  是否是大写字母
    0040134C   .  75 0A         jnz     short 00401358                   ;  不是则 输入不合法
    0040134E   .  41            inc     ecx
    0040134F   .  8A0431        mov     al, byte ptr [ecx+esi]
    00401352   >  3C 00         cmp     al, 0                            ;  字符创结尾?
    00401354   .^ 77 EC         ja      short 00401342                   ;  是则结束循环
    00401356   .  EB 07         jmp     short 0040135F
    00401358   >  C605 44304000>mov     byte ptr [403044], 40
    0040135F   >  BE 00304000   mov     esi, 00403000                    ;  ASCII 'pediy'
    00401364   .  33C9          xor     ecx, ecx
    00401366   .  B8 01000000   mov     eax, 1
    0040136B   .  33D2          xor     edx, edx
    0040136D   .  C705 45304000>mov     dword ptr [403045], 0
    00401377   >  B9 00000000   mov     ecx, 0
    0040137C   .  8A0C32        mov     cl, byte ptr [edx+esi]           ;  遍历用户名
    0040137F   .  80F9 00       cmp     cl, 0                            ;  字符创结尾?
    00401382   .  74 09         je      short 0040138D                   ;  是则结束循环
    00401384   .  42            inc     edx
    00401385   .  000D 45304000 add     byte ptr [403045], cl            ;  累加
    0040138B   .^ EB EA         jmp     short 00401377
    0040138D   >  A1 45304000   mov     eax, dword ptr [403045]
    00401392   .  B9 18000000   mov     ecx, 18
    00401397   .  99            cdq
    00401398   .  F7F9          idiv    ecx
    0040139A   .  8815 4F304000 mov     byte ptr [40304F], dl            ;  保存 累加结果除以 18 的余数
    004013A0   .  8A0D 44304000 mov     cl, byte ptr [403044]
    004013A6   .  80F9 40       cmp     cl, 40
    004013A9   .  75 05         jnz     short 004013B0
    004013AB   .  E9 45010000   jmp     004014F5
    004013B0   >  E9 CB000000   jmp     00401480
    004013B5   .  C3            retn

    比较序列号:

    004013B6   $  55            push    ebp
    004013B7   .  8BEC          mov     ebp, esp
    004013B9   .  68 23304000   push    00403023                         ;  ASCII 'TELBJTMPWM'
    004013BE   .  E8 7D010000   call    00401540                         ;  算出长度
    004013C3   .  83F8 0A       cmp     eax, 0A
    004013C6   .  0F85 29010000 jnz     004014F5
    004013CC   .  BE 23304000   mov     esi, 00403023                    ;  ASCII 'TELBJTMPWM'
    004013D1   .  B8 00000000   mov     eax, 0
    004013D6   .  BB 00000000   mov     ebx, 0
    004013DB   .  33C9          xor     ecx, ecx
    004013DD   .  EB 06         jmp     short 004013E5
    004013DF   >  8A0C30        mov     cl, byte ptr [eax+esi]
    004013E2   .  03D9          add     ebx, ecx                         ;  累加字符串前九位
    004013E4   .  40            inc     eax
    004013E5   >  83F8 09       cmp     eax, 9                           ;  循环结束?
    004013E8   .^ 72 F5         jb      short 004013DF
    004013EA   .  8BC3          mov     eax, ebx
    004013EC   .  B9 09000000   mov     ecx, 9
    004013F1   .  99            cdq
    004013F2   .  F7F9          idiv    ecx                              ;  累加数除以9
    004013F4   .  A3 4A304000   mov     dword ptr [40304A], eax          ;  保存商
    004013F9   .  8B7D 08       mov     edi, dword ptr [ebp+8]
    004013FC   .  8A15 4F304000 mov     dl, byte ptr [40304F]
    00401402   .  8AC2          mov     al, dl
    00401404   .  3C 18         cmp     al, 18                           ;  之前的余数大于18?
    00401406   .  76 02         jbe     short 0040140A
    00401408   .  2C 18         sub     al, 18
    0040140A   >  A2 4E304000   mov     byte ptr [40304E], al            ;  保存
    0040140F   .  33C0          xor     eax, eax
    00401411   .  A0 4E304000   mov     al, byte ptr [40304E]
    00401416   .  8A2438        mov     ah, byte ptr [eax+edi]           ;  取字符
    00401419   .  8A36          mov     dh, byte ptr [esi]
    0040141B   .  38F4          cmp     ah, dh                           ;  是否一致?
    0040141D   .  0F85 D2000000 jnz     004014F5
    00401423   .  80EE 41       sub     dh, 41
    00401426   .  8AF2          mov     dh, dl
    00401428   .  B4 00         mov     ah, 0
    0040142A   .  A2 4E304000   mov     byte ptr [40304E], al
    0040142F   .  33C0          xor     eax, eax
    00401431   .  A0 4E304000   mov     al, byte ptr [40304E]
    00401436   .  02C2          add     al, dl                           ;  余数相加
    00401438   .  3C 18         cmp     al, 18                           ;  大于18?
    0040143A   .  76 02         jbe     short 0040143E
    0040143C   .  2C 18         sub     al, 18
    0040143E   >  B9 02000000   mov     ecx, 2
    00401443   .  8A2438        mov     ah, byte ptr [eax+edi]           ;  从表中取字符
    00401446   .  8A3431        mov     dh, byte ptr [ecx+esi]
    00401449   .  38F4          cmp     ah, dh                           ;  是否一致?
    0040144B   .  0F85 A4000000 jnz     004014F5
    00401451   .  EB 24         jmp     short 00401477
    00401453   >  A2 4E304000   mov     byte ptr [40304E], al
    00401458   .  33C0          xor     eax, eax
    0040145A   .  A0 4E304000   mov     al, byte ptr [40304E]
    0040145F   .  80EE 41       sub     dh, 41                           ;  对于首字母A的偏移
    00401462   .  8AD6          mov     dl, dh
    00401464   .  41            inc     ecx
    00401465   .  02C2          add     al, dl
    00401467   .  3C 18         cmp     al, 18                           ;  低于等于18?
    00401469   .  76 02         jbe     short 0040146D
    0040146B   .  2C 18         sub     al, 18
    0040146D   >  8A2438        mov     ah, byte ptr [eax+edi]
    00401470   .  8A3431        mov     dh, byte ptr [ecx+esi]
    00401473   .  38F4          cmp     ah, dh
    00401475   .  75 7E         jnz     short 004014F5
    00401477   >  83F9 08       cmp     ecx, 8                           ;  3 - 8
    0040147A   .^ 72 D7         jb      short 00401453
    0040147C   .  C9            leave
    0040147D   .  C2 0400       retn    4

    序列号的最后一位字符 和 第二位:

    004014CE  /$  BE 23304000   mov     esi, 00403023                    ;  ASCII 'TELBJTMPWM'
    004014D3  |.  A1 4A304000   mov     eax, dword ptr [40304A]          ;  取出商
    004014D8  |.  8A5E 09       mov     bl, byte ptr [esi+9]
    004014DB  |.  38D8          cmp     al, bl
    004014DD  |.  75 16         jnz     short 004014F5
    00401510   $  A0 24304000   mov     al, byte ptr [403024]
    00401515   .  3C 45         cmp     al, 45                           ;  序列号第二个字符必须是E
    00401517   .^ 75 DC         jnz     short 004014F5

    看得晕了?不要紧,我来总结一下构成序列号的过程:

    1.必须全部大写

    2.长度为10.

    3.第一个字符按余数从表中载入

    4.第二个字符为“E”

    5.第三个字符按余数从表中载入

    6.第4-9个字符按照前一个字符取数,再从表中载入

    7.最后一位为前9位的平均数。

    整体算法不困难,就是稍微有点复杂,有点耐心就可以搞定啦。

    打开http://www.cnblogs.com/ZRBYYXDM/p/5115596.html中搭建的框架,将OnBtnDecrypt函数编辑如下:

    void CKengen_TemplateDlg::OnBtnDecrypt() 
    {
        // TODO: Add your control notification handler code here
        CString str;
        GetDlgItemText( IDC_EDIT_NAME,str );                    //获取用户名字串基本信息。
        int len = str.GetLength();
    
        if ( len != 0 ){                                        //格式控制。
            char* KeyList = 'ZWATRQLCGHPSXYENVBJDFKMU';
            char Key[11];
            byte sum = 0;
    
            for ( int i = 0 ; i != len ; i++ ){
                sum += str[i];
            }
            byte remainder = sum % 0x18;
    
            Key[0] = KeyList[remainder];
            Key[1] = 'E';
            
            remainder *= 2;
    
            if ( remainder > 0x18 )
                remainder -= 0x18;
    
            Key[2] = KeyList[remainder];
    
            for ( i = 3 ; i != 9 ; i++ ){
                remainder += (Key[i-1] - 'A');
    
                if ( remainder > 0x18 )
                    remainder -= 0x18;
    
                Key[i] = KeyList[remainder];
            }
    
            DWORD KeySum = 0;
            
            for ( i = 0 ; i != 9 ; i++ ){
                KeySum += Key[i];
            }
    
            Key[9] = KeySum / 9;
            Key[10] = '';
    
            SetDlgItemText( IDC_EDIT_PASSWORD,Key );
        }
        else
            MessageBox( '用户名格式错误!' );
    }

    再在OnInitDialog中添加此代码修改标题:SetWindowText(_T('Keygen'));

    运行效果:

     

    本例中,分别对几个程序关键点和API检测断点,在流程中不断则执行这些代码。这就让破解者比较头疼。

    我认为比较强大的防护不是无法攻破的城堡,而是尽量拖延时间,消耗对方的精力。能做到 破解成本 > 软件价值,那就是很好的防御措施。

    例如,代码开始执行的时候 重复检测断点,启动防调试机制(格盘,关机,等等不可描述的行为),让破解者头疼不已,只能选择爆破程序。

    这时,将反调试代码送入MD5函数,生成一段不可逆的子串。

    再给子串做操作生成特殊数值,最终跳转到特定地址( 基地址 + 特殊数值 )。

    这样,一旦动了反调试代码,程序将执行未知指令,即刻崩溃。

    想要逆向这样的程序还是比较痛苦的,特别是对于不熟悉加密算法的骇客来说。就算掌握了整个流程,要得到真正的地址就要先默默忍受反调试的折磨:)

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规