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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
|
## NB
The default assembly codes are generated by GDB in runtime.
Those generated by Objdump would be commited with `# OBJDUMP`
If it's connected to a grading system (eg. when this app is sent by your professor)
remember to set a breakpoint for ALL `<explode_bomb>`, so that your GPA won't be affected :)
English version will be available soon
# commands
`set disassembly-flavor att` nano ~/.gdbinit
`b phase_1` 设置断点
`x/s` 访问内存 string
`si` 步入
`ni` 步过
`x/6dw $rsp`
- /6 6个单位
- d 十进制
- w 4字节
`x/8a`
- a 作为地址(0x+偏移量)
`x/5i`
- i 作为指令
`checkpoint`: 创建当前时刻的快照。
`info checkpoints`: 查看所有已创建的快照。
`restart <id>`: 回滚(恢复)到指定的快照 ID。
`delete checkpoint <id>`: 删除快照。
# Phase 1
直接在寄存器里面找字符串
```asm
0000000000400ee0 <phase_1>:
400ee0: 48 83 ec 08 sub $0x8,%rsp # 栈对齐
400ee4: be 00 24 40 00 mov $0x402400,%esi # %edi存储的是phase_1被调用的参数,%esi就是存储第二个比较的参数
400ee9: e8 4a 04 00 00 call 401338 <strings_not_equal>
400eee: 85 c0 test %eax,%eax # 检测返回值是不是0
400ef0: 74 05 je 400ef7 <phase_1+0x17> # 如果返回值是0
400ef2: e8 43 05 00 00 call 40143a <explode_bomb>
400ef7: 48 83 c4 08 add $0x8,%rsp # 恢复rsp
400efb: c3 ret
```
# Phase 2
`ni` 单步步过
随便输入一些根本不可能是答案的数字
遇到了不跳转,然后下面就是bomb的
答案肯定在cmp的其中一个操作数里面
# Phase 3
%esi对应的是格式字符
0x4025cf: "%d %d"
需要输入2个整数
```asm
400f51: be cf 25 40 00 mov $0x4025cf,%esi
400f56: b8 00 00 00 00 mov $0x0,%eax
400f5b: e8 90 fc ff ff call 400bf0 <__isoc99_sscanf@plt>
```
```c
int sscanf(const char *str, const char *format, ...);
```
之后找到cmp,答案就在寄存器里面
# Phase 4
func返回值需要是0,才能通关
如果第一次就(mid <= input) 并且 (mid >= input),明显只有7满足,那就会直接返回0
如果第一次不是7,那么要确保每次都走左边
如果最深层的right - left = 1,那么range在400fe6为0,400fee操作之后也保持0
最终最外层的func4也会返回0
func4运行完之后还会检测第二个数字,别被坑了(第二个很简单的)
```asm
0x401048 <phase_4+003c> call 0x400fce <func4>
0x40104d <phase_4+0041> test %eax, %eax
0x40104f <phase_4+0043> jne 0x401058 <phase_4+76> TAKEN [Reason: !Z]
↳ 0x401058 <phase_4+004c> call 0x40143a <explode_bomb>
```
```asm
# OBJDUMP
0000000000400fce <func4>:
400fce: 48 83 ec 08 sub $0x8,%rsp
400fd2: 89 d0 mov %right,%range
400fd4: 29 f0 sub %left,%range
400fd6: 89 c1 mov %range,%mid
400fd8: c1 e9 1f shr $0x1f,%mid
400fdb: 01 c8 add %mid,%range
400fdd: d1 f8 sar $1,%range
400fdf: 8d 0c 30 lea (%range,%left,1),%mid
400fe2: 39 f9 cmp %input,%mid
# range = right - left
# mid = left + (range/2)
400fe4: 7e 0c jle 400ff2 <func4+0x24> # if (mid > input), left part
# left part
# [left, mid-1]
400fe6: 8d 51 ff lea -0x1(%mid),%right # right = mid - 1
400fe9: e8 e0 ff ff ff call 400fce <func4>
400fee: 01 c0 add %range,%range # range = 2 * range
400ff0: eb 15 jmp 401007 <func4+0x39>
# right part
# [mid+1, right]
400ff2: b8 00 00 00 00 mov $0x0,%range
400ff7: 39 f9 cmp %input,%mid
400ff9: 7d 0c jge 401007 <func4+0x39> # if (mid >= input) ret
400ffb: 8d 71 01 lea 0x1(%mid),%left # left = mid + 1
400ffe: e8 cb ff ff ff call 400fce <func4>
401003: 8d 44 00 01 lea 0x1(%range,%range,1),%range # range = 2*range + 1
401007: 48 83 c4 08 add $0x8,%rsp
40100b: c3 ret # ret range (eax)
# all caller saved
# range --- eax
# left --- esi
# right --- edx
# input --- rdi (constant)
```
## Solution
Border relations with Canada have never been better.
1 2 4 8 16 32
7 327
7 0
|