zoukankan      html  css  js  c++  java
  • 破解 k4n3程序(调♂教 绅(hen)士(tai) 程序猿)

    系统 : 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)

    我们一路奋战,不是为了改变世界,而是不让世界改变我们 ——《熔炉》
  • 相关阅读:
    php 小知识随手记 new self() 和new static()作用和区别
    静态页面制作:16结构与表现分离
    静态页面制作:15标签样式初始化
    静态页面制作:14综合实践
    静态页面制作:13padding的用法
    静态页面制作:12盒模margin特殊情况
    静态页面制作:11盒子模型
    通过JavaScript实现打印页面
    静态页面制作:10样式实战
    Handsontable-一款仿 Excel 的效果的表格插件使用总结
  • 原文地址:https://www.cnblogs.com/ZRBYYXDM/p/5068403.html
Copyright © 2011-2022 走看看