aboutsummaryrefslogtreecommitdiff
path: root/bomblab/solution.md
blob: 7458165ce63cdfeb381d50cc432022f2872ef0cd (plain)
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266


## 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

👍😍❤️ _ov4

```
➜  bomblab git:(main) ✗ ./bomb ans.txt
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!
➜  bomblab git:(main) ✗ 
```


# 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>`: 删除快照。

`objdump -s -j .rodata bomb` 查看静态只读数据


# 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)
```



# Phase 5

字符转码表
`4024b0 maduiersnfotvbyl`

答案
`40245e flyers`

简单的for循环,遍历每个字符,然后查表转换之后放到stack

```asm
# OBJDUMP

# phase_5(char* input)
# %index = %rax
# %input = %rdi

# FOR loop, index = [0, 6)
  40108b:	0f b6 0c 03          	movzbl (%rbx,%index,1),%ecx     # rcx = *(input + index), get each input char
  40108f:	88 0c 24             	mov    %cl,(%rsp)               # stacktop = rcx
  401092:	48 8b 14 24          	mov    (%rsp),%rdx              # rdx = stacktop = rcx
  401096:	83 e2 0f             	and    $0xf,%edx                # rdx = stacktop & 0xF
  401099:	0f b6 92 b0 24 40 00 	movzbl 0x4024b0(%rdx),%edx      # rdx = *(0x4024b0 + rdx), get lower 4 bits

                                                                # rdx = *[*(input+index) & 0xF + 0x4024b0]
                                                                # find 0x4024b0 + bias

  4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%index,1)  # *(rsp+index+10) = rdx (the correspoding data)
  4010a4:	48 83 c0 01          	add    $0x1,%index              # index++
  4010a8:	48 83 f8 06          	cmp    $0x6,%index              # if (index == 6) break
  4010ac:	75 dd                	jne    40108b <phase_5+0x29>
                                                                # rsp+(10 to 15) are where passwords are saved, on stack


  4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp)          # \0
  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi          # compare (coded input) against ($0x40245e)
  4010bd:	e8 76 02 00 00       	call   401338 <strings_not_equal>
  4010c2:	85 c0                	test   %index,%index
  4010c4:	74 13                	je     4010d9 <phase_5+0x77>
```


# Phase 6

汇编太长了,自己去`phase6.asm`看,有完整的注释

1. 输入`6`个数组,检测是否在`[1, 6]`,并且没有重复

2. 假设原来的输入是`x`,将它转换成`7-x`,存储在`[rsp, rsp+20]` (`rsp+23`是最后一个占用的字节)

3. 按coded input的顺序,将地址提取到`rsp+32`开始的连续内存;如果coded input是`4 5 1 2 3`,那么rsp+32开始的指针对应的就是`node 4, 5, 2, 1, 3`的地址

4. 更新`node.next`,指向重新连接之后的下一个`node`,使得`next`的顺序与`[rsp+32, rsp+72]`存储的顺序相符(ie. 不要了`rsp+32`之后的内容,也知道重新连接之后的链表的顺序)

5. 检测顺序(要求是降序);很明显 ,`node 3, 4, 5, 6, 1, 2`排序时刚好降序,答案自然就出来了



```c
// 16 bytes each
struct idk {
  int num;    // 4 bytes
  int index;  // 4 bytes
  idk *next;  // 8 bytes
} node[6];
```

```
gef➤  x/24dwx 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
```



## Solution

See `ans.txt`