下面是网上的160个CrackMe的部分逆向笔记,包括逆向思路、注册机实现和逆向中常用的知识整理
注意:逆向前一定要先操作一下软件,熟悉软件的运行现象;逆向一定要自己操作一遍,看是很难看会的(高手除外)
这个程序和CrackMe006是同一个作者,可以理解成是CrackMe006
的升级版,难点是if {A} else {B}
流程中{A}
和{B}
都要执行一次,后面会说明为什么都要执行
1.现象
对比会发现这个程序只需要使注册按钮隐藏即可(实际上隐藏完注册按钮还会有其他按钮显示出来,这个在开始逆向时不用关心)
2.程序基本信息
用CrackMe006
一样的方法,发现程序使用Delphi
语言编写,无壳
针对这个程序,寻找注册函数有2种方法:
-
1.字符串方式:这个程序密码不输入或者输入全是数字,会有对话框弹出错误原因,根据对话框里面的字符串在OD中很容定位到注册函数
-
2.用工具
Darkde4
分析程序,可以直接定位到注册函数(优先推荐这种方式,更通用)
下图是用Darkde4
分析的结果的部分截图,主要是找到按钮关联的事件和RVA
可以看到程序实际上有4个按钮,按钮和响应事件的对应关系已经标识出来
建议:分析注册按钮前有2点建议,这2点建议也适用于逆向其他程序
建议1:充分利用IDA
如果使用OD作为主力软件分析程序:分析前用IDA加载程序,然后保存MAP文件,OD再加载MAP文件进行分析会提高效率
建议2:先爆破程序,再分析注册机
逆向程序时,要重点关注跳转语句,可以修改相应标志位或者nop掉相关代码,更改程序走向,即想办法先爆破出程序,然后才涉及到分析注册机的事(没有爆破就瞎分析,非常有可能陷入作者故意设计的冗余代码中无法自拔)
3.注册按钮分析
按钮分析
注册按钮响应函数的整体框架如下(IDA分析结果,变量被适当修改了)
详细点介绍:
-
逻辑A:密码
不是
纯数字才会进入这个逻辑,这个逻辑里最重要的是全局变量g_KEY
-
逻辑B:密码
是
纯数字才会进入这个逻辑,真正生成有用密码的逻辑 -
函数C:关键函数,函数的参数是
g_KEY
、用户名,密码;即真正的密码是由用户输入的用户名和错误的序列号共同决定的函数C的返回结果期望是非零
笔记中,最开始提到的CrackMe007升级的地方是
if {A} else {B}
流程中{A}
和{B}
都要执行一次,这里指的就是逻辑A
和逻辑B
都要执行
爆破:一定要进逻辑
B
第一次分析程序时,目的是为了爆破程序,通常都会忽略掉
逻辑A
,会发现逻辑B
中的函数C
才是注册按钮不显示的关键逻辑;注册机:一定要进逻辑
A
当想要写出注册机时,才会发现
函数C
的参数g_KEY
一直是零,导致函数C
结果一直是零;回溯函数调用会发现逻辑A
一定要进入给g_KEY
进行赋值,函数C
才有可能计算出正确的密码
注意:如果单纯是为了爆破程序,后面就不需要看了,因为已经找了关键函数(函数C),直接nop
关键代码就可以
逆向整个程序思路
为了逆向整个程序,经上面的分析,要先用非数字密码计算一次g_KEY
,然后再用纯数字密码计算一次真正的密码才可以
下面是OD的分析截图,直观看可能没有IDA的显示效果友好,但这个因人而异,毕竟只是一个工具而已
4.注册机
第一次逆向这个程序写注册机,应该是先分析函数C
,然后是逻辑A
的流程,下面简单分析
注意:如果可以的话,最好用IDA调试一下,这样分析的效率会提高
key 1.函数C
下面是函数C
的处理逻辑的主要框架
简单说明,分成用户名和密码处理这2个部分:
用户名处理
- 外层
A
:从左到右,每次取用户名的一个字符,记为X
- 内层
B
:从右向左,每次取用户名的一个字符,记为Y
,累加和sum = g_KEY * X * Y
因此,用户名处理后的总结果有如下公式:res = (g_KEY * X * Y)% 666666
密码和比较逻辑
-
密码处理:
临时密码 = 密码 % 80 + 密码 / 89 + 1
-
运算
C
:要求用户名计算结果res与临时密码相等//最终要求如下: res == pwd_num % 80 + pwd_num /89 + 1 //如果知道res,就可以反推出pwd_num,即通过用户名得到正确的密码 //求模运算有类似的公式:a % b = a -(a / b)* b,逆向本程序不用这个公式,直接枚举就可以
通过用户名计算公式res = (g_KEY * X * Y)% 666666
会发现g_KEY
一定不能为零,g_KEY
是函数参数,由外层传入,因此要分析外层的处理逻辑
外层循环只要保证密码是非数字即可,因此分析一下逻辑A
的处理流程,看看是怎么得到g_KEY
的?
key 2.逻辑A
逻辑A中给g_KEY
赋值的主要逻辑如下:
函数参数是用户输入密码password
,先进行了一个for循环,然后对结果进行了取余数运算
因此,当密码长度符合要求时,g_KEY
有类似下面的处理逻辑
int res = 0x37B;
int g_KEY = 0;
for (int i = 1; i <= strlen(password) - 1; i++) {
res += password[i-1] * (password[i] % 0x11 + 1);
}
g_KEY = abs(res % 0x7148);
真正的密码是由用户输入的用户名和错误的序列号共同决定的
key 3.注册机
根据上面的分析,写出如下简单的注册机
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char pwd[100] = {0};
char user_name[100] = {0};
printf("请输入用户名(长度>5):");
scanf_s("%s", user_name, 100);
printf("请输入密码(必须是非纯数字,且长度>6):");
scanf_s("%s", pwd, 100);
//计算g_KEY
int res = 0x37B;
int g_KEY = 0;
for (int i = 1; i <= strlen(pwd) - 1; i++) {
res += pwd[i - 1] * (pwd[i] % 0x11 + 1);
}
g_KEY = abs(res % 0x7148);
//用户名,密码,g_KEY共同起作用,计算最终密码
int sum = 0;
for (int i = 1; i <= strlen(user_name); i++) {
for (int j = strlen(user_name); j >= 1; j--) {
sum += g_KEY * user_name[j - 1] * user_name[i - 1];
}
}
res = (int)abs(sum) % 666666;
//反向计算密码(枚举)
int key = INT_MIN;
for (key = INT_MIN; key <= INT_MAX; key++) {
if (res == key % 80 + key / 89 + 1)
break;
}
printf("用户名:%s, 对应的密码是%d\n", user_name, key);
system("pause");
return 0;
}
5.验证结果
验证整体流程有点繁琐,因此,简单整理如下:
- 图1:任意用户名 + 非纯数字的密码,更新全局变量
g_KEY
;示例:test123/abcdefg
,会有一个错误弹窗,不用管 - 图2:用户名 + 注册机生成的密码(会用到上一步骤的
g_KEY
);示例:test123/23632399
,注册按钮消失,出现了一个again
按钮 - 图3:将图1和图2的输入再输入一遍,
again
按钮也消失了,逆向成功
此时细心会发现图3中again
按钮还没有逆向呢???怎么就逆向成功了
下面是again
按钮的反编译代码,跟注册函数的逻辑是一样的,因此注册函数逆向成功,再输入一遍参数,again
按钮也会被逆向成功
CrackMe007逆向操作很容易找到关键点,但是要描述清楚写成笔记却浪费了一些时间
6.参考
- 1.VB程序逆向反汇编常见的函数 - 笨笨D幸福 - 博客园 (cnblogs.com)
- 2.常用软件可以在这里下载:爱盘 - 最新的在线破解工具包 (52pojie.cn)
- 3.【反汇编练习】160个CrackME索引目录1~160建议收藏备用
- 4.160个Crackme_鬼手56的博客-CSDN博客
- 5.《使用OllyDbg从零开始Cracking》系列的文章也不错