CSAPP实验二——二进制炸弹bomb

一、实验目的
通过二进制炸弹实验,熟悉 x86汇编语言,学习使用反汇编工具objdump以及gdb调试工具。

二、实验内容
根据序号表得到一个二进制炸弹:bomb_3。该炸弹需要接收几组输入。每一关都要输入正确的密码,才可以通过,否则炸弹爆炸。通过对炸弹进行反汇编得到相应的反汇编文件。阅读反汇编代码,根据代码之间的内部逻辑,找到相应的通关密钥,即可过关。

三、实验环境
本实验在 Linux 操作系统(kali虚拟机)中完成。
需要用到的工具包括 gdb,objdump,make。



第一关 Phase_1

把断点设置在phase_1,然后运行(r)
在这里插入图片描述

ca2行调用<strings_not_equal>,判断输入的字符串与程序内部某个字符串是否相当
ca7行比较%eax和 %eax中的内容
反汇编(disas)得到如图所示
在这里插入图片描述

test指令:若为0则字符串相等跳转到<+30>;否则,跳转到explode_bomb函数,程序爆炸。
在这里插入图片描述

<+4>行中lea指令:用来将一个内存地址直接赋给目的操作数
将0x5555556020a0地址传递给rsi然后进行比较
猜测0x5555556020a0<unique_str>处的数据是程序内置的正确答案
查看(x/s)
在这里插入图片描述

进入<strings_not_equal>比较用户输入的字符串rsp和esi处的字符串是否相等,输入验证
在这里插入图片描述

第一关通过

在这里插入图片描述

第二关 Phase_2

将断点设置在phase_2,运行
在这里插入图片描述

<+24>行call指令<read_six_numbers>
进入函数查看(disas read_six_numbers)
在这里插入图片描述

<+41>调用scanf函数,读取输入的字符串
查看<+29>fmt_six_num
在这里插入图片描述

格式化字符串,即本关要求输入六个十进制整数,并与程序设置答案对比。
分析phase_2
在这里插入图片描述

猜测<magic_number>0x555555602080 存放程序设置答案
在这里插入图片描述

109 2ec 56 2a5 30d 143 转化为十进制265 748 86 677 781 323
第二关通关

在这里插入图片描述

第三关 Phase_3

将断点设置在phase_3
在这里插入图片描述

同样调用了sscanf,读取输入的数字后进行比较跳转
在这里插入图片描述

由此判断输入两个整数,再分析对输入的数的要求
在这里插入图片描述

<+40>cmp指令:jle输入的个数≤1跳转爆炸
<+45>cmpl指令:js输入的数<0跳转爆炸
<+51>cmpl指令:jg(有符号)输入的数>7跳转爆炸
<+57>cmpl指令:ja(无符号)输入的数>7跳转爆炸
下面有8种类似的结构:在这里插入图片描述

以上均为将rsp保存到eax中,如果不等,则跳转爆炸
两段代码中间衔接处:
在这里插入图片描述

查看<+70> lea指令 把给rip加上偏移量传递到eax中
汇编代码中是这样写的
在这里插入图片描述

猜测为跳转表的索引值
如switch的索引list[0]可以取0,1,2,3,4,5,6,7以及default
以list[0]为例,真实的代码存放在0x555555602060后连续几位对应的实际代码地址
在这里插入图片描述

依次为8组对应值,一下任意一组均为答案。
在这里插入图片描述

第三关通关

在这里插入图片描述

第四关 Phase_4

将断点设置在phase_4,反汇编结果如下图
在这里插入图片描述

注意到同样有scanf函数
在这里插入图片描述

首先查看要求输入数据的格式
在这里插入图片描述

要求输入1位整数,继续分析反汇编代码
在这里插入图片描述

<+47>将跳转到函数,下一步查看<+63>该函数,反汇编结果如图所示
在这里插入图片描述

发现func4中仍有func4,猜测为递归函数
在这里插入图片描述

func4(n)=func4(n-1)+func4(n-2)
在这里插入图片描述

<+0>显示终止递归的条件:edi≤1,即传入func4的参数小于1时
回到phase_4主体部分
在这里插入图片描述

<+68>行显示:调用后,如果eax中的值≠0x37(十进制转化为55),则爆炸。
即递归函数最终返回值应为55。
编写递归函数如下

#include<iostream>
using namespace std;
int func4(int i)
{
    if(i<=1)
        return i == 0? 0:1;
    return func4(i-1) + func4(i-2);
}

int main()
{
    int num[10];
    int result[10];
    for(int i=1;i<=10;i++)
    {
        num[i-1]=i;
        result[i-1]=func4(i);
    }
    for(int j=0;j<10;j++)
    {
        cout<<"第"<<num[j]<<"项:"<<result[j]<<endl;
    }
    return 0;
}

输出结果如下图
在这里插入图片描述

已知程序设定默认项为1,所以当传入参数为9时,输出结果为55。
至此,phase_4通关。
在这里插入图片描述

实验结束。

心得体会

第一关是看了教辅的演示,所以刚开始拆第一关更多的是新奇,看着一条条汇编语句也不是很熟悉但是充满了好奇。初步学会了查看内存内容与判读那函数的返回值,对后续的拆弹过程起到引路的作用。第二关反汇编工具也都不太会用,对着百度查各个指令的意思,到最后也看不懂到底是说了些什么,后来没有办法就用gdb的ni指令一条一条的走,看怎么执行。后续两关稍微熟悉了一点,但是具体各种栈的操作和寄存器的变化还是不能很清楚。

问题及反思

(1)本次实验用到的基础命令
objdump -d bomb >bomb.s//将反汇编代码放在bomb.s文本中方便查看
gdb bomb //用gdb调试器调试代码
break + 行号//设置对应行号为断点
break + 函数名称//设置对应函数为断点
info break// 可以查看断点信息
delete + 断点编号//删除对应编号的断点
clear + 行号//删除对应行号断点
delete + (编号起点-编号终点)//删除起点和终点的所有断点(包括起点和终点)
disable + 断点编号//可以禁用断点
run //试运行代码
info x //查看寄存器
disas phase_1 //查看第一个炸弹的汇编代码
x/s 0x402400 //查看对应地址存放的内容 找到解题的线索
x /nfu addr //检测内存值, n 查看几个内存单元 f进制显示 u显示单字节双字节等
quit //退出
print $eax //打印寄存器
I reg //显示所有寄存器值

(2)本次实验涉及到跳转指令较多,故作总结
JE //等于则跳转JNE ;不等于则跳转
JZ //为 0 则跳转JNZ ;不为 0 则跳转
JS //为负则跳转JNS ;不为负则跳转
JC //进位则跳转JNC ;不进位则跳转
JO //溢出则跳转JNO ;不溢出则跳转
JA //无符号大于则跳转JNA ;无符号不大于则跳转
JAE //无符号大于等于则跳转JNAE ;无符号不大于等于则跳转
JG //有符号大于则跳转JNG ;有符号不大于则跳转
JGE //有符号大于等于则跳转JNGE ;有符号不大于等于则跳转
JB //无符号小于则跳转JNB ;无符号不小于则跳转
JBE //无符号小于等于则跳转JNBE ;无符号不小于等于则跳转
JL //有符号小于则跳转JNL ;有符号不小于则跳转
JLE //有符号小于等于则跳转JNLE ;有符号不小于等于则跳转
JP //奇偶位置位则跳转JNP ;奇偶位清除则跳转
JPE //奇偶位相等则跳转JPO ;奇偶位不等则跳转

(3)本次实验遇到的问题
感觉比较难理解的就是几种寻址方式:
① 直接寻址
② 寄存器间接寻址
③ 寄存器相对寻址
④ 基址变址寻址
⑤ 相对基址变址方式
对一些指令操作的内容找不到正确的内存地址,导致反复调试,耗费了很长的时间。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值