设为首页 加入收藏

TOP

二进制炸弹(第二次实验)(四)
2016-04-27 17:25:30 】 浏览:859
Tags:二进制 炸弹 第二 实验

oop 8048e70: mov 0x8(%esi),%edx /edx=esi->next/ 8048e73: mov (%esi),%eax /eax=esi->value/ 8048e75: cmp (%edx),%eax /比较eax和edx->value/ 8048e77: jge 8048e7e 8048e79: call 80494fc /链表元素出现升序,引爆/ 8048e7e: mov 0x8(%esi),%esi /esi=esi->next/ 8048e81: inc %edi 8048e82: cmp $0x4,%edi 8048e85: jle 8048e70 goto .loop ...

上述循环检查是否经上一步操作后的链表是否为非增的(从链表头开始)。
等价c代码如下:

typedef struct node{
    int value;
    int idx;
    struct node *next;
}node;
node node1={0xfd,1,&node2};
node node2={0x2d5,2,&node3};
node node3={0x12d,3,&node4};
node node4={0x3e5,4,&node5};
node node5={0xd4,5,&node6};
node node6={0x1b0,6,NULL};
void phase_6(input)
{
    int num[6];
    read_six_numbers(input,num);
    for (int edi=0;edi<=5;edi++)
    {
        if ((unsigned)(num[edi]-1)>5)
            explode_bomb();
        for (int ebx=edi+1;ebx<=5;ebx++)
            if (num[edi]==num[ebx])
                explode_bomb();
    }/*确保元素>0且<7*/
    node *p[6];
    for (int edi=1;edi<=5;edi++)
    {
        node *esi=&node1;
        for (int ebx=1;ebx
   
    next; p[edi]=esi; }/*令p[edi]为指向第num[edi]个节点的指针*/ node *esi=p[0]; for (int edi=1;edi<=5;edi++) { esi->next=p[edi]; esi=p[edi]; }/*令node(num[edi]).next=&node(num[edi+1])*/ esi=p[0]; for (int edi=0;edi<=4;edi++) { if (esi->value < esi->next->p->value) explode_bomb(); esi=esi->next; }/*确保重新排序的链表是不增序列*/ }
   

将链表元素按value值由大到小排序,得到链表如下:

node(4)->node(2)->node(6)->node(3)->node(1)->node(5)->NULL

故num={4,2,6,3,1,5},对应的,input值是以”4 2 6 3 1 5”为开头且后续字符串以非数字字符开头的字符串。

隐藏关

首先来分析phase_defused:

0804952c 
   
    : ... 8049533: cmpl $0x6,0x804b480 804953a: jne 804959f 
    
      ...
    
   

通过gdb 查看0x804b480处的值:

(gdb) x/20x 0x804b480
0x804b480 
   
    : 0x00000000 0x00000010 0x00000000 ...(省略)
   

阶段1通过后,再次查看此处的值:

0x804b480 
   
    : 0x00000001 0x00000010 0x00000000 0x7c010001...(省略)
   

重复上述操作,发现此处的值依次为2,3,4,5,6,因此这是输入字符串的个数,如果num_input_strings!=6,退出,反之继续分析:

0804952c 
   
    : ... 804953c: lea -0x50(%ebp),%ebx 804953f: push %ebx /push s/ 8049540: lea -0x54(%ebp),%eax 8049543: push %eax /push &n/ 8049544: push $0x8049d03 8049549: push $0x804b770 804954e: call 8048860 
    
      ...
    
   

用gdb查看0x8049d03,发现其值为”%d %s”再查看$0x804b770,其值如下:

(gdb) x/20x 0x804b770
0x804b770 
   
    : 0x00000039 0x00000000 0x00000000 0x00000000...(省略)
   

此时如果改变阶段4的输入,同样可以发现此处的值总是等于阶段4的输入字符串,说明read_line在这里存储了输入字符串的副本!相当于调用了sscanf(input,”%d %s”,&n,s),并且如果读入数据个数不是2就退出,接下来继续分析:

0804952c 
   
    : ... 804955e: push $0x8049d09 /pattern/ 8049563: push %ebx /s/ 8049564: call 8049030 
    
      ...
    
   

利用gdb可以判断此处将s和”austinpowers”比较,如果不同就退出,相同就调用secret_phase,下面分析secret_phase:

08048ee8 
   
    : ... 8048eef: call 80491fc 
    
      8048ef4: push $0x0 8048ef6: push $0xa /十进制/ 8048ef8: push $0x0 8048efa: push %eax /input/ 8048efb: call 80487f0 <__strtol_internal@plt> ...
    
   

上面的代码是将input以十进制转换为对应的数,之后截断为int类型,判断其返回值x是否在1~1001(包括边界值),是则继续,反之引爆,继续分析:

08048ee8 
   
    : ... 8048f17: push %ebx /x/ 8048f18: push $0x804b320 /&n1/ 8048f1d: call 8048e94 
    
      ...
    
   

首先我们用gdb查看0x804b320处的值:

(gdb) x/3x 0x804b320
0x804b320 
   
    : 0x00000024 0x0804b314 0x0804b308 (gdb) x/3x 0x804b314 0x804b314 
    
     : 0x00000008 0x0804b2e4 0x0804b2fc (gdb) x/3x 0x804b308 0x804b308 
     
      : 0x00000032 0x0804b2f0 0x0804b2d8
     
    
   

可以推测n1,n21,n22以及他们指向的节点都是树的节点,其结构如图所示:
这里写图片描述
因此可以推断,该处调用了fun7(&n1,x),fun7的原型为<http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> int fun7(tnode *pnode,int x);

下面分析fun7:

08048e94 
   
    : ... 8048ea0: test %edx,%edx /pnode是否为NULL/ 8048ea2: jne 8048eb0 
    
      /不是,继续/ 8048ea4: mov $0xffffffff,%eax /是的,返回-1/ 8048ea9: jmp 8048ee2 
     
       ...
     
    
   

上面的代码保证递归到树的底层时,返回-1,继续分析:

08048e94 
   
    : ... (pnode in %edx) 8048eb0: cmp (%edx),%eax /比较x,pnode->value/ 8048eb2: jge 8048ec5 
    
      8048eb4: add $0xfffffff8,%esp /x
     
      value/ 8048eb7: push %eax /push x/ 8048eb8: mov 0x4(%edx),%eax /eax= pnode->l/ 8048ebb: push %eax /push pnode->l/ 8048ebc: call 8048e94 
      
        8048ec1: add %eax,%eax /return 2*fun7(pnode->l,x)/ 8048ec3: jmp 8048ee2 
       
         8048ec5: cmp (%edx),%eax /x>=pnode->value,继续比较/ 8048ec7: je 8048ee0 
        
          8048ec9: add $0xfffffff8,%esp /x>pnode->value/ 8048ecc: push %eax /push x/ 8048ecd: mov 0x8(%edx),%eax /eax=pnode->r/ 8048ed0: push %eax /push pnode->r/ 8048ed1: call 8048e94 
         
           8048ed6: add %eax,%eax 8048ed8: inc %eax /return 2*fun7(pnode->r,x)+1/ 8048ed9: jmp 8048ee2 
          
            8048edb: nop 8048edc: lea 0x0(%esi,%eiz,1),%esi /esi=esi/ 8048ee0: xor %eax,%eax /x==pnode->value,return 0/ ...
          
         
        
       
      
     
    
   

从上面可以看出有两种可能的递归调用路径,下面是整个隐藏关等价c语言代码:

char *input_strings_4;
int  num_input_strings;
typedef struct tnode{
    int value;
    struct tnode *l,*r;
}tnode;
void secret_phase();
void phase_defused()
{
    if (num_input_strings!=6)
        return ;
    int n;
    char s[80];
    if (sscanf(input_strings_4,"%d %s",&n,s)!=2)
    {
        printf("Congratulations! You've defused the bomb!\n");
        return 0;
    }
    if (strings_not_equal(s,"austinpowers"))
    {
        printf("Congratulations! You've defused the bomb!\n");
        return 0;
    }
    printf("Curses, you've found the secret phase!\n");
    printf("But finding it and solving it are quite different...\n");
    secret_phase();
    printf("Congratulations! You've defused the bomb!\n");
}
int fun7(tnode *pnode,int x)
{
    if (pnode==NULL)
        return -1;
    if (x
   
    value) return 2*fun7(pnode->l,x); if (x!=s->value) return 2*fun7(pnode->r,x)+1; return 0; } void secret_phase() { input=read_line(); int x=(int)__strtol_internal(input,0,10,0); if ((unsigned)(x-1)>1000) explode_bomb(); if (fun7(&n1,x)!=7) explode_bomb(); printf("Wow! You've defused the secret stage!\n"); }
   

fun7的返回值是7,由此可以推知,递归产生的返回值依次是0,1,3,7,即递归是沿着最右边的路径下降的,并且在最后一层x=pnode->value=1001,故可推知正确的字符串是以”1001”为开头且后续字符串以非数字字符的字符串。
综上所述,解除各阶段炸弹的一种输入为:
这里写图片描述
运行结果为:
这里写图片描述

首页 上一页 1 2 3 4 下一页 尾页 4/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇OpenCV实践之路――行人检测 下一篇C++学习笔记之四 复合类型1

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

最新文章

热门文章

C 语言

C++基础

windows编程基础

linux编程基础

C/C++面试题目