zoukankan      html  css  js  c++  java
  • 深入底层逆向分析TDC‘s keygenme(手脱压缩壳)

    系统 : Windows xp

    程序 : TDC‘s keygenme

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

    要求 : 脱壳 & 注册机编写

    使用工具 :OD & IDA & PEID & LordPE & Import REC

    可在“PEDIY CrackMe 2007”中查找关于此程序的分析,标题为“TDC.Crackme10算法分析”。

     

    首先了解一下壳是什么:

    作者编好软件后,编译成exe可执行文件。

    1.有一些版权信息需要保护起来,不想让别人随便改动,如作者的姓名等,即为了保护软件不被破解,通常都是采用加壳来进行保护。

    2.需要把程序搞的小一点,从而方便使用。于是,需要用到一些软件,它们能将exe可执行文件压缩,

    3.在黑客界给木马等软件加壳脱壳以躲避杀毒软件。     

                                                    --源自百度百科

    简单说来,壳就是用来防止破解者对于程序文件的非法修改。壳又分为压缩壳、加密壳。他们的应用范围也不尽相同,压缩壳主要用来压缩程序文件的体积,对于破解者来说强度不大。而加密壳以加密保护为重点,用尽了各种反跟踪技术,保护重点是在OEP(程序入口点)的隐藏和IAT(输入表)的加密上。

     

    这篇博文中我们来尝试破解一个被压缩壳保护、带有简单反调试的程序。

    将程序拖入PEID查看状态:

    显示该程序加了upx壳,可以利用网上的脱壳机来脱壳。这里,为了加深印象,我们尝试手动脱壳。od载入程序,断在外壳处:

    00446330 > $  60            pushad
    00446331   .  BE 00804200   mov     esi, 00428000
    00446336   .  8DBE 0090FDFF lea     edi, dword ptr [esi+FFFD9000]
    0044633C   .  57            push    edi
    0044633D   .  83CD FF       or      ebp, FFFFFFFF
    00446340   .  EB 10         jmp     short 00446352

    单步执行pushad之后,esp指向0012FFA4 ,键入命令hr 12FFA4下硬件断点,F9运行程序断在此处 

    00446493   .- E9 73ABFBFF   jmp     0040100B

    单步执行跳转进入OEP:

    0040100B    6A 0A           push    0A
    0040100D    68 B90B0000     push    0BB9
    00401012    FF35 18624000   push    dword ptr [406218]
    00401018    E8 05030000     call    00401322                         ; jmp to kernel32.FindResourceA
    0040101D    A3 1C624000     mov     dword ptr [40621C], eax
    00401022    FF35 1C624000   push    dword ptr [40621C]
    00401028    FF35 18624000   push    dword ptr [406218]
    0040102E    E8 07030000     call    0040133A                         ; jmp to kernel32.LoadResource
    00401033    A3 20624000     mov     dword ptr [406220], eax
    00401038    E8 C3FFFFFF     call    00401000
    0040103D    FF35 1C624000   push    dword ptr [40621C]
    00401043    FF35 18624000   push    dword ptr [406218]
    00401049    E8 F8020000     call    00401346                         ; jmp to kernel32.SizeofResource
    0040104E    A3 24624000     mov     dword ptr [406224], eax
    00401053    FF35 20624000   push    dword ptr [406220]
    00401059    E8 E2020000     call    00401340                         ; jmp to kernel32.SetHandleCount
    0040105E    8BF0            mov     esi, eax
    00401060    A1 24624000     mov     eax, dword ptr [406224]
    00401065    83C0 04         add     eax, 4
    00401068    50              push    eax
    00401069    6A 40           push    40
    0040106B    E8 C4020000     call    00401334                         ; jmp to kernel32.GlobalAlloc
    00401070    A3 28624000     mov     dword ptr [406228], eax

    此时单击菜单Debug->hardware breakpoints删除之前设置的硬件断点。

    打开LordPE选择keygenme并单击右键选择“完整转存”:

    保存dump文件之后,再打开输入表重建工具Import REC附加到keygenme:

    填写OEP为“0000100B”,依次单击“自动查找IAT”、“获取输入表”:

    最后,单击“修复转存文件”,选中之前的dump文件,则脱壳成功。

     

    脱壳成功之后我们用IDA载入程序,shift + F12查看字符串表:

    发现提示成功的字串“Congratulations! You are now level 2! :-)”,双击定位,通过交叉引用来到调用它的位置并向上翻找出关键算法:

    TDC0:00401181                 cmp     dword ptr [ebp+0Ch], 111h
    TDC0:00401188                 jnz     loc_401292
    TDC0:0040118E                 cmp     dword ptr [ebp+10h], 3E9h
    TDC0:00401195                 jnz     loc_40125C
    TDC0:0040119B                 push    33h
    TDC0:0040119D                 push    offset dword_40622C
    TDC0:004011A2                 push    7D1h
    TDC0:004011A7                 push    dword ptr [ebp+8]
    TDC0:004011AA                 call    GetDlgItemTextA
    TDC0:004011AF                 imul    eax, 5
    TDC0:004011B2                 cmp     eax, 0FAh
    TDC0:004011B7                 ja      loc_401246
    TDC0:004011BD                 cmp     eax, 1Eh
    TDC0:004011C0                 jb      loc_401246
    TDC0:004011C6                 push    0
    TDC0:004011C8                 push    0
    TDC0:004011CA                 push    7D2h
    TDC0:004011CF                 push    dword ptr [ebp+8]
    TDC0:004011D2                 call    GetDlgItemInt
    ;省略一部分关键代码:

    这里,选中关键代码,单击“视图”->"图表"->"流程图"显示关键算法的流程:

    可以直观的看出,关键算法处用了不少分支语句,程序中肯定加了不少对输入的判断。

    打开OD载入程序,输入命令"bp 004011AA",F9运行,程序断在此处:

    0040119B    6A 33           push    33
    0040119D    68 2C624000     push    0040622C                         ; ASCII "pediy"
    004011A2    68 D1070000     push    7D1
    004011A7    FF75 08         push    dword ptr [ebp+8]
    004011AA    E8 AF010000     call    <jmp.&user32.GetDlgItemTextA>
    004011AF    6BC0 05         imul    eax, eax, 5                      ; 用户名字串长度*5
    004011B2    3D FA000000     cmp     eax, 0FA                         ; 高于0xFA?
    004011B7    0F87 89000000   ja      00401246                         ; 提示Name must be between 5 and 51 length
    004011BD    83F8 1E         cmp     eax, 1E                          ; 低于0x1E?
    004011C0    0F82 80000000   jb      00401246                         ; 提示Name must be between 5 and 51 length
    004011C6    6A 00           push    0
    004011C8    6A 00           push    0
    004011CA    68 D2070000     push    7D2
    004011CF    FF75 08         push    dword ptr [ebp+8]
    004011D2    E8 81010000     call    <jmp.&user32.GetDlgItemInt>      ; 获取序列号
    004011D7    A3 5F624000     mov     dword ptr [40625F], eax          ; 并保存
    004011DC    68 2C624000     push    0040622C                         ; 用户名入栈
    004011E1    E8 C2000000     call    004012A8

    跟入4012A8:

    004012A8    55              push    ebp
    004012A9    8BEC            mov     ebp, esp
    004012AB    803D B4124000 C>cmp     byte ptr [4012B4], 0CC           ; 检测断点,发现则跳转出错
    004012B2    74 53           je      short 00401307
    004012B4    33C0            xor     eax, eax
    004012B6    33C9            xor     ecx, ecx
    004012B8    803D D4124000 C>cmp     byte ptr [4012D4], 0CC           ; 检测断点,发现则跳转出错
    004012BF    74 46           je      short 00401307
    004012C1    33D2            xor     edx, edx
    004012C3    8B55 08         mov     edx, dword ptr [ebp+8]           ; 取用户名字串
    004012C6    8A02            mov     al, byte ptr [edx]               ; 迭代字串
    004012C8    84C0            test    al, al                           ; 迭代完毕?
    004012CA    74 08           je      short 004012D4                   ; 则跳转
    004012CC    03C8            add     ecx, eax                         ; 累加
    004012CE    C1C1 08         rol     ecx, 8                           ; 循环左移8位
    004012D1    42              inc     edx                              ; 循环变量自增
    004012D2  ^ EB F2           jmp     short 004012C6
    004012D4    83F1 02         xor     ecx, 2                           ; 结果与2异或
    004012D7    83E9 50         sub     ecx, 50
    004012DA    81F1 37130000   xor     ecx, 1337                        ; 结果与1337异或
    004012E0    56              push    esi
    004012E1    66:8B35 7961400>mov     si, word ptr [406179]            ; 获取当前系统年份
    004012E8    66:03CE         add     cx, si                           ; 加入cx
    004012EB    803D FA124000 C>cmp     byte ptr [4012FA], 0CC           ; 检测断点,发现则跳转出错
    004012F2    74 13           je      short 00401307
    004012F4    5E              pop     esi
    004012F5    A1 5F624000     mov     eax, dword ptr [40625F]          ; 获取序列号
    004012FA    3BC1            cmp     eax, ecx                         ; 与F(用户名)的结果是否一致?
    004012FC    75 04           jnz     short 00401302                   ; 否则置406263为1
    004012FE    33C0            xor     eax, eax
    00401300    EB 0E           jmp     short 00401310
    00401302    33C0            xor     eax, eax
    00401304    40              inc     eax
    00401305    EB 10           jmp     short 00401317
    00401307    C605 63624000 0>mov     byte ptr [406263], 1
    0040130E    EB 07           jmp     short 00401317
    00401310    C605 63624000 0>mov     byte ptr [406263], 0
    00401317    C9              leave
    00401318    C2 0400         retn    4

    该子程序布下了简单的反调试,众所周知,断点的原理是将指定地址的指令替换为int 3中断,int 3的十六进制表示为0CC。通过实时检测载入内存的指令是否是0CC,就可以判断自身有没有被调试。在不改动程序文件的条件下,我们只能尽量少下断点,避免触发反调试。

    回到剩下的关键算法:

    004011E6    803D 63624000 0>cmp     byte ptr [406263], 1
    004011ED    74 43           je      short 00401232
    004011EF    85C0            test    eax, eax
    004011F1    75 17           jnz     short 0040120A
    004011F3    68 06614000     push    00406106                         ; ASCII "Congratulations! You are now level 2! :-)"
    004011F8    68 D2070000     push    7D2
    004011FD    FF75 08         push    dword ptr [ebp+8]
    00401200    E8 77010000     call    <jmp.&user32.SetDlgItemTextA>
    00401205    E9 98000000     jmp     004012A2
    0040120A    803D 1C124000 C>cmp     byte ptr [40121C], 0CC
    00401211    74 1F           je      short 00401232
    00401213    803D 23124000 C>cmp     byte ptr [401223], 0CC
    0040121A    74 16           je      short 00401232
    0040121C    6A 00           push    0
    0040121E    68 D9604000     push    004060D9                         ; ASCII "Sorry, that is incorrect my mate. Try again."
    00401223    68 D2070000     push    7D2
    00401228    FF75 08         push    dword ptr [ebp+8]
    0040122B    E8 4C010000     call    <jmp.&user32.SetDlgItemTextA>
    00401230    EB 70           jmp     short 004012A2
    00401232    68 55614000     push    00406155                         ; ASCII "Debugger detected, check cancelled!"
    00401237    68 D2070000     push    7D2
    0040123C    FF75 08         push    dword ptr [ebp+8]
    0040123F    E8 38010000     call    <jmp.&user32.SetDlgItemTextA>
    00401244    EB 5C           jmp     short 004012A2
    00401246    68 30614000     push    00406130                         ; ASCII "Name must be between 5 and 51 length"
    0040124B    68 D2070000     push    7D2
    00401250    FF75 08         push    dword ptr [ebp+8]
    00401253    E8 24010000     call    <jmp.&user32.SetDlgItemTextA>

    至此,程序算法流程已经分析完毕。马上动手编写注册机。

    我们直接打开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 <= 50 && len >= 6 ){                            //格式控制。
            unsigned int sum = 0;
    
            for ( int i = 0 ; i != len ; i++ ){
                sum += str[i];
                sum = ( sum >> (32 - 8) ) | ( sum << 8 );        //循环左移8位
            }
    
            sum = ( (sum ^ 2) - 0x50 ) ^ 0x1337;
    
            SYSTEMTIME st;
            CString strDate,strTime;
            GetLocalTime(&st);                                    //获取系统时间
            int year = st.wYear;
    
            __asm{                                                //为了防止进位内嵌汇编进行低位相加。
                push eax;
                push ebx;
    
                mov eax,year;
                mov ebx,sum;
    
                add bx,ax;
                mov sum,ebx;
    
                pop ebx;
                pop eax;
            }
    
            CString PassWord;
            PassWord.Format( "%u",sum );
            SetDlgItemText( IDC_EDIT_PASSWORD,PassWord );
        }
        else
            MessageBox( "用户名格式错误!" );

    再在OnInitDialog中添加此代码修改标题:SetWindowText(_T("TDC‘s keygenme_Keygen"));

    运行效果:

      至此,TDC‘s keygenme的破解就完成了。

     

    PS:情人节到了,愿天下情侣可以天长地久,也希望和我一样的单身汪不要气馁,多多努力,争取早日脱单。

    祝诸君,胸中沟壑自成!

    我们一路奋战,不是为了改变世界,而是不让世界改变我们 ——《熔炉》
  • 相关阅读:
    POJ1239
    HDU 2829 四边形不等式优化
    返回数字二进制的最高位位数o(n)
    矩阵快速幂 模板
    HDU4718 The LCIS on the Tree(LCT)
    HDU4010 Query on The Trees(LCT)
    HDU3487 Play With Chains(Splay)
    CF444C DZY Loves Colors
    HDU4836 The Query on the Tree(树状数组&&LCA)
    HDU4831&&4832&&4834
  • 原文地址:https://www.cnblogs.com/ZRBYYXDM/p/5188913.html
Copyright © 2011-2022 走看看