实验目的:
通过二进制炸弹实验,熟悉汇编语言,反汇编工具objdump以及gdb调试工具。
实验内容:
1、eflags标志位的查看。
2、backtrace指令学习。
3、炸弹实验第3、4关。
实验过程:
1、进入gdb调试命令,设置断点运行之前课上得到的hello文件,然后输入i r指令即可查看寄存器的内容,当然也包括eflags标志位寄存器的值。
32位CPU的标志位寄存器的主要标志位分布如下:
17 |
16 |
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
VM |
RF |
|
NT |
IOPL |
OF |
DF |
IF |
TF |
SF |
ZF |
|
AF |
|
PF |
|
CF |
|
|
|
|
|
|
|
0 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
0 |
在上面我查出的标志位寄存器eflags的值为0x286转化为二进制形式即为001010000110,对应到相应位即可知道哪些标志位值为1,我们也可以通过例如set eflags=0x206语句来设置标志位的值,至于你想要什么标志位值为1,则可以通过把上表中对应位的值置为1,再转化为16进制数来改变标志位的值。
2、backtrace指令可以查看程序所有函数的栈帧,简写为bt,以下显示的即为main函数和sum函数栈帧的返回地址。
也可以通过以下指令查看帧的详细信息:
3、
第三关:
1)、有了前面两关的经验,在过第三关的时候,一开始我就直接进入bomb文件的gdb调试界面,反汇编得到phase_3函数的汇编代码,代码很长一次性显示不完全,回车即可接着显示余下的代码直到显示完(这里没有截完所有的图)。
观察整个phase_3函数的汇编代码,我发现它跟前面两关有一个相似之处,调用了两个函数,其中一个仍是爆炸函数,另一个是__isoc99_sscanf@plt函数,现在猜不出这个函数要干嘛了,所以,我索性直接开始分析phase_3函数汇编代码,希望能发现一些线索。
2)、开始分析phase_3函数的反汇编代码:
首先是为函数准备堆栈,这里开辟了40个字节的内存。
然后加载%ebp-0x10的有效地址通过中间寄存器%eax传给%esp+0xc,同时也加载%ebp-0xc的有效地址通过中间寄存器%eax传给%esp+0x8。
接着把地址0x804a23e中存储的值传给%esp+0x4处,再把%esp+0x8存储的值间接传给%esp,然后调用__isoc99_sscanf@plt函数,到这里大概就可以猜测地址0x804a23e中存储的值和%esp+0x8存储的值即为函数__isoc99_sscanf@plt的参数,此处即为准备参数阶段,根据前两关的经验,地址0x804a23e中存储的值即为第三关想要的通关密码。
3)、到目前为止,我自认为汇编代码分析到此处就可以顺利通关了,于是,我就打印出地址0x804a23e处存储的值,然后验证是否为正确的密码,结果显示我失败了,想了一会,并没有想出个所以然,所以我接着分析汇编代码。
4)、接着把%eax存储的值和1作比较,改变标志位进行跳转,若%eax存储的值小于等于1,就会跳转到爆炸函数,引爆,反之,若大于1,则跳转到
这里我在phase_3函数执行前(通过设置断点)查看了标志位,然后在执行完cmp $0x1,%eax语句后又查看了标志位,发现标志位有所改变以证实上述说法。
然后接着把%ebp-0xc处存储的值和7作比较,如果大于7就引爆(
接着无条件跳转到地址(0x804a1a0+4*%eax)存储的地址处,这其实是以0x804a1a0为基地址,4*%eax为步长进行跳转,典型的switch语句,由于%eax的值未知但要小于7,姑且先假设为0,所以直接打印0x804a1a0处存储的地址值,以16进制显示。
现在来分析余下的代码,首先传 了个立即数给%eax,而后跳转到
5)、基于以上的分析,大概就可以找到规律了,%ebp-0xc处存储的值即为参数一,然后根据参数1的值改变(0x804a1a0+4*%eax)处存储的值,跳转到不同的地址处以求解出参数2的值,这里参数1的范围为0~5。
在这里我验证了输入的参数为两个整数:
接着当参数1为1时,求得跳转地址为0x8048f19,分析跳转后的代码得:
%eax=0x0-0x35a+0x2ef-0x216+0x216+0x216+0x216-0x216=-641
之后依次更换参数1的值为2、3、4、5按照以上规律即可求得参数2的值,这里就不一一截图了。
根据上述跳转地址求得参数2的值:
参数1 |
2 |
3 |
4 |
5 |
参数2 |
217 |
-534 |
0 |
-534 |
依次验证这些参数组合的正确性:
全部通过,当然想要通关只需要6选1即可。
附phase_3函数的栈帧图:
以上,第三关即成功通过。
第四关:
1、查看phase_4函数的汇编代码,纵观全部的phase_4函数的汇编代码,发现此函数在phase_3函数的基础上多调用了一个func4函数。
2、分析整个函数的汇编代码,前半段仍是准备堆栈过程,然后是__isoc99_sscanf@plt函数的参数准备,有了第三关的经验,地址0x804a23e里面存储的应该不会是本关的通关密码,按照老规矩查看里面存储的数据类型,发现仍旧是两个整型数据。
3、接下来通过比较%eax和2是否相等来判断输入的参数个数是否为两个,如果不是,则函数爆炸。
4、在输入的参数个数为2时,进行test指令,而后进行当符号位为1时的跳转,为了不跳转,则%eax要大于0。
5、接着把%eax和14进行比较,为了让程序跳转到<+61>处,而不是调用爆炸函数,则%eax要小于等于14,所以这里可以推出其中一个参数的范围是(0,14]。
6、接下来为func4函数准备参数,参数1未知,参数2为0,参数3为14。
7、到这里,就得开始分析func4函数了,查看其汇编代码。
8、前部分堆栈准备,而后对传进来的三个参数做了一些操作。
下面是对应的计算过程,func4函数的初始化参数为n、0、14:
而后求得%ecx=7,把其与参数1作比较跳转到不同的代码段,以下是我的分析过程:
9、现在我们回到phase_4函数的剩余那部分代码,在调用完func4函数后,可以清晰的看到返回值为1,否则函数将跳转到<+99>处进而引发爆炸,紧接着,把%ebp-0x10处的值与1比较,若相等,则结束,否则爆炸,由此可见此处存储的值为1,且由phase_4的栈帧图可以得到此处为输入的第二个参数。
Phase_4的栈帧图:
10、以上得到func4函数的返回值为1,根据第8步的分析可知,func4函数要想返回1,需通过参数1大于7的那段函数体求得,它的返回值为2*%eax+1,由于这个func4是一个递归函数,所以,要想2*%eax+1=1,最快的方式就是底层调用的返回值为0且调用一次。
所以,倒推即可求得参数1为11:
11、接下来验证11和1的正确性:
结果显示,成功通关。
小结:
第三关主要考察的是switch语句,第四关主要考察的是递归调用,在第四关的炸弹拆除过程中,其实有一种简单快捷的方式,在未进入func4函数前已经把参数1的取值范围分析出来了,参数2也分析出来,所以,可以组合它们逐个检验直到得到正确答案,这一关的答案也不只一个。