二进制炸弹实验

    第一篇博客,就为记录下最近的学习,从对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,符合要求

输入,正确!

所有的关卡都解决。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐