系统 : Windows xp

程序 : k4n3

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

要求 : 注册机编写

使用工具 : IDA Pro & OD & Hex workshop

可在“PEDIY CrackMe 2007”中查找关于此程序的讨论,标题为“非明码比较的Crackme的破解”。

 

 

使用IDA载入程序,打开字符串表,根据“Wow you did it... Now write a valid keygen with NO ASM RIPPING :X”的字符串提示向上翻找到关键代码,首先来看看程序是怎样约束输入字串的:

004011BF  |.  6A 45         push    45                               ; /Count = 45 (69.)
004011C1  |.  50            push    eax                              ; |Buffer
004011C2  |.  A4            movs    byte ptr es:[edi], byte ptr [esi>; |
004011C3  |.  8B3D A8404000 mov     edi, dword ptr [<&USER32.GetDlgI>; |USER32.GetDlgItemTextA
004011C9  |.  68 E8030000   push    3E8                              ; |ControlID = 3E8 (1000.)
004011CE  |.  51            push    ecx                              ; |hWnd
004011CF  |.  FFD7          call    edi                              ; \GetDlgItemTextA
004011D1  |.  8BF0          mov     esi, eax
004011D3  |.  85F6          test    esi, esi                         ;  当用户名字串长度为0
004011D5  |.  0F84 4B010000 je      00401326                         ;  出错
004011DB  |.  83FE 40       cmp     esi, 40                          ;  当用户名长度大于64时
004011DE  |.  0F87 42010000 ja      00401326                         ;  出错
004011E4  |.  8B45 08       mov     eax, dword ptr [ebp+8]
004011E7  |.  8D55 94       lea     edx, dword ptr [ebp-6C]
004011EA  |.  6A 13         push    13                               ; /Count = 13 (19.)
004011EC  |.  52            push    edx                              ; |Buffer
004011ED  |.  68 E9030000   push    3E9                              ; |ControlID = 3E9 (1001.)
004011F2  |.  50            push    eax                              ; |hWnd
004011F3  |.  FFD7          call    edi                              ; \GetDlgItemTextA
004011F5  |.  6BC0 03       imul    eax, eax, 3
004011F8  |.  C1E0 02       shl     eax, 2
004011FB  |.  05 CD000000   add     eax, 0CD                         ;  密钥长度*3再左移2位,最后加上0CDH
00401200  |.  8945 FC       mov     dword ptr [ebp-4], eax
00401203  |.  817D FC A5010>cmp     dword ptr [ebp-4], 1A5           ;  如果结果不是1A5,则跳转
0040120A  |.  0F85 BC000000 jnz     004012CC                         ;  显示密钥有误
00401210  |.  33C0          xor     eax, eax
00401212  |.  8A45 94       mov     al, byte ptr [ebp-6C]            ;  取密钥首个字母
00401215  |.  84C0          test    al, al                           ;  判断字符值是否为0
00401217  |.  74 13         je      short 0040122C
00401219  |.  8D4D 94       lea     ecx, dword ptr [ebp-6C]          ;  取密钥字串地址
0040121C  |>  3C 30         /cmp     al, 30                          ;  如果字符小于30h
0040121E  |.  0F82 C6000000 |jb      004012EA                        ;  则跳转至提示出错函数
00401224  |.  8A41 01       |mov     al, byte ptr [ecx+1]            ;  下一个字符
00401227  |.  41            |inc     ecx                             ;  循环变量自增
00401228  |.  84C0          |test    al, al                          ;  当处理到字符串结尾
0040122A  |.^ 75 F0         \jnz     short 0040121C                  ;  则退出循环

我们看到,密钥长度必须是一个固定值,否则将提示出错。经过推算,可以知道密钥长度应该是18.

这里,我们随便填写一个长度为18的密钥,例如:“123456789ABCDEF123”。让程序流程继续执行:

0040122C  |> \E8 CFFDFFFF   call    00401000                         ;  该子程序清空eax,edx,ecx寄存器的值
00401231  |.  8D85 2CFFFFFF lea     eax, dword ptr [ebp-D4]          ;  取用户名字串地址
00401237  |.  50            push    eax
00401238  |.  E8 43FEFFFF   call    00401080                         ;  拼接用户名字串和‘ is a 。。。’字串,并根据字串算出一个值

这里,程序跳转进入401080子程序,我们F7进入:

00401080  /$  55            push    ebp
00401081  |.  8BEC          mov     ebp, esp
00401083  |.  51            push    ecx
00401084  |.  53            push    ebx
00401085  |.  56            push    esi
00401086  |.  57            push    edi
00401087  |.  68 80504000   push    00405080                         ;  ASCII "eheh"
0040108C  |.  6A 00         push    0
0040108E  |.  E8 ADFFFFFF   call    00401040                         ;  通过字串eheh对eax的值做一些修改

继续F7进入子程序401040:

00401040  /$  8B4C24 04     mov     ecx, dword ptr [esp+4]
00401044  |.  56            push    esi
00401045  |.  8B7424 0C     mov     esi, dword ptr [esp+C]
00401049  |.  33C0          xor     eax, eax
0040104B  |.  33D2          xor     edx, edx
0040104D  |.  8A4431 03     mov     al, byte ptr [ecx+esi+3]
00401051  |.  8A5431 02     mov     dl, byte ptr [ecx+esi+2]
00401055  |.  C1E0 08       shl     eax, 8                           ;  左移8位
00401058  |.  03C2          add     eax, edx
0040105A  |.  33D2          xor     edx, edx
0040105C  |.  8A5431 01     mov     dl, byte ptr [ecx+esi+1]
00401060  |.  C1E0 08       shl     eax, 8
00401063  |.  03C2          add     eax, edx
00401065  |.  33D2          xor     edx, edx
00401067  |.  8A1431        mov     dl, byte ptr [ecx+esi]
0040106A  |.  5E            pop     esi
0040106B  |.  C1E0 08       shl     eax, 8
0040106E  |.  03C2          add     eax, edx
00401070  \.  C3            retn

回到401080子程序领空中,发现整个子程序多次调用了401040子程序:

00401080  /$  55            push    ebp
00401081  |.  8BEC          mov     ebp, esp
00401083  |.  51            push    ecx
00401084  |.  53            push    ebx
00401085  |.  56            push    esi
00401086  |.  57            push    edi
00401087  |.  68 80504000   push    00405080                         ;  ASCII "eheh"
0040108C  |.  6A 00         push    0
0040108E  |.  E8 ADFFFFFF   call    00401040                         ;  通过字串eheh对eax的值做一些修改
00401093  |.  83C4 08       add     esp, 8
00401096  |.  8BD8          mov     ebx, eax
00401098  |.  E8 63FFFFFF   call    00401000                         ;  该子程序清空eax,edx,ecx寄存器的值
0040109D  |.  BF 70504000   mov     edi, 00405070                    ;  ASCII " is a whore."
004010A2  |.  83C9 FF       or      ecx, FFFFFFFF                    ;  ecx=FFFFFFFF
004010A5  |.  33C0          xor     eax, eax
004010A7  |.  F2:AE         repne   scas byte ptr es:[edi]           ;  扫描es:[edi]中的字串,循环次数ecx,找到与al相同的值则停止
004010A9  |.  F7D1          not     ecx                              ;  执行完这条语句ecx=‘is a 。。’字串长度+1
004010AB  |.  2BF9          sub     edi, ecx                         ;  重新指向字串首地址
004010AD  |.  8BF7          mov     esi, edi
004010AF  |.  8B7D 08       mov     edi, dword ptr [ebp+8]           ;  edi指向用户名字串
004010B2  |.  8BD1          mov     edx, ecx                         ;  edx=‘is a 。。’字串长度+1
004010B4  |.  83C9 FF       or      ecx, FFFFFFFF
004010B7  |.  F2:AE         repne   scas byte ptr es:[edi]           ;  扫描es:[edi]中的字串,循环次数ecx,找到与al相同的值则停止
004010B9  |.  8BCA          mov     ecx, edx                         ;  edx= ‘is a。。。’字串的长度+1
004010BB  |.  4F            dec     edi                              ;  此时es:[edi]指向字串结尾
004010BC  |.  C1E9 02       shr     ecx, 2                           ;  ecx右移两位
004010BF  |.  F3:A5         rep     movs dword ptr es:[edi], dword p>;  拼接字串
004010C1  |.  8BCA          mov     ecx, edx
004010C3  |.  83E1 03       and     ecx, 3                           ;  ecx=1
004010C6  |.  F3:A4         rep     movs byte ptr es:[edi], byte ptr>;  加上结尾
004010C8  |.  33FF          xor     edi, edi
004010CA  |.  33F6          xor     esi, esi
004010CC  |>  8B45 08       /mov     eax, dword ptr [ebp+8]          ;  将拼接好的字串地址放入eax
004010CF  |.  50            |push    eax
004010D0  |.  56            |push    esi
004010D1  |.  E8 6AFFFFFF   |call    00401040                        ;  通过入栈的字串对eax的值做一些修改
004010D6  |.  8B8E 30504000 |mov     ecx, dword ptr [esi+405030]
004010DC  |.  83C4 08       |add     esp, 8                          ;  平衡堆栈
004010DF  |.  33CF          |xor     ecx, edi                        ;  ecx与edi异或
004010E1  |.  03C1          |add     eax, ecx                        ;  eax再加上ecx
004010E3  |.  8945 FC       |mov     dword ptr [ebp-4], eax          ;  保存eax
004010E6  |.  C145 FC 07    |rol     dword ptr [ebp-4], 7            ;  将结果循环左移7次,每次从最高位(最左)移出的数据位都补充到最低位
004010EA  |.  8B45 FC       |mov     eax, dword ptr [ebp-4]          ;  再赋给eax
004010ED  |.  83C6 04       |add     esi, 4                          ;  循环变量+4
004010F0  |.  33D8          |xor     ebx, eax                        ;  ebx^eax
004010F2  |.  47            |inc     edi
004010F3  |.  83FE 40       |cmp     esi, 40
004010F6  |.^ 7C D4         \jl      short 004010CC
004010F8  |.  5F            pop     edi
004010F9  |.  8BC3          mov     eax, ebx                         ;  保存ebx的值
004010FB  |.  5E            pop     esi
004010FC  |.  5B            pop     ebx
004010FD  |.  8BE5          mov     esp, ebp
004010FF  |.  5D            pop     ebp
00401100  \.  C3            retn

这里,连接字串后,我们的用户名居然和“is a whore”连接在了一起!。。。翻译过来就是“看雪是个妓女”。

突然明白了《加密与解密》中为什么一直用固定格式的用户名进行测试了(┬_┬)

我x,这程序员好绅(hen)士(tai)啊!!

这里暂且忍下,等完全破解了他的算法再好好调♂教一番。

 

我们继续回来,看看接下来的流程:

0040123D  |.  8945 FC       mov     dword ptr [ebp-4], eax           ;  保存eax
00401240  |.  E8 BBFDFFFF   call    00401000                         ;  清空eax,edx,ecx寄存器的值
00401245  |.  8D8D 2CFFFFFF lea     ecx, dword ptr [ebp-D4]          ;  取拼接字串地址
0040124B  |.  56            push    esi
0040124C  |.  51            push    ecx
0040124D  |.  E8 BEFDFFFF   call    00401010                         ;  删去添加字串。
00401252  |.  83C4 0C       add     esp, 0C                          ;  平衡堆栈
00401255  |.  33C9          xor     ecx, ecx
00401257  |>  8B45 FC       /mov     eax, dword ptr [ebp-4]          ;  取401080子程序中计算的值
0040125A  |.  33D2          |xor     edx, edx                        ;  清零
0040125C  |.  BE 1A000000   |mov     esi, 1A
00401261  |.  F7F6          |div     esi
00401263  |.  8A9415 10FFFF>|mov     dl, byte ptr [ebp+edx-F0]       ;  根据除法余数来取子密钥
0040126A  |.  88540D C8     |mov     byte ptr [ebp+ecx-38], dl       ;  并保存
0040126E  |.  8B45 FC       |mov     eax, dword ptr [ebp-4]          ;  取401080子程序中计算的值
00401271  |.  C1E0 03       |shl     eax, 3                          ;  左移3位
00401274  |.  BA 45230100   |mov     edx, 12345
00401279  |.  F7E8          |imul    eax
0040127B  |.  03C2          |add     eax, edx
0040127D  |.  8945 FC       |mov     dword ptr [ebp-4], eax          ;  保存
00401280  |.  41            |inc     ecx                             ;  循环变量自增
00401281  |.  83F9 12       |cmp     ecx, 12
00401284  |.^ 72 D1         \jb      short 00401257
00401286  |.  E8 75FDFFFF   call    00401000                         ;  该子程序清空eax,edx,ecx寄存器的值
0040128B  |.  33C0          xor     eax, eax                         ;  在此之前的指令不对用户输入的密钥做操作 ↑

至此,对于用户名字串的操作就结束了,接下来的是密钥处理:

0040128D  |> /8A4C05 94     /mov     cl, byte ptr [ebp+eax-6C]       ;  迭代用户输入的密钥
00401291  |. |8A5405 C8     |mov     dl, byte ptr [ebp+eax-38]       ;  取之前算出的子密钥
00401295  |. |80E9 30       |sub     cl, 30
00401298  |. |32D1          |xor     dl, cl
0040129A  |. |885405 C8     |mov     byte ptr [ebp+eax-38], dl       ;  结果存回子密钥
0040129E  |. |40            |inc     eax                             ;  循环变量自增
0040129F  |. |83F8 12       |cmp     eax, 12
004012A2  |.^\72 E9         \jb      short 0040128D
004012A4  |.  E8 57FDFFFF   call    00401000                         ;  该子程序清空eax,edx,ecx寄存器的值
004012A9  |.  8D55 C8       lea     edx, dword ptr [ebp-38]          ;  取子密钥
004012AC  |.  52            push    edx
004012AD  |.  E8 5EFEFFFF   call    00401110                         ;  对子密钥进行操作

F7进入查看:

00401110  /$  8B4C24 04     mov     ecx, dword ptr [esp+4]           ;  取子密钥
00401114  |.  33C0          xor     eax, eax                         ;  清零
00401116  |>  8A1408        /mov     dl, byte ptr [eax+ecx]          ;  迭代子密钥
00401119  |.  32D0          |xor     dl, al                          ;  与循环变量异或
0040111B  |.  881408        |mov     byte ptr [eax+ecx], dl          ;  再存回去
0040111E  |.  40            |inc     eax                             ;  循环变量自增
0040111F  |.  83F8 12       |cmp     eax, 12
00401122  |.^ 72 F2         \jb      short 00401116
00401124  \.  C3            retn

返回后,程序将操作结果与固定值进行对比:

004012B2  |.  E8 49FDFFFF   call    00401000                         ;  该子程序清空eax,edx,ecx寄存器的值
004012B7  |.  8D45 C8       lea     eax, dword ptr [ebp-38]          ;  取子密钥
004012BA  |.  68 14514000   push    00405114                         ;  ASCII "KEYGENNING4NEWBIES"
004012BF  |.  50            push    eax
004012C0  |.  E8 6BFEFFFF   call    00401130                         ;  最终处理结束的子密钥是否等于固定密钥?
004012C5  |.  83C4 0C       add     esp, 0C                          ;  平衡堆栈
004012C8  |.  85C0          test    eax, eax
004012CA  |.  75 3C         jnz     short 00401308
004012CC  |>  8B4D 08       mov     ecx, dword ptr [ebp+8]
004012CF  |.  6A 10         push    10                               ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
004012D1  |.  68 0C514000   push    0040510C                         ; |Title = "Error"
004012D6  |.  68 FC504000   push    004050FC                         ; |Text = "Bad Serial :o("
004012DB  |.  51            push    ecx                              ; |hOwner
004012DC  |.  FF15 AC404000 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
004012E2  |.  5F            pop     edi
004012E3  |.  33C0          xor     eax, eax
004012E5  |.  5E            pop     esi
004012E6  |.  8BE5          mov     esp, ebp
004012E8  |.  5D            pop     ebp
004012E9  |.  C3            retn
004012EA  |>  8B55 08       mov     edx, dword ptr [ebp+8]
004012ED  |.  6A 10         push    10                               ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
004012EF  |.  68 0C514000   push    0040510C                         ; |Title = "Error"
004012F4  |.  68 FC504000   push    004050FC                         ; |Text = "Bad Serial :o("
004012F9  |.  52            push    edx                              ; |hOwner
004012FA  |.  FF15 AC404000 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA
00401300  |.  5F            pop     edi
00401301  |.  33C0          xor     eax, eax
00401303  |.  5E            pop     esi
00401304  |.  8BE5          mov     esp, ebp
00401306  |.  5D            pop     ebp
00401307  |.  C3            retn
00401308  |>  8B55 08       mov     edx, dword ptr [ebp+8]
0040130B  |.  6A 40         push    40                               ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
0040130D  |.  68 F4504000   push    004050F4                         ; |Title = "Great !"
00401312  |.  68 B0504000   push    004050B0                         ; |Text = "Wow you did it... Now write a valid keygen with NO ASM RIPPING :X"
00401317  |.  52            push    edx                              ; |hOwner
00401318  |.  FF15 AC404000 call    dword ptr [<&USER32.MessageBoxA>>; \MessageBoxA

至此,程序的整个加密, 比对流程已经分析完毕了。我们看到,此程序采用是的F(用户名)=F(密钥)的形式进行加密,

也就是非明码比较。对于这种形式我们先将F(用户名)翻译成高级语言,再写出F(密钥)的逆算法,就可以写出注册机。

废话不多说,马上复制一份http://www.cnblogs.com/ZRBYYXDM/p/5002789.html中搭建的MFC窗口程序,添加如下函数:

unsigned int CSerialNumber_KeygenDlg::F1040( unsigned char* str,int num )
{
    unsigned int eax = str[3+num];
    unsigned int edx = str[2+num];

    eax = eax << 8;
    eax += edx;
    edx = str[1+num];
    eax = eax << 8;
    eax += edx;
    edx = str[num];
    eax = eax << 8;
    eax += edx;

    return eax;
}

并修改OnOk函数如下:

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

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

    if ( len == 0 || len > 64 )    
        MessageBox( "用户名必须长度不等于3、不高于64!" );
    else
    {
        unsigned int Veax = 0;
        CString IAW = " is a whore.";
        unsigned char link[64 + 13] = {0};                //用户名最大程度+IAW长度
        DWORD table[16] = {                                //异或常量表
            0x12,0x5C,0x34,0x22,
            0xAB,0x9D,0x54,0x00,
            0xDD,0x84,0xAE,0x66,
            0x31,0x78,0x73,0xCF
        };

        str += IAW;                                        
        memcpy( link,str,str.GetLength() );                    
    
        int i = 0;
        unsigned int Vecx = 0,Vedi = 0,Vebx= F1040( (unsigned char*)"eheh",0 );
        for ( i = 0 ; i != 16 ; i++,Vedi++ ){
            Veax = F1040( link,i*4 );
            Vecx = table[i];                            //逐个取出进行异或。
            Vecx ^= Vedi;
            Veax += Vecx; 

            __asm {
                push eax
                mov eax,Veax
                rol eax,7
                mov Veax,eax
                pop eax
            }
            Vebx ^= Veax;    
        }
        Veax = Vebx;
        str = str.Left( len );                            //还原用户名

        CString subkey = "";                                //密钥
        CString subkeytable = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";    //子密钥
        unsigned int Temp = Veax;        

        for ( i = 0 ; i != 18 ; i++ ){
            Temp = Veax;
            Temp %= 0x1A;
            subkey += subkeytable[(int)Temp];
            Temp = Veax << 3;
            __asm {
                push eax
                push edx
                mov eax,Temp
                imul eax
                add eax,edx
                mov Temp,eax
                pop edx
                pop eax
            }
            Veax = Temp;
        }
        /*接下来先模拟F(密钥)函数的步骤,再根据这些步骤写出F(密钥)的逆算法

        CString key = "123456789ABCDEF123";                                //模拟一个输入的密钥。
        for ( i = 0 ; i != key.GetLength() ; i++ ){
            key.SetAt( i,( key.GetAt( i ) - 0x30 ) );
            subkey.SetAt( i,( subkey[i] ^ key[i] ^ i ) );
        }

        if ( subkey == "KEYGENNING4NEWBIES" )                            //如果算出的subkey值与一个固定的字串相等,则注册成功
            MessageBox( "done." );
        */

        CString Truekey;
        CString key= "KEYGENNING4NEWBIES";

        for ( i = 0 ; i != key.GetLength() ; i++ ){
            key.SetAt( i,( key[i] ^ subkey[i] ^ i ) );
            Truekey += ( key.GetAt( i ) + 0x30 );
        }

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

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

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

运行效果:

太棒了!我们终于写出了注册机!

那么,今天的工作就到这里结束咯!

 

 

 

 

 

 

 

那怎么可能呢,我们还要好好调♂教 作者一下~(乖♂乖♂站♂好♂)

首先,用hex workshop打开程序,搜索“ is a whore.”字串,并用“ is my dad!!”替换并保存:

选择是,将改动保存至文件。

再用OD载入程序,在401238处下断。输入“pediy”,“123456789ABCDEF123”,单击check:

此时堆栈界面如图示:

2333333333333

 

别急,还可以有更多变化。例如:

更多姿♂势请各位自行解♂锁(〜 ̄△ ̄)〜

感谢你的阅读哦~(Thank♂you♂sir)