Bomb Lab
介绍
邪恶博士在我们的班级机器上植入了许多“二进制炸弹”。二进制炸弹是一个由一系列阶段组成的程序。每个阶段都要求你在 stdin 上输入入特定的字符串。如果你输入正确的字符串,则该阶段将被消除,炸弹将进入下一个阶段。否则,炸弹通过打印“ BOOM !!!”而爆炸。然后终止。当每个阶段都已消除时,炸弹便已消除。
获取炸弹
进入官网下载 bomb.tar 文件,通过 tar -xvf bomb.tar
解压,会得到 3 个文件。
- README
- bomb:可执行二进制炸弹
- bomb.c:源文件包含炸弹的主要程序以及引导程序
一共有 6 个阶段,前 4 个阶段每个 10 分,后面两个比较难每个 15 分,在拆炸弹的过程中,每次引爆一次会从总分里扣 0.5 分(最多扣 20 分)。
小技巧
有一些小提示需要注意:
为了防止引爆炸弹,需要学会设置断点,在“炸弹”之前设置断点,防止爆炸
反编译出来的汇编代码很长,并不需要搞懂每行,通过 debugger 观察程序执行变化,根据这些信息去拆除炸弹
工具
gdb
:命令行调试器,可以一行行地追踪程序,设置断点,查看寄存器和内存objdump -t
:输出炸弹程序的符号表。符号表里包含了所有函数和全局变量的名字ojbdump -d
:反编译二进制文件,输出汇编代码
准备
- 汇编知识:汇编入门
- gdb 知识
gdb bomb
进入调试状态run
运行break phase_1
打断点next
简写n
表示下一行代码 C 语言代码nexti
简写ni
表示下一行汇编代码continue
简写c
表示下一个断点disas
显示汇编info registers
查看寄存器的值x/s $rax
以字符串形式查看
实验部分
在命令行中反编译可执行文件,输入到 bomb.txt
文件中
objdump -d bomb > bomb.txt |
我们先看一下 main 函数,一共 6 个阶段,每个阶段都是一个 phase_x
的函数,在函数正常结束之后运行phase_defused
拆除这个阶段,然后进入下一阶段。
400e19: e8 84 05 00 00 callq 4013a2 <initialize_bomb> |
阶段一
我们从上面我们可以看到调用了 phase_1
函数,找到对应的汇编代码。
... |
上面分析的很清楚了,我们开始拆吧,在终端输入 gdb bomb
进入调试模式
>(gdb) break phase_1 # 打断点 |
这个时候使用 info registers
查看寄存器信息。
> (gdb) info registers |
使用 disas
查看执行到哪一步了,stepi
可以使汇编一步一步执行,具体可以看箭头变化。
>(gdb) disas # 反汇编 |
我是用 x/s $rdi
和 x/s $rsi
查看寄存器的值
>(gdb) x/s $rdi |
就是比较这两个值是否相等,所以我们只要把这值记下来就可以拆调第一个炸弹了。
我们重新执行一遍,把 Border relations with Canada have never been better.
这段话输入进去,发现拆除了第一炸弹。
阶段二
同样我们先找到 phase_2
对应的汇编代码,从函数名 read_six_numbers
提示我们输入 6 个数字。
0000000000400efc <phase_2>: |
虽然我们知道 read_six_numbers
这个函数是提示我们输入 6 个数据, 但是我们不知道输入这 6 个数字的格式是怎样的,我们先输入123456
试试看。
我们先看看这个函数,对应的汇编,通过打断点 break read_six_numbers
进入。
>(gdb) disas |
首先看看第一个数是否为 1,然后循环判断后面一个数是否是前面一个数的倍数。
(gdb) disas |
对应内存中的值
0x7fffffffe368 0x28 -> %rbp |
最好我们输入 1 2 4 8 16 32
查看结果,就已经通过了
阶段三
同样我们先找到 phase_3
对应的汇编
Dump of assembler code for function phase_3: |
我们又看到 sscnaf
函数,x/s 0x4025cf
查看输入格式,发现是按 %d %d
输入两个数字。在 <+50>
行中是一个 switch 方法,等下 C 代码如下:
if (x1 > 7 || x1 < 0) |
假设我们输入的 x1 为 1,那么对应表的地址为 0x400fb9 <phase_3+118>
,所以我们 x2 的值就是 <+118>
中的 0x137
对应的十进制为 311。
最后我们输入 1 311
试试,已经解除
了。
阶段四
有看到了 sscanf
函数,根据前面的经验,查看一下输入格式。0x8(%rsp)
存放着第一个值,0xc(%rsp)
存放着第二个值。
(gdb) disas |
这里是一个递归,发现第一个数为 1 时,能是返回值为 0。
Dump of assembler code for function func4: |
根据 x86 汇编语言的约定,%rdi
、%rsi
、%rdx
分别为第一、二和第三个参数使用的寄存器。%rax
作为返回值所在的寄存器。func4 变换为C语言代码如下:
int func4(int target, int step, int limit) { |
最后看看测试结果
阶段五
先看汇编代码
=> 0x0000000000401062 <+0>: push %rbx |
传入一个长度为六的字符串,依次取一个字符,截取它ASCII表上对应二进制的后四位,作为index。在 maduiersnfotvbyl...
这个字符串中以这个 index 为偏移量取字符。按照这样的规则从 maduiersnfotvbyl...
这个字符串中取出六个字符,组成新的字符串,要和 flyers 一样。这个还是很直接的吧,解题就是反过来的顺序。先根据 flyers 找出六个 index。再找个 ASCII 码表,根据 index 找到符合要求的字符。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
所以 flyers 对应的 index 为 9 15 14 5 6 7
,所以输入ionefg
就是其中一个答案。
运行一下
阶段六
先看汇编代码,这个比较长。
(gdb) disas |
主要过程分为以下几步:
- 获取 6 个数字
- 判断每个数字都大于0且小于7,并且都不相同
- 交换一下位子,arr[i] 与 arr[7-i] 的数交换一下
- 按照获取 arr 的排序的值,把对应的链表进行重新排列
- 重排之后的链表要递减
最后我们看 0x006032d0
地址链表的值,大小顺序为3 4 5 6 1 2
然后我们只要输入4 3 2 1 6 5
就可以。
最后我们看看结果,发现已经全部通过了。
总结
做完这个 lab ,学习到了 gdb 调试方法,对汇编更加深入的了解,总体来说这个实验还是挺有意思的。
参考
- 深入理解计算机系统
- Bomb Lab
- CSAPP 之 Bomb Lab
- CSAPP bomb lab 问题