CSAPP Bomb Lab 达成

这是我第一个完成的 lab, 这篇博客其实是边做边记的解决过程改的, 所以比较简略, 其他 Lab 的记录博客会详细一点, 这篇博客以后有时间也会补充

Phase 0x1

Border relations with Canada have never been better. 根据调用约定, gdb 直接读 phase_1()$rdi 即可.

Phase 0x2

1 2 4 8 16 32 利用断点跳到 read_six_number 后, 发现核心代码 add %eax %eax, 即不断 \(\times 2\)

Phase 0x3

5 206 rsp+8, rsp+12 分别是输入的两个数, int 占四字节.

发现一堆 mov, jmp, 里只有第五个的差能整除 \(8\) 并且 mov 过去的值是 206

(最后几行是核心代码 cmp 0xc(%rsp), %eax)

Phase 0x4

7 0 打断点到 phase_4(), 阅读汇编得大概逻辑:

  • 输入两个数, 输入的第一个数 <= 14
  • %rdi: 输入的第一个数
  • %rsi: \(0\)
  • %rdx: \(14\)
  • 调用 func4(x1, 0, 14, 0);

进入 func4() 继续阅读汇编, 顺便翻译一下(伪代码), 是个比较麻烦的递归函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func4(rdi, rsi, rdx, rcx): 
rax = rdx - rsi
rcx = rax shr 31
rax += rcx
rax sar= 1
rcx = rax + rsi

// summary:
// rax = ((rdx - rsi) + (rdx - rsi) shr 31) sar 1
// rcx = ((rdx - rsi) + (rdx - rsi) shr 31) sar 1 + rsi
if (rdi <= rcx)
// 跳到ff2
if(rdi >= rcx)
return 0;
else
rax = func4(rdi, rsi = rcx + 1, rdx, rcx)
return (rax = 2 * rax + 1)
else
rax = fun4(rdi, rsi, rdx = rcx - 1, rcx)
return 2 * rax

优化后得:

1
2
3
4
5
6
7
8
9
f (x1, $2, $3): 
if x1 <= $2 + ($3 - $2)/2:
if x1 == $2 + ($3 - $2)/2: return 0
else: return 2 * f(x1, ($3 - $2)/2 + 1, $3) + 1
else:
return 2 * f(x1, $2, $2 + ($3 - $2)/2 - 1)
// 简单模拟一下看看
10 0 14
return 2 * f(10, 0, 6)

再后面的逻辑:

1
2
3
if(func4(..) == 0 && ($rsp + 8) == 0) {
ok
} else bomb!

然后发现其实令 \(x1 = \$2 + (\$3 - \$2) \div 2\)\(0 + (14 - 2) \div 2 = 7\) 就行了

Phase 0x5

y_^EFG

字符串长度为 6

简单把汇编翻译一下伪代码:

1
2
3
4
5
6
7
8
9
10
11
rbx = rdi(input)
rax = 0
while(rax < 6) {
rdx = (*(rbx + rax) & 0xf) + 0x4024b0 // input[i] & 1111 + 0x4024b0
*(rsp + 10 + rax) = rdx // input[i] =
rax += 1 // i++;
}
*(rsp + 16) = 0 // '\0'
rdi = rsp + 10 // input after processing
rsi = $0x40245e // "flyers"
strings_not_equal(rdi, rsi)

简单模拟一下:

1
2
3
4
           11
maduiersnfotvbyl
0123456789
10
1
2
3
4
5
6
7
8
@: 0x4024b0
f: 0x4024b9 +9 0x0009
l: 0x4024bf +15 0x000f
y: 0x4024be +14 0x000e
e: 0x4024b5 +5 0x0005
r: 0x4024b6 +6 0x0006
s: 0x4024b7 +7 0x0007
+dd
  • input[i] ASCII 取后四位 + 0x4024b0
  • 令一个字符c的ASCII的十六进制后四位分别为以上的
  • %rdx 64 位, %edx 32 位,$dl 8 位
  • p /x $dl 输出 0x61, 这里十六进制的每一位都是四位二进制: 6\(4\) bit, 1 也是 \(4\) bit, 十六进制最大 \(15_{16} = 1111_2\)
  • 所以只要ASCII 是 0x*9, 0x*f, 0x*e, 0x*5, 0x*6, 0x*7 的字符就可以了

这里随便选的 y_^EFG

Phase 0x6

1
2
r13 =  <+8>rsp  (init - 50)
rsi = <+8>rsp

这个难度有点大.

  1. 总之是先查6个数都<=6 且不能重复

  2. 然后将每个数 x 变为 7 - x

  3. 然后根据新的xi安排在0x6032d0处的一个链表, 安排完要求顺序值递减

查看一下这个链表:

1
2
3
4
5
6
7
(gdb) x/24w 0x6032d0
0x6032d0 <node1>: 332 1 6304480 0
0x6032e0 <node2>: 168 2 6304496 0
0x6032f0 <node3>: 924 3 6304512 0
0x603300 <node4>: 691 4 6304528 0
0x603310 <node5>: 477 5 6304544 0
0x603320 <node6>: 443 6 0 0

递减的话就是 3 4 5 6 1 2 根据7映射回去就是 4 3 2 1 6 5

读入6个数字(%rsp也变了)

以下伪代码, <> 用于标记行号, 这汇编写的链表可读性有点高啊

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
r14 = <+23>rsp
r12d = 0
rbp = r13
rax = *r13 - 1
if(rax >= 5) {
//跳到52
r12d += 1
if(r12d == 6) {
// 跳到 95
rsi = *(rsp + 0x18)
rax = r14
rcx = 0x7

<+118>
while(rax != rsi) {
<+108>
rdx = rcx
rdx -= *rax
*rax = rdx
rax += 0x4
}

<+123>
rsi = 0

<+166>
while(rcx >= 1) {
<+163>
rcx = *(rsp + rsi)
while(rcx >= 0x1) {
// 跳到<+143>
<+143>
rdx = $0x6032d0
<+148>
*(0x20 + rsp + 2 * rsi) = rdx
<+153>
rsi += 0x4
if(rsi = 0x18) {
// 跳到183
<+183>
codes here (201)
<+212>
while(rax != rsi) {

}else {

}
if(rax == rsi) {
//跳到222
<+222>
codes here
}else {
<+217>
codes here
<+220>
直接跳201了, 我不理解
}
}else {
<+163>
rcx = *(rsp + rsi)
}
}
}

rax = 0x1
rdx = 0x6032d0
// 跳到 + 130
<+130>
do {
rdx = *(rdx + 0x8)
rax += 1
}while(rax != rcx);
// 跳到<+148>
goto <+148>:
goto <+143>:

}
}else bomb!