CSAPP: bomb lab

来源:互联网 发布:软件销售 编辑:程序博客网 时间:2024/05/17 22:26

有兴趣做这个lab的可以到这里下载, 里面包含原文档和bomb二进制文件和我的解答及过程:

http://download.csdn.net/detail/xuzhezhaozhao/5288867 

或 

https://github.com/xuzhezhaozhao/CSAPP-Labs

花了两天时间把CSAPP中的bomb实验做了一下,果然是名不虚传,感觉学到不少东西,对于汇编和GDB熟悉不少!

实验内容很简单,就是输入6个特定的字符串, 输错一个就失败. 而要得到这6个字符串, 则要反汇编bomb文件, 通过汇编代码找到这6个字符串.

比较变态的是里面除了这6个字符串外, 还有一个secret phase, 就是隐藏的字符串, 必须输入特定的字符串才能得到!


不多废话, 开始实验!


首先用objdump得到bomb反汇编代码: 

# objdump -d bomb > bomb.s

打开bomb.s文件一看, 有1000多行, 咋一看头都大了, 再仔细一看, 发现里面有函数名的注释, 于是就能在其中看到有6个函数, 

phase_1, phase_2, phase_3, phase_4, phase_5, phase_6, 很明确了, 下面就是研究这6个函数了.

  • phase_1:
 08048b20 <phase_1>: 8048b20:55                           push   %ebp 8048b21:89 e5                mov    %esp,%ebp 8048b23:83 ec 08             sub    $0x8,%esp 8048b26:8b 45 08             mov    0x8(%ebp),%eax 8048b29:83 c4 f8             add    $0xfffffff8,%esp 8048b2c:68 c0 97 04 08       push   $0x80497c0                      <---------- 参数1, 标准字符串, 也就是答案 8048b31:50                     push   %eax                                  <--------- 参数2, 输入的字符串 8048b32:e8 f9 04 00 00       call   8049030 <strings_not_equal>  <------- 调用字符串比较函数, 用栈来传递参数 8048b37:83 c4 10             add    $0x10,%esp 8048b3a:85 c0                test   %eax,%eax                           <---------- 由返回值判断标准字符串与你输入的字符串是否相等 8048b3c:74 05                je     8048b43 <phase_1+0x23> 8048b3e:e8 b9 09 00 00       call   80494fc <explode_bomb>      <-------- 爆炸函数 8048b43:89 ec                mov    %ebp,%esp 8048b45:5d                    pop    %ebp 8048b46:c3                           ret     8048b47:90                   nop

这个比较简单, 仔细分析就发现这个函数只是调用了一个strings_not_equal来判断输入的字符串是否与标准字符串相等, 不等就爆炸.由上面的分析知标准字符串保存在
0x80497c0位置处. 用GDB调试, 设置好断点, 用 x/s 0x80497c0命令查看字符串的值, 得到 "Public speaking is very easy." 这个字符串, 这就是第一个字符串了!
solution: Public speaking is very easy.

  • phase_2:
 08048b48 <phase_2>: 8048b48:55                     push   %ebp 8048b49:89 e5                mov    %esp,%ebp 8048b4b:83 ec 20             sub    $0x20,%esp 8048b4e:56                     push   %esi 8048b4f:53                     push   %ebx 8048b50:8b 55 08             mov    0x8(%ebp),%edx             <---------- 由栈传递输入的字符串的首地址 8048b53:83 c4 f8             add    $0xfffffff8,%esp 8048b56:8d 45 e8             lea    -0x18(%ebp),%eax 8048b59:50                     push   %eax 8048b5a:52                     push   %edx 8048b5b:e8 78 04 00 00       call   8048fd8 <read_six_numbers>   <----- 调用函数, 很明显, 从函数名就可以看出来这是读取6个数字 8048b60:83 c4 10             add    $0x10,%esp                               +-------------    所以输入的字符串应该是6个数字的形式 8048b63:83 7d e8 01          cmpl   $0x1,-0x18(%ebp)                     <-------------- ($ebp)-0x18  所指向的地址中保存第一个数 a[0] 8048b67:74 05                je     8048b6e <phase_2+0x26>          <--------------  if == , goto .next, 说明第一个数一定是1 8048b69:e8 8e 09 00 00       call   80494fc <explode_bomb> 8048b6e:bb 01 00 00 00       mov    $0x1,%ebx 8048b73:8d 75 e8             lea    -0x18(%ebp),%esi                                               .loop 8048b76:8d 43 01             lea    0x1(%ebx),%eax 8048b79:0f af 44 9e fc       imul   -0x4(%esi,%ebx,4),%eax  8048b7e:39 04 9e             cmp    %eax,(%esi,%ebx,4)              <--------------  (esi+4*ebx) : eax 8048b81:74 05                je     8048b88 <phase_2+0x40>        <--------------  if ==,  goto next2 8048b83:e8 74 09 00 00       call   80494fc <explode_bomb>                                               .next2 8048b88:43                     inc    %ebx 8048b89:83 fb 05             cmp    $0x5,%ebx                                <------------- ebx : 5 8048b8c:7e e8                jle    8048b76 <phase_2+0x2e>           <-------------  if <=, goto .loop 8048b8e:8d 65 d8             lea    -0x28(%ebp),%esp 8048b91:5b                     pop    %ebx 8048b92:5e                     pop    %esi 8048b93:89 ec                mov    %ebp,%esp 8048b95:5d                     pop    %ebp 8048b96:c3                     ret     8048b97:90                     nop

用GDB单步调试可得到0x804b6d0 保存"%d %d %d %d %d %d"字符串, 0xffffd6a0 中保存的是输入的6个数字的起始位置, 6个数字满足关系,
a[n] * (n + 2) = a[n + 1] n=0...5, 且a[0] = 1. 这一关主要就考察循环.
solution: 1 2 6 24 120 720

  • phase_3
 08048b98 <phase_3>: 8048b98:55                           push   %ebp 8048b99:89 e5                mov    %esp,%ebp 8048b9b:83 ec 14             sub    $0x14,%esp 8048b9e:53                           push   %ebx 8048b9f:8b 55 08             mov    0x8(%ebp),%edx                <------ get &phase_3, 输入字符串首地址 8048ba2:83 c4 f4             add    $0xfffffff4,%esp             <----- esp-12 8048ba5:8d 45 fc             lea    -0x4(%ebp),%eax          <----- eax = ebp - 4 8048ba8:50                           push   %eax 8048ba9:8d 45 fb             lea    -0x5(%ebp),%eax 8048bac:50                      push   %eax 8048bad:8d 45 f4             lea    -0xc(%ebp),%eax 8048bb0:50                     push   %eax 8048bb1:68 de 97 04 08       push   $0x80497de                <------- 保存sscanf函数的输入格式参数 "%d %c %d" 8048bb6:52                     push   %edx                           <------ 输入的字符串, 形式为 "1个整数 1个字符 1个整数" 8048bb7:e8 a4 fc ff ff       call   8048860 <sscanf@plt> 8048bbc:83 c4 20             add    $0x20,%esp                  <------ esp+32 8048bbf:83 f8 02             cmp    $0x2,%eax                        <----  %eax是函数sscanf返回值, 是读取的参数个数 8048bc2:7f 05                  jg     8048bc9 <phase_3+0x31> 8048bc4:e8 33 09 00 00       call   80494fc <explode_bomb> 8048bc9:83 7d f4 07          cmpl   $0x7,-0xc(%ebp)                <----- 第一个数字a[0]与7比较, 大于7就跪了, 所以第一个数可以为0-7 8048bcd:0f 87 b5 00 00 00    ja     8048c88 <phase_3+0xf0> 8048bd3:8b 45 f4             mov    -0xc(%ebp),%eax              <------- eax 中保存第一个数字a[0] 8048bd6:ff 24 85 e8 97 04 08 jmp    *0x80497e8(,%eax,4)    <------ 直接跳转指令, 是一条switch语句,  8048bdd:8d 76 00             lea    0x0(%esi),%esi                         +----- 跳转地址用了一张table保存, $eax中的值作为偏移量 8048be0:b3 71                mov    $0x71,%bl                        <---------  case 0:  $bl保存的是第二个字符 8048be2:81 7d fc 09 03 00 00 cmpl   $0x309,-0x4(%ebp)      <--------   ($ebp)-0x4 中保存第三个数字 8048be9:0f 84 a0 00 00 00    je     8048c8f <phase_3+0xf7> 8048bef:e8 08 09 00 00       call   80494fc <explode_bomb> 8048bf4:e9 96 00 00 00       jmp    8048c8f <phase_3+0xf7> 8048bf9:8d b4 26 00 00 00 00 lea    0x0(%esi,%eiz,1),%esi 8048c00:b3 62                mov    $0x62,%bl                          <--------  case 1: 8048c02:81 7d fc d6 00 00 00 cmpl   $0xd6,-0x4(%ebp) 8048c09:0f 84 80 00 00 00    je     8048c8f <phase_3+0xf7> 8048c0f:e8 e8 08 00 00       call   80494fc <explode_bomb> 8048c14:eb 79                jmp    8048c8f <phase_3+0xf7> 8048c16:b3 62                mov    $0x62,%bl                           <------  case 2: 8048c18:81 7d fc f3 02 00 00 cmpl   $0x2f3,-0x4(%ebp) 8048c1f:74 6e                je     8048c8f <phase_3+0xf7> 8048c21:e8 d6 08 00 00       call   80494fc <explode_bomb> 8048c26:eb 67                jmp    8048c8f <phase_3+0xf7> 8048c28:b3 6b                mov    $0x6b,%bl                           <------ case 3: 8048c2a:81 7d fc fb 00 00 00 cmpl   $0xfb,-0x4(%ebp) 8048c31:74 5c                je     8048c8f <phase_3+0xf7> 8048c33:e8 c4 08 00 00       call   80494fc <explode_bomb> 8048c38:eb 55                jmp    8048c8f <phase_3+0xf7> 8048c3a:8d b6 00 00 00 00    lea    0x0(%esi),%esi 8048c40:b3 6f                 mov    $0x6f,%bl                            <------- case 4: 8048c42:81 7d fc a0 00 00 00 cmpl   $0xa0,-0x4(%ebp) 8048c49:74 44                je     8048c8f <phase_3+0xf7> 8048c4b:e8 ac 08 00 00       call   80494fc <explode_bomb> 8048c50:eb 3d                jmp    8048c8f <phase_3+0xf7> 8048c52:b3 74                mov    $0x74,%bl                           <------- case 5: 8048c54:81 7d fc ca 01 00 00 cmpl   $0x1ca,-0x4(%ebp) 8048c5b:74 32                je     8048c8f <phase_3+0xf7> 8048c5d:e8 9a 08 00 00       call   80494fc <explode_bomb> 8048c62:eb 2b                jmp    8048c8f <phase_3+0xf7> 8048c64:b3 76                mov    $0x76,%bl                           <------- case 6: 8048c66:81 7d fc 0c 03 00 00 cmpl   $0x30c,-0x4(%ebp) 8048c6d:74 20                je     8048c8f <phase_3+0xf7> 8048c6f:e8 88 08 00 00       call   80494fc <explode_bomb> 8048c74:eb 19                jmp    8048c8f <phase_3+0xf7> 8048c76:b3 62                mov    $0x62,%bl                           <------- case 7: 8048c78:81 7d fc 0c 02 00 00 cmpl   $0x20c,-0x4(%ebp) 8048c7f:74 0e                je     8048c8f <phase_3+0xf7> 8048c81:e8 76 08 00 00       call   80494fc <explode_bomb> 8048c86:eb 07                jmp    8048c8f <phase_3+0xf7> 8048c88:b3 78                mov    $0x78,%bl 8048c8a:e8 6d 08 00 00       call   80494fc <explode_bomb> 8048c8f:3a 5d fb             cmp    -0x5(%ebp),%bl 8048c92:74 05                je     8048c99 <phase_3+0x101> 8048c94:e8 63 08 00 00       call   80494fc <explode_bomb> 8048c99:8b 5d e8             mov    -0x18(%ebp),%ebx 8048c9c:89 ec                mov    %ebp,%esp 8048c9e:5d                     pop    %ebp 8048c9f:c3                     ret   
jmp  *0x80497e8(,%eax,4) 这句对应为switch 语句,指针的值为 0x80497e8+4*$eax, %eax中保存的是第一个数字,  0-7, $bl 第二个字符, 
($ebp)-0x4 第三个数字.   这一关主要考察的是switch语句.
0-7所有的情况.
solution:
0 q 777
1 b 214
2 b 755
3 k 251
4 o 160
5 t 458
6 v 780
7 b 524

  • phase_4
08048ca0 <func4>: 8048ca0:55                      push   %ebp 8048ca1:89 e5                mov    %esp,%ebp 8048ca3:83 ec 10             sub    $0x10,%esp 8048ca6:56                     push   %esi 8048ca7:53                     push   %ebx 8048ca8:8b 5d 08             mov    0x8(%ebp),%ebx           <---------- 得到递归函数参数var 8048cab:83 fb 01             cmp    $0x1,%ebx                    <----------  递归结束状态,var<= 1 8048cae:7e 20                jle    8048cd0 <func4+0x30>     <---------- goto done 8048cb0:83 c4 f4             add    $0xfffffff4,%esp 8048cb3:8d 43 ff             lea    -0x1(%ebx),%eax             <--------- result =  var - 1 8048cb6:50                     push   %eax                             <--------- 将var - 1作为下一次递归的参数 8048cb7:e8 e4 ff ff ff       call   8048ca0 <func4> 8048cbc:89 c6                mov    %eax,%esi                     <----------  %esi中保存返回值 8048cbe:83 c4 f4             add    $0xfffffff4,%esp 8048cc1:8d 43 fe             lea    -0x2(%ebx),%eax              <--------- var - 2 8048cc4:50                     push   %eax                              <--------- 再将var - 2 作为参数递归调用 8048cc5:e8 d6 ff ff ff       call   8048ca0 <func4> 8048cca:01 f0                  add    %esi,%eax                       <--------- result = func4(var-1) + func4(var-2) 8048ccc:eb 07                jmp    8048cd5 <func4+0x35>      <-------- goto over 8048cce:89 f6                  mov    %esi,%esi
                                                .done 8048cd0:b8 01 00 00 00       mov    $0x1,%eax                       <--------- return 1
                                                .over 8048cd5:8d 65 e8             lea    -0x18(%ebp),%esp 8048cd8:5b                     pop    %ebx 8048cd9:5e                     pop    %esi 8048cda:89 ec                mov    %ebp,%esp 8048cdc:5d                     pop    %ebp 8048cdd:c3                     ret     8048cde:89 f6                  mov    %esi,%esi08048ce0 <phase_4>: 8048ce0:55                     push   %ebp 8048ce1:89 e5                mov    %esp,%ebp 8048ce3:83 ec 18             sub    $0x18,%esp 8048ce6:8b 55 08             mov    0x8(%ebp),%edx             <------ get &phase_4 8048ce9:83 c4 fc             add    $0xfffffffc,%esp                 <----- esp - 4 8048cec:8d 45 fc             lea    -0x4(%ebp),%eax 8048cef:50                     push   %eax 8048cf0:68 08 98 04 08       push   $0x8049808                          <--------- 指向 "%d" 8048cf5:52                     push   %edx 8048cf6:e8 65 fb ff ff       call   8048860 <sscanf@plt> 8048cfb:83 c4 10             add    $0x10,%esp 8048cfe:83 f8 01             cmp    $0x1,%eax                           <----- 返回值为读入的参数个数, 应该读入一个整数, 可知 8048d01:75 06                jne    8048d09 <phase_4+0x29> 8048d03:83 7d fc 00          cmpl   $0x0,-0x4(%ebp)                   <-------- ($ebp)-4 指向读入的整数 8048d07:7f 05                  jg     8048d0e <phase_4+0x2e>        <-------  读入的数要大于0 8048d09:e8 ee 07 00 00       call   80494fc <explode_bomb> 8048d0e:83 c4 f4             add    $0xfffffff4,%esp ----- esp - 12 8048d11:8b 45 fc             mov    -0x4(%ebp),%eax  -               <-------- get input num  8048d14:50                     push   %eax 8048d15:e8 86 ff ff ff       call   8048ca0 <func4> 8048d1a:83 c4 10             add    $0x10,%esp 8048d1d:83 f8 37             cmp    $0x37,%eax                          <------- func4的返回值应该为0x37(55) 8048d20:74 05                je     8048d27 <phase_4+0x47> 8048d22:e8 d5 07 00 00       call   80494fc <explode_bomb> 8048d27:89 ec                mov    %ebp,%esp 8048d29:5d                     pop    %ebp 8048d2b:90                     nop
这一关主要考察递归. 
分析可知递归函数func4的返回值应该等于0x37 (55)
int func4(int num)
{
if (num <= 1) {
return 1;
} else {
return func(num-1)+func(num-2);
}
}
求得num = 9
solution: 9


  • phase_5
08048d2c <phase_5>: 8048d2c:55                     push   %ebp 8048d2d:89 e5                mov    %esp,%ebp 8048d2f:83 ec 10             sub    $0x10,%esp 8048d32:56                      push   %esi 8048d33:53                     push   %ebx 8048d34:8b 5d 08             mov    0x8(%ebp),%ebx                  <------ get &phase_5 8048d37:83 c4 f4             add    $0xfffffff4,%esp                    <----- esp - 12 8048d3a:53                     push   %ebx 8048d3b:e8 d8 02 00 00       call   8049018 <string_length> 8048d40:83 c4 10             add    $0x10,%esp 8048d43:83 f8 06             cmp    $0x6,%eax                           <----- 说明输入的字符串长度为6 8048d46:74 05                je     8048d4d <phfase_5+0x21> 8048d48:e8 af 07 00 00       call   80494fc <explode_bomb> 8048d4d:31 d2                xor    %edx,%edx   ---- %edx = 0 8048d4f:8d 4d f8             lea    -0x8(%ebp),%ecx 8048d52:be 20 b2 04 08       mov    $0x804b220,%esi                  <----这是一张table的起始地址 8048d57:8a 04 1a             mov    (%edx,%ebx,1),%al                <---- loop  %al = (%edx+%ebx),遍历6个字符 8048d5a:24 0f                  and    $0xf,%al                                 <----- %al = %al & 0xf, 将字符高4位置0 8048d5c:0f be c0             movsbl %al,%eax                             <------ 将al扩展为eax, 除低4位,高位都是0, 即只保留了字符的低4位的值 8048d5f:8a 04 30             mov    (%eax,%esi,1),%al                  <----- %al = (字符低4位 + %esi), 在table中查找 8048d62:88 04 0a             mov    %al,(%edx,%ecx,1)                  <-----将查找到的table中的字符放到(%ebp-8)为起始的位置 8048d65:42                     inc    %edx 8048d66:83 fa 05             cmp    $0x5,%edx                              <------- %edx : 5 8048d69:7e ec                jle    8048d57 <phase_5+0x2b>           <------- if <= goto loop 8048d6b:c6 45 fe 00          movb   $0x0,-0x2(%ebp)                      <----- 字符存在(%ebp-8)-(%ebp-1)中,(%ebp-2)存放'\0' 8048d6f:83 c4 f8             add    $0xfffffff8,%esp 8048d72:68 0b 98 04 08       push   $0x804980b                             <---------- 字符串 "giants"起始地址 8048d77:8d 45 f8             lea    -0x8(%ebp),%eax                       <-----  要比较的字符串起始地址 8048d7a:50                     push   %eax 8048d7b:e8 b0 02 00 00       call   8049030 <strings_not_equal> 8048d80:83 c4 10             add    $0x10,%esp 8048d83:85 c0                test   %eax,%eax 8048d85:74 05                je     8048d8c <phase_5+0x60>            <---- strings_not_equal返回值要为0 8048d87:e8 70 07 00 00       call   80494fc <explode_bomb> 8048d8c:8d 65 e8             lea    -0x18(%ebp),%esp 8048d8f:5b                     pop    %ebx 8048d90:5e                     pop    %esi 8048d91:89 ec                mov    %ebp,%esp 8048d93:5d                     pop    %ebp 8048d94:c3                     ret     8048d95:8d 76 00             lea    0x0(%esi),%esi
这一关考察指针. 分析知程序设置了一个table, 地址 0x804b220, 表中有16个字符, "isrveawhobpnutfg", 输入的字符串为6个字符,6个字符的低4位值对应为偏移量(0-f),
由偏移从表中得到新的字符串应该为"giants","giants"存放位置为0x804980b.
则得到偏移量应该为 f 0 5 b d 1
则字符串有多种情况,只要满足低4位为 f 0 5 b d 1 的顺序就可以
solution;
如: OPUKMQ
/ %+-Q

  • phase_6
8048d98 <phase_6>: 8048d98:55                     push   %ebp 8048d99:89 e5                mov    %esp,%ebp 8048d9b:83 ec 4c             sub    $0x4c,%esp 8048d9e:57                     push   %edi 8048d9f:56                     push   %esi 8048da0:53                     push   %ebx 8048da1:8b 55 08             mov    0x8(%ebp),%edx   ----------- get &phase_6 8048da4:c7 45 cc 6c b2 04 08 movl   $0x804b26c,-0x34(%ebp) 8048dab:83 c4 f8             add    $0xfffffff8,%esp ------------- esp - 8 8048dae:8d 45 e8             lea    -0x18(%ebp),%eax -----------  8048db1:50                     push   %eax    ----------- **($eax) = "GRADE_BOMB" 8048db2:52                     push   %edx    ----------- &phase_6 8048db3:e8 20 02 00 00       call   8048fd8 <read_six_numbers>          <--- 调用read_six_numbers之后,6个数(a0-a5)将依次放在 $ebp-24 - $ebp-4 的位置 8048db8:31 ff                  xor    %edi,%edi  <---- set $edi = 0 8048dba:83 c4 10             add    $0x10,%esp 8048dbd:8d 76 00             lea    0x0(%esi),%esi   <----此时 $esi == 0 8048dc0:8d 45 e8             lea    -0x18(%ebp),%eax <-----.loop *($eax) = a[0]  8048dc3:8b 04 b8             mov    (%eax,%edi,4),%eax <---- get a[$edi] 8048dc6:48                     dec    %eax   <--- get a[$edi] - 1 8048dc7:83 f8 05             cmp    $0x5,%eax    <------ (a[$edi]-1) : 5 , 无符号数比较 8048dca:76 05                jbe    8048dd1 <phase_6+0x39> ----------- if <=, goto .next, 注意是无符号比较,每个数都要小于或等于6且大于0, 不能等于0 8048dcc:e8 2b 07 00 00       call   80494fc <explode_bomb> 8048dd1:8d 5f 01             lea    0x1(%edi),%ebx <--- .next, get 数的序号数,(1-6) 8048dd4:83 fb 05             cmp    $0x5,%ebx 8048dd7:7f 23                jg     8048dfc <phase_6+0x64>       <--- if >, goto .next2 8048dd9:8d 04 bd 00 00 00 00 lea    0x0(,%edi,4),%eax <----  get 4*$edi 8048de0:89 45 c8             mov    %eax,-0x38(%ebp)      <----- set ($ebp-56) = 4*$edi 8048de3:8d 75 e8             lea    -0x18(%ebp),%esi          <----set $esi = &a[0] 8048de6:8b 55 c8             mov    -0x38(%ebp),%edx         <---- set $edx = 4*$edi ----- .next3 8048de9:8b 04 32             mov    (%edx,%esi,1),%eax       <--- set $eax = (4*$edi+&a[0]) = a[$edi] 8048dec:3b 04 9e             cmp    (%esi,%ebx,4),%eax        <----  a[$edi] : a[$ebx] 8048def:75 05                jne    8048df6 <phase_6+0x5e>   <--- if !=, goto .right 8048df1:e8 06 07 00 00       call   80494fc <explode_bomb> 8048df6:43                   inc    %ebx  -------.right 8048df7:83 fb 05             cmp    $0x5,%ebx 8048dfa:7e ea                jle    8048de6 <phase_6+0x4e>    <---- if <=, goto .next3 8048dfc:47                   inc    %edi  -------------- .next2 8048dfd:83 ff 05             cmp    $0x5,%edi  ---------- %edi : 5 8048e00:7e be                jle    8048dc0 <phase_6+0x28>     <------ if <=, goto .loop -------------------以上是判断任意两个数是否相等-------------------------------------------------- 8048e02:31 ff                xor    %edi,%edi 8048e04:8d 4d e8             lea    -0x18(%ebp),%ecx       <---- set $ecx = &a[0] 8048e07:8d 45 d0             lea    -0x30(%ebp),%eax       <---- get 0xffffd678,  8048e0a:89 45 c4             mov    %eax,-0x3c(%ebp)      <------ set ($ebp-0x3c) = 0xffffd678,  8048e0d:8d 76 00             lea    0x0(%esi),%esi             <--- $esi == 0xffffd60 .loop2 8048e10:8b 75 cc             mov    -0x34(%ebp),%esi         <---- .loop2  set $esi = 0x804b26c 8048e13:bb 01 00 00 00       mov    $0x1,%ebx                   <----- set $ebx = 1 8048e18:8d 04 bd 00 00 00 00 lea    0x0(,%edi,4),%eax   <----- set $eax = 4*$edi 8048e1f:89 c2                mov    %eax,%edx                   <----- set $edx = 4*$edi 8048e21:3b 1c 08             cmp    (%eax,%ecx,1),%ebx      <------ 1 : a[$edi] 8048e24:7d 12                jge    8048e38 <phase_6+0xa0>  <---- if >=, goto .next4 8048e26:8b 04 0a             mov    (%edx,%ecx,1),%eax         <---- set $eax = a[$edi] 8048e29:8d b4 26 00 00 00 00 lea    0x0(%esi,%eiz,1),%esi  .next5 8048e30:8b 76 08             mov    0x8(%esi),%esi   --- .next5 ------------ set $esi 8048e33:43                   inc    %ebx 8048e34:39 c3                cmp    %eax,%ebx  ----- $ebx : a[$edi] 8048e36:7c f8                jl     8048e30 <phase_6+0x98>  --- if <, goto .next5 .next4 8048e38:8b 55 c4             mov    -0x3c(%ebp),%edx  ----- .next4 set %edx = 0xffffd678------- goto.next4 8048e3b:89 34 ba             mov    %esi,(%edx,%edi,4) --------- $edx  ---------,以上循环是用来根据输入的数字生成链表----------------  8048e3e:47                   inc    %edi 8048e3f:83 ff 05             cmp    $0x5,%edi 8048e42:7e cc                jle    8048e10 <phase_6+0x78>  if <=, goto .loop2 8048e44:8b 75 d0             mov    -0x30(%ebp),%esi               <-------- set $esi = 第一个指针的值 p[0] 8048e47:89 75 cc             mov    %esi,-0x34(%ebp)               <-------- $epb-0x34中保存 p[0] 8048e4a:bf 01 00 00 00       mov    $0x1,%edi                           <---------- set $edi=1 8048e4f:8d 55 d0             lea    -0x30(%ebp),%edx                  <------ set $edx = $ebp-0x30, 则 $edx = &p[0] .next6 8048e52:8b 04 ba             mov    (%edx,%edi,4),%eax              <---- .next6 , set $eax = p[$edi] 8048e55:89 46 08             mov    %eax,0x8(%esi)                    <------ set      = p[$edi] 8048e58:89 c6                mov    %eax,%esi                            <--------- set $esi = p[$edi] 8048e5a:47                      inc    %edi 8048e5b:83 ff 05             cmp    $0x5,%edi 8048e5e:7e f2                  jle    8048e52 <phase_6+0xba>  if <=, goto .next6 8048e60:c7 46 08 00 00 00 00 movl   $0x0,0x8(%esi) 8048e67:8b 75 cc             mov    -0x34(%ebp),%esi                     <------- set $esi = p[0] 8048e6a:31 ff                  xor    %edi,%edi                                 <----------- set $edi = 0 8048e6c:8d 74 26 00          lea    0x0(%esi,%eiz,1),%esi .next8                               <-----------------$esi 为第一个数字对应的指针p[0] 8048e70:8b 56 08             mov    0x8(%esi),%edx   <---- .next8, set %edx = p[i+1] 8048e73:8b 06                mov    (%esi),%eax          <---------------get  *p[i] 8048e75:3b 02                cmp    (%edx),%eax  *p[i] : *p[i+1] 8048e77:7d 05                jge    8048e7e <phase_6+0xe6>   <---- if >=, goto .next7 8048e79:e8 7e 06 00 00       call   80494fc <explode_bomb> .nxxt7 8048e7e:8b 76 08             mov    0x8(%esi),%esi                    <----- .next7, set %esi指向下一个数字 8048e81:47                     inc    %edi 8048e82:83 ff 04             cmp    $0x4,%edi 8048e85:7e e9                jle    8048e70 <phase_6+0xd8>         <---- if <=, goto .next8 8048e87:8d 65 a8             lea    -0x58(%ebp),%esp 8048e8a:5b                     pop    %ebx 8048e8b:5e                     pop    %esi 8048e8c:5f                       pop    %edi 8048e8d:89 ec                mov    %ebp,%esp 8048e8f:5d                     pop    %ebp 8048e90:c3                     ret     8048e91:8d 76 00             lea    0x0(%esi),%esi
这个感觉难度最大,一堆循环,感觉人都绕晕了, 搞了好久才发现原来这是在对一个链表进行操作, 理清了这一点就好多了.
0x804b26c是一个已经有了的链表的第一个结点的位置,其数据域为0x000000fd, 
程序要做的是根据输入的6个不同大小的数字(1-6)来重新排列这个链表使其按降序排列.
$ebp-0x3c保存的是初始链表的头指针,链表的数据元素是个结构体,
struct LElement {
unsigned int data;
struct LElement *next;
}
%esi 寄存器是用来传递指针的,把握住它的值这个程序就好读一些.
%eiz 是个伪寄存器,其值永远为0.
输入的数字与对应的指针如下:
 1 -> 0x804b26c -> data = fd
 2 -> 0x804b260 -> data = 2d5
 3 -> 0x804b254 -> data = 12d
 4 -> 0x804b248 -> data = 3e5
 5 -> 0x804b23c -> data = d4
 6 -> 0x804b230 ->data = 1b0

按输入的顺序生成链表
6个指针:
0xffffd678: p[0]
0xffffd67c: p[1]
0xffffd680: p[2]
0xffffd684: p[3]
0xffffd688: p[4]
0xffffd68c: p[5]
由最后的循环来看,是判断链表是否为降序排列
*p[0] > *p[1] > *p[2] ... > *p[5]
则有    3e5 > 2d5 > 1b0 > 12d > fd > d4
对应数字顺序为: 4 2 6 3 1 5

solution: 4 2 6 3 1 5


  • secret_phase
8048ee8 <secret_phase>: 8048ee8:55                   push   %ebp 8048ee9:89 e5                mov    %esp,%ebp 8048eeb:83 ec 14             sub    $0x14,%esp 8048eee:53                   push   %ebx 8048eef:e8 08 03 00 00       call   80491fc <read_line> 8048ef4:6a 00                push   $0x0 8048ef6:6a 0a                push   $0xa 8048ef8:6a 00                push   $0x0 8048efa:50                   push   %eax ----------- get &secret_string 8048efb:e8 f0 f8 ff ff       call   80487f0 <__strtol_internal@plt> ------- 将字符串转换成整数value 8048f00:83 c4 10             add    $0x10,%esp 8048f03:89 c3                mov    %eax,%ebx 8048f05:8d 43 ff             lea    -0x1(%ebx),%eax   ----- $eax = value-1 8048f08:3d e8 03 00 00       cmp    $0x3e8,%eax   ---------- value-1 : 0x3e8 (1000) 8048f0d:76 05                jbe    8048f14 <secret_phase+0x2c>  ----- if <=,  无符号比较, OK 8048f0f:e8 e8 05 00 00       call   80494fc <explode_bomb> 8048f14:83 c4 f8             add    $0xfffffff8,%esp  ---------- esp-8 8048f17:53                     push   %ebx   ------- push value 8048f18:68 20 b3 04 08       push   $0x804b320 -------- push 0x24 8048f1d:e8 72 ff ff ff       call   8048e94 <fun7>  ------ 调用fun7 8048f22:83 c4 10             add    $0x10,%esp 8048f25:83 f8 07             cmp    $0x7,%eax    ----------- 比较返回值 : 0x7 8048f28:74 05                je     8048f2f <secret_phase+0x47>  ---------if ==, OK 8048f2a:e8 cd 05 00 00       call   80494fc <explode_bomb> 8048f2f:83 c4 f4             add    $0xfffffff4,%esp 8048f32:68 20 98 04 08       push   $0x8049820 8048f37:e8 d4 f8 ff ff       call   8048810 <printf@plt> 8048f3c:e8 eb 05 00 00       call   804952c <phase_defused> 8048f41:8b 5d e8             mov    -0x18(%ebp),%ebx 8048f44:89 ec                mov    %ebp,%esp 8048f46:5d                     pop    %ebp 8048f47:c3                     ret    

这个比较难, 因为按正常情况输入了正确的6个字符串之后, 这个程序就结束了. 你没有办法输入第7个字符串, 要想输入第7个secret字符串, 还得好好研究一下,
搜索整个汇编代码, 发现只在phase_defuse函数中调用过secret_phase函数, 所以显然我们就要先分析这个函数.
0804952c <phase_defused>: 804952c:55                     push   %ebp 804952d:89 e5                mov    %esp,%ebp 804952f:83 ec 64             sub    $0x64,%esp 8049532:53                     push   %ebx 8049533:83 3d 80 b4 04 08 06 cmpl   $0x6,0x804b480         <------ 0x804b480的值每执行一次phase_defused函数就会增1 804953a:75 63                jne    804959f <phase_defused+0x73> 804953c:8d 5d b0             lea    -0x50(%ebp),%ebx   804953f:53                     push   %ebx 8049540:8d 45 ac             lea    -0x54(%ebp),%eax 8049543:50                     push   %eax 8049544:68 03 9d 04 08       push   $0x8049d03             <---------- "%d %s" 8049549:68 70 b7 04 08       push   $0x804b770              <--------- 应该是要读取的字符串, 这个字符串应该包含一个数字和一个字符串 804954e:e8 0d f3 ff ff       call   8048860 <sscanf@plt> 8049553:83 c4 10             add    $0x10,%esp 8049556:83 f8 02             cmp    $0x2,%eax         <------- sscanf 要读入两个参数 8049559:75 37                jne    8049592 <phase_defused+0x66> 804955b:83 c4 f8             add    $0xfffffff8,%esp 804955e:68 09 9d 04 08       push   $0x8049d09        <-------- "austinpowers"首地址 8049563:53                     push   %ebx 8049564:e8 c7 fa ff ff       call   8049030 <strings_not_equal> 8049569:83 c4 10             add    $0x10,%esp 804956c:85 c0                test   %eax,%eax 804956e:75 22                jne    8049592 <phase_defused+0x66> 8049570:83 c4 f4             add    $0xfffffff4,%esp 8049573:68 20 9d 04 08       push   $0x8049d20 8049578:e8 93 f2 ff ff       call   8048810 <printf@plt> 804957d:83 c4 f4             add    $0xfffffff4,%esp 8049580:68 60 9d 04 08       push   $0x8049d60 8049585:e8 86 f2 ff ff       call   8048810 <printf@plt> 804958a:83 c4 20             add    $0x20,%esp 804958d:e8 56 f9 ff ff       call   8048ee8 <secret_phase> 8049592:83 c4 f4             add    $0xfffffff4,%esp 8049595:68 a0 9d 04 08       push   $0x8049da0 804959a:e8 71 f2 ff ff       call   8048810 <printf@plt> 804959f:8b 5d 98             mov    -0x68(%ebp),%ebx 80495a2:89 ec                mov    %ebp,%esp 80495a4:5d                     pop    %ebp 80495a5:c3                     ret    
在phase_defused函数中有cmpl $0x6, 0x804b480 语句,搜索0x804b480发现这个内存的值在每读入一条字符串后就会加1,
则显然secret_phase是在第6条字符串之后输入.又发现cmp $0x2, %eax,语句, 此时的%eax是sscanf读取的参数个数,
sscanf读取的参数格式保存在之前push的0x8049d03位置处,为"%d %s", 待读取的参数存放在最后push的0x804b770内存处,
问题就在这里了,用x/s 0x804b770看时发现这个内存位置的字符串为"9",明显不对,从后面的调用
strings_not_equal来看,还要读入的字符保存在0x8049d09中,应该为"austinpowers".下面
的问题就是要把这个字符串存放在0x804b774位置处,非常tricky的就是当我们逐个断点观察每个
read_line函数的时候,发现读入第4条字符串9的位置正好是0x804b770,于是我们要在第4条字符串9后
加上字符串"austinpowers",这样就可以正确执行到secret_phase函数调用位置处了.真是辛苦啊!

下面是解析secret_phase, 不难分析到函数是先将字符串转换成整数,并且其值<=1001再调用func7函数,
根据后面的分析,返回值要为0x07, 则可得参数value的值为1001.
typedef struct BiTree {
int data;
BiTree *lchild;
BiTree *rchild;
}BiTree;


/* p指向树的头结点, 树结构如下 */
36
8 50
6 22 45107
20 35 40 47 991001
int func7(int value, BiTree *p)
{
if (NULL == p) {
return -1;
} else if (value < p->data) {
return 2 * func7(value, p->lchild);
} else {
if (value == p->data) {
return 0;
} else {
return 2 * func(value, p->rchild) + 1;
}
}
}
08048e94 <fun7>: 8048e94:55                     push   %ebp 8048e95:89 e5                mov    %esp,%ebp 8048e97:83 ec 08             sub    $0x8,%esp 8048e9a:8b 55 08             mov    0x8(%ebp),%edx  <---- get 参数 setret_phase中传的参数地址,指向 36 8048e9d:8b 45 0c             mov    0xc(%ebp),%eax  <----- get 参数 setret_phase中传的参数 value 8048ea0:85 d2                test   %edx,%edx 8048ea2:75 0c                jne    8048eb0 <fun7+0x1c>     <-------- if $edx != 0, goto .next 8048ea4:b8 ff ff ff ff         mov    $0xffffffff,%eax      <----- set $eax = -1 8048ea9:eb 37                jmp    8048ee2 <fun7+0x4e>   <------ goto .done 8048eab:90                     nop 8048eac:8d 74 26 00          lea    0x0(%esi,%eiz,1),%esi       .next 8048eb0:3b 02                cmp    (%edx),%eax     <--- $eax : *$edx 8048eb2:7d 11                jge    8048ec5 <fun7+0x31>  <----- if >=, goto .next2 8048eb4:83 c4 f8             add    $0xfffffff8,%esp  <----- esp - 8 8048eb7:50                     push   %eax 8048eb8:8b 42 04             mov    0x4(%edx),%eax 8048ebb:50                     push   %eax 8048ebc:e8 d3 ff ff ff       call   8048e94 <fun7> 8048ec1:01 c0                add    %eax,%eax 8048ec3:eb 1d                jmp    8048ee2 <fun7+0x4e>   <---- goto .done   .next2 8048ec5:3b 02                cmp    (%edx),%eax     <----- $eax : *$edx 8048ec7:74 17                je     8048ee0 <fun7+0x4c>    <---- if ==, goto .next3 8048ec9:83 c4 f8             add    $0xfffffff8,%esp 8048ecc:50                     push   %eax 8048ecd:8b 42 08             mov    0x8(%edx),%eax 8048ed0:50                     push   %eax 8048ed1:e8 be ff ff ff       call   8048e94 <fun7> 8048ed6:01 c0                add    %eax,%eax 8048ed8:40                     inc    %eax 8048ed9:eb 07                jmp    8048ee2 <fun7+0x4e>  ---- goto .done 8048edb:90                     nop 8048edc:8d 74 26 00          lea    0x0(%esi,%eiz,1),%esi .next3 8048ee0:31 c0                xor    %eax,%eax ---------- set $eax = 0 .done 8048ee2:89 ec                mov    %ebp,%esp 8048ee4:5d                     pop    %ebp 8048ee5:c3                     ret     8048ee6:89 f6                  mov    %esi,%esi

solution: 1001


到这里bomb都解除了. 得到的7个字符串(包括隐藏字符串)应该为:
Public speaking is very easy.
1 2 6 24 120 720
7 b 524
9 austinpowers
/ %+-Q
4 2 6 3 1 5
1001


  • 总结: 
做了这个实验之后对GDB调试工具的使用熟悉了很多, 发现它确实很好很强大, 这里总结几个常用的命令.
si 单指令执行
display/i $pc  执行si命令时打印汇编代码
空行  重复上一条命令
b 断点,  b *0x8048a45 在指定内存位置停止, 也可以设在函数上, 按TAB能自动补全
n 单语句执行
c 继续执行
finish 结束当前函数
bt 查看函数堆栈
shell 执行shell命令
x/xw  Addr: 查看4个字节的内存, 16进制显示
x/s Addr: 查看以该地址开头的字符串
p/x $eax: 查看寄存器

做的过程中使用vim查看汇编代码, 很方便, 设置书签的功能和CTRL+O, CTRL+I在位置间跳转的功能常用到, 很不错.
做的过程虽然辛苦, 但收获也多多, 加油!

原创粉丝点击