拆除二进制炸弹
Writed by 20.4 隋春雨文章目录1. 第一关2. 第二关3. 第三关4. 第四关5. 第五关6. 第六关参考文章:常见指令MIPS指令官方文档gdb调试stack如何保存现场?常见寄存器:1. 第一关从beqz指令那里可以看出,如果是字符串相等的话,那么我们就直接跳到0x400da4,可以看到0x00400d9c是炸弹爆炸的地方,所以这一关的要点就变成了,查出原来的函数中保存的stri
Wrote by CS 20.4 隋春雨
参考文章:
常见指令
stack如何保存现场?
常见寄存器:
1. 第一关
从beqz
指令那里可以看出,如果是字符串相等的话,那么我们就直接跳到
0x400da4
,可以看到0x00400d9c
是炸弹爆炸的地方,所以这一关的要点就变成了,查出原来的函数中保存的string
的内容,然后输入即可
本来我最开始看到这个\
就不太理解,后来用pycharm输出了一下'
的asc码
发现就是39,才发现是哪个'
太小了没看到,所以最终拆除炸弹的结果就是Let's begin now!
PS:
GDB中X的用法
x /16c $a0
类似于事后诸葛亮了,因为这是我们在知道这个结果是16位的情况下,才能x /16c $a0
,才平时做实验的时候,可以多扩展几位看一看结果
2. 第二关
li
$v0
:子函数调用返回的结果
参考了一下
关于mips-fp(帧指针)寄存器的理解
和
MIPS汇编角度看C语言的指针
和
mips中gp寄存器的用法
简单来说,用fp
保存栈底的位置
fp:
栈基址寄存器
值得注意的是:
0x00400dc0 <+4>: sw ra,60(sp)
这条指令,按照mips
官方文档的解释,是这样的
个人感觉可能用lw
更合适一点,为了验证一下是否保存的是返回地址,找了一下资料
可以看出作者认为,它保存的确实是返回地址。。。。Ok。。。。。那就先这样
对于move
指令,查找了一下资料,
可以发现move $a0 $a1
的含义就是把$a1
的值送到$a0
寄存器
思考:为什么
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>
0x00400dec <+48>: nop
0x00400df0 <+52>: lw gp,16(s8)
0x00400df4 <+56>: lw v1,28(s8)
0x00400df8 <+60>: li v0,1
0x00400dfc <+64>: beq v1,v0,0x400e10 <phase_2+84>
0x00400e00 <+68>: nop
0x00400e04 <+72>: jal 0x4021f0 <explode_bomb>
0x00400e08 <+76>: nop
0x00400e0c <+80>: lw gp,16(s8)
0x00400e10 <+84>: li v0,1
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
每条指令之间的间隔都是4呢?
解决:查了一下MIPS
指令的构成发现,无论是R、I、J
型指令,都是
32位的,4个字节,所以MIPS
指令的间隔都是4
同时查了一下beqz
指令的用法
beqz $v location
:若$v
寄存器的值为0,那么程序跳转到location
在实验二中的0x00400dfc <+64>: beq v1,v0,0x400e10 <phase_2+84>
指令中
如果$v1==$v0
,那么跳转到0x400e10
,不会爆炸。
如果没有,那么一定会因为0x00400e04 <+72>: jal 0x4021f0 <explode_bomb>
而爆炸
到这里的时候,总是遇到
---Type <return> to continue, or q <return> to quit---q
的问题,查了一下,发现设置一下不分页显示就好了
set pagination off
Dump of assembler code for function phase_2:
0x00400dbc <+0>: addiu sp,sp,-64
0x00400dc0 <+4>: sw ra,60(sp)
0x00400dc4 <+8>: sw s8,56(sp)
0x00400dc8 <+12>: move s8,sp
0x00400dcc <+16>: lui gp,0x42
0x00400dd0 <+20>: addiu gp,gp,-20080
0x00400dd4 <+24>: sw gp,16(sp)
0x00400dd8 <+28>: sw a0,64(s8)
0x00400ddc <+32>: addiu v0,s8,28
0x00400de0 <+36>: lw a0,64(s8)
0x00400de4 <+40>: move a1,v0
0x00400de8 <+44>: jal 0x401ba8 <read_six_numbers>
0x00400dec <+48>: nop
0x00400df0 <+52>: lw gp,16(s8)
0x00400df4 <+56>: lw v1,28(s8)
0x00400df8 <+60>: li v0,1
0x00400dfc <+64>: beq v1,v0,0x400e10 <phase_2+84>
0x00400e00 <+68>: nop
0x00400e04 <+72>: jal 0x4021f0 <explode_bomb>
0x00400e08 <+76>: nop
0x00400e0c <+80>: lw gp,16(s8)
0x00400e10 <+84>: li v0,1
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//v1保存的起始位置
0x00400e34 <+120>: addu v0,v1,v0//找到上一个元素的位置
0x00400e38 <+124>: lw a0,4(v0)//当前的数组中的元素放到了$a0
0x00400e3c <+128>: li v1,12
0x00400e40 <+132>: lw v0,24(s8)//循环次数
0x00400e44 <+136>: nop
0x00400e48 <+140>: subu v0,v1,v0//$v0=12-循环次数
0x00400e4c <+144>: lw v1,-32660(gp)//$v1=$gp-32660
0x00400e50 <+148>: sll v0,v0,0x2//$v0=(12-循环次数)*4
0x00400e54 <+152>: addu v0,v1,v0//$v0=$gp-32660+(12-循环次数)*4
0x00400e58 <+156>: lw v0,0(v0)//$v0=Memory[$v0]
0x00400e5c <+160>: nop
0x00400e60 <+164>: mult a0,v0
0x00400e64 <+168>: mflo a0 //这里是a0最后一次被改变的位置
0x00400e68 <+172>: lw v0,24(s8)//这里是v0被改变的有效位置,$v0=循环次数
0x00400e6c <+176>: nop
0x00400e70 <+180>: sll v0,v0,0x2//$v0=循环次数*4
0x00400e74 <+184>: addiu v1,s8,24//$v1=2147479848
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
End of assembler dump.
其中对于
0x00400e18 <+92>: b 0x400ea8 <phase_2+236>
:猜也能猜到跳转到
0x400ea8 <phase_2+236>
,查了一下官方文档
发现确实是这样
然后我想像vs那样单步查看变量的值,于是查询了一下单步执行的命令
ni
例子:
在第一个值得怀疑的地方设置一个断点
p $v0
发现是1
,当然从上一行的指令中我们也能看到。所以第一个数字一定是1
然后一步一步使用ni
指令一步一步走,如下图
然后碰到了0x00400eb0 <+244>: slti v0,v0,6
查了一下
这条指令的含义是if $v0<6,then $v0=1,else $v0=0
继续就这么执行,直到跳转到了0x00400e20 <+100>: lw v0,24(s8)
我们之前手动分析的时候,这个地方保存的值就是1
,如果不放心的话可以打印看一看
(gdb) x/1ub $s8+24
结果
继续执行,发现程序总是在调用
$gp-32660
相当于基址
打印一下发现
这正好是我输入的ID-number
就 这么一步一步拆,最后发现结果是
170000
,当然这组数据可能跟我取的值有关系,我输入的ID-number=996007
,
结果:
3. 第三关
Dump of assembler code for function phase_3:
0x00400ed4 <+0>: addiu sp,sp,-56
0x00400ed8 <+4>: sw ra,52(sp)
0x00400edc <+8>: sw s8,48(sp)
0x00400ee0 <+12>: move s8,sp
0x00400ee4 <+16>: lui gp,0x42
0x00400ee8 <+20>: addiu gp,gp,-20080
0x00400eec <+24>: sw gp,24(sp)
0x00400ef0 <+28>: sw a0,56(s8)
0x00400ef4 <+32>: lw a0,56(s8)
0x00400ef8 <+36>: lui v0,0x40
0x00400efc <+40>: addiu a1,v0,10112
0x00400f00 <+44>: addiu v1,s8,44
0x00400f04 <+48>: addiu v0,s8,40
0x00400f08 <+52>: addiu a2,s8,36
0x00400f0c <+56>: sw a2,16(sp)
0x00400f10 <+60>: move a2,v1
0x00400f14 <+64>: move a3,v0
0x00400f18 <+68>: lw v0,-32636(gp)
0x00400f1c <+72>: nop
0x00400f20 <+76>: move t9,v0
0x00400f24 <+80>: jalr t9
0x00400f28 <+84>: nop
0x00400f2c <+88>: lw gp,24(s8)
0x00400f30 <+92>: slti v0,v0,3
0x00400f34 <+96>: beqz v0,0x400f48 <phase_3+116>
0x00400f38 <+100>: nop
0x00400f3c <+104>: jal 0x4021f0 <explode_bomb>
0x00400f40 <+108>: nop
0x00400f44 <+112>: lw gp,24(s8)
0x00400f48 <+116>: lw v0,44(s8)
0x00400f4c <+120>: nop
0x00400f50 <+124>: sltiu v1,v0,8
0x00400f54 <+128>: beqz v1,0x401190 <phase_3+700>
0x00400f58 <+132>: nop
0x00400f5c <+136>: sll v1,v0,0x2
0x00400f60 <+140>: lui v0,0x40
0x00400f64 <+144>: addiu v0,v0,10124
0x00400f68 <+148>: addu v0,v1,v0
0x00400f6c <+152>: lw v0,0(v0)
0x00400f70 <+156>: nop
0x00400f74 <+160>: jr v0
0x00400f78 <+164>: nop
0x00400f7c <+168>: li v0,113
0x00400f80 <+172>: sb v0,32(s8)
0x00400f84 <+176>: lw v0,-32660(gp)
0x00400f88 <+180>: nop
0x00400f8c <+184>: lw v1,44(v0)
0x00400f90 <+188>: lw v0,36(s8)
0x00400f94 <+192>: nop
0x00400f98 <+196>: mult v1,v0
0x00400f9c <+200>: mflo v1
0x00400fa0 <+204>: li v0,777
0x00400fa4 <+208>: beq v1,v0,0x4011ac <phase_3+728>
0x00400fa8 <+212>: nop
0x00400fac <+216>: jal 0x4021f0 <explode_bomb>
0x00400fb0 <+220>: nop
0x00400fb4 <+224>: lw gp,24(s8)
0x00400fb8 <+228>: b 0x4011f8 <phase_3+804>
0x00400fbc <+232>: nop
0x00400fc0 <+236>: li v0,98
0x00400fc4 <+240>: sb v0,32(s8)
0x00400fc8 <+244>: lw v0,-32660(gp)
0x00400fcc <+248>: nop
0x00400fd0 <+252>: lw v1,44(v0)
0x00400fd4 <+256>: lw v0,36(s8)
0x00400fd8 <+260>: nop
0x00400fdc <+264>: mult v1,v0
0x00400fe0 <+268>: mflo v1
0x00400fe4 <+272>: li v0,214
0x00400fe8 <+276>: beq v1,v0,0x4011b8 <phase_3+740>
0x00400fec <+280>: nop
0x00400ff0 <+284>: jal 0x4021f0 <explode_bomb>
0x00400ff4 <+288>: nop
0x00400ff8 <+292>: lw gp,24(s8)
0x00400ffc <+296>: b 0x4011f8 <phase_3+804>
0x00401000 <+300>: nop
0x00401004 <+304>: li v0,98
0x00401008 <+308>: sb v0,32(s8)
0x0040100c <+312>: lw v0,-32660(gp)
0x00401010 <+316>: nop
0x00401014 <+320>: lw v1,44(v0)
0x00401018 <+324>: lw v0,36(s8)
0x0040101c <+328>: nop
0x00401020 <+332>: mult v1,v0
0x00401024 <+336>: mflo v1
0x00401028 <+340>: li v0,755
0x0040102c <+344>: beq v1,v0,0x4011c4 <phase_3+752>
0x00401030 <+348>: nop
0x00401034 <+352>: jal 0x4021f0 <explode_bomb>
0x00401038 <+356>: nop
0x0040103c <+360>: lw gp,24(s8)
0x00401040 <+364>: b 0x4011f8 <phase_3+804>
0x00401044 <+368>: nop
0x00401048 <+372>: li v0,107
0x0040104c <+376>: sb v0,32(s8)
0x00401050 <+380>: lw v0,-32660(gp)
0x00401054 <+384>: nop
0x00401058 <+388>: lw v1,44(v0)
0x0040105c <+392>: lw v0,36(s8)
0x00401060 <+396>: nop
0x00401064 <+400>: mult v1,v0
0x00401068 <+404>: mflo v0
0x0040106c <+408>: beqz v0,0x4011d0 <phase_3+764>
0x00401070 <+412>: nop
0x00401074 <+416>: jal 0x4021f0 <explode_bomb>
0x00401078 <+420>: nop
0x0040107c <+424>: lw gp,24(s8)
0x00401080 <+428>: b 0x4011f8 <phase_3+804>
0x00401084 <+432>: nop
0x00401088 <+436>: li v0,111
0x0040108c <+440>: sb v0,32(s8)
0x00401090 <+444>: lw v0,-32660(gp)
0x00401094 <+448>: nop
0x00401098 <+452>: lw v1,44(v0)
0x0040109c <+456>: lw v0,36(s8)
0x004010a0 <+460>: nop
0x004010a4 <+464>: mult v1,v0
0x004010a8 <+468>: mflo v1
0x004010ac <+472>: li v0,228
0x004010b0 <+476>: beq v1,v0,0x4011dc <phase_3+776>
0x004010b4 <+480>: nop
0x004010b8 <+484>: jal 0x4021f0 <explode_bomb>
0x004010bc <+488>: nop
0x004010c0 <+492>: lw gp,24(s8)
0x004010c4 <+496>: b 0x4011f8 <phase_3+804>
0x004010c8 <+500>: nop
0x004010cc <+504>: li v0,116
0x004010d0 <+508>: sb v0,32(s8)
0x004010d4 <+512>: lw v0,-32660(gp)
0x004010d8 <+516>: nop
0x004010dc <+520>: lw v1,44(v0)
0x004010e0 <+524>: lw v0,36(s8)
0x004010e4 <+528>: nop
0x004010e8 <+532>: mult v1,v0
0x004010ec <+536>: mflo v1
0x004010f0 <+540>: li v0,513
0x004010f4 <+544>: beq v1,v0,0x4011e8 <phase_3+788>
0x004010f8 <+548>: nop
0x004010fc <+552>: jal 0x4021f0 <explode_bomb>
0x00401100 <+556>: nop
0x00401104 <+560>: lw gp,24(s8)
0x00401108 <+564>: b 0x4011f8 <phase_3+804>
0x0040110c <+568>: nop
0x00401110 <+572>: li v0,118
0x00401114 <+576>: sb v0,32(s8)
0x00401118 <+580>: lw v0,-32660(gp)
0x0040111c <+584>: nop
0x00401120 <+588>: lw v1,44(v0)
0x00401124 <+592>: lw v0,36(s8)
0x00401128 <+596>: nop
0x0040112c <+600>: mult v1,v0
0x00401130 <+604>: mflo v1
0x00401134 <+608>: li v0,780
0x00401138 <+612>: beq v1,v0,0x40114c <phase_3+632>
0x0040113c <+616>: nop
0x00401140 <+620>: jal 0x4021f0 <explode_bomb>
0x00401144 <+624>: nop
0x00401148 <+628>: lw gp,24(s8)
0x0040114c <+632>: li v0,98
0x00401150 <+636>: sb v0,32(s8)
0x00401154 <+640>: lw v0,-32660(gp)
0x00401158 <+644>: nop
0x0040115c <+648>: lw v1,44(v0)
0x00401160 <+652>: lw v0,36(s8)
0x00401164 <+656>: nop
0x00401168 <+660>: mult v1,v0
0x0040116c <+664>: mflo v1
0x00401170 <+668>: li v0,824
0x00401174 <+672>: beq v1,v0,0x4011f4 <phase_3+800>
0x00401178 <+676>: nop
0x0040117c <+680>: jal 0x4021f0 <explode_bomb>
0x00401180 <+684>: nop
0x00401184 <+688>: lw gp,24(s8)
0x00401188 <+692>: b 0x4011f8 <phase_3+804>
0x0040118c <+696>: nop
0x00401190 <+700>: li v0,120
0x00401194 <+704>: sb v0,32(s8)
0x00401198 <+708>: jal 0x4021f0 <explode_bomb>
0x0040119c <+712>: nop
0x004011a0 <+716>: lw gp,24(s8)
0x004011a4 <+720>: b 0x4011f8 <phase_3+804>
0x004011a8 <+724>: nop
0x004011ac <+728>: nop
0x004011b0 <+732>: b 0x4011f8 <phase_3+804>
0x004011b4 <+736>: nop
0x004011b8 <+740>: nop
0x004011bc <+744>: b 0x4011f8 <phase_3+804>
0x004011c0 <+748>: nop
0x004011c4 <+752>: nop
0x004011c8 <+756>: b 0x4011f8 <phase_3+804>
0x004011cc <+760>: nop
0x004011d0 <+764>: nop
0x004011d4 <+768>: b 0x4011f8 <phase_3+804>
0x004011d8 <+772>: nop
0x004011dc <+776>: nop
0x004011e0 <+780>: b 0x4011f8 <phase_3+804>
0x004011e4 <+784>: nop
0x004011e8 <+788>: nop
0x004011ec <+792>: b 0x4011f8 <phase_3+804>
0x004011f0 <+796>: nop
0x004011f4 <+800>: nop
0x004011f8 <+804>: lb v0,40(s8)
0x004011fc <+808>: lb v1,32(s8)
0x00401200 <+812>: nop
0x00401204 <+816>: beq v1,v0,0x401218 <phase_3+836>
0x00401208 <+820>: nop
0x0040120c <+824>: jal 0x4021f0 <explode_bomb>
0x00401210 <+828>: nop
0x00401214 <+832>: lw gp,24(s8)
0x00401218 <+836>: move sp,s8
0x0040121c <+840>: lw ra,52(sp)
0x00401220 <+844>: lw s8,48(sp)
0x00401224 <+848>: addiu sp,sp,56
0x00401228 <+852>: jr ra
0x0040122c <+856>: nop
End of assembler dump.
做实验的时候发生了很多问题,比如说,输出寄存器的值没有得到预期的结果,后来发现,是x的用法有错误,sw
指令,读取的时候应该读出来的是一个word
而不是byte
,像以下的结果就是正确的,输入一个byte
,读取一个byte
在拆除实验三的时候,我跟我数学建模的队(lxr巨巨)发现了一个问题,
在这条指令中,我们执行后输出$v0
发现是2137465136
,但是x/1ub $gp-32636
发现却是48
,最后经过一个形势政策的讨论,也没有结果
经过顽强的debug发现是输出格式的错误,应该是x/1uw %gp-32636
,这样就是2137465136
了,确认过眼神(下次一定好好看输出格式)
我输入的是1 4 6,但是因为它是按照字符读取的,所以我们输出它的4
的 asc
可以发现,是一样的,同时我们观察stack
中保存变量的顺序,正好从低到高
做到这里我产生了一个疑问,如何知道s8
和sp
中间的东西都是什么?
于是查了一下,
fp和sp的相关知识
简单来说,fp
和sp
都是保存的调用她的函数的栈底和栈顶
然后我继续看汇编代码,发现
这一行其实是令$v0=Memory[$v0]
,同时结合上下文我们推断出来$v0=0x400000+10124+a[0]*4
,其中a[0]
是我们输入的数字的第一位,那么这样以来,实验平台就能检测出来我们输入的第一个数字是什么了
我们调用一下寄存器
发现$v0
保存的是一个地址
那么问题来了,如果我们输入的第一位数字不是1而是别的数字会怎么办?
打印输出了10个值,结果发现正符合预期,最后2个乱码是因为8 9是UI直接爆炸,根本不会执行到这里
我们检测一下,
发现正好有(当然也必须有),同时看上下文,这个地址后面也只有一个li
指令,符合我们的预期
看到这几行指令,我认为那里保存的是我输入的数据,输出一看,确实是ID-num
,同时温馨提示,尽量不要选择特别奇怪的数字,比如连着2个0,你很有可能理解为那个是未初始化的内存单元的值
在分析的过程中,观察这段代码,最终发现这段其实就做了两件事情
1.将v0
寄存器的值放到了Memory[$s8+32]
2. ID-num
的最后一位和输入的最后一个数字相乘 ,最终和$v0
比较是否相等,若相等,则跳转,否则爆炸。因为我输入的学号的最后一位是7
,所以需要找到一个7的倍数的$v0
,最终发现是第一个函数段,所以我们输入的第一个数字应该是0
最终我们成功跳转到了
从上文的分析中,我们知道Memory[$s8+40]
保存的是输入的第2个参数,而Memory[$s8+32]
保存的是113
经过分析得出,这个113其实表示的是一个字符,所以我们第2个输入q
即可
4. 第四关
碰到的难点如下
func4
做了什么?我们看一下func4
的代码
Dump of assembler code for function func4:
0x00401230 <+0>: addiu sp,sp,-40
0x00401234 <+4>: sw ra,36(sp)
0x00401238 <+8>: sw s8,32(sp)
0x0040123c <+12>: sw s0,28(sp)
0x00401240 <+16>: move s8,sp
0x00401244 <+20>: sw a0,40(s8)
0x00401248 <+24>: lw v0,40(s8)
0x0040124c <+28>: nop
0x00401250 <+32>: slti v0,v0,2
0x00401254 <+36>: bnez v0,0x40129c <func4+108>
0x00401258 <+40>: nop
0x0040125c <+44>: lw v0,40(s8)
0x00401260 <+48>: nop
0x00401264 <+52>: addiu v0,v0,-1
0x00401268 <+56>: move a0,v0
0x0040126c <+60>: jal 0x401230 <func4>
0x00401270 <+64>: nop
0x00401274 <+68>: move s0,v0
0x00401278 <+72>: lw v0,40(s8)
0x0040127c <+76>: nop
0x00401280 <+80>: addiu v0,v0,-2
0x00401284 <+84>: move a0,v0
0x00401288 <+88>: jal 0x401230 <func4>
0x0040128c <+92>: nop
0x00401290 <+96>: addu v0,s0,v0
0x00401294 <+100>: b 0x4012a0 <func4+112>
0x00401298 <+104>: nop
0x0040129c <+108>: li v0,1
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
End of assembler dump.
我们先看这么几个关键点
看这一段汇编代码,我们可以很明显的看出,这一段调用了func4($v0-1)
再看
它调用了func($v0-2)
这是什么?这不是斐波那契数列吗?!
再看这一段
果然
所以接下来的问题就变成了,斐波那契的第几项是8
我们打表看一下
所以答案很明显,是5
5. 第五关
0x004013e8 <+0>: addiu sp,sp,-72
0x004013ec <+4>: sw ra,68(sp)
0x004013f0 <+8>: sw s8,64(sp)
0x004013f4 <+12>: move s8,sp
0x004013f8 <+16>: sw a0,72(s8)
0x004013fc <+20>: lw a0,72(s8)
0x00401400 <+24>: jal 0x401c78 <string_length>
0x00401404 <+28>: nop
0x00401408 <+32>: move v1,v0
0x0040140c <+36>: li v0,6
0x00401410 <+40>: beq v1,v0,0x401420 <phase_5+56>
0x00401414 <+44>: nop
0x00401418 <+48>: jal 0x4021f0 <explode_bomb>
0x0040141c <+52>: nop
0x00401420 <+56>: sw zero,24(s8)
0x00401424 <+60>: b 0x4014a8 <phase_5+192>
0x00401428 <+64>: nop
0x0040142c <+68>: lw v0,24(s8)
0x00401430 <+72>: lw v1,24(s8)
0x00401434 <+76>: lw a0,72(s8)
0x00401438 <+80>: nop
0x0040143c <+84>: addu v1,a0,v1
0x00401440 <+88>: lb v1,0(v1)
0x00401444 <+92>: nop
0x00401448 <+96>: andi v1,v1,0xff
0x0040144c <+100>: andi v1,v1,0xf
0x00401450 <+104>: sll v0,v0,0x2
0x00401454 <+108>: addiu a0,s8,24
0x00401458 <+112>: addu v0,a0,v0
0x0040145c <+116>: sw v1,12(v0)
0x00401460 <+120>: lw a0,24(s8)
0x00401464 <+124>: lw v0,24(s8)
0x00401468 <+128>: nop
0x0040146c <+132>: sll v0,v0,0x2
0x00401470 <+136>: addiu v1,s8,24
0x00401474 <+140>: addu v0,v1,v0
0x00401478 <+144>: lw v1,12(v0)
0x0040147c <+148>: lui v0,0x41
0x00401480 <+152>: addiu v0,v0,12524
0x00401484 <+156>: addu v0,v1,v0
0x00401488 <+160>: lb v1,0(v0)
0x0040148c <+164>: addiu v0,s8,24
0x00401490 <+168>: addu v0,v0,a0
0x00401494 <+172>: sb v1,4(v0)
0x00401498 <+176>: lw v0,24(s8)
0x0040149c <+180>: nop
0x004014a0 <+184>: addiu v0,v0,1
0x004014a4 <+188>: sw v0,24(s8)
0x004014a8 <+192>: lw v0,24(s8)
0x004014ac <+196>: nop
0x004014b0 <+200>: slti v0,v0,6
0x004014b4 <+204>: bnez v0,0x40142c <phase_5+68>
0x004014b8 <+208>: nop
0x004014bc <+212>: sb zero,34(s8)
0x004014c0 <+216>: addiu v0,s8,28
0x004014c4 <+220>: move a0,v0
0x004014c8 <+224>: lui v0,0x40
0x004014cc <+228>: addiu a1,v0,10160
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
首先我们要看一下string_length
的代码,我们大体猜测,这个函数应该是计算我们输入的字符串的长度,不过还需要看看代码验证一下
0x00401c78 <+0>: addiu sp,sp,-24
0x00401c7c <+4>: sw s8,20(sp)
0x00401c80 <+8>: move s8,sp
0x00401c84 <+12>: sw a0,24(s8)
0x00401c88 <+16>: lw v0,24(s8)
0x00401c8c <+20>: nop
0x00401c90 <+24>: sw v0,12(s8)
0x00401c94 <+28>: sw zero,8(s8)
0x00401c98 <+32>: b 0x401cb0 <string_length+56>
0x00401c9c <+36>: nop
0x00401ca0 <+40>: lw v0,8(s8)
0x00401ca4 <+44>: nop
0x00401ca8 <+48>: addiu v0,v0,1
0x00401cac <+52>: sw v0,8(s8)
0x00401cb0 <+56>: lw v0,12(s8)
0x00401cb4 <+60>: nop
0x00401cb8 <+64>: lb v0,0(v0)
0x00401cbc <+68>: nop
0x00401cc0 <+72>: sltu v0,zero,v0
0x00401cc4 <+76>: andi v0,v0,0xff
0x00401cc8 <+80>: lw v1,12(s8)
0x00401ccc <+84>: nop
0x00401cd0 <+88>: addiu v1,v1,1
0x00401cd4 <+92>: sw v1,12(s8)
0x00401cd8 <+96>: bnez v0,0x401ca0 <string_length+40>
0x00401cdc <+100>: nop
0x00401ce0 <+104>: lw v0,8(s8)
0x00401ce4 <+108>: move sp,s8
0x00401ce8 <+112>: lw s8,20(sp)
0x00401cec <+116>: addiu sp,sp,24
0x00401cf0 <+120>: jr ra
0x00401cf4 <+124>: nop
End of assembler dump.
做实验的时候,遇到了这行代码,不太理解,翻了一下指令集手册,发现这样
本来以为,这是个寄存器值和立即数的比较,但是看了一下,发现是两个寄存器,我就不太理解,后来恍然大悟,这个 zero
有没有可能是0
号寄存器?!
于是输出了一下,果然是
当然,这个$zero
保存的一直都是0
继续分析string_length
的代码
因为lb
中()
的参数一定是一个地址,所以我们往前找,
我们推测$s8+12
的值也一定是一个地址
继续往下看
这行代码其实是最令我无法理解的,我最开始以为,难道它的循环次数是从负数开始?
其次,那他怎么判断循环终结?(当然,最后我都搞懂了这些东西)
不过分析还是要一步一步来
我们推测,$v1
才是保存的循环次数,因为他有很明显的+1
并且保存到了$s8+12
,那么原来的$v0
就肯定不是了,并且我们结合上下文也能发现,$v0
中的值并没有保存,所以我们推测,v0
中的保存的值有别的含义
我们想,如果它读取结束了,那么它一定会退出,也就是
那么我们往前看
什么时候$v0
会等于0
呢?
我们突然想起,上一个实验的113
其实是q
的asc
码
那么这个0
是什么?
这个不是'\0'
的asc
码吗?!
我们输出一下看看
所以这样,这个函数就完全搞清除了
对于第5关,我的输入是half
对于这几行代码,我的猜测就是$a0
中保存的地址存放着输入的字符串,
输出一看
确实是
在做实验的时候,也遇到这样的错误
我想进入string_length
函数单步调试,但是使用ni
直接跳过,使用step
直接爆炸,后来发现,应该使用si
命令(或者stepi
)
同时,对于一个函数,如果被调用了很多次,我们不应该直接在那个位置上设置断电,在它前面的某个位置设置一个断点,然后si
即可
否侧一直c
会很麻烦
同时
我最开始读这段代码的时候没有搞懂,为什么v0
中存放的是保存的循环次数,你刚开始就+1然后保存,不怕读取到了'\0'
吗?
后来仔细阅读发现,这个+1
加的其实是上一次读取到的,如果上一次读取到了'\0'
那么它根本不会跳转到这里!
它就直接退出了!
所以它保存的一定是循环次数!也就是,字符串的长度!
然后继续往下看
不难猜出来,Memory[$s8+24]
保存的是for
循环的执行次数
从这两行代码中,我们可以才出来,a0
保存的是一个内存地址
那么我们输出一下
是我们输入的字符串
所以以下这两行的代码的作用就明晰了
22
行是用来保存当前读取到的位置
v1
用来保存读取出来的字符
所以
0x00401448 <+96>: andi v1,v1,0xff
0x0040144c <+100>: andi v1,v1,0xf//字符的asc和0xf相与
那我们输出一下我们输入的字符 的asc
和0xf
相与之后的结果
那么我们在虚拟机上看一看,第一位字符处理之后的结果是不是3
果然
并且我们知道
那么我们来调用一下看看
对于
$v1=Memory[$s8+36+4*循环次数],
读取到的字符asc
和0xf
相与的结果
查看寄存器
果然
对于这一段,基本上看到就能猜到,这肯定是一个地址了,那么我们输出一下,最开始我的猜测是,这个可能是我们输入的信息之类的,比如学号或者你输入的字符串
but…
又因为
所以我们再次输出,发现是118
,猜测是某个字符的asc
输出一下验证
的确!也正好是我们推测的结果!
对于
0x00401494 <+172>: sb v1,4(v0)
我们知道,把$v1
储存到了循环次数在内存中的地址+循环次数
再检验一次
设置断点在0x0040144c <+100>: andi v1,v1,0xf//字符的asc和0xf相与
这里,输出一下register
我们之前用pycharm
输出了scytcl
的asc
如下
发现正确
事实上,形如这种的指令(虽然对解题无帮助,但是个人觉得还是很有必要理解的)
0x0040145c <+116>: sw v1,12(v0)
这种,修改了系统内存的东西(类似于c++
中修改类内私有变量的成员的值,曾经在这个点上WA
了无数次。。。。)
对于0x0040145c <+116>
的指令,验证第1
次循环如下(循环从0
计数)
对于0x00401478 <+144>: lw v1,12(v0)//$v1=Memory[$s8+36+4*循环次数]
指令,验证如下
对于0x00401488 <+160>: lb v1,0(v0)//一个固定的地址+读取到的字符asc和0xf相与的结果
验证如下
对于0x00401494 <+172>: sb v1,4(v0)//把$v1储存到了循环次数在内存中的地址+循环次数+4
验证如下
哦对了,验证了时候发生了一点小的事故,其实从我的输入中也能看出来错在了哪里,输出格式不正确(这个也是我数学建模的队友lxr巨巨跟我讨论了一节课的问题),观察一下汇编的输入
这个是store byte
不是store word
!!!
再然后就碰到了这么一个函数
Dump of assembler code for function strings_not_equal:
0x00401cf8 <+0>: addiu sp,sp,-48
0x00401cfc <+4>: sw ra,44(sp)
0x00401d00 <+8>: sw s8,40(sp)
0x00401d04 <+12>: move s8,sp
0x00401d08 <+16>: sw a0,48(s8)
0x00401d0c <+20>: sw a1,52(s8)
0x00401d10 <+24>: lw a0,48(s8)
0x00401d14 <+28>: jal 0x401c78 <string_length>
0x00401d18 <+32>: nop
0x00401d1c <+36>: sw v0,36(s8)
0x00401d20 <+40>: lw a0,52(s8)
0x00401d24 <+44>: jal 0x401c78 <string_length>
0x00401d28 <+48>: nop
0x00401d2c <+52>: sw v0,32(s8)
0x00401d30 <+56>: lw v0,48(s8)
0x00401d34 <+60>: nop
0x00401d38 <+64>: sw v0,28(s8)
0x00401d3c <+68>: lw v0,52(s8)
0x00401d40 <+72>: nop
0x00401d44 <+76>: sw v0,24(s8)
0x00401d48 <+80>: lw v1,36(s8)
0x00401d4c <+84>: lw v0,32(s8)
0x00401d50 <+88>: nop
0x00401d54 <+92>: beq v1,v0,0x401dc4 <strings_not_equal+204>
0x00401d58 <+96>: nop
0x00401d5c <+100>: li v0,1
0x00401d60 <+104>: b 0x401de4 <strings_not_equal+236>
0x00401d64 <+108>: nop
0x00401d68 <+112>: lw v0,28(s8)
0x00401d6c <+116>: nop
0x00401d70 <+120>: lb v1,0(v0)
0x00401d74 <+124>: lw v0,24(s8)
0x00401d78 <+128>: nop
0x00401d7c <+132>: lb v0,0(v0)
0x00401d80 <+136>: nop
0x00401d84 <+140>: xor v0,v1,v0
0x00401d88 <+144>: sltu v0,zero,v0
0x00401d8c <+148>: andi v0,v0,0xff
0x00401d90 <+152>: lw v1,28(s8)
0x00401d94 <+156>: nop
0x00401d98 <+160>: addiu v1,v1,1
0x00401d9c <+164>: sw v1,28(s8)
0x00401da0 <+168>: lw v1,24(s8)
0x00401da4 <+172>: nop
0x00401da8 <+176>: addiu v1,v1,1
0x00401dac <+180>: sw v1,24(s8)
0x00401db0 <+184>: beqz v0,0x401dc8 <strings_not_equal+208>
0x00401db4 <+188>: nop
0x00401db8 <+192>: li v0,1
0x00401dbc <+196>: b 0x401de4 <strings_not_equal+236>
0x00401dc0 <+200>: nop
0x00401dc4 <+204>: nop
0x00401dc8 <+208>: lw v0,28(s8)
0x00401dcc <+212>: nop
0x00401dd0 <+216>: lb v0,0(v0)
0x00401dd4 <+220>: nop
0x00401dd8 <+224>: bnez v0,0x401d68 <strings_not_equal+112>
0x00401ddc <+228>: nop
0x00401de0 <+232>: move v0,zero
0x00401de4 <+236>: move sp,s8
0x00401de8 <+240>: lw ra,44(sp)
0x00401dec <+244>: lw s8,40(sp)
0x00401df0 <+248>: addiu sp,sp,48
0x00401df4 <+252>: jr ra
0x00401df8 <+256>: nop
End of assembler dump.
我们看这一行
0x00401d5c <+100>: li v0,1
0x00401d60 <+104>: b 0x401de4 <strings_not_equal+236>
我们结合函数调用的上下文可以得出,如果这一行被执行了,那么一定会爆炸
如果很幸运,我们没有爆炸
那么我们继续往下看
毫无疑问,从内存中读取出来了一组数据,我们非常有理由怀疑
读取出来的是我们在phase_5
中经过处理后的数据或者是本来我们输入的数据
我们往前找一下,因为s8
很少改变
所以我们在phase_5
中找一找
我们发现
所以更坚定了我们认为这里读取到的是经过处理后的数据
我们再往下看
0x00401d84 <+140>: xor v0,v1,v0//按位或
0x00401db0 <+184>: beqz v0,0x401dc8 <strings_not_equal+208>
这两行其实是这个函数的精髓所在(虽然其实也没有多难)
如果$v0 xor $v1==0
,那么$v0==$v1
然后
最后
判断是否为0
,如果是,则跳转,否则$v0=1
,爆炸
所以到这里,我们猜测,是要进行处理后的字符串和给定的字符串进行比较,如果处理后的字符串和给定的字符串相同,那么不爆炸,否则爆炸
那么接下来的问题就变成了
- 它给定的字符串是什么?
- 找到那个转换函数,使得经过映射后的字符串等于给定的字符串
好,知道这些后,我们先来看一下上文中提到的,那个位置存储的是不是我们经过处理后的字符串
我们重新开
果然
我们再从
写到这里我突然产生了疑问,如果按位或的话,那岂不是最小值取决于给定的字符串?
于是百度了一下xor
发现是异或
但是我们的思路方向还是正确的,必须相等,否则爆炸
这里,查看一下给定的字符串
当然,我相信你从我的繁杂的输入中也能看到我错在了哪里
如果没有,请仔细阅读前文
输出这个字符串
可以看出
字符串为giants
(当然,如果只知道asc
其实不影响我们拆炸弹)
那么接下来我们就需要找到那个函数
经过分析,函数主要就是这一段
注释写的比较详细
分析可得,我们只需要找到是giants
在内存中的地址即可
好的,问题来了
我们需要查看多少内存空间?
ans:16
因为是输入的字符和0xf
相与,所以结果一定是0-e
,共16
个
所以我们接下来只需要寻找giants
在内存中的地址即可
我们从这几段代码中可以发现,我们先从一个偏移的位置找到了函数映射后的结果,然后将这个映射后的结果保存到了一个位置,也正是strings_not_equal
调用出来的位置
所以这样问题就化简成了,在那段地址中,寻找giants
可以发现,这段内存中的值包含giants
最终查找发现,偏移量是
15 0 5 12 14 1
那么我们需要输入什么呢?
很显然
a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8
i 9
j 10
k 11
l 12
m 13
n 14
o 15
p 0
q 1
r 2
s 3
t 4
u 5
v 6
w 7
x 8
y 9
z 10
最终结果opekma
6. 第六关
我们猜测$s8+36+4*$v0
保存的是我们输入的6个数字,输出一看,果然是
写到这里csdn的 编辑器已经无比的卡,简单说一下思路吧
首先它判定了一下你输入的6个数字是否处于(0,7)之间,并且必须是全排列
然后它看了一下你的学号的最后一位,是奇数还是偶数,
最后你发现它用了一个链表存储,需要在循环中找到单调不减或者单调不增加的一个序列
我的学号最后一位为奇数,所以是单调不减少的
最后结果就是426315
7. 隐藏关卡
更多推荐
所有评论(0)