天天看点

csapp - lab2

花了一天多时间, 做完了csapp很有名的bomb实验,感到有趣的同时受益匪浅。这个lab一共有6个阶段,越往后难度越大。个人觉得最后一个lab是最有趣的,涉及到了链表以及排序。

上csapp官网下载bomb实验的相关资料,解压后利用

objdump -d bomb > bomb.asm

进行反汇编得到bomb.asm,再进行下面的六个阶段的拆炸弹过程。

phase_1

b20 <phase_1>:
 b20:                         push   %ebp
 b21:    e5                   mov    %esp,%ebp
 b23:    ec                 sub    $0x8,%esp
 b26:   b                  mov    (%ebp),%eax
 b29:    c4 f8                add    $0xfffffff8,%esp
 b2c:    c0             push   $0x80497c0
 b31:                         push   %eax
 b32:   e8 f9             call    <strings_not_equal>
 b37:    c4                 add    $0x10,%esp
 b3a:    c0                   test   %eax,%eax
 b3c:                       je     b43 <phase_1+>
 b3e:   e8 b9             call   fc <explode_bomb>
 b43:    ec                   mov    %ebp,%esp
 b45:   d                      pop    %ebp
 b46:   c3                      ret    
 b47:                         nop
           

查看此函数和函数

strings_not_equal

很简单就可以看出是比较输入的字符串和位于

0x80497c0

的字符串是否相等,所以直接

gdb

后打断点查看该位置的字符串即可。

phase_2

b48 <phase_2>:
 b48:                         push   %ebp
 b49:    e5                   mov    %esp,%ebp
 b4b:    ec                 sub    $0x20,%esp
 b4e:                         push   %esi
 b4f:                         push   %ebx
 b50:   b                  mov    (%ebp),%edx
 b53:    c4 f8                add    $0xfffffff8,%esp
 b56:   d  e8                lea    -(%ebp),%eax
 b59:                         push   %eax
 b5a:                         push   %edx
 b5b:   e8              call   fd8 <read_six_numbers>
 b60:    c4                 add    $0x10,%esp

 b63:    d e8              cmpl   $0x1,-(%ebp)
 b67:                       je     b6e <phase_2+>
 b69:   e8 e             call   fc <explode_bomb>
 b6e:   bb              mov    $0x1,%ebx
 b73:   d  e8                lea    -(%ebp),%esi
 b76:   d                  lea    (%ebx),%eax
 b79:   f af  e fc          imul   -(%esi,%ebx,),%eax
 b7e:     e                cmp    %eax,(%esi,%ebx,)
 b81:                       je     b88 <phase_2+>
 b83:   e8              call   fc <explode_bomb>
 b88:                         inc    %ebx
 b89:    fb                 cmp    $0x5,%ebx
 b8c:   e e8                   jle    b76 <phase_2+>

 b8e:   d  d8                lea    -(%ebp),%esp
 b91:   b                      pop    %ebx
 b92:   e                      pop    %esi
 b93:    ec                   mov    %ebp,%esp
 b95:   d                      pop    %ebp
 b96:   c3                      ret    
 b97:                         nop
           

第二个阶段是要输入6个数字作为答案满足后面的检查,先读入输入的6个数字

read_six_numbers

,再对这6个数字进行循环检查,第一个数字为1,从

8048b63

这一行可以看出来,后面的数字是根据前一个数字和索引加一相乘获得。

phase_3

08048b98 <phase_3>:
 b98:                         push   %ebp
 b99:    e5                   mov    %esp,%ebp
 b9b:    ec                 sub    $0x14,%esp

 8048b9e:   53                      push   %ebx
 8048b9f:   8b 55 08                mov    0x8(%ebp),%edx
 8048ba2:   83 c4 f4                add    $0xfffffff4,%esp
 8048ba5:   8d 45 fc                lea    -0x4(%ebp),%eax     ;; int
 ba8:                         push   %eax                
 ba9:   d  fb                lea    -(%ebp),%eax     ;; char
 bac:                         push   %eax    
 bad:   d  f4                lea    -(%ebp),%eax     ;; int
 bb:                         push   %eax
 bb1:    de   08          push   $0x80497de
 bb6:                         push   %edx
 bb7:   e8 a4 fc ff ff          call    <sscanf@plt>

 bbc:    c4                 add    $0x2,%esp
 bbf:    f8                 cmp    $0x2,%eax               ;; 参数个数必须大于
 bc2:   f                    jg     bc9 <phase_3+>
 bc4:   e8  09            call   fc <explode_bomb>

 bc9:    d f4              cmpl   $0x7,-(%ebp)         ;; 第一个数字必须不大于,注意这里使用的是无符号数字的比较
 bcd:   0f  b5          ja     c88 <phase_3+>

 bd3:   b  f4                mov    -(%ebp),%eax
 bd6:   ff   e8   08    jmp    *0x80497e8(,%eax,)     ;; 根据第一个数字n3的值决定跳转地址,取值范围-

 bdd:   d                  lea    (%esi),%esi 

 be:   b3                    mov    $0x71,%bl               ;; n1 = 
 be2:    d fc 09       cmpl   $0x309,-(%ebp)
 be9:   0f  a          je     c8f <phase_3+>
 bef:   e8 08 09            call   fc <explode_bomb>
 bf4:   e9              jmp    c8f <phase_3+>
 bf9:   d b4         lea    (%esi,%eiz,),%esi

 c0:   b3                    mov    $0x62,%bl               ;; n1 = 
 c02:    d fc d6       cmpl   $0xd6,-(%ebp)
 c09:   0f            je     c8f <phase_3+>
 c0f:   e8 e8 08            call   fc <explode_bomb>
 c14:   eb                    jmp    c8f <phase_3+>

 c16:   b3                    mov    $0x62,%bl               ;; n1 = 
 c18:    d fc f3       cmpl   $0x2f3,-(%ebp)
 c1f:    e                   je     c8f <phase_3+>
 c21:   e8 d6 08            call   fc <explode_bomb>
 c26:   eb                    jmp    c8f <phase_3+>

 c28:   b3 b                   mov    $0x6b,%bl               ;; n1 = 
 c2a:    d fc fb       cmpl   $0xfb,-(%ebp)
 c31:    c                   je     c8f <phase_3+>
 c33:   e8 c4 08            call   fc <explode_bomb>
 c38:   eb                    jmp    c8f <phase_3+>
 c3a:   d b6           lea    (%esi),%esi

 c4:   b3 f                   mov    $0x6f,%bl               ;; n1 = 
 c42:    d fc a       cmpl   $0xa,-(%ebp)
 c49:                       je     c8f <phase_3+>
 c4b:   e8 ac 08            call   fc <explode_bomb>
 c5:   eb d                   jmp    c8f <phase_3+>

 c52:   b3                    mov    $0x74,%bl               ;; n1 = 
 c54:    d fc ca       cmpl   $0x1ca,-(%ebp)
 c5b:                       je     c8f <phase_3+>
 c5d:   e8 a 08            call   fc <explode_bomb>
 c62:   eb b                   jmp    c8f <phase_3+>

 c64:   b3                    mov    $0x76,%bl               ;; n1 = 
 c66:    d fc 0c       cmpl   $0x30c,-(%ebp)
 c6d:                       je     c8f <phase_3+>
 c6f:   e8  08            call   fc <explode_bomb>
 c74:   eb                    jmp    c8f <phase_3+>

 c76:   b3                    mov    $0x62,%bl               ;; n1 = 
 c78:    d fc 0c       cmpl   $0x20c,-(%ebp)
 c7f:    0e                   je     c8f <phase_3+>
 c81:   e8  08            call   fc <explode_bomb>
 c86:   eb                    jmp    c8f <phase_3+>

 c88:   b3                    mov    $0x78,%bl               
 c8a:   e8 d 08            call   fc <explode_bomb>

 c8f:   a d fb                cmp    -(%ebp),%bl
 c92:                       je     c99 <phase_3+>
 c94:   e8  08            call   fc <explode_bomb>
 c99:   b d e8                mov    -(%ebp),%ebx
 c9c:    ec                   mov    %ebp,%esp
 c9e:   d                      pop    %ebp
 c9f:   c3                      ret 
           

先使用

print (char*)

查看

0x80497de

代表的字符串格式,得到

%d %c %d

,再往下看,得到输入的三个值以后,需要知道三个值的取值,避免炸弹爆炸。查看

0x80497e8+4*n3(n1<=7)

地址的16进制值,代表的是8个跳转地址

0x8048be0, 0x8048c00 ...

。选择其中一个跳转,我选择的是0,从

$0x309,-0x4(%ebp)

可以得到第三个参数的值,再看

0x8048c8f

处的代码,可以得到第二个字符类型的值是

0x71

,查ascii表即可得到该字符。至此,第三阶段拆弹完毕。

phase_4

08048ca <func4>:
 ca:                         push   %ebp
 ca1:    e5                   mov    %esp,%ebp
 ca3:    ec                 sub    $0x10,%esp

 8048ca6:   56                      push   %esi
 8048ca7:   53                      push   %ebx
 8048ca8:   8b 5d 08                mov    0x8(%ebp),%ebx       ;; (%ebx) = v1

 cab:    fb                 cmp    $0x1,%ebx
 cae:   e                    jle    cd <func4+> ;; 有符号小于等于,则v1 = 
 cb:    c4 f4                add    $0xfffffff4,%esp
 cb3:   d  ff                lea    -(%ebx),%eax
 cb6:                         push   %eax
 cb7:   e8 e4 ff ff ff          call   ca <func4>
 cbc:    c6                   mov    %eax,%esi
 cbe:    c4 f4                add    $0xfffffff4,%esp
 cc1:   d  fe                lea    -(%ebx),%eax
 cc4:                         push   %eax
 cc5:   e8 d6 ff ff ff          call   ca <func4>
 cca:    f                   add    %esi,%eax
 ccc:   eb                    jmp    cd5 <func4+>
 cce:    f6                   mov    %esi,%esi

 cd:   b8              mov    $0x1,%eax            ;; 如果v1 = ,(%eax)= ,返回
 cd5:   d  e8                lea    -(%ebp),%esp

 cd8:   b                      pop    %ebx
 cd9:   e                      pop    %esi
 cda:    ec                   mov    %ebp,%esp
 cdc:   d                      pop    %ebp
 cdd:   c3                      ret    
 cde:    f6                   mov    %esi,%esi

08048ce <phase_4>:
 ce:                         push   %ebp
 ce1:    e5                   mov    %esp,%ebp
 ce3:    ec                 sub    $0x18,%esp

 8048ce6:   8b 55 08                mov    0x8(%ebp),%edx
 8048ce9:   83 c4 fc                add    $0xfffffffc,%esp
 8048cec:   8d 45 fc                lea    -0x4(%ebp),%eax        ;; 假设值为v1
 cef:                         push   %eax
 cf:    08   08          push   $0x8049808             ;; () = "%d"
 cf5:                         push   %edx                   ;; 输入值的地址
 cf6:   e8  fb ff ff          call    <sscanf@plt>

 cfb:    c4                 add    $0x1,%esp
 cfe:    f8                 cmp    $0x1,%eax
 d01:                       jne    d09 <phase_4+> ;; 参数个数为,否则爆炸

 d03:    d fc              cmpl   $0x,-(%ebp)        ;; 将v1和比较,带符号数比较,需要大于
 d07:   f                    jg     d0e <phase_4+>
 d09:   e8 ee             call   fc <explode_bomb>

 d0e:    c4 f4                add    $0xfffffff4,%esp
 d11:   b  fc                mov    -(%ebp),%eax
 d14:                         push   %eax
 d15:   e8  ff ff ff          call   ca <func4>

 d1a:    c4                 add    $0x1,%esp
 d1d:    f8                 cmp    $0x37,%eax             ;; 不等于就爆炸
 d2:                       je     d27 <phase_4+>

 d22:   e8 d5             call   fc <explode_bomb>
 d27:    ec                   mov    %ebp,%esp
 d29:   d                      pop    %ebp
 d2a:   c3                      ret    
 d2b:                         nop
           

这里的

phase_4

函数调用了

func_4

函数,而

func_4

是fibinacci函数,所以这个炸弹还是很简单的,只需要求到

func_4(x) = 0x37 = 55

的数字x就可以了。

phase_5

08048d2c <phase_5>:
 d2c:                         push   %ebp
 d2d:    e5                   mov    %esp,%ebp
 d2f:    ec                 sub    $0x10,%esp
 8048d32:   56                      push   %esi
 8048d33:   53                      push   %ebx

 8048d34:   8b 5d 08                mov    0x8(%ebp),%ebx
 8048d37:   83 c4 f4                add    $0xfffffff4,%esp
 8048d3a:   53                      push   %ebx
 8048d3b:   e8 d8 02 00 00          call   8049018 <string_length>       ;; 获取输入的字符串长度,放在%eax中

 d4:    c4                 add    $0x1,%esp
 d43:    f8                 cmp    $0x6,%eax
 d46:                       je     d4d <phase_5+>        ;; 字符串长度为
 d48:   e8 af             call   fc <explode_bomb>
 d4d:    d2                   xor    %edx,%edx
 d4f:   d d f8                lea    -(%ebp),%ecx
 d52:   be  b2  08          mov    $0x804b22,%esi
 d57:   a  a                mov    (%edx,%ebx,),%al
 d5a:    0f                   and    $0xf,%al
 d5c:   0f be c                movsbl %al,%eax

 d5f:   a                  mov    (%eax,%esi,),%al
 d62:     0a                mov    %al,(%edx,%ecx,)
 d65:                         inc    %edx
 d66:    fa                 cmp    $0x5,%edx
 d69:   e ec                   jle    d57 <phase_5+>
 d6b:   c6  fe              movb   $0x,-(%ebp)
 d6f:    c4 f8                add    $0xfffffff8,%esp

 d72:    0b   08          push   $0x804980b
 d77:   d  f8                lea    -(%ebp),%eax
 d7a:                         push   %eax
 d7b:   e8 b             call    <strings_not_equal>
 d8:    c4                 add    $0x1,%esp
 d83:    c                   test   %eax,%eax
 d85:                       je     d8c <phase_5+>
 d87:   e8              call   fc <explode_bomb>
 d8c:   d  e8                lea    -(%ebp),%esp

 d8f:   b                      pop    %ebx
 d9:   e                      pop    %esi
 d91:    ec                   mov    %ebp,%esp
 d93:   d                      pop    %ebp
 d94:   c3                      ret    
 d95:   d                  lea    (%esi),%esi
           

push $0x804980b

和后面的

call 8049030 <strings_not_equal>

是为了比较两个字符串是否相等,查看地址

0x804980b

的字节码,会得到6个字节码

c0 c1 c2 c3 c4 c5

。而和这6个字节相比较的字符串是通过

mov %al,(%edx,%ecx,1)

循环写入的。而这里的

%al

则是来自

mov (%eax,%esi,1),%al

,这里的

%esi

的值是

0x804b220

,该地址后面的字节可以通过

x/2w 0x804b220

获取,其实就是根据我们输入的字符的低字节作为

mov (%eax,%esi,1),%al

%eax

,即索引值,获取我们想要的字符,理解之后就可以根据上面的分析很容易得到想要的字符低位字节,再在ascii表中选择字符就可以了,答案不是唯一的。

phase_6

d98 <phase_6>:
 d98:                         push   %ebp
 d99:    e5                   mov    %esp,%ebp
 d9b:    ec c                sub    $0x4c,%esp
 d9e:                         push   %edi
 d9f:                         push   %esi
 da0:                         push   %ebx

 da1:   b                  mov    (%ebp),%edx
 da4:   c7  cc c b2      movl   $0x804b26c,-(%ebp)
 dab:    c4 f8                add    $0xfffffff8,%esp
 dae:   d  e8                lea    -(%ebp),%eax
 db1:                         push   %eax
 db2:                         push   %edx
 db3:   e8              call   fd8 <read_six_numbers>

 db8:    ff                   xor    %edi,%edi
 dba:    c4                 add    $0x10,%esp
 dbd:   d                  lea    (%esi),%esi
 ;; 下面这段证明个数字为-,顺序不知,不过个数字不能重复
 dc0:   d  e8                lea    -(%ebp),%eax
 dc3:   b  b8                mov    (%eax,%edi,),%eax
 dc6:                         dec    %eax                      ;; 证明不能为,否则无符号数比较后不跳转
 dc7:    f8                 cmp    $0x5,%eax
 dca:                       jbe    dd1 <phase_6+>    ;; 无符号低于或等于
 dcc:   e8 b             call   fc <explode_bomb>
 dd1:   d f                 lea    (%edi),%ebx
 dd4:    fb                 cmp    $0x5,%ebx
 dd7:   f                    jg     dfc <phase_6+>
 dd9:   d  bd        lea    (,%edi,),%eax
 de0:     c8                mov    %eax,-(%ebp)
 de3:   d  e8                lea    -(%ebp),%esi
 de6:   b  c8                mov    -(%ebp),%edx
 de9:   b                  mov    (%edx,%esi,),%eax
 dec:   b  e                cmp    (%esi,%ebx,),%eax
 def:                       jne    df6 <phase_6+>
 df1:   e8              call   fc <explode_bomb>
 df6:                         inc    %ebx
 df7:    fb                 cmp    $0x5,%ebx
 dfa:   e ea                   jle    de6 <phase_6+>
 dfc:                         inc    %edi
 dfd:    ff                 cmp    $0x5,%edi
 :   e be                   jle    dc0 <phase_6+>

 :    ff                   xor    %edi,%edi
 :   d d e8                lea    -(%ebp),%ecx
 :   d  d0                lea    -(%ebp),%eax
 a:     c4                mov    %eax,-(%ebp)
 d:   d                  lea    (%esi),%esi

 :   b  cc                mov    -(%ebp),%esi
 :   bb              mov    $0x1,%ebx
 :   d  bd        lea    (,%edi,),%eax
 f:    c2                   mov    %eax,%edx
 :   b c                 cmp    (%eax,%ecx,),%ebx
 :   d                    jge     <phase_6+>
 :   b  a                mov    (%edx,%ecx,),%eax
 :   d b4         lea    (%esi,%eiz,),%esi
 :   b                  mov    (%esi),%esi
 :                         inc    %ebx
 :    c3                   cmp    %eax,%ebx
 :   c f8                   jl      <phase_6+>
 :   b  c4                mov    -(%ebp),%edx
 b:     ba                mov    %esi,(%edx,%edi,)
 e:                         inc    %edi
 f:    ff                 cmp    $0x5,%edi
 :   e cc                   jle     <phase_6+>

 :   b  d0                mov    -(%ebp),%esi
 :     cc                mov    %esi,-(%ebp)
 a:   bf              mov    $0x1,%edi
 f:   d  d0                lea    -(%ebp),%edx

 :   b  ba                mov    (%edx,%edi,),%eax
 :                     mov    %eax,(%esi)
 :    c6                   mov    %eax,%esi
 a:                         inc    %edi
 b:    ff                 cmp    $0x5,%edi
 e:   e f2                   jle     <phase_6+>

 :   c7          movl   $0x0,(%esi)
 :   b  cc                mov    -(%ebp),%esi
 a:    ff                   xor    %edi,%edi
 c:   d                lea    (%esi,%eiz,),%esi

 :   b                  mov    (%esi),%edx
 :   b                    mov    (%esi),%eax
 :   b                    cmp    (%edx),%eax
 :   d                    jge    e <phase_6+>
 :   e8 e             call   fc <explode_bomb>
 e:   b                  mov    (%esi),%esi
 :                         inc    %edi
 :    ff                 cmp    $0x4,%edi
 :   e e9                   jle     <phase_6+>

 :   d  a8                lea    -(%ebp),%esp
 a:   b                      pop    %ebx
 b:   e                      pop    %esi
 c:   f                      pop    %edi
 d:    ec                   mov    %ebp,%esp
 f:   d                      pop    %ebp
 :   c3                      ret    
 :   d                  lea    (%esi),%esi
           
  • 先利用函数

    read_six_numbers

    读入6个数字,根据bomb的条件可以得到6个数字为不重复的1-6,因为如果数字为0,

    dec %eax

    得到的二进制位0xffffffff, 从

    cmp $0x5,%eax

    jbe 8048dd1 <phase_6+0x39>

    可以得出是无符号比较,所以不会跳转引起bomb;
  • 0x8048e10

    0x8048e42

    的汇编语句必须联系地址为

    0x804b26c

    的情况才能得出结论,使用gdb获取该地址存储的值,可以得到该处是一个链表的node节点,再假设一组1-6的随机分布代入该段汇编,可以得出实际上有2个数组,分别命名为input[]和addr[]。input数组存储的是我们输入的6个数字,而addr数组则是存储input数组相应位置所对应的链表索引。举个例子,如果

    input[] = {5, 2, 1, 4, 3, 6}

    ,那么b[0]所对应的就是链表的第5个node,该node的第二个字存储的其实就是0x5;
  • 最后将addr[]数组元素即链表地址所对应的链表进行重新连接排序,根据最后避免bomb的条件很容易可以看出链表数据是倒序排列,根据

    0x804b26c

    处的链表信息可以得出最后的索引顺序。至此,第六阶段也结束了。

继续阅读