系统 : Windows xp

程序 : crackme3

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

要求 : 注册机编写

使用工具 : IDA Pro & OD

“PEDIY CrackMe 2007”中关于此程序的破文标题为“一个简单的非明码比较(适合新手)【原创】”,可以在“搜索”标签下查找出原文。

 

用IDA载入程序,打开字符串表,发现“注册成功”/“注册失败”的提示:

双击'Well done,Cracker'进入定义,然后双击交叉引用进入调用它的地方:

 loc_4015D9:                             ; CODE XREF: .text:004015C1j
.text:004015D9                 push    0
.text:004015DB                 push    offset aYouDidIt ; "YOU DID IT"
.text:004015E0                 push    offset aWellDoneCracke ; "Well done,Cracker"
.text:004015E5                 mov     ecx, [ebp-20h]
.text:004015E8                 call    ?MessageBoxA@CWnd@@QAEHPBD0I@Z ; CWnd::MessageBoxA(char const *,char const *,uint)

向上查找关键代码。。。

啊哈!这段代码中调用了两次GetWindowTextA函数,必定是用来获取用户名和密码:

.text:00401521 loc_401521:                             ; CODE XREF: .text:0040151Aj
.text:00401521                 mov     eax, [ebp-20h]
.text:00401524                 add     eax, 0E0h
.text:00401529                 push    eax
.text:0040152A                 mov     ecx, [ebp-20h]
.text:0040152D                 add     ecx, 0A0h
.text:00401533                 call    ?GetWindowTextA@CWnd@@QBEXAAVCString@@@Z ; CWnd::GetWindowTextA(CString &)
.text:00401538                 mov     ecx, [ebp-20h]
.text:0040153B                 add     ecx, 0E4h
.text:00401541                 push    ecx
.text:00401542                 mov     ecx, [ebp-20h]
.text:00401545                 add     ecx, 60h
.text:00401548                 call    ?GetWindowTextA@CWnd@@QBEXAAVCString@@@Z ; CWnd::GetWindowTextA(CString &)

打开OD载入程序,在00401548处下断点,接着F9运行程序并尝试输入一组用户名密码:

哎?为什么没有中断在00401548处就提示错误?

再分析下程序:

004014F5  |.  E8 AA030000   call    <jmp.&MFC42.#3876_CWnd::GetWindowTextLengthA>
004014FA  |.  8945 EC       mov     dword ptr [ebp-14], eax
004014FD  |.  837D EC 05    cmp     dword ptr [ebp-14], 5
00401501  |.  7F 05         jg      short 00401508
00401503  |.  E9 BB000000   jmp     004015C3
00401508  |>  8B4D E0       mov     ecx, dword ptr [ebp-20]
0040150B  |.  83C1 60       add     ecx, 60
0040150E  |.  E8 91030000   call    <jmp.&MFC42.#3876_CWnd::GetWindowTextLengthA>
00401513  |.  8945 E8       mov     dword ptr [ebp-18], eax
00401516  |.  837D E8 05    cmp     dword ptr [ebp-18], 5
0040151A  |.  7F 05         jg      short 00401521
0040151C  |.  E9 A2000000   jmp     004015C3

在获取字符串之前,程序调用了GetWindowTextLengthA来获取长度,并进行判断。如果用户名和序列号有一个是长度不 大于5的,就会显示出错!

那我们输入两个大于5的字符就可以通过检查了,在窗口中输入“pediy1”,“123456”,程序中断在00401548处:

00401548  |.  E8 51030000   call    <jmp.&MFC42.#3874_CWnd::GetWindowTextA>
0040154D  |.  8B55 E0       mov     edx, dword ptr [ebp-20]
00401550  |.  81C2 E0000000 add     edx, 0E0
00401556  |.  52            push    edx
00401557  |.  8D4D E4       lea     ecx, dword ptr [ebp-1C]
0040155A  |.  E8 39030000   call    <jmp.&MFC42.#858_CString::operator=>
0040155F  |.  8B45 E0       mov     eax, dword ptr [ebp-20]
00401562  |.  05 E4000000   add     eax, 0E4
00401567  |.  50            push    eax
00401568  |.  8D4D F0       lea     ecx, dword ptr [ebp-10]
0040156B  |.  E8 28030000   call    <jmp.&MFC42.#858_CString::operator=>
00401570  |.  33C0          xor     eax, eax
00401572  |.  33DB          xor     ebx, ebx
00401574  |.  33C9          xor     ecx, ecx
00401576  |.  B9 01000000   mov     ecx, 1                                                  ;  异或变量初值1
0040157B  |.  33D2          xor     edx, edx
0040157D  |.  8B45 E4       mov     eax, dword ptr [ebp-1C]                                 ;  用户名首地址存入eax
00401580  |>  8A18          /mov     bl, byte ptr [eax]
00401582  |.  32D9          |xor     bl, cl                                                 ;  字符与异或变量进行异或
00401584  |.  8818          |mov     byte ptr [eax], bl                                     ;  再存回去
00401586  |.  41            |inc     ecx                                                    ;  异或变量自增
00401587  |.  40            |inc     eax                                                    ;  下一个字符
00401588  |.  8038 00       |cmp     byte ptr [eax], 0
0040158B  |.^ 75 F3         \jnz     short 00401580                                         ;  处理完毕则退出循环
0040158D  |.  33C0          xor     eax, eax
0040158F  |.  33DB          xor     ebx, ebx
00401591  |.  33C9          xor     ecx, ecx
00401593  |.  B9 0A000000   mov     ecx, 0A                                                 ;  异或变量初值0Ah(10)
00401598  |.  33D2          xor     edx, edx
0040159A  |.  8B45 F0       mov     eax, dword ptr [ebp-10]                                 ;  序列号首地址存入eax
0040159D  |>  8A18          /mov     bl, byte ptr [eax]
0040159F  |.  32D9          |xor     bl, cl                                                 ;  字符与异或变量进行异或
004015A1  |.  8818          |mov     byte ptr [eax], bl                                     ;  再存回去
004015A3  |.  41            |inc     ecx                                                    ;  异或变量自增
004015A4  |.  40            |inc     eax                                                    ;  下一个字符
004015A5  |.  8038 00       |cmp     byte ptr [eax], 0
004015A8  |.^ 75 F3         \jnz     short 0040159D                                         ;  处理完毕则退出循环
004015AA  |.  8B45 E4       mov     eax, dword ptr [ebp-1C]
004015AD  |.  8B55 F0       mov     edx, dword ptr [ebp-10]
004015B0  |>  33C9          /xor     ecx, ecx
004015B2  |.  8A18          |mov     bl, byte ptr [eax]
004015B4  |.  8A0A          |mov     cl, byte ptr [edx]
004015B6  |.  3AD9          |cmp     bl, cl
004015B8  |.  75 09         |jnz     short 004015C3                                         ;  f(用户名) ≠ f(序列号)则退出
004015BA  |.  40            |inc     eax
004015BB  |.  42            |inc     edx
004015BC  |.  8038 00       |cmp     byte ptr [eax], 0
004015BF  |.^ 75 EF         \jnz     short 004015B0
004015C1  |.  EB 16         jmp     short 004015D9
004015C3  |>  6A 00         push    0
004015C5  |.  68 6C304000   push    0040306C                                                ;  ASCII "ERROR"
004015CA  |.  68 40304000   push    00403040                                                ;  ASCII "One of the Details you entered was wrong"
004015CF  |.  8B4D E0       mov     ecx, dword ptr [ebp-20]
004015D2  |.  E8 BB020000   call    <jmp.&MFC42.#4224_CWnd::MessageBoxA>

原来程序采用的是F(用户名)= F(序列号)的验证形式!写出F(序列号)的逆算法即可算出序列号!

这里分析下源码可知,异或加密的信息,想要解密只需要用同样的密钥再异或一次即可。也就是说,F(序列号)的逆算法就是它本身!

复制一份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 <= 5 )                                            //当字符串长度小于等于5时
        MessageBox( "用户名必须长度大于5!" );
    else
    {
        int XorVar = 1;                                        //异或变量
        CString Temp = "";
        for ( int i = 0 ; i < len ; i++ ){
            Temp += str[i] ^ XorVar;
            XorVar++;
        }

        XorVar = 0x0A;                                        //异或变量
        CString Serial = "";
        for ( int j = 0 ; j != len ; j++ ){
            Serial += Temp[j] ^ XorVar;
            XorVar++;
        }

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

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

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

运行程序,并将解密得到的序列号黏贴至crackme3程序中:

效果拔群: