逆向破解之160个CrackMe —— 003
程序信息:
编号 | 制作者 | 保护方式 |
---|---|---|
003 | Afkayas | Nag,Name/Serial |
工具:
-
ollyDbg(简称OD)
-
Exeinfo PE
开始破解:
1.先用Exeinfo PE查看下程序(打开工具,将程序拖拽进去即可查看),得到信息如下图,可知该程序没有加壳,而且是用VB写的
2.接下来我们运行下程序,发现程序会首先弹出个窗口显示一定时间(像这种无聊且烦人的窗口我们称之为nag窗口),然后就是对用户名与序列号进行验证,验证错误会弹出提示对话框,同时用户名不能为空,序列号只能是数字,不然程序会报运行时错误。
3.接下来我们先去掉nag窗口,这个nag窗口真的是单步跟踪了半天还是不知道怎么改,最后在网上搜索了下关于这个CrackMe的分析,提到了两种办法,一种是Timer搜索法(Timer是VB程序默认的定时器变量),另一种是4C法,但是两种方法使用都有局限性,试图尝试着有没有通用性的办法,在看雪论坛上找到了一篇,试了试还是找不到(还是太菜了)。那么就说说上面两种方法吧(其实还有其他的办法,不过需要用到其它工具就不说了)
4.用OD加载程序,然后在数据窗口区定位到程序开始处0x401000,右键->查找->二进制字符串(Ctrl+B),搜索Timer
5.我们找到Timer后 ,修改030b后面两个机制就可以,0x1b58十进制就是7000,定时器单位是毫秒,7000毫秒就是7秒,我们将其改为0x0001,这样nag窗口就会以极快的速度运行过(看不到即不存在定理)。但是如果制作者将定时器的名称加密改掉,就无法在内存中搜索到Timer关键字,也就无法使用此方法
6.下面再用4C法,操作起来也是很简单,我们这里也只说操作(原理目前我也不知道),VB程序有个特点,入口处都是一个PUSH指令,然后一个CALL指令。(如果你遇到的不是这种情况的话,那么该程序可能被加过壳),PUSH将要压入堆栈的是0x4067D4,我们在数据窗口定位到该值与0x4C的和(即0x4067D4+0x4C)
7.这里我们可以看到0x4067D4+0x4C处前四字节为0x406868,我们在数据窗口中定位到0x406868这个地址,如图,选中这四个字节,右键->数据窗口跟随DWORD,即可定位到
8.我们只需要把第一个窗体数据源块的00改为10,第二个窗体的数据块的10改成00,或者将窗体的序号调换,这样首先弹出的就是主窗口了,然后才是nag窗口。而主窗口关闭后,应用程序就退出了,nag窗口没有机会弹出,这里不得不说下这个程序,点右上角的X按钮,是不会真正结束程序的,只有点Cancel按钮才会真的结束。
9.到此nag窗口就算完了,下一步就是破解算法了,还是字符串搜索,其实差不多和CrakeMe002一样的,毕竟制作者是同一个人
10.那我们直接向上找到头部,到头部下断点,然后一路F8,注意观察右上角寄存器与右下角栈区,发现输入的用户名或者序列号就停下来分析
11.当程序运行到0x4081F2,EAX中的值指向了用户名,我们停下来上下分析,这一段代码作用是获取用户名长度,然后将用户名长度与0x15B38相乘,再加上用户名第一位字符对应的ASCII,最后将这个结果转换为十进制,此处我的为622336
12.下面一段是vb程序自身的一些操作,忽略,同样F8,观察寄存器与栈区,直到出现我们刚刚的计算结果
13.停下来,分析此处上下文,这段代码是将之前的结果加2,变成622338
14.同12步,然后再次分析,这段代码是将之前的结果与3相乘再减去2,变为1867012
15.同样的办法来到0x4084DC处,这段代码是将之前计算的结果加上15,变为1867027
16.继续单步F8,到0x4085D1处出现了输入的序列号,停下来分析,这段代码就是将我们输入的序列号与刚刚计算得到的结果进行比较,这里用的浮点数进行的比较而且是用的除法,结果为1就是相等,给ESI赋值为1,不相等,ESI赋值为0
17.下面先对ESI进行求补运算(用0减去ESI的值再将结果给ESI),就判断SI的值,如果为0,则弹出错误提示框,不为0,弹出正确提示框
18.至此,算法分析就结束了,我们就可以自己写个程序算出序列号了,还是用C++写的:
#include <stdio.h> #include <stdlib.h> #include <string> int main() { char name[20]; printf("请输入用户名:\r\n"); //获取用户名 scanf_s("%s", &name, 20); //获取用户名的长度 int len = strlen(name); //用户名长度 * 0x15B38 len *= 0x15B38; //加上用户名第一位字符对应的ASCII + 2 len = len + name[0] + 2; //乘以3再减去2最后加上15 len = (len * 3) - 2 + 15; printf("序列号:%4d\r\n", len); system("pause"); }
19,最后上效果图: