CSAPP:逆向工程【二进制炸弹】
二进制炸弹任务描述
"二进制炸弹包含若干个阶段,每个阶段需要输入特定的字符串,所有输入正确则炸弹被排除,否则….."
拆弹的任务也就是找出这些字符串将字符串记录到solution.txt文件中,用换行区别不同阶段的字符串,
Linux环境下可按下列方式验证拆弹结果:
主要方法
objdump反汇编与gdb调试。
分析流程
已知数据有编译好的二进制可执行文件bomb,也就是反汇编目标文件,以及bomb.c文件,用于辅助理解代码。
查看bomb.c可知程序利用phase_*函数(*为1~6) 检查输入字符串是否合法,不合法就引爆炸弹。
bomb.c没有给出phase的源码,我们实际的任务就是逆向出每个phase的检查规则,构造出合法字符串。
首先使用反汇编得到bomb的汇编文件,
以下将逐个分析phase1-phase6。
phase1【字符串对比】
<phase_1>汇编段前三行申请了8字节栈空间,
movl指令在高4字节存入了0x80499a8处的一份数据,随后的两条mov指令将我们提供的phase1的参数存入了栈的低4字节,
紧跟着一条call指令,跳转到<strings_not_equal>处,显然是在进行字符串对比,
通过分析我们可以得到,phase_1实际上是将输入参数与0x80499a8处存储的数据做对比,
在汇编文件中,数据段的内容并没有包含,所以我们需要通过gdb断点输出该处的存储数据,
断点处输出可以得到0x80499a8处的数据,即phase_1的字符串答案I turned the moon into something I like to call a Death Star.
phase2【循环数字】
从8048bbe行可以看出,phase_2的执行与读取6个数字有关,那么输入也应该是6个数字。
从8048bc3、8048bf0、8048bf3行可以得到phase_2的执行,进行了3次循环,也就是6个输入数字进行了3次对比。
有一个误区是,一开始去分析了read_six_numbers段的汇编码,没有得到什么有用信息,
由此可知后面对phase的分析,主要基于phase段的代码,其中涉及到的一些子操作只要按照字面意思理解即可。
分析循环语句中的8048bcc到8048bdd行,可以看到edx寄存器先从栈中读取了一个数字,
而后eax寄存器在索引+3(8048bd6行)的情况下,也从栈中读取了一个数字,
之后edx寄存器与eax寄存器进行了对比。
从已经分析到的信息可知,phase_2输入6个数字,对比只循环了3次,而对比的两个数字都在栈中(与phase_1不同)且下标相差3,可以猜测phase_2是将用户输入的6个数字中的前3个数字与后3个数字做了对比,下面通过gdb进行调试:
为了避免巧合,测试数据将数字设置成两组五位数,phase_2通过,证明phase_2确实是在对输入的数字进行索引加3的对比检查。
phase3【case分支】
有了phase1和phase2的经验,直接从8048c36行开始分析,根据<sscanf@plt>可以得到,phase_3的参数应该是调用了C语言中sscanf函数来输入,sscanf读取格式化的字符串中的数据。随后的mov、cmpl、jg、call四条指令中出现了对比操作与<explode_bomb>的调用,显然是在对输入数据进行分析判断。cmpl语句将寄存器eax与常数1进行对比,若不大于1则爆炸,而eax寄存器中存放的是上一次操作的返回值,查看sscanf函数定义可以得知,返回值为int类型,返回的是读取到的有效数据个数。
接下来,测试phase_3需要的参数个数,首先给两个输入参数:
得到sscanf函数返回值为2,接下来我们再给三个输入参数:
给了三个输入参数,但sscanf函数返回值仍然为2,由此可知,sscanf函数只读取两个格式化字串,也就是phase_3需要两个输入参数。
8048c49行将输入的参数存入eax寄存器,用gdb调试可以得到存入的是第一个输入参数,参数1与常数7进行对比跳转,若大于7则跳转到爆炸指令,可知参数1取值范围为0-7。8048c5f行根据eax寄存器的内容进行直接寻址跳转,8048c58行在计算跳转地址时,用到了地址0x80499ec,且以4*%edx作为偏移量,查看反汇编得到的汇编码,并没有0x80499ec行,gdb调试输出可以得到:
显然该段是数据段,存储了case跳转表,而跳转地址正是phase_3代码段的8个地址入口。
不同的入口地址,分别给-0x8(%ebp)赋予了不同的常数值。
再读取参数2,将参数2与case段中的赋值进行比较,相等则通过。