(还是存一下吧,毕竟是自己一个人熬了那么久一个一个炸弹拆过来的)
每道题的关键:
Phase_1:输入的字符串与内置字符串相同
Phase_2:共6位,第一位为1 ,剩下的五位为第i+1位为输入的第i位*学号倒数第i位
Phase_3:switch-case函数,学号最后一位对应不同的case
Phase_4:根据学号最后一位的奇偶对应斐波那契数列的不同项
Phase_5:输入的字符串通过某种映射关系对应内置字符串giants
Phase_6:链表重排,偶数升序,奇数降序
Secret_phase:二叉搜索树
具体细节如下:
一些功能和操作:
(1)Type <return> to continue, or q <return> to quit---q可以设置一下不分页显示set pagination off
(2)设置、查看、删除断点:
1. b * 0x233666
2. info b
3. delete 1
(3)查看寄存器、变量中的值
1. x $ra # 与p *$ra 类似
2. p $ra #打印参数
(4)单步调试:
- ni不进入子函数
- si 进入子函数
Phase_1:
首先查看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 #为当前函数开辟栈空间,把sp的值送到s8
0x00400d7c <+16>: sw a0,32(s8) #将a0的内容送入内存[s8+32]的位置
0x00400d80 <+20>: lw a0,32(s8) #将内存[s8+32]的值赋值给a0
0x00400d84 <+24>: lui v0,0x40 # Load Upper Immediate, 加载立即数到寄存器的高16位v0=0x0040 0000
0x00400d88 <+28>: addiu a1,v0,10092 #a1=v0+10092
0x00400d8c <+32>: jal 0x401cf8 <strings_not_equal> #跳转到函数<strings_not_equal>
0x00400d90 <+36>: nop
0x00400d94 <+40>: beqz v0,0x400da4 <phase_1+56> #如果v0=0则跳转到炸弹2
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
由于不知道参数是如何传递的,所以先跳转到main函数来查看参数的传递情况:
可知先由<read_line>函数读入字符串,由0x00400d8c <+32>: jal 0x401cf8 <strings_not_equal> #跳转到函数<strings_not_equal> 由函数名可知在这一步是在比较两个字符串是否相等,之前参与这个过程的有v0、a0和a1三个寄存器,因此主要考虑这三个寄存器中的内容。
在0x00d00d8c设置断点,随便输入一个字符串test,运行程序,提示命中断点,使用ni命令进行单步调试,此时查看三个寄存器中的状态:
由于三个寄存器都只显示了地址,因此通过x命令查看寄存器里的内容:由此可知如果要程序正常运行,则需要a0和a1寄存器中的内容相等,如果内容相等则返回值为0,将返回值传入v0中,如果v0的值为0,则炸弹1顺利通过,到达炸弹2.
通过查看<read_line>函数传入字符串,传入的字符串被作为返回值存入v0里,而后把v0寄存器中的值放入内存[s8+32]的位置,下一步将[s8+32]位置的值赋值给a0,将a0传入<phase_1>函数中参与运算,因此最终我们输入的字符串被存入a0与a1中的内容进行比较,由上图可知,应该输入的字符串为:Let’s begin now!
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个数字
0x00400dec <+48>: nop
0x00400df0 <+52>: lw gp,16(s8)
0x00400df4 <+56>: lw v1,28(s8)
0x00400df8 <+60>: li v0,1 #v0中存入1
0x00400dfc <+64>: beq v1,v0,0x400e10 <phase_2+84> #将v0与v1比较,如果相等则跳转
0x00400e00 <+68>: nop
0x00400e04 <+72>: jal 0x4021f0 <explode_bomb>
0x00400e08 <+76>: nop
0x00400e0c <+80>: lw gp,16(s8)
0x00400e10 <+84>: li v0,1
--Type <RET> for more, q to quit, c to continue without paging--c
0x00400e14 <+88>: sw v0,24(s8)
0x00400e18 <+92>: b 0x400ea8 <phase_2+236>
0x00400e1c <+96>: nop
0x00400e20 <+100>: lw v0,24(s8)
0x00400e24 <+104>: nop
0x00400e28 <+108>: addiu v0,v0,-1
0x00400e2c <+112>: sll v0,v0,0x2
0x00400e30 <+116>: addiu v1,s8,24
0x00400e34 <+120>: addu v0,v1,v0
0x00400e38 <+124>: lw a0,4(v0)
0x00400e3c <+128>: li v1,12
0x00400e40 <+132>: lw v0,24(s8)
0x00400e44 <+136>: nop
0x00400e48 <+140>: subu v0,v1,v0
0x00400e4c <+144>: lw v1,-32660(gp)
0x00400e50 <+148>: sll v0,v0,0x2
0x00400e54 <+152>: addu v0,v1,v0
0x00400e58 <+156>: lw v0,0(v0)
0x00400e5c <+160>: nop
0x00400e60 <+164>: mult a0,v0
0x00400e64 <+168>: mflo a0
0x00400e68 <+172>: lw v0,24(s8)
0x00400e6c <+176>: nop
0x00400e70 <+180>: sll v0,v0,0x2
0x00400e74 <+184>: addiu v1,s8,24
0x00400e78 <+188>: addu v0,v1,v0
0x00400e7c <+192>: lw v0,4(v0)
0x00400e80 <+196>: nop
0x00400e84 <+200>: beq a0,v0,0x400e98 <phase_2+220>
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)
0x00400eac <+240>: nop
0x00400eb0 <+244>: slti v0,v0,6
0x00400eb4 <+248>: bnez v0,0x400e20 <phase_2+100>
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
- 由0x00400de8 <+44>: jal 0x401ba8 <read_six_numbers>可知需要输入的是6个数字,先随便输入6个数字:1 2 3 4 5 6
- 0x00400df8 <+60>: li v0,1 #v0中存入1
0x00400dfc <+64>: beq v1,v0,0x400e10 <phase_2+84> #将v0与v1比较,如果相等则跳转到 <phase_2+84>,查看v0和v1的值:
由此可知传入的六个数字中第一位为1
2为我输入的第二个数,储存在了v0中,正确答案应该为a0中的8
重新以1 8为前两个数字输入然后一直单步调试也可以得到六位结果
第三位数为48
重复此过程,最终得到结果1 8 48 96 0 0
- 如果看汇编代码:
进入循环
0x00400e10 <+84>: li v0,1 #v0=1,v0为循环变量i=1
0x00400e14 <+88>: sw v0,24(s8) #把v0的值存入【s8+24】,循环变量入栈
0x00400e18 <+92>: b 0x400ea8 <phase_2+236> #跳转到<phase_2+236>
0x00400e1c <+96>: nop
0x00400e20 <+100>: lw v0,24(s8) #取出栈中的值即v0=内存【s8+24】的值
0x00400e24 <+104>: nop
(以第二次循环即i=2为例)
0x00400e28 <+108>: addiu v0,v0,-1 #v0=v0-1(如若此时v0=i=2,则v0=1)
0x00400e2c <+112>: sll v0,v0,0x2 #逻辑左移,把v0左移两位,两位是因为正好为4个字节,一个int型变量的长度即为4个字节(0001<<2=0100=4)
0x00400e30 <+116>: addiu v1,s8,24 #v1=【s8+24】,为储存循环变量i的地址(=2)
0x00400e34 <+120>: addu v0,v1,v0 #v0=v1+v0(=【v1+4】)
0x00400e38 <+124>: lw a0,4(v0) #a0=【v0+4】,将输入的第i个数传入a0
0x00400e3c <+128>: li v1,12 #v1=12
0x00400e40 <+132>: lw v0,24(s8) #v0=【s8+24】,读入循环变量i(=2)
0x00400e44 <+136>: nop
0x00400e48 <+140>: subu v0,v1,v0 #v0=v1-v0=12-i(12-2=10)
0x00400e4c <+144>: lw v1,-32660(gp) #读取输入的学号
(转化为十进制数)
0x00400e50 <+148>: sll v0,v0,0x2 #v0逻辑左移两位(0010<<2=1000=8)
0x00400e54 <+152>: addu v0,v1,v0 #v0=【v1+v0】,即输入学号的倒数第i位
0x00400e58 <+156>: lw v0,0(v0) #v0=v0+0,将输入学号的倒数第i位存入v0
0x00400e5c <+160>: nop
0x00400e60 <+164>: mult a0,v0 #把a0*v0的结果存入特殊寄存器hl或l0寄存器中(<+124>a0=输入的第i个数,<+156>输入学号的倒数第i位)
0x00400e64 <+168>: mflo a0 #将特殊寄存器l0的值赋给a0,即a0=a0*v0
0x00400e68 <+172>: lw v0,24(s8) #v0=【s8+24】,取出循环变量i
0x00400e6c <+176>: nop
0x00400e70 <+180>: sll v0,v0,0x2 #v0逻辑左移两位,将i变成int型长度
0x00400e74 <+184>: addiu v1,s8,24 #v1=s8+24
0x00400e78 <+188>: addu v0,v1,v0 #v0=v1-v0
0x00400e7c <+192>: lw v0,4(v0) #v0=v0+4,取得第i+1个数
0x00400e80 <+196>: nop
0x00400e84 <+200>: beq a0,v0,0x400e98 <phase_2+220> #比较a0与v0的值是否相等(a0=学号倒数第i位*输入的第i个数,v0为输入的第i+1个数)
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 #i++
0x00400ea4 <+232>: sw v0,24(s8)
0x00400ea8 <+236>: lw v0,24(s8) #跳转到这里,v0=内存【s8+24】位置的值,即目前的循环变量i的值
0x00400eac <+240>: nop
0x00400eb0 <+244>: slti v0,v0,6 #比较,如果v0<6,则v0=1,如果v0>=6,则v0=0(结合要输入的是六个数字可以猜测这是循环结束的条件)
0x00400eb4 <+248>: bnez v0,0x400e20 <phase_2+100> #v0不为0则跳转到<phase_2+100>
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
即最终结果为1 8 48 96 0 0
Phase_3:
(1)进入函数后,会先执行如下汇编语句:
400f18: 8f828084 lw v0,-32636(gp)
第一次输入1 2 3 4 5 6,结果查看v0的值为3
第二次输入1 2 3,结果查看v0的值仍然为3
第三次输入1 2,结果查看v0的值为2
由以下代码大概可以判断输入的参数应大于等于3个且只有前三个能被存入v0中
400f2c: 8fdc0018 lw gp,24(s8) # gp = [s8+24]
400f30: 28420003 slti v0,v0,3 # v0 = (v0<3)?1:0,如果v0<3,则v0=1,否则v0=0
400f34: 10400004 beqz v0,400f48 <phase_3+0x74> #判断v0是否为0,可知v0的大小应大于等于3 其为判断函数读入的参数是否大于三个
- 输入的第一个参数为0:学号最后一位*输入的第三个数=777
输入的三个参数分别为1 v 97,经调试可知它们分别以十六进制的形式被保存在【s8+36】【s8+40】【s8+44】中,因为一个mips指令的长度为4
400f48: 8fc2002c lw v0,44(s8) #v0=【44+s8】,为输入的第一个参数
400f4c: 00000000 nop
400f50: 2c430008 sltiu v1,v0,8 #v1=(v0<8?1:0),如果v0<8,v1=1;v0>=8,v1=0;
400f54: 1060008e beqz v1,401190 <phase_3+0x2bc> #判断v1是否为0,可知v0即输入的第一个参数的大小应小于8,否则爆炸
然后跳转到:
400f84: 8f82806c lw v0,-32660(gp) #v0=【gp-32660】,读取输入的学号400f88: 00000000 nop
400f8c: 8c43002c lw v1,44(v0) #v1=【v0+44】,学号为12位,44/4=11,v0首地址为学号第一位,则v1为学号最后一位8
400f90: 8fc20024 lw v0,36(s8) #v0=【s8+36】,为输入的第三个参数
400f94: 00000000 nop
400f98: 00620018 mult v1,v0 #v1*v0
400f9c: 00001812 mflo v1 #v1=v1*v0
400fa0: 24020309 li v0,777 #v0=777
400fa4: 10620081 beq v1,v0,4011ac <phase_3+0x2d8> #v1*v0=777(即学号最后一位*输入的第三个参数=777)
输入的第一个参数为1:(见下面代码绿色注释)学号最后一位*输入的第三个数=214
输入的第二个参数为2:学号最后一位*输入的第三个数=755
输入的第二个参数为3:学号最后一位*输入的第三个数=0
输入的第二个参数为4:学号最后一位*输入的第三个数=228
输入的第二个参数为5:学号最后一位*输入的第三个数=513
输入的第二个参数为6:学号最后一位*输入的第三个数=780
输入的第二个参数为7:学号最后一位*输入的第三个数=824=>可以整除8,因此第一个参数应为7,第三个参数应为103
代码:
00400ed4 <phase_3>:
400ed4: 27bdffc8 addiu sp,sp,-56
400ed8: afbf0034 sw ra,52(sp)
400edc: afbe0030 sw s8,48(sp)
400ee0: 03a0f021 move s8,sp
400ee4: 3c1c0042 lui gp,0x42
400ee8: 279cb190 addiu gp,gp,-20080
400eec: afbc0018 sw gp,24(sp)
400ef0: afc40038 sw a0,56(s8)
400ef4: 8fc40038 lw a0,56(s8)
400ef8: 3c020040 lui v0,0x40
400efc: 24452780 addiu a1,v0,10112
400f00: 27c3002c addiu v1,s8,44
400f04: 27c20028 addiu v0,s8,40
400f08: 27c60024 addiu a2,s8,36
400f0c: afa60010 sw a2,16(sp)
400f10: 00603021 move a2,v1
400f14: 00403821 move a3,v0
400f18: 8f828084 lw v0,-32636(gp) #调用输入函数
400f1c: 00000000 nop
400f20: 0040c821 move t9,v0
400f24: 0320f809 jalr t9 #计算输入参数个数并把结果存入v0
400f28: 00000000 nop
400f2c: 8fdc0018 lw gp,24(s8) #gp=【s8+24】
400f30: 28420003 slti v0,v0,3 #若v0<3,v0=1,若v0>=3,v0=0
400f34: 10400004 beqz v0,400f48 <phase_3+0x74> #v0=0,则跳转到400f48,由以上分析可知输入的参数数量应>=3,且只有前三个有用
400f38: 00000000 nop
400f3c: 0c10087c jal 4021f0 <explode_bomb> #v0=1,炸弹爆炸
400f40: 00000000 nop
400f44: 8fdc0018 lw gp,24(s8)
400f48: 8fc2002c lw v0,44(s8) #v0=【s8+44】,调试可知这里是输入的第一个参数,因为输入的参数储存在最初开辟的栈里,而栈自高地址向低地址增长,则【s8+44】~【s8+36】即分别为前三个参数
400f4c: 00000000 nop
400f50: 2c430008 sltiu v1,v0,8 #若v0<8,v1=1,若v0>=8,v1=0
400f54: 1060008e beqz v1,401190 <phase_3+0x2bc> #若v1=0,则跳转到401190=>炸弹爆炸,可知v1应=1,即v0<8
400f58: 00000000 nop
400f5c: 00021880 sll v1,v0,0x2 #v1=v0逻辑左移4个字节,0001<<2=0100(10进制为4)
400f60: 3c020040 lui v0,0x40 #v0=4194304
400f64: 2442278c addiu v0,v0,10124 #v0=v0+10124=4204428
400f68: 00621021 addu v0,v1,v0 #v0=v1+v0
400f6c: 8c420000 lw v0,0(v0) #v0=4198336=十六进制的400fc0
400f70: 00000000 nop400f74: 00400008 jr v0 #跳转到寄存器v0的地址0x400fc0,根据第一个参数确定第三个参数的值,不同的第一个参数对应不同的第三个参数,此时输入的第一个参数为1
400f78: 00000000 nop
400f7c: 24020071 li v0,113 #第一个参数为0,v0=113
400f80: a3c20020 sb v0,32(s8) #【s8+32】=v0=113
400f84: 8f82806c lw v0,-32660(gp) #v0=【gp-32660】,读取输入的学号
400f88: 00000000 nop
400f8c: 8c43002c lw v1,44(v0) #v1=【v0+44】,学号为12位,44/4=11,v0首地址为学号第一位,则v1为学号最后一位
400f90: 8fc20024 lw v0,36(s8) #v0=【s8+36】为输入的第3个参数
400f94: 00000000 nop
400f98: 00620018 mult v1,v0 #v1*v0
400f9c: 00001812 mflo v1 #v1=v1*v0
400fa0: 24020309 li v0,777 #v0=777 #第一个参数为0,学号最后一位*第三个参数=777
400fa4: 10620081 beq v1,v0,4011ac <phase_3+0x2d8> #v1*v0=777(即学号最后一位*输入的第三个参数=777),跳转到0x004011ac
400fa8: 00000000 nop
400fac: 0c10087c jal 4021f0 <explode_bomb>
400fb0: 00000000 nop
400fb4: 8fdc0018 lw gp,24(s8)
400fb8: 1000008f b 4011f8 <phase_3+0x324>
400fbc: 00000000 nop
400fc0: 24020062 li v0,98 #第一个参数为1,v0=98
400fc4: a3c20020 sb v0,32(s8) #【s8+32】=v0=98
400fc8: 8f82806c lw v0,-32660(gp) #v0=学号
400fcc: 00000000 nop
400fd0: 8c43002c lw v1,44(v0) #v1=【v0+44】=学号最后一位8
400fd4: 8fc20024 lw v0,36(s8) #v0=【s8+36】=输入的第三个参数
400fd8: 00000000 nop
400fdc: 00620018 mult v1,v0 #v1*v0
400fe0: 00001812 mflo v1 #v1=v1*v0
400fe4: 240200d6 li v0,214 #v0=214,第一个参数为1,214无法整除8,不能输入1
400fe8: 10620073 beq v1,v0,4011b8 <phase_3+0x2e4>
400fec: 00000000 nop
400ff0: 0c10087c jal 4021f0 <explode_bomb>
400ff4: 00000000 nop
400ff8: 8fdc0018 lw gp,24(s8)
400ffc: 1000007e b 4011f8 <phase_3+0x324>
401000: 00000000 nop
401004: 24020062 li v0,98 #第一个参数为2
401008: a3c20020 sb v0,32(s8)
40100c: 8f82806c lw v0,-32660(gp)
401010: 00000000 nop
401014: 8c43002c lw v1,44(v0)
401018: 8fc20024 lw v0,36(s8)
40101c: 00000000 nop
401020: 00620018 mult v1,v0
401024: 00001812 mflo v1
401028: 240202f3 li v0,755 #第一个参数为2,学号最后一位*第三个参数=755
40102c: 10620065 beq v1,v0,4011c4 <phase_3+0x2f0>
401030: 00000000 nop
401034: 0c10087c jal 4021f0 <explode_bomb>
401038: 00000000 nop
40103c: 8fdc0018 lw gp,24(s8)
401040: 1000006d b 4011f8 <phase_3+0x324>
401044: 00000000 nop
401048: 2402006b li v0,107 #第一个参数为3
40104c: a3c20020 sb v0,32(s8)
401050: 8f82806c lw v0,-32660(gp)
401054: 00000000 nop
401058: 8c43002c lw v1,44(v0)
40105c: 8fc20024 lw v0,36(s8)
401060: 00000000 nop
401064: 00620018 mult v1,v0
401068: 00001012 mflo v0
40106c: 10400058 beqz v0,4011d0 <phase_3+0x2fc> #第一个参数为3,学号最后一位*第三个参数=0
401070: 00000000 nop
401074: 0c10087c jal 4021f0 <explode_bomb>
401078: 00000000 nop
40107c: 8fdc0018 lw gp,24(s8)
401080: 1000005d b 4011f8 <phase_3+0x324>
401084: 00000000 nop
401088: 2402006f li v0,111 #第一个参数为4
40108c: a3c20020 sb v0,32(s8)
401090: 8f82806c lw v0,-32660(gp)
401094: 00000000 nop
401098: 8c43002c lw v1,44(v0)
40109c: 8fc20024 lw v0,36(s8)
4010a0: 00000000 nop
4010a4: 00620018 mult v1,v0
4010a8: 00001812 mflo v1
4010ac: 240200e4 li v0,228 #第一个参数为4,学号最后一位*第三个参数=228
4010b0: 1062004a beq v1,v0,4011dc <phase_3+0x308>
4010b4: 00000000 nop
4010b8: 0c10087c jal 4021f0 <explode_bomb>
4010bc: 00000000 nop
4010c0: 8fdc0018 lw gp,24(s8)
4010c4: 1000004c b 4011f8 <phase_3+0x324>
4010c8: 00000000 nop
4010cc: 24020074 li v0,116 #第一个参数为5
4010d0: a3c20020 sb v0,32(s8)
4010d4: 8f82806c lw v0,-32660(gp)
4010d8: 00000000 nop
4010dc: 8c43002c lw v1,44(v0)
4010e0: 8fc20024 lw v0,36(s8)
4010e4: 00000000 nop
4010e8: 00620018 mult v1,v0
4010ec: 00001812 mflo v1
4010f0: 24020201 li v0,513 #第一个参数为5,学号最后一位*第三个参数=513
4010f4: 1062003c beq v1,v0,4011e8 <phase_3+0x314>
4010f8: 00000000 nop
4010fc: 0c10087c jal 4021f0 <explode_bomb>
401100: 00000000 nop
401104: 8fdc0018 lw gp,24(s8)
401108: 1000003b b 4011f8 <phase_3+0x324>
40110c: 00000000 nop
401110: 24020076 li v0,118 #第一个参数为6
401114: a3c20020 sb v0,32(s8)
401118: 8f82806c lw v0,-32660(gp)
40111c: 00000000 nop
401120: 8c43002c lw v1,44(v0)
401124: 8fc20024 lw v0,36(s8)
401128: 00000000 nop
40112c: 00620018 mult v1,v0
401130: 00001812 mflo v1
401134: 2402030c li v0,780 #第一个参数为6,学号最后一位*第三个参数=780
401138: 10620004 beq v1,v0,40114c <phase_3+0x278>
40113c: 00000000 nop
401140: 0c10087c jal 4021f0 <explode_bomb>
401144: 00000000 nop
401148: 8fdc0018 lw gp,24(s8)
40114c: 24020062 li v0,98 #第一个参数为7
401150: a3c20020 sb v0,32(s8)
401154: 8f82806c lw v0,-32660(gp)
401158: 00000000 nop
40115c: 8c43002c lw v1,44(v0)
401160: 8fc20024 lw v0,36(s8)
401164: 00000000 nop
401168: 00620018 mult v1,v0
40116c: 00001812 mflo v1
401170: 24020338 li v0,824 #第一个参数为7,学号最后一位*第三个参数=824在这继续运行
401174: 1062001f beq v1,v0,4011f4 <phase_3+0x320> #跳转到4011f4
401178: 00000000 nop
40117c: 0c10087c jal 4021f0 <explode_bomb>
401180: 00000000 nop
401184: 8fdc0018 lw gp,24(s8)
401188: 1000001b b 4011f8 <phase_3+0x324>
40118c: 00000000 nop
401190: 24020078 li v0,120 #v0=120
401194: a3c20020 sb v0,32(s8) #【s8+32】=v0
401198: 0c10087c jal 4021f0 <explode_bomb> #炸弹爆炸
40119c: 00000000 nop
4011a0: 8fdc0018 lw gp,24(s8)
4011a4: 10000014 b 4011f8 <phase_3+0x324>
4011a8: 00000000 nop
4011ac: 00000000 nop #确定第三个参数之后跳转到这里
4011b0: 10000011 b 4011f8 <phase_3+0x324> #跳转到4011f8
4011b4: 00000000 nop
4011b8: 00000000 nop
4011bc: 1000000e b 4011f8 <phase_3+0x324>
4011c0: 00000000 nop
4011c4: 00000000 nop
4011c8: 1000000b b 4011f8 <phase_3+0x324>
4011cc: 00000000 nop
4011d0: 00000000 nop
4011d4: 10000008 b 4011f8 <phase_3+0x324>
4011d8: 00000000 nop
4011dc: 00000000 nop
4011e0: 10000005 b 4011f8 <phase_3+0x324>
4011e4: 00000000 nop
4011e8: 00000000 nop
4011ec: 10000002 b 4011f8 <phase_3+0x324>
4011f0: 00000000 nop
4011f4: 00000000 nop
4011f8: 83c20028 lb v0,40(s8) #v0=【s8+40】,这里是输入的第二个参数
4011fc: 83c30020 lb v1,32(s8) #v1=【s8+32】
401200: 00000000 nop
401204: 10620004 beq v1,v0,401218 <phase_3+0x344> #输入的第二个参数与v1的值比较,如果相等就跳转到401218,程序结束,否则炸弹爆炸,ASCII码为98对应的为b,因此输入的第二个参数为b(这里当我第一个参数为7时第二个参数为b,第二个参数的值要根据第一个参数来确定,【s8+32】为跳转前v0存入的值)
401208: 00000000 nop
40120c: 0c10087c jal 4021f0 <explode_bomb>
401210: 00000000 nop
401214: 8fdc0018 lw gp,24(s8)
401218: 03c0e821 move sp,s8 #把s8的值给sp
40121c: 8fbf0034 lw ra,52(sp) #ra=【sp+52】
401220: 8fbe0030 lw s8,48(sp) #s8=【sp+48】
401224: 27bd0038 addiu sp,sp,56
401228: 03e00008 jr ra #程序结束
40122c: 00000000 nop
炸弹三总体上是一个switch-case函数,学号不同的最后一位对应着不同的case
我得学号最后一位是8
即最终答案为7 b 103
Phase_4:
暂时输入6
代码:
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) #调用子程序输入v0
0x004012fc <+64>: nop
0x00401300 <+68>: move t9,v0 #t9=v0
0x00401304 <+72>: jalr t9 #跳转到t9并连接,计算输入参数个数并把结果存入v0
0x00401308 <+76>: nop
0x0040130c <+80>: lw gp,16(s8) #gp=【s8+16】
0x00401310 <+84>: move v1,v0 #v1=v0
0x00401314 <+88>: li v0,1 #v0=1
0x00401318 <+92>: bne v1,v0,0x401330 <phase_4+116> #v1!=v0则跳转,判断v1是否为1,不为1则跳转到 <phase_4+116>,炸弹爆炸,因此v1应为1,即输入参数的个数为1
0x0040131c <+96>: nop
0x00401320 <+100>: lw v0,24(s8) #v0=【s8+24】
0x00401324 <+104>: nop
0x00401328 <+108>: bgtz v0,0x401340 <phase_4+132> #v0>0则跳转,查看【s8+24】的值可知【s8+24】里储存的是我输入的值,可知输入的参数大小应大于0
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) #学号
0x00401344 <+136>: nop
0x00401348 <+140>: lw v0,44(v0) #学号最后一位v0=8
0x0040134c <+144>: nop
0x00401350 <+148>: andi v0,v0,0x1 #v0=0,位与运算,8(10)=1000(2),即1000与0001进行位与运算,有0就为0,位与结果为0000,只要学号最后一位是奇数,那v0=1,学号最后一位是偶数,v0=0
0x00401354 <+152>: andi v0,v0,0xff #v0=0,位与运算,00000000与11111111进行位与运算=>这两步是判断学号最后一位是奇数还是偶数(不明白为什么有这一步,上一步明明就可以判断奇偶了)
0x00401358 <+156>: beqz v0,0x40139c <phase_4+224> #v0=0,即学号最后一位是偶数,跳转
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=【s8+24】,取出输入的值
0x004013a0 <+228>: nop
0x004013a4 <+232>: move a0,v0 #a0=v0,此时a0为输入的参数
0x004013a8 <+236>: jal 0x401230 <func4> #跳转到<func4>
0x004013ac <+240>: nop
0x004013b0 <+244>: lw gp,16(s8)
0x004013b4 <+248>: move v1,v0 #v1=v0=1
0x004013b8 <+252>: li v0,13 #v0=13
0x004013bc <+256>: beq v1,v0,0x4013d0 <phase_4+276> #v1和v0相等就跳转至程序结束=>斐波那契数列哪一项为13(1,1,2,3,5,8,13)=》f(6)=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
Dump of assembler code for function func4: #递归=》斐波那契数列
>: 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) #【s8+40】=a0,存入传入的数值6->5->4->3->2->1->0
0x00401248 <+24>: lw v0,40(s8) #v0=【s8+40】,取出传入的数值6->5->4->3->2->1->0
0x0040124c <+28>: nop
0x00401250 <+32>: slti v0,v0,2 #v0=(v0<2?1:0),若v0<2,v0=1,若v0>=2,v0=0,传入数值为6->5->4->3->2时,v0=0,直到传入数值为<2时,v0=1
0x00401254 <+36>: bnez v0,0x40129c <func4+108> #若v0不等于0,即传入的数值>1时跳转到0x40129c,传入数值为6->4->3->2继续向下运行=(>传入函数的数值逐渐递减直到为1,当传入的数值大于1时继续运行)
0x00401258 <+40>: nop #调用了func($v0-1)
0x0040125c <+44>: lw v0,40(s8) #v0>=2时,即输入的数值>=2=>v0=【s8+40】,取出传入的数值v0=传入数为6->5->4->3->2
0x00401260 <+48>: nop
0x00401264 <+52>: addiu v0,v0,-1 #v0=v0-1=5->4->3->2->1
0x00401268 <+56>: move a0,v0 #a0=v0=5->4->3->2->1
0x0040126c <+60>: jal 0x401230 <func4> #jal用于子程序调用,返回值放在v0,a0--,继续传入<func4>函数递归
0x00401270 <+64>: nop #调用了func($v0-2)
0x00401274 <+68>: move s0,v0 #返回上一层递归的位置
0x00401278 <+72>: lw v0,40(s8) #v0=【s8+40】=2
0x0040127c <+76>: nop
0x00401280 <+80>: addiu v0,v0,-2 #v0=v0-2=0
0x00401284 <+84>: move a0,v0 #a0=v0=0->1->2->3->4
0x00401288 <+88>: jal 0x401230 <func4> #返回值放在v0,返回上一层递归之后运行到这里再跳转到函数开头
0x0040128c <+92>: nop
0x00401290 <+96>: addu v0,s0,v0 #v0=s0+v0=1+1=2
0x00401294 <+100>: b 0x4012a0 <func4+112> #跳转
0x00401298 <+104>: nop
0x0040129c <+108>: li v0,1 #跳转到这里,v0=1,继续运行0x004012a4
0x004012a0 <+112>: move sp,s8
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 #一层递归结束后跳转到0x00401274
即答案为6
Phase_5:
(1)0x00401400 <+24>: jal 0x401c78 <string_length> #判断输入字符串的长度,返回值放在v0
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> #v1和v0相等就跳转,否则炸弹爆炸,这里说明输入的字符串的长度必须为6
(2)0x00401420 <+56>: sw zero,24(s8) #跳转到这里,【s8+24】=zero=0
0x00401424 <+60>: b 0x4014a8 <phase_5+192> #跳转
0x004014a8 <+192>: lw v0,24(s8) #v0=【s8+24】=0
0x004014ac <+196>: nop
0x004014b0 <+200>: slti v0,v0,6 #如果v0<6,v0=1;否则v0=0,这里v0=0,所以v0新值为1
0x004014b4 <+204>: bnez v0,0x40142c <phase_5+68> #若v0不等于0则跳转,这里v0=1,所以跳转
接下来注释在代码段
(3)代码:
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) #猜测【s8+78】是输入的字符串,输出了一下果然是,我输入的字符串为asdfgh
0x00401400 <+24>: jal 0x401c78 <string_length> #判断输入字符串的长度,返回值放在v0
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> #v1和v0相等就跳转,否则炸弹爆炸,这里说明输入的字符串的长度必须为6
0x00401414 <+44>: nop
0x00401418 <+48>: jal 0x4021f0 <explode_bomb>
0x0040141c <+52>: nop
0x00401420 <+56>: sw zero,24(s8) #跳转到这里,【s8+24】=zero(代表八进制整数0),初始化循环变量i=0
0x00401424 <+60>: b 0x4014a8 <phase_5+192> #跳转,条件判断
0x00401428 <+64>: nop
0x0040142c <+68>: lw v0,24(s8) #v0=【s8+24】=0,取出之前的循环变量i
0x00401430 <+72>: lw v1,24(s8) #v1=【s8+24】=0,取出之前的循环变量i
0x00401434 <+76>: lw a0,72(s8) #a0=【s8+78】=输入的字符串
0x00401438 <+80>: nop
0x0040143c <+84>: addu v1,a0,v1 #到达输入的字符串的第i+1个字母,i从0到5
0x00401440 <+88>: lb v1,0(v1) #v1=拿到输入的字符串的第i+1个字母,例如我输入的字符串为asdfgh,在第一次循环时,v1首先为97,97为a的ASCII码
0x00401444 <+92>: nop
0x00401448 <+96>: andi v1,v1,0xff #位与:01100001&11111111=01100001
0x0040144c <+100>: andi v1,v1,0xf #位与:01100001&00001111=00000001
#这两步是为了得到目前取出的字母的八位二进制的后四位
0x00401450 <+104>: sll v0,v0,0x2 #向左逻辑移动2位,即变为3位,占4个字节,即一个int型变量的长度(一个int型变量占4个字节,4(10)=100(2))
0x00401454 <+108>: addiu a0,s8,24 #a0=s8+24,即之前储存的循环变量i的地址
0x00401458 <+112>: addu v0,a0,v0 #v0=v0+a0,从这个地址后移4位
0x0040145c <+116>: sw v1,12(v0) #【v0+12】=v1,把目前取出字母的二进制后四位放在【v0+12】即【s8+24+32】=【s8+36】中
#以上代码的目的是通过循环拿到输入的六个字母的每一个字母的ASCII码的二进制的后四位并保存在【v0+12】中
0x00401460 <+120>: lw a0,24(s8) #a0=【s8+24】=0,取出之前的循环变量
0x00401464 <+124>: lw v0,24(s8) #v0=【s8+24】=0,取出之前的循环变量
0x00401468 <+128>: nop
0x0040146c <+132>: sll v0,v0,0x2 #向左逻辑移动2位,变为int型变量长度
0x00401470 <+136>: addiu v1,s8,24 #v1=s8+24,v1为取得i的地址
0x00401474 <+140>: addu v0,v1,v0 #v0=v1+v0
0x00401478 <+144>: lw v1,12(v0) #v1=【v0+12】,这里是v1=刚刚取得的字母a的ASCII码二进制的后四位
0x0040147c <+148>: lui v0,0x41 #v0=0x41
0x00401480 <+152>: addiu v0,v0,12524 #v0=v0+12524 #找到这个内置字符串的开头,内置字符串为isrveawhobpnutfg
0x00401484 <+156>: addu v0,v1,v0 #v0=v1+v0 #从开头地址往后移动v0位,v0为目前取得的字母a的ascii码二进制的后四位,比如第一个输入的字母为a,后四位为0001,即十进制的1,则v0即为内置字符串从第2个字母开始向后的部分
#说明输入的字符串的后四位是为了在内置字符串中找到了相应的字母
0x00401488 <+160>: lb v1,0(v0) #v1=v0,内置字符串相应字母的首位
0x0040148c <+164>: addiu v0,s8,24 #v0=s8+24,循环变量i的地址
0x00401490 <+168>: addu v0,v0,a0 #v0=v0+a0,a0为之前的循环变量i,即v0=s8+24+i
0x00401494 <+172>: sb v1,4(v0) #【v0+4】=v1,把这个内置字符串的字母存入【v0+4】即【s8+28+i】的位置
0x00401498 <+176>: lw v0,24(s8) #v0=【s8+24】,取得之前的循环变量i
0x0040149c <+180>: nop
0x004014a0 <+184>: addiu v0,v0,1 #v0=v0+1,即循环变量i++
0x004014a4 <+188>: sw v0,24(s8) #【s8+24】=v0,把循环变量重新存入【s8+24】的位置方便进入下一次循环
0x004014a8 <+192>: lw v0,24(s8) #v0=【s8+24】=i,即取出+1后的循环变量i
0x004014ac <+196>: nop
0x004014b0 <+200>: slti v0,v0,6 #若v0<6,v0=1,循环条件判断
0x004014b4 <+204>: bnez v0,0x40142c <phase_5+68> #若v0不为0,跳转
#若i<6,则跳转到循环体部分,若i>=6,循环结束,继续向下执行
0x004014b8 <+208>: nop
0x004014bc <+212>: sb zero,34(s8) #【s8+34】=0,从【s8+28】到【s8+33】分别为输入的字母映射后的内置字符串的六个字母,然后在【s8+34】即字符串的末尾置空
0x004014c0 <+216>: addiu v0,s8,28 #v0=s8+28,取得映射后的六个内置字符串的字母
0x004014c4 <+220>: move a0,v0 #a0=v0
0x004014c8 <+224>: lui v0,0x40 #v0=0x40
0x004014cc <+228>: addiu a1,v0,10160 #映射后的六个内置字符串的字母与a1中的字母比较,经查看可知a1中的字符串为giants
内置字符串为isrveawhobpnutfg
所要找的为giants
对应的十进制分别为15 0 5 11 13 1
则对应的ASCII码后四位分别为1111 0000 0101 1011 1101 0001
则对应的字母分别为o(0110 1111) p(0111 0000) u(0111 0101) k(0110 1011) m(0110 1101) q(0111 0001)
即应输入opukmq
0x004014d0 <+232>: jal 0x401cf8 <strings_not_equal>
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
故答案为opukmq
Phase_6:
(1)0x00401538 <+56>: jal 0x401ba8 <read_six_numbers>
可知输入的为6个数字
(2)两层循环可得:输入数字大小在(0,7)之间且前后两个数不能重复
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> #读取6个数字,返回值存在v0
0x0040153c <+60>: nop
0x00401540 <+64>: lw gp,16(s8) #第一重循环
0x00401544 <+68>: sw zero,28(s8) #【s8+28】=0,设置循环变量i=0
0x00401548 <+72>: b 0x40163c <phase_6+316> #第一个循环的条件判断
0x0040154c <+76>: nop
0x00401550 <+80>: lw v0,28(s8) #v0=【s8+28】,即取出循环变量i
0x00401554 <+84>: nop
0x00401558 <+88>: sll v0,v0,0x2 #将i变为int型长度
0x0040155c <+92>: addiu v1,s8,24 #v1=s8+24
0x00401560 <+96>: addu v0,v1,v0 #v0=s8+24+i
0x00401564 <+100>: lw v0,12(v0) #v0=【v0+12】,取出输入的第i个数
(此时我输入的六个数字为1 2 3 4 5 6)
0x00401568 <+104>: nop
0x0040156c <+108>: slti v0,v0,7 #判断若v0>=7,则跳转,炸弹爆炸,可知输入的六个数字<7
0x00401570 <+112>: beqz v0,0x40159c <phase_6+156>
0x00401574 <+116>: nop
0x00401578 <+120>: lw v0,28(s8) #v0=【s8+28】,取出循环变量i
0x0040157c <+124>: nop
0x00401580 <+128>: sll v0,v0,0x2 #将i变为int型长度
0x00401584 <+132>: addiu v1,s8,24 #v1=s8+24
0x00401588 <+136>: addu v0,v1,v0 #v0=s8+24+i
0x0040158c <+140>: lw v0,12(v0) #v0=【v0+12】,取出输入的第i个数
0x00401590 <+144>: nop
0x00401594 <+148>: bgtz v0,0x4015a8 <phase_6+168> #判断若v0>0,则跳转,否则炸弹爆炸,可知输入的六个数字>0
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=【s8+28】,取得之前的循环变量i
0x004015ac <+172>: nop
0x004015b0 <+176>: addiu v0,v0,1 #v0=v0+1,设定第二重循环变量j=i+1
0x004015b4 <+180>: sw v0,24(s8) #【s8+24】=v0=j,即i的值存在【s8+28】,j的值存在【s8+24】
0x004015b8 <+184>: b 0x401618 <phase_6+280> #第二重循环的条件判断:j<6
0x004015bc <+188>: nop
0x004015c0 <+192>: lw v0,28(s8) #v0=【s8+24】,取出第一重循环变量i
0x004015c4 <+196>: nop
0x004015c8 <+200>: sll v0,v0,0x2 #j转换成int型变量长度
0x004015cc <+204>: addiu v1,s8,24 #v1=s8+24,即j的地址
0x004015d0 <+208>: addu v0,v1,v0 #v0=s8+24+i
0x004015d4 <+212>: lw v1,12(v0) #v1=【v0+12】,取得输入的第i个数字
0x004015d8 <+216>: lw v0,24(s8) #v0=【s8+24】即取出第二重循环变量j
0x004015dc <+220>: nop
0x004015e0 <+224>: sll v0,v0,0x2 #转换成int型变量长度
0x004015e4 <+228>: addiu a0,s8,24 #a0=s8+24
0x004015e8 <+232>: addu v0,a0,v0 #v0=s8+24+j
0x004015ec <+236>: lw v0,12(v0) #取得第j个数字
0x004015f0 <+240>: nop
0x004015f4 <+244>: bne v1,v0,0x401608 <phase_6+264> #比较v1和v0的值,相等就爆炸,可知前后两个数字不能重复
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=【s8+24】=j
0x0040160c <+268>: nop
0x00401610 <+272>: addiu v0,v0,1 #v0=v0+1,j++
0x00401614 <+276>: sw v0,24(s8) #【s8+24】=v0,更新循环变量的值j++
0x00401618 <+280>: lw v0,24(s8) #第二重循环条件判断,取出第二重循环变量j
0x0040161c <+284>: nop
0x00401620 <+288>: slti v0,v0,6 #判断j<6,v0=1,否则v0=0
0x00401624 <+292>: bnez v0,0x4015c0 <phase_6+192> #v0=1即j<6时跳转
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) #条件判断
0x00401640 <+320>: nop
0x00401644 <+324>: slti v0,v0,6 #如果v0<6,则v0=1
0x00401648 <+328>: bnez v0,0x401550 <phase_6+80> #v0不等于0就继续循环
(3)第二个循环:目的是在链表中根据输入的六个数字分别找到链表的六个节点的值,保存在一个新开辟的数组里
0x0040164c <+332>: nop
0x00401650 <+336>: sw zero,28(s8) #【s8+28】=0,设置第一重循环变量i=0,即i放在【s8+28】里
0x00401654 <+340>: b 0x4016f8 <phase_6+504> #第一重循环i的条件判断
0x00401658 <+344>: nop
0x0040165c <+348>: lui v0,0x41 #v0=0x410000,这里原来是让v0储存第0x410000的地址啊
0x00401660 <+352>: addiu v0,v0,12592 #node1
0x00401664 <+356>: sw v0,32(s8) #【s8+32】=v0,给v0在【s8+32】新建一个数组,把-3放到数组第一个位置
0x00401668 <+360>: li v0,1 #v0=1,设置第二重循环变量j=1
0x0040166c <+364>: sw v0,24(s8) #【s8+24】=v0=1,即j放在【s8+24】里
0x00401670 <+368>: b 0x40169c <phase_6+412> #j条件判断
0x00401674 <+372>: nop
0x00401678 <+376>: lw v0,32(s8) #v0=【s8+32】
0x0040167c <+380>: nop
0x00401680 <+384>: lw v0,8(v0) #v0=【v0+8】,找到它后面+8的位置
0x00401684 <+388>: nop
0x00401688 <+392>: sw v0,32(s8) #【s8+32】=v0=【s8+32+8】
0x0040168c <+396>: lw v0,24(s8) #v0=【s8+24】,取得第二重循环变量j
0x00401690 <+400>: nop
0x00401694 <+404>: addiu v0,v0,1 #v0+1,即j++
0x00401698 <+408>: sw v0,24(s8) #【s8+24】=v0,更新j的新值
0x0040169c <+412>: lw v0,28(s8) #第二重循环j的条件判断,v0=第一重循环变量i(这里大概相当于一个do-while循环?直到j=输入的第i个数)
0x004016a0 <+416>: nop
0x004016a4 <+420>: sll v0,v0,0x2 #将i变为int型长度
0x004016a8 <+424>: addiu v1,s8,24 #v1=s8+24,v1储存的是j的地址
0x004016ac <+428>: addu v0,v1,v0 #v0=s8+24+j
0x004016b0 <+432>: lw v1,12(v0) #v1=【v0+12】,取得输入的第i个数
0x004016b4 <+436>: lw v0,24(s8) #v0=【s8+24】,取出j
0x004016b8 <+440>: nop
0x004016bc <+444>: slt v0,v0,v1 #v0<v1=>v0=1
0x004016c0 <+448>: bnez v0,0x401678 <phase_6+376> #若j<输入的第i个数,则继续第二重循环,否则向下运行
0x004016c4 <+452>: nop
0x004016c8 <+456>: lw v0,28(s8) #取得第一重循环变量i
0x004016cc <+460>: nop
0x004016d0 <+464>: sll v0,v0,0x2
0x004016d4 <+468>: addiu v1,s8,24 #v1=s8+24第二重循环变量的地址
0x004016d8 <+472>: addu v0,v1,v0 #v0=s8+24+i
0x004016dc <+476>: lw v1,32(s8) #v1=【s8+32】,新建数组的首地址
0x004016e0 <+480>: nop
0x004016e4 <+484>: sw v1,36(v0) #【v0+36】=v1,把节点值装入新开辟数组首地址的下一个位置
0x004016e8 <+488>: lw v0,28(s8) #v0=【s8+28】,取得第一重循环变量i
0x004016ec <+492>: nop
0x004016f0 <+496>: addiu v0,v0,1 #i++
0x004016f4 <+500>: sw v0,28(s8) #【s8+28】=v0,即更新i的值
0x004016f8 <+504>: lw v0,28(s8) #第一重循环i的条件判断
0x004016fc <+508>: nop
0x00401700 <+512>: slti v0,v0,6 #v0<6=>v0=1
0x00401704 <+516>: bnez v0,0x40165c <phase_6+348> #i<6继续循环,否则跳出循环
(4)将链表按照在数组中的顺序重新链接(这里因为我输入的为123456,所以调试时发现新链表顺序和旧链表一样)
0x00401708 <+520>: nop
0x0040170c <+524>: lw v0,60(s8) #v0=【s8+60】
0x00401710 <+528>: nop
0x00401714 <+532>: sw v0,32(s8) #【s8+32】=v0
0x00401718 <+536>: li v0,1 #设置循环变量i=1
0x0040171c <+540>: sw v0,28(s8) #【s8+28】=v0,即循环变量储存在【s8+28】里
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
0x00401734 <+564>: addiu v1,s8,24 #v1=s8+24
0x00401738 <+568>: addu v0,v1,v0 #v0=s8+24+i*4
(这个6。。。难不成是答案第一位还是说我输入的最后一位)
0x0040173c <+572>: lw v1,36(v0) #v1=【s8+24+i*4+36】得到新链表第i个节点的值,第一次时这里是第二个节点,因为i=1,第一个节点为【0】
0x00401740 <+576>: lw v0,32(s8) #v0=【s8+32】第一次中这是第一个节点
0x00401744 <+580>: nop
0x00401748 <+584>: sw v1,8(v0) #【v0+8】=v1
(不太明白。。。)
0x0040174c <+588>: lw v0,28(s8) #取得循环变量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=s8+24+i*4+36
0x00401764 <+612>: nop
0x00401768 <+616>: sw v0,32(s8) #【s8+32】=v0,储存当前节点
0x0040176c <+620>: lw v0,28(s8) #v0=循环变量i
0x00401770 <+624>: nop
0x00401774 <+628>: addiu v0,v0,1 #v0=i++
0x00401778 <+632>: sw v0,28(s8)
0x0040177c <+636>: lw v0,28(s8) #循环条件判断:v0=i
0x00401780 <+640>: nop
0x00401784 <+644>: slti v0,v0,6 #若v0<6则继续循环
0x00401788 <+648>: bnez v0,0x401728 <phase_6+552>
(5)最后一位学号为偶数:前一个节点的数值要小于后一个节点的值,即升序
最后一位学号为奇数:前一个节点的数值要小于后一个节点的值,即降序
0x0040178c <+652>: nop
0x00401790 <+656>: lw v0,32(s8) #由<+616>可知这里是最后一个节点
0x00401794 <+660>: nop
0x00401798 <+664>: sw zero,8(v0) #【v0+8】=0,把最后一个节点的后面置空NULL
0x0040179c <+668>: lw v0,60(s8) #给v0找一个位置即【s8+60】,新开辟一个空间
0x004017a0 <+672>: nop
0x004017a4 <+676>: sw v0,32(s8) #【s8+32】=v0,储存这个空间的位置
0x004017a8 <+680>: sw zero,28(s8) #【s8+28】=0,设置循环变量i=0
0x004017ac <+684>: b 0x401878 <phase_6+888> #循环条件判断
0x004017b0 <+688>: nop
0x004017b4 <+692>: lw v0,-32660(gp) #取得学号
0x004017b8 <+696>: nop
0x004017bc <+700>: lw v0,44(v0) #取得学号最后一位=8
0x004017c0 <+704>: nop
0x004017c4 <+708>: andi v0,v0,0x1
0x004017c8 <+712>: andi v0,v0,0xff #判断奇偶
0x004017cc <+716>: beqz v0,0x401818 <phase_6+792>#偶数跳转
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) #v0=【s8+32】,即新开辟的空间
0x0040181c <+796>: nop
0x00401820 <+800>: lw v1,0(v0) #v1为这个新开辟空间的第【i】个节点
0x00401824 <+804>: lw v0,32(s8) #v0=【s8+32】,即新开辟的空间
0x00401828 <+808>: nop
0x0040182c <+812>: lw v0,8(v0) #v0到【i+1】节点的地址
0x00401830 <+816>: nop
0x00401834 <+820>: lw v0,0(v0) #v0为【i+1】节点的值
0x00401838 <+824>: nop
0x0040183c <+828>: slt v0,v0,v1 #如果v1<v0,则跳转,即前一个节点的数值要小于后一个节点的值
0x00401840 <+832>: beqz v0,0x401854 <phase_6+852> #v0=0跳转即v0>=v1
0x00401844 <+836>: nop
0x00401848 <+840>: jal 0x4021f0 <explode_bomb>
0x0040184c <+844>: nop
0x00401850 <+848>: lw gp,16(s8)
0x00401854 <+852>: lw v0,32(s8)
0x00401858 <+856>: nop
0x0040185c <+860>: lw v0,8(v0) #到达链表下一个节点
0x00401860 <+864>: nop
0x00401864 <+868>: sw v0,32(s8)
0x00401868 <+872>: lw v0,28(s8)
0x0040186c <+876>: nop
0x00401870 <+880>: addiu v0,v0,1 #i++
0x00401874 <+884>: sw v0,28(s8)
0x00401878 <+888>: lw v0,28(s8) #循环条件判断
0x0040187c <+892>: nop
0x00401880 <+896>: slti v0,v0,5 #当v0<5时继续循环
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
通过单步调试可知最初的链表的六个值十进制分别为-3 -43 45 -27 -44 -80
但是不能按十进制,因为这个十进制是取了十六进制转换成二进制的后八位转换成的十进制,是错误的数字,因此应该为十六进制。(因为我一开始尝试了很多次按十进制排序6 5 2 4 1 3不对。。。)
因此十六进制分别为:
0x000000fd 0x00002d5 0x0000012d 0x000003e5 0x000000d4 0x000001b0
我的学号为8,需要升序排列新链表
故输入的六个数应为5 1 3 6 2 4
Secret_phase:
(1)首先通过bomb.s文件看到需要输入的是%d %s格式,结合之前拆炸弹的答案,因为只有炸弹四的答案是一个整数,可以初步推断是在炸弹四的答案后面空格输入一个字符串进入phase_defused函数。
然后进入phase_defused函数汇编代码,看到<strings_not_equal>,由炸弹一的经验可以初步判断这应该是输入一个字符串从而判断是否与原本寄存器里的字符串是否相等。
单步调试查看a1寄存器的值,发现是austinpowers,因此需要在炸弹四的答案6后面输入一个空格和austinpowers,即可在炸弹六成功拆除后进入隐藏炸弹。
(2)secret_phase
进入secret_phase,通过单步调试可以直到v0中储存着输入的字符串
代码:
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) #通过单步调试发现输入的字符串储存在v0里,把输入的字符串放在内存【s8+28】里
=> 0x004019bc <+44>: lw v0,28(s8) #取出输入的数值给v0
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)
0x004019d4 <+68>: nop
0x004019d8 <+72>: move t9,v0
0x004019dc <+76>: jalr t9
0x004019e0 <+80>: nop
0x004019e4 <+84>: lw gp,16(s8)
--Type <RET> for more, q to quit, c to continue without paging--c
0x004019e8 <+88>: sw v0,24(s8) #【s8+24】=输入的数值
0x004019ec <+92>: lw v0,24(s8) #取出【s8+24】的值即输入的数值给v0
0x004019f0 <+96>: nop
0x004019f4 <+100>: addiu v0,v0,-1 #v0=v0-1
0x004019f8 <+104>: sltiu v0,v0,1001 #条件判断,v0的值必须小于1001,否则炸弹爆炸,即输入的数值应该小于1002
0x004019fc <+108>: bnez v0,0x401a10 <secret_phase+128>
0x00401a00 <+112>: nop
0x00401a04 <+116>: jal 0x4021f0 <explode_bomb>
0x00401a08 <+120>: nop
0x00401a0c <+124>: lw gp,16(s8)
0x00401a10 <+128>: lui v0,0x41 #跳转到这里
0x00401a14 <+132>: addiu a0,v0,12676
0x00401a18 <+136>: lw a1,24(s8) #a1=【s8+24】=输入的数值
0x00401a1c <+140>: jal 0x4018a4 <fun7> #跳转到<func>
- Fun7:
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) #【sp+24】=s8,输入的数值在【s8+24】
0x004018b0 <+12>: move s8,sp #s8=sp
0x004018b4 <+16>: sw a0,32(s8) #【s8+32】=a0=0x24,第二次a0=0x32
0x004018b8 <+20>: sw a1,36(s8) #【s8+36】=a1=输入的数值
0x004018bc <+24>: lw v0,32(s8) #v0=【s8+32】
0x004018c0 <+28>: nop
0x004018c4 <+32>: bnez v0,0x4018d8 <fun7+52> #如果v0不为0就跳转,v0=0x24,所以跳转,第二次v0=0x32,所以跳转
0x004018c8 <+36>: nop
0x004018cc <+40>: li v0,-1
0x004018d0 <+44>: b 0x401978 <fun7+212>
0x004018d4 <+48>: nop
0x004018d8 <+52>: lw v0,32(s8) #v0=【s8+32】=0x24,第二次v0=0x32
0x004018dc <+56>: nop
0x004018e0 <+60>: lw v1,0(v0) #查看寄存器v1里的值可知v1=36,第二次v1=50
0x004018e4 <+64>: lw v0,36(s8) #取出输入的数值
0x004018e8 <+68>: nop
0x004018ec <+72>: slt v0,v0,v1 #如果v0<v1,则v0=1,不跳转,否则v0=0,跳转
0x004018f0 <+76>: beqz v0,0x401924 <fun7+128> #如果v0=0,则跳转
0x004018f4 <+80>: nop
0x004018f8 <+84>: lw v0,32(s8) #v0=【s8+32】
--Type <RET> for more, q to quit, c to continue without paging--c
0x004018fc <+88>: nop
0x00401900 <+92>: lw v0,4(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 #v0左移一位,末尾为0,v0=2*v0,即进入该节点的左孩子
0x0040191c <+120>: b 0x401978 <fun7+212>
0x00401920 <+124>: nop
0x00401924 <+128>: lw v0,32(s8) #跳转到这里,v0=【s8+32】=0x24
0x00401928 <+132>: nop
0x0040192c <+136>: lw v1,0(v0) #v1=36
0x00401930 <+140>: lw v0,36(s8) #v0=输入的数值1001
0x00401934 <+144>: nop
0x00401938 <+148>: slt v0,v1,v0 #v0<v1就跳转,反之不跳转
0x0040193c <+152>: beqz v0,0x401974 <fun7+208>
0x00401940 <+156>: nop
0x00401944 <+160>: lw v0,32(s8) #v0=【s8+32】=0x24
0x00401948 <+164>: nop
0x0040194c <+168>: lw v0,8(v0) #v0=0x32
0x00401950 <+172>: nop
0x00401954 <+176>: move a0,v0
0x00401958 <+180>: lw a1,36(s8) #a1=【s8+36】=输入的数值
0x0040195c <+184>: jal 0x4018a4 <fun7> #跳转回函数开头
0x00401960 <+188>: nop
0x00401964 <+192>: sll v0,v0,0x1 #左移一位
0x00401968 <+196>: addiu v0,v0,1 #末尾补1,v0=2*v0+1,即进入该节点的右孩子
0x0040196c <+200>: b 0x401978 <fun7+212>
0x00401970 <+204>: nop
0x00401974 <+208>: move v0,zero #v0=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.
这个函数就相当于是一个二叉搜索树,根据 v0 和 v1 的大小关系来确定下一节点是左孩子还是右孩子。如果v0大则下一节点是右孩子,v1大则下一节点是左节点,同时,每执行结束一个函数都会将 v0 向左移动一位,末位补 1.
- secret_phase:
0x00401a20 <+144>: nop
0x00401a24 <+148>: lw gp,16(s8)
0x00401a28 <+152>: move v1,v0 #v1=v0,即把fun7的返回值存到v1里
0x00401a2c <+156>: li v0,7 #v0=7,即二进制的111
0x00401a30 <+160>: beq v1,v0,0x401a44 <secret_phase+180> #比较v1和v0是否相等,相等就跳转,否则炸弹爆炸,由前文可知函数每执行依次,即每循环依次,都会将v0左移一位,末尾补1,v0初始值为0即000,函数结束后v0=7即111,故v0=000->001->011->111,则循环一共只能执行三次,否则炸弹爆炸
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
通过单步调试,可以得出这棵二叉搜索树为:
由于7为奇数,故111<-011<-001-<000,即从根节点开始,比较三次右子树,故输入的值应为1001
所以最后答案为1001