一个CrackMe的分析

来源:互联网 发布:淘宝旺旺聊天刷单禁词 编辑:程序博客网 时间:2024/05/22 02:24

0x00 初探
程序有ASLR,不方便分析,使用FFI去掉。
这里写图片描述
OD载入后,发现程序退出。
这里写图片描述
给ExitProcess下断点,然后重新载入程序,成功断下。修改代码为retn 4即可。
这里写图片描述
此时即可使用OD调试。运行后,[确认]按钮是禁止状态。
0x01 寻找编辑框的输入事件
用彗星探测一下编辑框的句柄
这里写图片描述
给GetWindowTextW下条件断点。
这里写图片描述
输入一个字节即可断下。
这里写图片描述
单步回到004020D9处。
程序首先计算注册码的长度:
这里写图片描述
然后判断长度是否是16个字符,不是则返回。
这里写图片描述
构造一个长度是16个字符的key即可通过。我构造的是0123456789ABCDEF。
程序将16个字符的key转换成ANSI字符的形式。
这里写图片描述
然后调用函数004017C0。
0x02 重点函数004017C0的逆向
函数比较长,我直接放代码。注释已经标记。

004017C0   $  55            push    ebp004017C1   .  8BEC          mov     ebp, esp004017C3   .  8B4D 08       mov     ecx, dword ptr [ebp+8]                       ;  Key004017C6   .  83EC 08       sub     esp, 8004017C9   .  8BC1          mov     eax, ecx004017CB   .  56            push    esi004017CC   .  8D70 01       lea     esi, dword ptr [eax+1]004017CF   .  90            nop004017D0   >  8A10          mov     dl, byte ptr [eax]004017D2   .  40            inc     eax004017D3   .  84D2          test    dl, dl004017D5   .^ 75 F9         jnz     short 004017D0004017D7   .  2BC6          sub     eax, esi                                     ;  再次得到Key的长度004017D9   .  83F8 10       cmp     eax, 10                                      ;  不是16个字节则触发异常004017DC   .  74 01         je      short 004017DF004017DE      FF            db      FF004017DF   >  BA 58145700   mov     edx, 00571458004017E4   .  33F6          xor     esi, esi004017E6   .  2BD1          sub     edx, ecx004017E8   >  8A01          mov     al, byte ptr [ecx]                           ;  取出一个字符 判断是否是数字或者是大写字母004017EA   .  3C 29         cmp     al, 29004017EC   .  7E 08         jle     short 004017F6004017EE   .  3C 40         cmp     al, 40004017F0   .  7D 06         jge     short 004017F8004017F2   .  2C 30         sub     al, 30004017F4   .  EB 0A         jmp     short 00401800004017F6   >  3C 40         cmp     al, 40004017F8   >  7E 52         jle     short 0040184C004017FA   .  3C 47         cmp     al, 47004017FC   .  7D 4E         jge     short 0040184C                               ;  如果不是数字或者不是大写字母则返回NULL004017FE   .  2C 37         sub     al, 3700401800   >  88040A        mov     byte ptr [edx+ecx], al                       ;  将对应的数字或者大写字母转换成整数保存('0' -> 0;'A' -> A)00401803   .  46            inc     esi00401804   .  41            inc     ecx00401805   .  83FE 10       cmp     esi, 1000401808   .^ 7C DE         jl      short 004017E80040180A   .  BA 02000000   mov     edx, 20040180F   .  81EA 59145700 sub     edx, 0057145900401815   .  8955 FC       mov     dword ptr [ebp-4], edx00401818   .  33C0          xor     eax, eax0040181A   .  BA 03000000   mov     edx, 30040181F   .  81EA 59145700 sub     edx, 0057145900401825   .  53            push    ebx00401826   .  8D48 01       lea     ecx, dword ptr [eax+1]00401829   .  8955 F8       mov     dword ptr [ebp-8], edx0040182C   .  57            push    edi0040182D   .  8D49 00       lea     ecx, dword ptr [ecx]00401830   >  8BF8          mov     edi, eax00401832   .  83E7 01       and     edi, 100401835   .  75 1C         jnz     short 0040185300401837   .  8A90 58145700 mov     dl, byte ptr [eax+571458]                    ;  取出一个字节0040183D   .  C0E2 04       shl     dl, 4                                        ;  左移四位00401840   .  8BF0          mov     esi, eax00401842   .  D1EE          shr     esi, 100401844   .  8896 58145700 mov     byte ptr [esi+571458], dl                    ;  保存0040184A   .  EB 19         jmp     short 004018650040184C   >  32C0          xor     al, al0040184E   .  5E            pop     esi0040184F   .  8BE5          mov     esp, ebp00401851   .  5D            pop     ebp00401852   .  C3            retn00401853   >  8BD0          mov     edx, eax00401855   .  D1EA          shr     edx, 100401857   .  8DB2 58145700 lea     esi, dword ptr [edx+571458]0040185D   .  8A90 58145700 mov     dl, byte ptr [eax+571458]00401863   .  0016          add     byte ptr [esi], dl00401865   >  8A98 59145700 mov     bl, byte ptr [eax+571459]                    ;  再取出一个字节0040186B   .  8BD1          mov     edx, ecx0040186D   .  83E2 01       and     edx, 100401870   .  8BF1          mov     esi, ecx00401872   .  75 0D         jnz     short 0040188100401874   .  C0E3 04       shl     bl, 400401877   .  D1EE          shr     esi, 100401879   .  889E 58145700 mov     byte ptr [esi+571458], bl0040187F   .  EB 0E         jmp     short 0040188F00401881   >  D1EE          shr     esi, 100401883   .  009E 58145700 add     byte ptr [esi+571458], bl                    ;  和前一次运算后的结果相加00401889   .  8DB6 58145700 lea     esi, dword ptr [esi+571458]0040188F   >  8B75 FC       mov     esi, dword ptr [ebp-4]00401892   .  8A98 5A145700 mov     bl, byte ptr [eax+57145A]00401898   .  8DB406 591457>lea     esi, dword ptr [esi+eax+571459]0040189F   .  85FF          test    edi, edi004018A1   .  75 0D         jnz     short 004018B0004018A3   .  C0E3 04       shl     bl, 4004018A6   .  D1EE          shr     esi, 1004018A8   .  889E 58145700 mov     byte ptr [esi+571458], bl004018AE   .  EB 0E         jmp     short 004018BE004018B0   >  D1EE          shr     esi, 1004018B2   .  009E 58145700 add     byte ptr [esi+571458], bl004018B8   .  8DB6 58145700 lea     esi, dword ptr [esi+571458]004018BE   >  85D2          test    edx, edx004018C0   .  75 1D         jnz     short 004018DF004018C2   .  8A90 5B145700 mov     dl, byte ptr [eax+57145B]                    ;  再取出一个字节004018C8   .  8B75 F8       mov     esi, dword ptr [ebp-8]004018CB   .  C0E2 04       shl     dl, 4                                        ;  左移四位004018CE   .  8DB406 591457>lea     esi, dword ptr [esi+eax+571459]004018D5   .  D1EE          shr     esi, 1004018D7   .  8896 58145700 mov     byte ptr [esi+571458], dl                    ;  保存004018DD   .  EB 1E         jmp     short 004018FD004018DF   >  8B55 F8       mov     edx, dword ptr [ebp-8]004018E2   .  8DB402 591457>lea     esi, dword ptr [edx+eax+571459]004018E9   .  8A90 5B145700 mov     dl, byte ptr [eax+57145B]                    ;  再取出一个字节004018EF   .  D1EE          shr     esi, 1                                       ;  右移一位004018F1   .  0096 58145700 add     byte ptr [esi+571458], dl                    ;  加上前一次运算后的结果004018F7   .  8DB6 58145700 lea     esi, dword ptr [esi+571458]004018FD   >  83C1 04       add     ecx, 400401900   .  83C0 04       add     eax, 400401903   .  83F9 11       cmp     ecx, 1100401906   .^ 0F8C 24FFFFFF jl      004018300040190C   .  B8 58145700   mov     eax, 0057145800401911   .  5F            pop     edi00401912   .  8D50 01       lea     edx, dword ptr [eax+1]00401915   .  5B            pop     ebx00401916   >  8A08          mov     cl, byte ptr [eax]                           ;  取出一个字节00401918   .  40            inc     eax00401919   .  84C9          test    cl, cl                                       ;  判断是否是00040191B   .^ 75 F9         jnz     short 004019160040191D   .  2BC2          sub     eax, edx                                     ;  得到00字节的偏移0040191F   .  83F8 08       cmp     eax, 8                                       ;  和8比较00401922   .  0f95c0        setne   al                                           ;  不相等则返回1 相等返回000401925   .  5E            pop     esi00401926   .  8BE5          mov     esp, ebp00401928   .  5D            pop     ebp00401929   .  C3            retn

分析易知,这个函数的作用就是将输入的十六进制文本转换成字节集的形式,然后判断00字节的偏移是否是8,如果是8则返回0。
返回后,程序会判断这个函数的返回值,如果返回1则激活[确定]按钮,返回0则不激活[确定]按钮
这里写图片描述
0x03 寻找按钮事件
由刚刚分析的十六进制文本转换成字节集的函数可以得知,结果被存放在00571458处。给00571458下硬件访问断点,大小是1。然后点击[确定]按钮。在0040193A处断下。接着这个函数往下分析。
这里写图片描述
这段函数再判断00字节的偏移是不是8,如果是8则成功,否则触发异常。
这个和之前那个十六进制文本转换字节集的函数的验证结果是矛盾的。
那个函数判断00字节的偏移是否是8,如果是8则不会解锁[确定]按钮。但是在[确定]的按钮事件里又判断这个偏移是否是8,如果是8则成功,不是8则触发异常。
说明这个是一个烟雾弹。不是真正的算法。
0x04 分析真正的算法
虽然按钮事件是烟雾弹,但是我猜测这个字节集绝对会用上,因此我没有删掉这个硬件断点。
再按F9,即可来到真正的算法函数。看来我的猜想是正确的。
来到00401987处,找到函数头00401970即可。
这里写图片描述
用IDA载入,发现算法函数中有调用一个call,然后用转换成十六进制的Key去解一个方程组,八元一次方程组。
这里写图片描述
先解这个八元一次方程组,我手解的时候发现后面几个字节是除不开的。。。于是只好写个脚本来跑。
代码如下:

#include <stdio.h>#include <stdlib.h>int main(void){    int a[8];//77 33 31 6C 64 30 6E 65    for(a[0] = 0;a[0] <= 255;a[0]++)    {        for(a[1] = 0;a[1] <= 255;a[1]++)        {            for(a[2] = 0;a[2] <= 255;a[2]++)            {                for(a[3] = 0;a[3] <= 255;a[3]++)                {                    if((unsigned __int8)(a[3] + a[2] + a[1] + a[0]) != 71)                        continue;                    if((unsigned __int8)a[0] != (unsigned __int8)a[1] + 68)                        continue;                    if((unsigned __int8)a[1] != (unsigned __int8)a[2] + 2)                        continue;                    if((unsigned __int8)a[2] != (unsigned __int8)a[3] - 59)                        continue;                    //77 33 31 6C                    for(int i = 0;i < 4;i++)                        printf("%0.02X ",a[i]);                    goto NextWhile;                }            }        }    }NextWhile:    for(a[4] = 0;a[4] <= 255;a[4]++)    {        for(a[5] = 0;a[5] <= 255;a[5]++)        {            for(a[6] = 0;a[6] <= 255;a[6]++)            {                for(a[7] = 0;a[7] <= 255;a[7]++)                {                    if((unsigned __int8)(a[7] + a[6] + a[5]) != 3)                        continue;                    if((unsigned __int8)a[6] != (unsigned __int8)a[4] + 10)                        continue;                    if((unsigned __int8)a[6] != (unsigned __int8)a[7] + 9)                        continue;                    if((unsigned __int8)a[4] != (unsigned __int8)a[5] + 52)                        continue;                    //64 30 6E 65                    for(int i = 4;i < 8;i++)                        printf("%0.02X ",a[i]);                    goto Next;                }            }        }    }Next:    puts("");    system("pause");    return 0;}

运行结果如图所示:
这里写图片描述
由此可以得知,正确Key的十六进制形式应为77 33 31 6C 64 30 6E 65
0x05 重点函数00401970的逆向
函数代码如图所示:
这里写图片描述
这段算法是Rijndael算法中的S-Box变换。
Flag只有16个字节,每个字节都是从00~FF的,因此我用的枚举。
方式就是一个字节一个字节枚举,和刚刚的解方程的代码的枚举方式差不多。
为了节省时间,我写了一个DLL来做这些工作,然后注入进去枚举Key,得到正确的Key后用信息框提示。
代码如下:
这里写图片描述
Hex函数的功能就是将十六进制的字节(00~FF)转换成文本。
这里写图片描述
将这些代码编译成一个DLL,然后注入到CM的进程中即可。得到结果如下:
这里写图片描述
拿到Flag:C7C536FC625CEFCD
放到原版CM中验证一下,可以通过。
这里写图片描述
全文完。