计算机系统原理-二进制炸弹拆解实验记录

拆弹前置

mips学习

视频:

指令现查:

寄存器现查:

寄存器名地址编号用途说明
$00保存固定的常数0
$at1汇编器专用
$v0 ~ $v12 ~ 3表达式计算或函数调用的返回结果
$a0 ~ $a34 ~ 7函数调用参数1~3
$t0 ~ $t78 ~ 15临时变量,函数调用时不需要保存和恢复
$s0 ~ $s716 ~ 23函数调用时需要保存和恢复的寄存器变量
$t8 ~ $t924 ~ 25临时变量,函数调用时不需要保存和恢复
$k0 ~ $k126 ~ 27操作系统专用
$gp28全局指针变量(Global Pointer)
$sp29堆栈指针变量(Stack Pointer)
$fp30帧指针变量(Frame Pointer)
$ra31返回地址(Return Address)

GDB使用

启动gdb:gdb bomb

查看反编译dump:disas 函数名

查看断点:info breakpoints

删除断点:delete id

监控变量:display 变量地址,最好用之一,结合*

查看变量:x[/][num][d/c] 地址,最好用之一

打印变量值:p 地址

查看寄存器:i r 寄存器

下一条指令:ni

向下执行直到遇到断点:n

万能代码:help

方法

  • 阅读反编译代码、汇编代码

    • 反编译代码:顺式结构,但是会往回跳,对于一些对象常量赋值直接上地址而不是标签
    • 汇编代码:”跳来跳去”,但是常量一目了然,对于对象常量赋值使用了标签
  • gdb断点调试、变量监控

    • 看不懂反编译代码里寄存器附近有什么的时候,多用gdb的x指令
    • display结合ni,监控变量变化
  • 写注释以及对代码分段

    • 对于循环结构,容易找到循环节

愉快拆弹

下面涉及到题解,想要7倍的快乐的同学请自己拆弹。

注:注释有些许错误。

自己拆解可以得到乐趣,领悟真谛!

Main

首先简单看一下这个Main,基本上是6关相连着:读入、进关、出关。

完成一个关卡会执行一次phase_defused这个关卡完成函数,输出祝福语之类的。当然里面有点秘密就是了。

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
Dump of assembler code for function main:
0x00400900 <+0>: addiu sp,sp,-48
# ... 省略
0x00400ba8 <+680>: jal 0x401fec <read_line>
# ...
0x00400bbc <+700>: jal 0x400d6c <phase_1>
# ...
0x00400bc8 <+712>: jal 0x402264 <phase_defused>
# ...
0x00400bf4 <+756>: jal 0x401fec <read_line>
# ...
0x00400c08 <+776>: jal 0x400dbc <phase_2>
# ...
0x00400c14 <+788>: jal 0x402264 <phase_defused>
# ...
0x00400c40 <+832>: jal 0x401fec <read_line>
# ...
0x00400c54 <+852>: jal 0x400ed4 <phase_3>
# ...
0x00400c60 <+864>: jal 0x402264 <phase_defused>
# ...
0x00400c8c <+908>: jal 0x401fec <read_line>
# ...
0x00400ca0 <+928>: jal 0x4012bc <phase_4>
# ...
0x00400cac <+940>: jal 0x402264 <phase_defused>
# ...
0x00400cd8 <+984>: jal 0x401fec <read_line>
# ...
0x00400cec <+1004>: jal 0x4013e8 <phase_5>
# ...
0x00400cf8 <+1016>: jal 0x402264 <phase_defused>
# ...
0x00400d24 <+1060>: jal 0x401fec <read_line>
# ...
0x00400d38 <+1080>: jal 0x401500 <phase_6>
# ...
0x00400d44 <+1092>: jal 0x402264 <phase_defused>
# ...
End of assembler dump.

下面进入第一关:

Phase_1

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
Dump of assembler code for function phase_1:
0x00400d6c <+0>: addiu sp,sp,-32
0x00400d70 <+4>: sw ra,28(sp)
0x00400d74 <+8>: sw s8,24(sp)
0x00400d78 <+12>: move s8,sp
# 为函数所需空间压栈

0x00400d7c <+16>: sw a0,32(s8) # 32(s8) = a0
0x00400d80 <+20>: lw a0,32(s8) # a0 = 32(s8)
# 设置函数参数a0

0x00400d84 <+24>: lui v0,0x40
0x00400d88 <+28>: addiu a1,v0,10092
# 设置函数参数a1。0x40和10092不是简单的数字常量是地址这里,查汇编代码发现和"$LC12"标签有关,这个标签是常量'.ascii "Let's begin now!\000"'

0x00400d8c <+32>: jal 0x401cf8 <strings_not_equal>
# 进入函数<strings_not_equal>,出来后函数返回值放在v类寄存器

0x00400d90 <+36>: nop
0x00400d94 <+40>: beqz v0,0x400da4 <phase_1+56>
# v0 == 0就跳到+56行,跳过了下面+48行的炸弹,故这里要求<strings_not_equal>返回值为0,即不能"strings_not_equal", 两个字符串要相等,即输入串和常量串"Let's begin now!"相等,的解。

0x00400d98 <+44>: nop
0x00400d9c <+48>: jal 0x4021f0 <explode_bomb>
0x00400da0 <+52>: nop
# 引爆炸弹

0x00400da4 <+56>: move sp,s8
0x00400da8 <+60>: lw ra,28(sp)
0x00400dac <+64>: lw s8,24(sp)
0x00400db0 <+68>: addiu sp,sp,32
0x00400db4 <+72>: jr ra
0x00400db8 <+76>: nop
# 还原栈现场,返回
End of assembler dump.

输入Let's begin now!

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
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
Dump of assembler code for function phase_2:
0x00400dbc <+0>: addiu sp,sp,-64
0x00400dc0 <+4>: sw ra,60(sp)
0x00400dc4 <+8>: sw s8,56(sp)
0x00400dc8 <+12>: move s8,sp
0x00400dcc <+16>: lui gp,0x42
0x00400dd0 <+20>: addiu gp,gp,-20080
0x00400dd4 <+24>: sw gp,16(sp)
0x00400dd8 <+28>: sw a0,64(s8)
0x00400ddc <+32>: addiu v0,s8,28
0x00400de0 <+36>: lw a0,64(s8)
0x00400de4 <+40>: move a1,v0
0x00400de8 <+44>: jal 0x401ba8 <read_six_numbers>
# 压栈、读入6个数,存到28(s8)、32(s8)、36(s8)、40(s8)、44(s8)、48(s8)

0x00400dec <+48>: nop
0x00400df0 <+52>: lw gp,16(s8)
0x00400df4 <+56>: lw v1,28(s8) # 将v1 = 输入的六个数第1个
0x00400df8 <+60>: li v0,1 # 将v0 = 1
0x00400dfc <+64>: beq v1,v0,0x400e10 <phase_2+84> # 判断v1=v0=1,满足则跳+84行
0x00400e00 <+68>: nop
0x00400e04 <+72>: jal 0x4021f0 <explode_bomb>
0x00400e08 <+76>: nop
0x00400e0c <+80>: lw gp,16(s8)
# 要求第一个参数为1

0x00400e10 <+84>: li v0,1 # 将v0 = 1
0x00400e14 <+88>: sw v0,24(s8) # 将24(s8) = v0 = 1
0x00400e18 <+92>: b 0x400ea8 <phase_2+236> # 跳到+236行
0x00400e1c <+96>: nop
# 令24(s8) = 1,进入+236行

0x00400e20 <+100>: lw v0,24(s8) # v0 = 24(s8)
0x00400e24 <+104>: nop
0x00400e28 <+108>: addiu v0,v0,-1 # v0 -= 1
0x00400e2c <+112>: sll v0,v0,0x2 # v0 *= 4
0x00400e30 <+116>: addiu v1,s8,24 # v1 = s8+24,是一个地址
0x00400e34 <+120>: addu v0,v1,v0 # v0 += v1,即s8+24的地址向后偏移24(s8)个
0x00400e38 <+124>: lw a0,4(v0) # 参数寄存器,a0 = 4(v0)
# 总结上面几句就是从s8+28向后取第24(s8)个变量,即num[i],num是输入的六个数的数组
0x00400e3c <+128>: li v1,12 # v1 = 12
0x00400e40 <+132>: lw v0,24(s8) # v0 = 24(s8)
0x00400e44 <+136>: nop
0x00400e48 <+140>: subu v0,v1,v0 # v0=v1-v0=12-24(s8)
0x00400e4c <+144>: lw v1,-32660(gp)
# 又出现了奇怪的常数,-32660(gp),还是对全局指针的常数,反编译代码难看出什么,可以gdb一下,发现是现在0($v1) 为学号的第一位, 相应的4($v1)...一直到44($v1)。
# 当然也可以看汇编源码,发现上一行是"lw $3,%got(ID_num)($28)"一目了然了。
0x00400e50 <+148>: sll v0,v0,0x2 # v0 *= 4, 第一次的时候v0 = (12-1)*4,第二次是(12-2)*4=40
0x00400e54 <+152>: addu v0,v1,v0 # v0 = 学号第一位的地址+v0这个偏移量,即学号的从后数的第24(s8)位的地址
0x00400e58 <+156>: lw v0,0(v0) # 将 v0 = 学号的从后数的第24(s8)位的内容
0x00400e5c <+160>: nop
0x00400e60 <+164>: mult a0,v0 # 将lo,hi = a0*v0 = a0*学号的从后数的第24(s8)位,其中a0是
0x00400e64 <+168>: mflo a0 # 将a0 = lo = 乘法结果
0x00400e68 <+172>: lw v0,24(s8) # v0 = 24(s8)
0x00400e6c <+176>: nop
0x00400e70 <+180>: sll v0,v0,0x2 # v0 *= 2
0x00400e74 <+184>: addiu v1,s8,24 # v1 = s8+24,是一个地址
0x00400e78 <+188>: addu v0,v1,v0 # v0 = s8+24 + 24(s8),还是一个地址
0x00400e7c <+192>: lw v0,4(v0)
# 即v0 = 28(s8)后的第24(s8)个变量
0x00400e80 <+196>: nop
0x00400e84 <+200>: beq a0,v0,0x400e98 <phase_2+220> # 若a0=原a0*学号的从后数的第24(s8)位的内容 == v0=输入六个参数的第24(s8)个 则跳转+220行,不会爆炸
# 这里的原a0?????????????????????????
0x00400e88 <+204>: nop
0x00400e8c <+208>: jal 0x4021f0 <explode_bomb>
0x00400e90 <+212>: nop
0x00400e94 <+216>: lw gp,16(s8)
0x00400e98 <+220>: lw v0,24(s8)
0x00400e9c <+224>: nop
0x00400ea0 <+228>: addiu v0,v0,1
0x00400ea4 <+232>: sw v0,24(s8)
0x00400ea8 <+236>: lw v0,24(s8)
# v0 = ++ 24(s8)的意思
0x00400eac <+240>: nop
0x00400eb0 <+244>: slti v0,v0,6 # v0=(v0<6)
0x00400eb4 <+248>: bnez v0,0x400e20 <phase_2+100> # v0!=0,即满足上式,进入+100行
# 循环结构, 24(s8) 从0~5,执行6次

0x00400eb8 <+252>: nop
0x00400ebc <+256>: move sp,s8
0x00400ec0 <+260>: lw ra,60(sp)
0x00400ec4 <+264>: lw s8,56(sp)
0x00400ec8 <+268>: addiu sp,sp,64
0x00400ecc <+272>: jr ra
0x00400ed0 <+276>: nop
# 收栈,还原现场,返回
End of assembler dump.

输入:%d %d %d %d %d %d
1 学号倒数第1位*输入数字的第1个 学号倒数第2位*输入数字的第2个 学号倒数第3位*输入数字的第3个 学号倒数第4位输入数字的第4个 学号倒数第5位输入数字的第5个
我的答案:1 0 0 0 0 0

phase_3

从phase_3开始就注释就是拆弹现场的迷la乱ji笔记,没有精修了。

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
Dump of assembler code for function phase_3:
0x00400ed4 <+0>: addiu sp,sp,-56
0x00400ed8 <+4>: sw ra,52(sp)
0x00400edc <+8>: sw s8,48(sp)
0x00400ee0 <+12>: move s8,sp
0x00400ee4 <+16>: lui gp,0x42
0x00400ee8 <+20>: addiu gp,gp,-20080
0x00400eec <+24>: sw gp,24(sp)
0x00400ef0 <+28>: sw a0,56(s8)
0x00400ef4 <+32>: lw a0,56(s8)
0x00400ef8 <+36>: lui v0,0x40
0x00400efc <+40>: addiu a1,v0,10112
0x00400f00 <+44>: addiu v1,s8,44
0x00400f04 <+48>: addiu v0,s8,40
0x00400f08 <+52>: addiu a2,s8,36
0x00400f0c <+56>: sw a2,16(sp)
0x00400f10 <+60>: move a2,v1
0x00400f14 <+64>: move a3,v0
0x00400f18 <+68>: lw v0,-32636(gp) # 看到熟悉的-3xxxx(gp)
0x00400f1c <+72>: nop
0x00400f20 <+76>: move t9,v0 # 把v0放到t9
0x00400f24 <+80>: jalr t9 # 跳转到t9去
0x00400f28 <+84>: nop # 然后从t9回来后,v0=输入参数的个数
0x00400f2c <+88>: lw gp,24(s8) # gdb调试了一下 x /12 $s8 才发现我输入的"555555",也就说是在44(s8)是我输入的第一个数字
0x00400f30 <+92>: slti v0,v0,3 # v0=(v0<3) 然后就会爆炸,就是输入的数字少于3个会爆炸
0x00400f34 <+96>: beqz v0,0x400f48 <phase_3+116> # v0==0就到+116行
0x00400f38 <+100>: nop
0x00400f3c <+104>: jal 0x4021f0 <explode_bomb>
0x00400f40 <+108>: nop
0x00400f44 <+112>: lw gp,24(s8)
0x00400f48 <+116>: lw v0,44(s8) # 把我输入的第一个数字放到v0
0x00400f4c <+120>: nop
0x00400f50 <+124>: sltiu v1,v0,8 # v1=(输入的第一个数字<8)
0x00400f54 <+128>: beqz v1,0x401190 <phase_3+700> # 输入的第一个数>=8会跳到+700行,不用往下看了,是爆炸,也就是说输入的第一个数字要小于8
0x00400f58 <+132>: nop
0x00400f5c <+136>: sll v1,v0,0x2 # v1 = 输入的第一个数字*4
0x00400f60 <+140>: lui v0,0x40 # v0 = 64的高16位 = 64*65536 = 4194304
0x00400f64 <+144>: addiu v0,v0,10124 # v0 = 4194304+10124 = 4204428
0x00400f68 <+148>: addu v0,v1,v0 # v0 = 输入的第一个数字*4 + 4204428
0x00400f6c <+152>: lw v0,0(v0) # v0 = v0地址上的数字,应该是一个子程序地址,输入的第一个数是1的时候为4198336,是2的手为4198404
0x00400f70 <+156>: nop
0x00400f74 <+160>: jr v0 # 进入v0这个子程序,如果输入的第一个数是1则到+236行,为2到+304行,为3时候到+372行,为4时候到+436行,为5时到+504行,为6时到+572行,为7时到+632行,为0的时候直接顺着下去到+168
0x00400f78 <+164>: nop
0x00400f7c <+168>: li v0,113
0x00400f80 <+172>: sb v0,32(s8)
0x00400f84 <+176>: lw v0,-32660(gp)
0x00400f88 <+180>: nop
0x00400f8c <+184>: lw v1,44(v0)
0x00400f90 <+188>: lw v0,36(s8)
0x00400f94 <+192>: nop
0x00400f98 <+196>: mult v1,v0
0x00400f9c <+200>: mflo v1
0x00400fa0 <+204>: li v0,777
0x00400fa4 <+208>: beq v1,v0,0x4011ac <phase_3+728> # 要学号第12位*输入的第二个数字去掉首位 == 777
0x00400fa8 <+212>: nop
0x00400fac <+216>: jal 0x4021f0 <explode_bomb>
0x00400fb0 <+220>: nop
0x00400fb4 <+224>: lw gp,24(s8)
0x00400fb8 <+228>: b 0x4011f8 <phase_3+804>
0x00400fbc <+232>: nop
0x00400fc0 <+236>: li v0,98 # 输入第一个数字为1时候到达的地方
0x00400fc4 <+240>: sb v0,32(s8)
0x00400fc8 <+244>: lw v0,-32660(gp) # -3xxxx(gp)一看到,应该要出现学号了
# 此时0($v0)是学号的第1位 以此类推 4($v0)
0x00400fcc <+248>: nop
0x00400fd0 <+252>: lw v1,44(v0) # v1 = 学号第12位
0x00400fd4 <+256>: lw v0,36(s8) # v0 = 输入的第二个数字去掉首位
0x00400fd8 <+260>: nop
0x00400fdc <+264>: mult v1,v0
0x00400fe0 <+268>: mflo v1
0x00400fe4 <+272>: li v0,214
0x00400fe8 <+276>: beq v1,v0,0x4011b8 <phase_3+740> # 学号第12位*输入的第二个数字去掉首位 == 214 则跳到+720行,好的想都不用想了,我学号第12位是0,第一条路没了。
0x00400fec <+280>: nop
0x00400ff0 <+284>: jal 0x4021f0 <explode_bomb>
0x00400ff4 <+288>: nop
0x00400ff8 <+292>: lw gp,24(s8)
0x00400ffc <+296>: b 0x4011f8 <phase_3+804>
0x00401000 <+300>: nop
0x00401004 <+304>: li v0,98 # 第二条路
0x00401008 <+308>: sb v0,32(s8)
0x0040100c <+312>: lw v0,-32660(gp) # 依旧相似的重复
0x00401010 <+316>: nop
0x00401014 <+320>: lw v1,44(v0)
0x00401018 <+324>: lw v0,36(s8)
0x0040101c <+328>: nop
0x00401020 <+332>: mult v1,v0
0x00401024 <+336>: mflo v1
0x00401028 <+340>: li v0,755
0x0040102c <+344>: beq v1,v0,0x4011c4 <phase_3+752> # 学号第12位*输入的第二个数字去掉首位 == 755 则跳到+752行,好的想都不用想了,我学号第12位是0,第二条路没了。
0x00401030 <+348>: nop
0x00401034 <+352>: jal 0x4021f0 <explode_bomb>
0x00401038 <+356>: nop
0x0040103c <+360>: lw gp,24(s8)
0x00401040 <+364>: b 0x4011f8 <phase_3+804>
0x00401044 <+368>: nop
0x00401048 <+372>: li v0,107 # 第三条路,熟悉的场tao景lu
0x0040104c <+376>: sb v0,32(s8)
0x00401050 <+380>: lw v0,-32660(gp)
0x00401054 <+384>: nop
0x00401058 <+388>: lw v1,44(v0)
0x0040105c <+392>: lw v0,36(s8)
0x00401060 <+396>: nop
0x00401064 <+400>: mult v1,v0
0x00401068 <+404>: mflo v0
0x0040106c <+408>: beqz v0,0x4011d0 <phase_3+764> # 学号第12位*输入的第二个数字去掉首位 == 0 则跳转到764行,咦我的学号满足啊,但先去看看第四五六七条路
0x00401070 <+412>: nop
0x00401074 <+416>: jal 0x4021f0 <explode_bomb>
0x00401078 <+420>: nop
0x0040107c <+424>: lw gp,24(s8)
0x00401080 <+428>: b 0x4011f8 <phase_3+804>
0x00401084 <+432>: nop
0x00401088 <+436>: li v0,111 # 第四条路
0x0040108c <+440>: sb v0,32(s8)
0x00401090 <+444>: lw v0,-32660(gp)
0x00401094 <+448>: nop
0x00401098 <+452>: lw v1,44(v0)
0x0040109c <+456>: lw v0,36(s8)
0x004010a0 <+460>: nop
0x004010a4 <+464>: mult v1,v0
0x004010a8 <+468>: mflo v1
0x004010ac <+472>: li v0,228
0x004010b0 <+476>: beq v1,v0,0x4011dc <phase_3+776> # 要等于228,不行
0x004010b4 <+480>: nop
0x004010b8 <+484>: jal 0x4021f0 <explode_bomb>
0x004010bc <+488>: nop
0x004010c0 <+492>: lw gp,24(s8)
0x004010c4 <+496>: b 0x4011f8 <phase_3+804>
0x004010c8 <+500>: nop
0x004010cc <+504>: li v0,116 # 第5条路
0x004010d0 <+508>: sb v0,32(s8)
0x004010d4 <+512>: lw v0,-32660(gp)
0x004010d8 <+516>: nop
0x004010dc <+520>: lw v1,44(v0)
0x004010e0 <+524>: lw v0,36(s8)
0x004010e4 <+528>: nop
0x004010e8 <+532>: mult v1,v0
0x004010ec <+536>: mflo v1
0x004010f0 <+540>: li v0,513
0x004010f4 <+544>: beq v1,v0,0x4011e8 <phase_3+788> # 要等于513,不行
0x004010f8 <+548>: nop
0x004010fc <+552>: jal 0x4021f0 <explode_bomb>
0x00401100 <+556>: nop
0x00401104 <+560>: lw gp,24(s8)
0x00401108 <+564>: b 0x4011f8 <phase_3+804>
0x0040110c <+568>: nop
0x00401110 <+572>: li v0,118 # 第6条路
0x00401114 <+576>: sb v0,32(s8)
0x00401118 <+580>: lw v0,-32660(gp)
0x0040111c <+584>: nop
0x00401120 <+588>: lw v1,44(v0)
0x00401124 <+592>: lw v0,36(s8)
0x00401128 <+596>: nop
0x0040112c <+600>: mult v1,v0
0x00401130 <+604>: mflo v1
0x00401134 <+608>: li v0,780
0x00401138 <+612>: beq v1,v0,0x40114c <phase_3+632> #要等于780,不行
0x0040113c <+616>: nop
0x00401140 <+620>: jal 0x4021f0 <explode_bomb>
0x00401144 <+624>: nop
0x00401148 <+628>: lw gp,24(s8)
0x0040114c <+632>: li v0,98 # 第7条路
0x00401150 <+636>: sb v0,32(s8)
0x00401154 <+640>: lw v0,-32660(gp)
0x00401158 <+644>: nop
0x0040115c <+648>: lw v1,44(v0)
0x00401160 <+652>: lw v0,36(s8)
0x00401164 <+656>: nop
0x00401168 <+660>: mult v1,v0
0x0040116c <+664>: mflo v1
0x00401170 <+668>: li v0,824
0x00401174 <+672>: beq v1,v0,0x4011f4 <phase_3+800> # 要等于824,不行
0x00401178 <+676>: nop
0x0040117c <+680>: jal 0x4021f0 <explode_bomb>
0x00401180 <+684>: nop
0x00401184 <+688>: lw gp,24(s8)
0x00401188 <+692>: b 0x4011f8 <phase_3+804>
0x0040118c <+696>: nop
0x00401190 <+700>: li v0,120
0x00401194 <+704>: sb v0,32(s8)
0x00401198 <+708>: jal 0x4021f0 <explode_bomb>
0x0040119c <+712>: nop
0x004011a0 <+716>: lw gp,24(s8)
0x004011a4 <+720>: b 0x4011f8 <phase_3+804>
0x004011a8 <+724>: nop
0x004011ac <+728>: nop
0x004011b0 <+732>: b 0x4011f8 <phase_3+804>
0x004011b4 <+736>: nop
0x004011b8 <+740>: nop
0x004011bc <+744>: b 0x4011f8 <phase_3+804>
0x004011c0 <+748>: nop
0x004011c4 <+752>: nop
0x004011c8 <+756>: b 0x4011f8 <phase_3+804>
0x004011cc <+760>: nop
0x004011d0 <+764>: nop # 第三条路的到点
0x004011d4 <+768>: b 0x4011f8 <phase_3+804>
0x004011d8 <+772>: nop
0x004011dc <+776>: nop
0x004011e0 <+780>: b 0x4011f8 <phase_3+804>
0x004011e4 <+784>: nop
0x004011e8 <+788>: nop
0x004011ec <+792>: b 0x4011f8 <phase_3+804>
0x004011f0 <+796>: nop
0x004011f4 <+800>: nop
# 最终都会汇集到的地方
0x004011f8 <+804>: lb v0,40(s8) # 追踪了一下40($s8)在从t9出来后就没变了
0x004011fc <+808>: lb v1,32(s8) # lb 读末八位进来
0x00401200 <+812>: nop
0x00401204 <+816>: beq v1,v0,0x401218 <phase_3+836>
0x00401208 <+820>: nop
0x0040120c <+824>: jal 0x4021f0 <explode_bomb>
0x00401210 <+828>: nop
0x00401214 <+832>: lw gp,24(s8)
0x00401218 <+836>: move sp,s8
0x0040121c <+840>: lw ra,52(sp)
0x00401220 <+844>: lw s8,48(sp)
0x00401224 <+848>: addiu sp,sp,56
0x00401228 <+852>: jr ra
0x0040122c <+856>: nop
End of assembler dump.
输入%d %c %d
第一个数字44($s8),要小于8,代表了8个条件分支
第二个参数40($s8),是8个条件分支里会设置的最后比对ASCII相等
第三个数字乘以学号最后一位乘起来去和8个条件分支的常数判等

我的答案:
3 k 任意一个数字

恭喜完成Phase_3…

Phase_4

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
Dump of assembler code for function phase_4:
0x004012bc <+0>: addiu sp,sp,-40
0x004012c0 <+4>: sw ra,36(sp)
0x004012c4 <+8>: sw s8,32(sp)
0x004012c8 <+12>: move s8,sp
0x004012cc <+16>: lui gp,0x42
0x004012d0 <+20>: addiu gp,gp,-20080
0x004012d4 <+24>: sw gp,16(sp)
0x004012d8 <+28>: sw a0,40(s8)
0x004012dc <+32>: lw v1,40(s8)
0x004012e0 <+36>: lui v0,0x40
0x004012e4 <+40>: addiu v0,v0,10156
0x004012e8 <+44>: move a0,v1
0x004012ec <+48>: move a1,v0
0x004012f0 <+52>: addiu v0,s8,24
0x004012f4 <+56>: move a2,v0
0x004012f8 <+60>: lw v0,-32636(gp)
0x004012fc <+64>: nop
0x00401300 <+68>: move t9,v0 # 让读入函数的返回值为v0,看下一行就知道了“jalr”,调用了系统的ddl
0x00401304 <+72>: jalr t9
0x00401308 <+76>: nop
0x0040130c <+80>: lw gp,16(s8) # 可以从这里开始,x /7d $s8发现是输入的那一个参数,即24($s8)
0x00401310 <+84>: move v1,v0 # 令v1=v0=实际参数数目
0x00401314 <+88>: li v0,1 # 规定的输入参数数目为v0=1
0x00401318 <+92>: bne v1,v0,0x401330 <phase_4+116> # 判断输入参数为1个,不等的话就去+116行引爆炸弹吧
0x0040131c <+96>: nop
0x00401320 <+100>: lw v0,24(s8) # 把输入的那个参数读入到v0
0x00401324 <+104>: nop
0x00401328 <+108>: bgtz v0,0x401340 <phase_4+132> # 输入的那个参数大于0就跳到+132
0x0040132c <+112>: nop
0x00401330 <+116>: jal 0x4021f0 <explode_bomb>
0x00401334 <+120>: nop
0x00401338 <+124>: lw gp,16(s8)
0x0040133c <+128>: nop
0x00401340 <+132>: lw v0,-32660(gp) # 把学号第一位首"地址"放到v0
0x00401344 <+136>: nop
0x00401348 <+140>: lw v0,44(v0) # 0是第一位首地址,44就是取第十二位学号到v0
0x0040134c <+144>: nop
0x00401350 <+148>: andi v0,v0,0x1 # v0 &= 0x1
0x00401354 <+152>: andi v0,v0,0xff # v0 &= 0xff (是255)
0x00401358 <+156>: beqz v0,0x40139c <phase_4+224> # 两次与运算之后等于0的话就去+224行,否则继续,我的学号最后一位是0,与上什么都是0,那就去+224行
0x0040135c <+160>: nop
0x00401360 <+164>: lw v0,24(s8)
0x00401364 <+168>: nop
0x00401368 <+172>: move a0,v0
0x0040136c <+176>: jal 0x401230 <func4>
0x00401370 <+180>: nop
0x00401374 <+184>: lw gp,16(s8)
0x00401378 <+188>: move v1,v0
0x0040137c <+192>: li v0,8
0x00401380 <+196>: beq v1,v0,0x4013d0 <phase_4+276>
0x00401384 <+200>: nop
0x00401388 <+204>: jal 0x4021f0 <explode_bomb>
0x0040138c <+208>: nop
0x00401390 <+212>: lw gp,16(s8)
0x00401394 <+216>: b 0x4013d0 <phase_4+276>
0x00401398 <+220>: nop
0x0040139c <+224>: lw v0,24(s8) # 降落完毕,把输入的那个参数放到v0
0x004013a0 <+228>: nop
0x004013a4 <+232>: move a0,v0 # 把v0即输入的那个参数放到a0
0x004013a8 <+236>: jal 0x401230 <func4> # 进入函数func4
0x004013ac <+240>: nop
0x004013b0 <+244>: lw gp,16(s8) # 从func4回来
0x004013b4 <+248>: move v1,v0 # v1 = v0 = func4的返回值
0x004013b8 <+252>: li v0,13 # v0 = 13
0x004013bc <+256>: beq v1,v0,0x4013d0 <phase_4+276> # v1 == v0的话,就去276完成该关卡,蛤?也就是说要func的返回值为13。。。行吧,去看情况②
0x004013c0 <+260>: nop
0x004013c4 <+264>: jal 0x4021f0 <explode_bomb>
0x004013c8 <+268>: nop
0x004013cc <+272>: lw gp,16(s8)
0x004013d0 <+276>: move sp,s8
0x004013d4 <+280>: lw ra,36(sp)
0x004013d8 <+284>: lw s8,32(sp)
0x004013dc <+288>: addiu sp,sp,40
0x004013e0 <+292>: jr ra
0x004013e4 <+296>: nop
End of assembler dump.

引入func4

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
Dump of assembler code for function func4:      
0x00401230 <+0>: addiu sp,sp,-40
0x00401234 <+4>: sw ra,36(sp)
0x00401238 <+8>: sw s8,32(sp)
0x0040123c <+12>: sw s0,28(sp)
0x00401240 <+16>: move s8,sp
0x00401244 <+20>: sw a0,40(s8) # 把输入的那个参数存到40(s8)
0x00401248 <+24>: lw v0,40(s8) # v0 = 40(s8) = 输入的那个参数
0x0040124c <+28>: nop
0x00401250 <+32>: slti v0,v0,2 # v0 = (v0<2)
0x00401254 <+36>: bnez v0,0x40129c <func4+108> # 也就是说情况①输入参数小于2的话去+108行
0x00401258 <+40>: nop
0x0040125c <+44>: lw v0,40(s8) # 好的,情况②,我们把v0=输入的参数
0x00401260 <+48>: nop
0x00401264 <+52>: addiu v0,v0,-1 # v0 = 输入的参数-1
0x00401268 <+56>: move a0,v0 # a0 = v0
0x0040126c <+60>: jal 0x401230 <func4> # 再进一次func4,蛤?递归吗?每次的传参都会-1,但相应的应该也会影响返回的值,我们往下看
0x00401270 <+64>: nop
0x00401274 <+68>: move s0,v0 # 假设递归回来后, s0 = v0 = 返回值,想象s0寄存器的作用是啥呀?(我在干嘛?|_・)
0x00401278 <+72>: lw v0,40(s8) # v0 = 40(s8) = 此时应该叫phase_4输入参数已经不合适了,要叫func4输入参数
0x0040127c <+76>: nop
0x00401280 <+80>: addiu v0,v0,-2 # v0 -= 2
0x00401284 <+84>: move a0,v0 # a0 = v0
0x00401288 <+88>: jal 0x401230 <func4> # a0是参数寄存器,也就是说又递归去了。
0x0040128c <+92>: nop
0x00401290 <+96>: addu v0,s0,v0 # 然后把两个返回值加起来,可以得到递归程序了:
int func4(int a0)
{
if(a0<2) return 1;
return func4(a0-1) + func4(a0-2);
}# 也就是求斐波那契数列的第a0项

0x00401294 <+100>: b 0x4012a0 <func4+112>
0x00401298 <+104>: nop
0x0040129c <+108>: li v0,1 # 情况①:把v0=1,应该是作返回值?
0x004012a0 <+112>: move sp,s8 # 下面就是收栈返回了,情况①可以返回phase_4,那就先回去看看吧,爆了再回来(#^.^#)
0x004012a4 <+116>: lw ra,36(sp)
0x004012a8 <+120>: lw s8,32(sp)
0x004012ac <+124>: lw s0,28(sp)
0x004012b0 <+128>: addiu sp,sp,40
0x004012b4 <+132>: jr ra
0x004012b8 <+136>: nop
End of assembler dump.

# func4是求斐波那契数列第n项(从0开始),那么现在第几项=13,答案是固定的6

读入 %d
第四关答案:一个大于0的参数
6

Phase_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
Dump of assembler code for function phase_5:
0x004013e8 <+0>: addiu sp,sp,-72
0x004013ec <+4>: sw ra,68(sp)
0x004013f0 <+8>: sw s8,64(sp)
0x004013f4 <+12>: move s8,sp
0x004013f8 <+16>: sw a0,72(s8)
0x004013fc <+20>: lw a0,72(s8)
0x00401400 <+24>: jal 0x401c78 <string_length> # 看到这里,似乎要读入一串东西,然后取长度什么的
0x00401404 <+28>: nop
0x00401408 <+32>: move v1,v0 # v1 = v0 = 函数返回值 = 输入字符串的长度
0x0040140c <+36>: li v0,6 # v0 = 6
0x00401410 <+40>: beq v1,v0,0x401420 <phase_5+56> # 输入串长度=6的话就去+56行否则Boom
0x00401414 <+44>: nop
0x00401418 <+48>: jal 0x4021f0 <explode_bomb>
0x0040141c <+52>: nop
0x00401420 <+56>: sw zero,24(s8) # 24(s8) = 0
0x00401424 <+60>: b 0x4014a8 <phase_5+192> # 跳+192行
0x00401428 <+64>: nop
0x0040142c <+68>: lw v0,24(s8) # 取v0 = 24(s8) = 循环计次量
0x00401430 <+72>: lw v1,24(s8) # 取v1 = 24(s8) = 循环计次量
0x00401434 <+76>: lw a0,72(s8) # 取a0 = 输出字符串的首位char的地址(x *($s8+72)可以看到)
0x00401438 <+80>: nop
0x0040143c <+84>: addu v1,a0,v1 # v1 = str[0]+v1
0x00401440 <+88>: lb v1,0(v1) # 读入v1的值, 此时v1应该是地址,那么a0也是地址,上一步中v1就是下标偏移量,lb是读末八位进来,也就是v1=str[i]的ASCII码
# 上面几句应该就是第几次循环读下标为几的char
0x00401444 <+92>: nop
0x00401448 <+96>: andi v1,v1,0xff # v1 &= 0xff 11111111
0x0040144c <+100>: andi v1,v1,0xf # v1 &= 0xf 00001111
# 上面两步就是消掉这个char前4位
0x00401450 <+104>: sll v0,v0,0x2 # v0 *= 4
0x00401454 <+108>: addiu a0,s8,24 # a0 = s8+24是一个地址
0x00401458 <+112>: addu v0,a0,v0 # v0 = a0+v0循环计次量*4后的东西
0x0040145c <+116>: sw v1,12(v0) # 再令12(v0) = v1 = str[i]消掉前4位
0x00401460 <+120>: lw a0,24(s8) # a0 = 循环计次量
0x00401464 <+124>: lw v0,24(s8) # v0 = 循环计次量
0x00401468 <+128>: nop
0x0040146c <+132>: sll v0,v0,0x2 # a0 *= 4
0x00401470 <+136>: addiu v1,s8,24
0x00401474 <+140>: addu v0,v1,v0
0x00401478 <+144>: lw v1,12(v0)
0x0040147c <+148>: lui v0,0x41
0x00401480 <+152>: addiu v0,v0,12524
0x00401484 <+156>: addu v0,v1,v0
0x00401488 <+160>: lb v1,0(v0)
0x0040148c <+164>: addiu v0,s8,24
0x00401490 <+168>: addu v0,v0,a0
0x00401494 <+172>: sb v1,4(v0)
0x00401498 <+176>: lw v0,24(s8)
0x0040149c <+180>: nop
0x004014a0 <+184>: addiu v0,v0,1
0x004014a4 <+188>: sw v0,24(s8)
0x004014a8 <+192>: lw v0,24(s8) # v0 = 24(s8),就在这里打断点吧
0x004014ac <+196>: nop
0x004014b0 <+200>: slti v0,v0,6 # v0 = (v0<6)
0x004014b4 <+204>: bnez v0,0x40142c <phase_5+68> # 小于6就去68行,猜测,上面v0<6,所以应该是一个循环结构24(s8)是计次变量,循环6次,然后出来,先进去看循环结构
0x004014b8 <+208>: nop
0x004014bc <+212>: sb zero,34(s8) # 令34(s8)=0
0x004014c0 <+216>: addiu v0,s8,28 # v0 = s8+28 , 是一个地址
0x004014c4 <+220>: move a0,v0 # a0
0x004014c8 <+224>: lui v0,0x40
0x004014cc <+228>: addiu a1,v0,10160
0x004014d0 <+232>: jal 0x401cf8 <strings_not_equal> # 进入判断生成字符串和常数字符串“giants”相等不,gdb试探,
0x004014d4 <+236>: nop
0x004014d8 <+240>: beqz v0,0x4014e8 <phase_5+256>
0x004014dc <+244>: nop
0x004014e0 <+248>: jal 0x4021f0 <explode_bomb>
0x004014e4 <+252>: nop
0x004014e8 <+256>: move sp,s8
0x004014ec <+260>: lw ra,68(sp)
0x004014f0 <+264>: lw s8,64(sp)
0x004014f4 <+268>: addiu sp,sp,72
0x004014f8 <+272>: jr ra
0x004014fc <+276>: nop
End of assembler dump.

输入:%s
长度为6,是一个密文
原文giants,要求现在给出密文,映射表如下(小写应该也可以)
A->s,B->r,C->v,D->e,E->a,F->w,G->h,H->o,I->b,J->p,K->n,L->u,M->t,N->f,O->g,P->i
Q->s,R->r,S->v,T->e,U->a,V->w,W->h,X->o,Y->b,Z->p,循环咯

故其中一个答案为:OPUKMQ

Phase_6

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
Dump of assembler code for function phase_6:
0x00401500 <+0>: addiu sp,sp,-96
0x00401504 <+4>: sw ra,92(sp)
0x00401508 <+8>: sw s8,88(sp)
0x0040150c <+12>: move s8,sp
0x00401510 <+16>: lui gp,0x42
0x00401514 <+20>: addiu gp,gp,-20080
0x00401518 <+24>: sw gp,16(sp)
0x0040151c <+28>: sw a0,96(s8)
0x00401520 <+32>: lui v0,0x41
0x00401524 <+36>: addiu v0,v0,12592
0x00401528 <+40>: sw v0,32(s8)
0x0040152c <+44>: addiu v0,s8,36
0x00401530 <+48>: lw a0,96(s8)
0x00401534 <+52>: move a1,v0
0x00401538 <+56>: jal 0x401ba8 <read_six_numbers>
0x0040153c <+60>: nop
0x00401540 <+64>: lw gp,16(s8)
0x00401544 <+68>: sw zero,28(s8) # 令28(s8) = 0,猜测是循环计次变量
0x00401548 <+72>: b 0x40163c <phase_6+316> # 到+316行
0x0040154c <+76>: nop
0x00401550 <+80>: lw v0,28(s8) # v0 = i
0x00401554 <+84>: nop
0x00401558 <+88>: sll v0,v0,0x2 # v0 *= 4
0x0040155c <+92>: addiu v1,s8,24 # v1 = s8+24 地址
0x00401560 <+96>: addu v0,v1,v0
0x00401564 <+100>: lw v0,12(v0) # 再算上这个12,本质从36开始取,取num[i]到v0
0x00401568 <+104>: nop
0x0040156c <+108>: slti v0,v0,7 # v0=(num[i]<7)
0x00401570 <+112>: beqz v0,0x40159c <phase_6+156> # 当num[i]>=7就跳+156行爆炸,所以六个数都要小于7
0x00401574 <+116>: nop
0x00401578 <+120>: lw v0,28(s8) # v0 = 循环计次量i
0x0040157c <+124>: nop
0x00401580 <+128>: sll v0,v0,0x2 # v0 *= 4
0x00401584 <+132>: addiu v1,s8,24
0x00401588 <+136>: addu v0,v1,v0
0x0040158c <+140>: lw v0,12(v0) # 再算上这个12,本质从36开始取,取num[i]到v0
0x00401590 <+144>: nop
0x00401594 <+148>: bgtz v0,0x4015a8 <phase_6+168> # 判断是否大于0,大于就跳+168或者就下一行爆炸,到这里得到了六个数都是[1,6]间的数
0x00401598 <+152>: nop
0x0040159c <+156>: jal 0x4021f0 <explode_bomb>
0x004015a0 <+160>: nop
0x004015a4 <+164>: lw gp,16(s8)
0x004015a8 <+168>: lw v0,28(s8) # v0 = 循环计次量i
0x004015ac <+172>: nop
0x004015b0 <+176>: addiu v0,v0,1 # v0++
0x004015b4 <+180>: sw v0,24(s8) # 令24(s8) = v0,好像也是计次变量,但从1开始
0x004015b8 <+184>: b 0x401618 <phase_6+280> # 跳
0x004015bc <+188>: nop
0x004015c0 <+192>: lw v0,28(s8) # v0 = 循环计次量i
0x004015c4 <+196>: nop
0x004015c8 <+200>: sll v0,v0,0x2 # v0 *= 2
0x004015cc <+204>: addiu v1,s8,24
0x004015d0 <+208>: addu v0,v1,v0
0x004015d4 <+212>: lw v1,12(v0) # 上面几条就是取v1 = num[i]
0x004015d8 <+216>: lw v0,24(s8) # v0 = 24(s8) 这是啥,上次的i,也就是i-1
0x004015dc <+220>: nop
0x004015e0 <+224>: sll v0,v0,0x2
0x004015e4 <+228>: addiu a0,s8,24
0x004015e8 <+232>: addu v0,a0,v0
0x004015ec <+236>: lw v0,12(v0) # 取v0 = num[24(s8)]
0x004015f0 <+240>: nop
0x004015f4 <+244>: bne v1,v0,0x401608 <phase_6+264> # 两个不相等的时候跳过炸弹,现在的意思是相邻两个不相等蛤?
0x004015f8 <+248>: nop
0x004015fc <+252>: jal 0x4021f0 <explode_bomb>
0x00401600 <+256>: nop
0x00401604 <+260>: lw gp,16(s8)
0x00401608 <+264>: lw v0,24(s8) # v0 = 24(s8)
0x0040160c <+268>: nop
0x00401610 <+272>: addiu v0,v0,1 # v0++
0x00401614 <+276>: sw v0,24(s8) # 24(s8) = v0
0x00401618 <+280>: lw v0,24(s8) # 蛤?好像是双循环,i=0->5,j=i+1->5,使得六个数互不相等
0x0040161c <+284>: nop
0x00401620 <+288>: slti v0,v0,6 # v0 = (循环计次量i<6)
0x00401624 <+292>: bnez v0,0x4015c0 <phase_6+192> # 若v0!=0就到+192行
0x00401628 <+296>: nop
0x0040162c <+300>: lw v0,28(s8)
0x00401630 <+304>: nop
0x00401634 <+308>: addiu v0,v0,1
0x00401638 <+312>: sw v0,28(s8)
0x0040163c <+316>: lw v0,28(s8) # v0 = 28(s8) = 循环计次变量
0x00401640 <+320>: nop
0x00401644 <+324>: slti v0,v0,6 # 从0~5,共6次
0x00401648 <+328>: bnez v0,0x401550 <phase_6+80> # 进入循环体
0x0040164c <+332>: nop
0x00401650 <+336>: sw zero,28(s8) # 从上一个双重循环出来,28(s8)=0
0x00401654 <+340>: b 0x4016f8 <phase_6+504> # 跳到+504
0x00401658 <+344>: nop
0x0040165c <+348>: lui v0,0x41 # v0 = 0x41
0x00401660 <+352>: addiu v0,v0,12592 # v0 += 12592
0x00401664 <+356>: sw v0,32(s8) # 32(s8) = v0 此时发现32(s8)是node1的指针
0x00401668 <+360>: li v0,1 # v0 = 1
0x0040166c <+364>: sw v0,24(s8) # 24(s8) = v0
0x00401670 <+368>: b 0x40169c <phase_6+412> # 进入,应该是第二重循环了
0x00401674 <+372>: nop

# ------------------------------这一部分是链表指针后移, 32(s8)是current指针
0x00401678 <+376>: lw v0,32(s8) # v0=32(s8) node1的地址(x *(*($s8+32)))
0x0040167c <+380>: nop
0x00401680 <+384>: lw v0,8(v0) # v0=8(v0) node2的地址
0x00401684 <+388>: nop
0x00401688 <+392>: sw v0,32(s8) # node1的地址指向node2
# ------------------------------这一部分是链表指针后移, 32(s8)是current指针

0x0040168c <+396>: lw v0,24(s8) # v0 = 24(s8)
0x00401690 <+400>: nop
0x00401694 <+404>: addiu v0,v0,1 # v0++了
0x00401698 <+408>: sw v0,24(s8) # 24(s8) = v0
0x0040169c <+412>: lw v0,28(s8) # v0 = 28(s8) 外层循环变量
0x004016a0 <+416>: nop
0x004016a4 <+420>: sll v0,v0,0x2 # v0 *= 4
0x004016a8 <+424>: addiu v1,s8,24
0x004016ac <+428>: addu v0,v1,v0
0x004016b0 <+432>: lw v1,12(v0) # 意思是取36(s8)开始的即输入的第24(s8)个参数到v1
0x004016b4 <+436>: lw v0,24(s8) # 取v0 = 24(s8)
0x004016b8 <+440>: nop
0x004016bc <+444>: slt v0,v0,v1 # v0 = (num[j] < num[i])
0x004016c0 <+448>: bnez v0,0x401678 <phase_6+376> # 上式满足的话进入+376行


0x004016c4 <+452>: nop
0x004016c8 <+456>: lw v0,28(s8) # v0 = 28(s8) , 外层循环
0x004016cc <+460>: nop
0x004016d0 <+464>: sll v0,v0,0x2 # v0 *= 2
0x004016d4 <+468>: addiu v1,s8,24
0x004016d8 <+472>: addu v0,v1,v0
0x004016dc <+476>: lw v1,32(s8) # 取v1 = node的地址
0x004016e0 <+480>: nop
0x004016e4 <+484>: sw v1,36(v0) # 60之后(s8) = node的地址
0x004016e8 <+488>: lw v0,28(s8) # v0 = 28(s8) = i
0x004016ec <+492>: nop
0x004016f0 <+496>: addiu v0,v0,1 # v0++
0x004016f4 <+500>: sw v0,28(s8) # 保存
0x004016f8 <+504>: lw v0,28(s8) # v0 = 28(s8)
0x004016fc <+508>: nop
0x00401700 <+512>: slti v0,v0,6 # 也是循环6次
0x00401704 <+516>: bnez v0,0x40165c <phase_6+348> # 进入循环体
0x00401708 <+520>: nop
0x0040170c <+524>: lw v0,60(s8) # 拿链表指针数组[0]到v0
0x00401710 <+528>: nop
0x00401714 <+532>: sw v0,32(s8) # 存到32(s8)
0x00401718 <+536>: li v0,1 # v0=1
0x0040171c <+540>: sw v0,28(s8) # 28(s8) = v0 = 1
0x00401720 <+544>: b 0x40177c <phase_6+636> # 又要进循环了
0x00401724 <+548>: nop


0x00401728 <+552>: lw v0,28(s8) # v0=i
0x0040172c <+556>: nop
0x00401730 <+560>: sll v0,v0,0x2 # v0 *= 4
0x00401734 <+564>: addiu v1,s8,24
0x00401738 <+568>: addu v0,v1,v0
0x0040173c <+572>: lw v1,36(v0) # v1 = 链表指针数组[i]
0x00401740 <+576>: lw v0,32(s8) # v0 = current
0x00401744 <+580>: nop
0x00401748 <+584>: sw v1,8(v0) # v0.next = v1 ------重点
0x0040174c <+588>: lw v0,28(s8) # v0 = i
0x00401750 <+592>: nop
0x00401754 <+596>: sll v0,v0,0x2
0x00401758 <+600>: addiu v1,s8,24
0x0040175c <+604>: addu v0,v1,v0
0x00401760 <+608>: lw v0,36(v0) # v0 = 链表指针数组[i]
0x00401764 <+612>: nop
0x00401768 <+616>: sw v0,32(s8) # 32(s8) = 链表指针数组[i]
0x0040176c <+620>: lw v0,28(s8) # v0 = i
0x00401770 <+624>: nop
0x00401774 <+628>: addiu v0,v0,1
0x00401778 <+632>: sw v0,28(s8) # v0++存入
0x0040177c <+636>: lw v0,28(s8)
0x00401780 <+640>: nop
0x00401784 <+644>: slti v0,v0,6
0x00401788 <+648>: bnez v0,0x401728 <phase_6+552> # 正式进循环
# 所以上循环作用就是,链表重排


0x0040178c <+652>: nop
0x00401790 <+656>: lw v0,32(s8) # v0 = 32(s8) = 尾节点(最后留的指针)
0x00401794 <+660>: nop
0x00401798 <+664>: sw zero,8(v0) # 尾节点.next = 0
0x0040179c <+668>: lw v0,60(s8) # v0 = 头节点
0x004017a0 <+672>: nop
0x004017a4 <+676>: sw v0,32(s8) # 32(s8) = 头节点指针
0x004017a8 <+680>: sw zero,28(s8) # i = 0
0x004017ac <+684>: b 0x401878 <phase_6+888> # 进+888行
0x004017b0 <+688>: nop


0x004017b4 <+692>: lw v0,-32660(gp) # v0为学号第一位
0x004017b8 <+696>: nop
0x004017bc <+700>: lw v0,44(v0) # v0=学号第12位
0x004017c0 <+704>: nop
0x004017c4 <+708>: andi v0,v0,0x1 # 学号第十二位&=1
0x004017c8 <+712>: andi v0,v0,0xff # 学号第十二位&=255
0x004017cc <+716>: beqz v0,0x401818 <phase_6+792> # 我学号最后一位为0,然后进去了,应该是分支,不同分支不同要求
0x004017d0 <+720>: nop
0x004017d4 <+724>: lw v0,32(s8)
0x004017d8 <+728>: nop
0x004017dc <+732>: lw v1,0(v0)
0x004017e0 <+736>: lw v0,32(s8)
0x004017e4 <+740>: nop
0x004017e8 <+744>: lw v0,8(v0)
0x004017ec <+748>: nop
0x004017f0 <+752>: lw v0,0(v0)
0x004017f4 <+756>: nop
0x004017f8 <+760>: slt v0,v1,v0
0x004017fc <+764>: beqz v0,0x401854 <phase_6+852>
0x00401800 <+768>: nop
0x00401804 <+772>: jal 0x4021f0 <explode_bomb>
0x00401808 <+776>: nop
0x0040180c <+780>: lw gp,16(s8)
0x00401810 <+784>: b 0x401854 <phase_6+852>
0x00401814 <+788>: nop
0x00401818 <+792>: lw v0,32(s8) # 取链表指针数组头
0x0040181c <+796>: nop
0x00401820 <+800>: lw v1,0(v0) # v1 = 当前节点内容
0x00401824 <+804>: lw v0,32(s8)
0x00401828 <+808>: nop
0x0040182c <+812>: lw v0,8(v0) # v0 = 链表指针数组头.next的地址
0x00401830 <+816>: nop
0x00401834 <+820>: lw v0,0(v0) # v0 = 链表指针数组头.next的内容了
0x00401838 <+824>: nop
0x0040183c <+828>: slt v0,v0,v1 # 要求 v0 >= v1, 肉眼看感觉很费脑筋啊,display *(*($s8+32)) 和 display *(*( (*($s8+32))+8 ))一下,第二个变量一直是725应该我的分支要求,现在就是看第一个怎么得来的
0x00401840 <+832>: beqz v0,0x401854 <phase_6+852> # 这里爆炸
0x00401844 <+836>: nop
0x00401848 <+840>: jal 0x4021f0 <explode_bomb>
0x0040184c <+844>: nop
0x00401850 <+848>: lw gp,16(s8)
0x00401854 <+852>: lw v0,32(s8) # v0 = 取链表指针数组头的地址
0x00401858 <+856>: nop
0x0040185c <+860>: lw v0,8(v0) # v0 = 取链表指针数组头.next的地址
0x00401860 <+864>: nop
0x00401864 <+868>: sw v0,32(s8) # 存到32(s8)
0x00401868 <+872>: lw v0,28(s8)
0x0040186c <+876>: nop
0x00401870 <+880>: addiu v0,v0,1
0x00401874 <+884>: sw v0,28(s8)
0x00401878 <+888>: lw v0,28(s8) # 又一个循环
0x0040187c <+892>: nop
0x00401880 <+896>: slti v0,v0,5 # 这一次是从[0~5),4次
0x00401884 <+900>: bnez v0,0x4017b4 <phase_6+692>
# 这一循环是判重排链表非递减序

0x00401888 <+904>: nop
0x0040188c <+908>: move sp,s8
0x00401890 <+912>: lw ra,92(sp)
0x00401894 <+916>: lw s8,88(sp)
0x00401898 <+920>: addiu sp,sp,96
0x0040189c <+924>: jr ra
0x004018a0 <+928>: nop
End of assembler dump.

读入:%d %d %d %d %d %d
要求链表重排
链表节点node1~6 大小分别是 253,725,301,997,212,432
最后的顺序是node5_node1_node3_node6_node2_node4
要求重排成这个样子
重排程序中,对于第i个数,1~6个数中有多少个小于num[i],就取node几+1(因为执行了那么多次next())
那么第一个数应取node5,即执行4次next(),为5的时候有4个数小于5,第一个数取5
剩下同理
第六关答案为
5 1 3 6 2 4

secret_phase

找到这个隐藏关卡,要在phase_defused里看到触发条件

phase_defused探秘

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
Dump of assembler code for function phase_defused:
0x00402264 <+0>: addiu sp,sp,-120
0x00402268 <+4>: sw ra,116(sp)
0x0040226c <+8>: sw s8,112(sp)
0x00402270 <+12>: move s8,sp
0x00402274 <+16>: lui gp,0x42
0x00402278 <+20>: addiu gp,gp,-20080
0x0040227c <+24>: sw gp,16(sp)
0x00402280 <+28>: lui v0,0x41
0x00402284 <+32>: lw v1,12864(v0)
0x00402288 <+36>: li v0,6
0x0040228c <+40>: bne v1,v0,0x402374 <phase_defused+272> # 非第六关的完成,就直接跳272,否则继续

0x00402290 <+44>: nop
0x00402294 <+48>: lw v0,-32680(gp)
0x00402298 <+52>: nop
0x0040229c <+56>: addiu a0,v0,240 # 读入指针打到第三关(0 120 240)
0x004022a0 <+60>: lui v0,0x40
0x004022a4 <+64>: addiu v1,v0,10408
0x004022a8 <+68>: addiu v0,s8,104
0x004022ac <+72>: move a1,v1
0x004022b0 <+76>: move a2,v0
0x004022b4 <+80>: addiu v0,s8,24
0x004022b8 <+84>: move a3,v0
0x004022bc <+88>: lw v0,-32636(gp)
0x004022c0 <+92>: nop
0x004022c4 <+96>: move t9,v0
0x004022c8 <+100>: jalr t9
0x004022cc <+104>: nop
0x004022d0 <+108>: lw gp,16(s8)
0x004022d4 <+112>: move v1,v0
0x004022d8 <+116>: li v0,2
0x004022dc <+120>: bne v1,v0,0x402354 <phase_defused+240> # 判断读入了不等于两个参数,到+240行出函数
0x004022e0 <+124>: nop
0x004022e4 <+128>: addiu v0,s8,24
0x004022e8 <+132>: move a0,v0
0x004022ec <+136>: lui v0,0x40
0x004022f0 <+140>: addiu a1,v0,10416
0x004022f4 <+144>: jal 0x401cf8 <strings_not_equal>
0x004022f8 <+148>: nop
0x004022fc <+152>: lw gp,16(s8)
0x00402300 <+156>: bnez v0,0x402354 <phase_defused+240> # v0 = <strings_not_equal>返回值不等于0的话就跳过隐藏关,否则顺着下去+228就能进隐藏关
0x00402304 <+160>: nop
0x00402308 <+164>: lui v0,0x40
0x0040230c <+168>: addiu a0,v0,10432
0x00402310 <+172>: lw v0,-32712(gp)
0x00402314 <+176>: nop
0x00402318 <+180>: move t9,v0
0x0040231c <+184>: jalr t9
0x00402320 <+188>: nop
0x00402324 <+192>: lw gp,16(s8)
0x00402328 <+196>: lui v0,0x40
0x0040232c <+200>: addiu a0,v0,10472
0x00402330 <+204>: lw v0,-32712(gp)
0x00402334 <+208>: nop
0x00402338 <+212>: move t9,v0
0x0040233c <+216>: jalr t9
0x00402340 <+220>: nop
0x00402344 <+224>: lw gp,16(s8)
0x00402348 <+228>: jal 0x401990 <secret_phase> # 进入隐藏关
0x0040234c <+232>: nop
0x00402350 <+236>: lw gp,16(s8)
0x00402354 <+240>: lui v0,0x40 # 出phase_defused咯
0x00402358 <+244>: addiu a0,v0,10528
0x0040235c <+248>: lw v0,-32712(gp)
0x00402360 <+252>: nop
0x00402364 <+256>: move t9,v0
0x00402368 <+260>: jalr t9
0x0040236c <+264>: nop
0x00402370 <+268>: lw gp,16(s8)
0x00402374 <+272>: move sp,s8
0x00402378 <+276>: lw ra,116(sp)
0x0040237c <+280>: lw s8,112(sp)
0x00402380 <+284>: addiu sp,sp,120
0x00402384 <+288>: jr ra
0x00402388 <+292>: nop
End of assembler dump.

# 也就是说,在第6关完成时,会判断一下第4关是否读入了两个参数,找一下汇编源码常量,发现有"%d %s"没用过,还有常量$LC27"austinpowers"没用过

第4关答案改为
6 austinpowers
在完成第6关时进入隐藏关卡

正式进入secret_phase

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
Dump of assembler code for function secret_phase:
0x00401990 <+0>: addiu sp,sp,-40
0x00401994 <+4>: sw ra,36(sp)
0x00401998 <+8>: sw s8,32(sp)
0x0040199c <+12>: move s8,sp
0x004019a0 <+16>: lui gp,0x42
0x004019a4 <+20>: addiu gp,gp,-20080
0x004019a8 <+24>: sw gp,16(sp)
0x004019ac <+28>: jal 0x401fec <read_line> # 读一行
0x004019b0 <+32>: nop
0x004019b4 <+36>: lw gp,16(s8)
0x004019b8 <+40>: sw v0,28(s8) # 28(s8) = v0
0x004019bc <+44>: lw v0,28(s8) # v0 = 28(s8)
0x004019c0 <+48>: nop
0x004019c4 <+52>: move a0,v0 # a0 = v0
0x004019c8 <+56>: move a1,zero # a1 = 0
0x004019cc <+60>: li a2,10 # a2 = 10
0x004019d0 <+64>: lw v0,-32656(gp) # v0 = strtol的地址
0x004019d4 <+68>: nop
0x004019d8 <+72>: move t9,v0
0x004019dc <+76>: jalr t9
0x004019e0 <+80>: nop
0x004019e4 <+84>: lw gp,16(s8)
0x004019e8 <+88>: sw v0,24(s8)
0x004019ec <+92>: lw v0,24(s8)
0x004019f0 <+96>: nop
0x004019f4 <+100>: addiu v0,v0,-1
0x004019f8 <+104>: sltiu v0,v0,1001
0x004019fc <+108>: bnez v0,0x401a10 <secret_phase+128>
# strtol的返回值-1之后保证小于1001,因为是unsgined,故输入是小于等于1001大于0的一个数字字符串
0x00401a00 <+112>: nop
0x00401a04 <+116>: jal 0x4021f0 <explode_bomb>
0x00401a08 <+120>: nop
0x00401a0c <+124>: lw gp,16(s8)
0x00401a10 <+128>: lui v0,0x41 # v0 = 0x41
0x00401a14 <+132>: addiu a0,v0,12676 # a0 指向对象为n1
0x00401a18 <+136>: lw a1,24(s8) # a1 = 输入字符串截取的前半部分数字
0x00401a1c <+140>: jal 0x4018a4 <fun7> # 进fun7
0x00401a20 <+144>: nop
0x00401a24 <+148>: lw gp,16(s8)
0x00401a28 <+152>: move v1,v0
# 从fun7回来,将返回值放到v1
0x00401a2c <+156>: li v0,7
0x00401a30 <+160>: beq v1,v0,0x401a44 <secret_phase+180> # 现在就是v1要==7的意思
0x00401a34 <+164>: nop
0x00401a38 <+168>: jal 0x4021f0 <explode_bomb>
0x00401a3c <+172>: nop
0x00401a40 <+176>: lw gp,16(s8)
0x00401a44 <+180>: lui v0,0x40
0x00401a48 <+184>: addiu a0,v0,10168
0x00401a4c <+188>: lw v0,-32712(gp)
0x00401a50 <+192>: nop
0x00401a54 <+196>: move t9,v0
0x00401a58 <+200>: jalr t9
0x00401a5c <+204>: nop
0x00401a60 <+208>: lw gp,16(s8)
0x00401a64 <+212>: jal 0x402264 <phase_defused>
0x00401a68 <+216>: nop
0x00401a6c <+220>: lw gp,16(s8)
0x00401a70 <+224>: move sp,s8
0x00401a74 <+228>: lw ra,36(sp)
0x00401a78 <+232>: lw s8,32(sp)
0x00401a7c <+236>: addiu sp,sp,40
0x00401a80 <+240>: jr ra
0x00401a84 <+244>: nop
End of assembler dump.

引入fun7

看看什么时候返回值为7

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
Dump of assembler code for function fun7:
0x004018a4 <+0>: addiu sp,sp,-32
0x004018a8 <+4>: sw ra,28(sp)
0x004018ac <+8>: sw s8,24(sp)
0x004018b0 <+12>: move s8,sp
0x004018b4 <+16>: sw a0,32(s8)
0x004018b8 <+20>: sw a1,36(s8) # 36(s8) = a1 传参
0x004018bc <+24>: lw v0,32(s8) # v0 = 32(s8) = n1对象
0x004018c0 <+28>: nop
0x004018c4 <+32>: bnez v0,0x4018d8 <fun7+52> # v0 != null才跳+52
0x004018c8 <+36>: nop
0x004018cc <+40>: li v0,-1 # 返回值v0 = -1
0x004018d0 <+44>: b 0x401978 <fun7+212> # 跳+212行,212行是return
# 这里的意思是若v0==null就返回-1

0x004018d4 <+48>: nop
0x004018d8 <+52>: lw v0,32(s8) # v0 = 32(s8) = n1对象
0x004018dc <+56>: nop
0x004018e0 <+60>: lw v1,0(v0) # v1 = v0的第一个成员变量
0x004018e4 <+64>: lw v0,36(s8) # v0 = 36(s8) = 传参
0x004018e8 <+68>: nop
0x004018ec <+72>: slt v0,v0,v1 # v0 > v1 则 v0 = 1
0x004018f0 <+76>: beqz v0,0x401924 <fun7+128>
# 传参 <= v1就跳+128

0x004018f4 <+80>: nop
0x004018f8 <+84>: lw v0,32(s8)
0x004018fc <+88>: nop
0x00401900 <+92>: lw v0,4(v0) # v0的第二个成员变量
0x00401904 <+96>: nop
0x00401908 <+100>: move a0,v0
0x0040190c <+104>: lw a1,36(s8)
0x00401910 <+108>: jal 0x4018a4 <fun7> # 进入递归
0x00401914 <+112>: nop
0x00401918 <+116>: sll v0,v0,0x1 # 将fun7递归回来的返回值*2
0x0040191c <+120>: b 0x401978 <fun7+212>
0x00401920 <+124>: nop
# 传参 < v0的第一个成员变量,就递归fun7,从递归回来后进入212行,212行是return,
# 传参 < v0的第一个成员变量,就return fun7(v0的第二个成员变量)*2

0x00401924 <+128>: lw v0,32(s8)
0x00401928 <+132>: nop
0x0040192c <+136>: lw v1,0(v0) # v1 = v0的第一个成员变量
0x00401930 <+140>: lw v0,36(s8) # v0 = 36(s8) = 传参
0x00401934 <+144>: nop
0x00401938 <+148>: slt v0,v1,v0 # v1 > v0
0x0040193c <+152>: beqz v0,0x401974 <fun7+208>
# v1 <= 传参就跳+208行,208行是return 0,结合从+76过来的条件,<=和>=就是等于
# v0的第一个成员变量==传参,就return 0

0x00401940 <+156>: nop
0x00401944 <+160>: lw v0,32(s8)
0x00401948 <+164>: nop
0x0040194c <+168>: lw v0,8(v0)
0x00401950 <+172>: nop
0x00401954 <+176>: move a0,v0
0x00401958 <+180>: lw a1,36(s8)
0x0040195c <+184>: jal 0x4018a4 <fun7>
0x00401960 <+188>: nop
0x00401964 <+192>: sll v0,v0,0x1 # v0 *= 2
0x00401968 <+196>: addiu v0,v0,1 # v0 + 1
0x0040196c <+200>: b 0x401978 <fun7+212> # 跳到212返回v0
0x00401970 <+204>: nop
# 传参 > v0的第一个成员变量,就return fun7(v0的第三个成员变量)*2+1

0x00401974 <+208>: move v0,zero # 返回值为0
0x00401978 <+212>: move sp,s8
0x0040197c <+216>: lw ra,28(sp)
0x00401980 <+220>: lw s8,24(sp)
0x00401984 <+224>: addiu sp,sp,32
0x00401988 <+228>: jr ra
0x0040198c <+232>: nop
End of assembler dump.


可以发现fun7是对一个搜索树(链表描述):
这棵树从汇编源码中更易得到
36
8 50
6 22 45 107
1 7 20 35 40 47 99 1001
左小右大,是二叉搜索树
fun7翻译一下就是:
int fun7(传参)
{
if(current==null) return -1;
if(current==传参) return 0;
else if(current>传参) current=current.leftChild, return fun7(传参)*2;
else current=current.rightChild, return fun7(传参)*2+1;
}
二叉搜索树的搜索节点算法,有节点返回路径,无节点返回-1
比如搜索1001返回7,7的二进制是00000111,1代表往右子树,0代表往左子树,二进制从右往左数念h-1位(树的高度-1)就是"右 右 右",代表从根到被搜索节点的路径。

那么隐藏关卡答案就是:
1001

  CS

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×