一条汇编指令引发的 蝴蝶效应 (后记)

系统 : Windows xp

程序 : crackme1

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

要求 : 编写注册机

使用工具 :OD

可在“PEDIY CrackMe 2007”中查找关于此程序的讨论,标题为“muckis's crakcme #1破解(检测OD)”。

前天分析了一个crackme,对其中的浮点汇编指令有点头疼,并没有写出注册机。

这两天上网查找了一些关于浮点汇编指令集的资料,补充完基础知识,我们再来分析一下这个crackme。

接着上次,我们来到计算余数的子程序处:

004010E0  /> 55            push    ebp
004010E1  |.  8BEC          mov     ebp, esp
004010E3  |.  83EC 58       sub     esp, 58
004010E6  |.  53            push    ebx
004010E7  |.  56            push    esi
004010E8  |.  57            push    edi
004010E9  |.  8D7D A8       lea     edi, dword ptr [ebp-58]
004010EC  |.  B9 16000000   mov     ecx, 16
004010F1  |.  B8 CCCCCCCC   mov     eax, CCCCCCCC
004010F6  |.  F3:AB         rep     stos dword ptr es:[edi]          ;  用CC填充一段内存
004010F8  |.  C745 FC 00000>mov     dword ptr [ebp-4], 0
004010FF  |.  C745 F8 01000>mov     dword ptr [ebp-8], 1
00401106  |.  C745 F4 00000>mov     dword ptr [ebp-C], 0
0040110D  |.  C745 FC 0A000>mov     dword ptr [ebp-4], 0A
00401114  |.  EB 09         jmp     short 0040111F
00401116  |>  8B45 FC       /mov     eax, dword ptr [ebp-4]
00401119  |.  83E8 01       |sub     eax, 1                          ;  循环变量递减
0040111C  |.  8945 FC       |mov     dword ptr [ebp-4], eax
0040111F  |>  837D FC 00     cmp     dword ptr [ebp-4], 0            ;  小于0?
00401123  |.  7C 4F         |jl      short 00401174                  ;  则退出循环
00401125  |.  DB45 08       |fild    dword ptr [ebp+8]               ;  将传入的参数转化为浮点数,并压栈
00401128  |.  DD5D E8       |fstp    qword ptr [ebp-18]              ;  注意!这里将累加结果转化为双精度浮点型
0040112B  |.  DB45 FC       |fild    dword ptr [ebp-4]               ;  将循环变量转化为浮点数,并压栈
0040112E  |.  83EC 08       |sub     esp, 8                          ;  开辟8个字节的内存空间
00401131  |.  DD1C24        |fstp    qword ptr [esp]                 ;  保存转化成浮点数的循环变量
00401134  |.  68 00002440   |push    40240000                        ;  40240000入栈
00401139  |.  6A 00         |push    0
0040113B  |.  E8 C91E0000   |call    00403009
00401140  |.  83C4 10       |add     esp, 10
00401143  |.  DC7D E8       |fdivr   qword ptr [ebp-18]              ;  浮点反除
00401146  |.  E8 AD210000   |call    004032F8                        ;  将st的值转为整数存入eax
0040114B  |.  8945 F0       |mov     dword ptr [ebp-10], eax
0040114E  |.  837D F0 00    |cmp     dword ptr [ebp-10], 0           ;  商太小则直接进行累加
00401152  |.  7E 0F         |jle     short 00401163
00401154  |.  8B4D F8       |mov     ecx, dword ptr [ebp-8]
00401157  |.  51            |push    ecx
00401158  |.  E8 DAFEFFFF   |call    00401037                        ;  变换数据
0040115D  |.  83C4 04       |add     esp, 4
00401160  |.  8945 F8       |mov     dword ptr [ebp-8], eax
00401163  |>  8B55 F0       |mov     edx, dword ptr [ebp-10]         ;  取商
00401166  |.  0FAF55 F8     |imul    edx, dword ptr [ebp-8]          ;  乘以 变换数据
0040116A  |.  8B45 F4       |mov     eax, dword ptr [ebp-C]          ;  取出累加的值
0040116D  |.  03C2          |add     eax, edx                        ;  累加
0040116F  |.  8945 F4       |mov     dword ptr [ebp-C], eax          ;  保存
00401172  |.^ EB A2         jmp     short 00401116
00401174  |>  8B45 F4       mov     eax, dword ptr [ebp-C]           ;  算出一个值
00401177  |.  99            cdq                                      ;  把EDX的所有位都设成EAX最高位的值
00401178  |.  B9 0A000000   mov     ecx, 0A
0040117D  |.  F7F9          idiv    ecx                              ;  除以0A
0040117F  |.  8BC2          mov     eax, edx                         ;  余数放入eax
00401181  |.  5F            pop     edi
00401182  |.  5E            pop     esi
00401183  |.  5B            pop     ebx
00401184  |.  83C4 58       add     esp, 58
00401187  |.  3BEC          cmp     ebp, esp
00401189  |.  E8 32210000   call    004032C0
0040118E  |.  8BE5          mov     esp, ebp
00401190  |.  5D            pop     ebp
00401191  .  C3            retn

跟入403009:

00403009   $  8D5424 0C     lea     edx, dword ptr [esp+C]
0040300D   .  E8 630B0000   call    00403B75
00403012   $  8BC8          mov     ecx, eax
00403014   .  50            push    eax
00403015   .  9B            wait
00403016   .  D93C24        fstcw   word ptr [esp]                   ;  保存控制字寄存器
00403019   .  66:813C24 7F0>cmp     word ptr [esp], 27F
0040301F   .  74 05         je      short 00403026
00403021   .  E8 1F0B0000   call    00403B45
00403026   >  81E1 0000F07F and     ecx, 7FF00000
0040302C   .  8D5424 08     lea     edx, dword ptr [esp+8]           ;  取一段内存
00403030   .  81F9 0000F07F cmp     ecx, 7FF00000                    ;  是7FF00000吗?
00403036   .  0F84 9D000000 je      004030D9
0040303C   .  E8 340B0000   call    00403B75
00403041   .  0F84 8E000000 je      004030D5
00403047   .  A9 0000F07F   test    eax, 7FF00000
0040304C   .  0F84 F6000000 je      00403148
00403052   >  8A4C24 0F     mov     cl, byte ptr [esp+F]
00403056   .  80E1 80       and     cl, 80
00403059   .  0F85 61010000 jnz     004031C0
0040305F   >  D9F1          fyl2x
00403061   .  E8 CA0A0000   call    00403B30                         ;  生成除数
00403066   .  80F9 01       cmp     cl, 1
00403069   .  75 02         jnz     short 0040306D
0040306B   .  D9E0          fchs
0040306D   >  833D B8DE4200>cmp     dword ptr [42DEB8], 0
00403074   .  0F85 540B0000 jnz     00403BCE
0040307A   .  8D0D B8CB4200 lea     ecx, dword ptr [42CBB8]
00403080   .  BA 1D000000   mov     edx, 1D
00403085   .  E9 8F0B0000   jmp     00403C19
0040308A   >  833D B8DE4200>cmp     dword ptr [42DEB8], 0
00403091   .  0F85 370B0000 jnz     00403BCE
00403097   .  8D0D B8CB4200 lea     ecx, dword ptr [42CBB8]
0040309D   .  BA 1D000000   mov     edx, 1D
004030A2   .  E8 290A0000   call    00403AD0
004030A7   .  5A            pop     edx
004030A8   .  C3            retn

跟入关键call:

00403B30  /$  D9C0          fld     st
00403B32  |.  D9FC          frndint                                  ;  对st取整
00403B34  |.  DCE1          fsubr   st(1), st                        ;  st - st(1)
00403B36  |.  D9C9          fxch    st(1)                            ;  互换st和st(1)
00403B38  |.  D9E0          fchs                                     ;  取相反数
00403B3A  |.  D9F0          f2xm1
00403B3C  |.  D9E8          fld1                                     ;  将1.0压栈
00403B3E  |.  DEC1          faddp   st(1), st                        ;  加法,结果存入st(1),弹出st
00403B40  |.  D9FD          fscale                                   ;  这个指令是计算ST(0)*2的ST(1)次方之值,再把结果存入 ST(0) 里而 ST(1) 之值不变
00403B42  |.  DDD9          fstp    st(1)                            ;  将协处理器堆栈栈顶的数据传送到目标操作数中,并弹栈
00403B44  .  C3            retn

那么403009子程序主要就是自动生出一个除数出来,第一次生成10^10,第二次生成10 ^ 9,以此类推。

再来看看4032F8:

004032F8  /$  55            push    ebp
004032F9  |.  8BEC          mov     ebp, esp
004032FB  |.  83C4 F4       add     esp, -0C
004032FE  |.  9B            wait
004032FF  |.  D97D FE       fstcw   word ptr [ebp-2]                 ;  取出FCW
00403302  |.  9B            wait
00403303  |.  66:8B45 FE    mov     ax, word ptr [ebp-2]             ;  存入ax
00403307  |.  80CC 0C       or      ah, 0C                           ;  按位或
0040330A  |.  66:8945 FC    mov     word ptr [ebp-4], ax
0040330E  |.  D96D FC       fldcw   word ptr [ebp-4]                 ;  存回FCW
00403311  |.  DF7D F4       fistp   qword ptr [ebp-C]                ;  st转化为整数存入ebp-c,st出栈
00403314  |.  D96D FE       fldcw   word ptr [ebp-2]                 ;  将原本的FCW保存回去
00403317  |.  8B45 F4       mov     eax, dword ptr [ebp-C]           ;  结果存入eax
0040331A  |.  8B55 F8       mov     edx, dword ptr [ebp-8]
0040331D  |.  C9            leave
0040331E  .  C3            retn

还有401037:

00401037   $ /E9 44000000   jmp     00401080

继续跟:

00401080  /> 55            push    ebp
00401081  |.  8BEC          mov     ebp, esp
00401083  |.  83EC 44       sub     esp, 44                          ;  开辟内存空间
00401086  |.  53            push    ebx
00401087  |.  56            push    esi
00401088  |.  57            push    edi
00401089  |.  8D7D BC       lea     edi, dword ptr [ebp-44]          ;  取一段内存
0040108C  |.  B9 11000000   mov     ecx, 11
00401091  |.  B8 CCCCCCCC   mov     eax, CCCCCCCC
00401096  |.  F3:AB         rep     stos dword ptr es:[edi]          ;  用CC填充内存
00401098  |.  C745 FC 07000>mov     dword ptr [ebp-4], 7
0040109F  |.  837D 08 07    cmp     dword ptr [ebp+8], 7             ;  入栈数据是否等于7?
004010A3  |.  75 07         jnz     short 004010AC
004010A5  |.  C745 FC 03000>mov     dword ptr [ebp-4], 3
004010AC  |>  837D 08 03    cmp     dword ptr [ebp+8], 3             ;  入栈数据是否等于3?
004010B0  |.  75 07         jnz     short 004010B9
004010B2  |.  C745 FC 01000>mov     dword ptr [ebp-4], 1
004010B9  |>  8B45 FC       mov     eax, dword ptr [ebp-4]           ;  变换数据之后存入eax
004010BC  |.  5F            pop     edi
004010BD  |.  5E            pop     esi
004010BE  |.  5B            pop     ebx
004010BF  |.  8BE5          mov     esp, ebp
004010C1  |.  5D            pop     ebp
004010C2  .  C3            retn

这样,整个子程序分析完毕。它的功能就是根据入栈的累加结果生成一个数,取其与10的余数并返回。

我们直接打开之前搭建的框架,并修改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 ( !str.IsEmpty() ){                                    //格式控制。
        str.MakeUpper();

        unsigned int sum = 0;
        for ( int i = 0 ; i != len ; i++ ){
            if ( str[i] == 0x20 )                            //字符为空格则continue
                continue;
            sum += ( str[i] * 0x157A - 1 );
        }
        sum *= 0xA;

        sum += GetRes( sum );

        CString PassWord;
        PassWord.Format( "%d",sum );
        SetDlgItemText( IDC_EDIT_PASSWORD,PassWord );
    }
    else
        MessageBox( "用户名格式错误!" );
}

并添加如下类成员函数:

int CKengen_TemplateDlg::GetRes(unsigned int Var)
{
    double FVar;                                            //这里一定要设置成为double,否则会损失精度
    int ChangeVar = 1;
    unsigned int sum = 0;

    for ( int i = 0 ; i != 10 ; i++ ){
        __asm{                                                //转换为浮点数
            fild Var
            fstp FVar
        }

        int temp = (int)( FVar / GetDR(i) );                //进行除法,保存商
        if ( temp > 0 )
            Change( ChangeVar );                            //变换数据

        sum += temp * ChangeVar;                            //累加
    }
    
    /*
    CString PS;
    PS.Format( "%x",sum );
    MessageBox( PS );
    */

    return sum % 10;
}

float CKengen_TemplateDlg::GetDR(int temp)
{
    float res = 1;
    for ( int i = 0 ; i != 10 - temp ; i++ )
        res *= 10;

    return res;
}

void CKengen_TemplateDlg::Change(int &Var)
{
    int temp;

    if ( Var == 1 )
        temp = 7;
    if ( Var == 7 )
        temp = 3;
    if ( Var == 3 )
        temp = 1;

    Var = temp;
}

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

运行效果:

一处细微的差异,会引起一连串连锁反应,最终导致结果的巨大差异。程序中,一处细小的设计失误,可能引起整个系统的崩溃,体现了计算机系统的高精度和复杂性。这就要求操作这个复杂系统的程序员要对自己的技术精益求精,刻意养成良好的习惯,这样一直不断堆砌,才能成就日后的巨大成功。

原文地址:https://www.cnblogs.com/ZRBYYXDM/p/5216782.html