1. 软件怎么判断我们是否注册了?
别忘了,软件终究是人类思维的产物。我们不妨换个角度想想:如果是你,你会怎么判断别人是否注册了?答案很直接:让用户输入用户名和注册码,然后核对一下。不少软件就是这么做的,比如豪杰超级解霸。当然,不是所有软件都这么直白,方法多得是。友情提示:这世上没有放之四海皆准的套路,除了你那颗聪明的大脑。
再具体一点呢?我们把用户名按照某种规则运算,得到一个“真正的注册码”,然后跟用户输入的做比较,不就成了?没错,就是:
真正的注册码 = f(用户名)
跟函数 Y = f(x) 是一回事。
接下来就是经典的比较环节了。为什么说“经典”?因为大约 60% 的软件都采用这种模式。具体怎么做?看看流程:
注意,这里会附带错误处理。正是因为有错误提示(比如“注册错误”之类),我们才能轻松定位到那个经典的比较点。
那么,上面的流程在汇编语言里怎么实现呢?比较有两种方式:直接和间接。
- 直接比较:
cmp x, y
je (jne) label
这里的 x、y 只是符号,实际可能是寄存器或存储器。 - 间接比较:调用一个子程序来比较,比如:
if (strcmp(&x, &y)) // 如果 strcmp 返回 1
printf("right");
else ... // 当然是错误啦
这里的 strcmp 也只是个符号,现实世界里可能有别的名字。
用汇编语言描述间接比较:
push &y;
push &x;
call strcmp;
test ax,ax ; 判断返回值(也就是出口参数,也可以在子程序内部判断)
je ...
2. 为什么可以调试可执行程序呢?
可能不少初学者都有过这个疑惑。调试可执行程序的理论基础其实很简单:机器只识别 0 和 1(准确说是高电平和低电平,你可以简单理解为灯泡亮和灭),这就是机器码。而汇编语言与机器码是一一对应的,所以我们可以根据机器码反推出对应的汇编代码,反过来也能把汇编代码编译成机器码。
比如在 debug 下:
-a
1370:0100 mov ax,bx
1370:0102
-u100
1370:0100 89D8 MOV AX,BX
这里的 89D8 就是 MOV AX,BX 的机器码。
3. 破解教程都告诉我们,找到错误提示上方的第一个有条件跳转,改掉就能爆破,为什么?
程序是顺序执行的。只要我们找到提示出错的地方,那么在此之前必定已经完成了比较。再往前找找,就能看到关键的比较语句。关键点就在这个比较上。如果我们把判断条件改一下——比如改成“不相等就注册成功”——那么……嘿嘿,不管输入什么都是“正版”了,没掏钱的“正版”。这就是我们修改跳转的原因,也就是改变判断条件的逻辑。
4. 下面我们开始实战演习
虽然这只是一个用 S-Demo 做的简单动画,但看完下面的内容,你照样能收获不少。不信?把简单的事情做到极致,你就成功了。
(1) 爆破
这部分很简单,不多说。运行程序,随便输入密码,确定,提示“password wrong”。用 OllyDbg 载入这个动画,查找程序中的字符串,找到 “password wrong”,下断点。还记得前面的理论吗?再向前找找就能找到比较的关键地方(条件跳转)。找到地址:0040203F。
0040202A |. FFB6 A4000000 PUSH DWORD PTR DS:[ESI+A4] ; /s2
下个断点,看看压入堆栈的是什么。
00402030 |. 8D45 CC LEA EAX,DWORD PTR SS:[EBP-34] ; |
00402033 |. 50 PUSH EAX ; |s1
00402034 |. FF15 E0234100 CALL DWORD PTR DS:[<&MSVCRT._stricmp>] ; \_stricmp
这里看到了什么?stricmp,这不就是传说中的关键比较吗?比较之后没有跳转?仔细看,比较的结果放在 EAX 里。下面不是有个 je 吗?
0040203A |. 83C4 20 ADD ESP,20 ;平衡堆栈
0040203D |. 85C0 TEST EAX,EAX ;测试返回值
0040203F |. 74 15 JE SHORT test.00402056
把它修改为 JNE,保存,运行。搞定!请你再次回顾前面的流程图。
(2) 寻寻觅觅找密码
还记得前文的流程图吗?如果在程序比较的时候中断程序,会有什么发现?没错,能看到真正的密码。重新用 OllyDbg 载入动画,在 00402034 下断点。为什么选这里?因为这里调用了 DWORD PTR DS:[<&MSVCRT._stricmp>]。看看流程图,明白了吗?在堆栈区域,你能看到我们输入的试炼码和真正的密码。为什么在堆栈区域?因为在 Windows 下,参数是通过堆栈传递的。
(3) 文件补丁的编写(C 语言简单实现)
前面我们把 je 改成了 jne,随便输入密码都能成功。实质就是把机器码从 74h 改成 75h。由于机器码和汇编指令一一对应,我们只需要写一个小程序来修改文件即可。我已经写好了,很短,很好懂。Crack.c,我们来分析一下。
#include
#include
int main(void)
{
FILE *fp_out; // 要写入的文件
printf("\n\t\t\t\t Copy Right by ngaut\n");
printf("Cracking......\n");
// 打开文件 test.exe
if ((fp_out = fopen("test.exe", "r+"))==NULL)
{
printf("error!!! Can not open test.exe!!!\n\n");
printf("Press any key to continue\n");
getchar();
exit(0);
}
// 定位到要修改的地方,这里是 0x203f,为什么呢?下面给出回答
fseek(fp_out, 0x203f, SEEK_SET);
fputc(0x75, fp_out); // 写入数据 0x75,也就是把机器码 74 改为 75,
// 汇编则是 je 改为了 jne
fclose(fp_out);
}
这里 0x203f = 0x0040203F - 0x00400000。
(4) 让程序自动弹出正确的密码
(此处可根据原文后续内容补充,但用户原文到此结束,故按原样保留章节标题,内容留空或等待用户补充。)
