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

    祝诸君,胸中沟壑自成!

    我们一路奋战,不是为了改变世界,而是不让世界改变我们 ——《熔炉》
  • 相关阅读:
    5-1 Leetcode中和链表相关的问题
    4-7 带有尾指针的链表:使用链表实现队列
    4.6 使用链表实现栈
    4.5 链表元素的删除
    4.4 链表的遍历、查询和修改
    4.3 为链表设置虚拟头结点dummyhead
    4.2在链表中添加元素
    4.1链表
    mybatis 力量操作参数为List的非空校验
    linux 运行和停止jar的shell 脚本
  • 原文地址:https://www.cnblogs.com/ZRBYYXDM/p/5188913.html
Copyright © 2011-2022 走看看