当前位置 博文首页 > astrotycoon:CS:APP二进制炸弹phase4

    astrotycoon:CS:APP二进制炸弹phase4

    作者:[db:作者] 时间:2021-08-17 13:12

    写在前面

    OK,已经完成一半任务了,继续吧。let's go!!!


    分析

    首先找到调用函数phase_4的代码:


    反汇编函数phase_4如下:


    跟phase_3同样的套路,先从我们的输入中获取两个int型整数。我们命令为num1和num2。

    接着判断sscanf函数的返回值,如果不等于2则触发炸弹,否则继续。

    接下来的几条指令看出,num1必须小于等于14,否则触发炸弹,难道本阶段又跟phase_3一样,存在多组答案? 不得而知,继续。

    接下来调用函数func4,参数分别通过寄存器%edi,%esi和%edx传递,值分别为num1,0和14。

    test ? %eax,%eax用于检测函数func4的返回值是否为0,如果不为0,跳转到0x0000000000401058处,触发炸弹,否则继续。

    cmpl ? $0x0,0xc(%rsp)用于检测num2是否为0,为0的话,跳转到0x000000000040105d处,函数结束,一切OK,否则触发炸弹。所以可以断定输入的num2必须为0。

    现在来看函数func4反汇编如下:


    第一眼瞅过去,函数内部尽然有调用自己,原来这是个递归函数。通过刚才的分析,func4接受3个参数,并且都是int型的,所以func4的原型应该是int func4(int a, int b, int c);

    mov ? ?%edx,%eax,将参数c存储在寄存器%eax中。

    sub ? ?%esi,%eax,用%eax中的值减去%esi,结果存在寄存器%eaxz中,即c - b的值存储在寄存器%eax中。

    为了直观,我们将%eax定义为一个局部变量int x,即int x = c - b;

    mov ? ?%eax,%ecx,shr ? ?$0x1f,%ecx,add ? ?%ecx,%eax,sar ? ?%eax 这几句整合起来就是x = (x>>31 + x) >> 1。

    lea ? ?(%rax,%rsi,1),%ecx,这句%rsi,意思是%ecx = %rax + %rsi * 1,其中%rax就是我们刚刚求得的x,%rsi是参数b。因此%ecx =?(x>>31 + x) >> 1 + b; 我们将寄存器%ecx定义一个局部变量,名为tmp,即int tmp = (x>>31 + x) >> 1 + b;将x带进去就是int tmp = ((c - b) >> 31 + (c -b)) >> 1 + b;?

    好,得到了tmp的值,就好办了,因为我们发现后面的汇编一直在使用tmp的值与参数a在比较。继续分析。

    cmp ? ?%edi,%ecx,就是用参数a和tmp值比较,即tmp - a,如果tmp小于等于a,则跳转到0x0000000000400ff2处执行。我根据接下来的汇编,将func4用C实现,大概如下:

    //                %edi    %esi   %edx
    static int func4(int a, int b, int c)
    {
        int tmp = (((c - b) + ((c - b) >> 31)) >> 1) + b;
    
        if (tmp <= a) {
            if (tmp <= a) {
                return (0);
            } else {
                return func4(a, tmp + 1, c) * 2 + 1;
            }
        } else {
            return func4(a, b, tmp - 1) * 2;
        }
    }
    发现没有,这里的逻辑很奇怪! 在tmp小于等于a后,紧接着判断tmp是否大于等a,很显然这里应该是判断tmp是否等于a的情况,所以代码修改如下:

    //                %edi    %esi   %edx
    static int func4(int a, int b, int c)
    {
        int tmp = (((c - b) + ((c - b) >> 31)) >> 1) + b;
    
        if (tmp <= a) {
            if (tmp == a) {
                return (0);
            } else {
                return func4(a, tmp + 1, c) * 2 + 1;
            }
        } else {
            return func4(a, b, tmp - 1) * 2;
        }
    }
    从前面的分析得到,只要func4最终的返回值是0就成功了。那我们先着眼于代码中的return 0语句吧。假设第一次调用func4,参数分别为num1,0和14,计算tmp得到7,那么如果num1等于7,就会走到return 0那里,函数返回0,bingo!因此num1是7,num2是0时就能过这一个phase。试试,果然可以。

    但是本phase应该还有其他答案,不然不会大费周折的搞什么递归了。我们可以写个测试程序,输出最后的所有答案,程序如下:

    #include <stdio.h>
    #include <stdlib.h>
    //                %edi    %esi   %edx
    static int func4(int a, int b, int c)
    {
        int tmp = (((c - b) + ((c - b) >> 31)) >> 1) + b;
    
        if (tmp <= a) {
            if (tmp == a) {
                return (0);
            } else {
                return func4(a, tmp + 1, c) * 2 + 1;
            }
        } else {
            return func4(a, b, tmp - 1) * 2;
        }
    }
    
    int main(int argc, const char *argv[])
    {
        int i, result;
    
        for (i = 0; i < 14; ++i) {
            result = func4(i, 0, 14);
            if (result == 0) {
                printf("%d\n", i);
            }
        }
        return 0;
    }
    程序输出0 1 3 7,因此本阶段的答案有4组,分别为(0,0)、(1,0)、(3,0)、(7,0)。

    本阶段的C程序源码完整如下:

    //                %edi    %esi   %edx
    static int func4(int a, int b, int c)
    {
    	int tmp = (((c - b) + ((c - b) >> 31)) >> 1) + b;
    
    	if (tmp <= a) {
    		if (tmp == a) {
    			return (0);	
    		} else {
    			return func4(a, tmp + 1, c) * 2 + 1;
    		}
    	} else {
    		return func4(a, b, tmp - 1) * 2;	
    	}
    }
    
    void phase_4(const char *input)
    {
    	//  0x8(%rsp)  0xc(%rsp)
    	int num1, num2;
    
    	//     				%rdi    %rsi   %rdx   %rcx 
    	int result = sscanf(input, "%d %d", &num1, &num2);
    	if (result != 2) {
    		explode_bomb();	
    	}
    
    	if (num1 > 0xe) {
    		explode_bomb();	
    	}
    	
    	//   %edi  %esi %edx
    	result = func4(num1, 0, 0xe);
    	if (result != 0) {
    		explode_bomb();	
    	}
    
    	if (num2 != 0) {
    		explode_bomb();	
    	}
    }

    总结:

    本阶段的func4汇编代码有点晦涩难懂,其中shr和sar分别为逻辑右移和算术右移,并且sar ? ?%eax的意思是%eax中的值右移一位,即除以2。

    知道了这几个生疏的汇编指令,应该没什么问题的。好,继续下一个阶段吧。

    cs
    下一篇:没有了