MIPS - 反汇编 - 拆炸弹 - bomb

前言

整理文档发现了之前的实验报告,鉴于从17级开始才开始使用MIPS实验环境,取实验报告精华,整理主要思路如下。该博客叙述风格参考了窦优秀学长的博客。
该博客旨在帮助入门分析,不对整个过程详细介绍。前三个炸弹较为详细。
课程为山东大学计算机专业大二上学期计算机系统原理实验。

作者信息

2018级山东大学计算机LL

GDB

  • 设置、查看、删除断点:
1
2
3
1.	b * 0x233666  
2. info b
3. delete 1
  • 查看寄存器、变量中的值
1
2
1.	x $ra  # 与p *$ra 类似  
2. p $ra #打印参数

实验

先给出一些常用指令、寄存器和函数的说明:

1
2
3
4
1.	BEQZ RS, OFF18 IF RS = 0, PC = OFF18  
2. (如果寄存器中值为0,PC指针赋值为立即数)
3. BEQ RS, RT, OFF18 IF RS = RT, PC = OFF18±
4. SLTI RD, RS, CONST16 RD = (RS± < CONST16±) ? 1 : 0

炸弹一

虽然<phase_1>的拆除思路在指导书中已大致给出,下面还是对该炸弹进行简要分析。结合对MIPS寄存器的了解(详细分析见结论分析与体会),可知$a0 and $a1寄存器通常用来函数参数的传递。在<phase_1>中调用了函数<strings_not_equal>,并在调用前对$a0 and $a1寄存器进行修改。分析汇编带代码:
结束<strings_not_equal>后,判断v0值是否为0,若为0正常结束函数,若不为0执行<explode_bomb>,即炸弹爆炸。
因此,该炸弹即读入字符串和一预设字符串相同即可拆除。通过GDB工具在400d8c地址处查看$a1寄存器指向地址中的值 x /s $a1,可知该字符串为Let’s begin now!。读入时输入该字符串,即可正常拆除炸弹。

炸弹二

进入第二个炸弹时调用了<read_six_numbers>函数,可以知道该函数读入的为6个数字。
循环结束后执行下述指令:

1
2
3
4
5
1.	400d8c: 0c10073e    jal  401cf8 <strings_not_equal>  
2. 400d90: 00000000 nop
3. 400d94: 10400003 beqz v0,400da4 <phase_1+0x38>
4. 400d98: 00000000 nop
5. 400d9c: 0c10087c jal 4021f0 <explode_bomb>

在这里插入图片描述

分析可知循环会进行5次。
在每层循环中均有一个<explode_bomb> ,即会进行5次数的判断。且每次判断$a0和$v0寄存器中的值是否相同。

炸弹三

进入函数后,会先执行如下汇编语句:

1
2
3
1.	400f2c: 8fdc0018    lw  gp,24(s8)      # gp = M[s8+24]  
2. 400f30: 28420003 slti v0,v0,3 # v0 = (v0<3)?1:0
3. 400f34: 10400004 beqz v0,400f48 <phase_3+0x74>

其为判断函数读入的参数是否大于三个(实际只需要前三个)
$v0的值为44(s8),猜测其为读入的一个参数,且不能比8小。之后会根据这个值跳转到一个地址:

1
2
3
4
5
6
7
1.	400f5c: 00021880    sll v1,v0,0x2  
2. 400f60: 3c020040 lui v0,0x40
3. 400f64: 2442278c addiu v0,v0,10124
4. 400f68: 00621021 addu v0,v1,v0
5. 400f6c: 8c420000 lw v0,0(v0)
6. 400f70: 00000000 nop
7. 400f74: 00400008 jr v0

分析第一个数和第三个数应为数字,第二个数为字符。(233,将‘2’赋值给第二个参数,33赋给第3个参数)。的第三个参数判断与为学号最后一位相乘判断是不是与当前选参数相同。 777
如学号最后一位为7,应该为0 q 111

炸弹四

与炸弹3类似,开始时程序会先判断读入的是否为一个数字。

1
2
3
1.	401310: 00401821    move    v1,v0  
2. 401314: 24020001 li v0,1
3. 401318: 14620005 bne v1,v0,401330 <phase_4+0x74>

这里如果输入的第一个数是数字则v1为1,若为字符则v1为0。之后程序会判但学号最后一位是奇数还是偶数。
这里某Y大佬直接看出来是斐波那契,问他为什么,因为递归 -1 -2 ,直接秒出,绝了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.	40125c: 8fc20028    lw  v0,40(s8)  
2. 401260: 00000000 nop
3. 401264: 2442ffff addiu v0,v0,-1
4. 401268: 00402021 move a0,v0
5. 40126c: 0c10048c jal 401230 <func4>
6. 401270: 00000000 nop
7. 401274: 00408021 move s0,v0
8. 401278: 8fc20028 lw v0,40(s8)
9. 40127c: 00000000 nop
10. 401280: 2442fffe addiu v0,v0,-2
11. 401284: 00402021 move a0,v0
12. 401288: 0c10048c jal 401230 <func4>
13. 40128c: 00000000 nop
14. 401290: 02021021 addu v0,s0,v0

会递归的调用fun4($v0 – 1)和 fun4($v0-2)。并将这两个的返回结果相加存储在$v0中。其显然为斐波那契的调用方式。读入数位i是对应的数字是f[i+1](func4索引从零开始,斐波那契索引从1开始)。
奇数情况下会判断函数返回结果是否为8,偶数判断是否为13。则学号末尾为奇数应该输入5,为偶数应该输入6。

炸弹五

在这里插入图片描述

在这里插入图片描述

目标值位giants,根据对应关系可构造字符串位opekma。

炸弹六

在这里插入图片描述

放张图吧:

在这里插入图片描述

隐藏炸弹

入口

通过查看反汇编代码可以发现含有一个<secrect_phase>,进一步查看反汇编代码可以发现其进入的入口在<phase_defused>。而每一个炸弹调用完都会在主函数调用一次<phase_defused>。

1
2
1.	402288: 24020006    li  v0,6  
2. 40228c: 14620039 bne v1,v0,402374 <phase_defused+0x110>

查看代码可知,<phase_defused>仅在最后一个炸弹结束后才会往下进行判断,1-5炸弹中不会进入隐藏炸弹。

phase_defused

可以看到,目标字符串为austinpowers,即第四个炸弹出输入 5 austinpowers 即可进入隐藏炸弹。
也是放张图吧:

二叉树啊

答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 第一组
20180000777
Let's begin now!
1 7 49 343 0 0
0 q 111
5 austinpowers
opekma
4 2 6 3 1 5
1001
# 第二组
123456111132
Let’s begin now!
1 2 6 6 6 6
4 o 114
5 austinpowers
opekma
5 1 3 6 2 4

附录

MIPS常用寄存器

($v0-$v1)用于子程序的非浮点结果或返回值。
($a0-$a3)用来传递前四个参数给子程序,不够的用堆栈。$a0-$a3和$v0-$v1以及$ra一起来支持子程序/过程调用,分别用以传递参数,返回结果和存放返回地址。当需要使用更多的寄存器时,就需要堆栈(stack)。
($gp)为了简化静态数据的访问,MIPS软件保留了一个寄存器:全局指针gp,全局指针只想静态数据区中的运行时决定的地址。
($sp)指向当前正在操作的堆栈顶部。
($ra)在函数调用过程中,保持子函数返回后的指令地址。
($s8)也称为($fp)。不同编译器对其解释不同,在龙芯里更像传递给子函数的帧指针,这里不做讨论。

实验环境搭建

为了更好的的完成实验,我在自己电脑上搭建了MIPS指令集环境。可以采用两种策略,一种是qemu-user,一种是qemu-system。如果说qemu-system是模拟mips的操作系统,qemu-user更像是模拟mips的指令集。为了更快的响应时间,我选择了qemu-user。
并且采用了Ubuntu18.04操作系统。GDB采用了gdb-multiarch。