Lab2 - bomb
进入实验
需要工具:
- objump:c 语言反汇编工具
- gdb:c 程序调试工具
objdump -d ./bomb >> bomb.s
vim bomb.s
gdb bomb
解题策略:
1 - 4 题尽可能翻译出伪代码,5, 6 及隐藏题逐步分析。
phase_1
0000000000400ee0 <phase_1>:
400ee0: 48 83 ec 08 sub $0x8,%rsp
400ee4: be 00 24 40 00 mov $0x402400,%esi
400ee9: e8 4a 04 00 00 callq 401338 <strings_not_equal>
400eee: 85 c0 test %eax,%eax
400ef0: 74 05 je 400ef7 <phase_1+0x17>
400ef2: e8 43 05 00 00 callq 40143a <explode_bomb>
400ef7: 48 83 c4 08 add $0x8,%rsp
400efb: c3 retq
判断函数<strings_not_equal>
返回结果是否为0,若是则跳转400ef7,继续进行下一步;若否则执行<explode_bomb>
,炸弹爆炸。
在进入函数<strings_not_equal>
前,%rax和%rdi储存的是我们输入的字符串所在内存空间中的地址,进入函数<strings_not_equal>
:
0000000000401338 <strings_not_equal>:
401338: 41 54 push %r12
40133a: 55 push %rbp
40133b: 53 push %rbx
40133c: 48 89 fb mov %rdi,%rbx
40133f: 48 89 f5 mov %rsi,%rbp
401342: e8 d4 ff ff ff callq 40131b <string_length>
401347: 41 89 c4 mov %eax,%r12d
40134a: 48 89 ef mov %rbp,%rdi
40134d: e8 c9 ff ff ff callq 40131b <string_length>
401352: ba 01 00 00 00 mov $0x1,%edx
401357: 41 39 c4 cmp %eax,%r12d
40135a: 75 3f jne 40139b <strings_not_equal+0x63>
40135c: 0f b6 03 movzbl (%rbx),%eax
40135f: 84 c0 test %al,%al
401361: 74 25 je 401388 <strings_not_equal+0x50>
401363: 3a 45 00 cmp 0x0(%rbp),%al
401366: 74 0a je 401372 <strings_not_equal+0x3a>
401368: eb 25 jmp 40138f <strings_not_equal+0x57>
40136a: 3a 45 00 cmp 0x0(%rbp),%al
40136d: 0f 1f 00 nopl (%rax)
401370: 75 24 jne 401396 <strings_not_equal+0x5e>
401372: 48 83 c3 01 add $0x1,%rbx
401376: 48 83 c5 01 add $0x1,%rbp
40137a: 0f b6 03 movzbl (%rbx),%eax
40137d: 84 c0 test %al,%al
40137f: 75 e9 jne 40136a <strings_not_equal+0x32>
401381: ba 00 00 00 00 mov $0x0,%edx
401386: eb 13 jmp 40139b <strings_not_equal+0x63>
401388: ba 00 00 00 00 mov $0x0,%edx
40138d: eb 0c jmp 40139b <strings_not_equal+0x63>
40138f: ba 01 00 00 00 mov $0x1,%edx
401394: eb 05 jmp 40139b <strings_not_equal+0x63>
401396: ba 01 00 00 00 mov $0x1,%edx
40139b: 89 d0 mov %edx,%eax
40139d: 5b pop %rbx
40139e: 5d pop %rbp
40139f: 41 5c pop %r12
4013a1: c3 retq
寄存器%rdi,%rsi分别保存第一、第二个参数,%rbx,%rbp为被调用者保存寄存器,程序执行完毕后需还原。这里我们忽略函数的其他细节,从函数签名可以判断该函数的作用为判断两个入参字符串是否相等。第一个入参为%rdi也就是我们输入的字符串所在内存空间的地址,第二个入参为0x402400。注意到
40133c: 48 89 fb mov %rdi,%rbx
40133f: 48 89 f5 mov %rsi,%rbp
...
40135c: 0f b6 03 movzbl (%rbx),%eax
40135f: 84 c0 test %al,%al
401361: 74 25 je 401388 <strings_not_equal+0x50>
401363: 3a 45 00 cmp 0x0(%rbp),%al
这里比较的是 (%rbp) 与 (%rbx) ,即 (%rsi) 与 (%rdi),那么这里将内存地址为0x402400的值取出即可得到答案。
(gdb) x/s 0x402400
0x402400: "Border relations with Canada have never been better."
第一题的答案为
Border relations with Canada have never been better.
phase_2
0000000000400efc <phase_2>:
400efc: 55 push %rbp
400efd: 53 push %rbx
400efe: 48 83 ec 28 sub $0x28,%rsp
400f02: 48 89 e6 mov %rsp,%rsi
400f05: e8 52 05 00 00 callq 40145c <read_six_numbers>
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)
400f0e: 74 20 je 400f30 <phase_2+0x34>
400f10: e8 25 05 00 00 callq 40143a <explode_bomb>
400f15: eb 19 jmp 400f30 <phase_2+0x34>
400f17: 8b 43 fc mov -0x4(%rbx),%eax
400f1a: 01 c0 add %eax,%eax
400f1c: 39 03 cmp %eax,(%rbx)
400f1e: 74 05 je 400f25 <phase_2+0x29>
400f20: e8 15 05 00 00 callq 40143a <explode_bomb>
400f25: 48 83 c3 04 add $0x4,%rbx
400f29: 48 39 eb cmp %rbp,%rbx
400f2c: 75 e9 jne 400f17 <phase_2+0x1b>
400f2e: eb 0c jmp 400f3c <phase_2+0x40>
400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx
400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp
400f3a: eb db jmp 400f17 <phase_2+0x1b>
400f3c: 48 83 c4 28 add $0x28,%rsp
400f40: 5b pop %rbx
400f41: 5d pop %rbp
400f42: c3 retq
phase_2
的入参仍储存在 %rax 和 %rdi 中。我们来看函数read_six_numbers
000000000040145c <read_six_numbers>:
40145c: 48 83 ec 18 sub $0x18,%rsp
401460: 48 89 f2 mov %rsi,%rdx
401463: 48 8d 4e 04 lea 0x4(%rsi),%rcx
401467: 48 8d 46 14 lea 0x14(%rsi),%rax
40146b: 48 89 44 24 08 mov %rax,0x8(%rsp)
401470: 48 8d 46 10 lea 0x10(%rsi),%rax
401474: 48 89 04 24 mov %rax,(%rsp)
401478: 4c 8d 4e 0c lea 0xc(%rsi),%r9
40147c: 4c 8d 46 08 lea 0x8(%rsi),%r8
401480: be c3 25 40 00 mov $0x4025c3,%esi
401485: b8 00 00 00 00 mov $0x0,%eax
40148a: e8 61 f7 ff ff callq 400bf0 <__isoc99_sscanf@plt>
40148f: 83 f8 05 cmp $0x5,%eax
401492: 7f 05 jg 401499 <read_six_numbers+0x3d>
401494: e8 a1 ff ff ff callq 40143a <explode_bomb>
401499: 48 83 c4 18 add $0x18,%rsp
40149d: c3 retq
函数__isoc99_sscanf@plt
的返回值为满足格式的输入值参数个数,第一个参数为输入值,第二个为格式。这里的逻辑是(用伪代码表示)
if (input_numbers > 5) {
return
} else {
explode_bomb
}
所以我们至少要输入6个数字。假设执行<read_six_numbers>
前 %rsp的值为x,那么执行完毕后
寄存器/地址 | 值 |
---|---|
%rdi | 输入的字符串 |
%rsi | 0x4025c3(该地址的值为"%d %d %d %d %d %d",是读入字符串的格式) |
%rdx | x |
%rcx | x+4 |
%r8 | x+8 |
%r9 | x+12 |
%rax | 输入的参数个数(函数__isoc99_sscanf@plt 的返回值) |
因只有6个寄存器用来保存参数,故将其余两个参数保存在此时的栈顶(此时堆栈栈顶为x-24):
M
[
x
+
20
]
→
M
[
x
−
16
]
,
M
[
x
+
16
]
→
M
[
x
−
24
]
.
\text{M}[x+20] \rightarrow \text{M}[x-16], \\ \text{M}[x+16] \rightarrow \text{M}[x-24].
M[x+20]→M[x−16],M[x+16]→M[x−24].
这里我们可以得出输入字符串被解析后为6个,分别储存在 x, x+4, x+8, x+12, x+ 16, x+20 这6个位置中。
回到phase_2
,我们可以翻译出伪代码为
if (M[x] != 1) {
explode_bomb
}
%rbx = x+4
%rbp = x+24
while(%rbx != %rbp) {
if (2*M[%rbx-4]) != M[%rbx]) {
explode_bomb
}
%rbx += 4
}
所以,第二题答案显然是
1 2 4 8 16 32
phase_3
0000000000400f43 <phase_3>:
400f43: 48 83 ec 18 sub $0x18,%rsp
400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
400f51: be cf 25 40 00 mov $0x4025cf,%esi // 该地址的值为"%d %d", 说明读入两个数字
400f56: b8 00 00 00 00 mov $0x0,%eax
400f5b: e8 90 fc ff ff callq 400bf0 <__isoc99_sscanf@plt>
400f60: 83 f8 01 cmp $0x1,%eax
400f63: 7f 05 jg 400f6a <phase_3+0x27>
400f65: e8 d0 04 00 00 callq 40143a <explode_bomb>
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp)
400f6f: 77 3c ja 400fad <phase_3+0x6a>
400f71: 8b 44 24 08 mov 0x8(%rsp),%eax
400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)
400f7c: b8 cf 00 00 00 mov $0xcf,%eax // x = 0
400f81: eb 3b jmp 400fbe <phase_3+0x7b>
400f83: b8 c3 02 00 00 mov $0x2c3,%eax // x = 2
400f88: eb 34 jmp 400fbe <phase_3+0x7b>
400f8a: b8 00 01 00 00 mov $0x100,%eax // x = 3
400f8f: eb 2d jmp 400fbe <phase_3+0x7b>
400f91: b8 85 01 00 00 mov $0x185,%eax // x = 4
400f96: eb 26 jmp 400fbe <phase_3+0x7b>
400f98: b8 ce 00 00 00 mov $0xce,%eax // x = 5
400f9d: eb 1f jmp 400fbe <phase_3+0x7b>
400f9f: b8 aa 02 00 00 mov $0x2aa,%eax // x = 6
400fa4: eb 18 jmp 400fbe <phase_3+0x7b>
400fa6: b8 47 01 00 00 mov $0x147,%eax // x = 7
400fab: eb 11 jmp 400fbe <phase_3+0x7b>
400fad: e8 88 04 00 00 callq 40143a <explode_bomb>
400fb2: b8 00 00 00 00 mov $0x0,%eax
400fb7: eb 05 jmp 400fbe <phase_3+0x7b>
400fb9: b8 37 01 00 00 mov $0x137,%eax // x = 1
400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax
400fc2: 74 05 je 400fc9 <phase_3+0x86>
400fc4: e8 71 04 00 00 callq 40143a <explode_bomb>
400fc9: 48 83 c4 18 add $0x18,%rsp
400fcd: c3 retq
同理,这里需要我们输入两个数字。伪代码如下
phase_3(int x, y) {
if (x < 0 || x > 7) {
explode_bomb
}
jump to *(0x402470+8*x)
x = v
if (y != x) {
explode_bomb
}
return
}
x 的地址为 %rsp+0x8,y 的地址为 %rsp+0xc。
x 的取值范围为 [0,7]. 注意我们跳转至地址(0x402470+8*x)所在的值,打印地址 0x402470 附近的值如下
(gdb) x/16x 0x402470
0x402470: 0x00400f7c 0x00000000 0x00400fb9 0x00000000
0x402480: 0x00400f83 0x00000000 0x00400f8a 0x00000000
0x402490: 0x00400f91 0x00000000 0x00400f98 0x00000000
0x4024a0: 0x00400f9f 0x00000000 0x00400fa6 0x00000000
这里根据 x 取值从 1 到 6 可以跳转的位置及对寄存器%rax的赋值为
x | 跳转地址 | %rax ( v ) |
---|---|---|
0 | 0x00400f7c | 0xcf |
1 | 0x00400fb9 | 0x137 |
2 | 0x00400f83 | 0x2c3 |
3 | 0x00400f8a | 0x100 |
4 | 0x00400f91 | 0x185 |
5 | 0x00400f98 | 0xce |
6 | 0x00400f9f | 0x2aa |
7 | 0x00400fa6 | 0x147 |
显然,以下的数对均为第三题的答案
0 207
1 311
2 707
3 256
4 389
5 206
6 682
7 327
phase_4
000000000040100c <phase_4>:
40100c: 48 83 ec 18 sub $0x18,%rsp
401010: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
401015: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
40101a: be cf 25 40 00 mov $0x4025cf,%esi // 该地址的值为"%d %d", 说明读入两个数字
40101f: b8 00 00 00 00 mov $0x0,%eax
401024: e8 c7 fb ff ff callq 400bf0 <__isoc99_sscanf@plt>
401029: 83 f8 02 cmp $0x2,%eax
40102c: 75 07 jne 401035 <phase_4+0x29>
40102e: 83 7c 24 08 0e cmpl $0xe,0x8(%rsp)
401033: 76 05 jbe 40103a <phase_4+0x2e>
401035: e8 00 04 00 00 callq 40143a <explode_bomb>
40103a: ba 0e 00 00 00 mov $0xe,%edx
40103f: be 00 00 00 00 mov $0x0,%esi
401044: 8b 7c 24 08 mov 0x8(%rsp),%edi
401048: e8 81 ff ff ff callq 400fce <func4>
40104d: 85 c0 test %eax,%eax
40104f: 75 07 jne 401058 <phase_4+0x4c>
401051: 83 7c 24 0c 00 cmpl $0x0,0xc(%rsp)
401056: 74 05 je 40105d <phase_4+0x51>
401058: e8 dd 03 00 00 callq 40143a <explode_bomb>
40105d: 48 83 c4 18 add $0x18,%rsp
401061: c3 retq
同样地,这里仍需要我们输入两个数字,第一个参数储存在 %rsp+0x8,第二个参数储存在 %rsp+0xc。伪代码为
phase_4(int x, int y) {
if (x <= 0 || x > 14) {
explode_bomb
}
%rdx = 14 // 第三个参数
%rsi = 0 // 第二个参数
%rdi = x // 第一个参数
result = func4(x, 0, 14)
if (result != 0) {
explode_bomb
}
if (y != 0) {
explode_bomb
}
return
}
那么这里就要计算函数func4
的返回值:
0000000000400fce <func4>:
400fce: 48 83 ec 08 sub $0x8,%rsp
400fd2: 89 d0 mov %edx,%eax
400fd4: 29 f0 sub %esi,%eax
400fd6: 89 c1 mov %eax,%ecx
400fd8: c1 e9 1f shr $0x1f,%ecx
400fdb: 01 c8 add %ecx,%eax
400fdd: d1 f8 sar %eax // SAR %eax = SAR $1, %eax
400fdf: 8d 0c 30 lea (%rax,%rsi,1),%ecx
400fe2: 39 f9 cmp %edi,%ecx
400fe4: 7e 0c jle 400ff2 <func4+0x24>
400fe6: 8d 51 ff lea -0x1(%rcx),%edx
400fe9: e8 e0 ff ff ff callq 400fce <func4>
400fee: 01 c0 add %eax,%eax
400ff0: eb 15 jmp 401007 <func4+0x39>
400ff2: b8 00 00 00 00 mov $0x0,%eax
400ff7: 39 f9 cmp %edi,%ecx
400ff9: 7d 0c jge 401007 <func4+0x39>
400ffb: 8d 71 01 lea 0x1(%rcx),%esi
400ffe: e8 cb ff ff ff callq 400fce <func4>
401003: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401007: 48 83 c4 08 add $0x8,%rsp
40100b: c3 retq
翻译伪代码为
func4(int a, int b, int c) {
int result = c
result -= b
int tmp = result
tmp >>= 31
result += tmp
result >>= 1
tmp = b + result // 至此tmp = 7,令 a == tmp 即可
if (tmp > a) {
c = tmp - 1
result = func4(a, b, c)
result += result
return result
}
result = 0
if (tmp == a) {
return result // 跳转至此
}
b = tmp + 1
result = func4(a, b, c)
result = 2 * result + 1
return result
}
初始值 a = x,b = 0,c = 14,目的是输入 x 使得 func4
的返回值为0。阅读前半段代码可知 tmp = 7, 此时只需令 a == tmp 即可跳转至 result = 0 的分支。
所以,第四题答案显然为
7 0
phase_5
0000000000401062 <phase_5>:
401062: 53 push %rbx
401063: 48 83 ec 20 sub $0x20,%rsp
401067: 48 89 fb mov %rdi,%rbx
40106a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
401071: 00 00
401073: 48 89 44 24 18 mov %rax,0x18(%rsp)
401078: 31 c0 xor %eax,%eax
40107a: e8 9c 02 00 00 callq 40131b <string_length>
40107f: 83 f8 06 cmp $0x6,%eax
401082: 74 4e je 4010d2 <phase_5+0x70>
401084: e8 b1 03 00 00 callq 40143a <explode_bomb>
401089: eb 47 jmp 4010d2 <phase_5+0x70>
40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx
40108f: 88 0c 24 mov %cl,(%rsp)
401092: 48 8b 14 24 mov (%rsp),%rdx
401096: 83 e2 0f and $0xf,%edx
401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx
4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1)
4010a4: 48 83 c0 01 add $0x1,%rax
4010a8: 48 83 f8 06 cmp $0x6,%rax
4010ac: 75 dd jne 40108b <phase_5+0x29>
4010ae: c6 44 24 16 00 movb $0x0,0x16(%rsp)
4010b3: be 5e 24 40 00 mov $0x40245e,%esi
4010b8: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
4010bd: e8 76 02 00 00 callq 401338 <strings_not_equal>
4010c2: 85 c0 test %eax,%eax
4010c4: 74 13 je 4010d9 <phase_5+0x77>
4010c6: e8 6f 03 00 00 callq 40143a <explode_bomb>
4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
4010d0: eb 07 jmp 4010d9 <phase_5+0x77>
4010d2: b8 00 00 00 00 mov $0x0,%eax
4010d7: eb b2 jmp 40108b <phase_5+0x29>
4010d9: 48 8b 44 24 18 mov 0x18(%rsp),%rax
4010de: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4010e5: 00 00
4010e7: 74 05 je 4010ee <phase_5+0x8c>
4010e9: e8 42 fa ff ff callq 400b30 <__stack_chk_fail@plt>
4010ee: 48 83 c4 20 add $0x20,%rsp
4010f2: 5b pop %rbx
4010f3: c3 retq
我们来逐步分析该段程序。首先,栈帧开辟了 0x20 字节的空间。程序将金丝雀值 (canary value) 写入栈地址 %rsp+0x18 中。
401062: 53 push %rbx
401063: 48 83 ec 20 sub $0x20,%rsp
401067: 48 89 fb mov %rdi,%rbx
40106a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
401071: 00 00
401073: 48 89 44 24 18 mov %rax,0x18(%rsp)
判断输入字符串长度是否为6,不为6则爆炸
401078: 31 c0 xor %eax,%eax
40107a: e8 9c 02 00 00 callq 40131b <string_length>
40107f: 83 f8 06 cmp $0x6,%eax
401082: 74 4e je 4010d2 <phase_5+0x70>
401084: e8 b1 03 00 00 callq 40143a <explode_bomb>
若输入的长度为6,跳转至如下指令
4010d2: b8 00 00 00 00 mov $0x0,%eax
4010d7: eb b2 jmp 40108b <phase_5+0x29>
...
40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx
40108f: 88 0c 24 mov %cl,(%rsp)
401092: 48 8b 14 24 mov (%rsp),%rdx
401096: 83 e2 0f and $0xf,%edx
401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx
4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1)
4010a4: 48 83 c0 01 add $0x1,%rax
4010a8: 48 83 f8 06 cmp $0x6,%rax
4010ac: 75 dd jne 40108b <phase_5+0x29>
指令 40108b - 4010ac 是一个for循环,循环6次,依次读取输入字符串的某个字符压入栈中。
翻译上述汇编语言为伪代码:
String s = input()
char* a = 0x4024b0
// s.len = 6
for (int i = 0; i < 6; i++) {
char s_loc = s.charAt(i)
stack.at(0) = s_loc // 这里stack.at(x)指的是栈x位置的值
int tmp = stack.at(0)
tmp &= 0xF // 取低四位, 取值范围为[0,15]
tmp = *(a + tmp)
stack.at(i + 0x10) = tmp & 0xF
}
我们来看在地址 0x4024b0 附近都有哪些值
(gdb) x/16x 0x4024b0
0x4024b0 <array.3449>: 0x6d 0x61 0x64 0x75 0x69 0x65 0x72 0x73
0x4024b8 <array.3449+8>: 0x6e 0x66 0x6f 0x74 0x76 0x62 0x79 0x6c
这些值与什么对应呢?先看下面的程序。跳出上述循环后
4010ae: c6 44 24 16 00 movb $0x0,0x16(%rsp)
4010b3: be 5e 24 40 00 mov $0x40245e,%esi
4010b8: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
4010bd: e8 76 02 00 00 callq 401338 <strings_not_equal>
4010c2: 85 c0 test %eax,%eax
4010c4: 74 13 je 4010d9 <phase_5+0x77>
4010c6: e8 6f 03 00 00 callq 40143a <explode_bomb>
这里取出地址 0x40245e 与 %rsp + 0x10 处的值判断是否相等,不等则爆炸。
(gdb) x/s 0x40245e
0x40245e: "flyers"
那么就很明确了,我们需要在上述for循环中依次将地址 %rsp + 0x10 - %rsp + 0x15 中的值填入 ‘s’, ‘r’, ‘e’, ‘y’, ‘l’, ‘f’。
查询ASCII码表,可以推出输入字符串低4位的值。那么,我们输入的字符串对应的低4位满足条件即可。此时,对任意一个字符,我们可以任取高四位的值。不失一般性,取高四位0x0100(等价于 低4位 + 0x01000000),得到对应的字符位
字符 | ASCII | 输入字符串低4位 | 0x01000000 + 低4位 | 左列对应的ASCII字符 |
---|---|---|---|---|
f | 0x66 | 9 | 73 | I |
l | 0x6c | 15 | 79 | O |
y | 0x79 | 14 | 78 | N |
e | 0x65 | 5 | 69 | E |
r | 0x72 | 6 | 70 | F |
s | 0x73 | 7 | 71 | G |
则我们的输入可以是"IONEFG"。当然第一个字符串的高四位你也可以取0x0101,对应的ASCII码为89,字符为Y。那么"YONEFG"也可以是你的答案。
随后跳转至 4010d9 ,检测栈有没有被破坏(即查看栈帧中的金丝雀值有没有被改变)
4010d9: 48 8b 44 24 18 mov 0x18(%rsp),%rax
4010de: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4010e5: 00 00
4010e7: 74 05 je 4010ee <phase_5+0x8c>
4010e9: e8 42 fa ff ff callq 400b30 <__stack_chk_fail@plt>
4010ee: 48 83 c4 20 add $0x20,%rsp
4010f2: 5b pop %rbx
4010f3: c3 retq
我们输入字符串的长度为6,程序将这六个字节依次保存在 %rsp + 0x10 - %rsp + 0x15中。而金丝雀值在 %rsp + 0x18中,故金丝雀值没有被改变,成功拆除炸弹。所以第五题的答案为
IONEFG
phase_6
00000000004010f4 <phase_6>:
4010f4: 41 56 push %r14
4010f6: 41 55 push %r13
4010f8: 41 54 push %r12
4010fa: 55 push %rbp
4010fb: 53 push %rbx
4010fc: 48 83 ec 50 sub $0x50,%rsp
401100: 49 89 e5 mov %rsp,%r13
401103: 48 89 e6 mov %rsp,%rsi
401106: e8 51 03 00 00 callq 40145c <read_six_numbers>
40110b: 49 89 e6 mov %rsp,%r14
40110e: 41 bc 00 00 00 00 mov $0x0,%r12d
401114: 4c 89 ed mov %r13,%rbp
401117: 41 8b 45 00 mov 0x0(%r13),%eax
40111b: 83 e8 01 sub $0x1,%eax
40111e: 83 f8 05 cmp $0x5,%eax
401121: 76 05 jbe 401128 <phase_6+0x34>
401123: e8 12 03 00 00 callq 40143a <explode_bomb>
401128: 41 83 c4 01 add $0x1,%r12d
40112c: 41 83 fc 06 cmp $0x6,%r12d
401130: 74 21 je 401153 <phase_6+0x5f>
401132: 44 89 e3 mov %r12d,%ebx
401135: 48 63 c3 movslq %ebx,%rax
401138: 8b 04 84 mov (%rsp,%rax,4),%eax
40113b: 39 45 00 cmp %eax,0x0(%rbp)
40113e: 75 05 jne 401145 <phase_6+0x51>
401140: e8 f5 02 00 00 callq 40143a <explode_bomb>
401145: 83 c3 01 add $0x1,%ebx
401148: 83 fb 05 cmp $0x5,%ebx
40114b: 7e e8 jle 401135 <phase_6+0x41>
40114d: 49 83 c5 04 add $0x4,%r13
401151: eb c1 jmp 401114 <phase_6+0x20>
401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi
401158: 4c 89 f0 mov %r14,%rax
40115b: b9 07 00 00 00 mov $0x7,%ecx
401160: 89 ca mov %ecx,%edx
401162: 2b 10 sub (%rax),%edx
401164: 89 10 mov %edx,(%rax)
401166: 48 83 c0 04 add $0x4,%rax
40116a: 48 39 f0 cmp %rsi,%rax
40116d: 75 f1 jne 401160 <phase_6+0x6c>
40116f: be 00 00 00 00 mov $0x0,%esi
401174: eb 21 jmp 401197 <phase_6+0xa3>
401176: 48 8b 52 08 mov 0x8(%rdx),%rdx
40117a: 83 c0 01 add $0x1,%eax
40117d: 39 c8 cmp %ecx,%eax
40117f: 75 f5 jne 401176 <phase_6+0x82>
401181: eb 05 jmp 401188 <phase_6+0x94>
401183: ba d0 32 60 00 mov $0x6032d0,%edx
401188: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2)
40118d: 48 83 c6 04 add $0x4,%rsi
401191: 48 83 fe 18 cmp $0x18,%rsi
401195: 74 14 je 4011ab <phase_6+0xb7>
401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx
40119a: 83 f9 01 cmp $0x1,%ecx
40119d: 7e e4 jle 401183 <phase_6+0x8f>
40119f: b8 01 00 00 00 mov $0x1,%eax
4011a4: ba d0 32 60 00 mov $0x6032d0,%edx
4011a9: eb cb jmp 401176 <phase_6+0x82>
4011ab: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx
4011b0: 48 8d 44 24 28 lea 0x28(%rsp),%rax
4011b5: 48 8d 74 24 50 lea 0x50(%rsp),%rsi
4011ba: 48 89 d9 mov %rbx,%rcx
4011bd: 48 8b 10 mov (%rax),%rdx
4011c0: 48 89 51 08 mov %rdx,0x8(%rcx)
4011c4: 48 83 c0 08 add $0x8,%rax
4011c8: 48 39 f0 cmp %rsi,%rax
4011cb: 74 05 je 4011d2 <phase_6+0xde>
4011cd: 48 89 d1 mov %rdx,%rcx
4011d0: eb eb jmp 4011bd <phase_6+0xc9>
4011d2: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx)
4011d9: 00
4011da: bd 05 00 00 00 mov $0x5,%ebp
4011df: 48 8b 43 08 mov 0x8(%rbx),%rax
4011e3: 8b 00 mov (%rax),%eax
4011e5: 39 03 cmp %eax,(%rbx)
4011e7: 7d 05 jge 4011ee <phase_6+0xfa>
4011e9: e8 4c 02 00 00 callq 40143a <explode_bomb>
4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011f2: 83 ed 01 sub $0x1,%ebp
4011f5: 75 e8 jne 4011df <phase_6+0xeb>
4011f7: 48 83 c4 50 add $0x50,%rsp
4011fb: 5b pop %rbx
4011fc: 5d pop %rbp
4011fd: 41 5c pop %r12
4011ff: 41 5d pop %r13
401201: 41 5e pop %r14
401203: c3 retq
设当前栈顶地址为 x,由phase_2可知函数read_six_numbers
将输入字符串解析为6个,分别储存在 x, x+4, x+8, x+12, x+ 16, x+20 这6个位置中。不妨设这六个参数为 x1, x2, x3, x4, x5, x6。
首先来到第一个跳转点
4010fc: 48 83 ec 50 sub $0x50,%rsp
401100: 49 89 e5 mov %rsp,%r13
401103: 48 89 e6 mov %rsp,%rsi
401106: e8 51 03 00 00 callq 40145c <read_six_numbers>
40110b: 49 89 e6 mov %rsp,%r14
40110e: 41 bc 00 00 00 00 mov $0x0,%r12d
401114: 4c 89 ed mov %r13,%rbp
401117: 41 8b 45 00 mov 0x0(%r13),%eax
40111b: 83 e8 01 sub $0x1,%eax
40111e: 83 f8 05 cmp $0x5,%eax
401121: 76 05 jbe 401128 <phase_6+0x34>
401123: e8 12 03 00 00 callq 40143a <explode_bomb>
这里寄存器 %r13 的值为 %rsp,所以是把位置为 x,即第一个参数传给 %rax。我们可以求出 x1 的取值范围为 [1,6]。
跳转至0x401128
401128: 41 83 c4 01 add $0x1,%r12d
40112c: 41 83 fc 06 cmp $0x6,%r12d
401130: 74 21 je 401153 <phase_6+0x5f>
401132: 44 89 e3 mov %r12d,%ebx
401135: 48 63 c3 movslq %ebx,%rax
401138: 8b 04 84 mov (%rsp,%rax,4),%eax
40113b: 39 45 00 cmp %eax,0x0(%rbp)
40113e: 75 05 jne 401145 <phase_6+0x51>
401140: e8 f5 02 00 00 callq 40143a <explode_bomb>
这里将第二个参数移动至%rax中,与%rbp指向的值比较,而%rbp指向栈顶,故得到的结论是:x2 != x1。
跳转至0x401145
401145: 83 c3 01 add $0x1,%ebx
401148: 83 fb 05 cmp $0x5,%ebx
40114b: 7e e8 jle 401135 <phase_6+0x41>
初始%rbx = 1。这里执行循环5次,结合上述 0x401135 - 0x40113e 的指令,说明相邻的两个参数均不相等。
跳出循环后
40114d: 49 83 c5 04 add $0x4,%r13
401151: eb c1 jmp 401114 <phase_6+0x20>
401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi
401158: 4c 89 f0 mov %r14,%rax
40115b: b9 07 00 00 00 mov $0x7,%ecx
401160: 89 ca mov %ecx,%edx
401162: 2b 10 sub (%rax),%edx
401164: 89 10 mov %edx,(%rax)
401166: 48 83 c0 04 add $0x4,%rax
40116a: 48 39 f0 cmp %rsi,%rax
40116d: 75 f1 jne 401160 <phase_6+0x6c>
%r13 储存的是第二个参数的地址,先跳转到 0x401114
401114: 4c 89 ed mov %r13,%rbp
401117: 41 8b 45 00 mov 0x0(%r13),%eax
40111b: 83 e8 01 sub $0x1,%eax
40111e: 83 f8 05 cmp $0x5,%eax
401121: 76 05 jbe 401128 <phase_6+0x34>
401123: e8 12 03 00 00 callq 40143a <explode_bomb>
401128: 41 83 c4 01 add $0x1,%r12d
40112c: 41 83 fc 06 cmp $0x6,%r12d
401130: 74 21 je 401153 <phase_6+0x5f>
可以看出这里也是一个循环,共5次。至此,结合上述分析我们可以初步得到结论:
- 每个参数的取值范围为 [1,6]
- 相邻的两个参数均不相等
跳转至0x401153
401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi
401158: 4c 89 f0 mov %r14,%rax
40115b: b9 07 00 00 00 mov $0x7,%ecx
401160: 89 ca mov %ecx,%edx
401162: 2b 10 sub (%rax),%edx
401164: 89 10 mov %edx,(%rax)
401166: 48 83 c0 04 add $0x4,%rax
40116a: 48 39 f0 cmp %rsi,%rax
40116d: 75 f1 jne 401160 <phase_6+0x6c>
这里仍是一个循环。我们直接翻译伪代码为
%rsi = %rsp + 24
%rax = %rsp
while(%rax != %rsi) {
M[%rax] = 7 - M[%rax] // M[%rax + 4*k] = xk, 这里 k 取 0,1,2,3,4,5
%rax += 4
}
可以看到这段循环修改了栈帧中参数的值,不妨设修改后的值为 x1’, x2’, x3’, x4’, x5’, x6’,那么我们有
x
i
′
=
7
−
x
i
,
x_i' = 7 - x_i,
xi′=7−xi,
且
x
i
′
∈
[
1
,
6
]
.
x_i' \in [1,6].
xi′∈[1,6].
这里 i = 1, 2, 3, 4, 5, 6.
跳出循环后
40116f: be 00 00 00 00 mov $0x0,%esi
401174: eb 21 jmp 401197 <phase_6+0xa3>
401176: 48 8b 52 08 mov 0x8(%rdx),%rdx
40117a: 83 c0 01 add $0x1,%eax
40117d: 39 c8 cmp %ecx,%eax
40117f: 75 f5 jne 401176 <phase_6+0x82>
401181: eb 05 jmp 401188 <phase_6+0x94>
401183: ba d0 32 60 00 mov $0x6032d0,%edx
401188: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2)
40118d: 48 83 c6 04 add $0x4,%rsi
401191: 48 83 fe 18 cmp $0x18,%rsi
401195: 74 14 je 4011ab <phase_6+0xb7>
401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx
40119a: 83 f9 01 cmp $0x1,%ecx
40119d: 7e e4 jle 401183 <phase_6+0x8f>
40119f: b8 01 00 00 00 mov $0x1,%eax
4011a4: ba d0 32 60 00 mov $0x6032d0,%edx
4011a9: eb cb jmp 401176 <phase_6+0x82>
将 %rsi 赋值为0,随即立即跳转至 0x401197。这里有一个分支,我们先来看 x1’ > 1 , 即 x1’ != 1的分支:
跳转至 0x401176。我们查看内存地址为 0x6032d0 附近的值
(gdb) x/24x 0x6032d0
0x6032d0 <node1>: 0x0000014c 0x00000001 0x006032e0 0x00000000
0x6032e0 <node2>: 0x000000a8 0x00000002 0x006032f0 0x00000000
0x6032f0 <node3>: 0x0000039c 0x00000003 0x00603300 0x00000000
0x603300 <node4>: 0x000002b3 0x00000004 0x00603310 0x00000000
0x603310 <node5>: 0x000001dd 0x00000005 0x00603320 0x00000000
0x603320 <node6>: 0x000001bb 0x00000006 0x00000000 0x00000000
很显然这是一个链表。
0x401176 - 0x40117f 是一个循环,初始值 $rax = 1,%rdx = 0x6032d0, %ecx = x1’
翻译伪代码为
if (x1' == 1) {
%rdx = 0x6032d0
} else {
for(i = 1; i < x1'; i++) {
%rdx = M[%rdx + 0x8]
}
}
我们可以整理出 x 取值与 %rdx 取值对照表
x | %rdx | M[%rdx] |
---|---|---|
1 | 0x6032d0 | 0x14c |
2 | 0x6032e0 | 0xa8 |
3 | 0x6032f0 | 0x39c |
4 | 0x603300 | 0x2b3 |
5 | 0x603310 | 0x1dd |
6 | 0x603320 | 0x1bb |
可设该映射为
%
rdx
=
f
(
x
)
,
x
∈
[
1
,
6
]
.
\%\text{rdx} = f(x),\ x \in [1,6].
%rdx=f(x), x∈[1,6].
把 %rdx 移动到 %rsp + 32 的位置。随后仍是一个循环
翻译伪代码
%rsi = 0x0
x[] = {x1',x2',x3',x4',x5',x6'} // 参数列表
while(%rsi != 24) {
M[%rsp + 2*rsi + 0x20] = %rdx
%rsi += 4
int px = x[%rsi / 4 - 1] // 依次取x1',x2',x3',x4',x5',x6'
%rdx = f(px)
}
此时 %rsp + 0x20, 0x28, 0x30, 0x38, 0x40, 0x48 的值分别为f(x1’), f(x2’), … f(x6’).
继续查看其跳转位置 0x4011ab
4011ab: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx
4011b0: 48 8d 44 24 28 lea 0x28(%rsp),%rax
4011b5: 48 8d 74 24 50 lea 0x50(%rsp),%rsi
4011ba: 48 89 d9 mov %rbx,%rcx
4011bd: 48 8b 10 mov (%rax),%rdx
4011c0: 48 89 51 08 mov %rdx,0x8(%rcx)
4011c4: 48 83 c0 08 add $0x8,%rax
4011c8: 48 39 f0 cmp %rsi,%rax
4011cb: 74 05 je 4011d2 <phase_6+0xde>
4011cd: 48 89 d1 mov %rdx,%rcx
4011d0: eb eb jmp 4011bd <phase_6+0xc9>
0x 4011bd - 0x4011d0 仍是一个循环,翻译伪代码为
%rsi = %rsp + 0x50
%rax = %rsp + 0x28
%rbx = f(x1')
%rcx = %rbx
while(%rax != %rsi) {
%rdx = M[%rax] // 依次取f(x2'), f(x3'), ...,f(x6')
M[%rcx + 0x8] = %rdx
%rax += 0x8
%rcx = %rdx // 最后一次循环不执行该步骤,这里忽略
}
这里进行了如下操作
M
[
f
(
x
k
′
)
+
0x
8
]
=
f
(
x
k
+
1
′
)
,
k
∈
[
1
,
5
]
.
M[f(x_k') + \text{0x}8] = f(x_{k+1}'), \ k \in[1,5].
M[f(xk′)+0x8]=f(xk+1′), k∈[1,5].
跳出循环后来到 0x4011d2
4011d2: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx)
4011d9: 00
4011da: bd 05 00 00 00 mov $0x5,%ebp
4011df: 48 8b 43 08 mov 0x8(%rbx),%rax
4011e3: 8b 00 mov (%rax),%eax
4011e5: 39 03 cmp %eax,(%rbx)
4011e7: 7d 05 jge 4011ee <phase_6+0xfa>
4011e9: e8 4c 02 00 00 callq 40143a <explode_bomb>
4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx
4011f2: 83 ed 01 sub $0x1,%ebp
4011f5: 75 e8 jne 4011df <phase_6+0xeb>
这里仍是一个循环,翻译伪代码为
F[] = {f(x1'), f(x2'), f(x3'), f(x4'), f(x5'), f(x6')}
for(i = 0;i <= 5;i++) {
if (M[F[i]] < M[F[i+1]]) {
explode_bomb
}
}
综上所述,我们有如下结论:
- 每个参数的取值范围为 [1,6]
- 相邻的两个参数均不相等
- 有如下等式成立
M [ f ( x 1 ′ ) ] > = M [ f ( x 2 ′ ) ] > = . . . > = M [ f ( x 6 ′ ) ] . M[f(x_1')] >= M[f(x_2')] >= ... >= M[f(x6')]. M[f(x1′)]>=M[f(x2′)]>=...>=M[f(x6′)].
那么,我们只需对 M[f(x’)] 排序即可。对其从大到小排列的 x’ 取值依次为 3, 4, 5, 6, 1, 2,再转化为 x 的取值为 4, 3, 2, 1, 6, 5.
最终,恢复调用栈,程序结束。
4011f7: 48 83 c4 50 add $0x50,%rsp
4011fb: 5b pop %rbx
4011fc: 5d pop %rbp
4011fd: 41 5c pop %r12
4011ff: 41 5d pop %r13
401201: 41 5e pop %r14
401203: c3 retq
第 6 题的答案为
4 3 2 1 6 5
我们把结果保存在 solutions.txt里,运行可得
Border relations with Canada have never been better.
1 2 4 8 16 32
0 207
7 0
IONEFG
4 3 2 1 6 5
(gdb) run solutions.txt
Starting program: /csapp/bomb/bomb solutions.txt
warning: Error disabling address space randomization: Operation not permitted
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2. Keep going!
Halfway there!
So you got that one. Try this one.
Good work! On to the next...
Congratulations! You've defused the bomb!
[Inferior 1 (process 43) exited normally]
secret_phase
首先,查看汇编语言,发现在phase_defused
阶段可以进入secret_phase
:
00000000004015c4 <phase_defused>:
4015c4: 48 83 ec 78 sub $0x78,%rsp
4015c8: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
4015cf: 00 00
4015d1: 48 89 44 24 68 mov %rax,0x68(%rsp)
4015d6: 31 c0 xor %eax,%eax
4015d8: 83 3d 81 21 20 00 06 cmpl $0x6,0x202181(%rip) # 603760 <num_input_strings>
4015df: 75 5e jne 40163f <phase_defused+0x7b>
4015e1: 4c 8d 44 24 10 lea 0x10(%rsp),%r8
4015e6: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx
4015eb: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
4015f0: be 19 26 40 00 mov $0x402619,%esi
4015f5: bf 70 38 60 00 mov $0x603870,%edi
4015fa: e8 f1 f5 ff ff callq 400bf0 <__isoc99_sscanf@plt>
4015ff: 83 f8 03 cmp $0x3,%eax
401602: 75 31 jne 401635 <phase_defused+0x71>
401604: be 22 26 40 00 mov $0x402622,%esi
401609: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi
40160e: e8 25 fd ff ff callq 401338 <strings_not_equal>
401613: 85 c0 test %eax,%eax
401615: 75 1e jne 401635 <phase_defused+0x71>
401617: bf f8 24 40 00 mov $0x4024f8,%edi
40161c: e8 ef f4 ff ff callq 400b10 <puts@plt>
401621: bf 20 25 40 00 mov $0x402520,%edi
401626: e8 e5 f4 ff ff callq 400b10 <puts@plt>
40162b: b8 00 00 00 00 mov $0x0,%eax
401630: e8 0d fc ff ff callq 401242 <secret_phase>
401635: bf 58 25 40 00 mov $0x402558,%edi
40163a: e8 d1 f4 ff ff callq 400b10 <puts@plt>
40163f: 48 8b 44 24 68 mov 0x68(%rsp),%rax
401644: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
40164b: 00 00
40164d: 74 05 je 401654 <phase_defused+0x90>
40164f: e8 dc f4 ff ff callq 400b30 <__stack_chk_fail@plt>
401654: 48 83 c4 78 add $0x78,%rsp
401658: c3 retq
注意到在输入三个参数时,即可进入。我们把出现的地址对应的值都打出开,看看有什么端倪:
(gdb) x/s 0x402619
0x402619: "%d %d %s"
(gdb) x/s 0x603870
0x603870 <input_strings+240>: "7 0"
(gdb) x/s 0x402622
0x402622: "DrEvil"
(gdb) x/s 0x4024f8
0x4024f8: "Curses, you've found the secret phase!"
(gdb) x/s 0x402520
0x402520: "But finding it and solving it are quite different..."
注意到输入格式为 2个数字 + 字符串,那么满足条件的仅有第3和第4题。又注意到 “7 0” 其实是第4题的答案,那我们尝试在第4题的答案后加入"DrEvil"。我们将以下输入保存为 secret.txt 并运行
Border relations with Canada have never been better.
1 2 4 8 16 32
0 207
7 0 DrEvil
IONEFG
4 3 2 1 6 5
成功进入!
下面是 secret_phase
的代码:
0000000000401242 <secret_phase>:
401242: 53 push %rbx
401243: e8 56 02 00 00 callq 40149e <read_line>
401248: ba 0a 00 00 00 mov $0xa,%edx
40124d: be 00 00 00 00 mov $0x0,%esi
401252: 48 89 c7 mov %rax,%rdi
401255: e8 76 f9 ff ff callq 400bd0 <strtol@plt>
40125a: 48 89 c3 mov %rax,%rbx
40125d: 8d 40 ff lea -0x1(%rax),%eax
401260: 3d e8 03 00 00 cmp $0x3e8,%eax
401265: 76 05 jbe 40126c <secret_phase+0x2a>
401267: e8 ce 01 00 00 callq 40143a <explode_bomb>
40126c: 89 de mov %ebx,%esi
40126e: bf f0 30 60 00 mov $0x6030f0,%edi
401273: e8 8c ff ff ff callq 401204 <fun7>
401278: 83 f8 02 cmp $0x2,%eax
40127b: 74 05 je 401282 <secret_phase+0x40>
40127d: e8 b8 01 00 00 callq 40143a <explode_bomb>
401282: bf 38 24 40 00 mov $0x402438,%edi
401287: e8 84 f8 ff ff callq 400b10 <puts@plt>
40128c: e8 33 03 00 00 callq 4015c4 <phase_defused>
401291: 5b pop %rbx
401292: c3 retq
我们主要来看 func7
,需要保证其返回值为2。这里传入参数 %rdi = 0x6030f0:
0000000000401204 <fun7>:
401204: 48 83 ec 08 sub $0x8,%rsp
401208: 48 85 ff test %rdi,%rdi
40120b: 74 2b je 401238 <fun7+0x34>
40120d: 8b 17 mov (%rdi),%edx
40120f: 39 f2 cmp %esi,%edx
401211: 7e 0d jle 401220 <fun7+0x1c>
401213: 48 8b 7f 08 mov 0x8(%rdi),%rdi
401217: e8 e8 ff ff ff callq 401204 <fun7>
40121c: 01 c0 add %eax,%eax
40121e: eb 1d jmp 40123d <fun7+0x39>
401220: b8 00 00 00 00 mov $0x0,%eax
401225: 39 f2 cmp %esi,%edx
401227: 74 14 je 40123d <fun7+0x39>
401229: 48 8b 7f 10 mov 0x10(%rdi),%rdi
40122d: e8 d2 ff ff ff callq 401204 <fun7>
401232: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax
401236: eb 05 jmp 40123d <fun7+0x39>
401238: b8 ff ff ff ff mov $0xffffffff,%eax
40123d: 48 83 c4 08 add $0x8,%rsp
401241: c3 retq
%rsi 保存的值为我们输入的数字。我们看看 0x6030f0 附近的值:
(gdb) x/80x 0x6030f0
0x6030f0 <n1>: 0x00000024 0x00000000 0x00603110 0x00000000
0x603100 <n1+16>: 0x00603130 0x00000000 0x00000000 0x00000000
0x603110 <n21>: 0x00000008 0x00000000 0x00603190 0x00000000
0x603120 <n21+16>: 0x00603150 0x00000000 0x00000000 0x00000000
0x603130 <n22>: 0x00000032 0x00000000 0x00603170 0x00000000
0x603140 <n22+16>: 0x006031b0 0x00000000 0x00000000 0x00000000
0x603150 <n32>: 0x00000016 0x00000000 0x00603270 0x00000000
0x603160 <n32+16>: 0x00603230 0x00000000 0x00000000 0x00000000
0x603170 <n33>: 0x0000002d 0x00000000 0x006031d0 0x00000000
0x603180 <n33+16>: 0x00603290 0x00000000 0x00000000 0x00000000
0x603190 <n31>: 0x00000006 0x00000000 0x006031f0 0x00000000
0x6031a0 <n31+16>: 0x00603250 0x00000000 0x00000000 0x00000000
0x6031b0 <n34>: 0x0000006b 0x00000000 0x00603210 0x00000000
0x6031c0 <n34+16>: 0x006032b0 0x00000000 0x00000000 0x00000000
0x6031d0 <n45>: 0x00000028 0x00000000 0x00000000 0x00000000
0x6031e0 <n45+16>: 0x00000000 0x00000000 0x00000000 0x00000000
0x6031f0 <n41>: 0x00000001 0x00000000 0x00000000 0x00000000
0x603200 <n41+16>: 0x00000000 0x00000000 0x00000000 0x00000000
0x603210 <n47>: 0x00000063 0x00000000 0x00000000 0x00000000
0x603220 <n47+16>: 0x00000000 0x00000000 0x00000000 0x00000000
这个类似于结构体(是一个二叉树):
struct node {
int val;
struct node* left;
struct node* right;
}
画出其二叉树为
36
/ \
8 50
/ \ / \
/ \ / \
6 22 45 107
/ \ / \ / \ / \
1 7 20 35 40 47 99 1001
翻译代码为
func7(node* root, int x) {
if (root == 0) {
return 0xFFFFFFFF;
}
if (root->val == x) {
return 0;
}
if (root->val < x) {
int result = func7(root->right, x);
return 2 * result + 1;
} else {
int result = func(root->left, x);
return 2 * result;
}
}
为使 func7
返回2,根据上述代码可以凑出:2 = 2 * ( 2 * (0) + 1)。
该递归过程依次返回 0, 1, 2, 分别对应根节点值 等于,小于,大于输入的参数,那么正过来依次为根节点值大于,小于,等于输入的参数,对应二叉树路线为:36 -> 8 -> 22。
综上,我们输入的值,即隐藏关的答案应为
22
最后,让我们在 secret.txt 最后一行加入 22,运行结果为
(gdb) run secret.txt
Starting program: /csapp/bomb/bomb secret.txt
warning: Error disabling address space randomization: Operation not permitted
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2. Keep going!
Halfway there!
So you got that one. Try this one.
Good work! On to the next...
Curses, you've found the secret phase!
But finding it and solving it are quite different...
Wow! You've defused the secret stage!
Congratulations! You've defused the bomb!
[Inferior 1 (process 107) exited normally]
成功!