计算机系统基础实训二—BinaryBomb实验

实验目的与要求

1. 增强学生对于程序的机器级表示、汇编语言、调试器和逆向工程等方面原理与技能的掌握。

2. 掌握使用gdb调试器和objdump来反汇编炸弹的可执行文件,并单步跟踪调试每一阶段的机器代码,从中理解每一汇编语言代码的行为或作用,进而设法“推断”出拆除炸弹所需的目标字符串。

3. 需要拆除尽可能多的炸弹。

实验原理与内容

一个“binary bombs”(二进制炸弹,下文将简称为炸弹)是一个Linux可执行C程序,包含了7个阶段(phase1~phase6和一个隐藏阶段)。炸弹运行的每个阶段要求学生输入一个特定的字符串,若的输入符合程序预期的输入,该阶段的炸弹就被“拆除”,否则炸弹“爆炸”并打印输出 "BOOM!!!"字样。实验的目标是拆除尽可能多的炸弹层次。

每个炸弹阶段考察了机器级语言程序的一个不同方面,难度逐级递增:

n阶段1:字符串比较

n阶段2:for循环

n阶段3:switch分支

n阶段4:递归函数

n阶段5:数组元素按序访问

n阶段6:链表

n隐藏阶段:只有在阶段4的拆解字符串后再附加一特定字符串后才会出现(作为最后一个阶段)

为了完成二进制炸弹拆除任务,需要使用gdb调试器和objdump来反汇编炸弹的可执行文件,并单步跟踪调试每一阶段的机器代码,从中理解每一汇编语言代码的行为或作用,进而设法“推断”出拆除炸弹所需的目标字符串。这可能需要在每一阶段的开始代码前和引爆炸弹的函数前设置断点,以便于调试

实验设备与软件环境

1.Linux操作系统—32位debian

2. gdb调试器和objdump反汇编指令

3. 笔记本

实验过程与结果(可贴图)

Phase_1

Phase_2

可以看出第二关一开始就需要跳到<read_six_numbers>函数中

这是感觉$rsi的值有点怪怪的,查看了一下才得知密码格式是“%d %d %d %d %d %d”

得知格式后返回第二关的函数

cmpl   $0x0,(%rsp)

jne    124f <phase_2+0x2b>

callq  180d <explode_bomb>

得出(%rsp)需要等于0,不相等则跳转爆炸

cmpl   $0x1,0x4(%rsp)

je     1254 <phase_2+0x30>

callq  180d <explode_bomb>

得出0x4(%rsp)一定要等于1,都否则就无法跳转,按照程序顺序将会爆炸

add    $0x4,%rbx

cmp    %rbp,%rbx

je     1277 <phase_2+0x53>

mov    0x4(%rbx),%eax //%eax=1

add    (%rbx),%eax    //%eax=1+1=2

cmp    %eax,0x8(%rbx)

je     125d <phase_2+0x39>

由此可以推断出第四个数是前两个数之和,因此答案为:0 1 1 2 3 5

Phase_3

阅读 phase_3

分配栈针:利用%rax,也就是0x8+%rsp 和利用%rcx,也就是0x4+%rsp,传递sccanf函数用的第三和第四个参数

断点查询%rsi的值,答案格式是“%d  %d”

返回phase_3

cmp    $0x1,%eax

jle    12d9

callq  180d <explode_bomb>(定位此12d9)

由此可知:%eax>1

cmpl   $0x7,(%rsp)

ja     1311

callq  180d <explode_bomb>(定位此1311)

由此可知:%rsp<7

接着往下读,看到

lea    0x15a0(%rip),%rdx

查询了一下发现有刚好是两个整数“0 662”

浅试一下,发现成功了!

再继续往下读

把0x29f、39b、348、1ea、b9、86、8a转换成十进制分别是:671、923、840、490、185、134、138,Switch满足条件(cmpl   $0x7,(%rsp))【(%rsp)≤7】

因此,我尝试了一下答案,正确,所以感觉这个逻辑也没错

phase_4

首先还是一样都是判断输入是否合法

通过画图得知,rdx存储了我们读入的第一个参数,rcx存储了我们读入的第二个参数,在这里查询一下%rsi的值,可以推断出答案格式“%d %d”

接着往下读.

cmpl   $0xe,(%rsp)

jbe    13be <phase_4+0x38>

mov    $0xe,%edx (此行为:13be)

mov    $0x0,%esi

mov    (%rsp),%edi

算出这三个参数分别为:14、0、14,然后带着函数跳转到fun4函数

/ 注意一下的是 a in %edi, b in %esi, c in %rdx

 int func4(int a, int b, int c){

   // 获取l 和 r 的中位数

   int mid = b +(c - b)/2;

   // 进行递归调用

   if(mid > a) return 2*fun(a,b,(mid-1)) result=0;

   if(mid < a) {

result=2 * func4(a,mid+1,c)+1;

Return result;

}

 }

我就带入算了一下,返回结果值为23,预测小于23的都可以

cmp    $0x6,%eax

jne    13dc <phase_4+0x56> //bomb

由此可以得出%eax=6

因此第一个数≤23,第二个数为6,答案就可以正确,我输了23,6

但是我输入24,6也可以,到25,6就不行了,我也不知道哪里出了问题

phase_5

首先感觉应该是个递归问题

callq  16e4 <string_length>

$0x6,%eax

1472 <phase_5+0x77>(bomb)

因此初步判断字符串长度为6

lea    0x1467(%rip),%rcx    

查询 %rcx 的值

发现他是一个较长的字符串“maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?”

由lea    0x1(%rsp),%rdi,lea    0x1413(%rip),%rsi,则可查看rsi的值

观察两两对应关系,只需要发现“flyers”在其中的顺序为“9 15 14 5 6 7”,说明输入字符串中对应位的字符的最低4位的数值等于“9 f e 5 6 7”,即可通过这一关,查看ASCII值,得到“Y_^UVW”“ionefg”都可以,答案正确

phase_6

以上均为对调用者保存寄存器的保存过程

这里的rsi就是栈帧指针,然后调用 read_six_numbers

得到六个参数的分布

a1=%rsi;a2=%rsi+4,a3=%rsi+8;a4=%rsi+12;a5=%rsi+16;a6=%rsi+20

凭感觉直接查询有注释的$rsi,得到答案格式“%d %d %d %d %d %d”,跳出函数继续读phase_6

发现如果输入的参数>6则会直接爆炸,继续

mov    %edx,%eax

sub    (%r12),%eax

分析后一个循环可知,输入的6个数,分别被7减,然后保留在原处

lea    0x202cc9(%rip),%rdx        # 204210 <node1>

%rdx的初始值为204210

mov    0x8(%rbx),%rax

mov    (%rax),%eax

cmp    %eax,(%rbx)

jge    158f <phase_6+0x10a>

由此可知,链表值从大到小排列

我直接查了一下节点204210的值

从大到小依次排列为:6 3 2 4 1 5

减去7后的数值为:1 4 5 3 6 2

输入答案,正确

隐藏关

搜索隐藏关发需要从phase_defused进入

设置断点跳转到19f9,查询一下参数格式为"%d %d %s"

<+115>语句对两个字符串进行了比较。而其中一个字符串就是sscanf输入地址参数所指向的字符串。使用x/s命令查看,知道可以通过在第4关输入"23 6 DrEvil"作为通关密钥的同时,开启隐藏关卡。

成功进入隐藏阶段!

将断点断到secret_phase,分析secret_phase,由read_line可知,输入的是一个字符串,并且strtol函数是将一个字符串转化为十进制长整数赋给%rax作为返回值,不知道<read_line>前面的数值有什么用,先查一下,即第一个参数a1=0x48,对应的十进制是“72”,a2为要输入的数。

cmp    $0x5,%eax

je     1647 <secret_phase+0x3d>

再由func7之后,可以知道返回值是0x5

读secret_phase阶段

根据C库函数strtol的定义,我得知这是一个转换字符串为数字的字符串。所以,我们需要输入一个数字。根据*1628,该数字需要低于0x3e8。在进入到func7函数后,若返回值为5,则跳转到*1647打印出拆弹成功的字符串。因此,关键在于func7函数。注意,初次调用func7时,我们的参数分别是输入的数字(%esi),以及0x202af8(%rip)

进入func7函数

这一部分的代码比较短,也比较好读,下面调用了两次fun7说明这也是一个递归程序,而且观察得到%rsi的值在整个递归的过程中没有变化过,起到的只是一个比较的作用。

一开始还检测了一下%rdi是否为0,后面设置递归参数的时候用mov 0x8(%rdi),%rdi,自身加上一个偏移量的间接寻址代替自身,基本可以确定%rdi是一个指针,%rdi+0x8和%rdi+0x10同样也是一个指针,看到这里基本已经猜出这个数据结构就是二叉树了

转换成C

 int func7(int a, int b){

   // 获取l 和 r 的中位数

   int mid = b +(c - b)/2;

   // 进行递归调用

   if(a > b) return 2*fun7((*(a+8),b) result=0;

   if(a != b) {

result=2*fun7((*(a+16),b)+1;

Return result;

}

 }

算出答案为“47”

总结所有答案汇总:

And they have no disregard for human life.

0 1 1 2 3 5

0 662

6 6 DrEvil

ionefg

1 4 5 3 6 2

47

实验总结

这一次的bomb实验,包含了计算机系统中第三章汇编语言的几乎所有知识点。通过本次的练习,我的汇编语言能力获得了很好的锻炼,对于一些重要知识点(如跳转表,循环)的知识点,掌握的更加牢靠。而本实验中包含的许多有趣实用的汇编语言技巧(如一些精巧的中间变量的使用、灵活的jump跳转指令的运用)使我更加注意编程技巧的学习。

  • 35
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值