码迷,mamicode.com
首页 > 其他好文 > 详细

破解 “PEDIY CrackMe 2007” 之 k4n

时间:2015-12-19 16:27:32      阅读:288      评论:0      收藏:0      [点我收藏+]

标签:

系统 : Windows xp

程序 : k4n

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

要求 : 注册机编写

使用工具 : IDA Pro & OD

“PEDIY CrackMe 2007”中关于此程序的破文标题为“一个CRACKME的算法分析”,可在“搜索”标签下查找。

 

使用IDA载入程序,根据成功/失败的字符串提示找到关键代码,本程序中关键代码在401085处:

00401085  |.  6A 66         push    66                               ; /ControlID = 66 (102.)
00401087  |.  53            push    ebx                              ; |hWnd
00401088  |.  E8 159C0000   call    <jmp.&USER32.GetDlgItem>         ; \GetDlgItem
0040108D  |.  6A 64         push    64                               ; /Count = 64 (100.)
0040108F  |.  8D95 48FFFFFF lea     edx, dword ptr [ebp-B8]          ; |
00401095  |.  52            push    edx                              ; |Buffer
00401096  |.  50            push    eax                              ; |hWnd
00401097  |.  E8 129C0000   call    <jmp.&USER32.GetWindowTextA>     ; \GetWindowTextA
0040109C  |.  6A 68         push    68                               ; /ControlID = 68 (104.)
0040109E  |.  53            push    ebx                              ; |hWnd
0040109F  |.  E8 FE9B0000   call    <jmp.&USER32.GetDlgItem>         ; \GetDlgItem
004010A4  |.  6A 64         push    64                               ; /Count = 64 (100.)
004010A6  |.  8D8D E4FEFFFF lea     ecx, dword ptr [ebp-11C]         ; |
004010AC  |.  51            push    ecx                              ; |Buffer
004010AD  |.  50            push    eax                              ; |hWnd
004010AE  |.  E8 FB9B0000   call    <jmp.&USER32.GetWindowTextA>     ; \GetWindowTextA
004010B3  |.  6A 67         push    67                               ; /ControlID = 67 (103.)
004010B5  |.  53            push    ebx                              ; |hWnd
004010B6  |.  E8 E79B0000   call    <jmp.&USER32.GetDlgItem>         ; \GetDlgItem
004010BB  |.  8BF0          mov     esi, eax
004010BD  |.  8D85 48FFFFFF lea     eax, dword ptr [ebp-B8]
004010C3  |.  50            push    eax
004010C4  |.  E8 67050000   call    00401630
004010C9  |.  59            pop     ecx
004010CA  |.  8945 D8       mov     dword ptr [ebp-28], eax
004010CD  |.  8D95 E4FEFFFF lea     edx, dword ptr [ebp-11C]
004010D3  |.  52            push    edx
004010D4  |.  E8 57050000   call    00401630
004010D9  |.  59            pop     ecx
004010DA  |.  68 EAB04000   push    0040B0EA
004010DF  |.  E8 4C050000   call    00401630
004010E4  |.  59            pop     ecx
004010E5  |.  68 0EB14000   push    0040B10E
004010EA  |.  E8 41050000   call    00401630
004010EF  |.  59            pop     ecx
004010F0  |.  837D D8 03    cmp     dword ptr [ebp-28], 3            ;  字符串是否小于等于3
004010F4  |.  7E 7B         jle     short 00401171                   ;  是则出错
004010F6  |.  90            nop
004010F7  |.  90            nop
004010F8  |.  90            nop
004010F9  |.  90            nop
004010FA  |.  33C9          xor     ecx, ecx
004010FC  |.  33D2          xor     edx, edx
004010FE  |.  33DB          xor     ebx, ebx
00401100  |.  33C0          xor     eax, eax
00401102  |.  837D D8 32    cmp     dword ptr [ebp-28], 32           ;  字符串是否大于等于50?
00401106  |.  7D 69         jge     short 00401171                   ;  是则出错
00401108  |.  90            nop
00401109  |.  90            nop
0040110A  |.  90            nop
0040110B  |.  90            nop
0040110C  |>  0FBE840D 48FF>/movsx   eax, byte ptr [ebp+ecx-B8]      ;  逐个取出字符串中的字符
00401114  |.  41            |inc     ecx                             ;  循环变量自增
00401115  |.  33C1          |xor     eax, ecx                        ;  字符与循环变量异或
00401117  |.  03D8          |add     ebx, eax                        ;  累加异或后的字符数据
00401119  |.  3B4D D8       |cmp     ecx, dword ptr [ebp-28]         ;  字符串是否处理完?
0040111C  |.^ 75 EE         \jnz     short 0040110C
0040111E  |.  6BC0 06       imul    eax, eax, 6                      ;  eax *= 6
00401121  |.  C1E3 07       shl     ebx, 7                           ;  左移7位,空位填0
00401124  |.  03C3          add     eax, ebx
00401126  |.  8945 C8       mov     dword ptr [ebp-38], eax
00401129  |.  FF75 C8       push    dword ptr [ebp-38]               ;  eax + ebx的值入栈
0040112C  |.  68 38B44000   push    0040B438                         ;  ASCII "%lX"
00401131  |.  8D8D 80FEFFFF lea     ecx, dword ptr [ebp-180]
00401137  |.  51            push    ecx                              ;  缓冲区
00401138  |.  E8 873D0000   call    00404EC4
0040113D  |.  83C4 0C       add     esp, 0C
00401140  |.  8D85 80FEFFFF lea     eax, dword ptr [ebp-180]
00401146  |.  50            push    eax                              ; /String2
00401147  |.  8D95 E4FEFFFF lea     edx, dword ptr [ebp-11C]         ; |
0040114D  |.  52            push    edx                              ; |String1
0040114E  |.  E8 339C0000   call    <jmp.&KERNEL32.lstrcmpA>         ; \lstrcmpA
00401153  |.  85C0          test    eax, eax
00401155  |.  75 0D         jnz     short 00401164
00401157  |.  68 3CB44000   push    0040B43C                         ; /Text = "Congratulations! IF this number comes *FROM YOUR* keygen, Write a tutorial dude ;)."
0040115C  |.  56            push    esi                              ; |hWnd
0040115D  |.  E8 289B0000   call    <jmp.&USER32.SetWindowTextA>     ; \SetWindowTextA
00401162  |.  EB 18         jmp     short 0040117C
00401164  |>  68 90B44000   push    0040B490                         ; /Text = "This serial is *NOT* Valid!! Try again... : UNREGISTERED"
00401169  |.  56            push    esi                              ; |hWnd
0040116A  |.  E8 1B9B0000   call    <jmp.&USER32.SetWindowTextA>     ; \SetWindowTextA
0040116F  |.  EB 0B         jmp     short 0040117C
00401171  |>  68 C9B44000   push    0040B4C9                         ; /Text = "Name must contain more than 4 chars and less than 50 chars !!"
00401176  |.  56            push    esi                              ; |hWnd
00401177  |.  E8 0E9B0000   call    <jmp.&USER32.SetWindowTextA>     ; \SetWindowTextA

这里分析得出结论,程序通过对用户名字串进行操作,然后将之与密钥对比,得出结果。关键算法都差不多了解,现在只需探索下地址为00404EC4的子程序进行了什么操作:

00404EC4  /$  55            push    ebp
00404EC5  |.  8BEC          mov     ebp, esp
00404EC7  |.  8B45 08       mov     eax, dword ptr [ebp+8]           ;  缓冲区地址放入eax
00404ECA  |.  8D4D 08       lea     ecx, dword ptr [ebp+8]           ;  ecx指向缓冲区地址
00404ECD  |.  C600 00       mov     byte ptr [eax], 0                ;  向缓冲区填0
00404ED0  |.  8D45 10       lea     eax, dword ptr [ebp+10]
00404ED3  |.  50            push    eax                              ;  经处理过的数值入栈
00404ED4  |.  8B55 0C       mov     edx, dword ptr [ebp+C]
00404ED7  |.  52            push    edx                              ;  ‘%IX’入栈
00404ED8  |.  51            push    ecx                              ;  缓冲区地址入栈
00404ED9  |.  68 9C4E4000   push    00404E9C
00404EDE  |.  E8 B5010000   call    00405098
00404EE3  |.  83C4 10       add     esp, 10
00404EE6  |.  5D            pop     ebp
00404EE7  \.  C3            retn

咦?这里的函数调用和上一步好像没什么明显的不同?继续深入地址为405098的子程序看看。。。。

接着就发现一大堆乱七八糟的汇编指令。。。这么简单的CM怎么会有如此复杂的处理流程?

不如转换下思维好了,回到一开始的流程再想想。我们可以看见,调用完405098之后,ecx中就保存了密钥:

技术分享

我们一边执行405098子程序,一边关注寄存器和堆栈的变化,慢慢搜索。

终于,在这一行发现了密钥的踪迹:

技术分享

 

看到了吗?ebp+10的位置里存放着密钥“116E8”,往上翻,一定在不远处存放着处理字串的关键代码:

00407484  |. /EB 17         jmp     short 0040749D
00407486  |> |4B            /dec     ebx
00407487  |. |8A03          |mov     al, byte ptr [ebx]
00407489  |. |3C 0A         |cmp     al, 0A
0040748B  |. |7D 08         |jge     short 00407495
0040748D  |. |83C0 30       |add     eax, 30
00407490  |. |8806          |mov     byte ptr [esi], al
00407492  |. |46            |inc     esi
00407493  |. |EB 08         |jmp     short 0040749D
00407495  |> |0245 1C       |add     al, byte ptr [ebp+1C]
00407498  |. |04 F6         |add     al, 0F6
0040749A  |. |8806          |mov     byte ptr [esi], al
0040749C  |. |46            |inc     esi
0040749D  |> \8D55 BC        lea     edx, dword ptr [ebp-44]
004074A0  |.  3BDA          |cmp     ebx, edx
004074A2  |.^ 75 E2         \jnz     short 00407486

这几行汇编指令将一组数据以十六进制的格式转换为相应的字符串,用代码表示就是wsprintf( str,"%lX",res );

正如程序中调用00404EC4子程序时的方式一样:

00401129  |.  FF75 C8       push    dword ptr [ebp-38]               ;  eax + ebx的值入栈
0040112C  |.  68 38B44000   push    0040B438                         ;  ASCII "%lX"
00401131  |.  8D8D 80FEFFFF lea     ecx, dword ptr [ebp-180]
00401137  |.  51            push    ecx                              ;  缓冲区
00401138  |.  E8 873D0000   call    00404EC4

这里发现以上代码地址都是004开头(处于应用程序领空),也就是说以上流程处理都是CM作者自己实现的,虽然根据参数可以下判断

这就是个转换格式的函数,但如果CM作者对于转换的过程“动手动脚”,该怎么判断呢?

比如:本次的运算结果是116E8,转换成字符串"116E8"是没错,但万一程序中发现0FH数据,把它直接转换成字符1怎么办呢?

如果CM作者这样编码,我们写出的注册机将没有“通用性”。所以,发现手动实现的子程序,还是进入确认下关键算法比较稳妥。

 

好了,算法到这里分析完毕,我们根据加密算法开始编写注册机吧!

复制一份http://www.cnblogs.com/ZRBYYXDM/p/5002789.html中搭建的MFC窗口程序,打开并修改OnOk函数如下:

 

void CSerialNumber_KeygenDlg::OnOK() 
{
    // TODO: Add extra validation here
    CString str;
    GetDlgItem( IDC_EDIT_NAME )->GetWindowText( str );        //获取用户名

    int len = str.GetLength();                                //获取长度

    if ( len <= 3 || len >= 50 )    
        MessageBox( "用户名必须长度大于3、小于50!" );
    else
    {
        int res = 0,sum = 0;                    //储存累加、运算结果.
        CString Temp = str;
        for ( int i = 0 ; i < len ; i++ ){
            sum += ( Temp[i] ^ (i + 1) );        //字符与循环变量异或,并累加。
            if ( (i + 1) == len )                //当前处理的是最后一个元素时,
                res = ( Temp[i] ^ (i + 1) );    //保存字符与循环变量异或的值。
        }

        res *= 6;
        sum = sum << 7;
        res += sum;
        
        char* str1 = new char[50];
        wsprintf( str1,"%lX",res );        //以十六进制存储.


        GetDlgItem( IDC_EDIT_Number )->SetWindowText( str1 );
    }

    //CDialog::OnOK();                //屏蔽基类OnOk函数
}

 

 

 

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

运行程序,并将解密得到的序列号黏贴至k4n程序中,单击check。。。

效果拔群:

技术分享

破解 “PEDIY CrackMe 2007” 之 k4n

标签:

原文地址:http://www.cnblogs.com/ZRBYYXDM/p/5059159.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!