前言
160个CrackMe 是新手学习逆向破解的CrackMe系列,我尝试一步步将160个CrackMe破解,帖子记录学习过程。
由于百度云现在分享不了这160个crackmed程序,说什么有敏感内容,如果需要,回复邮箱,我邮箱发过来。
这是我在看雪的第一个帖,若有错误,还望指正谢谢。
0x1.工具和环境
WinXP SP3 + 52专用OD + PEID
由于win7系统对于程序开启了地址随机化,会给分析带来不必要的麻烦,所以我的分析环境是WinXP SP3
0x2.程序分析
想要分析一个程序,首先,需要运行一下程序,这一步很重要,它帮助我们理解作者的意图以及为我们提供一些有用的信息,比如字符串,弹窗等等。
运行程序,发现程序的注册分为两种方式,一种是Serial/Name,用户需要输入用户名和注册码,另外一种是Serial,用户只需输入一个注册码。
方式二:
我们随便选一种方式,就比如选只要求用户输入注册码的看似稍简单的第二种方式:
随便输入一个注册码:999
点击Check it Baby!
弹出一个对话框提示:Try Again!
现在我们将其拖入exeinfo查壳,无壳,程序由Delphi写的。然后拖入ollydbg查看,搜索字符串Try Again!
这里面搜索出来两个Try Again!字符串,我们先查看第一个(我们的运气比较好,第一个Try Again!字符串就是我们要找的,第二种方式的Try Again!的字符串)
那么我们可以推测,第二个Try Again!字符串将会出现在第一种方式的报错当中
可以看到此处就是第二种注册方式的代码领空,这里面有注册成功后的congratz!和注册失败的Try Again!而且,这上面只有一个跳转,也就是jnz,
我们若是把jnz给nop掉,程序便可以直接走到注册成功的提示信息congratulatz!处,也出现了注册成功的提示信息,这样我们便实现了对程序的爆破。
同时,我们注意到栈当中有个字符串,Hello Dude!我们没有理由不怀疑,这就是注册码,拿来尝试一下,果不其然,这就是注册码,而且是不变的。
方式一:
现在我们来看看第一种方式如何破解这个程序。随便输入:
Name:99999
Serial:11111
点击Check it Baby! 弹出一个对话框:Sorry,The Serial is incorrect!
现在我们还是在od里面看看这个程序,搜索字符串 Sorry,The Serial is incorrect!我们可以看到程序当中有两个Sorry,The Serial is incorrect!字符串,
我们先去第一个字符串的代码领空看看:
我们将断点设在42FA4D然后单步运行程序,在42FA57时,EAX的值是5,也就是说,函数的返回值为5,然后再与4比较大小,5>4,jge跳转实现。
这个地方的字符串并没有输出!刚才输出的是第二个Sorry,The Serial is incorrect!字符串。奇怪,现在也不知道为什么,继续向下面单步运行。
在下面我们可以看到堆栈出现奇怪的字符串CW-4674-CRACKED,并且也出现了我们的Name:99999。我们完全有理由怀疑这个由CW-
4674和-CRACKED拼接而成的字符串CW-4674-CRACKED是注册码,并且4674是被运算出来的!!!尝试一下,我们的猜测完全正确
而且下面有个可疑的跳转,这个跳转跳过congratz!成功跳到输出第二个Sorry,The Serial is incorrect!字符串。
显然,42A170就是MessageBoxA函数了。
我们将这个jnz给nop掉,就可以将这个程序爆破了。
但是!不找出注册算法,我们不能称之为将这个程序破解了。而且我们之前还遗留了一个问题,在地址42FA57处,EAX的值是5,
这个5是什么意思?第一个Sorry,The Serial is incorrect!字符串。在什么情况下输出,有朋友可能会怀疑第二种注册方式会输出,
但是我们已经将第二种方式摸透了,并不会输出这个字符串,那么这个字符串看肯定就是在第一种注册方式当中输出的,
但是我们不知道什么情况下会触发输出这个字符串的判断机制。
找到注册算法
上面是爆破,下面我们来找注册算法,并且尝试解决我们之前的疑问。
重新运行这个程序,还是在将断点设在42FA4D然后单步运行程序,这次我们输入:
Name:111
Serial:222
我们一步一步看,好巧不巧,在函数406903处,我们的Name字符串被传入了函数,然后返回3,上一次我们输入99999返回的值是5,那么现在这个
函数的功能确定了:就是判断Name字符串的长度,如果字符串长度<4,那么对不起,弹出错误提示弹窗,我们之前推断的42A170应该是MessageBoxA
函数了。于是我们的之前的疑惑便解决了,现在安安心心找注册算法:
我们重新输入:
Name:99999
Serial:11111
然后依然下断在42FA4D然后单步运行:程序的注册算法肯定在jmp下面的某个函数当中,或者就在下面的代码
0042FA79 |> \8D55 F0 lea edx,[local.4]
0042FA7C |. 8B83 DC010000 mov eax,dword ptr ds:[ebx+0x1DC]
0042FA82 |. E8 D1AFFEFF call Acid_bur.0041AA58
0042FA87 |. 8B45 F0 mov eax,[local.4] ; EAX=99999的地址
0042FA8A |. 0FB600 movzx eax,byte ptr ds:[eax] ; 取出第一个字节9=0x39
0042FA8D |. F72D 50174300 imul dword ptr ds:[0x431750] ; *0x29
0042FA93 |. A3 50174300 mov dword ptr ds:[0x431750],eax ; eax=0x921=0x29*0x39
0042FA98 |. A1 50174300 mov eax,dword ptr ds:[0x431750]
0042FA9D |. 0105 50174300 add dword ptr ds:[0x431750],eax ; 加一倍
0042FAA3 |. 8D45 FC lea eax,[local.1]
0042FAA6 |. BA ACFB4200 mov edx,Acid_bur.0042FBAC ; CW
0042FAAB |. E8 583CFDFF call Acid_bur.00403708
0042FAB0 |. 8D45 F8 lea eax,[local.2]
0042FAB3 |. BA B8FB4200 mov edx,Acid_bur.0042FBB8 ; CRACKED
0042FAB8 |. E8 4B3CFDFF call Acid_bur.00403708
0042FABD |. FF75 FC push [local.1] ; Acid_bur.0042FBAC
0042FAC0 |. 68 C8FB4200 push Acid_bur.0042FBC8 ; -
0042FAC5 |. 8D55 E8 lea edx,[local.6] ; UNICODE "-"
0042FAC8 |. A1 50174300 mov eax,dword ptr ds:[0x431750]
0042FACD |. E8 466CFDFF call Acid_bur.00406718
0042FAD2 |. FF75 E8 push [local.6] ; 注册码中间部分4674
0042FAD5 |. 68 C8FB4200 push Acid_bur.0042FBC8 ; UNICODE "-"
0042FADA |. FF75 F8 push [local.2] ; Acid_bur.0042FBB8
0042FADD |. 8D45 F4 lea eax,[local.3]
0042FAE0 |. BA 05000000 mov edx,0x5
0042FAE5 |. E8 C23EFDFF call Acid_bur.004039AC
0042FAEA |. 8D55 F0 lea edx,[local.4] ; edx=0012F998 = 11111
0042FAED |. 8B83 E0010000 mov eax,dword ptr ds:[ebx+0x1E0] ; eax=00A85E4C
0042FAF3 |. E8 60AFFEFF call Acid_bur.0041AA58
0042FAF8 |. 8B55 F0 mov edx,[local.4]
0042FAFB |. 8B45 F4 mov eax,[local.3]
0042FAFE |. E8 F93EFDFF call Acid_bur.004039FC ; 出现CW-4674-CRACKED 字符串拼接函数
0042FB03 |. 75 1A jnz short Acid_bur.0042FB1F ; 这个跳转很可疑
0042FB05 |. 6A 00 push 0x0
0042FB07 |. B9 CCFB4200 mov ecx,Acid_bur.0042FBCC ; Congratz !!
0042FB0C |. BA D8FB4200 mov edx,Acid_bur.0042FBD8 ; Good job dude =)
0042FB11 |. A1 480A4300 mov eax,dword ptr ds:[0x430A48]
0042FB16 |. 8B00 mov eax,dword ptr ds:[eax]
0042FB18 |. E8 53A6FFFF call Acid_bur.0042A170
0042FB1D |. EB 18 jmp short Acid_bur.0042FB37
0042FB1F |> 6A 00 push 0x0
0042FB21 |. B9 74FB4200 mov ecx,Acid_bur.0042FB74 ; Try Again!
0042FB26 |. BA 80FB4200 mov edx,Acid_bur.0042FB80 ; Sorry , The serial is incorect !
0042FB2B |. A1 480A4300 mov eax,dword ptr ds:[0x430A48]
0042FB30 |. 8B00 mov eax,dword ptr ds:[eax]
0042FB32 |. E8 39A6FFFF call Acid_bur.0042A170
分析了一下了这段代码,我们有可以清楚的看到注册码是如何生成的:
取Name第一个字母对应的ASNI数字,比如说99999中第一个字符9对应数字0x39,然后用它乘以0x29,结果再x2,将16进制数字转为10进制的字符串便是4674,然后再在前加上”CW-”,后加上”-CRACKED”,就组成了用户名对应的注册码。
写出注册机
#include <iostream>
int _tmain(int argc, _TCHAR* argv[])ch
{
printf("Input Name:\n");
// 取第一个字符值h
int ch = getchar();
if ( ch > 0x21) // 只处理可见字符
{
ch *= 0x29; // 乘法
ch *= 2; // 自增一倍
printf("Serial: CW-%4d-CRACKED\n",ch);
}
else
{
printf("error!\r\n");
}
system("pause");
return 0;
}
直接搜索字符串,这只是我处理这个程序的思路,当然也有其他更好的思路来处理这个程序,
看到弹窗可以下MessageBox断点,然后再一个一个排查,也可以到达程序领空
看到个老哥的方法,觉得挺不错的:直接运行程序,最后不点击确定,然后再在od里面将程序暂停,alt+k查看堆栈调用,由于有弹窗,我们肯定是可以在堆栈调用里面看到MessageBox函数调用的,然后右键查看调用,然后再在调用函数的领空查看返回的地址,我们便可以到达目标代码领空。这种方法适合于搜索不到字符串的情况。
阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开
发者可享99元/年,续费同价!
最后于 2020-9-16 17:11
被VEhl编辑
,原因: