【csapp lab】lab2_bomblab

文章目录

    • 前言
    • 实验内容
      • phase_1
      • phase_2
      • phase_3
      • phase_4
      • phase_5
      • phase_6
      • secret_phase

前言

刚做了csapp lab2,记录一下。

我这里用的的系统环境是Ubuntu22.04,是64位系统,与用32位系统可能有所差异。

实验共包括七个阶段,每个阶段考察机器级语言程序的不同方面,难度递增

  • 阶段一:字符串比较
  • 阶段二:循环
  • 阶段三:条件/分支,含switch语句
  • 阶段四:递归调用和栈
  • 阶段五:指针
  • 阶段六:链表/指针/结构
  • 隐藏阶段:第四阶段之后附加特定字符串后出现

在实验过程中gdb调试起到了相当重要的作用,下面是我调试过程中频繁使用的几条指令,不熟悉gdb的小伙伴一定要熟悉一下:

  • r ans.txt - 开始运行,ans.txt为命令行参数,可以省略

  • c - 运行至下一个断点处

  • b \*addr - 在addr处的指令打断点

  • d num - 删除断点num

  • display /x %reg - 每次执行完指令时打印寄存器rgx的值

  • undisplay num - 删除监视变量num

  • ni - 执行下一条指令

  • x/nws addr - x是examine,表示进行内存检查;n表示显示n个数据项;w表示以8个字节为单位进行显示;s是以字符串的形式显示解释,可以替换成x,以十六进制的形式进行解释,可以替换成u,以十进制的形式进行解释

为了方便,我们创建一个ans.txt文件,每做出来一题就把答案写进去,这样就避免了一条一条输入答案。一题占一行,要注意最后要多一个空行,目的是最后一题的输入以 换行符结尾,保证是正确输入:

【csapp lab】lab2_bomblab_第1张图片

实验内容

phase_1

找到 phase_1 函数在 main 函数中被调用的位置:

 8048a6d:	c7 04 24 70 a0 04 08 	movl   $0x804a070,(%esp)
 8048a74:	e8 47 fd ff ff       	call   80487c0 
 8048a79:	e8 69 07 00 00       	call   80491e7 
 8048a7e:	89 04 24             	mov    %eax,(%esp)
 8048a81:	e8 aa 00 00 00       	call   8048b30 
 8048a86:	e8 5f 08 00 00       	call   80492ea 

在反汇编文件中继续查找 phase_1 的位置:

08048b30 :
 8048b30:	55                  	push   %ebp
 8048b31:	89 e5                	mov    %esp,%ebp
 8048b33:	83 ec 10             	sub    $0x10,%esp
 8048b36:	68 ec a0 04 08       	push   $0x804a0ec
 8048b3b:	ff 75 08             	push   0x8(%ebp)
 8048b3e:	e8 3f 05 00 00       	call   8049082 
 8048b43:	83 c4 10             	add    $0x10,%esp
 8048b46:	85 c0                	test   %eax,%eax
 8048b48:	74 05                	je     8048b4f 
 8048b4a:	e8 36 06 00 00        	call   8049185 
 8048b4f:	c9                  	leave  
 8048b50:	c3                  	ret    

$0x804a0ec: 数据区地址,也是 strings_not_equal 的第二个参数

0x8(%ebp): phase_1 的参数,也是 strings_not_equal 的第一个参数

main()函数的汇编代码中,可以进一步找到:

 8048a79:	e8 69 07 00 00       	call   80491e7 
 8048a7e:	89 04 24             	mov    %eax,(%esp)

%eax 里存储的是调用 read_line 函数的返回值,也是用户输入的字符串首地址

推测拆弹密码字符串的存储地址为 $0x804a0ec,因为调用 strings_not_equal前有语句:

8048b36: 68 ec a0 04 08 push $0x804a0ec

执行 gdb bomb 找到答案When a problem comes along, you must zip it!

image-20231119023040251

phase_2

查看phase_2的代码:

08048b51 :
 8048b51:	55                   	push   %ebp
 8048b52:	89 e5                	mov    %esp,%ebp
 8048b54:	53                   	push   %ebx
 8048b55:	83 ec 2c             	sub    $0x2c,%esp
 8048b58:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8048b5e:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048b61:	31 c0                	xor    %eax,%eax
 8048b63:	8d 45 dc             	lea    -0x24(%ebp),%eax
 8048b66:	50                   	push   %eax
 8048b67:	ff 75 08             	push   0x8(%ebp)
 8048b6a:	e8 3e 06 00 00       	call   80491ad 
 8048b6f:	83 c4 10             	add    $0x10,%esp
 8048b72:	83 7d dc 00          	cmpl   $0x0,-0x24(%ebp)
 8048b76:	79 05                	jns    8048b7d 
 8048b78:	e8 08 06 00 00       	call   8049185 
 8048b7d:	bb 01 00 00 00       	mov    $0x1,%ebx			  
 8048b82:	89 d8                	mov    %ebx,%eax			 
 8048b84:	03 44 9d d8          	add    -0x28(%ebp,%ebx,4),%eax
 8048b88:	39 44 9d dc          	cmp    %eax,-0x24(%ebp,%ebx,4)
 8048b8c:	74 05                	je     8048b93 
 8048b8e:	e8 f2 05 00 00       	call   8049185 
 8048b93:	83 c3 01             	add    $0x1,%ebx
 8048b96:	83 fb 06             	cmp    $0x6,%ebx
 8048b99:	75 e7                	jne    8048b82 
 8048b9b:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048b9e:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048ba5:	74 05                	je     8048bac 
 8048ba7:	e8 e4 fb ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048bac:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048baf:	c9                   	leave  
 8048bb0:	c3                   	ret  

关注到8048b6a: e8 3e 06 00 00 call 80491ad ,这题应该是要输入六个数字。

观察这部分汇编代码:

 8048b7d:	bb 01 00 00 00       	mov    $0x1,%ebx			  
 8048b82:	89 d8                	mov    %ebx,%eax			 
 8048b84:	03 44 9d d8          	add    -0x28(%ebp,%ebx,4),%eax
 8048b88:	39 44 9d dc          	cmp    %eax,-0x24(%ebp,%ebx,4)
 8048b8c:	74 05                	je     8048b93 
 8048b8e:	e8 f2 05 00 00       	call   8049185 
 8048b93:	83 c3 01             	add    $0x1,%ebx
 8048b96:	83 fb 06             	cmp    $0x6,%ebx
 8048b99:	75 e7                	jne    8048b82 

可以看出这就是一段循环,循环次数为5,每次循环是做一次比较,%ebx是循环计数器,每轮循环结束后 +1。循环内会进行一次比较,如果两个数不相等就会爆炸。那么关键就是待比较的两个数。

ebx 比较数1 比较数2
1 1 + ebp-0x28+1*4 ebp-0x24+1*4
2 2 + ebp-0x28+2*4 ebp-0x24+2*4
3 3 + ebp-0x28+3*4 ebp-0x24+3*4
4 4 + ebp-0x28+4*4 ebp-0x24+4*4
5 5 + ebp-0x28+5*4 ebp-0x24+5*4

经过观察和不断地调试发现比较数1是第一个输入的数+ebx,比较数2是第二个输入的数。

经过大量试错调试,最终得出这一题的答案:输入六个数,每两个数之间的差依次为1/2/3/4/5

所以这样得到本题的一组答案:1 2 4 7 11 16,当然答案不唯一,比如符合条件的2 3 5 8 12 17也是可以通 过的。

部分调试记录截图:

【csapp lab】lab2_bomblab_第2张图片

【csapp lab】lab2_bomblab_第3张图片

这题后面的比较循环一开始看到以6结尾,想当然地认为循环比较六次,这样就多出来一个输入的数,百思不得其解,在网上查阅相关教程发现题目也不一样。最终就逐步调试,发现只是循环了5次,这样就对上了。在调试过程中也找到了本题的答案。

phase_3

先把phase_3的代码贴出来:

08048bb1 :
 8048bb1:	55                   	push   %ebp
 8048bb2:	89 e5                	mov    %esp,%ebp
 8048bb4:	83 ec 24             	sub    $0x24,%esp
 8048bb7:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8048bbd:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048bc0:	31 c0                	xor    %eax,%eax
 8048bc2:	8d 45 f0             	lea    -0x10(%ebp),%eax
 8048bc5:	50                   	push   %eax
 8048bc6:	8d 45 eb             	lea    -0x15(%ebp),%eax
 8048bc9:	50                   	push   %eax
 8048bca:	8d 45 ec             	lea    -0x14(%ebp),%eax
 8048bcd:	50                   	push   %eax
 8048bce:	68 42 a1 04 08       	push   $0x804a142
 8048bd3:	ff 75 08             	push   0x8(%ebp)
 8048bd6:	e8 35 fc ff ff       	call   8048810 <__isoc99_sscanf@plt>
 8048bdb:	83 c4 20             	add    $0x20,%esp
 8048bde:	83 f8 02             	cmp    $0x2,%eax
 8048be1:	7f 05                	jg     8048be8 
 8048be3:	e8 9d 05 00 00       	call   8049185 
 8048be8:	83 7d ec 07          	cmpl   $0x7,-0x14(%ebp)
 8048bec:	0f 87 ef 00 00 00    	ja     8048ce1 
 8048bf2:	8b 45 ec             	mov    -0x14(%ebp),%eax
 8048bf5:	ff 24 85 54 a1 04 08 	jmp    *0x804a154(,%eax,4)
 8048bfc:	b8 78 00 00 00       	mov    $0x78,%eax
 8048c01:	81 7d f0 44 01 00 00 	cmpl   $0x144,-0x10(%ebp)
 8048c08:	0f 84 dd 00 00 00    	je     8048ceb 
 8048c0e:	e8 72 05 00 00       	call   8049185 
 8048c13:	b8 78 00 00 00       	mov    $0x78,%eax
 8048c18:	e9 ce 00 00 00       	jmp    8048ceb 
 8048c1d:	b8 69 00 00 00       	mov    $0x69,%eax
 8048c22:	81 7d f0 99 01 00 00 	cmpl   $0x199,-0x10(%ebp)
 8048c29:	0f 84 bc 00 00 00    	je     8048ceb 
 8048c2f:	e8 51 05 00 00       	call   8049185 
 8048c34:	b8 69 00 00 00       	mov    $0x69,%eax
 8048c39:	e9 ad 00 00 00       	jmp    8048ceb 
 8048c3e:	b8 74 00 00 00       	mov    $0x74,%eax
 8048c43:	81 7d f0 f6 02 00 00 	cmpl   $0x2f6,-0x10(%ebp)
 8048c4a:	0f 84 9b 00 00 00    	je     8048ceb 
 8048c50:	e8 30 05 00 00       	call   8049185 
 8048c55:	b8 74 00 00 00       	mov    $0x74,%eax
 8048c5a:	e9 8c 00 00 00       	jmp    8048ceb 
 8048c5f:	b8 68 00 00 00       	mov    $0x68,%eax
 8048c64:	81 7d f0 37 02 00 00 	cmpl   $0x237,-0x10(%ebp)
 8048c6b:	74 7e                	je     8048ceb 
 8048c6d:	e8 13 05 00 00       	call   8049185 
 8048c72:	b8 68 00 00 00       	mov    $0x68,%eax
 8048c77:	eb 72                	jmp    8048ceb 
 8048c79:	b8 79 00 00 00       	mov    $0x79,%eax
 8048c7e:	81 7d f0 1c 03 00 00 	cmpl   $0x31c,-0x10(%ebp)
 8048c85:	74 64                	je     8048ceb 
 8048c87:	e8 f9 04 00 00       	call   8049185 
 8048c8c:	b8 79 00 00 00       	mov    $0x79,%eax
 8048c91:	eb 58                	jmp    8048ceb 
 8048c93:	b8 62 00 00 00       	mov    $0x62,%eax
 8048c98:	81 7d f0 bd 01 00 00 	cmpl   $0x1bd,-0x10(%ebp)
 8048c9f:	74 4a                	je     8048ceb 
 8048ca1:	e8 df 04 00 00       	call   8049185 
 8048ca6:	b8 62 00 00 00       	mov    $0x62,%eax
 8048cab:	eb 3e                	jmp    8048ceb 
 8048cad:	b8 64 00 00 00       	mov    $0x64,%eax
 8048cb2:	81 7d f0 5a 03 00 00 	cmpl   $0x35a,-0x10(%ebp)
 8048cb9:	74 30                	je     8048ceb 
 8048cbb:	e8 c5 04 00 00       	call   8049185 
 8048cc0:	b8 64 00 00 00       	mov    $0x64,%eax
 8048cc5:	eb 24                	jmp    8048ceb 
 8048cc7:	b8 65 00 00 00       	mov    $0x65,%eax
 8048ccc:	81 7d f0 50 02 00 00 	cmpl   $0x250,-0x10(%ebp)
 8048cd3:	74 16                	je     8048ceb 
 8048cd5:	e8 ab 04 00 00       	call   8049185 
 8048cda:	b8 65 00 00 00       	mov    $0x65,%eax
 8048cdf:	eb 0a                	jmp    8048ceb 
 8048ce1:	e8 9f 04 00 00       	call   8049185 
 8048ce6:	b8 6b 00 00 00       	mov    $0x6b,%eax
 8048ceb:	3a 45 eb             	cmp    -0x15(%ebp),%al
 8048cee:	74 05                	je     8048cf5 
 8048cf0:	e8 90 04 00 00       	call   8049185 
 8048cf5:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048cf8:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048cff:	74 05                	je     8048d06 
 8048d01:	e8 8a fa ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048d06:	c9                   	leave  
 8048d07:	c3                   	ret    

首先观察到phase_3的代码中有一段:

 8048bce:	68 42 a1 04 08       	push   $0x804a142
 8048bd3:	ff 75 08             	push   0x8(%ebp)
 8048bd6:	e8 35 fc ff ff       	call   8048810 <__isoc99_sscanf@plt>

其中$0x804a142是输入格式字符串,这里要作为参数压栈,传递给sscanf,查看一下:

(gdb) x/s 0x804a142
0x804a142: "%d %c %d"

这题的输入就是两个整数中间用字符隔开。

紧接着的一段代码也印证了这一点:

 8048bdb:	83 c4 20             	add    $0x20,%esp
 8048bde:	83 f8 02             	cmp    $0x2,%eax
 8048be1:	7f 05                	jg     8048be8 
 8048be3:	e8 9d 05 00 00       	call   8049185 

eax也就是sscanf的返回值,也就是输入的变量的个数,如果小于等于2就bomb。

紧接着有一个判断:

 8048be8:	83 7d ec 07          	cmpl   $0x7,-0x14(%ebp)
 8048bec:	0f 87 ef 00 00 00    	ja     8048ce1 

如果-0x14(%ebp) > 0x7,就会跳转,这里会跳转到call ,所以要求-0x14(%ebp) <= 7,那它是什么呢?观察调用sscanf之前的一段代码:

 8048bc2:	8d 45 f0             	lea    -0x10(%ebp),%eax
 8048bc5:	50                  	push   %eax
 8048bc6:	8d 45 eb             	lea    -0x15(%ebp),%eax
 8048bc9:	50                  	push   %eax
 8048bca:	8d 45 ec             	lea    -0x14(%ebp),%eax
 8048bcd:	50                  	push   %eax

根据参数传递的顺序应该是我们输入的第一个数,这也就要求我们输入的第一个数小于等于7。

继续往下看,发现程序会跳转,并且跳转的位置和我们输入的数相关联:

 8048bf2:	8b 45 ec             	mov    -0x14(%ebp),%eax
 8048bf5:	ff 24 85 54 a1 04 08 	jmp    *0x804a154(,%eax,4)

用第一个输入的数是1进行下一步调试:

【csapp lab】lab2_bomblab_第4张图片

跳转到了:

 8048c1d:	b8 69 00 00 00       	mov    $0x69,%eax
 8048c22:	81 7d f0 99 01 00 00 	cmpl   $0x199,-0x10(%ebp)
 8048c29:	0f 84 bc 00 00 00    	je     8048ceb 
 8048c2f:	e8 51 05 00 00       	call   8049185 

继续看,这里把eax更新成了0x69,并且比较了我们的第三个输入,也就是第二个整数和0x199也就是409,所以确定了我们要输入的第二个数是409,接下来程序跳转到了8048ceb

 8048ceb:	3a 45 eb             	cmp    -0x15(%ebp),%al
 8048cee:	74 05                	je     8048cf5 
 8048cf0:	e8 90 04 00 00       	call   8049185 
 8048cf5:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048cf8:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048cff:	    74 05               je     8048d06 
 8048d01:	e8 8a fa ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048d06:	c9                  	leave  
 8048d07:	c3                  	ret  

这里比较的是我们输入的字符和%al%al是累加器寄存器eax的低八位,如下图所示,转成十进制就是105,所以我们要输入的字符的ascii码值是105,也就是i。这样就得到了最终的答案1i409

image-20231119024053668

不过这道题应该有8个答案,因为输入的第一个整数有8个取值,所以会有八个分支,对应八个答案。

phase_4

先贴代码:

08048d08 :
 8048d08:	55                   	push   %ebp
 8048d09:	89 e5                	mov    %esp,%ebp
 8048d0b:	56                   	push   %esi
 8048d0c:	53                   	push   %ebx
 8048d0d:	8b 55 08             	mov    0x8(%ebp),%edx
 8048d10:	8b 4d 0c             	mov    0xc(%ebp),%ecx
 8048d13:	8b 75 10             	mov    0x10(%ebp),%esi
 8048d16:	89 f0                	mov    %esi,%eax
 8048d18:	29 c8                	sub    %ecx,%eax
 8048d1a:	89 c3                	mov    %eax,%ebx
 8048d1c:	c1 eb 1f             	shr    $0x1f,%ebx
 8048d1f:	01 d8                	add    %ebx,%eax
 8048d21:	d1 f8                	sar    %eax
 8048d23:	8d 1c 08             	lea    (%eax,%ecx,1),%ebx
 8048d26:	39 d3                	cmp    %edx,%ebx
 8048d28:	7e 15                	jle    8048d3f 
 8048d2a:	83 ec 04             	sub    $0x4,%esp
 8048d2d:	8d 43 ff             	lea    -0x1(%ebx),%eax
 8048d30:	50                   	push   %eax
 8048d31:	51                   	push   %ecx
 8048d32:	52                   	push   %edx
 8048d33:	e8 d0 ff ff ff       	call   8048d08 
 8048d38:	83 c4 10             	add    $0x10,%esp
 8048d3b:	01 d8                	add    %ebx,%eax
 8048d3d:	eb 19                	jmp    8048d58 
 8048d3f:	89 d8                	mov    %ebx,%eax
 8048d41:	39 d3                	cmp    %edx,%ebx
 8048d43:	7d 13                	jge    8048d58 
 8048d45:	83 ec 04             	sub    $0x4,%esp
 8048d48:	56                   	push   %esi
 8048d49:	8d 43 01             	lea    0x1(%ebx),%eax
 8048d4c:	50                   	push   %eax
 8048d4d:	52                   	push   %edx
 8048d4e:	e8 b5 ff ff ff       	call   8048d08 
 8048d53:	83 c4 10             	add    $0x10,%esp
 8048d56:	01 d8                	add    %ebx,%eax
 8048d58:	8d 65 f8             	lea    -0x8(%ebp),%esp
 8048d5b:	5b                   	pop    %ebx
 8048d5c:	5e                   	pop    %esi
 8048d5d:	5d                   	pop    %ebp
 8048d5e:	c3                   	ret    

08048d5f :
 8048d5f:	55                   	push   %ebp
 8048d60:	89 e5                	mov    %esp,%ebp
 8048d62:	83 ec 18             	sub    $0x18,%esp
 8048d65:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8048d6b:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048d6e:	31 c0                	xor    %eax,%eax
 8048d70:	8d 45 f0             	lea    -0x10(%ebp),%eax
 8048d73:	50                   	push   %eax
 8048d74:	8d 45 ec             	lea    -0x14(%ebp),%eax
 8048d77:	50                   	push   %eax
 8048d78:	68 93 a2 04 08       	push   $0x804a293
 8048d7d:	ff 75 08             	push   0x8(%ebp)
 8048d80:	e8 8b fa ff ff       	call   8048810 <__isoc99_sscanf@plt>
 8048d85:	83 c4 10             	add    $0x10,%esp
 8048d88:	83 f8 02             	cmp    $0x2,%eax
 8048d8b:	75 06                	jne    8048d93 
 8048d8d:	83 7d ec 0e          	cmpl   $0xe,-0x14(%ebp)
 8048d91:	76 05                	jbe    8048d98 
 8048d93:	e8 ed 03 00 00       	call   8049185 
 8048d98:	83 ec 04             	sub    $0x4,%esp
 8048d9b:	6a 0e                	push   $0xe
 8048d9d:	6a 00                	push   $0x0
 8048d9f:	ff 75 ec             	push   -0x14(%ebp)
 8048da2:	e8 61 ff ff ff       	call   8048d08 
 8048da7:	83 c4 10             	add    $0x10,%esp
 8048daa:	83 f8 13             	cmp    $0x13,%eax
 8048dad:	75 06                	jne    8048db5 
 8048daf:	83 7d f0 13          	cmpl   $0x13,-0x10(%ebp)
 8048db3:	74 05                	je     8048dba 
 8048db5:	e8 cb 03 00 00       	call   8049185 
 8048dba:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048dbd:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048dc4:	74 05                	je     8048dcb 
 8048dc6:	e8 c5 f9 ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048dcb:	c9                   	leave  
 8048dcc:	c3                   	ret    

还是先观察调用sscanf之前的代码确定输入格式:

 8048d70:	8d 45 f0             	lea    -0x10(%ebp),%eax
 8048d73:	50                  	push   %eax
 8048d74:	8d 45 ec             	lea    -0x14(%ebp),%eax
 8048d77:	50                  	push   %eax
 8048d78:	68 93 a2 04 08       	push   $0x804a293
 8048d7d:	ff 75 08             	push   0x8(%ebp)
 8048d80:	e8 8b fa ff ff       	call   8048810 <__isoc99_sscanf@plt>
 8048d85:	83 c4 10             	add    $0x10,%esp
 8048d88:	83 f8 02             	cmp    $0x2,%eax
 8048d8b:	75 06                	jne    8048d93 

image-20231119024159720

输入格式是两个整数,中间用空格隔开。

其中第一个数存在-0x14(%ebp),第二个数存在-0x10(%ebp)

继续向下看:

 8048d8d:	83 7d ec 0e          	cmpl   $0xe,-0x14(%ebp)
 8048d91:	76 05                	jbe    8048d98 
 8048d93:	e8 ed 03 00 00       	call   8049185 

这里比较了输入的第一个数和0xe,也就是14,说明第一个数要小于等于14,这里还是用输入1来进一步调试。继续看代码:

 8048d98:	83 ec 04             	sub    $0x4,%esp
 8048d9b:	6a 0e                	push   $0xe
 8048d9d:	6a 00                	push   $0x0
 8048d9f:	ff 75 ec             	push   -0x14(%ebp)
 8048da2:	e8 61 ff ff ff       	call   8048d08 

调用了func4,先分析一下参数:第一个参数是输入的第一个整数,第二个参数是 0,第三个参数是0xe也就是14。继续分析func4的代码,先看参数的处理:

 8048d0d:	8b 55 08             	mov    0x8(%ebp),%edx
 8048d10:	8b 4d 0c             	mov    0xc(%ebp),%ecx
 8048d13:	8b 75 10             	mov    0x10(%ebp),%esi

这里把第一个参数,也就是输入的第一个整数1赋给了edx,把0赋给了ecx14赋给了esi,继续看代码:

 8048d16:	89 f0                	mov    %esi,%eax
 8048d18:	29 c8                	sub    %ecx,%eax
 8048d1a:	89 c3                	mov    %eax,%ebx
 8048d1c:	c1 eb 1f             	shr    $0x1f,%ebx
 8048d1f:	01 d8                	add    %ebx,%eax
 8048d21:	d1 f8                	sar    %eax
 8048d23:	8d 1c 08             	lea    (%eax,%ecx,1),%ebx
 8048d26:	39 d3                	cmp    %edx,%ebx
 8048d28:	7e 15                	jle    8048d3f 

一直执行到比较之前,各个寄存器存放的值如下:

【csapp lab】lab2_bomblab_第5张图片

接下来过不了比较,会继续向下执行:

 8048d2a:	83 ec 04             	sub    $0x4,%esp
 8048d2d:	8d 43 ff             	lea    -0x1(%ebx),%eax
 8048d30:	50                   	push   %eax
 8048d31:	51                   	push   %ecx
 8048d32:	52                   	push   %edx
 8048d33:	e8 d0 ff ff ff       	call   8048d08 

这样就开始了递归,先看一下此时递归前各个寄存器存放的值:

【csapp lab】lab2_bomblab_第6张图片

从左到右三个参数依次为1、0、1,与上一次调用相比第三个参数发生了变化,继续调试到比较:

【csapp lab】lab2_bomblab_第7张图片

此时仍无法结束递归,下面会进行第三次调用,调用前各个寄存器的值如下:

【csapp lab】lab2_bomblab_第8张图片

第三个参数变成了2,继续调试到比较:

【csapp lab】lab2_bomblab_第9张图片

此时终于符合条件ebx <= edx,跳转到新阶段:

 8048d3f:	89 d8               mov    %ebx,%eax
 8048d41:	39 d3               cmp    %edx,%ebx
 8048d43:	7d 13               jge    8048d58 

接下来有个判断,要求ebx >= edx,先看一下此时各个寄存器的值:

【csapp lab】lab2_bomblab_第10张图片

符合条件,跳转到新位置:

 8048d58:	8d 65 f8             	lea    -0x8(%ebp),%esp
 8048d5b:	5b                  	pop    %ebx
 8048d5c:	5e                  	pop    %esi
 8048d5d:	5d                  	pop    %ebp
 8048d5e:	c3                  	ret   

此时跳出第三次调用,来到第二次调用的栈帧:

 8048d33:	e8 d0 ff ff ff       	call   8048d08 
 8048d38:	83 c4 10             	add    $0x10,%esp
 8048d3b:	01 d8                	add    %ebx,%eax
 8048d3d:	eb 19                	jmp    8048d58 

跳转之前各个寄存器的值如下:

【csapp lab】lab2_bomblab_第11张图片

第二次调用随之也结束,来到第一次调用的栈帧,还是与第二次一样,返回phase_4之前eax变为11

之后回到phase_4的栈帧:

 8048da2:	e8 61 ff ff ff       	call   8048d08 
 8048da7:	83 c4 10             	add    $0x10,%esp
 8048daa:	83 f8 13             	cmp    $0x13,%eax
 8048dad:	75 06                	jne    8048db5 
 8048daf:	83 7d f0 13          	cmpl   $0x13,-0x10(%ebp)
 8048db3:	74 05                	je     8048dba 
 8048db5:	e8 cb 03 00 00       	call   8049185 

此时各个寄存器的值如下:

【csapp lab】lab2_bomblab_第12张图片

此时eax(11) != 0x13(19),所以会爆炸,所以拆弹的关键来到了eax。在经过函数递归之后,eax的值要变为19,那就观察一下eax的值是如何变化的。不过这里还能看出第二个输入的整数应该是0x13(19)

在分析eax的过程中被绕晕了,索性摆烂,反正最大不超过14,一个个试,试出了正确答案4

【csapp lab】lab2_bomblab_第13张图片

所以这一题的最终答案就是:4 19

phase_5

贴代码:

08048dcd :
 8048dcd:	55                   	push   %ebp
 8048dce:	89 e5                	mov    %esp,%ebp
 8048dd0:	53                   	push   %ebx
 8048dd1:	83 ec 20             	sub    $0x20,%esp
 8048dd4:	8b 5d 08             	mov    0x8(%ebp),%ebx
 8048dd7:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8048ddd:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048de0:	31 c0                	xor    %eax,%eax
 8048de2:	53                   	push   %ebx
 8048de3:	e8 78 02 00 00       	call   8049060 
 8048de8:	83 c4 10             	add    $0x10,%esp
 8048deb:	83 f8 06             	cmp    $0x6,%eax
 8048dee:	74 05                	je     8048df5 
 8048df0:	e8 90 03 00 00       	call   8049185 
 8048df5:	b8 00 00 00 00       	mov    $0x0,%eax
 8048dfa:	0f b6 14 03          	movzbl (%ebx,%eax,1),%edx
 8048dfe:	83 e2 0f             	and    $0xf,%edx
 8048e01:	0f b6 92 74 a1 04 08 	movzbl 0x804a174(%edx),%edx
 8048e08:	88 54 05 ed          	mov    %dl,-0x13(%ebp,%eax,1)
 8048e0c:	83 c0 01             	add    $0x1,%eax
 8048e0f:	83 f8 06             	cmp    $0x6,%eax
 8048e12:	75 e6                	jne    8048dfa 
 8048e14:	c6 45 f3 00          	movb   $0x0,-0xd(%ebp)
 8048e18:	83 ec 08             	sub    $0x8,%esp
 8048e1b:	68 4b a1 04 08       	push   $0x804a14b
 8048e20:	8d 45 ed             	lea    -0x13(%ebp),%eax
 8048e23:	50                   	push   %eax
 8048e24:	e8 59 02 00 00       	call   8049082 
 8048e29:	83 c4 10             	add    $0x10,%esp
 8048e2c:	85 c0                	test   %eax,%eax
 8048e2e:	74 05                	je     8048e35 
 8048e30:	e8 50 03 00 00       	call   8049185 
 8048e35:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048e38:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048e3f:	74 05                	je     8048e46 
 8048e41:	e8 4a f9 ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048e46:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048e49:	c9                   	leave  
 8048e4a:	c3                   	ret    

先看部分代码:

 8048de3:	e8 78 02 00 00       	call   8049060 
 8048de8:	83 c4 10             	add    $0x10,%esp
 8048deb:	83 f8 06             	cmp    $0x6,%eax
 8048dee:	74 05                	je     8048df5 
 8048df0:	e8 90 03 00 00       	call   8049185 

一开始调用了一次string_length,并且比较了一下字符串的长度和6,如果不相等就会爆炸,需要知道string_length是计算的谁的长度:

img

img

经过测试发现是我们输入的字符串的长度,这样就确定了我们要输入的字符串的长度是6

接下来是一段循环:

 8048df5:	b8 00 00 00 00       	mov    $0x0,%eax
 8048dfa:	0f b6 14 03          	movzbl  (%ebx,%eax,1),%edx
 8048dfe:	83 e2 0f             	and    $0xf,%edx
 8048e01:	0f b6 92 74 a1 04 08 	movzbl  0x804a174(%edx),%edx
 8048e08:	88 54 05 ed          	mov    %dl,-0x13(%ebp,%eax,1)
 8048e0c:	83 c0 01             	add    $0x1,%eax
 8048e0f:	83 f8 06             	cmp    $0x6,%eax
 8048e12:	75 e6                	jne    8048dfa 

eax0开始,每次+1,直到等于6。分析一下这个循环做了什么。

首先给edx赋值,且这个值随着循环次数改变而改变,然后保留edx低4位,把某个地址加上第四位作为偏移量,得到的地址处的值赋给edx,然后把这个值赋给-0x13(%ebp,%eax,1),只占一个字节空间,猜测每一次取了一个字符,并且后面调用了strings_not_equal

 8048e1b:	68 4b a1 04 08       	push   $0x804a14b
 8048e20:	8d 45 ed             	lea    -0x13(%ebp),%eax
 8048e23:	50                  	push   %eax
 8048e24:	e8 59 02 00 00       	call   8049082 

六次循环得到一个字符串,并把这个字符串的首地址作为参数传给strings_not_equal,所以这六个字符串是什么呢,答案在0x804a14b

img

所以要通过循环构造出这么一个字符串。循环是从0x804a174加偏移得到的,先看一下0x804a174指向的内容:

img

很显然要从maduiersnfotvbylSo通过加偏移量的方式依次构造出f l a m e s,偏移量依次是9 15 1 0 5 7,偏移量从哪来呢:

8048dd4: 8b 5d 08 mov 0x8(%ebp),%ebx

很显然是传过来的参数,这个参数就是我们输入的字符串,所以是取我们输入的字符串对应的ascii码值的低四位作为偏移量,所以答案的要求就出来了:低四位分别是1001(9) 1111(15) 0001(1) 0000(0) 0101(5) 0111(7)

令高位为0100(64),得到一组解I(73) O(79) A(65) @(64) E(69) G(71),所以正确答案就是IOA@EG

phase_6

贴代码:

08048e4b :
 8048e4b:	55                   	push   %ebp
 8048e4c:	89 e5                	mov    %esp,%ebp
 8048e4e:	56                   	push   %esi
 8048e4f:	53                   	push   %ebx
 8048e50:	83 ec 48             	sub    $0x48,%esp
 8048e53:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8048e59:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048e5c:	31 c0                	xor    %eax,%eax
 8048e5e:	8d 45 c4             	lea    -0x3c(%ebp),%eax
 8048e61:	50                   	push   %eax
 8048e62:	ff 75 08             	push   0x8(%ebp)
 8048e65:	e8 43 03 00 00       	call   80491ad 
 8048e6a:	83 c4 10             	add    $0x10,%esp
 8048e6d:	be 00 00 00 00       	mov    $0x0,%esi
 8048e72:	8b 44 b5 c4          	mov    -0x3c(%ebp,%esi,4),%eax
 8048e76:	83 e8 01             	sub    $0x1,%eax
 8048e79:	83 f8 05             	cmp    $0x5,%eax
 8048e7c:	76 05                	jbe    8048e83 
 8048e7e:	e8 02 03 00 00       	call   8049185 

 8048e83:	83 c6 01             	add    $0x1,%esi
 8048e86:	83 fe 06             	cmp    $0x6,%esi
 8048e89:	74 33                	je     8048ebe 
 8048e8b:	89 f3                	mov    %esi,%ebx
 8048e8d:	8b 44 9d c4          	mov    -0x3c(%ebp,%ebx,4),%eax
 8048e91:	39 44 b5 c0          	cmp    %eax,-0x40(%ebp,%esi,4)
 8048e95:	75 05                	jne    8048e9c 
 8048e97:	e8 e9 02 00 00       	call   8049185 
 8048e9c:	83 c3 01             	add    $0x1,%ebx
 8048e9f:	83 fb 05             	cmp    $0x5,%ebx
 8048ea2:	7e e9                	jle    8048e8d 
 8048ea4:	eb cc                	jmp    8048e72 

 8048ea6:	8b 52 08             	mov    0x8(%edx),%edx
 8048ea9:	83 c0 01             	add    $0x1,%eax
 8048eac:	39 c8                	cmp    %ecx,%eax
 8048eae:	75 f6                	jne    8048ea6 
 8048eb0:	89 54 b5 dc          	mov    %edx,-0x24(%ebp,%esi,4)
 8048eb4:	83 c3 01             	add    $0x1,%ebx
 8048eb7:	83 fb 06             	cmp    $0x6,%ebx
 8048eba:	75 07                	jne    8048ec3 
 8048ebc:	eb 1c                	jmp    8048eda 

 8048ebe:	bb 00 00 00 00       	mov    $0x0,%ebx
 8048ec3:	89 de                	mov    %ebx,%esi
 8048ec5:	8b 4 c 9d c4          	mov    -0x3c(%ebp,%ebx,4),%ecx
 8048ec9:	b8 01 00 00 00       	mov    $0x1,%eax
 8048ece:	ba 3c c1 04 08       	mov    $0x804c13c,%edx
 8048ed3:	83 f9 01             	cmp    $0x1,%ecx
 8048ed6:	7f ce                	jg     8048ea6 
 8048ed8:	eb d6                	jmp    8048eb0 

 8048eda:	8b 5d dc             	mov    -0x24(%ebp),%ebx
 8048edd:	8d 45 dc             	lea    -0x24(%ebp),%eax
 8048ee0:	8d 75 f0             	lea    -0x10(%ebp),%esi
 8048ee3:	89 d9                	mov    %ebx,%ecx
 8048ee5:	8b 50 04             	mov    0x4(%eax),%edx
 8048ee8:	89 51 08             	mov    %edx,0x8(%ecx)
 8048eeb:	83 c0 04             	add    $0x4,%eax
 8048eee:	89 d1                	mov    %edx,%ecx
 8048ef0:	39 f0                	cmp    %esi,%eax
 8048ef2:	75 f1                	jne    8048ee5 

 8048ef4:	c7 42 08 00 00 00 00 	movl   $0x0,0x8(%edx)
 8048efb:	be 05 00 00 00       	mov    $0x5,%esi
 8048f00:	8b 43 08             	mov    0x8(%ebx),%eax
 8048f03:	8b 00                	mov    (%eax),%eax
 8048f05:	39 03                	cmp    %eax,(%ebx)
 8048f07:	7d 05                	jge    8048f0e 
 8048f09:	e8 77 02 00 00       	call   8049185 
 8048f0e:	8b 5b 08             	mov    0x8(%ebx),%ebx
 8048f11:	83 ee 01             	sub    $0x1,%esi
 8048f14:	75 ea                	jne    8048f00 

 8048f16:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048f19:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048f20:	74 05                	je     8048f27 
 8048f22:	e8 69 f8 ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048f27:	8d 65 f8             	lea    -0x8(%ebp),%esp
 8048f2a:	5b                   	pop    %ebx
 8048f2b:	5e                   	pop    %esi
 8048f2c:	5d                   	pop    %ebp
 8048f2d:	c3                   	ret    

先看:

 8048e5e:	8d 45 c4             	lea    -0x3c(%ebp),%eax
 8048e61:	50                  	push   %eax
 8048e62:	ff 75 08             	push   0x8(%ebp)
 8048e65:	e8 43 03 00 00       	call   80491ad 

通过开始的一段代码可知是输入六个数字,并且首个元素存放地址为-0x3c(%ebp),假设输入的六个数字分别是a[0], a[1], a[2], a[3], a[4], a[5]

之后有一个判断:

 8048e6a:	83 c4 10             	add    $0x10,%esp
 8048e6d:	be 00 00 00 00       	mov    $0x0,%esi
 8048e72:	8b 44 b5 c4          	mov    -0x3c(%ebp,%esi,4),%eax
 8048e76:	83 e8 01             	sub    $0x1,%eax
 8048e79:	83 f8 05             	cmp    $0x5,%eax
 8048e7c:	76 05                	jbe    8048e83 
 8048e7e:	e8 02 03 00 00       	call   8049185 

跳转条件是(a[0] - 1 <= 5),这就限制了a[0] <= 6,后面以输入1进行尝试。

 8048e83:	83 c6 01             	add    $0x1,%esi
 8048e86:	83 fe 06             	cmp    $0x6,%esi
 8048e89:	74 33                	je     8048ebe 
 8048e8b:	89 f3                	mov    %esi,%ebx
 8048e8d:	8b 44 9d c4          	mov    -0x3c(%ebp,%ebx,4),%eax
 8048e91:	39 44 b5 c0          	cmp    %eax,-0x40(%ebp,%esi,4)
 8048e95:	75 05                	jne    8048e9c 
 8048e97:	e8 e9 02 00 00       	call   8049185 

接下来esi + 16进行比较,可以猜想出这是一个循环,esi是计数器,循环六次。接下来又比较了a[0]和a[1],要求两个数不能相等。

 8048e9c:	83 c3 01             	add    $0x1,%ebx
 8048e9f:	83 fb 05             	cmp    $0x5,%ebx
 8048ea2:	7e e9                	jle    8048e8d 
 8048ea4:	eb cc                	jmp    8048e72 

然后ebx成了新计数器,循环5次,这里是把a[0]依次与a[1] ~ a[5]各比较一次,保证不相等。可以发现其实这是两层循环,先判断循环到的a[i]是否小于等于6,然后判断它与后面几个数是否相等。

整个大循环结束后来到新部分:

 8048ea6:	8b 52 08             	mov    0x8(%edx),%edx
 8048ea9:	83 c0 01             	add    $0x1,%eax
 8048eac:	39 c8                	cmp    %ecx,%eax
 8048eae:	75 f6                	jne    8048ea6 
 8048eb0:	89 54 b5 dc          	mov    %edx,-0x24(%ebp,%esi,4)
 8048eb4:	83 c3 01             	add    $0x1,%ebx
 8048eb7:	83 fb 06             	cmp    $0x6,%ebx
 8048eba:	75 07                	jne    8048ec3 
 8048ebc:	eb 1c                	jmp    8048eda 

 8048ebe:	bb 00 00 00 00       	mov    $0x0,%ebx
 8048ec3:	89 de                	mov    %ebx,%esi
 8048ec5:	8b 4 c 9d c4          	mov    -0x3c(%ebp,%ebx,4),%ecx
 8048ec9:	b8 01 00 00 00       	mov    $0x1,%eax
 8048ece:	ba 3c c1 04 08       	mov    $0x804c13c,%edx
 8048ed3:	83 f9 01             	cmp    $0x1,%ecx
 8048ed6:	7f ce                	jg     8048ea6 
 8048ed8:	eb d6                	jmp    8048eb0 

程序从8048ebe开始执行,这里的计数器是ebx,从0开始循环到6,共循环7次。

第一次cmp跳转之前各个寄存器的值如图:

【csapp lab】lab2_bomblab_第14张图片

比较的是ecx1ecxa[ebx],第一次是a[0],我输入的是1 2 3 4 5 6,显然jg不会跳转,程序来到了8048eb0。接下来把edx给到了-0x24(%ebp,%esi,4)地址处,推测ebp-0x24地址处存放的是一个指针804c13c

可以看一下这个地址处存放的内容:

img

推测是链表,进一步查看:

【csapp lab】lab2_bomblab_第15张图片

链表一共有六个节点,每个节点存放三个数据,显然第三个数据是下一个节点的地址。第二个数据从1~6,推测是链表的节点编号,那第一个数据应该就是链表存放的有效数据。

mov 0x8(%edx), %edx 操作其实是把edx更新成下个节点的指针,当eaxecx相等时把节点指针压栈,

否则会进入一个循环:eax++; edx继续指向下个指针,直到eax == ecx,这也就意味着六个节点压栈的顺序与我们输入的六个数字相关,如果输入的2 4 1 3 6 5,则六个节点在栈中存放的顺序依次为node2 node4 node1 node3 node6 node5,存放地址依次为%ebp-0x24、%ebp-0x20、%ebp-0x1c、%ebp-0x18、%ebp-0x14、%ebp-0x10。其实就是node[a[0]] ~ node[a[6]]

继续看代码:

 8048eda:	8b 5d dc             	mov    -0x24(%ebp),%ebx
 8048edd:	8d 45 dc             	lea    -0x24(%ebp),%eax
 8048ee0:	8d 75 f0             	lea    -0x10(%ebp),%esi
 8048ee3:	89 d9                	mov    %ebx,%ecx
 8048ee5:	8b 50 04             	mov    0x4(%eax),%edx
 8048ee8:	89 51 08             	mov    %edx,0x8(%ecx)
 8048eeb:	83 c0 04             	add    $0x4,%eax
 8048eee:	89 d1                	mov    %edx,%ecx
 8048ef0:	39 f0                	cmp    %esi,%eax
 8048ef2:	75 f1                	jne    8048ee5 

这段代码也是循环,会改变链表指向,通过多次调试可以发现:如果输入的是1 2 3 4 5 6,则链表指向不变;如果输入的是6 5 4 3 2 1,则链表指向变为6>5>4>3>2>1>2,最后有个环;如果输入的是1 3 5 2 4 6,则链表指向变为1>3>5>2>4>6

部分调试记录如下:

【csapp lab】lab2_bomblab_第16张图片

【csapp lab】lab2_bomblab_第17张图片

以上发现了链表指向顺序会跟输入的六个数有关。

来到最后一段代码:

 8048ef4:	c7 42 08 00 00 00 00 	movl   $0x0,0x8(%edx)
 8048efb:	be 05 00 00 00       	mov    $0x5,%esi
 8048f00:	8b 43 08             	mov    0x8(%ebx),%eax
 8048f03:	8b 00               	mov    (%eax),%eax
 8048f05:	39 03                	cmp    %eax,(%ebx)
 8048f07:	7d 05               	jge    8048f0e 
 8048f09:	e8 77 02 00 00       	call   8049185 
 8048f0e:	8b 5b 08             	mov    0x8(%ebx),%ebx
 8048f11:	83 ee 01             	sub    $0x1,%esi
 8048f14:	75 ea                	jne    8048f00 

这段代码其实就是遍历链表,遍历顺序就是刚刚重新排列好的顺序,然后依次取相邻两个链表的值进行比较,这一点可以调试到 8048f05 cmp %eax, (%ebx) 之前,看一下eax的值和ebx指向的值,如果前者大于后者就能通过,然后继续遍历,否则bomb!

所以链表的正确排列顺序就有依据了,先记录一下各个链表存放的数据都是多少:

【csapp lab】lab2_bomblab_第18张图片

1-995 2-959 3-779 4-921 5-853 6-363

要求前者大于后者,所以正确顺序也就是正确答案应该是:1 2 4 5 3 6

secret_phase

还有一个函数名叫secret_phase的,代码如下:

08048f80 :
 8048f80:	55                   	push   %ebp
 8048f81:	89 e5                	mov    %esp,%ebp
 8048f83:	53                   	push   %ebx
 8048f84:	83 ec 04             	sub    $0x4,%esp
 8048f87:	e8 5b 02 00 00       	call   80491e7 
 8048f8c:	83 ec 04             	sub    $0x4,%esp
 8048f8f:	6a 0a                	push   $0xa
 8048f91:	6a 00                	push   $0x0
 8048f93:	50                   	push   %eax
 8048f94:	e8 e7 f8 ff ff       	call   8048880 
 8048f99:	89 c3                	mov    %eax,%ebx
 8048f9b:	8d 40 ff             	lea    -0x1(%eax),%eax
 8048f9e:	83 c4 10             	add    $0x10,%esp
 8048fa1:	3d e8 03 00 00       	cmp    $0x3e8,%eax
 8048fa6:	76 05                	jbe    8048fad 
 8048fa8:	e8 d8 01 00 00       	call   8049185 
 8048fad:	83 ec 08             	sub    $0x8,%esp
 8048fb0:	53                   	push   %ebx
 8048fb1:	68 88 c0 04 08       	push   $0x804c088
 8048fb6:	e8 73 ff ff ff       	call   8048f2e 
 8048fbb:	83 c4 10             	add    $0x10,%esp
 8048fbe:	83 f8 03             	cmp    $0x3,%eax
 8048fc1:	74 05                	je     8048fc8 
 8048fc3:	e8 bd 01 00 00       	call   8049185 
 8048fc8:	83 ec 0c             	sub    $0xc,%esp
 8048fcb:	68 1c a1 04 08       	push   $0x804a11c
 8048fd0:	e8 eb f7 ff ff       	call   80487c0 
 8048fd5:	e8 10 03 00 00       	call   80492ea 
 8048fda:	83 c4 10             	add    $0x10,%esp
 8048fdd:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048fe0:	c9                   	leave  
 8048fe1:	c3                   	ret   

phase_defused中有调用,所以贴一下phase_defused的代码:

080492ea :
 80492ea:	55                   	push   %ebp
 80492eb:	89 e5                	mov    %esp,%ebp
 80492ed:	83 ec 68             	sub    $0x68,%esp
 80492f0:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 80492f6:	89 45 f4             	mov    %eax,-0xc(%ebp)
 80492f9:	31 c0                	xor    %eax,%eax
 80492fb:	83 3d cc c3 04 08 06 	cmpl   $0x6,0x804c3cc
 8049302:	75 6f                	jne    8049373 
 8049304:	83 ec 0c             	sub    $0xc,%esp
 8049307:	8d 45 a4             	lea    -0x5c(%ebp),%eax
 804930a:	50                   	push   %eax
 804930b:	8d 45 a0             	lea    -0x60(%ebp),%eax
 804930e:	50                   	push   %eax
 804930f:	8d 45 9c             	lea    -0x64(%ebp),%eax
 8049312:	50                   	push   %eax
 8049313:	68 ed a2 04 08       	push   $0x804a2ed
 8049318:	68 d0 c4 04 08       	push   $0x804c4d0
 804931d:	e8 ee f4 ff ff       	call   8048810 <__isoc99_sscanf@plt>
 8049322:	83 c4 20             	add    $0x20,%esp
 8049325:	83 f8 03             	cmp    $0x3,%eax
 8049328:	75 39                	jne    8049363 
 804932a:	83 ec 08             	sub    $0x8,%esp
 804932d:	68 f6 a2 04 08       	push   $0x804a2f6
 8049332:	8d 45 a4             	lea    -0x5c(%ebp),%eax
 8049335:	50                   	push   %eax
 8049336:	e8 47 fd ff ff       	call   8049082 
 804933b:	83 c4 10             	add    $0x10,%esp
 804933e:	85 c0                	test   %eax,%eax
 8049340:	75 21                	jne    8049363 
 8049342:	83 ec 0c             	sub    $0xc,%esp
 8049345:	68 bc a1 04 08       	push   $0x804a1bc
 804934a:	e8 71 f4 ff ff       	call   80487c0 
 804934f:	c7 04 24 e4 a1 04 08 	movl   $0x804a1e4,(%esp)
 8049356:	e8 65 f4 ff ff       	call   80487c0 
 804935b:	e8 20 fc ff ff       	call   8048f80 
 8049360:	83 c4 10             	add    $0x10,%esp
 8049363:	83 ec 0c             	sub    $0xc,%esp
 8049366:	68 1c a2 04 08       	push   $0x804a21c
 804936b:	e8 50 f4 ff ff       	call   80487c0 
 8049370:	83 c4 10             	add    $0x10,%esp
 8049373:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8049376:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 804937d:	74 05                	je     8049384 
 804937f:	e8 0c f4 ff ff       	call   8048790 <__stack_chk_fail@plt>
 8049384:	c9                   	leave  
 8049385:	c3                   	ret    

phase_defused的代码中发现了这句:

804935b: e8 20 fc ff ff call 8048f80

这也就意味着每次拆除炸弹都有可能触发secret_phase,继续对phase_defused的代码进行分析,寻找触发条件,着重关注最这行:

 8049312:	50                  	push   %eax
 8049313:	68 ed a2 04 08       	push   $0x804a2ed
 8049318:	68 d0 c4 04 08       	push   $0x804c4d0
 804931d:	e8 ee f4 ff ff       	call   8048810 <__isoc99_sscanf@plt>
 8049322:	83 c4 20             	add    $0x20,%esp
 8049325:	83 f8 03             	cmp    $0x3,%eax
 8049328:	75 39                	jne    8049363 

调用sscanf之前先看一下输入格式:

img

两个整数一个字符串,这里会判断输入了几个,如果输入的是两个数据并不会bomb,而是正常结束,输入两个的正好是phase_4,猜测phase_4多输一个字符串会触发secret_phase,下面继续分析这个字符串应该是什么:

 804932a:	83 ec 08             	sub    $0x8,%esp
 804932d:	68 f6 a2 04 08       	push   $0x804a2f6
 8049332:	8d 45 a4             	lea    -0x5c(%ebp),%eax
 8049335:	50                  	push   %eax
 8049336:	e8 47 fd ff ff       	call   8049082 
 804933b:	83 c4 10             	add    $0x10,%esp
 804933e:	85 c0                	test   %eax,%eax
 8049340:	75 21                	jne    8049363 

看到一个关键地址0x804a2f6,查看内容:

img

这就找到了我们要多输入的字符串:DrEvil

输入进去之后程序最后输出了小彩蛋,印证了它就是目标字符串;

【csapp lab】lab2_bomblab_第19张图片

回过头来对secret_phase进行分析:

 8048f8f:	6a 0a                	push   $0xa
 8048f91:	6a 00                	push   $0x0
 8048f93:	50                  	push   %eax
 8048f94:	e8 e7 f8 ff ff       	call   8048880 
 8048f99:	89 c3                	mov    %eax,%ebx
 8048f9b:	8d 40 ff             	lea    -0x1(%eax),%eax
 8048f9e:	83 c4 10             	add    $0x10,%esp
 8048fa1:	3d e8 03 00 00       	cmp    $0x3e8,%eax
 8048fa6:	76 05                	jbe    8048fad 
 8048fa8:	e8 d8 01 00 00       	call   8049185 

开始会调用一个strtol函数,将字符串转成long,这里要转的字符串的首地址是eax,也就是上一次调用的函数的返回值,上一个函数是read_line,所以也就是把我们的输入转成long-1后与0x3e8(1000)进行比较,要求小于等于1000,也就意味着我们要输入一个数字,并且这个数字要小于等于1001

继续向下看;

 8048fb0:	53                  	push   %ebx
 8048fb1:	68 88 c0 04 08       	push   $0x804c088
 8048fb6:	e8 73 ff ff ff       	call   8048f2e 
 8048fbb:	83 c4 10             	add    $0x10,%esp
 8048fbe:	83 f8 03             	cmp    $0x3,%eax
 8048fc1:	74 05                	je     8048fc8 
 8048fc3:	e8 bd 01 00 00       	call   8049185 
 8048fbb:	83 c4 10             	add    $0x10,%esp
 8048fbe:	83 f8 03             	cmp    $0x3,%eax
 8048fc1:	74 05                	je     8048fc8 
 8048fc3:	e8 bd 01 00 00       	call   8049185 

看最后三句可以看出当调用完fun7后,eax==3时才不会爆炸,secret_phase也就会结束,所以关键看eaxfun7中的变化。

调用fun7之前传递的参数为ebx0x804c088ebx根据之前的代码可知是我们输入的整数,看一下0x804c088存的是什么(64是显示64个单位,u是以十进制格式输出,换成x就是以十六进制格式输出,w是以8个字节为单位,我一开始是用默认的4个字节为单位,结果什么也看不出来,耽误了好久):

【csapp lab】lab2_bomblab_第20张图片

【csapp lab】lab2_bomblab_第21张图片

数据有nxx,后面还有nodex,其实就是phase_6中的链表节点结构。不难看出nxx也有着异曲同工之妙。再看一眼,能看出nxx是以三个单位为一组,第一个存放的是一个较小的数,猜测为有效数据,后两个显然是指针,并且前七个节点中的两个指针都有指向,猜测为二叉树结构,一共15个节点,8个叶子结点,根据各个节点之间的连接关系和对应地址处的标识,可以推断出这棵树的形状如下:

【csapp lab】lab2_bomblab_第22张图片

整理出来发现这是一颗平衡搜索二叉树,左节点都比父节点小,右节点都比父节点大。

那么fun7的参数就有了,第一个参数是这颗二叉树根节点的地址,第二个参数是我们输入的数,接下来可以继续分析fun7,先打出完整代码,然后顺着翻译一下:

08048f2e :
 8048f2e:	55                   	push   %ebp
 8048f2f:	89 e5                	mov    %esp,%ebp
 8048f31:	53                   	push   %ebx
 8048f32:	83 ec 04             	sub    $0x4,%esp
 8048f35:	8b 55 08             	mov    0x8(%ebp),%edx
 8048f38:	8b 4d 0c             	mov    0xc(%ebp),%ecx
 8048f3b:	85 d2                	test   %edx,%edx
 8048f3d:	74 37                	je     8048f76 

 8048f3f:	8b 1a                	mov    (%edx),%ebx
 8048f41:	39 cb                	cmp    %ecx,%ebx
 8048f43:	7e 13                	jle    8048f58 
 8048f45:	83 ec 08             	sub    $0x8,%esp
 8048f48:	51                   	push   %ecx
 8048f49:	ff 72 04             	push   0x4(%edx)
 8048f4c:	e8 dd ff ff ff       	call   8048f2e 

 8048f51:	83 c4 10             	add    $0x10,%esp
 8048f54:	01 c0                	add    %eax,%eax
 8048f56:	eb 23                	jmp    8048f7b 
  
 8048f58:	b8 00 00 00 00       	mov    $0x0,%eax
 8048f5d:	39 cb                	cmp    %ecx,%ebx
 8048f5f:	74 1a                	je     8048f7b 

 8048f61:	83 ec 08             	sub    $0x8,%esp
 8048f64:	51                   	push   %ecx
 8048f65:	ff 72 08             	push   0x8(%edx)
 8048f68:	e8 c1 ff ff ff       	call   8048f2e 

 8048f6d:	83 c4 10             	add    $0x10,%esp
 8048f70:	8d 44 00 01          	lea    0x1(%eax,%eax,1),%eax
 8048f74:	eb 05                	jmp    8048f7b 
 
 8048f76:	b8 ff ff ff ff       	mov    $0xffffffff,%eax

 8048f7b:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048f7e:	c9                   	leave  
 8048f7f:	c3                   	ret    

顺着思路翻译一下还是很简单的:

fun7 (node* root, int num) {
	if (root == null) {
		eax = 0xffffffff;
		return
    }
	if (root->val <= num) {
		eax = 0;				# ①
		if (root->val == num)
			return;
		fun7(root->right, num);
		eax = eax * 2 + 1;		# ②
		return;
    }
	fun7(root->left, num);
	eax *= 2;					# ③
	return;
}

本来是想一步步分析汇编的,但递归实在太绕了:

【csapp lab】lab2_bomblab_第23张图片

还是顺着把汇编翻译一下比较简单。

最终目标是让eax变为3。这里有关eax的有效操作有①eax = 0;②eax = eax * 2 + 1;③eax *= 2

所以一个可行的顺序是①②②,,对应的在递归第一层要执行②,第二层要执行②,第三层执行①。

来到第一层,此时root->val = 36,第一层要走到②之前那一句进入二层调用,这就要求num > 36

来到第二层,此时root->val = 50,仍需要走到②之前那一句进入三层调用,这就要求num > 50

来到第三层,此时root->val = 107,这次需要走到①处然后返回,要求num == 107

这样就推理出了最终答案:107

测试一下完全通过:

【csapp lab】lab2_bomblab_第24张图片

你可能感兴趣的:(随想随写,c语言,汇编,linux,csapp,lab2)