首页
社区
课程
招聘
[求助]旧帖新读之OllyDBG入门系列(五)CrackMe算法分析
发表于: 2014-8-1 10:29 4040

[求助]旧帖新读之OllyDBG入门系列(五)CrackMe算法分析

2014-8-1 10:29
4040

最近因为工作需要,重新学习了CCDebuger大大的《OllyDBG入门系列》,受益匪浅,在学到第五篇教程“OllyDBG 入门系列(五)-消息断点及 RUN 跟踪”时,大大给出了整个调试流程而没有给出最终算法分析和注册机编写过程,原文如下:

“写到这准备跟踪算法时,才发现这个 crackme 还是挺复杂的,具体算法我就不写了,实在没那么多时间详细跟踪。有兴趣的可以跟一下,注册码是17位,用户名采用复制的方式扩展到 16 位,如我输入“CCDebuger”,扩展后就是“CCDebugerCCDebug”。大致是先取扩展后用户名的前 8 位和注册码的前 8 位,把用户名的前四位和后四位分别与注册码的前四位和后四位进行运算,算完后再把扩展后用户名的后 8 位和注册码的后 8 位分两部分,再与前面用户名和注册码的前 8 位计算后的值进行异或计算,最后结果等于 0 就成功。注册码的第 17 位我尚未发现有何用处。对于新手来说,可能这个 crackme 的难度大了一点。没关系,我们主要是学习 OllyDBG 的使用,方法掌握就可以了。”

小菜出于好奇尝试分析了一下,感觉难度大了不止一点儿~~~ 水平还是不够啊 分析工作已经完成了算法的C语言实现部分,但是到算法流程分析阶段因为涉及递归函数,小菜被绕晕了~~~现将已经完成的工作贴出,希望哪位大大能不吝赐教,谢谢么么哒~

前面的调试过程就不再赘述,详见CCDebuger大大的教程(OllyDBG 入门系列(五)-消息断点及 RUN 跟踪,地址:http://bbs.pediy.com/showthread.php?t=21532),我们从以下位置开始:
004010E2 |. 8BFE             MOV EDI,ESI                                         ; 用户名送 EDI
004010E4 |. 03F8             ADD EDI,EAX
004010E6 |. FC               CLD
004010E7 |. F3:A4            REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
004010E9 |. 33C9             XOR ECX,ECX                                         ; 清零,设循环计数器
004010EB |. BE 71214000      MOV ESI,cycle.00402171                              ; 注册码送ESI
004010F0 |> 41               INC ECX
004010F1 |. AC               LODS BYTE PTR DS:[ESI]                              ; 取注册码的每个字符
004010F2 |. 0AC0             OR AL,AL                                            ; 判断是否为空
004010F4 |. 74 0A            JE SHORT cycle.00401100                             ; 没有则跳走
004010F6 |. 3C 7E            CMP AL,7E                                           ; 判断字符是否为非ASCII字符
004010F8 |. 7F 06            JG SHORT cycle.00401100                             ; 非ASCII字符跳走
004010FA |. 3C 30            CMP AL,30                                           ; 看是否小于30H,主要是判断是不是数字或字母等
004010FC |. 72 02            JB SHORT cycle.00401100                             ; 小于跳走
004010FE |.^ EB F0           JMP SHORT cycle.004010F0
00401100 |> 83F9 11          CMP ECX,11                                          ; 比较注册码位数,必须为十进制17位
00401103 |. 75 1A            JNZ SHORT cycle.0040111F
00401105 |. E8 E7000000      CALL cycle.004011F1                                 ; 关键,F7跟进去
0040110A |. B9 01FF0000      MOV ECX,0FF01
0040110F |. 51               PUSH ECX
00401110 |. E8 7B000000      CALL cycle.00401190                                 ; 关键,跟进去
00401115 |. 83F9 01          CMP ECX,1
00401118 |. 74 06            JE SHORT cycle.00401120
0040111A |> E8 47000000      CALL cycle.00401166                                 ; 注册失败对话框
0040111F |> C3               RETN
00401120 |> A1 68214000      MOV EAX,DWORD PTR DS:[402168]
00401125 |. 8B1D 6C214000    MOV EBX,DWORD PTR DS:[40216C]
0040112B |. 33C3             XOR EAX,EBX
0040112D |. 3305 82214000    XOR EAX,DWORD PTR DS:[402182]
00401133 |. 0D 40404040      OR EAX,40404040
00401138 |. 25 77777777      AND EAX,77777777
0040113D |. 3305 79214000    XOR EAX,DWORD PTR DS:[402179]
00401143 |. 3305 7D214000    XOR EAX,DWORD PTR DS:[40217D]
00401149 |.^ 75 CF           JNZ SHORT cycle.0040111A                             ; 这里跳走就完蛋
0040114B |. E8 2B000000      CALL cycle.0040117B                                  ; 注册成功对话框

(一) 004011F1关键函数一分析

(1)函数处理过程分为三部分:

1. 处理输入用户名(下记为user[17])/注册码(下记为code[17])

004011F1  /$  A1 60214000   mov     eax, dword ptr [402160]       ;  [eax] = *(int *)user
004011F6  |.  8B1D 64214000 mov     ebx, dword ptr [402164]       ;  [ebx] = *(int *)(user+4)

004011FC  |.  3305 71214000 xor     eax, dword ptr [402171]       ;  [eax] ^= *(int *)code
00401202  |.  331D 75214000 xor     ebx, dword ptr [402175]       ;  [ebx] ^= *(int *)(code+4)

00401208  |.  25 0F1F3F7F   and     eax, 7F3F1F0F                 ;  [eax] &= 0x7F3F1F0F
0040120D  |.  81E3 00010307 and     ebx, 7030100                  ;  [ebx] &= 0x07030100

处理结果分别存放于eax和ebx。

(2, 3 两步位于同一循环)

00401213  |.  33C9          xor     ecx, ecx                      ;  ecx清零(counter, 设为i)

<循环开始>

2. 生成用于后续处理的字节

00401215  |> /8BF0          /mov     esi, eax                     ;  初始:[esi] = 用户名/注册

码前0~3位处理结果
00401217  |. |8BFB          |mov     edi, ebx                     ;  初始:[edi] = 用户名/注册

码前4~7位处理结果

00401219  |. |D3E6          |shl     esi, cl
0040121B  |. |D3E7          |shl     edi, cl

0040121D  |. |81E6 80808080 |and     esi, 80808080                ;  [esi] &= 0x80808080
00401223  |. |81E7 80808080 |and     edi, 80808080                ;  [edi] &= 0x80808080

00401229  |. |8BD6          |mov     edx, esi
0040122B  |. |C0EE 07       |shr     dh, 7
0040122E  |. |66:C1E2 07    |shl     dx, 7
00401232  |. |C1EA 08       |shr     edx, 8
00401235  |. |C0EE 07       |shr     dh, 7
00401238  |. |66:C1E2 07    |shl     dx, 7                        ;  (([edx] & 0x000000FF) >>

(3+0)) |
0040123C  |. |C1EA 08       |shr     edx, 8                       ;  (([edx] & 0x00008000) >>

(2+8)) |
0040123F  |. |C0EE 07       |shr     dh, 7                        ;  (([edx] & 0x00800000) >>

(1+16)) |
00401242  |. |66:D1EA       |shr     dx, 1                        ;  (([edx] & 0x80000000) >>

(0+24))
00401245  |. |8BF2          |mov     esi, edx                     ;   => [esi]

00401247  |. |8BD7          |mov     edx, edi
00401249  |. |C0EE 07       |shr     dh, 7
0040124C  |. |66:C1E2 07    |shl     dx, 7
00401250  |. |C1EA 08       |shr     edx, 8
00401253  |. |C0EE 07       |shr     dh, 7
00401256  |. |66:C1E2 07    |shl     dx, 7                        ;  (([edx] & 0x00000080) >>

(7+0)) |
0040125A  |. |C1EA 08       |shr     edx, 8                       ;  (([edx] & 0x00008000) >>

(6+8)) |
0040125D  |. |C0EE 07       |shr     dh, 7                        ;  (([edx] & 0x00800000) >>

(5+16)) |
00401260  |. |66:C1EA 05    |shr     dx, 5                        ;  (([edx] & 0x80000000) >>

(4+24))
00401264  |. |8BFA          |mov     edi, edx                     ;   => [edi]

00401266  |. |33FE          |xor     edi, esi                     ;  [edi] ^ [esi]
00401268  |. |8BD7          |mov     edx, edi
0040126A  |. |81E2 FF000000 |and     edx, 0FF                     ;  最终结果取最后1字节

结果存放于edx。

3. 分情况处理eax和ebx

00401270  |.  51            |push    ecx

00401271  |.  52            |push    edx
00401272  |.  BA 08000000   |mov     edx, 8
00401277  |.  91            |xchg    eax, ecx
00401278  |.  83F8 03       |cmp     eax, 3                       ;  if([eax(原ecx)] <= 3) {
0040127B  |.  7F 0F         |jg      short 0040128C
0040127D  |.  F6E2          |mul     dl                           ;  [al(原cl)] * [dl(8)] ==>

ax
0040127F  |.  5A            |pop     edx
00401280  |.  83C0 08       |add     eax, 8                       ;  [ax] += 8
00401283  |.  91            |xchg    eax, ecx                     ;  以上: [ecx] = [ecx] * 8 +

8
00401284  |.  D3C0          |rol     eax, cl                      ;  初始: [eax]为用户名/注册

码前0~3位处理结果
00401286  |.  33C2          |xor     eax, edx
00401288  |.  D3C8          |ror     eax, cl                      ;  }
0040128A  |.  EB 0D         |jmp     short 00401299               ;  else {
0040128C  |>  83E8 03       |sub     eax, 3
0040128F  |.  F6E2          |mul     dl                           ;  [al(原[cl]-3)] * [dl(8)]

==> ax
00401291  |.  5A            |pop     edx
00401292  |.  91            |xchg    eax, ecx                     ;  以上: [ecx] = ([ecx] - 3)

* 8
00401293  |.  D3C3          |rol     ebx, cl                      ;  初始: [ebx]为用户名/注册

码前4~7位处理结果
00401295  |.  33DA          |xor     ebx, edx
00401297  |.  D3CB          |ror     ebx, cl                      ;  }

00401299  |>  59            |pop     ecx

至此一趟循环结束,eax和ebx中内容变为处理结果,并作为下一趟循环输入。

0040129A  |.  41            |inc     ecx                          ;  i++
0040129B  |.  83F9 08       |cmp     ecx, 8                       ;  i < 8
0040129E  |.^ 0F85 71FFFFFF \jnz     00401215

<循环结束>

(2) C语言实现如下:

unsigned char user[17] = "CCDebugerCCDebug";
unsigned char code[17] = "7878787878787878";
unsigned int num1, num2;                //eax/ebx

void Func1(void)
{
        int i, j;                        //ecx/ecx暂存
        unsigned int temp1, temp2;        //esi/edi
        unsigned int seq;                //edx

        num1 =
                ((*(unsigned int *)user)       ^ (*(unsigned int *)code))     & 0x7F3F1F0F;
        num2 =
                ((*(unsigned int *)(user + 4)) ^ (*(unsigned int *)(code+4))) & 0x07030100;

        for(i = 0; i < 8; i++)
        {
                temp1 = (num1 << i) & 0x80808080;
                temp2 = (num2 << i) & 0x80808080;

                temp1 = ((temp1 & 0x000000FF) >> (3+0))  |
                        ((temp1 & 0x00008000) >> (2+8))  |
                        ((temp1 & 0x00800000) >> (1+16)) |
                        ((temp1 & 0x80000000) >> (0+24));
                temp2 = ((temp2 & 0x00000080) >> (7+0))  |
                        ((temp2 & 0x00008000) >> (6+8))  |
                        ((temp2 & 0x00800000) >> (5+16)) |
                        ((temp2 & 0x80000000) >> (4+24));
                seq = (temp1 ^ temp2) & 0xFF;

                j = i;
                if(j <= 3)
                {
                        j = j * 8 + 8;

                        num1 = (num1 >> (32 - j)) | (num1 << j);
                        num1 ^= seq;
                        num1 = (num1 << (32 - j)) | (num1 >> j);
                }
                else
                {
                        j = (j - 3) * 8;

                        num2 = (num2 >> (32 - j)) | (num2 << j);
                        num2 ^= seq;
                        num2 = (num2 << (32 - j)) | (num2 >> j);
                }
        }
}

分析可知

Func1输入:user,code的前8字节

Func1输出:eax, ebx (即num1,num2)

(二) 00401190关键函数二分析

因为该部分函数涉及递归运算,实在分析不出具体的算法流程和用途,故只将这部分函数用C语言实现了一

遍,希望哪位大大能帮忙分析一下,不胜感激O(∩_∩)O~

C语言实现如下:

//Func1相关
unsigned char user[17] = "CCDebugerCCDebug";
unsigned char code[17] = "7878787878787878";
unsigned int num1, num2;                //eax/ebx

//Func2相关
unsigned int check =  0xFF01;                //ecx
unsigned int common = 0x0FEDCBA98;        //addr: 0x402182

void Func2(void)
{
        unsigned int temp;                                //esi
        unsigned int tempnum;                        //edi

        if(check > 0x80)
        {
                temp = check & 0xFF;

                tempnum = num1;
                if(temp > 8)
                {
                        tempnum = num2;
                        temp >>= 4;                                //使用temp的高4位
                }

                do
                {
                        tempnum = (tempnum >> (32 - 8)) | (tempnum << 8);
                        temp >>= 1;                                //temp /= 2;

                }while(temp != 0);
                tempnum = (tempnum & (check >> 8)) & 0xFF;

                while(1)
                {
                        temp = 0x80;

                        while(!(temp & tempnum))//test(esi, edi)
                        {
                                temp >>= 1;

                                if(!temp) break;
                        }
                        if(!temp) break;

                        tempnum ^= temp;                //去除左边第一个1

                        /*以下三句对应汇编代码*/
                        //xchg    esi, ecx
                        //xor     ch, cl
                        //xor     esi, ecx
                        //xchg    ecx, esi
                        check &= 0xFF00;
                        temp = temp ^ ((temp & 0xFF) << 8);
                        check ^= temp;

                        common++;
                        Func2();
                }
        }
}

分析可知

Func2输入:eax, ebx (即num1,num2)

Func2输出:[402182](用于Func3运算),ecx(判断是否为1,完成关键跳转一00401118)

(三) 00401120关键函数三分析

Func3比较简单,整个流程为:

    user[9~12] xor user[13~16];
    result xor [402182];
    result or  0x40404040 and 0x77777777;
    result xor code[9~12] xor code[13~16];

对应汇编代码如下:

00401120  |> \A1 68214000   mov     eax, dword ptr [402168]
00401125  |.  8B1D 6C214000 mov     ebx, dword ptr [40216C]
0040112B  |.  33C3          xor     eax, ebx
0040112D  |.  3305 82214000 xor     eax, dword ptr [402182]
00401133  |.  0D 40404040   or      eax, 40404040
00401138  |.  25 77777777   and     eax, 77777777
0040113D  |.  3305 79214000 xor     eax, dword ptr [402179]
00401143  |.  3305 7D214000 xor     eax, dword ptr [40217D]
00401149  |.^ 75 CF         jnz     short 0040111A                   ;  验证失败
0040114B  |.  E8 2B000000   call    0040117B                         ;  验证成功,显示对话框
00401150  \.  C3            retn

相应C语言实现如下:

//Func1相关
unsigned char user[17] = "CCDebugerCCDebug";
unsigned char code[17] = "7878787878787878";

//Func2相关
unsigned int common = 0x0FEDCBA98;        //addr: 0x402182

unsigned int Func3(void)
{
        unsigned int temp1, temp2;                //eax/ebx
        unsigned int result;                        //最终判断是否为零

        temp1 = *(unsigned int *)(user + 8);
        temp2 = *(unsigned int *)(user + 12);

        result = (temp1 ^ temp2) ^ common;
        result = (result | 0x40404040) & 0x77777777;

        result = result ^
                (*(unsigned int *)(code + 8)) ^
                (*(unsigned int *)(code + 12));

        return result;
}

分析可知

Func3输入:user,code的后8字节,[402182]

Func3输出:result(判断是否为0)

具体就是以上这些,如有错误希望大家斧正。附件包括:CrackMe程序,算法分析TXT文档,以及我所实现的实验程序(VS2008编译通过)。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回