CSAPP-----LAB---------二进制炸弹
二进制炸弹实验第一篇博客,就为记录下最近的学习,从对gdb什么都不懂到完成这个实验,两天时间。这个实验还是很好的。通过阅读相关文件,可以了解到,我们需要做的是通过阅读七段代码以及相关的函数,采用gdb调试,即可得到七个炸弹的密码,通关。 虚拟机与Win...
二进制炸弹实验
第一篇博客,就为记录下最近的学习,从对gdb什么都不懂到完成这个实验,两天时间。这个实验还是很好的。
通过阅读相关文件,可以了解到,我们需要做的是通过阅读七段代码以及相关的函数,采用gdb调试,即可得到七个炸弹的密码,通关。
虚拟机与Windows系统的共享文件夹被自己搞坏了,汇编代码只有图片了,有点不爽。
第一关:
先来简单观察下这段程序在做什么,callq 的两行就是调用 strings_not_equal 和 explode_bomb 这两个函数的,而这里 %esi 对应的是第二个参数,第一个参数呢?当然就是我们拆弹时需要输入的字符串了。之后的 test 是用来判断函数的返回值 %eax 是否为 0, 如果为 0 则进行跳转,否则炸弹爆炸,所以我们实际上要做的,就是看看$0x402730 这个地址里对应存放的是什么字符串,也就是拆炸弹的关键了。
先 gdb bomb,然后设置断点 break explode_bomb 和 break phase_1;接着运行 run,就会在断点处停下,这里会先让我们输入第一关的密码,随便输入一个抵达断点再说。由gdb的指令,我们可以查看0x402730这个地址中的内容,这里根据我的分析就是炸弹的密码,然后如下所示:
The future will be better tomorrow. 这就是第一关的答案,代入,正确。
第二关:
第二关代码如图所示,根据代码可知,这次需要读入六个数据,也就是密码;我们就是根据代码找出这六个数据。如下图所示,我们知道代码是根据输入的六个数据,循环比较,每一次比较的结果都指向下一次比较的代码,最后一次会是代码进入下一关。我们知道,关键部分是%eax,%rbp和%rsp中的数据,三者关系,如下图累加过程,由第六句代码可以,循环的起点是0,以次相加,以次得到1 ,3 , 6 , 10 , 15,所以这6个数据就是密码,代入,通过。
第三关:
第三代码较长,但是代码第5句,可以看到贸然出现的地址0x40277e,我们可以看下其中内容,如下所示,“%d,%c,%d”所以这一关的密码是一个字母和两个数据。
继续看代码,可以在第6句到第11句代码中,发现这会有个比较,即是第一个数据的要求,第一个数据<7,否则会跳转到触发炸弹。所以得到第一个输入数字的要求。,而且在代码中,我们可以发现,代码中暗示我们使用0进行比较,所以,暂时确定第一个数据是0.
继续阅读代码,我们会发现,这题的答案就隐藏在代码中,根据代码中的比较条件,我们可知,第二次比较应是0x74,第三次比较应是0x6a,即是106。而且通过查看0想0x402790中的内容,进一步验证了我们的猜测,但是又知第二个应是字母,则根据下图的字母与ASCII码的对应关系知道,字母是t ,所以最后答案是0 t 106
第四关:
代码不多,但是调用了一个函数,函数如下所示。
通过代码可知,可以通过看0x402a2d的内容,知道这关的关键,如下所示,所以密码是俩个数字。有代码可以知道,fun4中返回的结果应是45,这样在后续比较中,就可以通过,从而进入下关,所以第二个数据就可以确定是45了,而且第一个数据是使fun4结果返回到45的数据,所以关键是了解fun4 的作用。
这个递归比较绕,而且运算比较多,不易在汇编中看出结果,可以如下,改写成C语言,而且知道这个输入数据必定是0到14的一个数据,为了得到最后结果,将整个函数编写为C语言代码,再加上已知条件,则得到输入数据是14,所以答案是14 45。
第五关:
根据代码来判断,我们要输入的是一个长度为6 的字符串(+9 那一句)。然后会经过一系列复杂的跳转,匹配的话就正常返回。而且有代码可知,关键在于0x402787和0x4027d0中的内容,如下所示。我们根据代码可以知道sabres就是解题关键。
但是,sabres并不是答案,我们在代码中可知,他们代表的是实际答案的偏移量,然后根据这个偏移量,在字母的ASCII码表中寻找相应偏移量的字母,得到gamfeg,这就是最后而定答案,正确。
第六关:
代码很长,但是我们可以看出,这是一个关于比较,排序的问题。排序的关键就在于0x604310地址中存储的内容。我们查看其中的内容,并将对应的输出,print出相应变量的内容,如下所示。
根据print出的内容,从小到大排序,有 1 2 3 6 4 5 ,这就是最后答案。
致此,前6关卡都通过了。
然后通过代码,我们知道,还有隐藏关卡。为了进入隐藏关卡,我们在第4关输入答案时,应在其后加上DrEvil,就可以了。
在这关中,我们可以看到,这个函数很关键,但是又很麻烦,根据第四关的经验,做如下转换:
int fun7(int *x, int y)
{
if (x == NULL)
return -1;
int ret = 0;
if (*x - y < 0)
{
ret = fun7(*(x + 0x10), y);
ret = ret * 2 + 1;
}
else if (*x == y)
{
return 0;
}
else
{
ret = fun7(*(x + 0x8), b);
ret *= 2;
}
}
我们需要是fun7的结果为7,则相应地址和递归的变化如下所示:
递归最深处的返回值肯定是0(mov $0x0,%eax eax=0)且那是*a1==a2,最外层返回值为7,则可得出如下反递归过程:(因为是7,所以肯定是2*a+1 而不是2a,后续步骤也是这样原理) (下面的a1不是一个固定地址,它是变换的,a2不变为我们输入的数)
(eax)*2+1=7-->eax=3 所以走下面的循环 有*a1<a2 a1+=0x10
(eax)*2==2-->eax=1 所以 走下面的循环 有*a1<a2 a1+=0x10
(eax)*2+1=1-->eax=0 所以 走下面的循环 有*a1<a2 a1+=0x10
*a1==a2 跳出
一共四层
现在的关键就是找到a1初始的地址,a1是由隐藏函数传入的,从隐藏函数中找到
0x604130 0x24
接下来
则使用p/x*(0x604130+0x10)得到了地址0x604170,这里存储的数字是0x32。
接下来是p/x *(0x604170+0x10)得到地址0x6041f0,这里存储的数字是0x6b。
最后一次,p/x *(0x6041f0+0x10),得到地址0x6042f0,这里存储的数字就是我们所要输入的0x3e9,也就是十进制的1001。
0x3e9大于0x24走下面循环 0x3e9大于0x32 走下面循环0x3e9大于0x6b走下面循环 0x3e9=0x3e9回退递归
且这个值减1后小于等于0x3e8,符合要求
输入,正确!
所有的关卡都解决。
更多推荐
所有评论(0)