山东大学计算机系统原理实验二进制炸弹拆除

实验要求

要求根据反汇编指令分析程序运行需要的参数,即需要正确的输入,以拆除 炸弹。根据通过的关卡数目评判最终的实验得分。

实验目的

(1) 熟悉 MIPS 指令集;

(2) 根据反汇编程序可以分析程序的功能和执行流程;

(3) 熟悉 GDB 调试工具,帮助程序理解。

实验软件和硬件环境

用 GDB 调试工具

操作系统:Linux Ubuntu

实验原理和方法

通过阅读MIPS 指令集找到炸弹爆炸的炸点,并绕开这个炸点避免炸弹,最后成功拆除炸弹。运用GDB调试工具,分析汇编代码,了解寄存器的内容,避免程序跳转到“explode_bomb”程序段的地方。阅读bomb.s文件,对拆除炸弹也有帮助。

实验步骤

准备工作:

首先打开一个终端启动qemu-mips

打开另一个终端利用gdb-multiarch进行调试,并依次输入

(gdb) set arch mips

(gdb) set endian little

(gdb) target remote localhost:12345

这样就可以继续利用gdb调试

拆除 phase_1

jal 0x401cf8 <strings_not_equal>表示跳转到函数<strings_not_equal>,在这设断点。beqz   v0,400da4 <phase_1+0x38>表示如果v0=0则跳转到炸弹2。

设置断点0x00400d90(其实在0x00400d8c),使用ni命令进行但不调试,使用 i r $a0 和 i r $a1 可以查看当前 a0 和a1 的寄存器状态。x /16c $a1查看从寄存器$a1开始的16个字节的内容,a0 里面保存着输入的字串。

由此可知应该输入字符串Let’s begin now!

拆除phase_2

说明要输入六个数,

要将v0与v1比较,在此处设置断点,用p $v1,p $v0查看,得v1是输入的数,要求v1和v0相等,所以第一个数是1。

此处造成了一个循环,

要想避免爆炸,要求a0和v0相等,在此处设断点,通过p $a0,p $v0查看,可得v0为输入的数,要求输入的数和a0的数相等,

所以输入为1 8 8 16 0 0

拆除phase_3

在bomb.s中找到炸弹3,应该输入整数、字符、整数。

slti v0,v0,3作用为,当v0小于3时v0为1,等于3时为0,如果想要跳过炸弹,不能让v0小于3,经过在此设置断点多次调试可知,v0为输入的数据的个数(炸点1)

跳转到phase_3+116

此时要求v0小于8,使v1为1,防止跳转到爆炸处。

查$s8+36~$s8+44存储的内容,为输入的三个数据,

可知v0处为第一个输入的值,66为输入的102的十六进制,所以第一个输入要小于8(炸点2)

jr v0表示跳转,跳转位置与第一个参数有关,通过x $v0查询

可知跳转到phase_3+632位置

读此处可知lw v0,36(s8)次数存的是输入的第三个参数,用p $v1查看得,v1为8,是学号的最后一位。相乘后v1=v1*v0。v0被赋值为824

同时要想避免爆炸,要使v1和v0相等,输入不同的第一个参数,v0为不同值,所以通过反复尝试,找到能整除学号最后一位的v0是824,824/8=103,第三个输入为103,调试得第一个输入为7。(炸点3)

跳到phase_3+800

读此处可知v0为第二个输入的字符,v1为s8+32的内容,由phase_3+632和phase_3+636可知,v1为98,ASCII码为98的字符是b,所以第二哥输入为b。(炸点四)。

拆除phase_4

由bomb.s可知要输入一个整数

这样就会跳过炸点1。

在phase_4+144设断点,通过调试可知v0为学号的最后一位

通过v0与0x1的按位与,如果最后一位为偶数则v0变为0,否则为1,最后一位为8,所以v0为0,需要跳转到phase_4+224

进入到递归函数

这个递归函数相当于一个计算斐波那契数列,计算第a0个斐波那契数列(从0计数),a0为输入的数。

最后一个要保证 v0和v1相等(炸点二),v0为13,v1为输入的数进入递归函数最后的返回值,即第a0个斐波那契数。

拆除phase_5

看bomb.s文件无法得出输入的类型,通过在0x004013fc处设置断点查看可知

S8+72处存的是输入的字符串。下面来到第一个炸点:

要求v1与v0相等,都为6。通过设断点调试可知,v1为输入字符串的长度,所以输入的字符串长度应为6。

接下来进入了一个循环

Sw zero,24(s8),表示初始化该位置数为0,由phase_5+200和phase_5+201可知,v0为6时循环结束,所以循环要进行6次。在phase_5+88处设置断点,看v1存的是什么。

此时是第二次循环,ASCII码98对应的是b也是输入的字符串的第二个字符。到phase_5+100执行完之后,v1先后和0xff,0xf按位与运算。得到的是该字符ASCII码的十六进制的后4位,即0010,十进制位2,v1为2。

在phase_5+160处有一次加载v1值,是经过了

0x0040147c <+148>:   lui v0,0x41

0x00401480 <+152>:   addiu   v0,v0,12524

0x00401484 <+156>:   addu    v0,v1,v0

这三个步骤,通过设置断点调试

V1由2变成了114,转换成字符为r,v0存储的字符串为isrveawhobpnutfg,从0开始计数2所对应的字符正好为r。所以该循环的作用是将6个字符依次的ASII码的十六进制的后四位,转换成十进制后,在isrveawhobpnutfg找到对应的字符替代原来的字符。

由以上可知要使a1和a0相等,在phase_5+232处设置断点调试

由此可知a0为输入的字符经过循环转换后的字符串,a1里是存的字符串giants,要想避免第二个炸点,要使v0为0,即两者相同,通过查ascii码表可知应该输入opukma

拆除phase_6

由此处可知要输入6个数字,(输入的初始数为1 2 3 4 5 6)

以上是一个双重循环

在phase_6+108处有一个v0的比较,要想避免爆炸要使v0小于7(炸点一),在此处设置断点,看v0为何值

由调试结果可知v0为输入的第i个数(具体i由循环次数决定),当v0小于7,比较后v0为1,未发生跳转,避免爆炸。

在phase_6+148处有一个比较,在此处设置断点调试

此处v0为输入的第i个数(具体i由循环次数决定),当v0大于0时发生跳转,避开了炸点(炸点二)。

在从phase_6+176到phase_6+292的内部循环,相当于j从i+1开始循环到j等于6停止。在phase_6+264处有一个比较,要使v0不等于v1才能跳转避开炸点(炸点三),在此处设置断点调试

由此可知v1为第i个数,v0为输入的第j个数比较。

由这个双重循环可知输入的数要大于0小于7,同时输入的数不能和它后面输入的数相等。

这一部分又是一个双重循环。外部循环是i从0开始,为6时停止循环,内部循环是从1开始。循环停止条件在phase_6+444处有一个判断,在此处设置断点调试,

由此可知v0为内部的循环变量j,v1为输入的第i个数,也就是当j小于第i个输入的数时要继续循环。其中内部循环有一句

通过后面调试知道,这一句的作用为指向下一个节点。也知道了s8+32为第一个节点的位置,在phase_6+478处在内部循环结束后,读取了节点的值v1,在此处设置断点依次调试(由于我输入的数谁1 2 3 4 5 6,所以依次出现节点1 2 3 4 5 6)

由此可知每个节点都存了一个数字。这个双重循环的作用是,将输入的数对应位置节点的数取出,放到一个数组中。

此处是一个一重循环。

由此句猜测,此处可能涉及到节点的连接。

在phase_6+580和phase_6+612处设置断点调试

通过前两次循环可知,phase_6+580处循环中v1从第2个节点开始,v0为v1的前一个结点,也就是说

这里的连接是,将取出的结点依次连接,在phase_6+612处v0指向后一个结点为下一轮循环做准备。所以这个循环的作用就是将上一个循环从原链表取出数得到的数组,重新连接成一个链表。

为了查看此处取得值是什么,在次设置断点调试。

此处v0为学号的最后一位。

此处有一个比较若v0即学好最后为8,则v0变为0发生跳转

此处有一个v0和v1的比较,在phase_6+824处设置断点调试

由此可知v1为当前结点的值,v0为后一个节点,如果后一个结点小于前一个结点则,比较后v0为1会爆炸,所以使结点的值为升序(炸点四)。根据前面对每个结点的值的调试,找到正确的输入为5 1 3 6 2 4,就是对应的第5、1、3、6、2、4结点的值使升序。

拆除隐藏的炸弹

在bomb.s文件中查找phase会发现下图

于是查找phase_defused发现

这个隐藏炸弹,通过输入的比较发现要输入%d %s,而phase_4输入为%d所以猜测可能在phase_4输入时加一个字符串,

为保证进入隐藏炸弹,要保证,输入的字符串要和phase_defused里的相等,所以在此处设置断点调试

a0为输入的字符串,a1为phase_defused中的字符串,所以想进入要使a1和a0相同

成功找到隐藏的炸弹。

要输入一个数字

在此处调试可知v0为输入的数字,v0-1<1001才能避免爆炸(炸点1)

然后进入了一个递归函数fun7中

会发现fun7中有许多v1,v0的比较,输入123,通过调试,和读语句可知,

在fun7+32处a1为输入,a0和a1为将要比较的节点元素,fun7+72处v0为输入的数,v1为结点的元素。多次递归后发现,类似与一个二叉搜索树。如果大于则向右孩子,如果小于则向左孩子。通过读secret_phase

可知,要经历三次向右查询,使v0变成111即十进制的7,才能避免爆炸,

输入123可以找到结点36,50,107,1001。由上图可知,只要有向左孩子的,即小于节点元素的数,都会爆炸。所以再次调试输入1001。

第一个节点为36,输入1001,比36大

在fun7+172处查看v0发现v0向下一个节点即50

再下一个节点为107

最后一个节点1001,与输入相同

跳转

在跳转

次数就要进行递归中归的部分,统计v0值,因为在输入大于节点元素时在fun7+152处不跳转,为了查v0值,在fun7+200处设置断点调试。

在secret_phase中

在secret_phase+160处设置断点调试

成功拆除

提示

上面给大家演示了拆除炸弹 1 的过程,这也是一个最简单的炸弹。接下来的 5 个炸弹难度逐级递增,大家不要拘泥于代码,死读代码;要结合 GDB 调试器, 查看内存以及各个寄存器的值,这样对于绕过一些棘手的函数很有帮助,可以让 大家切中炸弹爆炸条件的要害进行分析。GDB 的具体使用也不止上面所说的几 个命令,其他的希望大家可以自行上网搜索,掌握 GDB 的其他命令。GDB 调试 器可以在拆除炸弹的过程中给予你很大的帮助,但是仍希望你将它当作一个辅助 的工具来使用,帮助你更好的了解与汇编代码有关的内容,对汇编代码的理解是 该实验的重点。

有时候不要纠结于代码,可以借助GDB调试器,查看内存以及各个寄存器的值,可以方便了解各种函数,也可以直接在跳转开炸点处设置断点调试,可以更直观的找到拆除的方法。善用GDB 汇编调试指令x查看内存地址信息,并且要记住/x按十六进制格式显示变量;/d按十进制格式显示变量;/c按字符格式显示变量;/s 按字符串格式显示变量。在刚开始不知道如何改变格式显示变量时,会多不少麻烦。查看寄存器的内容,更好理解汇编语言产生的作用。

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值