phase_4 反汇编代码
分析
我们还是从头开始看看这段函数做了哪些事情。
前期
首先,sub $0x18,%rsp 开辟了栈空间,%rsp,即栈指针减去0x18(即24),所指向的位置下移24,增加了6个储存空间。
然后,lea 0x8(%rsp),%rcx 将把0x8(%rsp)中的值存入寄存器 %rcx ,我们假设(%rsp + 8)值为x1,同理,lea 0xc(%rsp),%rdx 将地址%rsp + 12 中的值设为 x2。
接下来,lea 0x1760(%rip),%rsi 将 0x1760(%rip) 所代表的地址存入%rsi,根据行末的注释,地址的值为0x2aa5。那我们不妨看看,这个地址里面存了什么东西吧。
在倒数第二行,我们看到 “%d %d"。这段代码代表的是两个int整型数字,可能这个函数里要输入两个整数来操作。
然后我们继续,mov $0x0 ,%eax。很简单,这里就是将 %eax 赋初值为0,我们设 a = 0 ,然后调用接下来的函数0xec __isoc99_sscanf@plt。
接下来,cmp $0x2,%eax 则是在比较2与%eax中的值。下一行的 je 判断两者是否相等:若相等,则跳转至<phase_4+75>,继续执行函数;否则,炸弹爆炸。因此%eax中的值应该为2,即 a = 2,这里的意思应该和phase_3中的一样,即判断我们输入的是否是两个数字,根据“%d %d"也能进行如此猜测。
然后,我们跳到<phase_4+75>。这里在比较双字——$0xe与0xc(%rsp)中的值,即14和x2。
若$0xe<=x2,则跳转至<phase_4+41>;否则,炸弹爆炸。那么我们便确定了x2的范围,即 x2 < 14 。接下来,我们转到<phase_4+41>来看看
这里进行了三个赋值操作:先把$0xe(即15)赋给值进入寄存器%edx,即 x2 = 15;再将$0x0赋值进%esi,我们这里设%esi中的值为 t,即 t = 0;最后,将0xc(%rsp)中的值复制进%edi中,我们设%edi中的值为x3,则 x3 = x2。
接下来,它调用了一个函数< func4 >,我们来看看它是做什么的。
func4
同样,这里首先开辟了储存空间,%rsp地址减去8,增加了两个数据储存位置。
我们暂且把%ecx中的值设为 c ,而这也是函数 func4 中才有的局部变量。由接下来的三个操作,我们能得到:%eax = x2-t,c = %eax 。其中,调用了%edx,%esi中的参数。
而后,我们看到,后面4个目的操作数都是%ecx,我们尝试写在一起:c = (c >> ( $0x1f ) + %eax ) >> 1 + t 。
与之前三个操作合并在一起可以得到:c = ((( (x2-t) >> 31 ) + (x2-t))>>1 )+ t
接下来,又是比较 %edi和%ecx 的值 ,即x3 和 c 的大小,此处又调用了 %edi 中的参数:若 c > x3,则跳转至< func_4 +37>,即将 -0x1(%rcx)的值 存进%edx中,即 x2 = c -1,然后返回 func4,进入循环;否则使a = 0,比较%edi的值 x3 与 c ,若 c < x3,则跳转至< func_4 +49>,即将0x1(%rcx)的值存进%esi中,即 t = c +1,然后返回func4,进入循环,否则 add $0x8,%rsp,即退出函数。最后返回%eax中的值 a 。
总的来说,func4 调用了三个参数,分别是%edx,%esi与%edi,即x2,t,x3。但是 fun4 函数还没有结束,我们还要退出最初的第一层 fun4 函数:
跳转至< func_4 +37>的那一部分,在得到迭代结果后,add %eax,%eax 说明,需要把结果 乘以二 以作为最终的输出值。
跳转至< func_4 +49>的那一部分,在得到迭代结果后,lea 0x1(%rax,%rax,1),%eax 说明,需要把结果 乘以二再加一 以作为最终的输出值。
于是我们完成了整个的func函数
func4 的C语言描述
// %edx %esi %edi
int func4(int x2,int t,int x3){
int c;
c = ((( (x2-t) >> 31 ) + (x2-t))>>1 )+ t ;
if (c > x3)
return func4(c-1,t,x3)*2;
else {
if (c < x3)
return func4(x2,c+1,x3)*2+1;
else
return 0;
}
}
末尾
接下来让我们跳出 func4 函数,继续看最后的内容。
从 func4 中返回 %eax 的值 a 后,立即与 7 进行比较,若两者相等,则跳转至<phase_4+84>,否则炸弹爆炸。因此,func4 的返回值必然应该是 7 。
而后在<phase_4+84>又进行了一次比较:当$0x8(%rsp)的值 x1 != 7,则跳转至<phase_4+65>,炸弹爆炸;相等的话,则跳转至<phase_4+70>,结束函数。
解答
从最初来看,我们的确是要输入两个值以破解该阶段。
在最后“末尾”部分,许久没露脸的x1终于又出来了,而且是必须等于7的一个值。而另一个需要判断的值是 func4 的返回值,也是必须等于7。那么,我们就用一段代码试着寻找另一个输入值x2,使得 func4 返回值为 7 。
#include<stdio.h>
// %edx %esi %edi
int func4(int x2,int t,int x3){
int c;
c = ((( (x2-t) >> 31 ) + (x2-t))>>1 )+ t ;
if (c > x3)
return func4(c-1,t,x3)*2;
else {
if (c < x3)
return func4(x2,c+1,x3)*2+1;
else
return 0;
}
}
int main()
{
int i, result=0;
for (i = 0; i <= 14; i++) {
result = func4(14, 0, i);
if (result == 7)
printf("%d\n", i);
}
return -1;
}
最终,根据程序输出,我们能得到一个值:14。由此,我们确定了输入值一定是7和14。但是我们不能忽略的是,我们需要判断两者的输入顺序。由于栈按照后进后出的原则进行基本的数据操作,因此第一个进入sscanf中的数据是0xc(%rsp),第二个是0x8(%rsp);那么我们在输入时,就应该先输入x2,再输入x1,即 14 7。
事实也证明,我们成功破解了phase_4,那么,就让我们继续向下一个函数进发吧!