山东大学计算机系统原理实验——拆除炸弹(反汇编)

<phase_1>:

不难发现,在

400d8c:   0c10073e   jal   401cf8 <strings_not_equal>

处进行了输入,先在 0x00400bb8 处设置一个断点,查看寄存器 $v0 的值,与输入的内容相同。说明v0保存了输入的字符

说明v0 中的值存到(s8+32)中去,并且从该内存中读取字符串保存到寄存器 a0中。

然后发现 

400d88:   2445276c   addiu      a1,v0,10092

说明a1 和 v0 即输入进行了比较,并且可以得知返回结果储存在v0中(0为不相等,1为相等,

400d94:      10400003 beqz v0,400da4 <phase_1+0x38>

在这里进行了v0值的判断,若v0为0(则继续,炸弹爆炸),不为 0 跳转到 <+38> 的位置,炸弹不爆炸。

然后使用ni命令看出a1处的内容为“Let’s begin now!”

当输入为“Let’s begin now!”时,炸弹不会触发

 

<phase_2>:

注意到,

400dfc:     10620004   beq v1,v0,400e10 <phase_2+0x54>

Beq指令对比v1,v0寄存器中的内容,相等时发生转移,发现转移的位置为

400e10: 24020001   li     v0,1,

跳过了指令:

400e04: 0c10087c jal 4021f0 <explode_bomb>,

避免了一次炸弹爆炸,说明首先v0和v1的内容要相等。

设置400dfc:     10620004   beq v1,v0,400e10 <phase_2+0x54>

为断点,试着输入 hello world!炸弹爆炸。

使用测试数据发现,输入有格式要求,发现

400de8:    0c1006ea   jal   401ba8 <read_six_numbers>

要求输入六个数字

试着输入 1 1 4 5 1 4 六个数字

发现v0,v1皆为1,合理怀疑其中一个为输入的字母,发现

400df8:    24020001    li                //v0,1将1加载到v0寄存器中
400e68:    8fc20018    lw   v0,24(s8)    //从内存中加载一个字的内容

寻找该内存的其他信息

发现前六位为输入数据,后两位为无效数据,说明输入的第一个字符必须为1

继续向下,跳转到400e10:  24020001   li     v0,1,

通过查询指令可得知

400e10:   24020001   li     v0,1                //把1加载到寄存器v0里
400e14:   afc20018   sw  v0,24(s8)              //把v0寄存器的内容存至内存中
400e18:   10000023   b    400ea8 <phase_2+0xec> //跳转至400ea8

将400ea8设置为断点

400eb0:   28420006   slti  v0,v0,6                        //v0小于6,则v0为1,否则为0
400eb4:   1440ffda   bnez     v0,400e20 <phase_2+0x64>    //v0不为0时发生跳转

跳转的位置是

400e20:   8fc20018    lw   v0,24(s8)

说明在e20和eb4之间进行了六次循环,推测是进行了六个数字的对比

发现400e84:     10820004   beq a0,v0,400e98 <phase_2+0xdc>

对a0和v0进行了对比,相同则跳转至e98,跳过了炸弹爆炸

对中间的指令进行查询:

400e20:   8fc20018   lw   v0,24(s8)            //循环开始(循环次数)
400e24:   00000000   nop
400e28:   2442ffff   addiu    v0,v0,-1         //把v0-1的结果存入v0
400e2c:   00021080   sll   v0,v0,0x2           //把v0左移两位,即乘四
400e30:   27c30018   addiu    v1,s8,24         //把s8+24的结果存入v1
400e34:   00621021   addu    v0,v1,v0          //把v0+v1的结果存入v0
400e38:   8c440004   lw   a0,4(v0)
400e3c:   2403000c   li     v1,12              //把12加载到寄存器v1里
400e40:   8fc20018   lw   v0,24(s8)            //把内存中的内容加载至v0(循环次数)
400e44:   00000000   nop
400e48:   00621023   subu     v0,v1,v0         //把v1-v0的值存进v0
400e4c:   8f83806c   lw   v1,-32660(gp)        //把内存加载至v1
400e50:   00021080   sll   v0,v0,0x2           //v0左移两位,即乘4
400e54:   00621021   addu    v0,v1,v0          //把v1+v0的值存进v0
400e58:   8c420000   lw   v0,0(v0)
400e5c:   00000000   nop
400e60:   00820018   mult  a0,v0               //将a0与v0相乘,储存在HI,LO寄存器中
400e64:   00002012   mflo     a0               //将HI,LO寄存器中的内容存放至a0
400e68:   8fc20018   lw   v0,24(s8)            //把内存中的内容加载至v0(循环次数
400e6c:   00000000   nop
400e70:   00021080   sll   v0,v0,0x2           //把v0左移两位,即乘四
400e74:   27c30018   addiu    v1,s8,24         //把s8+24的结果存入v1
400e78:   00621021   addu    v0,v1,v0          //把v0+v1的结果存入v0
400e7c:   8c420004   lw   v0,4(v0)             //把v0+4的内容存入v0
400e80:   00000000   nop
400e84:   10820004   beq a0,v0,400e98 <phase_2+0xdc>

发现,24(s8)处储存循环次数,-32660(gp)加十二个字是输入学号的最后一位,即为a0的初始值。

每循环一次,a0就与前一位相乘。然后将a0与输入的数字进行比较,相等炸弹就不会爆炸

我的学号为:202200130021,应当输入1 1 2 0 0 0时炸弹不爆炸

<phase_3>:

炸弹三代码量很大,但总的来看是一个switch结构,通过输入的第一个数字来判断进入的case;

输入的结构是int char int;

400f2c:    8fdc0018   lw   gp,24(s8)
400f30:    28420003   slti  v0,v0,3
400f34:    10400004   beqz     v0,400f48 <phase_3+0x74>

通过断点调试,gp这里加载的是输入的个数,即输入个数大于等于3时才发生跳转,即跳过炸弹。

输入的第一个数的范围是0到7

400f50:    2c430008   sltiu    v1,v0,8
400f54:    1060008e   beqz     v1,401190 <phase_3+0x2bc>

V0的内容即为输入的第一个数,若第一个数大于等于8就会跳转,发现跳转后炸弹爆炸。

接下来,对于不同的v0的值有8种情况:

当v0 = 0:

400f7c:    24020071   li     v0,113         //设置 v0 寄存器为 113
400f80:    a3c20020   sb   v0,32(s8)        //将v0的值存储到 (s8 + 32) 的地址
400f84:    8f82806c   lw   v0,-32660(gp)    //从全局变量加载 v0 寄存器的值,经确认,为学号
400f88:    00000000   nop 
400f8c:    8c43002c   lw   v1,44(v0)        //加载学号的最后一位为v1
400f90:    8fc20024   lw   v0,36(s8)        //此处加载的为最后一个输入
400f94:    00000000   nop
400f98:    00620018   mult     v1,v0        //计算 v1 * v0,并将结果存储在 v1 中
400f9c:    00001812   mflo     v1           //将乘积的低 32 位存储在 v1 中
400fa0:    24020309   li     v0,777         //设置 v0 寄存器为 777
400fa4:    10620081   beq v1,v0,4011ac      //如果 v1 等于 v0,则跳转到 4011ac,炸弹不会爆炸

4011ac:    00000000   nop
4011b0:    10000011   b    4011f8 <phase_3+0x324>跳转到 4011ac

4011f8:    83c20028   lb   v0,40(s8)        //此处为第二个输入,即字母的ascll码
4011fc:    83c30020   lb   v1,32(s8)        // v1即为113
401200:    00000000   nop
401204:    10620004   beq v1,v0,401218 <phase_3+0x344> 若字母ascll码等于113,即输入‘q’,发生跳转,炸弹不会爆炸。

以400f98为断点,查看v0寄存器以及v1寄存器的值,

发现v1为学号的最后一位,v0为最后一个输入

综上所述,当输入的第一个数为0,要确保输入的第二个数字与学号的最后一位的乘积为777,且输入的字母为q

当v0 = 1:

相似的,当输入的第一个数为1,要确保输入的第二个数字与学号的最后一位的乘积为214,且输入的字母为b

当v0 = 2:

相似的,当输入的第一个数为2,要确保输入的第二个数字与学号的最后一位的乘积为755,且输入的字母为b

当v0 = 3:

相似的,当输入的第一个数为3,要确保输入的第二个数字与学号的最后一位的乘积为0,且输入的字母为k,若学号的最后一位为0,也只能用这种解法

当v0 = 4:

相似的,当输入的第一个数为4,要确保输入的第二个数字与学号的最后一位的乘积为228,且输入的字母为o

当v0 = 5:

相似的,当输入的第一个数为5,要确保输入的第二个数字与学号的最后一位的乘积为513,且输入的字母为t

当v0 = 6:

此种情况不同,前面判断完第二个数字与最后一位的乘积为780后,虽然跳过了炸弹,但是跳转到了v0 = 7的情况里

40112c:   00620018   mult     v1,v0
401130:   00001812   mflo     v1
401134:   2402030c   li     v0,780
401138:   10620004   beq v1,v0,40114c <phase_3+0x278> //40114c并没有跳转到最后,而是在v0 = 7里

而v0 = 7里对于乘积的要求与v0 = 6的并不一样(悲,怎么可能有一个数字既等于780,又等于824)

所以当输入的第一个数字是6的时候,已经注定了炸弹爆炸的命运。

当v0 = 7:

相似的,当输入的第一个数为7,要确保输入的第二个数字与学号的最后一位的乘积为824,且输入的字母为b

<phase_4>:

顺着phase_4 发现401328: 1c400005   bgtz     v0,401340 <phase_4+0x84>

判断v0是否大于0,大于0则发生跳转,跳过了炸弹

设置401328为断点,查看v0寄存器的值

发现与输入的数字相同,所以输入要大于0。继续探索

401340:   8f82806c   lw   v0,-32660(gp)
401344:   00000000   nop
401348:   8c42002c   lw   v0,44(v0)

已经多次见到,v0的内容是学号的最后一位

401350:   30420001   andi      v0,v0,0x1
401354:   304200ff   andi      v0,v0,0xff
401358:   10400010   beqz     v0,40139c <phase_4+0xe0> 

将v0寄存器先与0x1进行按位与操作,再与0xff进行按位与操作

如果v0为奇数,结果为1,不跳转,如果为偶数,跳转到40139c

如果v0为奇数: 

401360:     8fc20018   lw   v0,24(s8)  //加载 v0 寄存器的值。通过经验以及断点调试,此处为输入的数字
401364:     00000000   nop
401368:     00402021   move    a0,v0           //将 v0 的值赋给 a0
40136c:     0c10048c   jal   401230 <func4>    //调用函数 func4
401370:     00000000   nop
401374:     8fdc0010   lw   gp,16(s8)          //从栈上加载 gp的值
401378:     00401821   move    v1,v0           //将返回值保存到 v1 寄存器
40137c:     24020008   li     v0,8             //设置 v0 寄存器为 8
401380:     10620013   beq v1,v0,4013d0 <phase_4+0x114>  //如果 v1的值等于 8,跳转到 4013d0 跳过炸弹
401384:     00000000   nop
401388:     0c10087c   jal   4021f0 <explode_bomb> 
40138c:     00000000   nop
401390:     8fdc0010   lw   gp,16(s8)  
401394:     1000000e   b    4013d0 <phase_4+0x114>       //跳转到 4013d0

即把输入的数字作为func4的参数,然后判断返回值是否是8,如果是炸弹就不会爆炸。接下来探索func4,注意到

401250:     28420002   slti  v0,v0,2         //判断 v0 是否小于 2
401254:     14400011   bnez     v0,40129c <func4+0x6c> //如果 v0 不为零,跳转到 40129c
401258:     00000000   nop
40125c:     8fc20028   lw   v0,40(s8)        //加载v0的值,即为当前的循环
401260:     00000000   nop
401264:     2442ffff   addiu    v0,v0,-1     // 将 v0 减 1
401268:     00402021   move    a0,v0         //将 v0 的值赋给 a0
40126c:     0c10048c   jal   401230 <func4>  //递归调用函数 func4
401270:     00000000   nop
401274:     00408021   move    s0,v0         //将返回值保存到 s0 寄存器
401278:     8fc20028   lw   v0,40(s8)        //从栈上加载 v0 寄存器的值
40127c:     00000000   nop
401280:     2442fffe   addiu    v0,v0,-2     //将 v0 减 2
401284:     00402021   move    a0,v0         //将 v0 的值赋给 a0
401288:     0c10048c   jal   401230 <func4>  //递归调用函数 func4
40128c:     00000000   nop
401290:     02021021   addu    v0,s0,v0      //将 s0 和返回值相加
401294:     10000002   b    4012a0 <func4+0x70>  //跳转到 4012a0
401298:     00000000   nop
40129c:     24020001   li     v0,1           //设置 v0为1
4012a0:     03c0e821   move    sp,s8         //设置 sp 寄存器为 s8 的值

发现func4实为求斐波那契数列的函数,即func4(v0) = func4(v0-1)+ func4(v0-2),返回值为f(v0),即斐波那契数列第v0位的值,储存在寄存器上。

所以对于学号最后一位为奇数的情况,要想炸弹不爆炸,需要输入8在斐波那契数列中的位置,即5。

接下来,应对学号最后一位为偶数的情况:

4013b0:     8fdc0010   lw   gp,16(s8)
4013b4:     00401821   move    v1,v0
4013b8:     2402000d   li     v0,13
4013bc:     10620004   beq v1,v0,4013d0 <phase_4+0x114>

相似地,对于学号最后一位为偶数的情况,要想炸弹不爆炸,需要输入13在斐波那契数列中的位置,即6。

<phase_5>:

两条判断是否爆炸的语句,分别为:

401418: 0c10087c   jal   4021f0 <explode_bomb>与

4014e0: 0c10087c   jal   4021f0 <explode_bomb>

第一条前有401410: 10620003   beq v1,v0,401420 <phase_5+0x38>

比较了v0 与 v1的值,v0的为6,设置401410为断点

可知v1 的值为输入字段的长度

说明输入必须为6个字符

继续向下看,

发现(s8 + 72)处储存的就是输入的字符

401420:   afc00018   sw zero,24(s8)  //将零值存储在(s8 + 24)

. . . . . .

401498:   8fc20018   lw v0,24(s8)    //从(s8 + 24)处加载值到寄存器 v0
40149c:   00000000   nop
4014a0:   24420001   addiu v0,v0,1   //将寄存器 v0 的值加 1
4014a4:   afc20018   sw v0,24(s8)    //将v0的值存储在(s8 + 24)
4014a8:   8fc20018   lw v0,24(s8)    //从(s8 + 24)加载值到寄存器 v0
4014ac:   00000000   nop
4014b0:   28420006   slti v0,v0,6    //将v0 的值与 6 比较,v0比6小则为1
4014b4:   1440ffdd   bnez v0,40142c <phase_5+0x44> //如果 v0 不为零,则跳转到地址

这些代码实现了一个次数为6的循环,接下来让我们看看循环内的内容

401430:   8fc30018   lw v1,24(s8)      //从(s8 + 24)加载值到v1(循环次数)
401434:   8fc40048   lw a0,72(s8)      //从(s8 + 72)加载a0 (输入字符)
401438:   00000000   nop
40143c:   00831821   addu v1,a0,v1     //计算 v1 = a0 + v1
401440:   80630000   lb v1,0(v1)       //从内存中加载一个字节到寄存器 v1
401444:   00000000   nop 
401448:   306300ff   andi v1,v1,0xff   //将 v1 按位与 0xff,保留低 8 位
40144c:   3063000f   andi v1,v1,0xf    //将 v1 按位与 0xf,保留低 4 位
401450:   00021080   sll v0,v0,0x2     //将寄存器 v0 左移 2 位
401454:   27c40018   addiu a0,s8,24    //将(s8 + 24)存储在a0 中(循环次数)
401458:   00821021   addu v0,a0,v0     //计算 v0 = a0 + v0
40145c:   ac43000c   sw v1,12(v0)      //将v1 中的值存储在内存地址 v0 + 12 处

在第n次循环中,处理第n位输入,保存此字符的ascll码后四位

401460:   8fc40018   lw a0,24(s8)       //从(s8 + 24)加载参数 a0(循环次数)
401464:   8fc20018   lw v0,24(s8)       //从(s8 + 24)加载值到v0(循环次数)
401468:   00000000   nop
40146c:   00021080   sll v0,v0,0x2      //将寄存器 v0 左移 2 位
401470:   27c30018   addiu v1,s8,24     //将当(s8 + 24)存储在v1 中(循环次数)
401474:   00621021   addu v0,v1,v0      //计算 v0 = v1 + v0
401478:   8c43000c   lw v1,12(v0)       //从内存地址 v0 + 12 处加载值到寄存器 v1
40147c:   3c020041   lui v0,0x41        //将常数 0x41 存储在寄存器 v0 的高 16 位
401480:   244230ec   addiu v0,v0,12524  //将常数12524存储在v0 的低 16 位
401484:   00621021   addu v0,v1,v0      //计算 v0 = v1 + v0
401488:   80430000   lb v1,0(v0)        //从内存地址 v0 处加载一个字节到v1
40148c:   27c20018   addiu v0,s8,24     //将(s8 + 24)存储在v0 中(循环次数)
401490:   00441021   addu v0,v0,a0      //计算 v0 = v0 + a0
401494:   a0430004   sb v1,4(v0)        //将v1 中的值存储在内存地址 v0 + 4 处

401488这里访问了内存地址v0,将401488设置为断点,查看v0处的内容

是一个字符串

401470:   27c30018   addiu v1,s8,24       //将当(s8 + 24)存储在v1 中(循环次数)
401474:   00621021   addu v0,v1,v0        //计算 v0 = v1 + v0
401478:   8c43000c   lw v1,12(v0)         //从内存地址 v0 + 12 处加载值到寄存器 v1
40147c:   3c020041   lui v0,0x41          //将常数 0x41 存储在寄存器 v0 的高 16 位
401480:   244230ec   addiu v0,v0,12524    //将常数12524存储在v0 的低 16 位
401484:   00621021   addu v0,v1,v0        //计算 v0 = v1 + v0

这里v1处的值,即为第n位输入的ascll码的后四位,v0为0x00410000加上12524,计算过后为0x004130ec,发现正是内存v0地址的前一位

即为完整的16位字符表

所以输入的字符,将会转换为字符表里的某一位,即从0x004130ec开始,加上输入的字符ascll码的后四位,第几个即为转换过的字符。

如输入b,后四位为0010,则为2,对应字符表里是r。

4014bc:   a3c00022   sb zero,34(s8)       //将零值存储在当前栈帧的偏移 34 处
4014c0:   27c2001c   addiu v0,s8,28       //计算 v0 = s8 + 28
4014c4:   00402021   move a0,v0           //将 v0 的值存储在 a0 中
4014c8:   3c020040   lui v0,0x40          //将0x40 存储在v0 的高 16 位
4014cc:   244527b0   addiu a1,v0,10160    //将v0 + 10160的值存储在a1 中
4014d0:   0c10073e   jal 401cf8 <strings_not_equal>  //调用对比函数
4014d4:   00000000   nop
4014d8:   10400003   beqz v0,4014e8 <phase_5+0x100> //如果 v0 为零,则跳转到地址

接下来将转换后的字符串与a1处对比,若相等则v0为0,同时跳过炸弹

设置4014d0断点查看a1的值

 

说明要让转换后的字符串为giants

通过计算,输入为opekma时,转换为giants,答案不唯一

<phase_6>:

401500:   27bdffa0   addiu    sp,sp,-96     //分配 96 字节的栈空间
401504:   afbf005c   sw  ra,92(sp)          //保存返回地址
401508:   afbe0058   sw  s8,88(sp)          //保存调用者的栈帧指针
40150c:   03a0f021   move    s8,sp          //设置当前栈帧指针
401510:   3c1c0042   lui   gp,0x42          //设置全局指针 gp 的高 16 位为 0x42
401514:   279cb190   addiu    gp,gp,-20080  //设置指针gp的低16位为-20080
401518:   afbc0010   sw  gp,16(sp)          //将全局指针 gp 存储在(sp + 16)处
40151c:   afc40060   sw  a0,96(s8)          //将参数 a0 存储在(sp + 96) 处
401520:   3c020041   lui   v0,0x41          //设置寄存器 v0 的高 16 位为 0x41
401524:   24423130   addiu    v0,v0,12592   //设置v0 的低 16 位为 12592
401528:   afc20020   sw v0,32(s8)           //将v0 存储在(sp + 32)处
40152c:   27c20024   addiu    v0,s8,36      //设置v0 为sp + 36
401530:   8fc40060   lw   a0,96(s8)         //从当(sp + 96)处加载参数 a0
401534:   00402821   move    a1,v0          //将 v0 的值存储在 a1 中
401538:   0c1006ea   jal   401ba8 <read_six_numbers>//调用函数读入六个数字

以上指令分配了96字节栈空间,读取了六个数,若不是六个数字炸弹将会爆炸

以下是一个循环从401550到40163c,循环次数为6,让我们看看循环内的指令

401540:   8fdc0010   lw   gp,16(s8)        //从(s8 + 16)处加载全局指针 gp
401544:   afc0001c   sw  zero,28(s8)       //将零值存储在(s8 + 28)处(循环次数)
401548:   1000003c   b    40163c <phase_6+0x13c>//跳转到地址 40163c
40154c:   00000000   nop
401550:   8fc2001c   lw   v0,28(s8)        //从(s8 + 28)处加载值到v0(循环次数)

…………
…………

40163c:   8fc2001c   lw   v0,28(s8)        //从(s8 + 28)处加载值到v0(循环次数)
401640:   00000000   nop
401644:   28420006   slti  v0,v0,6         //将v0与6比较,相等就令v0为0
401648:   1440ffc1   bnez     v0,401550 <phase_6+0x50> //如果v0不等于零,则跳转到401550

401550:   8fc2001c   lw   v0,28(s8)        //从(s8 + 28)处加载值到v0(循环次数)
401554:   00000000   nop
401558:   00021080   sll   v0,v0,0x2       //将v0左移2位
40155c:   27c30018   addiu    v1,s8,24     //设置v1 为(s8 + 24)
401560:   00621021   addu    v0,v1,v0      //计算 v0 = v1 + v0
401564:   8c42000c   lw   v0,12(v0)        //从内存(v0 + 12)处加载值到v0
401568:   00000000   nop
40156c:   28420007   slti  v0,v0,7         //将v0与7比较,v0大于7就令v0为0
401570:   1040000a   beqz     v0,40159c <phase_6+0x9c> //如果v0为0,则跳转到地址 40159c,炸弹爆炸

401574:   00000000   nop
401578:   8fc2001c   lw   v0,28(s8)        //从(s8  + 28)处加载值到v0(循环次数)
40157c:   00000000   nop
401580:   00021080   sll   v0,v0,0x2       //将v0左移 2 位
401584:   27c30018   addiu    v1,s8,24     //设置v1 为(s8 + 24)
401588:   00621021   addu    v0,v1,v0      //计算 v0 = v1 + v0
40158c:   8c42000c   lw   v0,12(v0)        //从内存(v0 + 12)处加载值到v0
401590:   00000000   nop

401594:   1c400004   bgtz     v0,4015a8 <phase_6+0xa8>  //如果 v0 大于零,则跳转到地址 4015a8,跳过炸弹爆炸

401598:   00000000   nop
40159c:   0c10087c   jal   4021f0 <explode_bomb>  //如果v0不大于零,则炸弹爆炸

以上两段代码对内存中的一个值进行了比较,这个值必须小于7大于0,所以只能在1到6中,猜测是输入的第n个数,设置断点为401564,进行查看

发现内存v0处储存的即为输入的数字,第n个循环对比的数字为输入的第n个数字。

4015a4:   8fdc0010   lw   gp,16(s8)        //从(s8 + 16)处加载全局指针 gp
4015a8:   8fc2001c   lw   v0,28(s8)        //从(s8 + 28)处加载到v0(循环次数)
4015ac:   00000000   nop
4015b0:   24420001   addiu    v0,v0,1      //将v0的值加 1
4015b4:   afc20018   sw  v0,24(s8)         //将v0的值存储在(s8 + 24)处(循环次数+1)
4015b8:   10000017   b    401618 <phase_6+0x118>//无条件跳转到地址 401618
4015bc:   00000000   nop
4015c0:   8fc2001c   lw   v0,28(s8)        //从(s8 + 28)处加载到v0(循环次数)
4015c4:   00000000   nop
4015c8:   00021080   sll   v0,v0,0x2       //将v0 左移 2 位
4015cc:   27c30018   addiu    v1,s8,24     //设置v1 为(s8 + 24)(循环次数+1)
4015d0:   00621021   addu    v0,v1,v0      //计算 v0 = v1 + v0
4015d4:   8c43000c   lw   v1,12(v0)        //从v0+12处加载到v1(输入的循环次数+1个数)
4015d8:   8fc20018   lw   v0,24(s8)        //从(s8 + 24)处加载到v0(循环次数+1)
4015dc:   00000000   nop
4015e0:   00021080   sll   v0,v0,0x2       //将寄存器 v0 左移 2 位
4015e4:   27c40018   addiu    a0,s8,24     //设置a0为(s8 + 24)(循环次数+1)
4015e8:   00821021   addu    v0,a0,v0      //计算 v0 = a0 + v0
4015ec:   8c42000c   lw   v0,12(v0)        //从v0+12处加载值到寄存器 v0
4015f0:   00000000   nop
4015f4:   14620004   bne v1,v0,401608 <phase_6+0x108> // 如果v1不等于v0,则跳转到401608
4015f8:   00000000   nop
4015fc:   0c10087c   jal   4021f0 <explode_bomb>//如果 v1 等于 v0,则炸弹爆炸
401600:   00000000   nop                     
401604:   8fdc0010   lw   gp,16(s8)        //从(s8 + 16)处加载全局指针 gp
401608:   8fc20018   lw   v0,24(s8)        //从(s8 + 24)处加载值到寄存器 v0
40160c:   00000000   nop
401610:   24420001   addiu    v0,v0,1      //将v0 的值加 1
401614:   afc20018   sw  v0,24(s8)         //将v0 的值存储在(s8 + 24)处
401618:   8fc20018   lw   v0,24(s8)        //从(s8 + 24)处加载值到v0(循环次数+1)
40161c:   00000000   nop
401620:   28420006   slti  v0,v0,6         //将v0与6比较,相等就令v0为0
401624:   1440ffe6   bnez     v0,4015c0 <phase_6+0xc0> //如果v0不等于零,则跳转到地址4015c0

又是一个4015c0到401618的循环,这个循环意在比较两个数的值是否相等,循环的次数由外部循环第几次决定,即从第n个数,n+1,n+2到6。循环内部比较的两个数,从上文可以得出,是第n个数与当前内部循环的数,也就是将第n个数与后面的数都比较一遍,若有相等的情况则炸弹爆炸,说明输入的6个数不能重复。到这里六个循环就结束了,让我们看看循环后的指令。

40164c:   00000000   nop
401650:   afc0001c   sw  zero,28(s8)      //将零存储在(s8 + 28)处(循环次数)
401654:   10000028   b    4016f8 <phase_6+0x1f8> //无条件跳转到地址 4016f8

…………
…………

4016f8:   8fc2001c   lw   v0,28(s8)       //从(s8 + 28)处加载值到v0(循环次数)
4016fc:   00000000   nop
401700:   28420006   slti  v0,v0,6        //将v0 与6 比较,小于6为1,否则为0
401704:   1440ffd5   bnez     v0,40165c <phase_6+0x15c>  //如果 v0 不等于零,则跳转到地址 40165c

这里又是一个从401654 到4016f8,次数为6的循环,让我们看看循环里的指令

40165c:   3c020041   lui   v0,0x41          //设置v0 的高 16 位为 0x41
401660:   24423130   addiu    v0,v0,12592   //设置v0 的低 16 位为 12592
401664:   afc20020   sw  v0,32(s8)          //将v0 存储在(s8 + 32)处(0x413130)
401668:   24020001   li     v0,1            //将v0 的值设置为 1
40166c:   afc20018   sw  v0,24(s8)          //将v0 存储在(s8 + 24)处(内层循环)

这里的(s8 + 24)处储存着的是内层的循环变量,初始化为1

401670:   1000000a   b    40169c <phase_6+0x19c> //无条件跳转到40169c
401674:   00000000   nop
401678:   8fc20020   lw   v0,32(s8)      //从(s8 + 32)处加载值到v0
40167c:   00000000   nop
401680:   8c420008   lw   v0,8(v0)       //从内存地址 v0 + 8 处加载值到v0
401684:   00000000   nop
401688:   afc20020   sw  v0,32(s8)       //将v0的值存储在(s8 + 32)处
40168c:   8fc20018   lw   v0,24(s8)      //从(s8 + 24)加载到 v0(内层循环)
401690:   00000000   nop
401694:   24420001   addiu    v0,v0,1    //将 v0 的值加 1
401698:   afc20018   sw  v0,24(s8)       //将 v0 存储在(s8 + 24)处(内层循环)
40169c:   8fc2001c   lw   v0,28(s8)      //从(s8 + 28)加载到v0(循环次数)

这里的v0原先是一个指针,为0x413130+12592,v0还会增加内层循环次数,每次循环v0取的都是(v0 + 8),而v1为(v0 + 12)

4016a0:   00000000   nop
4016a4:   00021080   sll   v0,v0,0x2      //将寄存器 v0 左移 2 位
4016a8:   27c30018   addiu    v1,s8,24    //设置 v1 为(s8 + 24)(内层循环)
4016ac:   00621021   addu    v0,v1,v0     //计算 v0 = v1 + v0
4016b0:   8c43000c   lw   v1,12(v0)       //从内存地址 v0 + 12 处加载值到 v1

这里的(v0 + 12)是输入的第(s8 + 24)个数

4016b4:   8fc20018   lw   v0,24(s8)      //从(s8 + 24)处加载到 v0(内层循环)
4016b8:   00000000   nop
4016bc:   0043102a   slt   v0,v0,v1      //将v0 与v1 比较,设置条件码
4016c0:   1440ffed   bnez     v0,401678 <phase_6+0x178> //如果v0不等于0,则跳转到 401678

发现内层循环的循环次数为(第外层循环个数个输入-1),即v0跳转到(v0 + 8)所指的位置n次,n为内层循环的次数,并且把值存储到(s8 + 32)处

4016c8:   8fc2001c   lw   v0,28(s8)      //从(s8 + 28)处加载到 v0(循环次数)
4016cc:   00000000   nop
4016d0:   00021080   sll   v0,v0,0x2     //将寄存器 v0 左移 2 位
4016d4:   27c30018   addiu  v1,s8,24     //设置v1 为(s8 + 24)(内层循环)
4016d8:   00621021   addu    v0,v1,v0    //计算 v0 = v1 + v0
4016dc:   8fc30020   lw   v1,32(s8)      //从(s8 + 32)处加载值到v1
4016e0:   00000000   nop
4016e4:   ac430024   sw  v1,36(v0)       //将v1 的值存储在内存 v0 + 24 处
4016e8:   8fc2001c   lw   v0,28(s8)      //从(s8 + 28)处加载到 v0(循环次数)
4016ec:   00000000   nop
4016f0:   24420001   addiu    v0,v0,1    //将v0 的值加 1
4016f4:   afc2001c   sw  v0,28(s8)       //将 v0 存储在(s8 + 28)(循环次数)

这里把之前v0循环的值从(s8 + 32)处取出,拷贝了一份又放到(v0 + 24)处

然后按照惯例增加循环次数,直到六次循环完成。让我们继续往下看

40170c:   8fc2003c   lw   v0,60(s8)     //从(s8 + 60)处加载值到v0
401710:   00000000   nop
401714:   afc20020   sw  v0,32(s8)      //将v0 存储在(s8 + 32) 处
401718:   24020001   li     v0,1        //将v0 设置为 1
40171c:   afc2001c   sw  v0,28(s8)      //将v0 存储在(s8 + 28)处(循环次数)
401720:   10000016   b    40177c <phase_6+0x27c> //跳转到地址 40177c

…………
…………

40176c:   8fc2001c   lw   v0,28(s8)     //从(s8 + 28)加载到v0(循环次数)
401770:   00000000   nop              
401774:   24420001   addiu    v0,v0,1   //将v0 中的值加1
401778:   afc2001c   sw  v0,28(s8)      //将v0 中的值存储到(s8 + 28)处
40177c:   8fc2001c   lw   v0,28(s8)     //从(s8 + 28)处加载值到v0
401780:   00000000   nop
401784:   28420006   slti  v0,v0,6      //如果v0小于6,将v0设为1,否则0
401788:   1440ffe7   bnez   v0,401728 <phase_6+0x228> //如果v0不为零,跳转到地址401728

这里又是一个从401720 到40177c,次数为6的循环,循环次数储存在(s8 + 28)中让我们看看循环里的指令

401728:   8fc2001c   lw   v0,28(s8)       //从(s8 + 28)处加载到v0(循环次数)
40172c:   00000000   nop
401730:   00021080   sll   v0,v0,0x2      //将寄存器 v0 左移 2 位
401734:   27c30018   addiu    v1,s8,24    //设置 v1 为s8 + 24
401738:   00621021   addu    v0,v1,v0     //计算 v0 = v1 + v0
40173c:   8c430024   lw   v1,36(v0)       //从内存地址v0 + 36 处加载值到v1
401740:   8fc20020   lw   v0,32(s8)       //从(s8 + 32)处加载值到 v0

此处v0为上一个循环中确定的值,设置401740为断点,查看v0与v1的值,发现v1为下一个v0的地址

401744:   00000000   nop
401748:   ac430008   sw  v1,8(v0)         //将v1 的值存储在内存 v0 + 8 处
40174c:   8fc2001c   lw   v0,28(s8)       //从(s8 + 28)处加载到 v0(循环次数)
401750:   00000000   nop
401754:   00021080   sll   v0,v0,0x2      //将寄存器 v0 左移 2 位
401758:   27c30018   addiu    v1,s8,24    //设置 v1 为s8 + 24
40175c:   00621021   addu    v0,v1,v0     //计算 v0 = v1 + v0
401760:   8c420024   lw   v0,36(v0)       //从内存 v0 + 36 处加载到寄存器 v0
401764:   00000000   nop
401768:   afc20020   sw  v0,32(s8)        //将寄存器 v0 的值存储在(s8 + 32) 处

通过上个循环确定的首个节点,以及两节点地址v 1 ,v0,然后将v1的地址放到(v0 + 8),即v0的下一个节点的地址,然后更新v0的节点,以便下一次链接,五次循环以后构成一个新链表。让我们继续往下看

40178c:   00000000   nop                
401790:   8fc20020   lw   v0,32(s8)       //从(s8 + 32)处加载值到v0
401794:   00000000   nop                
401798:   ac400008   sw  zero,8(v0)       //将零存储到内存(v0 + 8)
40179c:   8fc2003c   lw   v0,60(s8)       //从(s8 + 60)处加载值到v0
4017a0:   00000000   nop                 
4017a4:   afc20020   sw  v0,32(s8)        //将v0中的值存储到(s8 + 32)中
4017a8:   afc0001c   sw  zero,28(s8)      //将零存储到(s8 + 28)中
4017ac:   10000032   b    401878 <phase_6+0x378> ;//跳转到地址401878

…………
…………
 
401878:   8fc2001c   lw   v0,28(s8)       //从(s8 + 28)处加载值到v0
40187c:   00000000   nop                
401880:   28420005   slti  v0,v0,5        //如果v0小于5,将v0设为1,否则为0
401884:   1440ffcb   bnez     v0,4017b4 <phase_6+0x2b4> ; 如果v0不为零,跳转到地址4017b4

这里又是一个从4017b4到401878,次数为5的循环,循环次数储存在(s8 + 28)中让我们看看循环里的指令

4017b4:   8f82806c   lw   v0,-32660(gp)  //从全局指针(gp - 32660)加载到v0
4017b8:   00000000   nop                
4017bc:   8c42002c   lw   v0,44(v0)      //从(v0 + 44)处加载值到v0

通过经验得知,(v0 + 44)中的内容为学号的最后一位

4017c0:   00000000   nop                
4017c4:   30420001   andi     v0,v0,0x1    //将v0中的值与1按位与运算
4017c8:   304200ff   andi     v0,v0,0xff   //将v0中的值与0xff按位与运算
4017cc:   10400012   beqz     v0,401818 <phase_6+0x318> ; 如果v0为零,跳转到地址401818

这里v0与1按位与以后,若v0是奇数,则继续,若v0是偶数,则发生跳转。

4017d0:   00000000   nop                
4017d4:   8fc20020   lw   v0,32(s8)  //从(s8 + 32)处加载值到v0
4017d8:   00000000   nop                
4017dc:   8c430000   lw   v1,0(v0)   //从内存 v0 处加载值到v1

从(s8 + 32)处获取上一节点地址,加载上一节点值到v1

4017e0:   8fc20020   lw   v0,32(s8)     //从(s8 + 32)处加载值到v0
4017e4:   00000000   nop                
4017e8:   8c420008   lw   v0,8(v0)      //从(v0 + 8)处加载值到v0
4017ec:   00000000   nop                
4017f0:   8c420000   lw   v0,0(v0)      //从内存 v0 处加载值到v0

从(s8 + 32)处获取当前节点地址,加载当前节点值到v0

4017f4:   00000000   nop                
4017f8:   0062102a   slt   v0,v1,v0    //如果v1<v0,将v0寄存器设置为1,否则设置为0
4017fc:   10400015   beqz   v0,401854 <phase_6+0x354> //如果v0为零,跳转到地址401854
401800:   00000000   nop                
401804:   0c10087c   jal 4021f0 <explode_bomb> //调用函数,炸弹爆炸
401808:   00000000   nop                
40180c:   8fdc0010   lw   gp,16(s8)    //从(s8 + 16)加载一个值到gp寄存器
401810:   10000010   b    401854 <phase_6+0x354> //无条件跳转到地址401854
401814:   00000000   nop

发现4017d0至401814的指令与401818至401850的指令基本相同,区别在于slt判断指令,学号最后一位偶数时判断为

40183c:   0043102a   slt   v0,v0,v1  //如果v0<v1,将v0设置为1,否则为0

整个部分意在判断上一节点与当前节点存储值的大小关系,学号最后一位为偶数时要确保链表保存值为降序,学号最后一位为奇数时要确保链表保存值为升序。

401854:   8fc20020   lw   v0,32(s8)     //从(s8 + 32)处加载一个值到v0
401858:   00000000   nop                
40185c:   8c420008   lw   v0,8(v0)      //从v0+8处加载一个值到v0
401860:   00000000   nop                
401864:   afc20020   sw  v0,32(s8)      //将v0的值存储到(s8 + 28)处
401868:   8fc2001c   lw   v0,28(s8)     //从(s8 + 28)处加载一个值到v0
40186c:   00000000   nop                
401870:   24420001   addiu    v0,v0,1   //将v0的值加1
401874:   afc2001c   sw  v0,28(s8)      //将v0的值存储到(s8 + 28)处

正常的把当前节点的地址保存在(s8 + 32)中,然后增加(s8 + 28)中保存的循环次数。

综上所述,实验六通过输入一个1到6的排列,确定一个链表中节点的顺序,如果学号最后一位为奇数,需要保证从头遍历链表时,节点里存储的值为升序,若学号最后一位为偶数,则保证为降序。节点一共有六个,通过指针来确定各个节点的值的大小。

通过断点调试,可知每个节点储存的值:

node1  

0xfd
node2 0x2d5
node3 0x12d
node4  0x3e5
node5 0xd4
node60x1b0

比较大小,可知

学号最后一位为奇数,输入:4 2 6 3 1 5

学号最后一位为偶数,输入:5 1 3 6 2 4

<secret_phase>:

这里发现,如果按照正常输入,前面六个炸弹都输入完成后,程序就结束了,并没有进入隐藏炸弹,所以需要输入其他东西触发隐藏炸弹

观察bomb.s文件,发现上图中的语句,可以得知当输入格式为 %d %s 时进入隐藏炸弹。回想以上炸弹,输入为整数的有不少,但是只有炸弹4里没有检测输入整数个数的指令,所以要进入炸弹4,应该在炸弹4输入数字后再输入一个字符串,让我们看看隐藏炸弹。

4019ac:    0c1007fb   jal  401fec <read_line>  // 调用read_line函数
4019b0:    00000000   nop                 
4019b4:    8fdc0010   lw   gp,16(s8)       // 从栈上加载gp的值
4019b8:    afc2001c   sw  v0,28(s8)        // 将返回值保存在(s8 + 28)
4019bc:    8fc2001c   lw   v0,28(s8)       // 加载read_line的返回值到v0

…………
…………

4019e8:    afc20018   sw  v0,24(s8)        // 将返回值保存在(s8 + 24)
4019ec:    8fc20018   lw   v0,24(s8)       // 从栈上加载返回值
4019f0:    00000000   nop                  // 无操作
4019f4:    2442ffff   addiu    v0,v0,-1    // 将v0的值减1
4019f8:    2c4203e9   sltiu v0,v0,1001//若v0小于1001,将v0设为1否则为0
4019fc:    14400004   bnez     v0,401a10 <secret_phase+0x80>  // 如果v0不为零,则跳转到401a10
401a00:    00000000   nop              
401a04:    0c10087c   jal   4021f0 <explode_bomb>  //调用函数,炸弹爆炸
401a08:    00000000   nop       

以上指令,确保进入隐藏炸弹后输入的数小于1002

401a1c:     0c100629   jal   4018a4 <fun7>    // 调用fun7函数
401a20:     00000000   nop                 
401a24:     8fdc0010   lw   gp,16(s8)         // 从栈上加载gp的值
401a28:     00401821   move    v1,v0          // 将fun7的返回值传递给v1
401a2c:     24020007   li     v0,7            // 将常数7传递给v0寄存器
401a30:     10620004   beq v1,v0,401a44 <secret_phase+0xb4>  // 如果v1等于v0,则跳转到401a44
401a34:     00000000   nop             
401a38:     0c10087c   jal   4021f0 <explode_bomb> //调用函数,炸弹爆炸
401a3c:     00000000   nop       

这里将fun7函数的返回值与7进行比较,若返回值为7则炸弹不爆炸,让我们看看fun7里的内容

4018b4:     afc40020   sw  a0,32(s8)        //将a0参数保存在栈上
4018b8:     afc50024   sw  a1,36(s8)        //将a1参数保存在栈上
4018bc:     8fc20020   lw   v0,32(s8)       //从栈上加载a0参数
4018c0:     00000000   nop                
4018c4:     14400004   bnez   v0,4018d8 <fun7+0x34>  //如果a0不为零,则跳转到4018d8
4018c8:     00000000   nop             
4018cc:     2402ffff   li     v0,-1         //将v0设置为-1
4018d0:     10000029   b    401978 <fun7+0xd4>  //跳转到函数结尾
4018d4:     00000000   nop             
4018d8:     8fc20020   lw   v0,32(s8)       //从(s8 + 32)上加载参数到v0
4018dc:     00000000   nop                  
4018e0:     8c430000   lw   v1,0(v0)        //从a0指向的地址加载一个值到v1
4018e4:     8fc20024   lw   v0,36(s8)       //从栈上加载a1参数到v0
4018e8:     00000000   nop                 
4018ec:     0043102a   slt   v0,v0,v1       //将v0设置为a1与 v1的比较结果
4018f0:     1040000c   beqz     v0,401924 <fun7+0x80>  //如果 a1 > v1,则跳转到401924

根据以上内容可确定,fun7内传入的参数包括a0指针,保存在(s8 + 32)上。还有通过转换变为整形的字符串,保存在(s8 + 36)上。

首先输入的整数会与根节点进行比较,若输入的不小于当前节点的值,则会发生跳转,以下是跳转后的指令:

401924:     8fc20020   lw   v0,32(s8)       //从(s8 + 32)上加载参数到v0
401928:     00000000   nop           
40192c:     8c430000   lw   v1,0(v0)        //从a0指向的地址加载一个值到v1
401930:     8fc20024   lw   v0,36(s8)       //加载输入的整形到v0
401934:     00000000   nop                 
401938:     0062102a   slt   v0,v1,v0       //将v0设置为 v1 与 v0 的比较结果
40193c:     1040000d   beqz     v0,401974 <fun7+0xd0>  如果 v1 = v0 ,则跳转到401974,函数结束
401944:     8fc20020   lw   v0,32(s8)       //从(s8 + 32)上加载参数到v0
401948:     00000000   nop                
40194c:     8c420008   lw   v0,8(v0)        //从(a0 + 8)指向的地址加载值到v0
401950:     00000000   nop               
401954:     00402021   move  a0,v0          //将v0的值传递给a0,完成a0的更新
401958:     8fc50024   lw   a1,36(s8)       //加载输入的整形到a1
40195c:     0c100629   jal   4018a4 <fun7>  //递归调用fun7函数

401964:     00021040   sll   v0,v0,0x1      //将v0左移1位
401968:     24420001   addiu    v0,v0,1     //将v0加1,v0 = v0 * 2 + 1
40196c:     10000002   b    401978 <fun7+0xd4>  //跳转到函数结尾

进行当前节点,与输入的数的比较,若比较结果为输入的数不大于当前节点的值,则发生跳转。不过之前已经确定输入的数不大于当前节点的值,说明当前值与输入的相等时跳转。又发现跳转的位置相较于其他位置多增加了一条指令

401974: 00001021   move    v0,zero      //将v0设置为零

说明可能有妙用,我们接着看之前的代码,进行了根节点,即a0的自增,指向了新的节点(a0 + 8),然后以新的a0作为参数递归调用fun7,调用结束后,对v0寄存器内存储的值进行运算,v0 = v0 * 2 + 1

接下来我们回到第一次判断后小于的情况:

4018f8:     8fc20020   lw   v0,32(s8)       //从(s8 + 32)上加载参数到v0
4018fc:     00000000   nop                  
401900:     8c420004   lw   v0,4(v0)        //从(v0 + 4)指向的地址加载值到v0
401904:     00000000   nop                  
401908:     00402021   move    a0,v0        //将v0的值传递给a0寄存器
40190c:     8fc50024   lw   a1,36(s8)       //加载输入的整形到a1
401910:     0c100629   jal   4018a4 <fun7>  //递归调用fun7函数
401918:     00021040   sll   v0,v0,0x1      //将v0左移1位,v0 = v0 * 2
40191c:     10000016   b    401978 <fun7+0xd4> //跳转到函数结尾

进行了根节点,即a0的自增,指向了新的节点(a0 + 4),然后以新的a0作为参数递归调用fun7,调用结束后,对v0寄存器内存储的值进行运算,v0=v0 * 2

根据以上指令不难发现,fun7为一个二叉搜索树的搜索操作,进行输入值,以及各个节点的值的比较。若输入值小于当前节点,则把当前节点a0的下一个节点(a0 + 4)作为当前节点,递归调用fun7,若大于,则以(a0 + 8)作为当前节点,说明(a0 + 4)为左孩子,(a0 + 8)为右孩子。调用后会对v0进行操作,最后当前节点与输入值相等,或该值不存在,函数返回v0。

结合secrete_phase里的内容,函数返回值应该为7,否则炸弹将会爆炸,说明v0应该为7。经过倒推可知:

7 = 2 * 3 + 1

3 = 2 * 1 + 1

1 = 2 * 0 + 1

每次进行的操作为v0 = v0 * 2 +1,并且要求v0的初始值为0,想到之前判断相等条件后进行跳转到401974,说明不仅要进行三次大于的判断,还得让输入的值与二叉搜索树的最右叶节点相等。

回到secret_phase,

401a64:     0c100899   jal   402264 <phase_defused>//调用phase_defused

接下来看看phase_defused里的内容

402274:     3c1c0042   lui   gp,0x42          //将0x42左移16位并存入gp寄存器
402278:     279cb190   addiu    gp,gp,-20080  //gp = gp - 20080
40227c:     afbc0010   sw  gp,16(sp)          //将gp的值保存在栈上
402280:     3c020041   lui   v0,0x41          //将0x41左移16位并存入v0寄存器
402284:     8c433240   lw   v1,12864(v0)      //从地址0x41 + 12864加载一个值到v1寄存器
402288:     24020006   li     v0,6            //将v0寄存器设置为6
40228c:     14620039   bne v1,v0,402374 <phase_defused+0x110> //如果v1不等于6,则跳转到402374,不进入secrete_phase

这里将地址0x41 + 12864与6进行比较,经过断点调试可知这里储存的是当前的第几个炸弹,所以隐藏炸弹会在六个炸弹都排查完毕后才进入

4022e4:     27c20018   addiu(v0,s8,24)        //将s8 + 24的值存入v0寄存器
4022e8:     00402021   move(a0,v0)            //将v0的值传递给a0寄存器
4022ec:     3c020040   lui(v0,0x40)           //将0x40左移16位并存入v0
4022f0:     244528b0   addiu(a1,v0,10416)     //将v0 + 10416的值存入a1
4022f4:     0c10073e   jal   401cf8 <strings_not_equal>//调用strings_not_equal函数
4022f8:     00000000   nop                  
4022fc:     8fdc0010   lw   gp,16(s8)         //从栈上加载gp的值
402300:     14400014   bnez(v0,402354 <phase_defused+0xf0>) //如果v0不为零,则跳转到402354,不进入secrete_phase

这里判断了a1 和 a0两个寄存器内的内容是否相同,若不相同,则不进入secrete_phase。通过设置断点,可知会将输入的字符串与austinpowers进行比较

说明要进入隐藏炸弹需要在phase_4 后输入austinpowers

然后查看fun7里节点的值

设置断点查看每个根节点的地址,输入的是1000,是范围内较大的值,方便找到最右叶节点

可以发现,v0存储的即为输入的数字1000的十六进制,v1即为最右叶节点的值的十六进制,1001。

说明进入secret_phase后输入1001。

同样方法可查看所有节点的值

综上所述,拆解secret_phase需要在phase_4后输入austinpowers,然后拆除六个炸弹后进入隐藏炸弹,输入1001即可拆除成功。

完结撒花!!!

  • 21
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值