赞
踩
不难发现,在
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!”时,炸弹不会触发
注意到,
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时炸弹不爆炸
炸弹三代码量很大,但总的来看是一个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 发现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。
两条判断是否爆炸的语句,分别为:
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,答案不唯一
- 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 |
node6 | 0x1b0 |
比较大小,可知
学号最后一位为奇数,输入:4 2 6 3 1 5
学号最后一位为偶数,输入:5 1 3 6 2 4
这里发现,如果按照正常输入,前面六个炸弹都输入完成后,程序就结束了,并没有进入隐藏炸弹,所以需要输入其他东西触发隐藏炸弹
观察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即可拆除成功。
完结撒花!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。