CSAPP: Bomb Lab 实验解析

这是CSAPP课本配套的第二个实验,主要任务是“拆炸弹”。所谓炸弹,其实就是一个二进制的可执行文件,要求输入六个字符串,每个字符串对应一个phase。如果字符串输入错误,系统就会提示BOOM!!!
解决这次实验需要将二进制文件反汇编,通过观察理解汇编语言描述的程序行为来猜测符合条件的字符串。

Linux & GDB Basic Commands

  • 反汇编指令:
    objdump -d [objfile]
    其中objfile为待解析的二进制程序。
    -d
    --disassemble
    Display the assembler mnemonics for the machine instructions from objfile.
    This option only disassembles those sections which are expected to contain instructions.

  • GDB Commands:

    • set args [inputfile]
      输入重定向到指定的文件

    • run
      启动gdb

    break [function_name] or *[address]
    在某个函数或某条指令处下断点

    continue
    继续调试

    x /[Length][Format] [Address expression]
    以给定的参数查看内存中的内容。
    详见:
    X command

phase_1

在文件夹内打开终端,执行objdump -d bomb > bomb.txt获取bomb程序反汇编的全部内容。
以下是第一关phase_1函数对应的汇编代码:

0000000000400ee0 <phase_1>:
  400ee0:   48 83 ec 08             sub    $0x8,%rsp
  400ee4:   be 00 24 40 00          mov    $0x402400,%esi
  400ee9:   e8 4a 04 00 00          callq  401338 <strings_not_equal>
  400eee:   85 c0                   test   %eax,%eax
  400ef0:   74 05                   je     400ef7 <phase_1+0x17>
  400ef2:   e8 43 05 00 00          callq  40143a <explode_bomb>
  400ef7:   48 83 c4 08             add    $0x8,%rsp
  400efb:   c3                      retq   

0x400ee9处调用了函数strings_not_equal(%rdi,%rsi),在这之前向%esi移入了一个地址0x402400,猜测%rdi处是我们之前输入的字符串。
使用gdb bomb开始调试程序,首先在explode_bombphase_1函数处设置断点。

输入的字符串为hello!

观察以%rdi为地址的字符串验证了之前的假设。


%rdi处的字符串

所以经过分析,phase_1只是简单地把输入的字符串与0x402400处的字符串相比较,如果相等则拆弹成功。
使用命令x/s 0x402400得到待比较的字符串。

答案

到这里第一关拆弹成功。

phase_2

phase_2函数对应的汇编代码如下:

0000000000400efc <phase_2>:
  400efc:   55                      push   %rbp
  400efd:   53                      push   %rbx
  400efe:   48 83 ec 28             sub    $0x28,%rsp
  400f02:   48 89 e6                mov    %rsp,%rsi
  400f05:   e8 52 05 00 00          callq  40145c <read_six_numbers>
#由函数名可以看出这次要求输入的是六个数字,以上部分结束后,%rsp即为输入的第一个数字的地址
-----------------------分割线----------------------------------------  
  400f0a:   83 3c 24 01             cmpl   $0x1,(%rsp) #要求第一个数字必须为1
  400f0e:   74 20                   je     400f30 <phase_2+0x34>
  400f10:   e8 25 05 00 00          callq  40143a <explode_bomb>
  400f15:   eb 19                   jmp    400f30 <phase_2+0x34>
  400f17:   8b 43 fc                mov    -0x4(%rbx),%eax
  400f1a:   01 c0                   add    %eax,%eax
  400f1c:   39 03                   cmp    %eax,(%rbx)
  400f1e:   74 05                   je     400f25 <phase_2+0x29>
  400f20:   e8 15 05 00 00          callq  40143a <explode_bomb>
  400f25:   48 83 c3 04             add    $0x4,%rbx
  400f29:   48 39 eb                cmp    %rbp,%rbx
  400f2c:   75 e9                   jne    400f17 <phase_2+0x1b>
  400f2e:   eb 0c                   jmp    400f3c <phase_2+0x40>
  400f30:   48 8d 5c 24 04          lea    0x4(%rsp),%rbx
  400f35:   48 8d 6c 24 18          lea    0x18(%rsp),%rbp
  400f3a:   eb db                   jmp    400f17 <phase_2+0x1b>
-----------------------分割线---------------------------------------- 
  400f3c:   48 83 c4 28             add    $0x28,%rsp
  400f40:   5b                      pop    %rbx
  400f41:   5d                      pop    %rbp
  400f42:   c3                      retq   
 

在内存中输入的六个数字分布如下:


Layout of Six Numbers

分割出来的代码用类C语言可以表示为:

void phase_2()
{//Number in %rsp,Edge in %rbp,(%register)表示寻址得到的值
    if((%rsp)==1)   //保证第一个数是1
    {
        goto Label_400f30;
    }
Label_400f17:
    %eax=(%rbx-0x4);
    %eax=2*%eax;
    if((%rbx)!=%eax)   //保证后一个数为前一个数的两倍
    {
        explode_bomb();
    }
    %rbx=%rbx+0x4;
    if(%rbx==%rbp)
    {
        return;
    }
    else
    {
        goto Label_400f17;
    }
Label_400f30:
    %rbx=%rsp+0x4;
    %rbp=%rsp+0x18;
    goto Label_400f17;
}

经过以上推理得到所要求的六个数是

1 2 4 8 16 32

phase_3

0000000000400f43 <phase_3>:
  400f43:   48 83 ec 18             sub    $0x18,%rsp
  400f47:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx
  400f4c:   48 8d 54 24 08          lea    0x8(%rsp),%rdx
  400f51:   be cf 25 40 00          mov    $0x4025cf,%esi
  400f56:   b8 00 00 00 00          mov    $0x0,%eax
  400f5b:   e8 90 fc ff ff          callq  400bf0 <__isoc99_sscanf@plt>
  400f60:   83 f8 01                cmp    $0x1,%eax
  400f63:   7f 05                   jg     400f6a <phase_3+0x27>
  400f65:   e8 d0 04 00 00          callq  40143a <explode_bomb>
  400f6a:   83 7c 24 08 07          cmpl   $0x7,0x8(%rsp)
  400f6f:   77 3c                   ja     400fad <phase_3+0x6a>
  400f71:   8b 44 24 08             mov    0x8(%rsp),%eax
  400f75:   ff 24 c5 70 24 40 00    jmpq   *0x402470(,%rax,8)
  400f7c:   b8 cf 00 00 00          mov    $0xcf,%eax   #当第一个数为0时跳转到此处
  400f81:   eb 3b                   jmp    400fbe <phase_3+0x7b>
  400f83:   b8 c3 02 00 00          mov    $0x2c3,%eax #当第一个数为2时跳转到此处
  400f88:   eb 34                   jmp    400fbe <phase_3+0x7b>
  400f8a:   b8 00 01 00 00          mov    $0x100,%eax  #第一个数为3时跳转到此处
  400f8f:   eb 2d                   jmp    400fbe <phase_3+0x7b>
  400f91:   b8 85 01 00 00          mov    $0x185,%eax #第一个数为4时跳转到此处
  400f96:   eb 26                   jmp    400fbe <phase_3+0x7b>
  400f98:   b8 ce 00 00 00          mov    $0xce,%eax #第一个数为5时跳转到此处
  400f9d:   eb 1f                   jmp    400fbe <phase_3+0x7b>
  400f9f:   b8 aa 02 00 00          mov    $0x2aa,%eax #第一个数为6时跳转到此处
  400fa4:   eb 18                   jmp    400fbe <phase_3+0x7b>
  400fa6:   b8 47 01 00 00          mov    $0x147,%eax
  400fab:   eb 11                   jmp    400fbe <phase_3+0x7b>
  400fad:   e8 88 04 00 00          callq  40143a <explode_bomb>
  400fb2:   b8 00 00 00 00          mov    $0x0,%eax
  400fb7:   eb 05                   jmp    400fbe <phase_3+0x7b>
  400fb9:   b8 37 01 00 00          mov    $0x137,%eax #当第一个数为1时跳转到此处
  400fbe:   3b 44 24 0c             cmp    0xc(%rsp),%eax
  400fc2:   74 05                   je     400fc9 <phase_3+0x86>
  400fc4:   e8 71 04 00 00          callq  40143a <explode_bomb>
  400fc9:   48 83 c4 18             add    $0x18,%rsp
  400fcd:   c3                      retq 

0x400f51处的指令move $0x4025cf,%esi入手,观察其值:

字符串格式.png

可见应是输入两个整数。在0x400f6a处,将0x8+%rsp处的值与0x7相比较,要求其小于7,又ja是针对无符号数的跳转指令,可知其大于等于0。
使用gdb查看%rsp+0x8处的值可以知道要求输入的两个值在内存中的分布如下:
两个数的内存分布

0x400f75处的跳转指令依据第一个数的值做间接跳转,利用GDB可以得到跳转后的位置,如注释所示。之后将第二个数与分支中得到的值相比较,若相等则拆弹成功。
综上,全部的解如下表所示:

可能的答案

phase_4

000000000040100c <phase_4>:
  40100c:   48 83 ec 18             sub    $0x18,%rsp
  401010:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx
  401015:   48 8d 54 24 08          lea    0x8(%rsp),%rdx
  40101a:   be cf 25 40 00          mov    $0x4025cf,%esi
  40101f:   b8 00 00 00 00          mov    $0x0,%eax
  401024:   e8 c7 fb ff ff          callq  400bf0 <__isoc99_sscanf@plt>
  401029:   83 f8 02                cmp    $0x2,%eax
  40102c:   75 07                   jne    401035 <phase_4+0x29>
  40102e:   83 7c 24 08 0e          cmpl   $0xe,0x8(%rsp)
  401033:   76 05                   jbe    40103a <phase_4+0x2e>
  401035:   e8 00 04 00 00          callq  40143a <explode_bomb>
  40103a:   ba 0e 00 00 00          mov    $0xe,%edx
  40103f:   be 00 00 00 00          mov    $0x0,%esi
  401044:   8b 7c 24 08             mov    0x8(%rsp),%edi
  401048:   e8 81 ff ff ff          callq  400fce <func4>
  40104d:   85 c0                   test   %eax,%eax
  40104f:   75 07                   jne    401058 <phase_4+0x4c>
  401051:   83 7c 24 0c 00          cmpl   $0x0,0xc(%rsp)
  401056:   74 05                   je     40105d <phase_4+0x51>
  401058:   e8 dd 03 00 00          callq  40143a <explode_bomb>
  40105d:   48 83 c4 18             add    $0x18,%rsp
  401061:   c3                      retq   

使用gdb运行至如下图所示的步骤:

调用sscanf函数后

查看0x4025cf处的字符串如下所示:
对输入字符串的格式要求

所以这次也是要求我们输入两个字符串。
由下图可以知道输入的数据存放在%rsp+0x8和%rsp+0xc处。
输入数据的位置

phase_4部分结束对func4的调用后立刻验证%eax和%rsp+0xc是否为0,可见关键在于:

  • 确保func4执行完毕后%eax的值为0
  • 输入的第二个数为0

接下来我们看func4的函数部分:

0000000000400fce <func4>:
  400fce:   48 83 ec 08             sub    $0x8,%rsp
  400fd2:   89 d0                   mov    %edx,%eax
  400fd4:   29 f0                   sub    %esi,%eax
  400fd6:   89 c1                   mov    %eax,%ecx
  400fd8:   c1 e9 1f                shr    $0x1f,%ecx
  400fdb:   01 c8                   add    %ecx,%eax
  400fdd:   d1 f8                   sar    %eax                
  400fdf:   8d 0c 30                lea    (%rax,%rsi,1),%ecx
  400fe2:   39 f9                   cmp    %edi,%ecx
  400fe4:   7e 0c                   jle    400ff2 <func4+0x24>      //skip recursion
  400fe6:   8d 51 ff                lea    -0x1(%rcx),%edx
  400fe9:   e8 e0 ff ff ff          callq  400fce <func4>
  400fee:   01 c0                   add    %eax,%eax
  400ff0:   eb 15                   jmp    401007 <func4+0x39>
  400ff2:   b8 00 00 00 00          mov    $0x0,%eax
  400ff7:   39 f9                   cmp    %edi,%ecx
  400ff9:   7d 0c                   jge    401007 <func4+0x39>
  400ffb:   8d 71 01                lea    0x1(%rcx),%esi
  400ffe:   e8 cb ff ff ff          callq  400fce <func4>
  401003:   8d 44 00 01             lea    0x1(%rax,%rax,1),%eax
  401007:   48 83 c4 08             add    $0x8,%rsp
  40100b:   c3                      retq  

根据X86汇编语言的约定,%rdi、%rsi、%rdx分别为第一、二和第三个参数使用的寄存器。%rax作为返回值所在的寄存器。func4变换为C语言代码如下:

void func4(int x,int y,int z)
{//x in %rdi,y in %rsi,z in %rdx,t in %rax,k in %ecx
 //y的初始值为0,z的初始值为14
  int t=z-y;
  int k=t>>31;
  t=(t+k)>>1;
  k=t+y;
  if(k>x)
  {
    z=k-1;
    func4(x,y,z);
    t=2t;
    return;
  }
  else
   {
     t=0;
     if(k<x)
     {
        y=k+1;
        func4(x,y,z);
        t=2*t+1;
        return;
     }
     else
     {
         return;
     }
   }
}

易得当x=k时,%eax=t=0。
所以第一个参数为7,拆弹成功。

phase_5

0000000000401062 <phase_5>:
  401062:   53                      push   %rbx
  401063:   48 83 ec 20             sub    $0x20,%rsp
  401067:   48 89 fb                mov    %rdi,%rbx            #string address in %rdi,%rbx
  40106a:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  401071:   00 00 
  401073:   48 89 44 24 18          mov    %rax,0x18(%rsp)
  401078:   31 c0                   xor    %eax,%eax
  40107a:   e8 9c 02 00 00          callq  40131b <string_length>
  40107f:   83 f8 06                cmp    $0x6,%eax  #string's length=6
  401082:   74 4e                   je     4010d2 <phase_5+0x70>
  401084:   e8 b1 03 00 00          callq  40143a <explode_bomb>
  401089:   eb 47                   jmp    4010d2 <phase_5+0x70>
  --------------------------Key Section Start-------------------------------
  40108b:   0f b6 0c 03             movzbl (%rbx,%rax,1),%ecx  #access single character using %rax as index
  40108f:   88 0c 24                mov    %cl,(%rsp)          
  401092:   48 8b 14 24             mov    (%rsp),%rdx         #move character to %rdx
  401096:   83 e2 0f                and    $0xf,%edx           #get the second half of the character
  401099:   0f b6 92 b0 24 40 00    movzbl 0x4024b0(%rdx),%edx 
  4010a0:   88 54 04 10             mov    %dl,0x10(%rsp,%rax,1)#move certain characters from %rsp+10 to %rsp+15 
  4010a4:   48 83 c0 01             add    $0x1,%rax
  4010a8:   48 83 f8 06             cmp    $0x6,%rax            #loop 6 times
  4010ac:   75 dd                   jne    40108b <phase_5+0x29>
---------------------------Key Section End----------------------------------
  4010ae:   c6 44 24 16 00          movb   $0x0,0x16(%rsp)      #last '0' in the string
  4010b3:   be 5e 24 40 00          mov    $0x40245e,%esi       #standard string to compare
  4010b8:   48 8d 7c 24 10          lea    0x10(%rsp),%rdi
  4010bd:   e8 76 02 00 00          callq  401338 <strings_not_equal>
  4010c2:   85 c0                   test   %eax,%eax
  4010c4:   74 13                   je     4010d9 <phase_5+0x77>
  4010c6:   e8 6f 03 00 00          callq  40143a <explode_bomb>
  4010cb:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  4010d0:   eb 07                   jmp    4010d9 <phase_5+0x77>
  4010d2:   b8 00 00 00 00          mov    $0x0,%eax
  4010d7:   eb b2                   jmp    40108b <phase_5+0x29>
  4010d9:   48 8b 44 24 18          mov    0x18(%rsp),%rax
  4010de:   64 48 33 04 25 28 00    xor    %fs:0x28,%rax
  4010e5:   00 00 
  4010e7:   74 05                   je     4010ee <phase_5+0x8c>
  4010e9:   e8 42 fa ff ff          callq  400b30 <__stack_chk_fail@plt>
  4010ee:   48 83 c4 20             add    $0x20,%rsp
  4010f2:   5b                      pop    %rbx
  4010f3:   c3                      retq    

如以上代码中的注释所示,phase_5接受由六个字符组成的字符串,根据获得每个字符串二进制表示的后半截,以此作为偏移量从0x4024b0处的字符串中获得对应的字母。将这些字母构成的字符串与0x40245e处的字符串相比较,如果相等则拆弹成功。

两处内存中的字符串

phase_6

将第六阶段的汇编代码按照功能分为几个部分。

  • Section 1
  --------------------Section 1 Start------------------------------
  4010fc:   48 83 ec 50             sub    $0x50,%rsp
  401100:   49 89 e5                mov    %rsp,%r13
  401103:   48 89 e6                mov    %rsp,%rsi
  401106:   e8 51 03 00 00          callq  40145c <read_six_numbers>
  40110b:   49 89 e6                mov    %rsp,%r14        #Store the begining adress of input string
  40110e:   41 bc 00 00 00 00       mov    $0x0,%r12d
  401114:   4c 89 ed                mov    %r13,%rbp  
  401117:   41 8b 45 00             mov    0x0(%r13),%eax   #the element
  40111b:   83 e8 01                sub    $0x1,%eax
  40111e:   83 f8 05                cmp    $0x5,%eax        #less or equal than 0x6
  401121:   76 05                   jbe    401128 <phase_6+0x34>
  401123:   e8 12 03 00 00          callq  40143a <explode_bomb>
  401128:   41 83 c4 01             add    $0x1,%r12d       #count++
  40112c:   41 83 fc 06             cmp    $0x6,%r12d
  401130:   74 21                   je     401153 <phase_6+0x5f>
  401132:   44 89 e3                mov    %r12d,%ebx
  401135:   48 63 c3                movslq %ebx,%rax
  401138:   8b 04 84                mov    (%rsp,%rax,4),%eax
  40113b:   39 45 00                cmp    %eax,0x0(%rbp)
  40113e:   75 05                   jne    401145 <phase_6+0x51>
  401140:   e8 f5 02 00 00          callq  40143a <explode_bomb>
  401145:   83 c3 01                add    $0x1,%ebx
  401148:   83 fb 05                cmp    $0x5,%ebx
  40114b:   7e e8                   jle    401135 <phase_6+0x41>
  40114d:   49 83 c5 04             add    $0x4,%r13
  401151:   eb c1                   jmp    401114 <phase_6+0x20>
--------------------Section 1 End------------------------------

由函数read_six_numbers可知这次要求输入六个数字。使用gdb测试可以知道%rsp~%rsp+0x14分别存放这他们的地址。
这段汇编代码可以用C语言如下表示:

    int i,j=0;
Start:
/***Section 1:要求数组中所有元素都小于等于6且互不相等***/
    if(array[j]>6)
    {
        explode_bomb();
    }
    i=j+1;
    if(i==6)
    {
        goto Section_2;
    }
Part_2:
    if(array[i]==array[j])
    {
        explode_bomb();
    }
    i++;
    if(i<=5)
    {
        goto Part_2; 
    }
    j++;
    goto Start;
  • Section 2
--------------------Section 2 Start----------------------------
  401153:   48 8d 74 24 18          lea    0x18(%rsp),%rsi
  401158:   4c 89 f0                mov    %r14,%rax    
  40115b:   b9 07 00 00 00          mov    $0x7,%ecx
  401160:   89 ca                   mov    %ecx,%edx
  401162:   2b 10                   sub    (%rax),%edx
  401164:   89 10                   mov    %edx,(%rax)
  401166:   48 83 c0 04             add    $0x4,%rax
  40116a:   48 39 f0                cmp    %rsi,%rax
  40116d:   75 f1                   jne    401160 <phase_6+0x6c>
--------------------Section 2 End------------------------------

这段代码的作用就是用7减去数组中的值并替换原来的值。

  • Section 3
--------------------Section 3 Start------------------------------
  40116f:   be 00 00 00 00          mov    $0x0,%esi   #%esi=0     
  401174:   eb 21                   jmp    401197 <phase_6+0xa3>
  401176:   48 8b 52 08             mov    0x8(%rdx),%rdx     #%rdi=%rdi->next
  40117a:   83 c0 01                add    $0x1,%eax          #%eax++
  40117d:   39 c8                   cmp    %ecx,%eax          #if %eax=%ecx?
  40117f:   75 f5                   jne    401176 <phase_6+0x82>  #after loop,%rdx=&node(value of input array's element)
  401181:   eb 05                   jmp    401188 <phase_6+0x94>
  401183:   ba d0 32 60 00          mov    $0x6032d0,%edx
  401188:   48 89 54 74 20          mov    %rdx,0x20(%rsp,%rsi,2) 
  40118d:   48 83 c6 04             add    $0x4,%rsi
  401191:   48 83 fe 18             cmp    $0x18,%rsi
  401195:   74 14                   je     4011ab <phase_6+0xb7>
  401197:   8b 0c 34                mov    (%rsp,%rsi,1),%ecx   #%rsi as index of input array
  40119a:   83 f9 01                cmp    $0x1,%ecx
  40119d:   7e e4                   jle    401183 <phase_6+0x8f>#require element>=1
  40119f:   b8 01 00 00 00          mov    $0x1,%eax            #Store 0x1 in %eax
  4011a4:   ba d0 32 60 00          mov    $0x6032d0,%edx        #address of node1
  4011a9:   eb cb                   jmp    401176 <phase_6+0x82>

--------------------Section 3 End------------------------------

使用gdb查看0x6032d0处的值:

0x6032d0处的值

结合注释可知这一步根据输入数组中元素的值取合适的node地址放在%rsp+0x20的一段内存中。如果输入的数据为3 2 4 6 5 1, 那么这一步的效果可以用下图表示:

image.png

  • Section 4
  4011ab:   48 8b 5c 24 20          mov    0x20(%rsp),%rbx    #Store the first address
  4011b0:   48 8d 44 24 28          lea    0x28(%rsp),%rax    #Store the second address
  4011b5:   48 8d 74 24 50          lea    0x50(%rsp),%rsi    #Store the edge of address
  4011ba:   48 89 d9                mov    %rbx,%rcx          #初始为首地址
  4011bd:   48 8b 10                mov    (%rax),%rdx        
  4011c0:   48 89 51 08             mov    %rdx,0x8(%rcx)     
  4011c4:   48 83 c0 08             add    $0x8,%rax
  4011c8:   48 39 f0                cmp    %rsi,%rax            
  4011cb:   74 05                   je     4011d2 <phase_6+0xde>
  4011cd:   48 89 d1                mov    %rdx,%rcx
  4011d0:   eb eb                   jmp    4011bd <phase_6+0xc9>

这段代码可以简化为如下的表述:

rbx=&node_a1;
rax=&node_a2;
rsi=&node_edge;  //%rsi=%rsp+0x50
rcx=rbx;
start:
rdx=rax;
rcx->next=rdx;
rax=rax->next;
if(rsi==rax)
{
    goto next_section;
}
rcx=rdx;
goto start;

主要作用是修改%rsp+0x20到%rsp+0x48各个节点的next域,将它们“连”起来。

  • Section 5
----------------------Section 5 Start-----------------------------
  4011d2:   48 c7 42 08 00 00 00    movq   $0x0,0x8(%rdx)    #最后一个的next应该为null
  4011d9:   00 
  4011da:   bd 05 00 00 00          mov    $0x5,%ebp
  4011df:   48 8b 43 08             mov    0x8(%rbx),%rax #%rax=%rbx->next
  4011e3:   8b 00                   mov    (%rax),%eax #比较结构体node中第一个值的大小
  4011e5:   39 03                   cmp    %eax,(%rbx)
  4011e7:   7d 05                   jge    4011ee <phase_6+0xfa>
  4011e9:   e8 4c 02 00 00          callq  40143a <explode_bomb>
  4011ee:   48 8b 5b 08             mov    0x8(%rbx),%rbx
  4011f2:   83 ed 01                sub    $0x1,%ebp
  4011f5:   75 e8                   jne    4011df <phase_6+0xeb>

  4011f7:   48 83 c4 50             add    $0x50,%rsp
  4011fb:   5b                      pop    %rbx
  4011fc:   5d                      pop    %rbp
  4011fd:   41 5c                   pop    %r12
  4011ff:   41 5d                   pop    %r13
  401201:   41 5e                   pop    %r14
  401203:   c3                      retq   

这一部分要求前面得到的数组中第一个部分的值域按照递减的次序排列。
node3(924)->node4(691)->node5(477)->node6(443)->node1(332)->node2(168)
综上所述,一开始输入的6个数字应为

4 3 2 1 6 5

secret_phase

00000000004015c4 <phase_defused>:
  4015c4:   48 83 ec 78             sub    $0x78,%rsp
  4015c8:   64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
  4015cf:   00 00 
  4015d1:   48 89 44 24 68          mov    %rax,0x68(%rsp)
  4015d6:   31 c0                   xor    %eax,%eax
  4015d8:   83 3d 81 21 20 00 06    cmpl   $0x6,0x202181(%rip)        # 603760 <num_input_strings>
  4015df:   75 5e                   jne    40163f <phase_defused+0x7b>
  4015e1:   4c 8d 44 24 10          lea    0x10(%rsp),%r8
  4015e6:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx
  4015eb:   48 8d 54 24 08          lea    0x8(%rsp),%rdx
  4015f0:   be 19 26 40 00          mov    $0x402619,%esi #"%d %d %s"
  4015f5:   bf 70 38 60 00          mov    $0x603870,%edi #"7 0"
  4015fa:   e8 f1 f5 ff ff          callq  400bf0 <__isoc99_sscanf@plt>
  4015ff:   83 f8 03                cmp    $0x3,%eax   #还需要一个字符串凑成三个
  401602:   75 31                   jne    401635 <phase_defused+0x71>
  401604:   be 22 26 40 00          mov    $0x402622,%esi   #"DrEvil"
  401609:   48 8d 7c 24 10          lea    0x10(%rsp),%rdi
  40160e:   e8 25 fd ff ff          callq  401338 <strings_not_equal>
  401613:   85 c0                   test   %eax,%eax
  401615:   75 1e                   jne    401635 <phase_defused+0x71>
  401617:   bf f8 24 40 00          mov    $0x4024f8,%edi
  40161c:   e8 ef f4 ff ff          callq  400b10 <puts@plt>
  401621:   bf 20 25 40 00          mov    $0x402520,%edi
  401626:   e8 e5 f4 ff ff          callq  400b10 <puts@plt>
  40162b:   b8 00 00 00 00          mov    $0x0,%eax
  401630:   e8 0d fc ff ff          callq  401242 <secret_phase>
  401635:   bf 58 25 40 00          mov    $0x402558,%edi
  40163a:   e8 d1 f4 ff ff          callq  400b10 <puts@plt>
  40163f:   48 8b 44 24 68          mov    0x68(%rsp),%rax
  401644:   64 48 33 04 25 28 00    xor    %fs:0x28,%rax
  40164b:   00 00 
  40164d:   74 05                   je     401654 <phase_defused+0x90>
  40164f:   e8 dc f4 ff ff          callq  400b30 <__stack_chk_fail@plt>
  401654:   48 83 c4 78             add    $0x78,%rsp
  401658:   c3                      retq   
  401659:   90                      nop
  40165a:   90                      nop
  40165b:   90                      nop
  40165c:   90                      nop
  40165d:   90                      nop
  40165e:   90                      nop
  40165f:   90                      nop

根据注释,猜测由phase_defused来调用secret_phase的条件是在7 0后面加上DrEvil

0000000000401242 <secret_phase>:
  401242:   53                      push   %rbx
  401243:   e8 56 02 00 00          callq  40149e <read_line>
  401248:   ba 0a 00 00 00          mov    $0xa,%edx
  40124d:   be 00 00 00 00          mov    $0x0,%esi
  401252:   48 89 c7                mov    %rax,%rdi
  401255:   e8 76 f9 ff ff          callq  400bd0 <strtol@plt>#把字符串转换为数字,其实输入一个数字就够了
  40125a:   48 89 c3                mov    %rax,%rbx #%rax处存放输入的数字
  40125d:   8d 40 ff                lea    -0x1(%rax),%eax
  401260:   3d e8 03 00 00          cmp    $0x3e8,%eax
  401265:   76 05                   jbe    40126c <secret_phase+0x2a>
  401267:   e8 ce 01 00 00          callq  40143a <explode_bomb>
  40126c:   89 de                   mov    %ebx,%esi
  40126e:   bf f0 30 60 00          mov    $0x6030f0,%edi #这里是重点,使用gdb查看该处的值
  401273:   e8 8c ff ff ff          callq  401204 <fun7>
  401278:   83 f8 02                cmp    $0x2,%eax
  40127b:   74 05                   je     401282 <secret_phase+0x40>
  40127d:   e8 b8 01 00 00          callq  40143a <explode_bomb>
  401282:   bf 38 24 40 00          mov    $0x402438,%edi
  401287:   e8 84 f8 ff ff          callq  400b10 <puts@plt>
  40128c:   e8 33 03 00 00          callq  4015c4 <phase_defused>
  401291:   5b                      pop    %rbx
  401292:   c3                      retq   
  401293:   90                      nop
  401294:   90                      nop
  401295:   90                      nop
  401296:   90                      nop
  401297:   90                      nop
  401298:   90                      nop
  401299:   90                      nop
  40129a:   90                      nop
  40129b:   90                      nop
  40129c:   90                      nop
  40129d:   90                      nop
  40129e:   90                      nop
  40129f:   90                      nop

如注释所示,调用gdb结果如下:

x/120a 0x6030f0
0x6030f0 <n1>:  0x24    0x603110 <n21>
0x603100 <n1+16>:   0x603130 <n22>  0x0
0x603110 <n21>: 0x8 0x603190 <n31>
0x603120 <n21+16>:  0x603150 <n32>  0x0
0x603130 <n22>: 0x32    0x603170 <n33>
0x603140 <n22+16>:  0x6031b0 <n34>  0x0
0x603150 <n32>: 0x16    0x603270 <n43>
0x603160 <n32+16>:  0x603230 <n44>  0x0
0x603170 <n33>: 0x2d    0x6031d0 <n45>
0x603180 <n33+16>:  0x603290 <n46>  0x0
0x603190 <n31>: 0x6 0x6031f0 <n41>
0x6031a0 <n31+16>:  0x603250 <n42>  0x0
0x6031b0 <n34>: 0x6b    0x603210 <n47>
0x6031c0 <n34+16>:  0x6032b0 <n48>  0x0
0x6031d0 <n45>: 0x28    0x0
0x6031e0 <n45+16>:  0x0 0x0
0x6031f0 <n41>: 0x1 0x0
0x603200 <n41+16>:  0x0 0x0
0x603210 <n47>: 0x63    0x0
0x603220 <n47+16>:  0x0 0x0
0x603230 <n44>: 0x23    0x0
0x603240 <n44+16>:  0x0 0x0
0x603250 <n42>: 0x7 0x0
0x603260 <n42+16>:  0x0 0x0
0x603270 <n43>: 0x14    0x0
0x603280 <n43+16>:  0x0 0x0
0x603290 <n46>: 0x2f    0x0
0x6032a0 <n46+16>:  0x0 0x0
0x6032b0 <n48>: 0x3e9   0x0
0x6032c0 <n48+16>:  0x0 0x0

上面表示的是一个二叉树,其中n1为根节点,nxy为第x层第y个节点。
secret_phase中的fun7函数是一个关键部分,正是这个函数根据输入的值对二叉树进行操作。
代码如下:

0000000000401204 <fun7>:
  401204:   48 83 ec 08             sub    $0x8,%rsp
  401208:   48 85 ff                test   %rdi,%rdi
  40120b:   74 2b                   je     401238 <fun7+0x34>
  40120d:   8b 17                   mov    (%rdi),%edx
  40120f:   39 f2                   cmp    %esi,%edx
  401211:   7e 0d                   jle    401220 <fun7+0x1c>
  401213:   48 8b 7f 08             mov    0x8(%rdi),%rdi
  401217:   e8 e8 ff ff ff          callq  401204 <fun7>
  40121c:   01 c0                   add    %eax,%eax
  40121e:   eb 1d                   jmp    40123d <fun7+0x39>
  401220:   b8 00 00 00 00          mov    $0x0,%eax
  401225:   39 f2                   cmp    %esi,%edx
  401227:   74 14                   je     40123d <fun7+0x39>
  401229:   48 8b 7f 10             mov    0x10(%rdi),%rdi
  40122d:   e8 d2 ff ff ff          callq  401204 <fun7>
  401232:   8d 44 00 01             lea    0x1(%rax,%rax,1),%eax
  401236:   eb 05                   jmp    40123d <fun7+0x39>
  401238:   b8 ff ff ff ff          mov    $0xffffffff,%eax
  40123d:   48 83 c4 08             add    $0x8,%rsp
  401241:   c3                      retq 

可以用C语言重写如下:

void fun7(Node* node,int value)
{//node in %rdi,value in %rsi,return_value in %eax
 //require %eax to be 2(Very important)
    int t=node->val;
    if(t>value)
    {
        node=node->left;
        fun7(node,value);
        return_value=2*return_value;
        return;
    }
    else
    {
        if(value==t)
        {
            return;
        }
        node=node->right;
        fun7(node,value);
        return_value=0x1+2*return_value;
        return;
    }
}

要想return_value为2,则其返回的模式必须为2*(1+2*0),所以输入的数字须得等于n32节点中的值0x16(22)。这样我们所有的弹都拆完了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容