CSAPP - Bomb Lab

phase_1

第一关整体上就是比较字符串,判断输入的字符串于目的字符串不一样的时候炸弹就发生爆炸。

输入比较的字符串即可通过第一关:

1
Border relations with Canada have never been better.

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
43
44
45
46
47
48
49
50
51
52
53
54
55
text:0000000000400EFC push    rbp
.text:0000000000400EFD push rbx
.text:0000000000400EFE sub rsp, 28h
.text:0000000000400F02 mov rsi, rsp
.text:0000000000400F05 call read_six_numbers
.text:0000000000400F05
.text:0000000000400F0A cmp [rsp+38h+var_38], 1 ; 第一个开始为1
.text:0000000000400F0A ; 输入的数偏移依次为38 34 30 26 22 18
.text:0000000000400F0E jz short loc_400F30 ; 将输入的值从第二个开始取
.text:0000000000400F0E ; 刚好对应上面便宜量为38,此处为34,向下取了4个单位的地址
.text:0000000000400F0E
.text:0000000000400F10 call explode_bomb
.text:0000000000400F10
.text:0000000000400F15 ; ---------------------------------------------------------------------------
.text:0000000000400F15 jmp short loc_400F30 ; 将输入的值从第二个开始取
.text:0000000000400F15 ; 刚好对应上面便宜量为38,此处为34,向下取了4个单位的地址
.text:0000000000400F15
.text:0000000000400F17 ; ---------------------------------------------------------------------------
.text:0000000000400F17
.text:0000000000400F17 loc_400F17: ; CODE XREF: phase_2+30↓j
.text:0000000000400F17 ; phase_2+3E↓j
.text:0000000000400F17 mov eax, [rbx-4] ; 取输入的奇数个存放到eax
.text:0000000000400F1A add eax, eax ; eax * 2
.text:0000000000400F1C cmp [rbx], eax ; 输入的偶数个等于上一个数的两倍
.text:0000000000400F1E jz short loc_400F25 ; 向下取4个字节
.text:0000000000400F1E
.text:0000000000400F20 call explode_bomb
.text:0000000000400F20
.text:0000000000400F25 ; ---------------------------------------------------------------------------
.text:0000000000400F25
.text:0000000000400F25 loc_400F25: ; CODE XREF: phase_2+22↑j
.text:0000000000400F25 add rbx, 4 ; 向下取4个字节
.text:0000000000400F29 cmp rbx, rbp ; 不为0就跳转
.text:0000000000400F2C jnz short loc_400F17 ; 取输入的奇数个存放到eax
.text:0000000000400F2C
.text:0000000000400F2E jmp short loc_400F3C
.text:0000000000400F2E
.text:0000000000400F30 ; ---------------------------------------------------------------------------
.text:0000000000400F30
.text:0000000000400F30 loc_400F30: ; CODE XREF: phase_2+12↑j
.text:0000000000400F30 ; phase_2+19↑j
.text:0000000000400F30 lea rbx, [rsp+38h+var_34] ; 将输入的值从第二个开始取
.text:0000000000400F30 ; 刚好对应上面便偏移为38,此处为34,向下取了4个单位的地址
.text:0000000000400F35 lea rbp, [rsp+38h+var_20] ; %rbp=0
.text:0000000000400F3A jmp short loc_400F17 ; 取输入的奇数个存放到eax
.text:0000000000400F3A
.text:0000000000400F3C ; ---------------------------------------------------------------------------
.text:0000000000400F3C
.text:0000000000400F3C loc_400F3C: ; CODE XREF: phase_2+32↑j
.text:0000000000400F3C add rsp, 28h
.text:0000000000400F40 pop rbx
.text:0000000000400F41 pop rbp
.text:0000000000400F42 retn
.text:0000000000400F42 ; } // starts at 400EFC
.text:0000000000400F42

通过对汇编的调试与分析可以得到相关的逻辑,程序会对输入的第一个数进行判断,不为 1 则引爆炸弹,之后的每一项都是前一项的两倍,一共输入 6 个数字,则有对应输入为:

1
1 2 4 8 16 32

phase_3

第三关整体上也比较简单,让输入两个数,通过第一个数跳到对应的case段,将result进行赋值,之后与输入的第二个数进行比较,不相等就爆炸。

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
__int64 __fastcall phase_3(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+8h] [rbp-10h] BYREF
int v3; // [rsp+Ch] [rbp-Ch] BYREF

if ( (int)__isoc99_sscanf(a1, "%d %d", &v2, &v3) <= 1 ) // 输入两个数字
explode_bomb();
switch ( v2 ) //第一个数字用于决定result的值
{
case 0:
result = 0xCFLL;
break;
case 1:
result = 0x137LL;
break;
case 2:
result = 0x2C3LL;
break;
case 3:
result = 0x100LL;
break;
case 4:
result = 0x185LL;
break;
case 5:
result = 0xCELL;
break;
case 6:
result = 0x2AALL;
break;
case 7:
result = 0x147LL;
break;
default:
explode_bomb();
}
if ( (_DWORD)result != v3 )//将输入的第二个数字与 result 进行比较,相等则通过。
explode_bomb();
return result;
}

phase_4

程序的逻辑也比较简单,输入两个数字,将第一个数字送到对应的递归中,获得的返回值必须为0 ,同时第二个数字也要为0,两个值均为0的情况下成功

同时递归为:

1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __fastcall func4(__int64 a1, __int64 a2, __int64 a3)
{
int v3; // ecx
__int64 result; // rax

v3 = ((int)a3 - (int)a2) / 2 + a2; // 7
if ( v3 > (int)a1 )
return 2 * (unsigned int)func4(a1, a2, (unsigned int)(v3 - 1));
result = 0LL; // v3 == a1
if ( v3 < (int)a1 )
return 2 * (unsigned int)func4(a1, (unsigned int)(v3 + 1), a3) + 1;
return result;
}

func4的功能:

  • 二分法找出元素x,目标值在数组的前半部分则返回奇数,后半部分则返回偶数;
  • 找到元素x返回0;
  • 如果目标值在二分查找过程中一直在左半边,则会返回0。

所以对应的第一个输入值可以为 0 、1 、 3 、7

判断部分有:

1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __fastcall phase_4(__int64 a1)
{
__int64 result; // rax
unsigned int v2; // [rsp+8h] [rbp-10h] BYREF
int v3; // [rsp+Ch] [rbp-Ch] BYREF

if ( (unsigned int)__isoc99_sscanf(a1, "%d %d", &v2, &v3) != 2 || v2 > 14 )
explode_bomb();
result = func4(v2, 0LL, 14LL); // 返回值为0
if ( (_DWORD)result || v3 ) // v3输入为0
explode_bomb();
return result;
}

通过对上述的分析可以得到输入可以为:

1
2
3
4
0 0
1 0
3 0
7 0

phase_5

第五关其实也是比较简单的一个部分,采用了一个映射表的方式进行加密,将我们的输入与0xF进行&运算形成一个下标,通过这个下标到对应的码表内寻找对应的字符之后进行保存,最后将得到的字符串与flyers进行比较。

加密部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned __int64 __fastcall phase_5(__int64 a1)
{
__int64 i; // rax
char v3[8]; // [rsp+10h] [rbp-18h] BYREF
unsigned __int64 v4; // [rsp+18h] [rbp-10h]

v4 = __readfsqword(0x28u);
if ( (unsigned int)string_length(a1) != 6 ) // 输入字符串长度为6
explode_bomb();
for ( i = 0LL; i != 6; ++i )
v3[i] = array_3449[*(_BYTE *)(a1 + i) & 0xF];
v3[6] = 0;
if ( (unsigned int)strings_not_equal(v3, "flyers") )
explode_bomb();
return __readfsqword(0x28u) ^ v4;
}

对应的码表array_3449为:maduiersnfotvbyl

对此写一个小脚本进行爆破可行的输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ans = "maduiersnfotvbyl"
aim = "flyers"
lists = [0]*6
for i in range(len(aim)):
for j in range(len(ans)):
if aim[i] == ans[j]:
lists[i] = j
break

print(lists)
num =0
for num in range(6):
for i in range(30,127):
if i & 0xF == lists[num]:
print(chr(i),"->",lists[num])

有如下输出:

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
[9, 15, 14, 5, 6, 7]
) -> 9
9 -> 9
I -> 9
Y -> 9
i -> 9
y -> 9
-> 15
/ -> 15
? -> 15
O -> 15
_ -> 15
o -> 15
-> 14
. -> 14
> -> 14
N -> 14
^ -> 14
n -> 14
~ -> 14
% -> 5
5 -> 5
E -> 5
U -> 5
e -> 5
u -> 5
& -> 6
6 -> 6
F -> 6
V -> 6
f -> 6
v -> 6
' -> 7
7 -> 7
G -> 7
W -> 7
g -> 7
w -> 7

从中对应的字符表进行选取6个对应的字符即可。

phase_6

6中的难度明显比前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
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
public phase_6
.text:00000000004010F4 phase_6 proc near ; CODE XREF: main+126↑p
.text:00000000004010F4
.text:00000000004010F4 input= dword ptr -78h
.text:00000000004010F4 var_60= byte ptr -60h
.text:00000000004010F4 var_58= qword ptr -58h
.text:00000000004010F4 var_50= byte ptr -50h
.text:00000000004010F4 var_28= byte ptr -28h
.text:00000000004010F4
.text:00000000004010F4 ; __unwind {
.text:00000000004010F4 000 push r14
.text:00000000004010F6 008 push r13
.text:00000000004010F8 010 push r12
.text:00000000004010FA 018 push rbp
.text:00000000004010FB 020 push rbx
.text:00000000004010FC 028 sub rsp, 50h ; Integer Subtraction
.text:0000000000401100 078 mov r13, rsp
.text:0000000000401103 078 mov rsi, rsp
.text:0000000000401106 078 call read_six_numbers ; Call Procedure
.text:0000000000401106
.text:000000000040110B 078 mov r14, rsp
.text:000000000040110E 078 mov r12d, 0
.text:000000000040110E
.text:0000000000401114
.text:0000000000401114 loc_401114: ; CODE XREF: phase_6+5D↓j
.text:0000000000401114 078 mov rbp, r13 ; 返回处偏移加了4
.text:0000000000401117 078 mov eax, [r13+0]
.text:000000000040111B 078 sub eax, 1 ; 输入的第一个数字减1
.text:000000000040111E 078 cmp eax, 5 ; 小于5
.text:0000000000401121 078 jbe short loc_401128 ; Jump if Below or Equal (CF=1 | ZF=1)
.text:0000000000401121
.text:0000000000401123 078 call explode_bomb ; Call Procedure
.text:0000000000401123
.text:0000000000401128 ; ---------------------------------------------------------------------------
.text:0000000000401128
.text:0000000000401128 loc_401128: ; CODE XREF: phase_6+2D↑j
.text:0000000000401128 078 add r12d, 1 ; Add
.text:000000000040112C 078 cmp r12d, 6 ; 控制循环,共6次
.text:0000000000401130 078 jz short loc_401153 ; Jump if Zero (ZF=1)
.text:0000000000401130
.text:0000000000401132 078 mov ebx, r12d
.text:0000000000401132
.text:0000000000401135
.text:0000000000401135 loc_401135: ; CODE XREF: phase_6+57↓j
.text:0000000000401135 078 movsxd rax, ebx ; Move with Sign-Extend Doubleword
.text:0000000000401138 078 mov eax, [rsp+rax*4]
.text:000000000040113B 078 cmp [rbp+0], eax ; 依次比较,判断每个输入都不同
.text:000000000040113E 078 jnz short loc_401145 ; 不相等就跳转
.text:000000000040113E
.text:0000000000401140 078 call explode_bomb ; Call Procedure
.text:0000000000401140
.text:0000000000401145 ; ---------------------------------------------------------------------------
.text:0000000000401145
.text:0000000000401145 loc_401145: ; CODE XREF: phase_6+4A↑j
.text:0000000000401145 078 add ebx, 1 ; 控制循环,共6次
.text:0000000000401148 078 cmp ebx, 5 ; Compare Two Operands
.text:000000000040114B 078 jle short loc_401135 ; Jump if Less or Equal (ZF=1 | SF!=OF)
.text:000000000040114B
.text:000000000040114D 078 add r13, 4 ; 向下偏移4个字节,取后面输入数字
.text:0000000000401151 078 jmp short loc_401114 ; 返回处偏移加了4
.text:0000000000401151
.text:0000000000401153 ; ---------------------------------------------------------------------------
.text:0000000000401153
.text:0000000000401153 loc_401153: ; CODE XREF: phase_6+3C↑j
.text:0000000000401153 078 lea rsi, [rsp+24] ; Load Effective Address
.text:0000000000401158 078 mov rax, r14
.text:000000000040115B 078 mov ecx, 7
.text:000000000040115B
.text:0000000000401160
.text:0000000000401160 loc_401160: ; CODE XREF: phase_6+79↓j
.text:0000000000401160 078 mov edx, ecx ; 7
.text:0000000000401160 ; 把ecx的值给edx
.text:0000000000401162 078 sub edx, [rax] ; Integer Subtraction
.text:0000000000401164 078 mov [rax], edx ; 7 - 每一位输入,将其结果替换为输入的数字中
.text:0000000000401166 078 add rax, 4 ; 输入数字向下取一位
.text:000000000040116A 078 cmp rax, rsi ; 判断是否处理完输入的6个数字
.text:000000000040116D 078 jnz short loc_401160 ; 7
.text:000000000040116D ; 把ecx的值给edx
.text:000000000040116D
.text:000000000040116F 078 mov esi, 0
.text:0000000000401174 078 jmp short loc_401197 ; Jump
.text:0000000000401174
.text:0000000000401176 ; ---------------------------------------------------------------------------
.text:0000000000401176
.text:0000000000401176 loc_401176: ; CODE XREF: phase_6+8B↓j
.text:0000000000401176 ; phase_6+B5↓j
.text:0000000000401176 078 mov rdx, [rdx+8] ; node1向下偏移8字节
.text:000000000040117A 078 add eax, 1 ; Add
.text:000000000040117D 078 cmp eax, ecx ; 找到对应数字所对应的node
.text:000000000040117F 078 jnz short loc_401176 ; node1向下偏移8字节
.text:000000000040117F
.text:0000000000401181 078 jmp short loc_401188 ; 存储对应node数据
.text:0000000000401181 ; 将node进行保存到输入中,
.text:0000000000401181 ; 每次偏移两个单位,当好去除掉了空格
.text:0000000000401181
.text:0000000000401183 ; ---------------------------------------------------------------------------
.text:0000000000401183
.text:0000000000401183 loc_401183: ; CODE XREF: phase_6+A9↓j
.text:0000000000401183 078 mov edx, offset node1
.text:0000000000401183
.text:0000000000401188
.text:0000000000401188 loc_401188: ; CODE XREF: phase_6+8D↑j
.text:0000000000401188 078 mov [rsp+rsi*2+32], rdx ; 存储对应node数据
.text:0000000000401188 ; 将node进行保存到输入中,
.text:0000000000401188 ; 每次偏移两个单位,当好去除掉了空格
.text:000000000040118D 078 add rsi, 4 ; Add
.text:0000000000401191 078 cmp rsi, 18h ; 循环6次
.text:0000000000401195 078 jz short loc_4011AB ; Jump if Zero (ZF=1)
.text:0000000000401195
.text:0000000000401197
.text:0000000000401197 loc_401197: ; CODE XREF: phase_6+80↑j
.text:0000000000401197 078 mov ecx, [rsp+rsi]
.text:000000000040119A 078 cmp ecx, 1 ; 取减完后的每一项与1进行比较
.text:000000000040119D 078 jle short loc_401183 ; 不等于往左,等于往右
.text:000000000040119D
.text:000000000040119F 078 mov eax, 1
.text:00000000004011A4 078 mov edx, offset node1 ; 将node1地址给edx
.text:00000000004011A9 078 jmp short loc_401176 ; node1向下偏移8字节
.text:00000000004011A9
.text:00000000004011AB ; ---------------------------------------------------------------------------
.text:00000000004011AB
.text:00000000004011AB loc_4011AB: ; CODE XREF: phase_6+A1↑j
.text:00000000004011AB 078 mov rbx, [rsp+32]
.text:00000000004011B0 078 lea rax, [rsp+40] ; Load Effective Address
.text:00000000004011B5 078 lea rsi, [rsp+80] ; 将rsi置0
.text:00000000004011BA 078 mov rcx, rbx
.text:00000000004011BA
.text:00000000004011BD
.text:00000000004011BD loc_4011BD: ; CODE XREF: phase_6+DC↓j
.text:00000000004011BD 078 mov rdx, [rax]
.text:00000000004011C0 078 mov [rcx+8], rdx ; rcx对应node起始地址
.text:00000000004011C4 078 add rax, 8 ; Add
.text:00000000004011C8 078 cmp rax, rsi ; 判断是否循环完毕
.text:00000000004011CB 078 jz short loc_4011D2 ; Jump if Zero (ZF=1)
.text:00000000004011CB
.text:00000000004011CD 078 mov rcx, rdx
.text:00000000004011D0 078 jmp short loc_4011BD ; Jump
.text:00000000004011D0
.text:00000000004011D2 ; ---------------------------------------------------------------------------
.text:00000000004011D2
.text:00000000004011D2 loc_4011D2: ; CODE XREF: phase_6+D7↑j
.text:00000000004011D2 078 mov qword ptr [rdx+8], 0
.text:00000000004011DA 078 mov ebp, 5
.text:00000000004011DA
.text:00000000004011DF
.text:00000000004011DF loc_4011DF: ; CODE XREF: phase_6+101↓j
.text:00000000004011DF 078 mov rax, [rbx+8] ; 将node中的数据段给rax
.text:00000000004011E3 078 mov eax, [rax]
.text:00000000004011E5 078 cmp [rbx], eax ; 比较后一项和前一项的大小
.text:00000000004011E7 078 jge short loc_4011EE ; 后一项的数据小于前一项时跳转
.text:00000000004011E7
.text:00000000004011E9 078 call explode_bomb ; Call Procedure
.text:00000000004011E9
.text:00000000004011EE ; ---------------------------------------------------------------------------
.text:00000000004011EE
.text:00000000004011EE loc_4011EE: ; CODE XREF: phase_6+F3↑j
.text:00000000004011EE 078 mov rbx, [rbx+8]
.text:00000000004011F2 078 sub ebp, 1 ; Integer Subtraction
.text:00000000004011F5 078 jnz short loc_4011DF ; 将node中的数据段给rax
.text:00000000004011F5
.text:00000000004011F7 078 add rsp, 50h ; Add
.text:00000000004011FB 028 pop rbx
.text:00000000004011FC 020 pop rbp
.text:00000000004011FD 018 pop r12
.text:00000000004011FF 010 pop r13
.text:0000000000401201 008 pop r14
.text:0000000000401203 000 retn ; Return Near from Procedure
.text:0000000000401203 ; } // starts at 4010F4

可以看到有多个函数,我们切换到图形模式:


在左半部分,多次调试壳以判断出对应函数的第一个部分是确保输入不同,采用了两个循环进行遍历。

再看看右半部分:

9.png

在函数loc_40153loc_401160中将我们输入的每一位数字用7减去后保存在原来的位置上,完成后跳转到下一个部分。

10.png

在这段程序中我们可以注意到有一个node的偏移,可是打开node查看对应的数据会发现十分的奇怪,但是稍微修复一下可以得到:

发现什么了吗?每一个node中都存放着一共偏移量,用来指向另一个node,意味着这个node构成的是一个链表结构,而第一个数据也便是其链表中的数据部分。

我们在调试中可以发现程序将用7减去我们的输入后的数据保存在原来位置上,之后通过loc_401176对整个链表进行遍历,直到我们的输入和寄存器eax中数据大小一致时将链表中的数据进行保存。

遍历完后寄存器rdi中便是对应对应的节点,标号与我们的输入相互对应。

node1 -> 1 | node2 -> 2 | …

保存之后程序会对我们保存的寄存器值每次取两个,共5次,将寄存器中数据的前一个节点和后一个节点的值进行比较。当后面的数据小于前面的数据时,继续下一次循环,如果出现小于等于时,则引爆炸弹。

更正上图一个错误点,于loc_4011FF处,应该是后一项的数据小于前一项时跳转。

同过分析我们了解到,phase_6有如下判断条件:

  • 输入的六个数字各不相同
  • 用 7 减去对应的输入数字保存在原来输入的地址处
  • 通过遍历链表找到减去后数据对应的链表,取其数据部分保存
  • 循环 5 次,确保每个保存的链表数据值大小小于后一项

我们提出对应 6 个节点的大小,有如下:

1
2
3
4
5
6
node 1 -> 0x14C	(332)
node 2 -> 0xA8 (168)
node 3 -> 0x39C (924)
node 4 -> 0x2B3 (691)
node 5 -> 0x1DD (477)
node 6 -> 0x1BB (443)

所以排序有:

1
node 2 < node 1 < node 6 < node 5 < node 4 < node 3 

因为保存的node具体大小是经过7减去后得到的,即输入为:

1
4 3 2 1 6 5

secret_phase

当我们完成六个关卡时,会有一个隐藏关卡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void phase_defused()
{
char v0; // [rsp+8h] [rbp-70h] BYREF
char v1; // [rsp+Ch] [rbp-6Ch] BYREF
char v2[88]; // [rsp+10h] [rbp-68h] BYREF
unsigned __int64 v3; // [rsp+68h] [rbp-10h]

v3 = __readfsqword(0x28u);
if ( (unsigned int)__isoc99_sscanf(&unk_603870, "%d %d %s", &v0, &v1, v2) == 3
&& !(unsigned int)strings_not_equal(v2, "DrEvil") )
{
puts("Curses, you've found the secret phase!");
puts("But finding it and solving it are quite different...");
secret_phase();
}
puts("Congratulations! You've defused the bomb!");
}

当我们输入的字符串与DrEvil相同时进入道内部的一共函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __int64 secret_phase()
{
const char *line; // rax
unsigned int v1; // ebx
line = read_line();
v1 = strtol(line, 0LL, 10); // 将输入转化为long int
if ( v1 - 1 > 1000 ) // 输入数据小于1001
explode_bomb();
if ( (unsigned int)fun7((__int64)&n1, v1) != 2 )// fun7返回值为2
explode_bomb();
puts("Wow! You've defused the secret stage!");
return phase_defused();
}

可以看到,核心的函数为fun7,点开进入:

1
2
3
4
5
6
7
8
9
10
11
12
__int64 __fastcall fun7(__int64 a1, __int64 a2)
{
__int64 result; // rax
if ( !a1 )
return 0xFFFFFFFFLL;
if ( *(_DWORD *)a1 > (int)a2 )
return 2 * (unsigned int)fun7(*(_QWORD *)(a1 + 8), a2);
result = 0LL;
if ( *(_DWORD *)a1 != (_DWORD)a2 )
return 2 * (unsigned int)fun7(*(_QWORD *)(a1 + 16), a2) + 1;
return result;
}

看起来似乎又是一个递归调用,我们回到传入的数据a1中,经过修复后可以看到:

所以对应的fun7是一个二叉树,我们画出对应的树状图:

若访问到空节点,则返回值会出现0xffffffff,所以我们只能在树上(软件原因树的最右分枝未补全)的节点选择。通过代码我们知道,若从节点n1出发初始值为0x24,若改节点为右儿子则a=2*a+1、为左儿子则a=a*2。所以0x160x14节点都可以在到达根节点时返回a=2,输入任意一值即可通关。


CSAPP - Bomb Lab
https://equinox-shame.github.io/2022/04/03/Bomb Lab/
作者
梓曰
发布于
2022年4月3日
许可协议