BombLab Phase-3 & Phase-4 &Phase-5

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Gease_Gg/article/details/83378171

导航

BombLab Phase-1 & Phase-2

BombLab phase-6 & secret_phase

Phase_3

关于风格改变:因为一段段分析代码可以逻辑清晰有序一点,那么就摒弃了以前先上汇编代码再画栈帧图的方式,我们直接给出函数调用的所有栈帧,这样读者有个大概印象后再看后面的汇编代码会逻辑清晰一点。

  • Phase_3栈帧图

 

  • Scanf栈帧

因为scanf函数是动态链接库的函数,比较特殊,不方便直接查看反汇编,但我们可以画出它的栈帧图如下:

 反汇编phase_3函数

1.

开栈

首先,是标准的ebp进栈和栈扩张操作,我们看到栈扩张了40个字节。

之后我们可以看到这段代码将 ebp-0x10 、ebp-0xc 、 0x804a23e 、ebp+8(也就是输入字符串的地址) 依此放在了 esp+0xc、esp+0x8、esp+0x4、esp的位置,把这些值都放在esp上面的位置,我们可以猜想这肯定是为了调用scanf函数用的。

那么0x804a23e是什么?放在scanf上什么作用呢?我们查看下内存:

第一个参数是我们传入的是我们的输入,第二个是“%d %d”,说明我们输入的是两个正整数,存放在了ebp-0x10 、ebp-0xc这两个位置。

 

2.

scanf函数调用返回结果

Scanf函数返回的结果保存在eax中,至于scanf函数返回的结果到底是什么我们之后再谈。现在我们知道返回结果 eax>1 炸弹才不会爆炸。

 

3.

这边说明输入的第一个值一定要<=7,否则就爆炸。

 

4.

我们可以看到这个跳转语句是根据eax的取值进行跳转。而eax里存放的是ebp+0xc地址的值,也就是我们输入的第一个数。那么当我们输入不同的数它将跳转到不同的位置开始。而我们输入的数只可能是 0 1 2 3 4 5 6 7。那么我们依此看看这几个数对应地址存放了什么东西:

我们可以看到是7个地址,而且七个跳转地址分别也可以在程序中找到。

那假设我们输入的是0,那么我们将跳转到0x8048f12的位置。我们从这个位置继续往下运算。

 

5.

我们发现eax赋值完毕后,又进行了跳转。

之后又进行了一些列的加减运算:

Eax=0x314-0x35a+0x2ef-0x216+0x216-0x216+0x216-0x216=147

最后eax中的值为147.之后又进行跳转。

到这里我们突然发现输入的第一个参数的值必须要 <5 ,所以之前那个《=7 还不够,还好我们输入的是0,不会爆炸。

接着,它又拿我们输入的第二个数和eax中的值进行比较,如果不相等就爆炸,也就是说我们输入的第二个数必须等于147.

函数结束!

我们测试结果:

成功!

 

注:

但是之前我们也提到了,输入的第一个数的值只要<5就可以了,所以这关不仅仅是真么一个解,还有对应的另外4个解,而每个输入的第一个数都回决定第二个数的值,经过计算,分别的对应的几个解为:

0,147),(1,-641),(2,217),(3,-534),(4,0),(5,-534


Phase_4

  • phase_4栈帧

  • Scanf函数栈帧

可以看到,第四阶段的栈帧和第三阶段是一摸一样的。

Phase_4和phase_3都是一开始要求输入两个整数。

下面进phase_4的具体分析

1.

常规得,这些都是为了调用scanf函数准备,可以看见,输入的两个数保存在了ebp-0x10和 ebp-0xc的位置。

 

2.

从这里也可以看出,输入不输入两个数,就爆炸了。

 

3.

有两个跳转,第一是输入的第一个数为负数爆炸,输入的数>15也爆炸。

所以现在看来输入的第一个数应该在[0,15]之间。

 

4.

可以看到又要调用func4函数了,所以这里也是为了在做参数传递给func4。可以看到传给func4的三个参数分别为(你输入的第一个数,0,14)

 

5.

Func返回后,要求返回值必须为1,不然就爆照。

当返回值为1,又会拿你输入的第二个数(ebp-0x10)和1比较,如果等于1,则函数结束,不为1,则又爆炸。

总结来看,我们要输入两个数,第二个数必须为1,并且我们输入的第一个数又要使func4的返回值为1,并且这个数在0~15之间,那么现在的目标转移到func4上面。

 

6.

func4进行反汇编

没想到func4函数汇编还要长,我们还是先给出栈帧:

      

麻烦的是这是一个递归调用,并且我们观察到函数里面递归的地方不只一个,情况比较复杂,所以我们简略画一个栈帧图,而读者暂且明白这是一个递归调用即可。

 

7.Func4代码分析

 

标准函数开头,注意,寄存器%ebx, %esi, %edi被划分为由被调用者保存的寄存器。之后,分别将参数1,参数2,参数3赋给了 edx(你输入的数),eax(0),ebx(14)。

8.

这里全是算术运算,为表述清楚,我们将写出算术过程:

Ecx=ebx=14;

Ecx=ecx-eax=14-0=14;

Esi=ecx=14;

Esi=0=esi>>31;(逻辑右移)

Ecx=14+0=14;

Ecx=7=ecx/2=ecx>>1;(算术右移)

Ecx=ecx+eax=7+0=7;

将7和你输入的第一个数比较,

如果输入数>7,跳转到func4+64;

那么当大于7,跳转到这里:

eax置为0,即返回值为0,然后比较ecx和edx,

7>=输入的数则又跳转

这里函数就返回了,并且返回值为0;

7<输入的数则进行下面这步:

又是依此递归入口:func4(你输入的数,7+1,14)

但是一定要注意返回值这个递归的返回值最后还要+1,因为有个lea语句!

那么如果 <=7则到这里:

 

Ecx=7-1=6;

然后又递归func4(edx=输入的数, 0 , 6);

假设递归完成,得到返回值之后,

返现他要将返回值*2再返回。

注意,以上都是我第一层的递归分析,当递归深入下去,其中的7和其他常数都会不同。鉴于以上的代码分析,直觉告诉我们,通过汇编反推结果几乎不可能,你几乎肯定会出错,所以我们转化为C代码:

反推没有思路,所以又要走另一条路:枚举

因为我们知道了x的范围是0-15,所以我们只要从0-15来枚举返回值,找到返回值为1的情况即可。

所以,我们要用如下代码测试返回值:

输出结果:

终于找到了三个符合的数 8、9、11,我们测试看看:

 

Phase_5

  • Phase_5 栈帧

由于scanf函数栈帧和前面一摸一样,就不重复了

1.Phase_5汇编代码分析

如常,这也是常规的开栈操作。

 

2.

这个样式的代码我们也见过了很多,这个是为scanf函数调用做准备,唯一注意的是需要输入几位数,但根据临时变量的数量来看,我们几乎确定就是要输入两个数。

不信,我们查看格式控制串内容:

的确是输入两个数。

 

3.

将scanf返回值和1比较,即如果只输入了一个数,那么炸弹爆炸。说明输入两个数。

 

4.

第一二行代码是取出了我们输入的第一个数,然后将这个数和 0xf进行与运算,这个与运算的意义是取出该数二进制表示的低四位的值,然后又赋值回去,将我们输入的数进行了改变。例如:

0xf  & 31 = 1111 & 11111=1111;

之后,我们发现它又将 运算结果和0xf做比较,如果相等的话就跳转到+106的地方,就爆炸了,所以我们输入的第一个数的二进制表达的低四位一定不能是1111.

 

5.

这里我们观察到了最后一个跳转语句是往上跳转,说明这里开始是一个循环语句。

我们可以这样来看这个循环:

 

这里说明了如果eax的最终运算结果不是15就会一直循环,并且我们知道eax的初值是<15的,而eax每次循环之后的值为4*eax+ebx地址存放的值。

 

6.

这里,我们将第二个变量值赋值为15了,然后我们看edx的值是多少,从5中我们可以明显看出edx是循环计数的临时变量,相当于c中的i,如果edx!=15,也要爆炸,所以这个循环必须执行满15次才不会爆照。

那这样,综合5中的信息,我们得到的就是  这个循环我们必须要执行15次,并且执行完毕后eax的值必须为15,否则失败。

 

7.

这里将ecx与我们输入的第一个数进行比较(从5中我们知道ecx是我们保存计算结果的一个变量),如果相等则成功,,否则,爆炸。

 

8.

好了,代码我们大致分析完了,这道题的意图就是让我们输入两个数,我们要对输入的第二个数进行一系列操作——与操作+循环,并且循环里参与运算的还有一个变量,在循环完毕后,这个变量值要和输入的第一个数值相等。就是让我们找到这样的一个数对。那么怎么找呢?

关键点就在于我们之前看到的那个5中赋给ebx地址值,我们看到ecx的最终取值,都是基于ebx这个地址再+eax*4的偏移量去取值,那么我们猜想ebx这个地址值之后肯定有一个值等于15;

所以我们开始就查看一个ebx地址之后存放了哪些值?

 

当我们去查看它后面20个数字的时候,是不是发现了什么不得了的事?原来这些值都被保存在了一个数组里!而且一共16个数字!

我们正着去找怎么得到15这很难,因为我们都不知道输入的数是多少,那么我们就因该转化一下思路,反过来找!

我们的目的是15,我们发现15在距离数组首地址偏移了6个位置,而我们知道一个int占4字节,所以怪不得它eax要乘以4!那么我们就可以确定上一个eax的值一定为6.

逆推得到:15->6

好,现在eax=6,那么我们在数组又去找6,发现6在首地址偏移14个字节,同理,我们可以确定下一个eax=14;

更新:15->6->14

同理,我们可以一直寻找,并且寻找15次,更新这个序列,我们得到逆推序列为:

15->6->14->2->1->10->0->8->4->9->13->11->7->3->12->5

最后,原来eax=5,这就说明我们输入的第一个数二进制表达的低四位一定是:

0101.

推完了第一个数字,现在看看第二个数字,第二个数字是同时在循环进行时进行运算的,并且也就是对eax的一个累加过程,所以我们现在有了运算序列,可以很轻松的计算出ecx的值:

但是要注意:

Eax是先变化之后,才被ecx累加,所以eax的初值5并没有被加到ecx中!

Ecx=12+3+7+11+13+9+4+8+0+10+1+2+14+6+15=115;

所以第二个数为115;

综,第一个数,我们只要凑出一个低位为0101的值即可,那么很简单我们就取5,第二个数已经固定是115,那么就输入115.

结果:

Bingo!

正确。

提示:此题的第一个数的答案可以变化,因为我们只要最低的四位数保持不变,所以每次你可以在5的基础上加上16的整数倍,因为0101既然不能变,那么最低的累加单位只能是10000,即16了。

猜你喜欢

转载自blog.csdn.net/Gease_Gg/article/details/83378171
今日推荐