Bomblab
本文最后更新于:2023年4月20日 晚上
计算机系统第三次实验 Bomb lab
本实验用到的环境、命令如下:
- Ubuntu 20.04
objdump
- GDB(~peda插件版)
Phase 1
将 bomb 这个 elf 文件使用
file bomb
查看其基本信息,得知它是一个 64 bit 程序,放入 Ubuntu 20.04 中进行后续操作;chmod 777 bomb
为其赋予权限;使用
objdump -d bomb
获取其反汇编代码如下(节选部分)
1 |
|
分析如下:
注意不要运行炸弹函数
<explode_bomb>
!那么需要执行 1603 处的代码;
注意到,15fd 处的跳转若触发,则程序跳转至 1604 处,会失败;
那么 eax 需要为 0,这样 15fb 行跳转不会触发;
需要
strings_not_equal
函数返回值为 0 ;上述函数:
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
331aef: f3 0f 1e fa endbr64
1af3: 41 54 push %r12
1af5: 55 push %rbp
1af6: 53 push %rbx
1af7: 48 89 fb mov %rdi,%rbx
1afa: 48 89 f5 mov %rsi,%rbp
1afd: e8 cc ff ff ff callq 1ace <string_length>
1b02: 41 89 c4 mov %eax,%r12d
1b05: 48 89 ef mov %rbp,%rdi
1b08: e8 c1 ff ff ff callq 1ace <string_length>
1b0d: 89 c2 mov %eax,%edx
1b0f: b8 01 00 00 00 mov $0x1,%eax
1b14: 41 39 d4 cmp %edx,%r12d
1b17: 75 31 jne 1b4a <strings_not_equal+0x5b>
1b19: 0f b6 13 movzbl (%rbx),%edx
1b1c: 84 d2 test %dl,%dl
1b1e: 74 1e je 1b3e <strings_not_equal+0x4f>
1b20: b8 00 00 00 00 mov $0x0,%eax
1b25: 38 54 05 00 cmp %dl,0x0(%rbp,%rax,1)
1b29: 75 1a jne 1b45 <strings_not_equal+0x56>
1b2b: 48 83 c0 01 add $0x1,%rax
1b2f: 0f b6 14 03 movzbl (%rbx,%rax,1),%edx
1b33: 84 d2 test %dl,%dl
1b35: 75 ee jne 1b25 <strings_not_equal+0x36>
1b37: b8 00 00 00 00 mov $0x0,%eax
1b3c: eb 0c jmp 1b4a <strings_not_equal+0x5b>
1b3e: b8 00 00 00 00 mov $0x0,%eax
1b43: eb 05 jmp 1b4a <strings_not_equal+0x5b>
1b45: b8 01 00 00 00 mov $0x1,%eax
1b4a: 5b pop %rbx
1b4b: 5d pop %rbp
1b4c: 41 5c pop %r12
1b4e: c3 retqGDB 动态调试中,找到字符串
I am not part of the problem. I am a Republican.
传入上述字符串即可通关 phase_1。
Phase 2
1 |
|
关键行 165b ,其无条件跳转回 1644 ,且需注意 1654,eax 必须与 0x4(%rbx)相等,否则触发引爆函数;
由 162d ,1631 得知,第一个传入的数必须是 1;
注意 164f ,eax 值乘2,然后与内存中的一个值比较,这个值是上一次循环中的 eax;
至此,可以看出是一个 2 的幂次的循环。
payload = 1 2 4 8 16 32;
成功解决 phase2;
Phase 3
1 |
|
注意前面几行,sscanf 调用之前,设置了四个参数,可以看出是传入两个数;
且第一个数要小于 7 ,否则触发引爆函数;
随后跳转到 rax 指向的位置执行;
进入 gdb 进行动态调试,跟踪一下进入 rax 指向空间后的堆栈情况;
1 |
|
payload = 1 334;
成功解决phase_3;
Phase 4
1 |
|
- 读入两个数,其中第一个数要小于等于14;
- 17b8和17bd可看出,func4 返回值和 第二个数均应为 45;
- 于是一共就有14种可能(1 45、2 45、…… 、 14 45);
- 分析 func4 可发现其是一个递归函数,当传入参数为 14 时,返回值为 45;
- payload = 14 45;
- 第四题解决。
Phase 5
1 |
|
- 第五题仍然读入两个数,称之为第一个数、第二个数;
- 由181a~1820可看出,第一个数不能为15;
- 1833~1840这块代码可以看成:ecx不断地加上array中的不同的数,这个数,是上一个取出的数对应的数组中相应偏移量*4的数。当去取出的数为0xf时,结束,且此时取的次数应该为15,否则触发引爆函数;
1 |
|
- 以上是数组中的数,4字节为一个数(int)
- 经过计算,payload = 5 115;即从5号元素开始取,最后取15次,取出数的和正好是115。
第五题解决完毕。
Phase_6
1 |
|
这个题比较复杂
首先,18a0可看出,其读入6个数;
18f0以上,使用一系列的判断,最终效果是,输入的六个数均小于等于6大于等于1,且各不相同。
18f0之下的部分,就是按顺序将 node1 这个链表对应的数及地址逐个读入,读入的具体位次,是由输入的6个数决定的,如 1 就读入一次,也就是读入 node1 内存放的数据
知道了node1~node6中存放数据大小关系后,由小到大排序,输入即可通关
payload = 2 4 1 5 6 3
本题结束
Secret Phase
首先,发现有 secret phase 函数,其在 bomb_defused()内被调用;
进入这个函数后,先判断 num_input_strings == 6 是否成立,再在offset 57F0处读入3个数据;
1 |
|
注意RDI,也就是第一个参数的值,可以看出这个是 phase 4 的payload,故在修改 phase 4 payload = 14 45 DrEvil;
即可触发隐藏 phase
1 |
|
- 先读入一行字符串,然后转换为整型;
- 注意 1a04 和 1a09,输入转换的整型,要小于等于1001,由此可发现,输入的数据应该是一个数,而不是几个。
- 随后调用 fun7,且最后返回值必须是2,否则爆炸。
- 来到fun7 内;
- fun7 是一个递归函数,需要明确的是,rdi 是 fun7 的第一个参数,大概率是个指针(因为后面引用它时用了括号),esi是第二个参数,这个是我们最开始输入进去的一个数。
- 在 fun 7 内,esi的值是不改变的,也就是我们输入的值,一直都是参数之一,而另一个参数;
- 如果当前节点的值大于输入值,当前节点变为当前节点+8;
- 返回两倍的下一个节点调用fun7的返回值;
- 不满足大于的话,result 化为0;
- 若相等,直接返回0;
- 若当前节点值小于输入值,将result值赋为2倍的下一节点的值+1,且注意,下一节点是当前节点+16;
- 返回result值
来看看这些节点,以及它们的值
1 |
|
再想想题目,我们最后需要构造这样的结构:
返回值为2,有一种可能:
- 通过最外层调用中,输入值小于节点值实现,要求最外层-1次调用返回值为1;
故:倒数第二层调用必须返回值为1;
若某层返回值为1,这说明了什么捏?
说明该层调用的内一层调用,必须走a1 != a2 路线,且要求内层调用返回值为0;
若某层返回值为0,这又说明了什么捏?
一定是第一种调用,也就是a1 > =a2,且内层一直是第一种调用,输入值小于节点值。
综上,输入值应满足:一直小于某些节点值,以及最外一层的节点值,但最内一层,输入值必须等于某一节点值,否则循环不完了。
有以下节点值:0x24, 0, 0x8, 0x32,0x16,0x2d,0x6,0x6b,0x0;
选择0x24尝试:
- 先是等于,然后直接返回,不行;
选择0x0尝试:
- 0一直是最小的数,也不可能实现;
选择0x8尝试:
- 8 < 24 , goto 1, node -> node + 8, 8 > 6, goto 2, node += 16, 16 > 8 , 2d>8,
太多数据,不再尝试。
再次分析,由于最初的节点是0x24,且最初节点必须走第一种跳转,也就是说第一层节点值大于输入值。
然后,节点+8,来到50号的 0x8,这里要求,返回值为1,也就是传入值 >=8,然后最后一层需要相等。这样一来,答案就明朗了,答案就是0x16。
测试一下:
1 |
|
ok
全部通过
1 |
|
实验至此结束
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!