CSAPP 3e: Bomb lab (phase_1)

  这一个lab拖了好久才开始慢慢完成,花的时间比较多,我也是没想到会需要这么多时间来折腾。考虑到如果把所有关卡的内容都一次发出来,太长了。所以分开操作吧。

  然后,有一点是,在开始解题前的确该好好认识一下GDB,因为要使用这个工具。虽然我也感觉有些东西是可以等需要的时候再查的,但是后来找到了一篇介绍gdb命令的,写的比较详细,就下载了打印出来,占用了几节课的时间好好看了一下,就感觉特别有用,比之前更加熟悉了GDB。大概是在网页上看的时候比较急躁,所以吸收不好吧23333。还有,在解题过程中参考了不少网上前辈的资料,向每位分享知识的前辈致敬。

  我的操作方法是把程序反汇编到一个文本中,反汇编了两个版本,一个是objdump -d  bomb > bomb_disas.s (反汇编bomb中需要执行指令的那些section),另一个是 objdump -D bomb > bomb_disas2.s (反汇编bomb中所有的section). 这个放在Windows系统中,便于查看,分析,主要是感觉notepad++看着舒服啦。然后,调试是在虚拟机中进行的。

  关于GDB命令的东西,如果你还没有接触过,那还是先了解一下的好,或者可以在阅读途中碰上了再百度。

 

  在Linux中,gdb bomb用GDB打开bomb,输入list可以查看代码。不过bomb lab 文件夹中也有源码。通过这个可以看到代码中每个关卡的通用格式,比如:

    input = read_line();             /* Get input                   */
    phase_1(input);                  /* Run the phase               */
    phase_defused();                 /* Drat!  They figured it out!
                      * Let me know how they did it. */

 

  每一关的函数是phase_x,这里x是关卡数。看第一关的汇编函数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

  一个是 strings_not_eaqual ,一个是 explode_bomb .这里先说这个explode_bomb是解密失败后引发爆炸(bomb)的函数。具体可以自行去调用查看,在bomb_disas.s中查找该函数即可查看其执行过程,不予细说了。然后,这里另一个函数是 strings_not_eaqual .从字面上也可以理解,这个函数是检查两个字符串是否一样。应用就是你输入的字符串如果跟他指向的另一串字符串不一样,就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是储存输入的字符串首地址
  40133f:    48 89 f5                 mov    %rsi,%rbp            ;由phase(1)知道;;%rsi是储存拆弹密码字符串的首地址.
  401342:    e8 d4 ff ff ff           callq  40131b <string_length>            ;计算字符串长度,
  401347:    41 89 c4                 mov    %eax,%r12d
  40134a:    48 89 ef                 mov    %rbp,%rdi
  40134d:    e8 c9 ff ff ff           callq  40131b <string_length>
  401352:    ba 01 00 00 00           mov    $0x1,%edx
  401357:    41 39 c4                 cmp    %eax,%r12d                        ;字符串长度不一样的话,就bomb.
  40135a:    75 3f                    jne    40139b <strings_not_equal+0x63>
  40135c:    0f b6 03                 movzbl (%rbx),%eax                        ;零扩展传递第一个字符到%eax.
  40135f:    84 c0                    test   %al,%al                            ;第一个为0就ret,以0作为返回值。    
  401361:    74 25                    je     401388 <strings_not_equal+0x50>  ;但是要考虑字符串长度,所以0不是正确答案
  401363:    3a 45 00                 cmp    0x0(%rbp),%al                    ;对比第一个字符串,相同则进入循环,否则bomb
  401366:    74 0a                    je     401372 <strings_not_equal+0x3a>  ;0x401372进入循环。
  401368:    eb 25                    jmp    40138f <strings_not_equal+0x57>
  40136a:    3a 45 00                 cmp    0x0(%rbp),%al                    ;循环的起始地址
  40136d:    0f 1f 00                 nopl   (%rax)
  401370:    75 24                    jne    401396 <strings_not_equal+0x5e>    ;第N个字符不相同则bomb.
  401372:    48 83 c3 01              add    $0x1,%rbx                        ;所输入字符串向后移一位,为了向后检查
  401376:    48 83 c5 01              add    $0x1,%rbp                        ;密码字符串后移,为了向后检查
  40137a:    0f b6 03                 movzbl (%rbx),%eax                        ;零扩展传递第N个字符到%eax
  40137d:    84 c0                    test   %al,%al
  40137f:    75 e9                    jne    40136a <strings_not_equal+0x32>    ;第N个字符不是%0,即字符串未结束,则继续循环
  401381:    ba 00 00 00 00           mov    $0x0,%edx
  401386:    eb 13                    jmp    40139b <strings_not_equal+0x63>
  401388:    ba 00 00 00 00           mov    $0x0,%edx
  40138d:    eb 0c                    jmp    40139b <strings_not_equal+0x63>
  40138f:    ba 01 00 00 00           mov    $0x1,%edx
  401394:    eb 05                    jmp    40139b <strings_not_equal+0x63>
  401396:    ba 01 00 00 00           mov    $0x1,%edx
  40139b:    89 d0                    mov    %edx,%eax
  40139d:    5b                       pop    %rbx
  40139e:    5d                       pop    %rbp
  40139f:    41 5c                    pop    %r12
  4013a1:    c3                       retq   

  这里重点就是%rdi存储输入所输入的字符串的首地址,%rsi存储拆弹密码字符串的首地址。

  再回过来查看phase_1, 第二行将0x402400赋值给了寄存器%esi。

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                    ;%eax返回值为1就bomb
  400ef0:    74 05                    je     400ef7 <phase_1+0x17>        ;通过查看strings_not_equal函数知道
  400ef2:    e8 43 05 00 00           callq  40143a <explode_bomb>        ;0x402400位置的字符串即为第一关答案.
  400ef7:    48 83 c4 08              add    $0x8,%rsp                    ;在gdb中用 x/s 0x402400查看内存即可看到答案。            
  400efb:    c3                       retq 

  在gdb中用 x/s 0x402400查看内存中的字符串就得到了答案。

  

  输入该字符串即可通过第一关。

  

  至此,第一关结束。加油挑战!

 

  对我来说,感觉这bomb lab拖了好久才完成的原因,主要还是在于对GDB工具的不了解,所以弄得是无从下手。所以如果有后来者看到这篇文章,希望你先好好熟悉一下GDB,这样才可以事半功倍。

posted @ 2017-09-14 18:08  西湖又雨  阅读(858)  评论(0编辑  收藏  举报