【BinaryBomb实验】计算机系统基础

实验原理与内容

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

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

  1. 阶段1:字符串比较
  2. 阶段2:for循环
  3. 阶段3:switch分支
  4. 阶段4:递归函数
  5. 阶段5:数组元素按序访问
  6. 阶段6:链表
  7. 隐藏阶段:只有在阶段4的拆解字符串后再附加一特定字符串后才会出现(作为最后一个阶段)

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

拆弹密码的输入分文两种模式。

模式1:正常手动输入,每次程序运行到某一阶段会停下来要求用户输入数据。这种方式比较原始,不推荐使用。如果使用这种做法,在程序调试到后期时,每次为了进入后期的断点位置都需要在之前的每一个阶段进行手动输入,极其浪费时间。

模式2:采用输入重定向。首先将答案文本写至一个.txt文本中,每个阶段的拆弹密码占一行。

在调试程序时直接使用输入重定向指令,例如(假设密码已被写入到之前的拆弹密码文本文件solution.txt中):

./bomb < solution.txt

通过执行以上指令即可直接根据屏幕输出来判断程序正确地进行了几个阶段或者在第几个阶段出现了错误。如果密码全部正确,提示结果如下图所示:

过关前,要了解一下gdb的基本用法:

//编译一个测试程序,-g (可调试)
gcc -g test.c -o test
gdb -q test  //启动gdb;-q(不打印版本信息)
(gdb) list   //查看源码list;注意!默认10行,回车可继续查看
(gdb) run    //运行程序(简写r)
(gdb) b 10   //设置断点(格式:b 行号)例如:在第十行设断点
(gdb) info breakpoints   //显示断点信息
(gdb)quit    //退出
(gdb)layout regs  //进入UI
UI界面内:
(gdb)start   //开始
(gdb)c    //继续运行
(gdb)si    //单步执行
(gdb)p/x    //打印(print)
(gdb)x/s    //查看内存地址内容
(gdb)finish //结束当前函数

前提提醒:

1、每过完一关,就del本关的断点,去设置下一关的断点。
2、在未确定钥匙前,先随便输入一些字符串。

第一关:

 先执行以下代码:进入关卡

gdb bomb      //进入gdb调试器
b phase_1     //设置第一关断点
layout regs   //进入UI界面
start         //开始
run           //运行

由于现在不知道钥匙,先随便输入字符串,回车

使用si指令,单行执行;执行到光标停在callq <string_not_equal>,表示下一个要执行

 由此看到上一行有 lea ....,%rsi;通过x/s  $rsi指令,查看内容,输入该内容就过关!

 (注:用IDA可直接看到答案,IDA的用法搜一下就有)

第二关:

执行到callq<read_six_number>里面,lea 0x112a(%rip),%rsi,通过x/s $rsi命令,查看内容,得“需要输入6个数”

可通过finish指令,跳出函数

第一个数要等于1

 继续向下执行 由此,可得出第二个数为2

 

此可以推断,后面的第4、5、6个数为:8、16、32

所以,第二关钥匙为:1、2、4、8、16、32

第三关:

 一样,先查看需要输入多少个数

继续执行(这里写错了,应该是指:所输入值的个数小于等于1则爆炸,至少要2)

而第一个值不能超过7

 继续向下执行jmpq  *%rax,跳转到跳转表上

根据第一个值的不同,跳的位置不同!!!

 执行结束后,通过界面可看到第二个值为4294966646(第一个值为:2)

 最后还有个比较:


第四关: 

 第一步,一样,执行到lea 0x15e4(%rip),%rsi,通过x/s $rsi命令,查看到“%d %d”

 %eax要等于2,(%rsp)输入的第一个值要≤14 (由此,可以得到取值范围)

 执行到callq <func4>,调用函数内进行计算(递归),计算结束后pop出栈rbx的值->rax

 计算所得的值 比较 0x13=19;第二个输入的值 比较 0x13=19

由此,可以得到第二个数为:19;第一个数则需要通过符合func4函数的计算;

我采用了穷举法进行得出…

 第五关:

第一步,一样,需要输入6位

执行到lea …,%rcx,通过x/16c $rcx可看到数组元素

 经过运算,得到新的rdx为98“b”——>对应“m”

 

 以此类推,所输入的6位字节,转换为新的

继续向下执行,可以得到%rsi钥匙“bruins”

 继续执行,跳到strings_not_equal函数里,可以看到rdi与rsi进行访问以及比较。(rdi:为输入的值通过转换得到全新的值)rsi:钥匙

 最后通过test测试

 第六关:

 第一步,一样

执行到callq <six_numbers>,可以知道需要输入6个数

继续执行可以看到需要输入数值的范围,每个数-1后,不能超过5

 继续跳转,执行后,可以看到需要输入的每个数值都不能相同

 (此处重点是判断这6个数是否在减1后≤5,并且每个数值都不同)

 继续执行,行用7减每一个数,所得的数存到r12中

 每个数都要比较,比较完6个数后跳转

 

跳转到重要部分,重排链表指针域

 

 执行完重排后,需要进行比较rax与rbx

 方法:通过x/6c $rax ;x/6c $rbx 命令,去查看双方每个阶段的值

 条件:rbx值≥rax

 

 

 隐藏阶段:

 

第7关: 

执行到mov (%rdi),%edx,可以看到值为36

 继续执行,通过mov 0x8(%rdi),%rdi获得新的值,返回test上,再次进行比较

 由此,我们可以确定32到8的范围,继续执行

 最后,继续出栈返回(只有前两次才需要相加,最后一次直接出栈返回值rax)总共进行了3次sub $0x8,%rsp 

 要求rax的值等于2

由此,可确定范围只能在36-8之间,再通过测试可知,取36-8的中间值即可

所以,fun7的钥匙为22

 (穷举也行)

实验总结:(cv)

通过此次实验,增强对于程序的机器级表示、汇编语言、调试器和逆向工程等方面原理与技能的掌握。掌握使用gdb调试器和objdump来反汇编炸弹的可执行文件,并单步跟踪调试每一阶段的机器代码,从中理解每一汇编语言代码的行为或作用,进而设法“推断”出拆除炸弹所需的目标字符串。需要拆除尽可能多的炸弹。

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

在调试程序时直接使用输入重定向指令,例如(假设密码已被写入到之前的拆弹密码文本文件solution.txt中):

./bomb < solution.txt

通过执行以上指令即可直接根据屏幕输出来判断程序正确地进行了几个阶段或者在第几个阶段出现了错误。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值