第三课:第一次拆钟——找到那把钥匙
对应原书第 4 章《静态分析》完整工作流 + 第 5 章《动态分析》
第三天,陈师傅从柜台底下拿出一只"特别"的钟。它没有表盘,只有一堆齿轮,最中央挂着一把小小的铜锁。
"今天的功课:这把锁,钥匙是六个数字。你把它打开。"
小林翻来覆去看了看:"师傅,这……这不是钟吧?"
"是钟,也不是钟。"陈师傅说,"这是一只'会撒谎的钟'。它的齿轮都在正常转,但其中几个齿轮,是被'焊死'的——它们根本不是钟的一部分,是有人故意加进去的机关。我们的活儿,就是找出这几个'多余的齿轮',看看机关把真正的钥匙藏哪儿了。"
小林若有所思:"就像上一个程序?"
"对。上次我们用'听诊'找到了钥匙。这次它学乖了——它把钥匙藏起来了。听诊听不到,得拆。"
陈师傅把钟翻过来,露出齿轮背面刻着的一行小字:
ELF 64-bit LSB executable, x86-64, not stripped
"这是它的'出生证明'。"陈师傅指着小字,"写着:这是给 64 位电脑的程序,而且——没脱衣服。"
"没脱衣服?"
"意思是它的'零件标签'还在。你看——"
陈师傅敲了几个命令,屏幕上出现一长串齿轮名:
r2 -A 程序
[0x00400560]> afl
0x00400560 3 23 entry0
0x004005a6 4 99 sym.main ← 这是"大门"
0x004005c9 8 32 sym.check_key ← 这是"守卫"
0x004005e9 6 41 sym.decrypt ← 这是"密室"
"看到了吗?程序自己也标了名字:这个叫 main,是大门;这个叫 check_key,是守卫;这个叫 decrypt,是密室。"
"所以钥匙在守卫那儿?"
"去看看。"
[0x004005a6]> s sym.check_key
[0x004005c9]> pdf
屏幕上出现了守卫的"齿轮图"——一行行看不懂的符号。但陈师傅指着一行:
mov rsi, 0x0040052a ; ← 这里!守卫手里攥着一样东西
call strcmp ; ← 它要把你的输入和这个东西比一比
"看见了吗?守卫把'某个地址'的东西拿出来和你的输入比较。那个地址里放的就是钥匙。"
[0x004005c9]> psz @ 0x0040052a
19640807
小林凑近一看——六个数字:19640807。
他赶紧把钟上的锁转到 19640807。"咔哒"一声,锁开了。
课程正文
刚才你看到了逆向的完整"三件套"流程:
1. 听诊(strings / file) → 先听它说什么
2. 拆开(r2 -A / IDA / Ghidra)→ 再拆它骨头
3. 找关键点(xref / 交叉引用) → 顺着线索摸到答案
核心概念:交叉引用(xref)
程序里,任何东西都不是孤立的:字符串会被人"引用",函数会被人"调用"。交叉引用就是"谁在用它"的地图。
你想知道: 这个字符串在哪被用?
→ axt 0x4052a (radare2 命令)
→ 找到: main 里 0x5c9 处的 strcmp 用了它
→ 顺藤摸瓜: 到那个地址看它在干什么
口诀:跟着"谁引用它",就能从'它是什么'追到'它干什么'。
三个"第一次"必会命令
file 程序 # 出生证明(类型/架构)
r2 -A 程序 # 拆开(radare2 自动分析)
afl # 零件清单(所有函数)
关于"比较"——程序的心跳
还记得第一课的口诀吗?程序只会比较和跳转。 刚才的守卫,本质就是在做一件事:
strcmp(你的输入, 地址里的钥匙) ; 比较
test eax, eax ; 看结果
jne 走错分支 ; 不等于就拒绝
看懂这三行,你就看懂了 80% 的"验证类程序"。不管是注册码、密码还是 flag 校验,最后都是这三板斧。
如果它把钥匙藏得更深?
钥匙可能会被"加密"(下次课讲),但思路不变:
先听(strings)
→ 再拆(看函数)
→ 找比较点(xref 到 strcmp / memcmp)
→ 在比较点停下来,看它拿什么和你的输入比
→ 那个东西,要么是钥匙,要么是算钥匙的原料
本课口诀
找比较点,看它比什么——比的那个东西,就是答案的入口。
动手做
- 自己写一个"输入密码"程序,编译后用
r2 -A找到密码校验函数。 - 练习
axt:找一个字符串,追踪谁在用它。 - 想一想:如果钥匙不是明文,而是一个"加密结果",你怎么拿到真正的钥匙?(下节课)
想学更深的
见原书 第 4 章《静态分析》(完整 crackme 演示)、第 5 章《动态分析》(GDB 断点法)。