【吾爱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);
}
上面是根据提供的字符串做的一个验证:
感谢大牛发布详细分析/ 代码还原的很漂亮。 牛逼啊。膜拜啊{:1_890:} 看看学习下! 分析过程很漂亮,赞一个。
页:
[1]