-
-
[求助]旧帖新读之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直播授课