• 破解 crackme(完全拆解警告窗口)


    系统 : Windows xp

    程序 : crackme

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

    要求 : 注册机编写 & 去除Nag窗口

    使用工具 : IDA Pro & OD

    可在“PEDIY CrackMe 2007”中查找关于此程序的讨论,标题为“Pj一个超简单的Crackme,适合像我一样的新手~_~”。

     

     

    运行该程序,发现一开始就给了我们一个大大的警告窗口:

      这不找抽吗?载入IDA查看字串表,发现字串“Patch Me if you can”,双击定位到定义位置,再双击交叉引用来到调用处。发现程序调用MessageBox来显示窗口,OD载入程序,将call MessageBox函数这一行改为add esp 10:

    运行程序:

    咦?我保存了的啊,什么还是显示了Nag窗口?而且我的断点也失效了?

    初步判断,该程序开始运行时,进行清除断点的工作。并且,对载入内存的MessageBox部分代码进行了还原。

    想要去除Nag窗口,可能就需要书后面的反跟踪以及打补丁的技巧。这里,我们暂时忍一下,先放过它。

     

    重新返回字串表,发现该程序没有错误提示。。。找到成功注册的提示,向上翻找定位关键代码:

    00401219   .  C705 643C4000>mov     dword ptr [403C64], <jmp.&user32.GetWindowTextA>   ;  将函数地址存入 403C64
    00401223   .  832D 643C4000>sub     dword ptr [403C64], 9
    0040122A   >  68 FF010000   push    1FF                                                ;  循环两次读入用户名和密码。
    0040122F   .  83FB 01       cmp     ebx, 1                                             ;  用户名存入403054,序列号403654
    00401232   .  75 0D         jnz     short 00401241
    00401234   .  68 54304000   push    00403054                                           ;  ASCII "pediy"
    00401239   .  FF35 4C304000 push    dword ptr [40304C]
    0040123F   .  EB 0B         jmp     short 0040124C
    00401241   >  68 54364000   push    00403654                                           ;  ASCII "1111111111"
    00401246   .  FF35 50304000 push    dword ptr [403050]
    0040124C   >  83FB 01       cmp     ebx, 1
    0040124F   .  74 07         je      short 00401258
    00401251   .  8305 643C4000>add     dword ptr [403C64], 9
    00401258   >  FF15 643C4000 call    dword ptr [403C64]                                 ;  <jmp.&user32.GetWindowTextA>
    0040125E   .  43            inc     ebx
    0040125F   .  83FB 02       cmp     ebx, 2
    00401262   .^ 7C C6         jl      short 0040122A
    00401264   .  C705 703C4000>mov     dword ptr [403C70], <jmp.&kernel32.lstrlenA>
    0040126E   .  FF0D 703C4000 dec     dword ptr [403C70]                                 ;  crackme.004016DC
    00401274   .  C705 683C4000>mov     dword ptr [403C68], <jmp.&user32.GetWindowTextLeng>
    0040127E   .  FF05 683C4000 inc     dword ptr [403C68]                                 ;  crackme.00401670
    00401284   .  FF35 4C304000 push    dword ptr [40304C]
    0040128A   .  FF0D 683C4000 dec     dword ptr [403C68]                                 ;  crackme.00401670
    00401290   .  FF15 683C4000 call    dword ptr [403C68]                                 ;  <jmp.&user32.GetWindowTextLengthA>
    00401296   .  83F8 05       cmp     eax, 5                                             ;  用户名是否小于5?
    00401299   .  0F8C 43010000 jl      004013E2                                           ;  是则跳转至出错代码
    0040129F   .  83F8 08       cmp     eax, 8                                             ;  用户名是否大于等于8?
    004012A2   .  0F8D 3A010000 jge     004013E2                                           ;  是则跳转至出错代码
    004012A8   .  8BC8          mov     ecx, eax
    004012AA   .  890D 543C4000 mov     dword ptr [403C54], ecx                            ;  长度存入内存
    004012B0   .  33DB          xor     ebx, ebx
    004012B2   .  8D35 54304000 lea     esi, dword ptr [403054]                            ;  取用户名
    004012B8   .  8D3D 54324000 lea     edi, dword ptr [403254]                            ;  开辟内存储存密钥
    004012BE   >  8A4431 FF     mov     al, byte ptr [ecx+esi-1]                           ;  倒序迭代字串
    004012C2   .  80F9 04       cmp     cl, 4                                              ;  长度为4?
    004012C5   .  74 13         je      short 004012DA                                     ;  是则存储‘-’字符并结束本次循环
    004012C7   .  3C 4D         cmp     al, 4D                                             ;  是否小于0x4D?
    004012C9   .  7C 04         jl      short 004012CF
    004012CB   .  2C 11         sub     al, 11                                             ;  大于0x4D则减去0x11
    004012CD   .  EB 02         jmp     short 004012D1
    004012CF   >  04 15         add     al, 15
    004012D1   >  32C1          xor     al, cl                                             ;  处理过的字符值 ^ 长度
    004012D3   .  34 02         xor     al, 2                                              ;  处理过的字符值^2
    004012D5   .  88043B        mov     byte ptr [ebx+edi], al                             ;  保存密钥
    004012D8   .  EB 04         jmp     short 004012DE
    004012DA   >  C6043B 2D     mov     byte ptr [ebx+edi], 2D
    004012DE   >  43            inc     ebx                                                ;  指向下一个存储单元
    004012DF   .^ E0 DD         loopdne short 004012BE                                     ;  类似loop,另外测试ZF标志位是否为1则退出循环
    004012E1   .  8D35 54324000 lea     esi, dword ptr [403254]                            ;  取序列号
    004012E7   .  8D3D 54344000 lea     edi, dword ptr [403454]                            ;  开辟内存储存新密钥
    004012ED   .  8B0D 543C4000 mov     ecx, dword ptr [403C54]                            ;  长度存入ecx
    004012F3   .  33DB          xor     ebx, ebx
    004012F5   .  C705 5C3C4000>mov     dword ptr [403C5C], <jmp.&kernel32.lstrcatA>
    004012FF   .  832D 5C3C4000>sub     dword ptr [403C5C], 2
    00401306   .  C705 783C4000>mov     dword ptr [403C78], 0040207F                       ;  ASCII ":-)"
    00401310   .  C705 7C3C4000>mov     dword ptr [403C7C], 00402070                       ;  ASCII "Correct Serial"
    0040131A   .  832D 783C4000>sub     dword ptr [403C78], 5
    00401321   .  832D 7C3C4000>sub     dword ptr [403C7C], 5
    00401328   >  80F9 04       cmp     cl, 4                                              ;  长度为4?
    0040132B   .  74 17         je      short 00401344                                     ;  是则存储‘-’字符并结束本次循环
    0040132D   .  8A4431 FF     mov     al, byte ptr [ecx+esi-1]                           ;  倒序迭代密钥
    00401331   .  3C 4D         cmp     al, 4D                                             ;  是否小于0x4D?
    00401333   .  7C 04         jl      short 00401339                                     ;  小于0x4D则加上0x15
    00401335   .  2C 11         sub     al, 11                                             ;  大于0x4D则减去0x11
    00401337   .  EB 02         jmp     short 0040133B
    00401339   >  04 15         add     al, 15
    0040133B   >  32C1          xor     al, cl                                             ;  处理过的字符值 ^ 长度
    0040133D   .  34 02         xor     al, 2                                              ;  处理过的字符值^2
    0040133F   .  88043B        mov     byte ptr [ebx+edi], al                             ;  保存密钥
    00401342   .  EB 04         jmp     short 00401348
    00401344   >  C6043B 2D     mov     byte ptr [ebx+edi], 2D
    00401348   >  43            inc     ebx                                                ;  指向下一个存储单元
    00401349   .^ E0 DD         loopdne short 00401328                                     ;  类似loop,另外测试ZF标志位是否为1则退出循环
    0040134B   .  C705 603C4000>mov     dword ptr [403C60], <jmp.&user32.MessageBoxA>      ;  Entry address
    00401355   .  832D 603C4000>sub     dword ptr [403C60], 7
    0040135C   .  68 54324000   push    00403254                                           ;  ASCII "o-RT"
    00401361   .  68 54344000   push    00403454                                           ;  ASCII "L-@B]o-RT"
    00401366   .  8305 5C3C4000>add     dword ptr [403C5C], 2                              ;  ↓将旧密钥连接到新密钥后面
    0040136D   .  FF15 5C3C4000 call    dword ptr [403C5C]                                 ;  <jmp.&kernel32.lstrcatA>
    00401373   .  68 54344000   push    00403454                                           ;  ASCII "L-@B]o-RT"
    00401378   .  FF05 703C4000 inc     dword ptr [403C70]                                 ;  crackme.004016DC
    0040137E   .  FF15 703C4000 call    dword ptr [403C70]                                 ;  <jmp.&kernel32.lstrlenA>
    00401384   .  A3 743C4000   mov     dword ptr [403C74], eax                            ;  ↑算出密钥长度↓算出输入的序列号长度
    00401389   .  68 54364000   push    00403654                                           ;  ASCII "1111111111"
    0040138E   .  FF15 703C4000 call    dword ptr [403C70]                                 ;  <jmp.&kernel32.lstrlenA>
    00401394   .  3B05 743C4000 cmp     eax, dword ptr [403C74]                            ;  长度一样?
    0040139A   .  75 46         jnz     short 004013E2
    0040139C   .  FF35 543C4000 push    dword ptr [403C54]                                 ;  长度入栈
    004013A2   .  68 54344000   push    00403454                                           ;  ASCII "L-@B]o-RT"
    004013A7   .  68 54364000   push    00403654                                           ;  ASCII "1111111111"
    004013AC   .  E8 9F000000   call    00401450                                           ;  字串是否相等?
    004013B1   .  83F8 01       cmp     eax, 1                                             ;  不相等则跳转出错
    004013B4   .  74 2C         je      short 004013E2                                     ;  相等则提示成功

    这是一个F(用户名)=序列号的例子,我们将F(用户名)用高级语言写出来即可完成注册机。

    打开http://www.cnblogs.com/ZRBYYXDM/p/5115596.html中搭建的框架,为CKengen_TemplateDlg添加成员函数CreateSerial:

    CString CKengen_TemplateDlg::CreateSerial(CString str)
    {
        CString res = "";
        int len = str.GetLength();
    
        int i = len - 1,count = len;
        for ( ; i != -1 ; i--,count-- ){
            if ( count == 4 ){
                res += '-';
                continue;
            }
    
            if ( str[i] < 0x4D )                                    //小于0x4D
                str.SetAt( i,str[i] + 0x15 );
            else
                str.SetAt( i,str[i] - 0x11 );
    
            str.SetAt( i,str[i] ^ ( i+1 ) ^ 2 );
            res += str[i];
        }
    
        return res;
    }

    并修改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 >= 5 && len < 8 ){                                        //格式控制。
            CString Temp = CreateSerial( str );                            //第一个密钥
            CString Serial = CreateSerial( Temp );                        //第二个密钥
    
            Serial += Temp;
    
            SetDlgItemText( IDC_EDIT_PASSWORD,Serial );
        }
        else
            MessageBox( "用户名格式错误!" );
    }

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

    运行效果:

     

    -------------------------------------------------------------------------------------------------------------------

    后记:看了pediy crackme 2007中的原破文受了启发,既然反跟踪和恢复MessageBox函数都是作者之前实现的,那找到它nop掉不就行了?这里,一步步跟踪开头的代码:发现调用401477子程序之后,MessageBox被恢复了:

    00401000 >/$  6A 00         push    0                                ; /pModule = NULL
    00401002  |.  E8 B7060000   call    <jmp.&kernel32.GetModuleHandleA> ; GetModuleHandleA
    00401007  |.  A3 40304000   mov     dword ptr [403040], eax
    0040100C  |.  E8 A1060000   call    <jmp.&kernel32.GetCommandLineA>  ; [GetCommandLineA
    00401011  |.  A3 44304000   mov     dword ptr [403044], eax
    00401016  |.  6A 0A         push    0A
    00401018  |.  FF35 44304000 push    dword ptr [403044]
    0040101E  |.  6A 00         push    0
    00401020  |.  FF35 40304000 push    dword ptr [403040]               ;  crackme.00400000
    00401026  |.  E8 06000000   call    00401031
    0040102B  |.  50            push    eax                              ; /ExitCode
    0040102C  .  E8 7B060000   call    <jmp.&kernel32.ExitProcess>      ; ExitProcess
    00401031  /$  55            push    ebp
    00401032  |.  8BEC          mov     ebp, esp
    00401034  |.  83C4 B0       add     esp, -50
    00401037      E8 3B040000   call    00401477

    我们nop掉它就OK了^▼^

    但运行保存后的程序,单击check之后会出错,这又是为什么?帖子评论里就有牛人来讲解了:

    程序一开始调用4次WriteProcessMemory恢复40103C处的机器码
    并在按下check后调用ReadProcessMemory检查自身一大段机器码

    动态SMC 值得学习  

    原来的确运用了补丁技术,而且断点失效并非我一开始推测的断点全部清除。我们都知道断点其实就是int 3指令,WriteProcessMemory恢复了MessageBox的机器码,自然就覆盖了断点的int 3指令,所以MessageBox处的断点失效而注册算法的断点没有失效。

    现在,我们重新调整一下思路。如果光是nop掉对于重写自身代码的子程序没有用,那么干脆连检测自身代码的子程序也patch掉好了。

    跟入401477:

    00401477  /$  C705 803C4000>mov     dword ptr [403C80], <jmp.&kernel32.GetCurrentProcessId>
    00401481  |.  832D 803C4000>sub     dword ptr [403C80], 4
    00401488  |.  8305 843C4000>add     dword ptr [403C84], 4
    0040148F  |.  8105 843C4000>add     dword ptr [403C84], <jmp.&kernel32.OpenProcess>
    00401499  |.  8305 803C4000>add     dword ptr [403C80], 4
    004014A0  |.  C705 883C4000>mov     dword ptr [403C88], <jmp.&kernel32.WriteProcessMemory>
    004014AA  |.  FF05 883C4000 inc     dword ptr [403C88]
    004014B0  |.  FF15 803C4000 call    dword ptr [403C80]                                        ;  <jmp.&kernel32.GetCurrentProcessId>
    004014B6  |.  832D 843C4000>sub     dword ptr [403C84], 4
    004014BD  |.  50            push    eax
    004014BE  |.  6A 00         push    0
    004014C0  |.  68 FF0F1F00   push    1F0FFF
    004014C5  |.  FF15 843C4000 call    dword ptr [403C84]
    004014CB  |.  A3 9C3C4000   mov     dword ptr [403C9C], eax
    004014D0  |.  FF0D 883C4000 dec     dword ptr [403C88]
    004014D6  |.  FF35 A03C4000 push    dword ptr [403CA0]
    004014DC  |.  6A 02         push    2
    004014DE  |.  68 83204000   push    00402083
    004014E3  |.  68 3C104000   push    0040103C
    004014E8  |.  FF35 9C3C4000 push    dword ptr [403C9C]
    004014EE  |.  FF15 883C4000 call    dword ptr [403C88]
    004014F4  |.  C705 A03C4000>mov     dword ptr [403CA0], 0                                     ;  ↑重写MessageBox的Style参数部分
    004014FE  |.  FF35 A03C4000 push    dword ptr [403CA0]
    00401504  |.  6A 06         push    6
    00401506  |.  68 86204000   push    00402086                                                  ;  ASCII "h50@"
    0040150B  |.  68 3E104000   push    0040103E
    00401510  |.  FF35 9C3C4000 push    dword ptr [403C9C]
    00401516  |.  FF15 883C4000 call    dword ptr [403C88]
    0040151C  |.  C705 A03C4000>mov     dword ptr [403CA0], 0                                     ;  ↑重写MessageBox的Title参数部分
    00401526  |.  FF35 A03C4000 push    dword ptr [403CA0]
    0040152C  |.  6A 06         push    6
    0040152E  |.  68 8D204000   push    0040208D                                                  ;  ASCII "h!0@"
    00401533  |.  68 43104000   push    00401043
    00401538  |.  FF35 9C3C4000 push    dword ptr [403C9C]
    0040153E  |.  FF15 883C4000 call    dword ptr [403C88]
    00401544  |.  C705 A03C4000>mov     dword ptr [403CA0], 0                                     ;  ↑重写MessageBox的Text参数部分
    0040154E  |.  FF35 A03C4000 push    dword ptr [403CA0]
    00401554  |.  6A 02         push    2
    00401556  |.  68 94204000   push    00402094
    0040155B  |.  68 48104000   push    00401048
    00401560  |.  FF35 9C3C4000 push    dword ptr [403C9C]
    00401566  |.  FF15 883C4000 call    dword ptr [403C88]
    0040156C  |.  C705 A03C4000>mov     dword ptr [403CA0], 0                                     ;  ↑重写MessageBox的hOwner参数部分
    00401576  |.  FF35 A03C4000 push    dword ptr [403CA0]
    0040157C  |.  6A 05         push    5
    0040157E  |.  68 97204000   push    00402097
    00401583  |.  68 4A104000   push    0040104A
    00401588  |.  C705 8C3C4000>mov     dword ptr [403C8C], <jmp.&kernel32.ReadProcessMemory>
    00401592  |.  FF35 9C3C4000 push    dword ptr [403C9C]
    00401598  |.  FF15 883C4000 call    dword ptr [403C88]
    0040159E  |.  832D 8C3C4000>sub     dword ptr [403C8C], 2                                     ;  ↑重写MessageBox的Call部分

    这里子程序的作用只有重写,那么我们可以按照之前的方法直接nop掉他的Call。(Flag

    然后,对于ReadProcessMemory下断点,单击check,然后返回用户领空:

    004015B3  /$  C705 943C4000>mov     dword ptr [403C94], 43
    004015BD  |.  BB 00104000   mov     ebx, <ModuleEntryPoint>
    004015C2  |.  FF15 803C4000 call    dword ptr [403C80]                                        ;  <jmp.&kernel32.GetCurrentProcessId>
    004015C8  |.  50            push    eax
    004015C9  |.  6A 00         push    0
    004015CB  |.  68 FF0F1F00   push    1F0FFF
    004015D0  |.  FF15 843C4000 call    dword ptr [403C84]
    004015D6  |.  A3 9C3C4000   mov     dword ptr [403C9C], eax
    004015DB  |>  6A 00         /push    0
    004015DD  |.  6A 01         |push    1
    004015DF  |.  68 983C4000   |push    00403C98
    004015E4  |.  53            |push    ebx                                                      ;  程序入口点
    004015E5  |.  FF35 9C3C4000 |push    dword ptr [403C9C]
    004015EB  |.  FF15 8C3C4000 |call    dword ptr [403C8C]                                       ;  ReadProcessMemory
    004015F1  |.  43            |inc     ebx                                                      ;  循环读取程序在内存中的数据
    004015F2  |.  C705 A43C4000>|mov     dword ptr [403CA4], 004016BC
    004015FC  |.  8B15 983C4000 |mov     edx, dword ptr [403C98]
    00401602  |.  81FB 01104000 |cmp     ebx, 00401001                                            ;  第一次循环?
    00401608  |.  75 06         |jnz     short 00401610                                           ;  不是则跳转
    0040160A  |.  8915 943C4000 |mov     dword ptr [403C94], edx                                  ;  是则赋值给存放结果的内存
    00401610  |>  81FB DD200600 |cmp     ebx, 620DD
    00401616  |.  74 10         |je      short 00401628
    00401618  |.  83C2 04       |add     edx, 4                                                   ;  异或变量+4
    0040161B  |.  8335 943C4000>|xor     dword ptr [403C94], 19                                   ;  进行异或
    00401622  |.  3115 943C4000 |xor     dword ptr [403C94], edx
    00401628  |>  81FB 2A164000 |cmp     ebx, 0040162A                                            ;  检测40100-40162A处的程序
    0040162E  |.^ 75 AB         jnz     short 004015DB                                           ;  检测完则循环结束。
    00401630  |.  813D 943C4000>cmp     dword ptr [403C94], 0C0                                   ;  校验异或值是否正确?
    0040163A  |.  74 07         je      short 00401643                                            ;  不正确的话将直接退出进程
    0040163C  |.  6A 00         push    0                                                         ; /ExitCode = 0
    0040163E  |.  E8 69000000   call    <jmp.&kernel32.ExitProcess>                               ; ExitProcess
    00401643  |>  FF35 9C3C4000 push    dword ptr [403C9C]                                        ; /hObject = NULL
    00401649  |.  E8 58000000   call    <jmp.&kernel32.CloseHandle>                               ; CloseHandle
    0040164E  |.  33C0          xor     eax, eax
    00401650  .  C3            retn

    这里,读取对程序自身一大段代码,并取值与其异或,全部读取完毕之后,取其校验异或值与正确的校验值进行比较。如果不正确将直接退出进程,这将引发一个错误。

    以下流程图描述了程序的结构:

    接下来,我尝试了按照之前的方法把重写MB的程序call给nop掉,然后jump到MB之后的代码中,最后,修改检测patch子程序的跳转指令。做完之后,尝试启动,OD会提示你程序尝试访问0000内存,引发了异常,这实际上是应用程序崩溃了。

    经过一番研究之后,我推导出nop重写mb的子程序引发了该错误。肯定在该子程序中有其他代码而我一时忽略了,现在只能进入子程序,将修改MBstyle参数的WriteProcessMemory函数清除掉,在该子程序返回后,立刻将style参数部分改成跳转指令,最后,我们修改检测patch子程序的跳转指令。这样,Nag窗口就被我们完全地拆解了。

  • 相关阅读:
    『Python』进程同步
    『Python』多进程
    『GoLang』协程与通道
    『GoLang』错误处理
    『码农翻身』语言的学习
    『GoLang』fmt包的使用
    异或运算符(^)、与运算符(&)、或运算符(|)、反运算符(~)、右移运算符(>>)、无符号右移运算符(>>>)
    HTML DOM focus() 方法
    JavaScript中的indexOf使用方法
    HTML 5 中的textarea标签
  • 原文地址:https://www.cnblogs.com/ZRBYYXDM/p/5118105.html
Copyright © 2020-2023  润新知