关于c:深入理解计算机系统实验二-Bomb-Lab

50次阅读

共计 21144 个字符,预计需要花费 53 分钟才能阅读完成。

这是 CSAPP 的第二个试验,次要让咱们了解代码的机器级示意,最重要的是了解每个寄存器的作用以及如何应用这些寄存器。本次的试验内容有点艰涩难懂,对于这些内容多看下习惯就好了。

  本次试验中的 bomb 文件中共有 7 个炸弹问题(6 个显式的和 1 个暗藏的),每条问题只有输出正确的答案能力进入下一题,否则则会触发爆炸。通过浏览 bomb 文件的汇编代码了解各个问题的运作形式,推出正确的输出答案。暗藏的问题须要通过 gdb 间接调用解决。

  我的编译环境:Ubuntu 16.04,gcc 5.4.0。

筹备工作

  从官网下载到试验,解压后一共三个文件,具体如下图所示。

  readme 中没写什么有用的内容,bomb 文件是编译实现的文件,bomb.c 是本次试验的源码,关上看下,大略浏览了一遍,一共有 phase_1 ~ phase_6 6 个炸弹,从命令行输出的内容必须要和 phase 函数中的统一,否则就会爆炸退出程序。phase 函数并没有给出源码,所以无奈得悉其冀望的字符串是什么。给了 bomb 可执行文件,咱们就把这个文件反汇编下,从反汇编推算下其内容是什么。

  首先应用 objdump -d bomb > bomb.asm 命令生成反汇编文件。

  先运行 bomb 文件,提醒没有权限,我的文件是从 windwos 拷贝到 Linux 虚拟机中的,所以会报这个谬误。执行 chmod +777 bomb 赋予权限。如下图所示。

  而后轻易输出一些内容看下会有什么结果,如下图所示,提醒曾经爆炸。

phase_1

  上面从 main 函数开始剖析下反汇编。

0000000000400da0 <main>:
  400da0:    53                       push   %rbx
  400da1:    83 ff 01                 cmp    $0x1,%edi                  #if (argc == 1)
  400da4:    75 10                    jne    400db6 <main+0x16>         # 不相等就跳转到 400db6
  400da6:    48 8b 05 9b 29 20 00     mov    0x20299b(%rip),%rax        # 603748 <stdin@@GLIBC_2.2.5>
  400dad:    48 89 05 b4 29 20 00     mov    %rax,0x2029b4(%rip)        # 603768 <infile> 相等就读取输出
  400db4:    eb 63                    jmp    400e19 <main+0x79>         #跳转到 initialize_bomb
  400db6:    48 89 f3                 mov    %rsi,%rbx
  400db9:    83 ff 02                 cmp    $0x2,%edi                  #else if (argc == 2)
  400dbc:    75 3a                    jne    400df8 <main+0x58>         #不相等跳转到 400df8
  400dbe:    48 8b 7e 08              mov    0x8(%rsi),%rdi             
  400dc2:    be b4 22 40 00           mov    $0x4022b4,%esi
  400dc7:    e8 44 fe ff ff           callq  400c10 <fopen@plt>         
  400dcc:    48 89 05 95 29 20 00     mov    %rax,0x202995(%rip)        # 603768 <infile>
  400dd3:    48 85 c0                 test   %rax,%rax
  400dd6:    75 41                    jne    400e19 <main+0x79>         #跳转到 initialize_bomb
  400dd8:    48 8b 4b 08              mov    0x8(%rbx),%rcx
  400ddc:    48 8b 13                 mov    (%rbx),%rdx
  400ddf:    be b6 22 40 00           mov    $0x4022b6,%esi  
  400de4:    bf 01 00 00 00           mov    $0x1,%edi                  #传参
  400de9:    e8 12 fe ff ff           callq  400c00 <__printf_chk@plt>  #printf("%s: Error: Couldn't open %s\n", argv[0], argv[1]);
  400dee:    bf 08 00 00 00           mov    $0x8,%edi
  400df3:    e8 28 fe ff ff           callq  400c20 <exit@plt>          #exit(8);
   400df8:    48 8b 16                 mov    (%rsi),%rdx
  400dfb:    be d3 22 40 00           mov    $0x4022d3,%esi
  400e00:    bf 01 00 00 00           mov    $0x1,%edi
  400e05:    b8 00 00 00 00           mov    $0x0,%eax                  #传参
  400e0a:    e8 f1 fd ff ff           callq  400c00 <__printf_chk@plt>  #printf("Usage: %s [<input_file>]\n", argv[0]);
  400e0f:    bf 08 00 00 00           mov    $0x8,%edi
  400e14:    e8 07 fe ff ff           callq  400c20 <exit@plt>          #exit(8);
  400e19:    e8 84 05 00 00           callq  4013a2 <initialize_bomb>   #调用 initialize_bomb();
  400e1e:    bf 38 23 40 00           mov    $0x402338,%edi
  400e23:    e8 e8 fc ff ff           callq  400b10 <puts@plt>          #printf("Welcome to my fiendish little bomb. You have 6 phases with\n");
  400e28:    bf 78 23 40 00           mov    $0x402378,%edi 
  400e2d:    e8 de fc ff ff           callq  400b10 <puts@plt>          #printf("which to blow yourself up. Have a nice day!\n");
  400e32:    e8 67 06 00 00           callq  40149e <read_line>         #调用 read_line();
  400e37:    48 89 c7                 mov    %rax,%rdi                  #传参
  400e3a:    e8 a1 00 00 00           callq  400ee0 <phase_1>           #调用 phase_1();
  400e3f:    e8 80 07 00 00           callq  4015c4 <phase_defused>     #调用 phase_defused();
  400e44:    bf a8 23 40 00           mov    $0x4023a8,%edi     
  400e49:    e8 c2 fc ff ff           callq  400b10 <puts@plt>          #printf("Phase 1 defused. How about the next one?\n");
  400e4e:    e8 4b 06 00 00           callq  40149e <read_line>         #调用 read_line();
  400e53:    48 89 c7                 mov    %rax,%rdi                  #传参
  400e56:    e8 a1 00 00 00           callq  400efc <phase_2>           #调用 phase_2();
  400e5b:    e8 64 07 00 00           callq  4015c4 <phase_defused>
  400e60:    bf ed 22 40 00           mov    $0x4022ed,%edi
  400e65:    e8 a6 fc ff ff           callq  400b10 <puts@plt>
  400e6a:    e8 2f 06 00 00           callq  40149e <read_line>
  400e6f:    48 89 c7                 mov    %rax,%rdi
  400e72:    e8 cc 00 00 00           callq  400f43 <phase_3>           #调用 phase_3();
  400e77:    e8 48 07 00 00           callq  4015c4 <phase_defused>
  400e7c:    bf 0b 23 40 00           mov    $0x40230b,%edi
  400e81:    e8 8a fc ff ff           callq  400b10 <puts@plt>
  400e86:    e8 13 06 00 00           callq  40149e <read_line>
  400e8b:    48 89 c7                 mov    %rax,%rdi
  400e8e:    e8 79 01 00 00           callq  40100c <phase_4>           #调用 phase_4();
  400e93:    e8 2c 07 00 00           callq  4015c4 <phase_defused>
  400e98:    bf d8 23 40 00           mov    $0x4023d8,%edi
  400e9d:    e8 6e fc ff ff           callq  400b10 <puts@plt>
  400ea2:    e8 f7 05 00 00           callq  40149e <read_line>
  400ea7:    48 89 c7                 mov    %rax,%rdi
  400eaa:    e8 b3 01 00 00           callq  401062 <phase_5>          #调用 phase_4();
  400eaf:    e8 10 07 00 00           callq  4015c4 <phase_defused>
  400eb4:    bf 1a 23 40 00           mov    $0x40231a,%edi
  400eb9:    e8 52 fc ff ff           callq  400b10 <puts@plt>
  400ebe:    e8 db 05 00 00           callq  40149e <read_line>
  400ec3:    48 89 c7                 mov    %rax,%rdi
  400ec6:    e8 29 02 00 00           callq  4010f4 <phase_6>          #调用 phase_4();
  400ecb:    e8 f4 06 00 00           callq  4015c4 <phase_defused>
  400ed0:    b8 00 00 00 00           mov    $0x0,%eax
  400ed5:    5b                       pop    %rbx
  400ed6:    c3                       retq   

  大略剖析了下主函数,次要还是传参和函数的调用,想要得出后果还是要看 phase_1 ~ phase_6 这些函数的反汇编。

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>       #strings_not_equal 比拟两个字符串后果保留在 %eax
  400eee:    85 c0                    test   %eax,%eax                        # 测试 %eax 为 0 还是 1
  400ef0:    74 05                    je     400ef7 <phase_1+0x17>            #相等就跳转到 400ef7
  400ef2:    e8 43 05 00 00           callq  40143a <explode_bomb>            #否则就 explode_bomb,失败
  400ef7:    48 83 c4 08              add    $0x8,%rsp                        #失常完结
  400efb:    c3                       retq   

  第二行示意为函数开拓内存空间。第三行给 %esi 传进了一个参数,而后就调用了 strings_not_equal。由 strings_not_equal 的反汇编能够看出,这个函数是承受两个参数的。那么,除了 %esi 传进的一个,另一个就是咱们输出的字符串了。strings_not_equal 比拟的后果放在 %eax 中。接下来测试 %eax 为 0 还是 1,如果 %eax 为 1 就表明两个字符串相等,跳转到相等 400ef7,失常完结程序。否则就跳转到 explode_bomb,失败。

0000000000401338 <strings_not_equal>:
  401338:    41 54                    push   %r12
  40133a:    55                       push   %rbp
  40133b:    53                       push   %rbx
  40133c:    48 89 fb                 mov    %rdi,%rbx           # 这里传入了两个参数 %rdi,%rsi,这两个参数一个是输出的字符串,另一个是冀望的字符串
  40133f:    48 89 f5                 mov    %rsi,%rbp
  401342:    e8 d4 ff ff ff           callq  40131b <string_length>

  通过以上剖析,咱们能够得出结论,在内存为 0x402400 的中央存储的就是程序冀望咱们输出的字符串,那么咱们利用 GDB 工具调试下代码,打印 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>         #read_six_numbers 读取 6 个数字
  400f0a:    83 3c 24 01              cmpl   $0x1,(%rsp)                       #比拟 1 和 (%rsp) 的值
  400f0e:    74 20                    je     400f30 <phase_2+0x34>             #相等就持续 400f30
  400f10:    e8 25 05 00 00           callq  40143a <explode_bomb>             #否则就调用 explode_bomb
  400f15:    eb 19                    jmp    400f30 <phase_2+0x34>
  400f17:    8b 43 fc                 mov    -0x4(%rbx),%eax                   # %rbx-0x4 的值放到 %eax  也就是上上个元素 1
  400f1a:    01 c0                    add    %eax,%eax                         # 2*%eax 
  400f1c:    39 03                    cmp    %eax,(%rbx)                       # 比拟 2*%eax (%rbx)
  400f1e:    74 05                    je     400f25 <phase_2+0x29>             #相等就跳转到 400f25
  400f20:    e8 15 05 00 00           callq  40143a <explode_bomb>             #否则就调用 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                     #0x4+%rsp 的值放到 %rbx
  400f35:    48 8d 6c 24 18           lea    0x18(%rsp),%rbp                    #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   

  伪代码:

// %rsp   %rsp+0x4  %rsp+0x18
if((%rsp) == 1)
{goto 400f30;}
     400f17:
     %eax = %rbx-4;             //%eax 其实就是 %rsp
     %eax=2*%eax;               //%rsp = 2*%rsp
     if((%rbx)==%eax)           // 要满足两倍关系  %rbx 代表的是 %rsp+0x4 地址处的数据,%eax 代表的是 2*(%rsp),意思就是说下一个数要是上一个数的两倍
     {
        %rbx=%rbx+0x4;          // 下一地址 +0x4
        if(%rbx==%rbp)          //%rbp = %rsp+0x18  %rbp 存储的是完结的条件  
        {return;}
         else
         {goto 400f17}
      }
     else
     {explode_bomb}
else
{explode_bomb}
400f30:
%rbx=%rsp+0x4;
%rbp=%rsp+0x18;
goto 400f17;

  间接看汇编代码有点简单,所以捋了下思路,写一个伪代码进去会不便看一点。从反汇编的 read_six_numbers 能够看出,答案肯定是 6 个数字。

  程序一开始将 %rsp 的值和 1 比拟,只有相等时,才会持续进行。阐明第一个数肯定要是 1。接着跳转到 400f30 将 %rsp+0x4,%rsp+0x18。跳转到 400f17,到这里后,把 %rbx 的值减去了 4,减完之后,%eax 其实就是 %rsp 的值,回到了最后的状态。接着将 %eax 乘以 2,%rbx 代表的是 %rsp+0x4 地址处的数据,%eax 代表的是 2*(%rsp),意思就是说下一个数要是上一个数的两倍才会跳进 if 语句中,否则就会爆炸。接着将 %rbx+0x4,看起来像是将地址 +4. 指向下一个数字。这个时候再将 %rbx 和 %rbp 比拟,从这里能够判断出 %rbp 很可能就是完结的条件,只有 %rbx == %rbp,才会失常完结程序,而且只有这一条路能够完结正序,否则就会爆炸。

  剖析到这里能够得出三个重要的论断:1. 第一个数是 1。2. 6 个数字的关系为:后一个数是前一个数的两倍。3. 完结的条件寄存在 %rsp+0x18。所以依据这三个论断咱们能够推断出 6 个数字为: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                     # %rcx = 0xc + %rsp 第二个参数
  400f4c:    48 8d 54 24 08           lea    0x8(%rsp),%rdx                     # %rdx = 0x8 + %rsp 第一个参数
  400f51:    be cf 25 40 00           mov    $0x4025cf,%esi                     # %esi = 0x4025cf    %d %d
  400f56:    b8 00 00 00 00           mov    $0x0,%eax                          # %eax = 0
  400f5b:    e8 90 fc ff ff           callq  400bf0 <__isoc99_sscanf@plt>        
  400f60:    83 f8 01                 cmp    $0x1,%eax                          # %eax 和 1 比拟  输出的数的个数要大于 1
  400f63:    7f 05                    jg     400f6a <phase_3+0x27>              # %eax > 0x1, 大于就跳转到 400f6a,否则 explode_bomb
  400f65:    e8 d0 04 00 00           callq  40143a <explode_bomb>              # %eax <= 0x1 explode_bomb
  400f6a:    83 7c 24 08 07           cmpl   $0x7,0x8(%rsp)                     # 0x8(%rsp)  0x7 
  400f6f:    77 3c                    ja     400fad <phase_3+0x6a>              # 0x8(%rsp)>=0x7 跳转 explode_bomb 第一个参数大于 7 爆炸 <0
  400f71:    8b 44 24 08              mov    0x8(%rsp),%eax                     # 0x8(%rsp) < 0x7 则 %eax = 0x8(%rsp) [0,6]
  400f75:    ff 24 c5 70 24 40 00     jmpq   *0x402470(, %rax,8)                # 跳转表 *0x402470 + 8 * %rax  *0x402470 = 0x400f7c 
  400f7c:    b8 cf 00 00 00           mov    $0xcf,%eax                         # %eax = 0xcf = 207  case 0      
  400f81:    eb 3b                    jmp    400fbe <phase_3+0x7b>              # 400fbe
  400f83:    b8 c3 02 00 00           mov    $0x2c3,%eax                        # %eax  = 0x2c3 = 707 case 2   
  400f88:    eb 34                    jmp    400fbe <phase_3+0x7b>
  400f8a:    b8 00 01 00 00           mov    $0x100,%eax                        # %eax  = 0x100 = 256 case 3
  400f8f:    eb 2d                    jmp    400fbe <phase_3+0x7b>
  400f91:    b8 85 01 00 00           mov    $0x185,%eax                        # %eax  = 0x185 = 389  case 4
  400f96:    eb 26                    jmp    400fbe <phase_3+0x7b>
  400f98:    b8 ce 00 00 00           mov    $0xce,%eax                         # %eax  = 0xce = 206  case 5
  400f9d:    eb 1f                    jmp    400fbe <phase_3+0x7b>
  400f9f:    b8 aa 02 00 00           mov    $0x2aa,%eax                        # %eax  = 0x2aa = 682 case 6
  400fa4:    eb 18                    jmp    400fbe <phase_3+0x7b>
  400fa6:    b8 47 01 00 00           mov    $0x147,%eax                        # %eax  = 0x147 = 327 case 7
  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                          # %eax  = 0x0 = 0 
  400fb7:    eb 05                    jmp    400fbe <phase_3+0x7b>
  400fb9:    b8 37 01 00 00           mov    $0x137,%eax                        # %eax  = 0x137 = 311  case 1
  400fbe:    3b 44 24 0c              cmp    0xc(%rsp),%eax                     # 0xc(%rsp)  %eax
  400fc2:    74 05                    je     400fc9 <phase_3+0x86>              # 0xc(%rsp)==%eax  输出的第二个参数 和 %eax 相等则解除
  400fc4:    e8 71 04 00 00           callq  40143a <explode_bomb>              # 0xc(%rsp)!=%eax explode_bomb
  400fc9:    48 83 c4 18              add    $0x18,%rsp
  400fcd:    c3                       retq   

  从第 5 行的 0x4025cf 动手,查看后发现是 %d %d , 阐明输出的是两个整数。

  第 7 行,第 8 行阐明输出的参数个数要大于 1。第 11 即将第一个参数 0x8(%rsp) 和 7 比拟,大于 7 则爆炸,阐明输出的参数要小于等于 7,同时 ja 为无符号跳转,则参数还有大于 0,因而得出第一个参数的范畴[0,7]。第 14 行为间接跳转,以 0x402470 处的值为基地址,再加上 8 %rax 进行跳转,不同的 %rax 跳转到不同的地位。

  咱们能够看下 0x402470 的值为 0x400f7c。当 %rax 为 0 时,跳转到 0x400f7c。此时 %eax = 207,最初跳转到 33 行,将 207 于输出的值比拟。阐明 0 和 207 为一组正确数据。测试下

  其余后果如汇编中的正文所示。

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                     # %d %d
  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)                    # 第一个参数和 14 比拟,要比 14 小
  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>                      # 三个参数:0x8(%rsp),0,14       
  40104d:    85 c0                    test   %eax,%eax                           # 测试返回值是否为 0
  40104f:    75 07                    jne    401058 <phase_4+0x4c>               # 返回值不为 0 爆炸      
  401051:    83 7c 24 0c 00           cmpl   $0x0,0xc(%rsp)                      # 测试第二个参数是否为 0
  401056:    74 05                    je     40105d <phase_4+0x51>               # 为 0 不会爆炸,所以第二个数肯定要输出 0
  401058:    e8 dd 03 00 00           callq  40143a <explode_bomb>
  40105d:    48 83 c4 18              add    $0x18,%rsp
  401061:    c3                       retq   

  从第 5 行看起,0x4025cf 指向的中央存储的依然是 两个 int 型整数。第 8 行和 2 比拟,阐明输出参数的个数为 2。第 10 行和 14 比拟,阐明输出的第一个参数肯定要小于 14。第 13,14,15 行向 func4()传递三个参数 0x8(%rsp),0,14。第 17 行测试函数返回值是否为 0,要想不爆炸,函数返回值肯定要为 0。第 19 行阐明输出的第二个参数肯定要为 0。

  所以,咱们要确定的是当输出的第一个参数为多少的时候,fun4()的返回值为 0。上面看下 fun4()的反汇编。

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

  将汇编翻译为 C 如下所示

//x: %edi y:%esi z:%edx k: %ecx t:%eax
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 时,返回值为 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
  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                       # 字符串长度要为 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>
  ##############################################start################################################################################
  40108b:    0f b6 0c 03              movzbl (%rbx,%rax,1),%ecx              # %ecx = %rbx + %rax ASCII 码值 给 %ecx 
  40108f:    88 0c 24                 mov    %cl,(%rsp)                      # ASCII 码值取低 8 位
  401092:    48 8b 14 24              mov    (%rsp),%rdx         
  401096:    83 e2 0f                 and    $0xf,%edx                       # ASCII 码值取低 4 位
  401099:    0f b6 92 b0 24 40 00     movzbl 0x4024b0(%rdx),%edx             # %edx = %rdx + 0x4024b0(maduiersnfotvbyl)%edx:%rdx 低 4 位无效
  4010a0:    88 54 04 10              mov    %dl,0x10(%rsp,%rax,1)           # %edx 低八位存在 0x10 + %rsp + %rax 中
  4010a4:    48 83 c0 01              add    $0x1,%rax                       # %rax = %rax + 1
  4010a8:    48 83 f8 06              cmp    $0x6,%rax                       
  4010ac:    75 dd                    jne    40108b <phase_5+0x29>           # %rax 不等于 6 则循环   #################################################end###################################################################################
  4010ae:    c6 44 24 16 00           movb   $0x0,0x16(%rsp)
  4010b3:    be 5e 24 40 00           mov    $0x40245e,%esi                  # %esi 指向从 0x40245e 内存单元读入的字符串 flyers 
  4010b8:    48 8d 7c 24 10           lea    0x10(%rsp),%rdi                 # %rdi 指向后面循环中结构好的长度为 6 的字符串
  4010bd:    e8 76 02 00 00           callq  401338 <strings_not_equal>      # 判断 %esi 和 %rdi 指向的字符串是否相等
  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  
  #%rbp %rbx %r12~%15 被调用者保留寄存器
#  %r10 %r11 调用者保留寄存器
%rdi %rsi %rdx %rcx %r8 %r9 顺次保留参数 1~6
%dl 示意 %rdx 寄存器最低 8bit

  这个题目有点扰啊,兜了很大一个圈子!

  第 7 行 ~ 13 行阐明输出的字符串长度要为 6。

  第 15 行 ~ 23 行为一个循环。输出的字符串存储在 %rbx 中,第 15 行示意把输出字符串的第 %eax 个字符的 ASCII 码值给 %ecx,%cl 为 %ecx 的低 8 位,所以第 16 行为取 %ecx 的低八位。

  第 18 行示意再取低 4 位。

  第 19 行的 0x4024b0 查看内容为 maduiersnfotvbyl,这句话的意思是以 0x4024b0 为基地址,以 %rdx 为偏移,从 maduiersnfotvbyl 字符串中取字符的 低 32 位,后果放在 %edx 中。

  第 20 行,%dl 中的值应为 0x4024b0+%rdx 示意的字符,将其赋值给 0x10(%rsp,%rax,1),最初计数器 %rax+1。

  第 22 行,示意是否循环够了 6 次。

  第 25 行,0x40245e 字符串为 flyers,比拟两个字符串,如果 %eax 为 0(两个字符串雷同),则解除炸弹,否则爆炸。

  所以,0x4024b0 + %rdx = {flyers 的 ASCII 码}。

  flyers 对应的 ascii 值 0x66 0x6c 0x79 0x65 0x72 0x73。

  与 0x4024b0 内存地址开始的查找表比拟取得偏移量 0x9 0xF 0xE 0x5 0x6 0x72。

  因而输出长度为 6 的字符串中每个字符的低 4bit 的值别离为 0x9 0xF 0xE 0x5 0x6 0x72。

  若输出为大写字母, 将低 4bit 的值加上 0x40, 取得输出字符串 IONEFG。

  若输出为小写字母, 将低 4bit 的值加上 0x60, 取得输出字符串 ionefg。

phase_6

(gdb) disas phase_6
Dump of assembler code for function phase_6:
   0x00000000004010f4 <+0>: push   %r14                                将被调用者保留寄存器压入栈
   0x00000000004010f6 <+2>: push   %r13
   0x00000000004010f8 <+4>: push   %r12
   0x00000000004010fa <+6>: push   %rbp
   0x00000000004010fb <+7>: push   %rbx                                %rsp = 0x7fffffffe2c0
   0x00000000004010fc <+8>: sub    $0x50,%rsp                          调配栈空间 %rsp = 0x7fffffffe270
   0x0000000000401100 <+12>:    mov    %rsp,%r13

   0x0000000000401103 <+15>:    mov    %rsp,%rsi
   0x0000000000401106 <+18>:    callq  0x40145c <read_six_numbers>     读入 6 个值, 保留至从 %rsi 开始的地址

   0x000000000040110b <+23>:    mov    %rsp,%r14
   0x000000000040110e <+26>:    mov    $0x0,%r12d                      %r12 置 0, 并且 %r13 %r14 %rbp 均和 %rsp 指向雷同地址 0x7fffffffe270

   0x0000000000401114 <+32>:    mov    %r13,%rbp
   0x0000000000401117 <+35>:    mov    0x0(%r13),%eax                  将第 %r13 指向的输出数复制到 %eax
   0x000000000040111b <+39>:    sub    $0x1,%eax                       将输出数减 1
   0x000000000040111e <+42>:    cmp    $0x5,%eax                       判断输出数是否小于等于 6, 因为上一步中减 1 操作
   0x0000000000401121 <+45>:    jbe    0x401128 <phase_6+52>           若大于 6, 则调用 explode_bomb
   0x0000000000401123 <+47>:    callq  0x40143a <explode_bomb>
=========================================================================================================================================================
   0x0000000000401128 <+52>:    add    $0x1,%r12d                      将 %r12 加 1
   0x000000000040112c <+56>:    cmp    $0x6,%r12d                      判断 %r12 是否等于 6
   0x0000000000401130 <+60>:    je     0x401153 <phase_6+95>           若等于 6, 跳转, 否则继续执行
   0x0000000000401132 <+62>:    mov    %r12d,%ebx                      将 %r12 复制到 %ebx

   0x0000000000401135 <+65>:    movslq %ebx,%rax                       将 %ebx 符号位扩大复制到 %rax
   0x0000000000401138 <+68>:    mov    (%rsp,%rax,4),%eax              将第 %ebx 输出数复制到 %eax
   0x000000000040113b <+71>:    cmp    %eax,0x0(%rbp)                  比拟 %r13 指向的输出数和 第 %ebx 输出数 是否相等
   0x000000000040113e <+74>:    jne    0x401145 <phase_6+81>           如果相等, 则调用 explode_bomb
   0x0000000000401140 <+76>:    callq  0x40143a <explode_bomb>
   0x0000000000401145 <+81>:    add    $0x1,%ebx                       将 %ebx 加 1
   0x0000000000401148 <+84>:    cmp    $0x5,%ebx                       判断 %ebx 是否小于等于 5
   0x000000000040114b <+87>:    jle    0x401135 <phase_6+65>           若小于等于, 跳转, 否则继续执行; 该循环判断 %r13 指向的数据和其后输出数不相等

   0x000000000040114d <+89>:    add    $0x4,%r13                       将 %r13 指向下一个输出数, 该循环判断所有的输出数全副不相等
   0x0000000000401151 <+93>:    jmp    0x401114 <phase_6+32>
=========================================================================================================================================================
   0x0000000000401153 <+95>:    lea    0x18(%rsp),%rsi                 将 %rsi 指向栈中跳过读入数据地位作为完结标记, 并且 %r14 仍和 %rsp 指向同一个地位
   0x0000000000401158 <+100>:   mov    %r14,%rax                       将 %r14 复制到 %rax
   0x000000000040115b <+103>:   mov    $0x7,%ecx
   0x0000000000401160 <+108>:   mov    %ecx,%edx                       将立刻数 0x7 复制到 %edx
   0x0000000000401162 <+110>:   sub    (%rax),%edx                     立刻数 7 减去 %r14 指向的数据
   0x0000000000401164 <+112>:   mov    %edx,(%rax)                     将 7 减的后果存回 %r14 执行的内存单元
   0x0000000000401166 <+114>:   add    $0x4,%rax                       %rax 指向下一个输出数
   0x000000000040116a <+118>:   cmp    %rsi,%rax                       比拟是否达到输出数组的开端,
   0x000000000040116d <+121>:   jne    0x401160 <phase_6+108>          该循环应用立刻数 7 减去每个输出数据
==========================================================================================================================================================
   0x000000000040116f <+123>:   mov    $0x0,%esi                       将 %rsi 置 0
   0x0000000000401174 <+128>:   jmp    0x401197 <phase_6+163>

   0x0000000000401176 <+130>:   mov    0x8(%rdx),%rdx                  将 0x8(%rdx) 指向内存单元的内容复制到 %rdx, 指向链表下一个元素
   0x000000000040117a <+134>:   add    $0x1,%eax                       将 %eax 加 1
   0x000000000040117d <+137>:   cmp    %ecx,%eax                       比拟 %ecx 和 %eax 是否相等
   0x000000000040117f <+139>:   jne    0x401176 <phase_6+130>          不相等, 持续遍历链表, 最终 %rdx 指向链表的第 %ecx 个节点
   0x0000000000401181 <+141>:   jmp    0x401188 <phase_6+148>
   0x0000000000401183 <+143>:   mov    $0x6032d0,%edx                  重置链表首地址
   0x0000000000401188 <+148>:   mov    %rdx,0x20(%rsp,%rsi,2)
   0x000000000040118d <+153>:   add    $0x4,%rsi
   0x0000000000401191 <+157>:   cmp    $0x18,%rsi
   0x0000000000401195 <+161>:   je     0x4011ab <phase_6+183>

   0x0000000000401197 <+163>:   mov    (%rsp,%rsi,1),%ecx              将 (%rsp + %rsi) 指向的数据复制到 %ecx
   0x000000000040119a <+166>:   cmp    $0x1,%ecx                       比拟 %ecx 是否小于等于 1
   0x000000000040119d <+169>:   jle    0x401183 <phase_6+143>          若小于等于, 跳转, 否则继续执行, 等于 1, %edx 间接指向链表首地址
   0x000000000040119f <+171>:   mov    $0x1,%eax                       将 %eax 置 1
   0x00000000004011a4 <+176>:   mov    $0x6032d0,%edx                  将 %rdx 指向内存单元 0x6032d0
   0x00000000004011a9 <+181>:   jmp    0x401176 <phase_6+130>          跳转; 该循环依据输出数将链表中对应的第输出数个节点的地址复制到 0x20(%rsp) 开始的栈中
 ==========================================================================================================================================================
   0x00000000004011ab <+183>:   mov    0x20(%rsp),%rbx                 将 0x20(%rsp)的链表节点地址复制到 %rbx
   0x00000000004011b0 <+188>:   lea    0x28(%rsp),%rax                 将 %rax 指向栈中下一个链表节点的地址
   0x00000000004011b5 <+193>:   lea    0x50(%rsp),%rsi                 将 %rsi 指向保留的链表节点地址的开端
   0x00000000004011ba <+198>:   mov    %rbx,%rcx

   0x00000000004011bd <+201>:   mov    (%rax),%rdx
   0x00000000004011c0 <+204>:   mov    %rdx,0x8(%rcx)                  将栈中指向的后一个节点的地址复制到前一个节点的地址地位
   0x00000000004011c4 <+208>:   add    $0x8,%rax                       挪动到下一个节点
   0x00000000004011c8 <+212>:   cmp    %rsi,%rax                       判断 6 个节点是否遍历结束
   0x00000000004011cb <+215>:   je     0x4011d2 <phase_6+222>
   0x00000000004011cd <+217>:   mov    %rdx,%rcx
   0x00000000004011d0 <+220>:   jmp    0x4011bd <phase_6+201>
   0x00000000004011d2 <+222>:   movq   $0x0,0x8(%rdx)                  该循环依照 7 减去输出数据的索引从新调整链表
==========================================================================================================================================================
   0x00000000004011da <+230>:   mov    $0x5,%ebp
   0x00000000004011df <+235>:   mov    0x8(%rbx),%rax                  将 %rax 指向 %rbx 下一个链表节点
   0x00000000004011e3 <+239>:   mov    (%rax),%eax
   0x00000000004011e5 <+241>:   cmp    %eax,(%rbx)                     比拟链表节点中第一个字段值的大小, 如果前一个节点值大于后一个节点值, 跳转
   0x00000000004011e7 <+243>:   jge    0x4011ee <phase_6+250>
   0x00000000004011e9 <+245>:   callq  0x40143a <explode_bomb>
   0x00000000004011ee <+250>:   mov    0x8(%rbx),%rbx                  将 %rbx 向后挪动, 指向栈中下一个链表节点的地址
   0x00000000004011f2 <+254>:   sub    $0x1,%ebp                       判断循环是否完结, 该循环判断栈中从新调整后的链表节点是否依照降序排列
   0x00000000004011f5 <+257>:   jne    0x4011df <phase_6+235>
   0x00000000004011f7 <+259>:   add    $0x50,%rsp
   0x00000000004011fb <+263>:   pop    %rbx
   0x00000000004011fc <+264>:   pop    %rbp
   0x00000000004011fd <+265>:   pop    %r12
   0x00000000004011ff <+267>:   pop    %r13
   0x0000000000401201 <+269>:   pop    %r14
   0x0000000000401203 <+271>:   retq
End of assembler dump.
%rsi 存储调用者 phase_2 栈帧的局部变量开始地址
%rdx = %rsi + 0
%rcx = %rsi + 4
%r8 =  %rsi + 8
%r9 =  %rsi + 12
(%rsp)  = %rsi + 16
8(%rsp) = %rsi + 20

Dump of assembler code for function read_six_numbers:
   0x000000000040145c <+0>: sub    $0x18,%rsp
   0x0000000000401460 <+4>: mov    %rsi,%rdx
   0x0000000000401463 <+7>: lea    0x4(%rsi),%rcx
   0x0000000000401467 <+11>:    lea    0x14(%rsi),%rax
   0x000000000040146b <+15>:    mov    %rax,0x8(%rsp)
   0x0000000000401470 <+20>:    lea    0x10(%rsi),%rax
   0x0000000000401474 <+24>:    mov    %rax,(%rsp)
   0x0000000000401478 <+28>:    lea    0xc(%rsi),%r9
   0x000000000040147c <+32>:    lea    0x8(%rsi),%r8
   0x0000000000401480 <+36>:    mov    $0x4025c3,%esi
   0x0000000000401485 <+41>:    mov    $0x0,%eax
   0x000000000040148a <+46>:    callq  0x400bf0 <__isoc99_sscanf@plt>
   0x000000000040148f <+51>:    cmp    $0x5,%eax
   0x0000000000401492 <+54>:    jg     0x401499 <read_six_numbers+61>
   0x0000000000401494 <+56>:    callq  0x40143a <explode_bomb>
   0x0000000000401499 <+61>:    add    $0x18,%rsp
   0x000000000040149d <+65>:    retq
End of assembler dump.

%rbp %rbx %r12~%15 被调用者保留寄存器
%r10 %r11 调用者保留寄存器
%rdi %rsi %rdx %rcx %r8 %r9 顺次保留输出数 1~6

  假如输出数据为 4 3 2 1 6 5

  猜想 0x6032d8 为链表首地址, 链表中每个节点占用 12 个 Byte, 前 8 字节保留两个 4 字 Byte 的整型数, 残余的 4Byte 寄存下个节点地址

  GDB 查看应用 7 减去对应的输出后的数据

(gdb) p /x $rsp
$1 = 0x7fffffffe270
(gdb) x/6dw 0x7fffffffe270
0x7fffffffe270: 3   4   5   6
0x7fffffffe280: 1   2

  从新调整链表前的链表的构造

(gdb) x/24xw 0x006032d0
0x6032d0 <node1>:   0x0000014c  0x00000001  0x006032e0  0x00000000
0x6032e0 <node2>:   0x000000a8  0x00000002  0x006032f0  0x00000000
0x6032f0 <node3>:   0x0000039c  0x00000003  0x00603300  0x00000000
0x603300 <node4>:   0x000002b3  0x00000004  0x00603310  0x00000000
0x603310 <node5>:   0x000001dd  0x00000005  0x00603320  0x00000000
0x603320 <node6>:   0x000001bb  0x00000006  0x00000000  0x00000000

  保留在栈中链表节点信息

(gdb) x/6xg 0x7fffffffe290
0x7fffffffe290: 0x00000000006032f0  0x0000000000603300
0x7fffffffe2a0: 0x0000000000603310  0x0000000000603320
0x7fffffffe2b0: 0x00000000006032d0  0x00000000006032e0

  依照 7 减去对应的输出后从新调整链表后的链表构造, 索引程序为 3 4 5 6 1 2

(gdb) x/24xw 0x006032d0
0x6032d0 <node1>:   0x0000014c  0x00000001  0x006032e0  0x00000000
0x6032e0 <node2>:   0x000000a8  0x00000002  0x00000000  0x00000000
0x6032f0 <node3>:   0x0000039c  0x00000003  0x00603300  0x00000000
0x603300 <node4>:   0x000002b3  0x00000004  0x00603310  0x00000000
0x603310 <node5>:   0x000001dd  0x00000005  0x00603320  0x00000000
0x603320 <node6>:   0x000001bb  0x00000006  0x006032d0  0x00000000

  破解思路:

  将链表中每个节点依照前 4 字节降序排序:3 4 5 6 1 2

  因为在后面应用 7 减去对应的值, 所以破解明码:4 3 2 1 6 5

phase6 太难了,心力憔悴。phase6 的解析来自以下链接:
这是链接

总结

  试验太难了。剖析汇编像看天书,不过通过本次试验也进步了本人浏览汇编代码的能力,学会了根本的 GDB 调试代码的步骤。播种颇丰!

  养成习惯,先赞后看!如果感觉写的不错,欢送关注,点赞,转发,谢谢!

如遇到排版错乱的问题,能够通过以下链接拜访我的 CSDN。

CSDN:CSDN 搜寻“嵌入式与 Linux 那些事”

欢送欢送关注我的公众号:嵌入式与 Linux 那些事,支付秋招口试面试大礼包(华为小米等大厂面经,嵌入式知识点总结,口试题目,简历模版等)和 2000G 学习材料。

正文完
 0