BombLab

CSAPP 之 Bomb Lab 解题记录,真的是太有趣辣

寄存器速查

寄存器名称 备注
%rax 返回值
%rdi, %rsi, %rdx, %rcx, %r8, %r9 参数,按照顺序排列
%rsp 栈指针
%rbx, %rbp, %r12-%r15 callee saved
%10, %r11 caller saved

Bomblab

GDB 的使用

gdb 常用指令

指令 全称 描述
r run 开始执行程序,直到下一个断点或程序结束
q quit 退出 GDB 调试器
ni nexti 执行下一条指令,但不进入函数内部
si stepi 执行当前指令,如果是函数调用则进入函数
b break 在指定位置设置断点
c cont 从当前位置继续执行程序,直到下一个断点或程序结束
p print 打印变量的值
x 打印内存中的值
j jump 跳转到程序指定位置
disas 反汇编当前函数或指定的代码区域
layout asm 显示汇编代码视图
layout regs 显示当前的寄存器状态和它们的值

备注:退出视图模式使用 Ctrl+X,然后按 A

关于 p 和 x,更详细的解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
p $rax  # 打印寄存器 rax 的值
p $rsp # 打印栈指针的值
p/x $rsp # 打印栈指针的值,以十六进制显示
p/d $rsp # 打印栈指针的值,以十进制显示

x/2x $rsp # 以十六进制格式查看栈指针 %rsp 指向的内存位置 M[%rsp] 开始的两个单位。
x/2d $rsp # 以十进制格式查看栈指针 %rsp 指向的内存位置 M[%rsp] 开始的两个单位。
x/2c $rsp # 以字符格式查看栈指针 %rsp 指向的内存位置 M[%rsp] 开始的两个单位。
x/s $rsp # 把栈指针指向的内存位置 M[%rsp] 当作 C 风格字符串来查看。

x/b $rsp # 检查栈指针指向的内存位置 M[%rsp] 开始的 1 字节。
x/h $rsp # 检查栈指针指向的内存位置 M[%rsp] 开始的 2 字节(半字)。
x/w $rsp # 检查栈指针指向的内存位置 M[%rsp] 开始的 4 字节(字)。
x/g $rsp # 检查栈指针指向的内存位置 M[%rsp] 开始的 8 字节(双字)。

info registers # 打印所有寄存器的值
info breakpoints # 打印所有断点的信息

delete breakpoints 1 # 删除第一个断点,可以简写为 d 1

.gdbinit 文件妙用

这是一个可以在每次 gdb 启动时默认加载指令的文件,我们用它来安全化炸弹

使用如下命令创建.gdbinit 文件

1
2
3
4
5
6
# 创建当前目录下的 .gdbinit 文件
touch .gdbinit
# 创建 .config/gdb 文件夹
mkdir -p ~/.config/gdb
# 允许 gdb 预加载根目录下所有的文件
echo "set auto-load safe-path /" > ~/.config/gdb/gdbinit

在工作目录下的.gdbinit 里便可以输入我们的初始化指令

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
# 设置文件输入,也可以不用
set args sol.txt

#防止炸弹爆炸,安全化炸弹
b explode_bomb

# 为各个 phase 函数设置断点
b phase_1
b phase_2
b phase_3
b phase_4
b phase_5
b phase_6

# 为校验函数设置断点
b phase_defused
# 为此断点编程
command
# 直接跳到返回语句处,跳过校验流程
jump *(phase_defused + 0x3F)
end
#以上是下来练习时用的,考试时注释掉

layout asm

r

本来还可以直接 jump 掉 send_msg 速通,但是这个 bug 今年貌似被修复了,所以不再使用

汇编代码解读 and 解题

获得汇编代码

1
objdump -d bomb > asm

在汇编代码中找到 main 所在,即可找到各个 phase 代码

Phase 1

找到对应的汇编代码:

1
2
3
4
5
6
7
8
9
10
11
00000000004016b4 <phase_1>:
4016b4: f3 0f 1e fa endbr64
4016b8: 48 83 ec 08 sub $0x8,%rsp
4016bc: 48 8d 35 8d 1a 00 00 lea 0x1a8d(%rip),%rsi # 403150 <_IO_stdin_used+0x150>
4016c3: e8 4e 05 00 00 call 401c16 <strings_not_equal>
4016c8: 85 c0 test %eax,%eax
4016ca: 75 05 jne 4016d1 <phase_1+0x1d>
4016cc: 48 83 c4 08 add $0x8,%rsp
4016d0: c3 ret
4016d1: e8 2d 0e 00 00 call 402503 <explode_bomb>
4016d6: eb f4 jmp 4016cc <phase_1+0x18>

第一行代码是防止 ROP 攻击的,不用管

注意到第四行调用了字符串是否相等的函数,考虑校验方式为对比输入的字符串是否与预存的字符串相等

打开 gdb,注意这时候已经初始化完毕,可以先在文件中随便输入一些东西,然后前进到调用函数的前一句,查看%rsi 内存的东西即可(注意%rsi 是第二个参数,第一个参数在%rdi 里即为我们输入的东西)

1
x/s $rsi

得到的即为答案,放到文件里即可

1
I turned the moon into something I like to call a Death Star.

Phase 2

找到对应的汇编代码:

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
34
35
36
37
38
39
40
41
42
00000000004016d8 <phase_2>:
4016d8: f3 0f 1e fa endbr64
4016dc: 41 55 push %r13
4016de: 41 54 push %r12
4016e0: 55 push %rbp
4016e1: 53 push %rbx
4016e2: 48 83 ec 28 sub $0x28,%rsp
4016e6: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
4016ed: 00 00
4016ef: 48 89 44 24 18 mov %rax,0x18(%rsp)
4016f4: 31 c0 xor %eax,%eax
4016f6: 48 89 e3 mov %rsp,%rbx
4016f9: 48 89 de mov %rbx,%rsi
4016fc: e8 44 0e 00 00 call 402545 <read_six_numbers>
401701: 4c 8d 6c 24 0c lea 0xc(%rsp),%r13
401706: bd 00 00 00 00 mov $0x0,%ebp
40170b: eb 0d jmp 40171a <phase_2+0x42>
40170d: 41 03 2c 24 add (%r12),%ebp
401711: 48 83 c3 04 add $0x4,%rbx
401715: 4c 39 eb cmp %r13,%rbx
401718: 74 11 je 40172b <phase_2+0x53>
40171a: 49 89 dc mov %rbx,%r12
40171d: 8b 43 0c mov 0xc(%rbx),%eax
401720: 39 03 cmp %eax,(%rbx)
401722: 74 e9 je 40170d <phase_2+0x35>
401724: e8 da 0d 00 00 call 402503 <explode_bomb>
401729: eb e2 jmp 40170d <phase_2+0x35>
40172b: 85 ed test %ebp,%ebp
40172d: 74 1b je 40174a <phase_2+0x72>
40172f: 48 8b 44 24 18 mov 0x18(%rsp),%rax
401734: 64 48 2b 04 25 28 00 sub %fs:0x28,%rax
40173b: 00 00
40173d: 75 12 jne 401751 <phase_2+0x79>
40173f: 48 83 c4 28 add $0x28,%rsp
401743: 5b pop %rbx
401744: 5d pop %rbp
401745: 41 5c pop %r12
401747: 41 5d pop %r13
401749: c3 ret
40174a: e8 b4 0d 00 00 call 402503 <explode_bomb>
40174f: eb de jmp 40172f <phase_2+0x57>
401751: e8 6a fb ff ff call 4012c0 <__stack_chk_fail@plt>

略去栈保护,直接看核心部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
4016f6:	48 89 e3             	mov    %rsp,%rbx
4016f9: 48 89 de mov %rbx,%rsi
4016fc: e8 44 0e 00 00 call 402545 <read_six_numbers>
401701: 4c 8d 6c 24 0c lea 0xc(%rsp),%r13
401706: bd 00 00 00 00 mov $0x0,%ebp
40170b: eb 0d jmp 40171a <phase_2+0x42>
40170d: 41 03 2c 24 add (%r12),%ebp
401711: 48 83 c3 04 add $0x4,%rbx
401715: 4c 39 eb cmp %r13,%rbx
401718: 74 11 je 40172b <phase_2+0x53>
40171a: 49 89 dc mov %rbx,%r12
40171d: 8b 43 0c mov 0xc(%rbx),%eax
401720: 39 03 cmp %eax,(%rbx)
401722: 74 e9 je 40170d <phase_2+0x35>

注意到每次循环后加的是 0x4,但循环体内部比较的数字是它自己和地址加上 0xc 后的数字。所以这里的比较方式实际上是每个数字和它往后的第三个数字比,即第一个和第四个,第二个和第五个,第三个和第六个,具体内容如代码所示:

  • 每对数字必须相等
  • 数字总和不能是 0

直接输入一套

1
1 1 1 1 1 1

即可

还有一种 phase 2,内容是递归数列,每一项根据前一项递归而来,且第一项一般为 1,注意一下这种情况

Phase 3

这一部分的内容是跳表,我们找到对应代码,并找到跳表前的核心段落

1
2
3
4
5
6
7
8
9
10
11
12
13
14
40176e:	48 8d 4c 24 0f       	lea    0xf(%rsp),%rcx
401773: 48 8d 54 24 10 lea 0x10(%rsp),%rdx
401778: 4c 8d 44 24 14 lea 0x14(%rsp),%r8
40177d: 48 8d 35 32 1a 00 00 lea 0x1a32(%rip),%rsi # 4031b6 <_IO_stdin_used+0x1b6>
401784: e8 17 fc ff ff call 4013a0 <__isoc99_sscanf@plt>
401789: 83 f8 02 cmp $0x2,%eax
40178c: 7e 20 jle 4017ae <phase_3+0x58>
40178e: 83 7c 24 10 07 cmpl $0x7,0x10(%rsp)
401793: 0f 87 0d 01 00 00 ja 4018a6 <phase_3+0x150>
401799: 8b 44 24 10 mov 0x10(%rsp),%eax
40179d: 48 8d 15 1c 1a 00 00 lea 0x1a1c(%rip),%rdx # 4031c0 <_IO_stdin_used+0x1c0>
4017a4: 48 63 04 82 movslq (%rdx,%rax,4),%rax
4017a8: 48 01 d0 add %rdx,%rax
4017ab: 3e ff e0 notrack jmp *%rax

注意到这里调用了 sscanf,观察上方传参可以发现参数顺序为%rdx, %rcx, %r8

再用 gdb 读取%rsi 的内容获取格式 %d %c %d ,其实也可以从传参时内存大小来判断(%rdx %r8 都占用 4 个字节,但%rcx 占用 1 个)

然后便是构造条表并跳转,这里要求第一个数字必须小于 7,然后根据第一个数字的大小来跳转到不同的分支

如果单看代码分析不出来跳表的话,可以直接启动 gdb,检查跳转前寄存器的值即可

这里仅记录第一个数字为 0 的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
4017b5:	b8 61 00 00 00       	mov    $0x61,%eax
4017ba: 81 7c 24 14 48 02 00 cmpl $0x248,0x14(%rsp)
4017c1: 00
4017c2: 0f 84 e8 00 00 00 je 4018b0 <phase_3+0x15a>

......

4018b0: 38 44 24 0f cmp %al,0xf(%rsp)
4018b4: 75 15 jne 4018cb <phase_3+0x175>
4018b6: 48 8b 44 24 18 mov 0x18(%rsp),%rax
4018bb: 64 48 2b 04 25 28 00 sub %fs:0x28,%rax
4018c2: 00 00
4018c4: 75 0c jne 4018d2 <phase_3+0x17c>
4018c6: 48 83 c4 28 add $0x28,%rsp
4018ca: c3 ret

于是第三个数字为 0x248,即 584,第二个字符为 0x61,即 a

故一组可行解为

1
0 a 584

Phase 4

这一段是函数的递归调用,贴出核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
401919:	48 8d 54 24 04       	lea    0x4(%rsp),%rdx
40191e: 48 8d 35 97 18 00 00 lea 0x1897(%rip),%rsi # 4031bc <_IO_stdin_used+0x1bc>
401925: e8 76 fa ff ff call 4013a0 <__isoc99_sscanf@plt>
40192a: 83 f8 01 cmp $0x1,%eax
40192d: 75 07 jne 401936 <phase_4+0x35>
40192f: 83 7c 24 04 00 cmpl $0x0,0x4(%rsp)
401934: 7f 05 jg 40193b <phase_4+0x3a>
401936: e8 c8 0b 00 00 call 402503 <explode_bomb>
40193b: 8b 7c 24 04 mov 0x4(%rsp),%edi
40193f: e8 93 ff ff ff call 4018d7 <func4>
401944: 3d f7 90 0c 00 cmp $0xc90f7,%eax
401949: 75 15 jne 401960 <phase_4+0x5f>

其实就是先读数,然后用这个参数调用 func4,比较返回值和 0xc90f7 的大小

那观察 func4 即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
00000000004018d7 <func4>:
4018d7: f3 0f 1e fa endbr64
4018db: b8 01 00 00 00 mov $0x1,%eax
4018e0: 85 ff test %edi,%edi
4018e2: 7e 1c jle 401900 <func4+0x29>
4018e4: 48 83 ec 08 sub $0x8,%rsp
4018e8: 83 ef 01 sub $0x1,%edi
4018eb: e8 e7 ff ff ff call 4018d7 <func4>
4018f0: 89 c2 mov %eax,%edx
4018f2: 8d 04 c5 00 00 00 00 lea 0x0(,%rax,8),%eax
4018f9: 29 d0 sub %edx,%eax
4018fb: 48 83 c4 08 add $0x8,%rsp
4018ff: c3 ret
401900: c3 ret

原来是通过递归计算

$$
7^n
$$

注意到

$$
7^7=0xc90f7
$$

即可,答案为

1
7

Phase 5

最后两关逐渐变难,先贴上代码

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
34
35
36
37
38
39
40
41
42
000000000040196c <phase_5>:
40196c: f3 0f 1e fa endbr64
401970: 48 83 ec 18 sub $0x18,%rsp
401974: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
40197b: 00 00
40197d: 48 89 44 24 08 mov %rax,0x8(%rsp)
401982: 31 c0 xor %eax,%eax
401984: 48 8d 4c 24 04 lea 0x4(%rsp),%rcx
401989: 48 89 e2 mov %rsp,%rdx
40198c: 48 8d 35 bd 1b 00 00 lea 0x1bbd(%rip),%rsi # 403550 <array.0+0x370>
401993: e8 08 fa ff ff call 4013a0 <__isoc99_sscanf@plt>
401998: 83 f8 01 cmp $0x1,%eax
40199b: 7e 5a jle 4019f7 <phase_5+0x8b>
40199d: 8b 04 24 mov (%rsp),%eax
4019a0: 83 e0 0f and $0xf,%eax
4019a3: 89 04 24 mov %eax,(%rsp)
4019a6: 83 f8 0f cmp $0xf,%eax
4019a9: 74 32 je 4019dd <phase_5+0x71>
4019ab: b9 00 00 00 00 mov $0x0,%ecx
4019b0: ba 00 00 00 00 mov $0x0,%edx
4019b5: 48 8d 35 24 18 00 00 lea 0x1824(%rip),%rsi # 4031e0 <array.0>
4019bc: 83 c2 01 add $0x1,%edx
4019bf: 48 98 cltq
4019c1: 8b 04 86 mov (%rsi,%rax,4),%eax
4019c4: 01 c1 add %eax,%ecx
4019c6: 83 f8 0f cmp $0xf,%eax
4019c9: 75 f1 jne 4019bc <phase_5+0x50>
4019cb: c7 04 24 0f 00 00 00 movl $0xf,(%rsp)
4019d2: 83 fa 0a cmp $0xa,%edx
4019d5: 75 06 jne 4019dd <phase_5+0x71>
4019d7: 39 4c 24 04 cmp %ecx,0x4(%rsp)
4019db: 74 05 je 4019e2 <phase_5+0x76>
4019dd: e8 21 0b 00 00 call 402503 <explode_bomb>
4019e2: 48 8b 44 24 08 mov 0x8(%rsp),%rax
4019e7: 64 48 2b 04 25 28 00 sub %fs:0x28,%rax
4019ee: 00 00
4019f0: 75 0c jne 4019fe <phase_5+0x92>
4019f2: 48 83 c4 18 add $0x18,%rsp
4019f6: c3 ret
4019f7: e8 07 0b 00 00 call 402503 <explode_bomb>
4019fc: eb 9f jmp 40199d <phase_5+0x31>
4019fe: e8 bd f8 ff ff call 4012c0 <__stack_chk_fail@plt>

大体上是一个 while 循环,同时维护了%ecx, %edx 两个循环变量,要求是用输入值作为下标访问数组,同时每次循环用访问得到的值作为新的下标继续访问,直到得到 15,且这时候一共跳转了 10 次,输入的第二个数字即为访问过程中的累计求和

关键是如何获得数组的内容,观察到代码里给出了数组首地址,那我们直接在 gdb 中访问即可

1
2
3
4
5
(gdb) x/16w 0x4031e0
0x4031e0 <array.0>: 10 2 14 7
0x4031f0 <array.0+16>: 8 12 15 11
0x403200 <array.0+32>: 0 4 1 13
0x403210 <array.0+48>: 3 9 6 5

过程略,答案为

1
13 69

Phase 6

这一部分是链表重排,也是最难的地方,关键是理解链表重排的函数以及解题的要求

贴上 phase6 的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
0000000000401a6e <phase_6>:
401a6e: f3 0f 1e fa endbr64
401a72: 48 83 ec 08 sub $0x8,%rsp
401a76: ba 0a 00 00 00 mov $0xa,%edx
401a7b: be 00 00 00 00 mov $0x0,%esi
401a80: e8 eb f8 ff ff call 401370 <strtol@plt>
401a85: 89 05 e5 3c 00 00 mov %eax,0x3ce5(%rip) # 405770 <node0>
401a8b: 48 8d 3d de 3c 00 00 lea 0x3cde(%rip),%rdi # 405770 <node0>
401a92: e8 6c ff ff ff call 401a03 <fun6>
401a97: 48 8b 40 08 mov 0x8(%rax),%rax
401a9b: 48 8b 40 08 mov 0x8(%rax),%rax
401a9f: 48 8b 40 08 mov 0x8(%rax),%rax
401aa3: 48 8b 40 08 mov 0x8(%rax),%rax
401aa7: 48 8b 40 08 mov 0x8(%rax),%rax
401aab: 48 8b 40 08 mov 0x8(%rax),%rax
401aaf: 48 8b 40 08 mov 0x8(%rax),%rax
401ab3: 8b 0d b7 3c 00 00 mov 0x3cb7(%rip),%ecx # 405770 <node0>
401ab9: 39 08 cmp %ecx,(%rax)
401abb: 75 05 jne 401ac2 <phase_6+0x54>
401abd: 48 83 c4 08 add $0x8,%rsp
401ac1: c3 ret
401ac2: e8 3c 0a 00 00 call 402503 <explode_bomb>
401ac7: eb f4 jmp 401abd <phase_6+0x4f>

大概就是将输入值送进 node0,然后进行链表重排,重排后返回的值为链表最大值的地址,要求从这里开始往后访问七次能回到 node0

明白到这里就发现其实是不难的,关键是知道链表内容,然后把 node0 设置成链表里第 8 小的数字就行了

1
2
3
4
5
6
7
8
9
10
11
12
(gdb) x/36w 0x405770
0x405770 <node0>: 0 0 4216704 0
0x405780 <node1>: 104 1 4216720 0
0x405790 <node2>: 624 2 4216736 0
0x4057a0 <node3>: 812 3 4216752 0
0x4057b0 <node4>: 81 4 4216768 0
0x4057c0 <node5>: 201 5 4216784 0
0x4057d0 <node6>: 859 6 4216800 0
0x4057e0 <node7>: 108 7 4216816 0
0x4057f0 <node8>: 751 8 4215392 0
(gdb) x/4w 0x405260
0x405260 <node9>: 975 9 0 0

那么答案即为 104-108 里的随便一个数,为了吉利我选择 108

1
108

这里有一个小建议,如果被链表函数绕晕了,那我们可以先随便输入一个数,用 gdb 运行到 func6 刚返回那里,然后去找到这时候链表的内容即可(前提是你还是能大致看出来是在排序)

Secret Phase

由于 Lab 2 已经过了 ddl,我一开始跳过了和服务器通讯的那部分,所以这里的 secret phase 我现在已经无法访问,但是我可以写出来具体方法

进入 secret phase

通常是在 phase_defused 里暗藏玄机

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
34
35
36
37
38
39
40
41
42
43
000000000040267f <phase_defused>:
40267f: f3 0f 1e fa endbr64
402683: 48 83 ec 78 sub $0x78,%rsp
402687: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
40268e: 00 00
402690: 48 89 44 24 68 mov %rax,0x68(%rsp)
402695: 31 c0 xor %eax,%eax
402697: bf 01 00 00 00 mov $0x1,%edi
40269c: e8 06 f8 ff ff call 401ea7 <send_msg>
4026a1: 83 3d e8 31 00 00 06 cmpl $0x6,0x31e8(%rip) # 405890 <num_input_strings>
4026a8: 74 15 je 4026bf <phase_defused+0x40>
4026aa: 48 8b 44 24 68 mov 0x68(%rsp),%rax
4026af: 64 48 2b 04 25 28 00 sub %fs:0x28,%rax
4026b6: 00 00
4026b8: 75 7f jne 402739 <phase_defused+0xba>
4026ba: 48 83 c4 78 add $0x78,%rsp
4026be: c3 ret
4026bf: 48 8d 4c 24 10 lea 0x10(%rsp),%rcx
4026c4: 48 8d 54 24 0c lea 0xc(%rsp),%rdx
4026c9: 48 8d 35 ca 0e 00 00 lea 0xeca(%rip),%rsi # 40359a <array.0+0x3ba>
4026d0: 48 8d 3d b9 32 00 00 lea 0x32b9(%rip),%rdi # 405990 <input_strings+0xf0>
4026d7: b8 00 00 00 00 mov $0x0,%eax
4026dc: e8 bf ec ff ff call 4013a0 <__isoc99_sscanf@plt>
4026e1: 83 f8 02 cmp $0x2,%eax
4026e4: 74 1a je 402700 <phase_defused+0x81>
4026e6: 48 8d 3d f3 0b 00 00 lea 0xbf3(%rip),%rdi # 4032e0 <array.0+0x100>
4026ed: e8 8e eb ff ff call 401280 <puts@plt>
4026f2: 48 8d 3d 17 0c 00 00 lea 0xc17(%rip),%rdi # 403310 <array.0+0x130>
4026f9: e8 82 eb ff ff call 401280 <puts@plt>
4026fe: eb aa jmp 4026aa <phase_defused+0x2b>
402700: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
402705: 48 8d 35 94 0e 00 00 lea 0xe94(%rip),%rsi # 4035a0 <array.0+0x3c0>
40270c: e8 05 f5 ff ff call 401c16 <strings_not_equal>
402711: 85 c0 test %eax,%eax
402713: 75 d1 jne 4026e6 <phase_defused+0x67>
402715: 48 8d 3d 64 0b 00 00 lea 0xb64(%rip),%rdi # 403280 <array.0+0xa0>
40271c: e8 5f eb ff ff call 401280 <puts@plt>
402721: 48 8d 3d 80 0b 00 00 lea 0xb80(%rip),%rdi # 4032a8 <array.0+0xc8>
402728: e8 53 eb ff ff call 401280 <puts@plt>
40272d: b8 00 00 00 00 mov $0x0,%eax
402732: e8 d3 f3 ff ff call 401b0a <secret_phase>
402737: eb ad jmp 4026e6 <phase_defused+0x67>
402739: e8 82 eb ff ff call 4012c0 <__stack_chk_fail@plt>

答案就藏在 0x4035a0!

1
2
(gdb) x/s 0x4035a0
0x4035a0: "austinpowers"

在哪里输入呢,注意到

1
lea    0x32b9(%rip),%rdi        # 405990 <input_strings+0xf0>

而这是 phase_4 的输入地址,于是我们只需要在 phase4 后面添加上述字符串即可

对于 secret_phase 本身,是一个二叉树访问的递归函数问题,关键和 phase6 一样,一方面要看懂函数,另一方面要能在内存中找到二叉树的存储位置并访问之

这样我们的 sol.txt 即为:

1
2
3
4
5
6
7
I turned the moon into something I like to call a Death Star.
1 2 3 1 2 3
0 a 584
7 austinpowers
13 69
108
(secret phase的答案我忘了)

完结!


BombLab
http://example.com/2024/12/09/BombLab/
作者
思源南路世一劈
发布于
2024年12月9日
许可协议