一、实验目的
本实验通过要求你使用课程所学知识拆除一个“binary bombs”来增强对程序的机
器级表示、汇编语言、调试器和逆向工程等方面原理与技能的掌握。一个“binary bombs”
(二进制炸弹,下文将简称为炸弹)是一个 Linux 可执行程序,包含了 6 个阶段(或层
次、关卡)。炸弹运行的每个阶段要求你输入一个特定字符串,你的输入符合程序预期
的输入,该阶段的炸弹就被拆除引信即解除了,否则炸弹“爆炸”打印输出 "BOOM!!!"。
实验的目标是拆除尽可能多的炸弹层次。
每个炸弹阶段考察了机器级程序语言的一个不同方面,难度逐级递增:
阶段 1:字符串比较
阶段 2:循环
阶段 3:条件/分支
阶段 4:递归调用和栈
阶段 5:指针
阶段 6:链表/指针/结构
另外还有一个隐藏阶段,只有当你在第 4 阶段的解后附加一特定字符串后才会出现。
为完成二进制炸弹拆除任务,你需要使用 gdb 调试器和 objdump 来反汇编炸弹的可执
行文件并单步跟踪调试每一阶段的机器代码,从中理解每一汇编语言代码的行为或作用,
进而设法推断拆除炸弹所需的目标字符串。比如在每一阶段的开始代码前和引爆炸弹的
函数前设置断点。
二、报告要求
本次报告需要同学们对破解炸弹过程中的 6 个阶段进行推理说明,尽可能结合实验
截图和代码分析。本次实验略有难度,希望同学们耐心研究,细心解答。
三、实验分析
1. 阶段一
调用
前通过读取一个字符串,入栈
在中,调用来比较读取的结果和正确结果
(存放在$0x80497c0)
所以只需要查看在$0x80497c0 处存放的字符串就能得到第一个答案.
2. 阶段二
根据以上分析得出:数字的递推公式——n=1 num1=1;num(n+1)=(n+1)*num(n);
所以六个数字为:1 2 6 24 120 720
3. 阶段三
分析调用 sscanf 函数之前的堆栈信息
通 过 查 看 $0x80497de 处 的 内 存 能 够 得 到 sscanf 读 取 的 数 据 类 型 :
%eax 中存放的是什么?sscanf 的返回值?如果读取的参数个数大于 2 就爆炸。
可知读取了三个变量,和之前堆栈中分配的内存照应
判断:-0xc(%ebp)处储存的是第一个整数 num1.
if(num1>7) %bl=0x78,explode_bomb;
看来 if 应该小于等于 7
跳转表的地址在:0x80497e8,用 x/8xw 指令检查一下跳转表的内容:
得到 8 种情况的地址,根据地址找到对应代码,以 case0 为例:
找到对应的 case 代码,可知 case 中的内容:先把%ebx 寄存器的低 8 位置为 0x71,
接着比较 0x309 和第三个整数(储存在-0x4(%ebp)中)如果相同,接着比较%bl 和
第二个 char 类型的数据(储存在-0x5(%ebp)中),如果都相同则结束,因此,可以
推断满足 case0 的 char 为 q,整数值为 777。其他 case 同理。default 为 explode。
八种能够通过的情况对应的内容:
num1
0
1
2
3
4
5
6
7
char
q
b
b
k
o
t
v
b
num2
777
214
755
251
160
458
780
524
4. 阶段四
对 phase_4 的分析:如果调用 fun4 的结果是 55,成功返回。
对 fun4 的分析:fun4 需要一个参数 a,如果 a<=1 返回 1,否则返回 fun4(a-1)+fun4(a-2)
的值。即 fun4(a)=fun4(a-1)+fun(a-2),其中 a=1 时 fun4(1)=1;
由以上递推关系可得数列为(1)1 2 3 5 8 13 21 34 55……第 9 项是 55 因此输入 9
可以得到正确答案.
5. 阶段五
上面%eax 保存 string_length 函数的返回值,说明 phase_5 的输入要求有 6 个字符。
以上的代码的分析:程序从输入字符串 s[0]开始到 s[5]结束。对于第 i 次循环:把 s[i]
的后四位作为偏移量,以 0x804b220 为起始地址。得到对应内容后把这个内容写回
s[i]。
0x804b220 处的内容:
程序修改完毕之后进行比较。被比较的地址在 0x804980b。
可知经过变换后的正确结果应该为“giants”。
根据 0x804b220 处的内容,我们可以得到每个字符的偏移量:15 0 5 11 13 1。
即输入字符的后四位对应的以上六个偏移量。
可能的结果
(理论上每个位置的输入都有七种可能(高四位从 0000 到 0111),但是由于某
些 ascii 码对应的符号无法被显示或为转义字符,下面列举的符合条件的字符都不足
七个,并且有些字符被当做‘’显示):
/?O_o
0@P`
%5EUe
+;K[k
-=M]m
!1AQa
从每组中任选一个输入:
6. 阶段六
阶段六可以分成 4 个部分:
part1:输入检查。输入六个数字,检查
检查内容为:1.每个数字 a 是否满足:1<=a&&a<=6
2.六个数字之间必须两两不相等
为方便后期整合以上内容的 C 语言代码为:
int a[6];
for(int i=0;i<6;i++){
scanf("%d",a[i]);
}
for(int i=0;i<6;i++){
if(a[i]>6)printf("BOOM!");
for(int j=i+1;j<6;j++){
if(a[i]=a[j])printf("BOOM!");
}
}
part2:地址修改
part2: 根据上面输入的 6 个数字中的每一个,计算出一个对应的偏移量,这个偏
移量加基地址,得到一个 node 的地址。
首先载入一个基地址:0x804b26c
执行过 0x8048e30 语句后
在循环中依次查看寄存器中的内容就可以得到下面的地址表:
1
2
3
4
5
6
0x804b26c
0x804b260
0x804b254
0x804b248
0x804b23c
0x804b230
part3 链表构建:
假设:存放“被修改过的地址”的容器为数组 b,b[i]存放,根据 a[i]内容进行基地
址偏移的结果。
根据数组 b 的元素内容构建链表:b[i]->next=b[i+1]
part4 链表检查:
遍历链表。需要确保链表的前一个结点的数据值 val 大于后一个节点的数据值。
因此我们需要查看每个节点的数据值是什么,再根据这些数据值对节点进行排序,
找到正确的顺序之后就可以得到每个节点相对基地址的偏移量,就能够得出数组 a
中的内容,即可得到正确答案。
node 内容:
node 内容
0xfd
1
2
0x2d5
0x12d
3
0x3e5
4
5
0xd4
6
0x1b0
正确结果:
4 2 6 3 1 5
7 secret phase
part1 发现:secret phase
程序每次执行完一个 phase 判断程序后都会执行 phase_defused 程序,会不会在这里
呢?
phase_defused: