工具安装

160个CrackMe的打包文件下载地址:https://www.lanzoub.com/ipwFHha3bbcc 密码:52pj

win10虚拟机:VMware中安装win10教程

IDA pro7.2:逆向分析学习小纪——IDA Pro工具的安装与基本使用

ollydby1.0 :OllyDbg下载与安装

程序分析

选择CrackMe–001的 Acid burn.exe 进行逆向分析

首先运行程序,看看程序的功能

可以看到程序提供了两种登陆方法,分别是:通过序列号和用户名验证,和单独通过序列号验证

image-20240401171549838

选择serial登陆模式,随便输入,弹窗显示验证失败

image-20240402112538216

serial+name登陆方式也一样。

破解的思路如下:

因为程序后台肯定是由一个正确的序列号的(不管是代码写死的,还是动态生成的,反正在点击验证按钮的时候,这个序列号肯定是生成好的)。这里出现了对话框,说明作者在校验注册码之后发现如果错误了就直接弹窗,所以只要找到弹出对话框的代码处,向上跟踪,就可以找出判断是否正确的地方了,从而进一步破解程序。

逆向分析

脱壳

用Exeinfo PE工具查看该程序,得到信息如下图,可知该程序没有加壳,而且是用Delphi语言编写的。所以并不需要进行脱壳

image-20240401113359966

爆破

接下来对程序进行爆破,修改代码使得输入任何子串都提示正确或成功;

首先将程序放到OD中,Debug –> Run运行程序

选择serial+name登陆模式,随便输入,弹窗显示验证失败

image-20240401171710770

保持 Try Again 的窗口不要关,然后切换到 OD 界面,按F12暂停程序,进入调试状态。

点击 Window –> 7 call stack of main thread ,或者直接点击工具栏中的 K 按钮,查看线程堆栈,额可以看到我们选中的这行应该就是弹窗对应的 MessageBoxA函数

image-20240401173244715

选中这一行,右键选择show call跳转到对应的汇编代码出

跳转到的选中行就是反汇编之后,程序中控制弹出提示框的语句。

image-20240401173704942

所以,序列号验证的逻辑肯定就在这句前面不远处。

往上翻,我们找到了 push ebp 这句代码,这句代码在汇编语言中,一般在程序入口会出现,在 42A193 这里 F2 打个断点

image-20240401191338213

重新运行程序(直接点击运行按钮或鼠标选中后按F9)

重新点击 Check it baby 按钮,在右下角堆栈处找到定位语句最近的一条Return 语句:

image-20240401202513876

右键 Follow in Disassm..(反汇编跟随),这里直接连接了一个跳转,往下面翻,可以看到有这一段JNZJMP比较后跳转的两部分代码 Jnz 则进入Good job Dude, 否则就进入JMP Sorry

image-20240401202933245

选中JNZ语句,右键选择 Fill with NOPs,通过nops(空指令填充)替换JNZ

image-20240401203349476

重新执行程序,仍然是输入 aa ,发现出现了验证失败弹窗,往前翻,发现在执行选择之前有一个审查,如果serial小于4直接验证失败

image-20240402000609854

所以我们再重新执行,输入 aaaa,可以看到成功绕过

image-20240401212619104

分析序号生成算法

从这里的反汇编部分代码,可以看到用户名的计算算法流程, 首先取出用户名的第一位,乘以0x29。然后再乘以2。保留结果。假设结果为16BE,也就是十 进制的5822

image-20240402000725167

接着,将计算的结果转为字符串,然后和实现准备好的字符串进行拼接,结果为—— CW-5822-CRACKED 这个就是序 列号,也就是说序列号是根据用户名动态生成的。

image-20240402001450319

注册机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def main():
# 密码
key1 = "CW"
key2 = "CRACKED"
# 输入用户名
username = input("请输入用户名 长度必须大于等于4: ")
# 判断长度
if len(username) < 4:
print("长度必须大于等于4,请重新输入\n")
return
# 根据用户名生成密码
# 计算用户名的第一位
username1 = ord(username[0])
result1 = (username1 << 0x3) - username1 # 左移0x3位再减去原来的值
# 计算用户名第二位
username2 = ord(username[1])
result1 = (username1 << 0x4) + result1 # 左移4位再加上原来的结果
# 计算用户名的第四位
username4 = ord(username[3])
result2 = username4 * 0xB # 乘以0xB
# 计算用户名的第三位
username3 = ord(username[2])
result2 = (username3 * 0xE) + result2 # 乘以0xE再加上第四位的结果
# 再次计算第一位
result3 = username1 * 0x29 * 2 # 乘以0x29再乘以2
# 将result3转为ASCII 并拼接密钥
key = f"{key1}-{result3}-{key2}"
# 打印key
print(f"密钥为:{key}")

if __name__ == "__main__":
main()

运行程序,随便输入一个用户名

image-20240402112453492

程序根据输入的用户名算出了一组序列号,输入到目标程序里,成功进入

image-20240402112440883

参考文章

CrackMe 实战】逆向破解实战之 001 Acid burn.exe