CSAPP: bomb lab

有兴趣做这个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 :
 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   <------- 调用字符串比较函数, 用栈来传递参数
 8048b37:	83 c4 10             	add    $0x10,%esp
 8048b3a:	85 c0                	test   %eax,%eax                           <---------- 由返回值判断标准字符串与你输入的字符串是否相等
 8048b3c:	74 05                	je     8048b43 
 8048b3e:	e8 b9 09 00 00       	call   80494fc       <-------- 爆炸函数
 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 :
 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    <----- 调用函数, 很明显, 从函数名就可以看出来这是读取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           <--------------  if == , goto .next, 说明第一个数一定是1
 8048b69:	e8 8e 09 00 00       	call   80494fc 
 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         <--------------  if ==,  goto next2
 8048b83:	e8 74 09 00 00       	call   80494fc 
                                               .next2
 8048b88:	43                     	inc    %ebx
 8048b89:	83 fb 05             	cmp    $0x5,%ebx                                <------------- ebx : 5
 8048b8c:	7e e8                	jle    8048b76            <-------------  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 :
 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 
 8048bbc:	83 c4 20             	add    $0x20,%esp                  <------ esp+32
 8048bbf:	83 f8 02             	cmp    $0x2,%eax                        <----  %eax是函数sscanf返回值, 是读取的参数个数
 8048bc2:	7f 05                  	jg     8048bc9 
 8048bc4:	e8 33 09 00 00       	call   80494fc 
 8048bc9:	83 7d f4 07          	cmpl   $0x7,-0xc(%ebp)                <----- 第一个数字a[0]与7比较, 大于7就跪了, 所以第一个数可以为0-7
 8048bcd:	0f 87 b5 00 00 00    	ja     8048c88 
 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 
 8048bef:	e8 08 09 00 00       	call   80494fc 
 8048bf4:	e9 96 00 00 00       	jmp    8048c8f 
 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 
 8048c0f:	e8 e8 08 00 00       	call   80494fc 
 8048c14:	eb 79                	jmp    8048c8f 
 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 
 8048c21:	e8 d6 08 00 00       	call   80494fc 
 8048c26:	eb 67                	jmp    8048c8f 
 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 
 8048c33:	e8 c4 08 00 00       	call   80494fc 
 8048c38:	eb 55                	jmp    8048c8f 
 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 
 8048c4b:	e8 ac 08 00 00       	call   80494fc 
 8048c50:	eb 3d                	jmp    8048c8f 
 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 
 8048c5d:	e8 9a 08 00 00       	call   80494fc 
 8048c62:	eb 2b                	jmp    8048c8f 
 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 
 8048c6f:	e8 88 08 00 00       	call   80494fc 
 8048c74:	eb 19                	jmp    8048c8f 
 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 
 8048c81:	e8 76 08 00 00       	call   80494fc 
 8048c86:	eb 07                	jmp    8048c8f 
 8048c88:	b3 78                	mov    $0x78,%bl
 8048c8a:	e8 6d 08 00 00       	call   80494fc 
 8048c8f:	3a 5d fb             	cmp    -0x5(%ebp),%bl
 8048c92:	74 05                	je     8048c99 
 8048c94:	e8 63 08 00 00       	call   80494fc 
 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 :
 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      <---------- 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 
 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 
 8048cca:	01 f0                  	add    %esi,%eax                       <--------- result = func4(var-1) + func4(var-2)
 8048ccc:	eb 07                	jmp    8048cd5       <-------- 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,%esi

08048ce0 :
 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 
 8048cfb:	83 c4 10             	add    $0x10,%esp
 8048cfe:	83 f8 01             	cmp    $0x1,%eax                           <----- 返回值为读入的参数个数, 应该读入一个整数, 可知
 8048d01:	75 06                	jne    8048d09 
 8048d03:	83 7d fc 00          	cmpl   $0x0,-0x4(%ebp)                   <-------- ($ebp)-4 指向读入的整数
 8048d07:	7f 05                  	jg     8048d0e         <-------  读入的数要大于0
 8048d09:	e8 ee 07 00 00       	call   80494fc 
 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 
 8048d1a:	83 c4 10             	add    $0x10,%esp
 8048d1d:	83 f8 37             	cmp    $0x37,%eax                          <------- func4的返回值应该为0x37(55)
 8048d20:	74 05                	je     8048d27 
 8048d22:	e8 d5 07 00 00       	call   80494fc 
 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 :
 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 
 8048d40:	83 c4 10             	add    $0x10,%esp
 8048d43:	83 f8 06             	cmp    $0x6,%eax                           <----- 说明输入的字符串长度为6
 8048d46:	74 05                	je     8048d4d 
 8048d48:	e8 af 07 00 00       	call   80494fc 
 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            <------- 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 
 8048d80:	83 c4 10             	add    $0x10,%esp
 8048d83:	85 c0                	test   %eax,%eax
 8048d85:	74 05                	je     8048d8c             <---- strings_not_equal返回值要为0
 8048d87:	e8 70 07 00 00       	call   80494fc 
 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 :
 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之后,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  ----------- if <=, goto .next, 注意是无符号比较,每个数都要小于或等于6且大于0, 不能等于0
 8048dcc:	e8 2b 07 00 00       	call   80494fc 
 8048dd1:	8d 5f 01             	lea    0x1(%edi),%ebx <--- .next, get 数的序号数,(1-6)
 8048dd4:	83 fb 05             	cmp    $0x5,%ebx
 8048dd7:	7f 23                	jg     8048dfc        <--- 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    <--- if !=, goto .right
 8048df1:	e8 06 07 00 00       	call   80494fc 
 8048df6:	43                   	inc    %ebx  -------.right
 8048df7:	83 fb 05             	cmp    $0x5,%ebx
 8048dfa:	7e ea                	jle    8048de6     <---- if <=, goto .next3
 8048dfc:	47                   	inc    %edi  -------------- .next2
 8048dfd:	83 ff 05             	cmp    $0x5,%edi  ---------- %edi : 5
 8048e00:	7e be                	jle    8048dc0      <------ 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   <---- 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   --- 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   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   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    <---- if >=, goto .next7
 8048e79:	e8 7e 06 00 00       	call   80494fc 
 					.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          <---- 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 :
 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 
 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   ----- if <=,  无符号比较, OK
 8048f0f:	e8 e8 05 00 00       	call   80494fc 
 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
 8048f22:	83 c4 10             	add    $0x10,%esp
 8048f25:	83 f8 07             	cmp    $0x7,%eax    ----------- 比较返回值 : 0x7
 8048f28:	74 05                	je     8048f2f   ---------if ==, OK
 8048f2a:	e8 cd 05 00 00       	call   80494fc 
 8048f2f:	83 c4 f4             	add    $0xfffffff4,%esp
 8048f32:	68 20 98 04 08       	push   $0x8049820
 8048f37:	e8 d4 f8 ff ff       	call   8048810 
 8048f3c:	e8 eb 05 00 00       	call   804952c 
 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 :
 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 
 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 
 8049553:	83 c4 10             	add    $0x10,%esp
 8049556:	83 f8 02             	cmp    $0x2,%eax         <------- sscanf 要读入两个参数
 8049559:	75 37                	jne    8049592 
 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 
 8049569:	83 c4 10             	add    $0x10,%esp
 804956c:	85 c0                	test   %eax,%eax
 804956e:	75 22                	jne    8049592 
 8049570:	83 c4 f4             	add    $0xfffffff4,%esp
 8049573:	68 20 9d 04 08       	push   $0x8049d20
 8049578:	e8 93 f2 ff ff       	call   8048810 
 804957d:	83 c4 f4             	add    $0xfffffff4,%esp
 8049580:	68 60 9d 04 08       	push   $0x8049d60
 8049585:	e8 86 f2 ff ff       	call   8048810 
 804958a:	83 c4 20             	add    $0x20,%esp
 804958d:	e8 56 f9 ff ff       	call   8048ee8 
 8049592:	83 c4 f4             	add    $0xfffffff4,%esp
 8049595:	68 a0 9d 04 08       	push   $0x8049da0
 804959a:	e8 71 f2 ff ff       	call   8048810 
 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 :
 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      <-------- if $edx != 0, goto .next
 8048ea4:	b8 ff ff ff ff         	mov    $0xffffffff,%eax      <----- set $eax = -1
 8048ea9:	eb 37                	jmp    8048ee2    <------ 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   <----- 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 
 8048ec1:	01 c0                	add    %eax,%eax
 8048ec3:	eb 1d                	jmp    8048ee2    <---- goto .done
 					  .next2
 8048ec5:	3b 02                	cmp    (%edx),%eax     <----- $eax : *$edx
 8048ec7:	74 17                	je     8048ee0     <---- 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 
 8048ed6:	01 c0                	add    %eax,%eax
 8048ed8:	40                     	inc    %eax
 8048ed9:	eb 07                	jmp    8048ee2   ---- 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在位置间跳转的功能常用到, 很不错.
做的过程虽然辛苦, 但收获也多多, 加油!

你可能感兴趣的:(CSAPP)