原软件下载地址:http://crackmes.de/users/yo_mismo/crackme_v1/
注:软件下载原文说加壳了,结果下下来发现没壳。
分析成功了之后再看这些代码感觉简单了,但是在没分析清楚之前真是累得我头晕眼花。不断地尝试之后总算完成了,大牛就不要来拍我了。
OD载入很轻松就可以找到关键call。进入后我给予瓜分分为五个步骤:
0、对用户名、密码长度进行判定
1、对用户名前四个字符进行一次异或处理
2、对处理的四个字符进一步进行处理
3、将用户名+处理的字符(有个转化十进制的过程) == 新用户名
4、拿新用户名与密码比较。
下面进行详细分析:
0、对用户名、密码长度进行判定
strlen()然后比较,很简单就略过了。
1、对用户名前四个字符进行一次异或处理
00401302 |> /8A91 10404000 /mov dl, byte ptr [ecx+404010] // 取user [1-4]字符。404010==user;404020==pass
00401308 |. |8A81 13204000 |mov al, byte ptr [ecx+402013] // 取0X59 0X6F 0X2D 0X4D进行
0040130E |. |30D0 |xor al, dl **异或
00401310 |. |8881 30404000 |mov byte ptr [ecx+404030], al //将异或的值传递到pass的第[17-20]个字符
00401316 |. |41 |inc ecx //ecx循环四次
00401317 |. |83F9 04 |cmp ecx, 4
0040131A |.^\75 E6 \jnz short 00401302 //403030处为目前处理过后的字符串。
2、对处理的四个字符进一步进行处理
0040131C |. 68 30404000 push 00404030 ; /s = ""
00401321 |. E8 CA040000 call <jmp.&msvcrt.strlen> ; \strlen
00401326 |. 89C2 mov edx, eax ; //将4030处字符串长度放入edx
00401328 |. 31C0 xor eax, eax ; //eax清零
0040132A |. 31C9 xor ecx, ecx ; //ecx清零
0040132C |> /39D1 /cmp ecx, edx //循环四次
0040132E |. |74 0A |je short 0040133A
00401330 |. |66:0381 30404>|add ax, word ptr [ecx+404030] //[40301][40300] + [40302][40301] + [40303][40302]+[xx][0x40303]
00401337 |. |41 |inc ecx
00401338 |.^\EB F2 \jmp short 0040132C //结果就是eax中保存了处理后的值。
0040133A |> \69C0 66060000 imul eax, eax, 666 //eax *666 -》eax
00401340 |. C1E8 02 shr eax, 2 //EAX/4
00401343 |. C1C0 0E rol eax, 0E //循环左移14位
00401346 |. C1C8 14 ror eax, 14 //循环右移20位 ==> 此两步最终运算结果为将eax进行循环右移6位。
00401349 |. 6BC0 02 imul eax, eax, 2 //eax *2 -》eax 【溢出部分舍弃】
0040134C |. F7D0 not eax //取反
0040134E |. 05 99090000 add eax, 999 //eax+0x999h
【此处wsprintf的作用是将上文处理的eax的值(如今存放在404040中)转化为无符号十进制数,放入404050处】
00401353 |. A3 40404000 mov dword ptr [404040], eax ; |//将处理过后的eax值放到404040处
00401358 |. FF35 40404000 push dword ptr [404040] ; |/<%u> = 97F7B3D2 (2549593042.)
0040135E |. 68 22204000 push 00402022 ; ||Format = "%u"
00401363 |. 68 50404000 push 00404050 ; ||s = crack_No.00404050
00401368 |. E8 F3040000 call <jmp.&USER32.wsprintfA> ; |\wsprintfA
3、将用户名+处理的字符(有个转化十进制的过程) == 新用户名
【此处strcat是将输入的用户名+转化后的十进制字符串相连接。】
0040136D |. 68 50404000 push 00404050 ; |/src = "2549593042"
00401372 |. 68 10404000 push 00404010 ; ||dest = "AAAAAAAA"
00401377 |. E8 84040000 call <jmp.&msvcrt.strcat> ; |\strcat
4、拿新用户名与密码比较。
0040137C |. 68 10404000 push 00404010 ; |s1 = "AAAAAAAA"
00401381 |. C74424 04 204>mov dword ptr [esp+4], 00404020 ; |ASCII "BBBBBBBB"
00401389 |. E8 52040000 call <jmp.&msvcrt.strcmp> ; \strcmp
0040138E |. 83F8 00 cmp eax, 0
00401391 74 02 je short 00401395 ; //成功!
故可以推算出一组序列号:
AAAAA
AAAAA2549593042
注册机算法如下:
/*crack_1
*name cr_1_noname
*author houlen
*date 2011-4-18
*/
#include <stdio.h>
#define MAX 100
int main()
{
char user[MAX], str[MAX];
int i=0, cat[4], sum =0;
printf("User:");
scanf("%s",user);
if(strlen(user) <4 )
{
printf("the len of user is too low\n");
return 0;
}
cat[0] = (int)user[0]^0x59;
cat[1] = (int)user[1]^0x6f;
cat[2] = (int)user[2]^0x2d;
cat[3] = (int)user[3]^0x4d;
for(i=0; i<3; i++)
{
sum+= cat[i+1]*16*16+cat[i];
}
sum+= cat[3];
sum*= 0x666;
sum=sum>>2;
_asm
{
push eax;
mov eax,sum;
ror eax,6;
imul eax,eax,2
not eax;
add eax,0x999;
mov sum,eax;
pop eax;
}
sprintf(str, "%u", sum);
strcat(user, str);
printf("Serial: %s\n", user);
getchar();
return 0;
}
在分析这个crackme的过程中遇到的几个问题,可能新手都会有这类疑问,1、ror、rol是循环右移,左移。2、wsprintf函数在本软件作用是将数字转化为字符串。3、IA-32机的小头问题,别弄错了顺序。4、c语言中的异或操作符"^"移位“>>”.5、嵌入汇编注意保护寄存器
原软件包好像有源码,但是自始至终没看过它的源代码,所以分析起来很辛苦,但是分析出来也很有成就感。。哈哈
~
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!