目录
实验概述
- 实验目的:增强对程序的机器级表示、汇编语言、调试器和逆向工程等方面原理与技能的掌握。
- 实验目标:需要拆除尽可能多的炸弹。
- 实验要求:使用gdb调试器和objdump来反汇编炸弹的可执行文件,并单步跟踪调试每一阶段的机器代码,从中理解每一汇编语言代码的行为或作用,进而设法“推断”出拆除炸弹所需的目标字符串。
- 实验语言:c。
- 实验环境:linux(root用户名为vicai)
2.2 实验内容及过程
一个“binary bombs”(二进制炸弹,下文将简称为炸弹)是一个Linux可执行C程序,包含了6个阶段(phase1~phase6)。炸弹运行的每个阶段要求你输入一个特定的字符串,若你的输入符合程序预期的输入,该阶段的炸弹就被“拆除”,否则炸弹“爆炸”并打印输出 "BOOM!!!"字样。实验的目标是拆除尽可能多的炸弹层次。
每个炸弹阶段考察了机器级语言程序的一个不同方面,难度逐级递增:
- 阶段1:字符串比较
- 阶段2:循环
- 阶段3:条件/分支
- 阶段4:递归调用和栈
- 阶段5:指针
- 阶段6:链表/指针/结构
- 另外还有一个隐藏阶段,但只有当你在第4阶段的解之后附加一特定字符串后才会出现。
- 为了完成二进制炸弹拆除任务,你需要使用gdb调试器和objdump来反汇编炸弹的可执行文件,并单步跟踪调试每一阶段的机器代码,从中理解每一汇编语言代码的行为或作用,进而设法“推断”出拆除炸弹所需的目标字符串。这可能需要你在每一阶段的开始代码前和引爆炸弹的函数前设置断点,以便于调试。
通过gdb调试bomb程序:
通过执行以下指令,可以反汇编bomb,并将结果保存到bomb.asm文件中,然后通过gedit或more可以方便地察看汇编代码:
2.2.1 阶段1 字符串比较
- 任务描述:通过phase_1的反汇编代码找出要输入的字符串;
- 实验设计:利用gdb结合断点来动态地分析;
- 实验过程:
观察phase_1的反汇编代码,如图2.1.1所示:
通过分析,发现在调用strings_not_equal对比字符串之前,有两个压栈的指令,其中一个是将函数入参送入栈中,还有一个地址送入了栈,函数入参应该就是输入的字符串所在地址,猜测另一个地址就是正确字符串的首址,于是在phase_1处下个断点,然后运行,随意输入一个字符,触发断点,再用x命令查看字符串。如图2.1.2所示:
故猜测“Public speaking is very easy.”就是所需字符串。重新执行该程序,直接输入该字符串,观察结果。如图2.1.3所示:
- 实验结果:如图2.1.3所示,阶段一拆弹成功!
阶段一很简单,找出字符串首地址就可以了,没有什么大的难点。
为了提高调试效率,可以将解析的结果保存到文件中,在调试时以参数方式提供给程序运行,方法如下:
第一步:建立一个参数文件,比如“b.txt”文件,然后用vim或gedit编辑该文件,写入“Public speaking is very easy.”:
2.2.2 阶段2 循环
- 任务描述:通过phase_2的反汇编代码推断第二阶段要输入的数据;
- 实验设计:利用gdb结合断点来动态分析;
- 实验过程:
观察phase_2的前一部分反汇编代码,如图2.2.1所示:
a) 由1处可发现输入的应该是6个数字,而且通过<read_six_numbers>将字符串拆分为6个数,存入ebp -0x18的位置上,可能是通过数组存储;
b) 在2处马上就比较了第一个数:cmpl $0x1, -0x18(%ebp),说明输入的第一个数如果不是1,那么炸弹就将爆炸;
c) 紧接着进入了一个循环,关键语句为cmp %eax,(%esi,%ebx,4)。其中,%eax可根据前一条语句计算得出值为1×2=2,而(%esi,%ebx,4)中存储的是后面输入中的5个数其中的一个。比较前后两者,如果不满足“第n个数的值=第n-1个数的值×n”就爆炸;
由图2.2.3可知,%ebx寄存器的值就是用来计数的,inc之后表示当前为第n个数,当n大于5时就退出循环。而每次循环都会进行以上的判断,当循环结束时,如果都符合要求则本关通过。推测可能的代码如下:
1. if (arr[0