导航
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了。