Thend 发表于 2013-12-18 13:49

【吾爱2013CM大赛解答】-- White Decrypt & CrackMe -- White 算法详细分析+还原


[文章标题]: 【吾爱2013CM大赛解答】-- White Decrypt & CrackMe -- White算法详细分析+还原
[作者信息]: 0n1y3nd
[操作平台]: Win7 Sp1 & Windows XP Sp3
[使用工具]: OD
[软件名称]: White Decrypt & CrackMe -- White
[下载地址]: 【吾爱2013CM大赛题目】-- White Decrypt & CrackMe -- White   http://www.52pojie.cn/thread-228419-1-1.html
正文:
查壳,无壳,MFC写的程序。
运行下,随意输入,提示不能为空,提示字符串长度必须为23.
载入OD,直接查找MFC的按钮事件特征码:FF 55 14 EB 7F FF 75 0C,下好断点。
输入一组测试数据:12345678901234567890123和abcdefghijklmnopqrstuvw
断下,F7跟进:
00401460/.55            push ebp                              ;按钮事件call
00401461|.8BEC          mov ebp,esp
00401463|.83EC 10       sub esp,0x10
00401466|.53            push ebx
00401467|.56            push esi
00401468|.57            push edi
00401469|.8BF1          mov esi,ecx
0040146B|.6A 01         push 0x1
0040146D|.E8 22B80000   call White_De.0040CC94                ;取注册码
00401472|.68 C8000000   push 0xC8
00401477|.E8 C28A0000   call White_De.00409F3E
0040147C|.8BD8          mov ebx,eax
0040147E|.68 C8000000   push 0xC8
00401483|.895D F8       mov ,ebx
00401486|.E8 B38A0000   call White_De.00409F3E                ;取用户名
0040148B|.8B8E D4000000 mov ecx,dword ptr ds:
00401491|.8BF8          mov edi,eax
00401493|.8B86 D8000000 mov eax,dword ptr ds:
00401499      83C4 08       add esp,0x8                           ;eax存注册码ecx存用户名
0040149C|.8B49 F8       mov ecx,dword ptr ds:      ;ecx是用户名长度
0040149F|.33D2          xor edx,edx
004014A1|.8B40 F8       mov eax,dword ptr ds:
004014A4|.894D FC       mov ,ecx                     ;eax是注册码长度
004014A7|.85C9          test ecx,ecx                        ;是否为空
004014A9|.0F94C2      sete dl
004014AC|.33C9          xor ecx,ecx
004014AE|.897D F0       mov ,edi
004014B1|.85C0          test eax,eax                        ;是否为空
004014B3|.0F94C1      sete cl
004014B6|.0BD1          or edx,ecx
004014B8|.74 1A         je XWhite_De.004014D4
004014BA|.6A 10         push 0x10
004014BC|.68 28614100   push White_De.00416128                ;错误
004014C1|.68 14614100   push White_De.00416114                ;请输入用户名和密码
004014C6|.8BCE          mov ecx,esi
004014C8|.E8 6BB00000   call White_De.0040C538                ;提示 不能为空
004014CD|.5F            pop edi
004014CE|.5E            pop esi
004014CF|.5B            pop ebx
004014D0|.8BE5          mov esp,ebp
004014D2|.5D            pop ebp
004014D3|.C3            retn
004014D4|>8B4D FC       mov ecx,
004014D7|.33D2          xor edx,edx
004014D9|.83F9 17       cmp ecx,0x17                        ;比较注册码长度
004014DC|.0F9CC2      setl dl
004014DF|.33C9          xor ecx,ecx
004014E1|.83F8 17       cmp eax,0x17                        ;比较用户名长度
004014E4|.0F9CC1      setl cl
004014E7|.0BD1          or edx,ecx
004014E9|.74 1A         je XWhite_De.00401505
004014EB|.6A 10         push 0x10
004014ED|.68 28614100   push White_De.00416128                ;错误
004014F2|.68 00614100   push White_De.00416100                ;字符串长度等于23
004014F7|.8BCE          mov ecx,esi
004014F9|.E8 3AB00000   call White_De.0040C538                ;提示长度必须是23位
004014FE|.5F            pop edi
004014FF|.5E            pop esi
00401500|.5B            pop ebx
00401501|.8BE5          mov esp,ebp
00401503|.5D            pop ebp
00401504|.C3            retn
00401505|>8B86 D4000000 mov eax,dword ptr ds:       ;用户名存入eax
0040150B|.50            push eax
0040150C|.57            push edi
0040150D|.E8 EC0D0000   call White_De.004022FE
00401512|.8B96 D8000000 mov edx,dword ptr ds:       ;注册码存入edx
00401518|.52            push edx
00401519|.53            push ebx
0040151A|.E8 DF0D0000   call White_De.004022FE                ;返回注册码长度
0040151F|.8B86 D4000000 mov eax,dword ptr ds:       ;用户名存入eax
00401525|.83C4 10       add esp,0x10
00401528|.33C9          xor ecx,ecx
0040152A|.8078 04 65    cmp byte ptr ds:,0x65      ;用户名第5位 和 e 比较必须相等
0040152E|.0F95C1      setne cl
00401531|.33D2          xor edx,edx
00401533|.8038 57       cmp byte ptr ds:,0x57            ;用户名第1位 和 W 比较必须相等
00401536|.0F95C2      setne dl
00401539|.0BCA          or ecx,edx
0040153B|.74 1A         je XWhite_De.00401557
0040153D|.6A 10         push 0x10
0040153F|.68 28614100   push White_De.00416128                ;错误
00401544|.68 F4604100   push White_De.004160F4                ;错误字符串
00401549|.8BCE          mov ecx,esi
0040154B|.E8 E8AF0000   call White_De.0040C538
00401550|.5F            pop edi
00401551|.5E            pop esi
00401552|.5B            pop ebx
00401553|.8BE5          mov esp,ebp
00401555|.5D            pop ebp
00401556|.C3            retn
00401557|>83C9 FF       or ecx,0xFFFFFFFF
0040155A|.33C0          xor eax,eax
0040155C|.F2:AE         repne scas byte ptr es:
0040155E|.F7D1          not ecx
00401560|.83C1 FE       add ecx,-0x2
00401563|.8BFB          mov edi,ebx
00401565|.894D FC       mov ,ecx
00401568|.83C9 FF       or ecx,0xFFFFFFFF
0040156B|.F2:AE         repne scas byte ptr es:
0040156D|.F7D1          not ecx
0040156F|.83C1 FE       add ecx,-0x2
00401572|.894D F4       mov ,ecx
00401575|.60            pushad
00401576|.61            popad                                 ;下面是第一次循环处理
00401577|.8B4D F4       mov ecx,                     ;ecx 存注册码的长度 -1
0040157A|.8B45 F8       mov eax,                     ;eax存入注册码
0040157D|.03C1          add eax,ecx                           ;用eax + 长度减1就是eax取注册码最后一位
0040157F|.8D51 01       lea edx,dword ptr ds:
00401582|>8A48 FF       /mov cl,byte ptr ds:         ;cl 中 存入 eax-1也就是现在字符前一位的字符
00401585|.8A18          |mov bl,byte ptr ds:             ;bl中存入 eax 也就是最后一个
00401587|.32D9          |xor bl,cl                            ;也就是说 注册码的后一位与前一位亦或操作 结果放入bl
00401589|.8ACB          |mov cl,bl                            ;亦或后的值 传给cl
0040158B|.8818          |mov byte ptr ds:,bl             ;替换掉原始数据后面的那一位
0040158D|.0FBE58 FF   |movsx ebx,byte ptr ds:      ;取倒数第二位放入EBX
00401591|.0FBEC9      |movsx ecx,cl
00401594|.03D9          |add ebx,ecx                        ;倒数第二位 + 异或得到的值
00401596|.48            |dec eax
00401597|.4A            |dec edx
00401598|.^ 75 E8         \jnz XWhite_De.00401582
0040159A|.8B7D F0       mov edi,                     ;用户名存入 edi
0040159D|.8B55 FC       mov edx,                     ;用户名长度存入 edx
004015A0|.33F6          xor esi,esi                           ;循环计数器清零
004015A2|.8D043A      lea eax,dword ptr ds:
004015A5|>8A48 FF       /mov cl,byte ptr ds:         ;倒数第二位存入 cl
004015A8|.8A10          |mov dl,byte ptr ds:             ;倒数第一位 存入 dl
004015AA|.32D1          |xor dl,cl                            ;倒数第一和第二位异或 存入dl
004015AC|.8810          |mov byte ptr ds:,dl             ;异或后的值 替换倒数第一位
004015AE|.0FBE0C3E      |movsx ecx,byte ptr ds:
004015B2|.0FBED2      |movsx edx,dl
004015B5|.03CA          |add ecx,edx
004015B7|.8B55 FC       |mov edx,
004015BA|.46            |inc esi
004015BB|.48            |dec eax
004015BC|.3BF2          |cmp esi,edx
004015BE|.^ 76 E5         \jbe XWhite_De.004015A5
004015C0|.8B45 F8       mov eax,                     ;注册码替换完成之后新的注册码 存入 eax
004015C3|.23CB          and ecx,ebx
004015C5|.51            push ecx                              ;push了 and ecx,ebx
004015C6|.57            push edi                              ;edi是新的用户名的地址
004015C7|.50            push eax                              ;eax是新的注册码的地址
004015C8|.E8 13000000   call White_De.004015E0                ;很重要的call
004015CD|.83C4 0C       add esp,0xC
004015D0|.5F            pop edi
004015D1|.5E            pop esi
004015D2|.5B            pop ebx
004015D3|.8BE5          mov esp,ebp
004015D5|.5D            pop ebp
004015D6\.C3            retn

上面处理了输入的用户名和注册码。
从用户名的最后一位开始,与前一位异或,替换本身。第一位和0x08异或。
注册码做同样的处理。

跟进那个最重要的call里面去:004015C8 |. E8 13000000 call White_De.004015E0
004015E0/$51            push ecx
004015E1|.53            push ebx                              ;ebx貌似是注册码的第一位
004015E2|.55            push ebp
004015E3|.56            push esi                              ;注册码长度
004015E4|.57            push edi                              ;新的用户名的地址
004015E5|.6A 32         push 0x32                           ;push 0x32
004015E7|.E8 52890000   call White_De.00409F3E
004015EC|.6A 64         push 0x64
004015EE|.8BE8          mov ebp,eax
004015F0|.E8 49890000   call White_De.00409F3E
004015F5|.8BF8          mov edi,eax
004015F7|.B9 19000000   mov ecx,0x19
004015FC|.33C0          xor eax,eax
004015FE|.897C24 18   mov dword ptr ss:,edi
00401602|.F3:AB         rep stos dword ptr es:
00401604|.B9 0C000000   mov ecx,0xC
00401609|.8BFD          mov edi,ebp
0040160B|.F3:AB         rep stos dword ptr es:
0040160D|.66:AB         stos word ptr es:
0040160F|.B9 08000000   mov ecx,0x8
00401614|.BE B0604100   mov esi,White_De.004160B0
00401619|.8BFD          mov edi,ebp
0040161B|.33C0          xor eax,eax
0040161D|.F3:A5         rep movs dword ptr es:,dword ptr>;向上面申请的地址空间 填充数据
0040161F|.8B7424 20   mov esi,dword ptr ss:       ;esi存入新的注册码
00401623|.83C9 FF       or ecx,0xFFFFFFFF
00401626|.8BFE          mov edi,esi                           ;edi也是新的注册码
00401628|.83C4 08       add esp,0x8
0040162B|.F2:AE         repne scas byte ptr es:
0040162D|.8B5424 1C   mov edx,dword ptr ss:       ;edx存入新的用户名
00401631|.F7D1          not ecx
00401633|.49            dec ecx                               ;not ecx 然后 dec ecxecx中得到长度
00401634|.8BFA          mov edi,edx                           ;edi中存入新的用户名
00401636|.8BD9          mov ebx,ecx                           ;长度 存入 ebx
00401638|.83C9 FF       or ecx,0xFFFFFFFF
0040163B|.F2:AE         repne scas byte ptr es:          ;edi指向一个新的地址
0040163D|.F7D1          not ecx                               ;esi 存的 注册码的地址
0040163F|.49            dec ecx                               ;ebp 存的 初始字符串的地址
00401640|.894C24 18   mov dword ptr ss:,ecx       ;edx 存的 用户名的地址
00401644|>8A0C30      /mov cl,byte ptr ds:         ;取注册码的第一位 给cl
00401647|.300C28      |xor byte ptr ds:,cl         ;注册码第一位和初始字符串第一位 xor然后替换初始字符串对应位置
0040164A|.83F8 0A       |cmp eax,0xA                        ;第一次解码 只解0xA次   解码到第十一个字符
0040164D|.75 13         |jnz XWhite_De.00401662
0040164F|.8D7B F6       |lea edi,dword ptr ds:
00401652|.3BF8          |cmp edi,eax
00401654|.72 0C         |jb XWhite_De.00401662
00401656|>8A0C06      |/mov cl,byte ptr ds:      ;新注册码的第十一位 赋值 给cl
00401659|.304C06 01   ||xor byte ptr ds:,cl    ;注册码的第十二位 与 第十一位 异或 新值赋给 第十三位
0040165D|.40            ||inc eax
0040165E|.3BC7          ||cmp eax,edi
00401660|.^ 76 F4         |\jbe XWhite_De.00401656            ;循环处理更新 注册码的第十二位到第十四位 3位
00401662|>40            |inc eax
00401663|.83F8 0A       |cmp eax,0xA
00401666|.^ 76 DC         \jbe XWhite_De.00401644
00401668|.8B4C24 18   mov ecx,dword ptr ss:
0040166C|.B8 0A000000   mov eax,0xA                           ;从第十二位开始 解码 原始字符串
00401671|>8A1C10      /mov bl,byte ptr ds:         ;新用户名的第十一位 赋值 给bl
00401674|.301C28      |xor byte ptr ds:,bl         ;原始字符串第十一位和用户名第十一位 异或
00401677|.83F8 14       |cmp eax,0x14                         ;从第十一位解码到第二十一位还剩下5位没有解码
0040167A|.75 13         |jnz XWhite_De.0040168F
0040167C|.8D79 EC       |lea edi,dword ptr ds:
0040167F|.3BF8          |cmp edi,eax
00401681|.72 0C         |jb XWhite_De.0040168F
00401683|>8A1C02      |/mov bl,byte ptr ds:
00401686|.205C02 01   ||and byte ptr ds:,bl
0040168A|.40            ||inc eax
0040168B|.3BC7          ||cmp eax,edi
0040168D|.^ 76 F4         |\jbe XWhite_De.00401683
0040168F|>40            |inc eax
00401690|.83F8 14       |cmp eax,0x14
00401693|.^ 76 DC         \jbe XWhite_De.00401671
00401695|.8BCE          mov ecx,esi                           ;ecx 存入 注册码的地址
00401697|.8BC2          mov eax,edx                           ;eax 存入 用户名的地址
00401699|.2BCA          sub ecx,edx                           ;相 减 得到 28
0040169B|.BF 0B000000   mov edi,0xB
004016A0|>8A1C01      /mov bl,byte ptr ds:         ;注册码的第一位 赋值 给bl
004016A3|.3018          |xor byte ptr ds:,bl             ;用户名的第一位 异或 注册码的第一位然后更新用户名
004016A5|.40            |inc eax                              ;移动向 下一位
004016A6|.4F            |dec edi
004016A7|.^ 75 F7         \jnz XWhite_De.004016A0               ;一共更新了 十一位
004016A9|.8BFA          mov edi,edx                           ;更新之后的用户名 存入edi中esi中是注册码的地址\
004016AB|.8D46 0A       lea eax,dword ptr ds:      ;注册码第十一位赋值 给eax
004016AE|.2BFE          sub edi,esi
004016B0|.BE 0B000000   mov esi,0xB
004016B5|>8A1C07      /mov bl,byte ptr ds:         ;新的用户名的第十一位 赋值 给bl
004016B8|.2018          |and byte ptr ds:,bl             ;注册码的第十一位 and 用户名的第十一位更新注册码
004016BA|.40            |inc eax
004016BB|.4E            |dec esi
004016BC|.^ 75 F7         \jnz XWhite_De.004016B5               ;一直更新到第二十一位
004016BE|.8D42 14       lea eax,dword ptr ds:       ;用户名的第二十一位 赋值 给eax
004016C1|.BE 06000000   mov esi,0x6
004016C6|>8A1C08      /mov bl,byte ptr ds:         ;注册码的第二十一位 赋值 给bl
004016C9|.2018          |and byte ptr ds:,bl             ;用户名的第二十一位 and 注册码的第二十一位更新用户名
004016CB|.40            |inc eax
004016CC|.4E            |dec esi
004016CD|.^ 75 F7         \jnz XWhite_De.004016C6               ;从第二十一位 更新到第二十六位
004016CF|.8D45 14       lea eax,                     ;初始字符串身下的十位 存入eax
004016D2|.2BD5          sub edx,ebp
004016D4|.BE 06000000   mov esi,0x6
004016D9|>8A0C02      /mov cl,byte ptr ds:         ;新的用户名的第二十一位 赋值 给cl
004016DC|.8A18          |mov bl,byte ptr ds:             ;初始字符串的第二十一位 赋值 给bl
004016DE|.32D9          |xor bl,cl                            ;初始字符串第二十一位和用户名第二十一位 异或 更新字符串
004016E0|.8818          |mov byte ptr ds:,bl
004016E2|.40            |inc eax
004016E3|.4E            |dec esi
004016E4|.^ 75 F3         \jnz XWhite_De.004016D9               ;一直更新到第二十六位
004016E6|.8B7424 10   mov esi,dword ptr ss:
004016EA|.55            push ebp                              ;这儿就可以看到 把解码完成的字符串压栈了
004016EB|.68 38614100   push White_De.00416138                ;%s
004016F0|.56            push esi
004016F1|.E8 080C0000   call White_De.004022FE


算法不是很复杂,几个循环处理而已。

下面是我用C语言还原的算法部分的源代码:
#include <stdio.h>
#include <string.h>

void main()
{
        char UserName = "White_CrackMe_JUST_For_Fun";
        char PassWord = "Do_You_Like_It_Or_NOT__:)_";
       
        char MsgText = {0x1F, 0x44, 0x5D, 0x63, 0x42, 0x72, 0x43, 0x7D,
                0x42, 0x22, 0x4F, 0x55, 0x08, 0x5B, 0x79, 0x68, 0x67,
                0x7E, 0x78, 0x39, 0x63, 0x61, 0x61, 0x6F, 0x74, 0x7B,
                0x6E, 0x67, 0x20, 0x21};
        int i;

        UserName = '\0';
        PassWord = '\0';

        printf("用户名:%s\n",UserName);
        printf("注册码:%s\n", PassWord);
        //第一次处理用户名和注册码
        for (i=25; i>0; i--)
        {
                UserName = UserName ^ UserName;
                PassWord = PassWord ^ PassWord;
        }
        UserName = UserName ^ 0x08;
        PassWord = PassWord ^ 0x08;
        //第一次解码解到0xA处
        //注册码的第n位 和 原始字符串的第n位 异或
        for (i=0; i<11; i++)
        {
                MsgText = MsgText ^ PassWord;       //解码出 Something A
        }
        //更新注册码的第 12 13 14 位
        //注册码从第12位开始、与前一位异或,更新自身。
        for (i=11; i<14; i++)
        {
                PassWord = PassWord ^ PassWord;   //只更新了3个数据
        }
        //第二次解码 从第12位处解码到第21位
        //用户名的第n位 和 原始字符串的第n位 异或
        for (i=10; i<21; i++)   //从第11位开始的 解码到第21位   
        {
                MsgText = MsgText ^ UserName;
        }
        //更新用户名的第 1 到 11位
        //用户名的第n位 和 注册码的第n位 异或更新用户名
        for (i=0; i<11;i++)
        {
                UserName = UserName ^ PassWord;
        }
        //更新注册码的第 11 到 22位
        //注册码的 第n位 和 用户名的第n位 and操作 更新注册码
        for (i=10; i<21; i++)                                    
        {
                PassWord = PassWord & UserName;
        }
        //更新用户名的第 23到 26位
        //用户名的 第n位 和 注册码的第n位 and操作 更新用户名
        for (i=20; i<26; i++)                                       
        {
                UserName = UserName & PassWord;
        }

        //第三次解码 从第21位 解码到第26位
        //初始字符串第n位 和 用户名 第n位异或
        for (i=20; i<26; i++)
        {
                MsgText = MsgText ^ UserName;   
        }
        MsgText = '\0';

        printf("解码完成:%s\n", MsgText);
}

上面是根据提供的字符串做的一个验证:














brack 发表于 2013-12-18 13:52

感谢大牛发布详细分析/

黑夜伴影 发表于 2013-12-18 14:00

代码还原的很漂亮。

kooood 发表于 2013-12-18 14:00

牛逼啊。膜拜啊{:1_890:}

nizsm123 发表于 2013-12-18 15:07

看看学习下!

bambooqj 发表于 2013-12-18 15:08

Some 发表于 2013-12-18 16:13

分析过程很漂亮,赞一个。
页: [1]
查看完整版本: 【吾爱2013CM大赛解答】-- White Decrypt & CrackMe -- White 算法详细分析+还原