当前位置: 首页 > news >正文

【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文件,每做出来一题就把答案写进去,这样就避免了一条一条输入答案。一题占一行,要注意最后要多一个空行,目的是最后一题的输入以 换行符结尾,保证是正确输入:

image-20231119105551853

实验内容

phase_1

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

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

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

08048b30 <phase_1>:8048b30:	55                  	push   %ebp8048b31:	89 e5                	mov    %esp,%ebp8048b33:	83 ec 10             	sub    $0x10,%esp8048b36:	68 ec a0 04 08       	push   $0x804a0ec8048b3b:	ff 75 08             	push   0x8(%ebp)8048b3e:	e8 3f 05 00 00       	call   8049082 <strings_not_equal>8048b43:	83 c4 10             	add    $0x10,%esp8048b46:	85 c0                	test   %eax,%eax8048b48:	74 05                	je     8048b4f <phase_1+0x1f>8048b4a:	e8 36 06 00 00        	call   8049185 <explode_bomb>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 <read_line>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 <phase_2>:8048b51:	55                   	push   %ebp8048b52:	89 e5                	mov    %esp,%ebp8048b54:	53                   	push   %ebx8048b55:	83 ec 2c             	sub    $0x2c,%esp8048b58:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax8048b5e:	89 45 f4             	mov    %eax,-0xc(%ebp)8048b61:	31 c0                	xor    %eax,%eax8048b63:	8d 45 dc             	lea    -0x24(%ebp),%eax8048b66:	50                   	push   %eax8048b67:	ff 75 08             	push   0x8(%ebp)8048b6a:	e8 3e 06 00 00       	call   80491ad <read_six_numbers>8048b6f:	83 c4 10             	add    $0x10,%esp8048b72:	83 7d dc 00          	cmpl   $0x0,-0x24(%ebp)8048b76:	79 05                	jns    8048b7d <phase_2+0x2c>8048b78:	e8 08 06 00 00       	call   8049185 <explode_bomb>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),%eax8048b88:	39 44 9d dc          	cmp    %eax,-0x24(%ebp,%ebx,4)8048b8c:	74 05                	je     8048b93 <phase_2+0x42>8048b8e:	e8 f2 05 00 00       	call   8049185 <explode_bomb>8048b93:	83 c3 01             	add    $0x1,%ebx8048b96:	83 fb 06             	cmp    $0x6,%ebx8048b99:	75 e7                	jne    8048b82 <phase_2+0x31>8048b9b:	8b 45 f4             	mov    -0xc(%ebp),%eax8048b9e:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax8048ba5:	74 05                	je     8048bac <phase_2+0x5b>8048ba7:	e8 e4 fb ff ff       	call   8048790 <__stack_chk_fail@plt>8048bac:	8b 5d fc             	mov    -0x4(%ebp),%ebx8048baf:	c9                   	leave  8048bb0:	c3                   	ret  

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

观察这部分汇编代码:

 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),%eax8048b88:	39 44 9d dc          	cmp    %eax,-0x24(%ebp,%ebx,4)8048b8c:	74 05                	je     8048b93 <phase_2+0x42>8048b8e:	e8 f2 05 00 00       	call   8049185 <explode_bomb>8048b93:	83 c3 01             	add    $0x1,%ebx8048b96:	83 fb 06             	cmp    $0x6,%ebx8048b99:	75 e7                	jne    8048b82 <phase_2+0x31>

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

ebx比较数1比较数2
11 + ebp-0x28+1*4ebp-0x24+1*4
22 + ebp-0x28+2*4ebp-0x24+2*4
33 + ebp-0x28+3*4ebp-0x24+3*4
44 + ebp-0x28+4*4ebp-0x24+4*4
55 + ebp-0x28+5*4ebp-0x24+5*4

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

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

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

部分调试记录截图:

image-20231119023438558

image-20231119023442324

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

phase_3

先把phase_3的代码贴出来:

08048bb1 <phase_3>:8048bb1:	55                   	push   %ebp8048bb2:	89 e5                	mov    %esp,%ebp8048bb4:	83 ec 24             	sub    $0x24,%esp8048bb7:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax8048bbd:	89 45 f4             	mov    %eax,-0xc(%ebp)8048bc0:	31 c0                	xor    %eax,%eax8048bc2:	8d 45 f0             	lea    -0x10(%ebp),%eax8048bc5:	50                   	push   %eax8048bc6:	8d 45 eb             	lea    -0x15(%ebp),%eax8048bc9:	50                   	push   %eax8048bca:	8d 45 ec             	lea    -0x14(%ebp),%eax8048bcd:	50                   	push   %eax8048bce:	68 42 a1 04 08       	push   $0x804a1428048bd3:	ff 75 08             	push   0x8(%ebp)8048bd6:	e8 35 fc ff ff       	call   8048810 <__isoc99_sscanf@plt>8048bdb:	83 c4 20             	add    $0x20,%esp8048bde:	83 f8 02             	cmp    $0x2,%eax8048be1:	7f 05                	jg     8048be8 <phase_3+0x37>8048be3:	e8 9d 05 00 00       	call   8049185 <explode_bomb>8048be8:	83 7d ec 07          	cmpl   $0x7,-0x14(%ebp)8048bec:	0f 87 ef 00 00 00    	ja     8048ce1 <phase_3+0x130>8048bf2:	8b 45 ec             	mov    -0x14(%ebp),%eax8048bf5:	ff 24 85 54 a1 04 08 	jmp    *0x804a154(,%eax,4)8048bfc:	b8 78 00 00 00       	mov    $0x78,%eax8048c01:	81 7d f0 44 01 00 00 	cmpl   $0x144,-0x10(%ebp)8048c08:	0f 84 dd 00 00 00    	je     8048ceb <phase_3+0x13a>8048c0e:	e8 72 05 00 00       	call   8049185 <explode_bomb>8048c13:	b8 78 00 00 00       	mov    $0x78,%eax8048c18:	e9 ce 00 00 00       	jmp    8048ceb <phase_3+0x13a>8048c1d:	b8 69 00 00 00       	mov    $0x69,%eax8048c22:	81 7d f0 99 01 00 00 	cmpl   $0x199,-0x10(%ebp)8048c29:	0f 84 bc 00 00 00    	je     8048ceb <phase_3+0x13a>8048c2f:	e8 51 05 00 00       	call   8049185 <explode_bomb>8048c34:	b8 69 00 00 00       	mov    $0x69,%eax8048c39:	e9 ad 00 00 00       	jmp    8048ceb <phase_3+0x13a>8048c3e:	b8 74 00 00 00       	mov    $0x74,%eax8048c43:	81 7d f0 f6 02 00 00 	cmpl   $0x2f6,-0x10(%ebp)8048c4a:	0f 84 9b 00 00 00    	je     8048ceb <phase_3+0x13a>8048c50:	e8 30 05 00 00       	call   8049185 <explode_bomb>8048c55:	b8 74 00 00 00       	mov    $0x74,%eax8048c5a:	e9 8c 00 00 00       	jmp    8048ceb <phase_3+0x13a>8048c5f:	b8 68 00 00 00       	mov    $0x68,%eax8048c64:	81 7d f0 37 02 00 00 	cmpl   $0x237,-0x10(%ebp)8048c6b:	74 7e                	je     8048ceb <phase_3+0x13a>8048c6d:	e8 13 05 00 00       	call   8049185 <explode_bomb>8048c72:	b8 68 00 00 00       	mov    $0x68,%eax8048c77:	eb 72                	jmp    8048ceb <phase_3+0x13a>8048c79:	b8 79 00 00 00       	mov    $0x79,%eax8048c7e:	81 7d f0 1c 03 00 00 	cmpl   $0x31c,-0x10(%ebp)8048c85:	74 64                	je     8048ceb <phase_3+0x13a>8048c87:	e8 f9 04 00 00       	call   8049185 <explode_bomb>8048c8c:	b8 79 00 00 00       	mov    $0x79,%eax8048c91:	eb 58                	jmp    8048ceb <phase_3+0x13a>8048c93:	b8 62 00 00 00       	mov    $0x62,%eax8048c98:	81 7d f0 bd 01 00 00 	cmpl   $0x1bd,-0x10(%ebp)8048c9f:	74 4a                	je     8048ceb <phase_3+0x13a>8048ca1:	e8 df 04 00 00       	call   8049185 <explode_bomb>8048ca6:	b8 62 00 00 00       	mov    $0x62,%eax8048cab:	eb 3e                	jmp    8048ceb <phase_3+0x13a>8048cad:	b8 64 00 00 00       	mov    $0x64,%eax8048cb2:	81 7d f0 5a 03 00 00 	cmpl   $0x35a,-0x10(%ebp)8048cb9:	74 30                	je     8048ceb <phase_3+0x13a>8048cbb:	e8 c5 04 00 00       	call   8049185 <explode_bomb>8048cc0:	b8 64 00 00 00       	mov    $0x64,%eax8048cc5:	eb 24                	jmp    8048ceb <phase_3+0x13a>8048cc7:	b8 65 00 00 00       	mov    $0x65,%eax8048ccc:	81 7d f0 50 02 00 00 	cmpl   $0x250,-0x10(%ebp)8048cd3:	74 16                	je     8048ceb <phase_3+0x13a>8048cd5:	e8 ab 04 00 00       	call   8049185 <explode_bomb>8048cda:	b8 65 00 00 00       	mov    $0x65,%eax8048cdf:	eb 0a                	jmp    8048ceb <phase_3+0x13a>8048ce1:	e8 9f 04 00 00       	call   8049185 <explode_bomb>8048ce6:	b8 6b 00 00 00       	mov    $0x6b,%eax8048ceb:	3a 45 eb             	cmp    -0x15(%ebp),%al8048cee:	74 05                	je     8048cf5 <phase_3+0x144>8048cf0:	e8 90 04 00 00       	call   8049185 <explode_bomb>8048cf5:	8b 45 f4             	mov    -0xc(%ebp),%eax8048cf8:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax8048cff:	74 05                	je     8048d06 <phase_3+0x155>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   $0x804a1428048bd3:	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,%esp8048bde:	83 f8 02             	cmp    $0x2,%eax8048be1:	7f 05                	jg     8048be8 <phase_3+0x37>8048be3:	e8 9d 05 00 00       	call   8049185 <explode_bomb>

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

紧接着有一个判断:

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

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

 8048bc2:	8d 45 f0             	lea    -0x10(%ebp),%eax8048bc5:	50                  	push   %eax8048bc6:	8d 45 eb             	lea    -0x15(%ebp),%eax8048bc9:	50                  	push   %eax8048bca:	8d 45 ec             	lea    -0x14(%ebp),%eax8048bcd:	50                  	push   %eax

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

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

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

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

image-20231119023945131

跳转到了:

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

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

 8048ceb:	3a 45 eb             	cmp    -0x15(%ebp),%al8048cee:	74 05                	je     8048cf5 <phase_3+0x144>8048cf0:	e8 90 04 00 00       	call   8049185 <explode_bomb>8048cf5:	8b 45 f4             	mov    -0xc(%ebp),%eax8048cf8:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax8048cff:	    74 05               je     8048d06 <phase_3+0x155>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 <func4>:8048d08:	55                   	push   %ebp8048d09:	89 e5                	mov    %esp,%ebp8048d0b:	56                   	push   %esi8048d0c:	53                   	push   %ebx8048d0d:	8b 55 08             	mov    0x8(%ebp),%edx8048d10:	8b 4d 0c             	mov    0xc(%ebp),%ecx8048d13:	8b 75 10             	mov    0x10(%ebp),%esi8048d16:	89 f0                	mov    %esi,%eax8048d18:	29 c8                	sub    %ecx,%eax8048d1a:	89 c3                	mov    %eax,%ebx8048d1c:	c1 eb 1f             	shr    $0x1f,%ebx8048d1f:	01 d8                	add    %ebx,%eax8048d21:	d1 f8                	sar    %eax8048d23:	8d 1c 08             	lea    (%eax,%ecx,1),%ebx8048d26:	39 d3                	cmp    %edx,%ebx8048d28:	7e 15                	jle    8048d3f <func4+0x37>8048d2a:	83 ec 04             	sub    $0x4,%esp8048d2d:	8d 43 ff             	lea    -0x1(%ebx),%eax8048d30:	50                   	push   %eax8048d31:	51                   	push   %ecx8048d32:	52                   	push   %edx8048d33:	e8 d0 ff ff ff       	call   8048d08 <func4>8048d38:	83 c4 10             	add    $0x10,%esp8048d3b:	01 d8                	add    %ebx,%eax8048d3d:	eb 19                	jmp    8048d58 <func4+0x50>8048d3f:	89 d8                	mov    %ebx,%eax8048d41:	39 d3                	cmp    %edx,%ebx8048d43:	7d 13                	jge    8048d58 <func4+0x50>8048d45:	83 ec 04             	sub    $0x4,%esp8048d48:	56                   	push   %esi8048d49:	8d 43 01             	lea    0x1(%ebx),%eax8048d4c:	50                   	push   %eax8048d4d:	52                   	push   %edx8048d4e:	e8 b5 ff ff ff       	call   8048d08 <func4>8048d53:	83 c4 10             	add    $0x10,%esp8048d56:	01 d8                	add    %ebx,%eax8048d58:	8d 65 f8             	lea    -0x8(%ebp),%esp8048d5b:	5b                   	pop    %ebx8048d5c:	5e                   	pop    %esi8048d5d:	5d                   	pop    %ebp8048d5e:	c3                   	ret    08048d5f <phase_4>:8048d5f:	55                   	push   %ebp8048d60:	89 e5                	mov    %esp,%ebp8048d62:	83 ec 18             	sub    $0x18,%esp8048d65:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax8048d6b:	89 45 f4             	mov    %eax,-0xc(%ebp)8048d6e:	31 c0                	xor    %eax,%eax8048d70:	8d 45 f0             	lea    -0x10(%ebp),%eax8048d73:	50                   	push   %eax8048d74:	8d 45 ec             	lea    -0x14(%ebp),%eax8048d77:	50                   	push   %eax8048d78:	68 93 a2 04 08       	push   $0x804a2938048d7d:	ff 75 08             	push   0x8(%ebp)8048d80:	e8 8b fa ff ff       	call   8048810 <__isoc99_sscanf@plt>8048d85:	83 c4 10             	add    $0x10,%esp8048d88:	83 f8 02             	cmp    $0x2,%eax8048d8b:	75 06                	jne    8048d93 <phase_4+0x34>8048d8d:	83 7d ec 0e          	cmpl   $0xe,-0x14(%ebp)8048d91:	76 05                	jbe    8048d98 <phase_4+0x39>8048d93:	e8 ed 03 00 00       	call   8049185 <explode_bomb>8048d98:	83 ec 04             	sub    $0x4,%esp8048d9b:	6a 0e                	push   $0xe8048d9d:	6a 00                	push   $0x08048d9f:	ff 75 ec             	push   -0x14(%ebp)8048da2:	e8 61 ff ff ff       	call   8048d08 <func4>8048da7:	83 c4 10             	add    $0x10,%esp8048daa:	83 f8 13             	cmp    $0x13,%eax8048dad:	75 06                	jne    8048db5 <phase_4+0x56>8048daf:	83 7d f0 13          	cmpl   $0x13,-0x10(%ebp)8048db3:	74 05                	je     8048dba <phase_4+0x5b>8048db5:	e8 cb 03 00 00       	call   8049185 <explode_bomb>8048dba:	8b 45 f4             	mov    -0xc(%ebp),%eax8048dbd:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax8048dc4:	74 05                	je     8048dcb <phase_4+0x6c>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),%eax8048d73:	50                  	push   %eax8048d74:	8d 45 ec             	lea    -0x14(%ebp),%eax8048d77:	50                  	push   %eax8048d78:	68 93 a2 04 08       	push   $0x804a2938048d7d:	ff 75 08             	push   0x8(%ebp)8048d80:	e8 8b fa ff ff       	call   8048810 <__isoc99_sscanf@plt>8048d85:	83 c4 10             	add    $0x10,%esp8048d88:	83 f8 02             	cmp    $0x2,%eax8048d8b:	75 06                	jne    8048d93 <phase_4+0x34>

image-20231119024159720

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

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

继续向下看:

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

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

 8048d98:	83 ec 04             	sub    $0x4,%esp8048d9b:	6a 0e                	push   $0xe8048d9d:	6a 00                	push   $0x08048d9f:	ff 75 ec             	push   -0x14(%ebp)8048da2:	e8 61 ff ff ff       	call   8048d08 <func4>

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

 8048d0d:	8b 55 08             	mov    0x8(%ebp),%edx8048d10:	8b 4d 0c             	mov    0xc(%ebp),%ecx8048d13:	8b 75 10             	mov    0x10(%ebp),%esi

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

 8048d16:	89 f0                	mov    %esi,%eax8048d18:	29 c8                	sub    %ecx,%eax8048d1a:	89 c3                	mov    %eax,%ebx8048d1c:	c1 eb 1f             	shr    $0x1f,%ebx8048d1f:	01 d8                	add    %ebx,%eax8048d21:	d1 f8                	sar    %eax8048d23:	8d 1c 08             	lea    (%eax,%ecx,1),%ebx8048d26:	39 d3                	cmp    %edx,%ebx8048d28:	7e 15                	jle    8048d3f <func4+0x37>

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

img

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

 8048d2a:	83 ec 04             	sub    $0x4,%esp8048d2d:	8d 43 ff             	lea    -0x1(%ebx),%eax8048d30:	50                   	push   %eax8048d31:	51                   	push   %ecx8048d32:	52                   	push   %edx8048d33:	e8 d0 ff ff ff       	call   8048d08 <func4>

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

img

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

img

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

img

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

img

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

 8048d3f:	89 d8               mov    %ebx,%eax8048d41:	39 d3               cmp    %edx,%ebx8048d43:	7d 13               jge    8048d58 <func4+0x50>

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

img

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

 8048d58:	8d 65 f8             	lea    -0x8(%ebp),%esp8048d5b:	5b                  	pop    %ebx8048d5c:	5e                  	pop    %esi8048d5d:	5d                  	pop    %ebp8048d5e:	c3                  	ret   

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

 8048d33:	e8 d0 ff ff ff       	call   8048d08 <func4>8048d38:	83 c4 10             	add    $0x10,%esp8048d3b:	01 d8                	add    %ebx,%eax8048d3d:	eb 19                	jmp    8048d58 <func4+0x50>

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

img

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

之后回到phase_4的栈帧:

 8048da2:	e8 61 ff ff ff       	call   8048d08 <func4>8048da7:	83 c4 10             	add    $0x10,%esp8048daa:	83 f8 13             	cmp    $0x13,%eax8048dad:	75 06                	jne    8048db5 <phase_4+0x56>8048daf:	83 7d f0 13          	cmpl   $0x13,-0x10(%ebp)8048db3:	74 05                	je     8048dba <phase_4+0x5b>8048db5:	e8 cb 03 00 00       	call   8049185 <explode_bomb>

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

img

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

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

img

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

phase_5

贴代码:

08048dcd <phase_5>:8048dcd:	55                   	push   %ebp8048dce:	89 e5                	mov    %esp,%ebp8048dd0:	53                   	push   %ebx8048dd1:	83 ec 20             	sub    $0x20,%esp8048dd4:	8b 5d 08             	mov    0x8(%ebp),%ebx8048dd7:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax8048ddd:	89 45 f4             	mov    %eax,-0xc(%ebp)8048de0:	31 c0                	xor    %eax,%eax8048de2:	53                   	push   %ebx8048de3:	e8 78 02 00 00       	call   8049060 <string_length>8048de8:	83 c4 10             	add    $0x10,%esp8048deb:	83 f8 06             	cmp    $0x6,%eax8048dee:	74 05                	je     8048df5 <phase_5+0x28>8048df0:	e8 90 03 00 00       	call   8049185 <explode_bomb>8048df5:	b8 00 00 00 00       	mov    $0x0,%eax8048dfa:	0f b6 14 03          	movzbl (%ebx,%eax,1),%edx8048dfe:	83 e2 0f             	and    $0xf,%edx8048e01:	0f b6 92 74 a1 04 08 	movzbl 0x804a174(%edx),%edx8048e08:	88 54 05 ed          	mov    %dl,-0x13(%ebp,%eax,1)8048e0c:	83 c0 01             	add    $0x1,%eax8048e0f:	83 f8 06             	cmp    $0x6,%eax8048e12:	75 e6                	jne    8048dfa <phase_5+0x2d>8048e14:	c6 45 f3 00          	movb   $0x0,-0xd(%ebp)8048e18:	83 ec 08             	sub    $0x8,%esp8048e1b:	68 4b a1 04 08       	push   $0x804a14b8048e20:	8d 45 ed             	lea    -0x13(%ebp),%eax8048e23:	50                   	push   %eax8048e24:	e8 59 02 00 00       	call   8049082 <strings_not_equal>8048e29:	83 c4 10             	add    $0x10,%esp8048e2c:	85 c0                	test   %eax,%eax8048e2e:	74 05                	je     8048e35 <phase_5+0x68>8048e30:	e8 50 03 00 00       	call   8049185 <explode_bomb>8048e35:	8b 45 f4             	mov    -0xc(%ebp),%eax8048e38:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax8048e3f:	74 05                	je     8048e46 <phase_5+0x79>8048e41:	e8 4a f9 ff ff       	call   8048790 <__stack_chk_fail@plt>8048e46:	8b 5d fc             	mov    -0x4(%ebp),%ebx8048e49:	c9                   	leave  8048e4a:	c3                   	ret    

先看部分代码:

 8048de3:	e8 78 02 00 00       	call   8049060 <string_length>8048de8:	83 c4 10             	add    $0x10,%esp8048deb:	83 f8 06             	cmp    $0x6,%eax8048dee:	74 05                	je     8048df5 <phase_5+0x28>8048df0:	e8 90 03 00 00       	call   8049185 <explode_bomb>

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

img

img

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

接下来是一段循环:

 8048df5:	b8 00 00 00 00       	mov    $0x0,%eax8048dfa:	0f b6 14 03          	movzbl  (%ebx,%eax,1),%edx8048dfe:	83 e2 0f             	and    $0xf,%edx8048e01:	0f b6 92 74 a1 04 08 	movzbl  0x804a174(%edx),%edx8048e08:	88 54 05 ed          	mov    %dl,-0x13(%ebp,%eax,1)8048e0c:	83 c0 01             	add    $0x1,%eax8048e0f:	83 f8 06             	cmp    $0x6,%eax8048e12:	75 e6                	jne    8048dfa <phase_5+0x2d>

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

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

 8048e1b:	68 4b a1 04 08       	push   $0x804a14b8048e20:	8d 45 ed             	lea    -0x13(%ebp),%eax8048e23:	50                  	push   %eax8048e24:	e8 59 02 00 00       	call   8049082 <strings_not_equal>

六次循环得到一个字符串,并把这个字符串的首地址作为参数传给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 <phase_6>:8048e4b:	55                   	push   %ebp8048e4c:	89 e5                	mov    %esp,%ebp8048e4e:	56                   	push   %esi8048e4f:	53                   	push   %ebx8048e50:	83 ec 48             	sub    $0x48,%esp8048e53:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax8048e59:	89 45 f4             	mov    %eax,-0xc(%ebp)8048e5c:	31 c0                	xor    %eax,%eax8048e5e:	8d 45 c4             	lea    -0x3c(%ebp),%eax8048e61:	50                   	push   %eax8048e62:	ff 75 08             	push   0x8(%ebp)8048e65:	e8 43 03 00 00       	call   80491ad <read_six_numbers>8048e6a:	83 c4 10             	add    $0x10,%esp8048e6d:	be 00 00 00 00       	mov    $0x0,%esi8048e72:	8b 44 b5 c4          	mov    -0x3c(%ebp,%esi,4),%eax8048e76:	83 e8 01             	sub    $0x1,%eax8048e79:	83 f8 05             	cmp    $0x5,%eax8048e7c:	76 05                	jbe    8048e83 <phase_6+0x38>8048e7e:	e8 02 03 00 00       	call   8049185 <explode_bomb>8048e83:	83 c6 01             	add    $0x1,%esi8048e86:	83 fe 06             	cmp    $0x6,%esi8048e89:	74 33                	je     8048ebe <phase_6+0x73>8048e8b:	89 f3                	mov    %esi,%ebx8048e8d:	8b 44 9d c4          	mov    -0x3c(%ebp,%ebx,4),%eax8048e91:	39 44 b5 c0          	cmp    %eax,-0x40(%ebp,%esi,4)8048e95:	75 05                	jne    8048e9c <phase_6+0x51>8048e97:	e8 e9 02 00 00       	call   8049185 <explode_bomb>8048e9c:	83 c3 01             	add    $0x1,%ebx8048e9f:	83 fb 05             	cmp    $0x5,%ebx8048ea2:	7e e9                	jle    8048e8d <phase_6+0x42>8048ea4:	eb cc                	jmp    8048e72 <phase_6+0x27>8048ea6:	8b 52 08             	mov    0x8(%edx),%edx8048ea9:	83 c0 01             	add    $0x1,%eax8048eac:	39 c8                	cmp    %ecx,%eax8048eae:	75 f6                	jne    8048ea6 <phase_6+0x5b>8048eb0:	89 54 b5 dc          	mov    %edx,-0x24(%ebp,%esi,4)8048eb4:	83 c3 01             	add    $0x1,%ebx8048eb7:	83 fb 06             	cmp    $0x6,%ebx8048eba:	75 07                	jne    8048ec3 <phase_6+0x78>8048ebc:	eb 1c                	jmp    8048eda <phase_6+0x8f>8048ebe:	bb 00 00 00 00       	mov    $0x0,%ebx8048ec3:	89 de                	mov    %ebx,%esi8048ec5:	8b 4 c 9d c4          	mov    -0x3c(%ebp,%ebx,4),%ecx8048ec9:	b8 01 00 00 00       	mov    $0x1,%eax8048ece:	ba 3c c1 04 08       	mov    $0x804c13c,%edx8048ed3:	83 f9 01             	cmp    $0x1,%ecx8048ed6:	7f ce                	jg     8048ea6 <phase_6+0x5b>8048ed8:	eb d6                	jmp    8048eb0 <phase_6+0x65>8048eda:	8b 5d dc             	mov    -0x24(%ebp),%ebx8048edd:	8d 45 dc             	lea    -0x24(%ebp),%eax8048ee0:	8d 75 f0             	lea    -0x10(%ebp),%esi8048ee3:	89 d9                	mov    %ebx,%ecx8048ee5:	8b 50 04             	mov    0x4(%eax),%edx8048ee8:	89 51 08             	mov    %edx,0x8(%ecx)8048eeb:	83 c0 04             	add    $0x4,%eax8048eee:	89 d1                	mov    %edx,%ecx8048ef0:	39 f0                	cmp    %esi,%eax8048ef2:	75 f1                	jne    8048ee5 <phase_6+0x9a>8048ef4:	c7 42 08 00 00 00 00 	movl   $0x0,0x8(%edx)8048efb:	be 05 00 00 00       	mov    $0x5,%esi8048f00:	8b 43 08             	mov    0x8(%ebx),%eax8048f03:	8b 00                	mov    (%eax),%eax8048f05:	39 03                	cmp    %eax,(%ebx)8048f07:	7d 05                	jge    8048f0e <phase_6+0xc3>8048f09:	e8 77 02 00 00       	call   8049185 <explode_bomb>8048f0e:	8b 5b 08             	mov    0x8(%ebx),%ebx8048f11:	83 ee 01             	sub    $0x1,%esi8048f14:	75 ea                	jne    8048f00 <phase_6+0xb5>8048f16:	8b 45 f4             	mov    -0xc(%ebp),%eax8048f19:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax8048f20:	74 05                	je     8048f27 <phase_6+0xdc>8048f22:	e8 69 f8 ff ff       	call   8048790 <__stack_chk_fail@plt>8048f27:	8d 65 f8             	lea    -0x8(%ebp),%esp8048f2a:	5b                   	pop    %ebx8048f2b:	5e                   	pop    %esi8048f2c:	5d                   	pop    %ebp8048f2d:	c3                   	ret    

先看:

 8048e5e:	8d 45 c4             	lea    -0x3c(%ebp),%eax8048e61:	50                  	push   %eax8048e62:	ff 75 08             	push   0x8(%ebp)8048e65:	e8 43 03 00 00       	call   80491ad <read_six_numbers>

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

之后有一个判断:

 8048e6a:	83 c4 10             	add    $0x10,%esp8048e6d:	be 00 00 00 00       	mov    $0x0,%esi8048e72:	8b 44 b5 c4          	mov    -0x3c(%ebp,%esi,4),%eax8048e76:	83 e8 01             	sub    $0x1,%eax8048e79:	83 f8 05             	cmp    $0x5,%eax8048e7c:	76 05                	jbe    8048e83 <phase_6+0x38>8048e7e:	e8 02 03 00 00       	call   8049185 <explode_bomb>

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

 8048e83:	83 c6 01             	add    $0x1,%esi8048e86:	83 fe 06             	cmp    $0x6,%esi8048e89:	74 33                	je     8048ebe <phase_6+0x73>8048e8b:	89 f3                	mov    %esi,%ebx8048e8d:	8b 44 9d c4          	mov    -0x3c(%ebp,%ebx,4),%eax8048e91:	39 44 b5 c0          	cmp    %eax,-0x40(%ebp,%esi,4)8048e95:	75 05                	jne    8048e9c <phase_6+0x51>8048e97:	e8 e9 02 00 00       	call   8049185 <explode_bomb>

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

 8048e9c:	83 c3 01             	add    $0x1,%ebx8048e9f:	83 fb 05             	cmp    $0x5,%ebx8048ea2:	7e e9                	jle    8048e8d <phase_6+0x42>8048ea4:	eb cc                	jmp    8048e72 <phase_6+0x27>

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

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

 8048ea6:	8b 52 08             	mov    0x8(%edx),%edx8048ea9:	83 c0 01             	add    $0x1,%eax8048eac:	39 c8                	cmp    %ecx,%eax8048eae:	75 f6                	jne    8048ea6 <phase_6+0x5b>8048eb0:	89 54 b5 dc          	mov    %edx,-0x24(%ebp,%esi,4)8048eb4:	83 c3 01             	add    $0x1,%ebx8048eb7:	83 fb 06             	cmp    $0x6,%ebx8048eba:	75 07                	jne    8048ec3 <phase_6+0x78>8048ebc:	eb 1c                	jmp    8048eda <phase_6+0x8f>8048ebe:	bb 00 00 00 00       	mov    $0x0,%ebx8048ec3:	89 de                	mov    %ebx,%esi8048ec5:	8b 4 c 9d c4          	mov    -0x3c(%ebp,%ebx,4),%ecx8048ec9:	b8 01 00 00 00       	mov    $0x1,%eax8048ece:	ba 3c c1 04 08       	mov    $0x804c13c,%edx8048ed3:	83 f9 01             	cmp    $0x1,%ecx8048ed6:	7f ce                	jg     8048ea6 <phase_6+0x5b>8048ed8:	eb d6                	jmp    8048eb0 <phase_6+0x65>

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

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

img

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

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

img

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

img

链表一共有六个节点,每个节点存放三个数据,显然第三个数据是下一个节点的地址。第二个数据从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),%ebx8048edd:	8d 45 dc             	lea    -0x24(%ebp),%eax8048ee0:	8d 75 f0             	lea    -0x10(%ebp),%esi8048ee3:	89 d9                	mov    %ebx,%ecx8048ee5:	8b 50 04             	mov    0x4(%eax),%edx8048ee8:	89 51 08             	mov    %edx,0x8(%ecx)8048eeb:	83 c0 04             	add    $0x4,%eax8048eee:	89 d1                	mov    %edx,%ecx8048ef0:	39 f0                	cmp    %esi,%eax8048ef2:	75 f1                	jne    8048ee5 <phase_6+0x9a>

这段代码也是循环,会改变链表指向,通过多次调试可以发现:如果输入的是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

部分调试记录如下:

img

img

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

来到最后一段代码:

 8048ef4:	c7 42 08 00 00 00 00 	movl   $0x0,0x8(%edx)8048efb:	be 05 00 00 00       	mov    $0x5,%esi8048f00:	8b 43 08             	mov    0x8(%ebx),%eax8048f03:	8b 00               	mov    (%eax),%eax8048f05:	39 03                	cmp    %eax,(%ebx)8048f07:	7d 05               	jge    8048f0e <phase_6+0xc3>8048f09:	e8 77 02 00 00       	call   8049185 <explode_bomb>8048f0e:	8b 5b 08             	mov    0x8(%ebx),%ebx8048f11:	83 ee 01             	sub    $0x1,%esi8048f14:	75 ea                	jne    8048f00 <phase_6+0xb5>

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

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

img

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

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

secret_phase

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

08048f80 <secret_phase>:8048f80:	55                   	push   %ebp8048f81:	89 e5                	mov    %esp,%ebp8048f83:	53                   	push   %ebx8048f84:	83 ec 04             	sub    $0x4,%esp8048f87:	e8 5b 02 00 00       	call   80491e7 <read_line>8048f8c:	83 ec 04             	sub    $0x4,%esp8048f8f:	6a 0a                	push   $0xa8048f91:	6a 00                	push   $0x08048f93:	50                   	push   %eax8048f94:	e8 e7 f8 ff ff       	call   8048880 <strtol@plt>8048f99:	89 c3                	mov    %eax,%ebx8048f9b:	8d 40 ff             	lea    -0x1(%eax),%eax8048f9e:	83 c4 10             	add    $0x10,%esp8048fa1:	3d e8 03 00 00       	cmp    $0x3e8,%eax8048fa6:	76 05                	jbe    8048fad <secret_phase+0x2d>8048fa8:	e8 d8 01 00 00       	call   8049185 <explode_bomb>8048fad:	83 ec 08             	sub    $0x8,%esp8048fb0:	53                   	push   %ebx8048fb1:	68 88 c0 04 08       	push   $0x804c0888048fb6:	e8 73 ff ff ff       	call   8048f2e <fun7>8048fbb:	83 c4 10             	add    $0x10,%esp8048fbe:	83 f8 03             	cmp    $0x3,%eax8048fc1:	74 05                	je     8048fc8 <secret_phase+0x48>8048fc3:	e8 bd 01 00 00       	call   8049185 <explode_bomb>8048fc8:	83 ec 0c             	sub    $0xc,%esp8048fcb:	68 1c a1 04 08       	push   $0x804a11c8048fd0:	e8 eb f7 ff ff       	call   80487c0 <puts@plt>8048fd5:	e8 10 03 00 00       	call   80492ea <phase_defused>8048fda:	83 c4 10             	add    $0x10,%esp8048fdd:	8b 5d fc             	mov    -0x4(%ebp),%ebx8048fe0:	c9                   	leave  8048fe1:	c3                   	ret   

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

080492ea <phase_defused>:80492ea:	55                   	push   %ebp80492eb:	89 e5                	mov    %esp,%ebp80492ed:	83 ec 68             	sub    $0x68,%esp80492f0:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax80492f6:	89 45 f4             	mov    %eax,-0xc(%ebp)80492f9:	31 c0                	xor    %eax,%eax80492fb:	83 3d cc c3 04 08 06 	cmpl   $0x6,0x804c3cc8049302:	75 6f                	jne    8049373 <phase_defused+0x89>8049304:	83 ec 0c             	sub    $0xc,%esp8049307:	8d 45 a4             	lea    -0x5c(%ebp),%eax804930a:	50                   	push   %eax804930b:	8d 45 a0             	lea    -0x60(%ebp),%eax804930e:	50                   	push   %eax804930f:	8d 45 9c             	lea    -0x64(%ebp),%eax8049312:	50                   	push   %eax8049313:	68 ed a2 04 08       	push   $0x804a2ed8049318:	68 d0 c4 04 08       	push   $0x804c4d0804931d:	e8 ee f4 ff ff       	call   8048810 <__isoc99_sscanf@plt>8049322:	83 c4 20             	add    $0x20,%esp8049325:	83 f8 03             	cmp    $0x3,%eax8049328:	75 39                	jne    8049363 <phase_defused+0x79>804932a:	83 ec 08             	sub    $0x8,%esp804932d:	68 f6 a2 04 08       	push   $0x804a2f68049332:	8d 45 a4             	lea    -0x5c(%ebp),%eax8049335:	50                   	push   %eax8049336:	e8 47 fd ff ff       	call   8049082 <strings_not_equal>804933b:	83 c4 10             	add    $0x10,%esp804933e:	85 c0                	test   %eax,%eax8049340:	75 21                	jne    8049363 <phase_defused+0x79>8049342:	83 ec 0c             	sub    $0xc,%esp8049345:	68 bc a1 04 08       	push   $0x804a1bc804934a:	e8 71 f4 ff ff       	call   80487c0 <puts@plt>804934f:	c7 04 24 e4 a1 04 08 	movl   $0x804a1e4,(%esp)8049356:	e8 65 f4 ff ff       	call   80487c0 <puts@plt>804935b:	e8 20 fc ff ff       	call   8048f80 <secret_phase>8049360:	83 c4 10             	add    $0x10,%esp8049363:	83 ec 0c             	sub    $0xc,%esp8049366:	68 1c a2 04 08       	push   $0x804a21c804936b:	e8 50 f4 ff ff       	call   80487c0 <puts@plt>8049370:	83 c4 10             	add    $0x10,%esp8049373:	8b 45 f4             	mov    -0xc(%ebp),%eax8049376:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax804937d:	74 05                	je     8049384 <phase_defused+0x9a>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>

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

 8049312:	50                  	push   %eax8049313:	68 ed a2 04 08       	push   $0x804a2ed8049318:	68 d0 c4 04 08       	push   $0x804c4d0804931d:	e8 ee f4 ff ff       	call   8048810 <__isoc99_sscanf@plt>8049322:	83 c4 20             	add    $0x20,%esp8049325:	83 f8 03             	cmp    $0x3,%eax8049328:	75 39                	jne    8049363 <phase_defused+0x79>

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

img

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

 804932a:	83 ec 08             	sub    $0x8,%esp804932d:	68 f6 a2 04 08       	push   $0x804a2f68049332:	8d 45 a4             	lea    -0x5c(%ebp),%eax8049335:	50                  	push   %eax8049336:	e8 47 fd ff ff       	call   8049082 <strings_not_equal>804933b:	83 c4 10             	add    $0x10,%esp804933e:	85 c0                	test   %eax,%eax8049340:	75 21                	jne    8049363 <phase_defused+0x79>

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

img

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

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

img

回过头来对secret_phase进行分析:

 8048f8f:	6a 0a                	push   $0xa8048f91:	6a 00                	push   $0x08048f93:	50                  	push   %eax8048f94:	e8 e7 f8 ff ff       	call   8048880 <strtol@plt>8048f99:	89 c3                	mov    %eax,%ebx8048f9b:	8d 40 ff             	lea    -0x1(%eax),%eax8048f9e:	83 c4 10             	add    $0x10,%esp8048fa1:	3d e8 03 00 00       	cmp    $0x3e8,%eax8048fa6:	76 05                	jbe    8048fad <secret_phase+0x2d>8048fa8:	e8 d8 01 00 00       	call   8049185 <explode_bomb>

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

继续向下看;

 8048fb0:	53                  	push   %ebx8048fb1:	68 88 c0 04 08       	push   $0x804c0888048fb6:	e8 73 ff ff ff       	call   8048f2e <fun7>8048fbb:	83 c4 10             	add    $0x10,%esp8048fbe:	83 f8 03             	cmp    $0x3,%eax8048fc1:	74 05                	je     8048fc8 <secret_phase+0x48>8048fc3:	e8 bd 01 00 00       	call   8049185 <explode_bomb>8048fbb:	83 c4 10             	add    $0x10,%esp8048fbe:	83 f8 03             	cmp    $0x3,%eax8048fc1:	74 05                	je     8048fc8 <secret_phase+0x48>8048fc3:	e8 bd 01 00 00       	call   8049185 <explode_bomb>

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

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

img

img

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

img

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

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

08048f2e <fun7>:8048f2e:	55                   	push   %ebp8048f2f:	89 e5                	mov    %esp,%ebp8048f31:	53                   	push   %ebx8048f32:	83 ec 04             	sub    $0x4,%esp8048f35:	8b 55 08             	mov    0x8(%ebp),%edx8048f38:	8b 4d 0c             	mov    0xc(%ebp),%ecx8048f3b:	85 d2                	test   %edx,%edx8048f3d:	74 37                	je     8048f76 <fun7+0x48>8048f3f:	8b 1a                	mov    (%edx),%ebx8048f41:	39 cb                	cmp    %ecx,%ebx8048f43:	7e 13                	jle    8048f58 <fun7+0x2a>8048f45:	83 ec 08             	sub    $0x8,%esp8048f48:	51                   	push   %ecx8048f49:	ff 72 04             	push   0x4(%edx)8048f4c:	e8 dd ff ff ff       	call   8048f2e <fun7>8048f51:	83 c4 10             	add    $0x10,%esp8048f54:	01 c0                	add    %eax,%eax8048f56:	eb 23                	jmp    8048f7b <fun7+0x4d>8048f58:	b8 00 00 00 00       	mov    $0x0,%eax8048f5d:	39 cb                	cmp    %ecx,%ebx8048f5f:	74 1a                	je     8048f7b <fun7+0x4d>8048f61:	83 ec 08             	sub    $0x8,%esp8048f64:	51                   	push   %ecx8048f65:	ff 72 08             	push   0x8(%edx)8048f68:	e8 c1 ff ff ff       	call   8048f2e <fun7>8048f6d:	83 c4 10             	add    $0x10,%esp8048f70:	8d 44 00 01          	lea    0x1(%eax,%eax,1),%eax8048f74:	eb 05                	jmp    8048f7b <fun7+0x4d>8048f76:	b8 ff ff ff ff       	mov    $0xffffffff,%eax8048f7b:	8b 5d fc             	mov    -0x4(%ebp),%ebx8048f7e:	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;
}

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

img

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

最终目标是让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

测试一下完全通过:

img

相关文章:

【csapp lab】lab2_bomblab

文章目录 前言实验内容phase_1phase_2phase_3phase_4phase_5phase_6secret_phase 前言 刚做了csapp lab2&#xff0c;记录一下。 我这里用的的系统环境是Ubuntu22.04&#xff0c;是64位系统&#xff0c;与用32位系统可能有所差异。 实验共包括七个阶段&#xff0c;每个阶段考…...

开发者分享 | Ascend C算子开发及单算子调用

本文分享自《AscendC算子开发及单算子调用》&#xff0c;作者&#xff1a;goldpancake。 笔者在阅读Ascend C官方文档的过程中发现&#xff0c;对于初学者来说&#xff0c;尤其是第一次接触异构编程思想的初学者&#xff0c;有部分内容是无需特别关注的&#xff0c;例如算子工…...

如何在 Linux 上部署 RabbitMQ

如何在 Linux 上部署 RabbitMQ 文章目录 如何在 Linux 上部署 RabbitMQ安装 Erlang从预构建的二进制包安装从源代码编译 Erlang RabbitMQ 的安装使用 RabbitMQ Assistant 连接 RabbitMQ Assistant 是一款优秀的RabbitMQ 可视化管理工具&#xff0c;提供丰富的管理功能。下载地址…...

解决更换NodeJs版本后npm -v返回空白

一、问题描述 win11电脑上输入cmd进入控制台&#xff0c;输入 node --version 有正常返回安装的nodejs的版本号 再输入 npm -v 返回空白。正常情况应该是要返回版本号。 二、问题背景 最近准备学习vue&#xff0c;在不久前已经安装了NodeJs和python。运行了好几个开源项…...

【ES常用查询】基于ElasticsearchRestTemplate及NativeSearchQuery的查询

包含当前es所有的查询&#xff0c; 需要什么代码直接照搬&#xff0c;改个参数就行&#xff01; 用的好请务必给我点赞&#xff01;&#xff01;&#xff01;感谢爱你们&#xff01;&#xff01;&#xff01; &#xff08;周末更 筒&#xff09; 为啥写这篇文章呢&#xff…...

全志XR806基于http的无线ota功能实验

XR806不仅硬件功能多&#xff0c;XR806也提供了功能极其丰富的SDK&#xff0c;几天体验下来非常容易上手。常见的功能几乎都有相应的cmd或demo实现&#xff0c;HAL也做得非常全面&#xff0c;非常适合快速开发。这一点超级好评&#xff01;本文章要实现的无线OTA也基于该SDK。 …...

2023年11月15号期中测验选择题(Java)

本篇续接《2023年11月15号期中测验判断题&#xff08;Java&#xff09;》->传送门 2-1 以下程序运行结果是 public class Test extends Father{private String name"test";public static void main(String[] args){Test test new Test();System.out.println(tes…...

C# static关键字详解

在C#中&#xff0c;static关键字有许多重要的用途。以下是关于如何使用static关键字的一些详细信息&#xff1a; 静态类&#xff08;Static Classes&#xff09;&#xff1a;静态类是不能实例化的类&#xff0c;它的所有成员都是静态的。静态类常常用作工具类或帮助类&#xff…...

开发一款回合制游戏,需要注意什么?

随着游戏行业的蓬勃发展&#xff0c;回合制游戏因其深度的策略性和令人着迷的游戏机制而受到玩家们的热烈欢迎。如果你计划投身回合制游戏的开发领域&#xff0c;本文将为你提供一份详细的指南&#xff0c;从游戏设计到发布&#xff0c;助你成功打造一款引人入胜的游戏。 1. 游…...

java的包装类

目录 1. 包装类 1.1 基本数据类型和对应的包装类 1.2 装箱和拆箱 1.3 自动装箱和自动拆箱 1. 包装类 在Java中&#xff0c;由于基本类型不是继承自Object&#xff0c;为了在泛型代码中可以支持基本类型&#xff0c;Java给每个基本类型都对应了 一个包装类型。 若想了解…...

【数据结构(一)】线性结构和非线性结构

文章目录 线性结构和非线性结构1. 线性结构2. 非线性结构 线性结构和非线性结构 数据结构包括&#xff1a;线性结构和非线性结构。 1. 线性结构 线性结构作为最常用的数据结构&#xff0c;其特点是数据元素之间存在一对一的线性关系。线性结构有两种不同的存储结构&#xff…...

持续集成指南:GitHubAction 自动构建+部署AspNetCore项目

前言 之前研究了使用 GitHub Action 自动构建和发布 nuget 包&#xff1a;开发现代化的.NetCore控制台程序&#xff1a;(4)使用GithubAction自动构建以及发布nuget包 现在更进一步&#xff0c;使用 GitHub Action 在其提供的 runner 里构建 docker 镜像&#xff0c;之后提交到阿…...

Docker 笔记(三)--容器

Docker 笔记&#xff08;三&#xff09;–容器 记录Docker 安装操作记录&#xff0c;便于查询。 参考 链接: Docker 入门到实战教程(三)镜像和容器链接: docker run中的-itd参数正确使用链接: docker官方文档链接: 阿里云Debian 镜像链接: Debian 全球镜像站链接: Debian/Ub…...

gd32关于IO引脚配置的一些问题

一、gd32f103的PA15问题 1、 #define GPIO_SWJ_NONJTRST_REMAP ((uint32_t)0x00300100U) /*!< full SWJ(JTAG-DP SW-DP),but without NJTRST */ #define GPIO_SWJ_SWDPENABLE_REMAP ((uint32_t)0x00300200U) /*!< JTAG-DP disabled and SW-DP enab…...

QT小记:警告Use multi-arg instead

"Use multi-arg instead" 是一个提示&#xff0c;建议使用 QObject::tr() 函数的多参数版本来处理多个占位符&#xff0c;而不是使用单参数版本。 在 Qt 中&#xff0c;tr() 是用于进行文本翻译&#xff08;国际化&#xff09;的函数。它允许你在应用程序中使用多种…...

皮肤性病科专家谭巍主任提出HPV转阴后饮食七点建议

HPV转阴是每一位感染者都期盼的&#xff0c;因为转阴所以健康&#xff0c;只有转为阴性才意味着不具备传染性&#xff0c;从此也不必再害怕将病毒传染给家人的风险&#xff0c;也不必再担忧持续感染而引发的健康风险。总之&#xff0c;HPV转阴是预示感染者恢复健康与否的主要标…...

快速弄懂C++中的智能指针

智能指针是C中的一个对象&#xff0c;它的行为类似于指针&#xff0c;但它提供了自动的内存管理功能。当智能指针超出作用域时&#xff08;比如说在函数中使用智能指针指向了一个对象&#xff0c;当该函数结束时会自动销毁该对象&#xff09;&#xff0c;它会自动删除其所指向的…...

C#调用C++ dll教程

文章目录 一、创建C dll项目二、C#程序员调用C dll三、C与C#数据类型对应基本数据类型对应表C指针类型与C#类型 在使用C#开发客户端时&#xff0c;有时需要调用C dll&#xff0c;本篇博客来介绍C#程序如何调用C dll。 一、创建C dll项目 首先使用VS2022创建C dll项目&#xf…...

计算机毕设 深度学习 大数据 股票预测系统 - python lstm

文章目录 0 前言1 课题意义1.1 股票预测主流方法 2 什么是LSTM2.1 循环神经网络2.1 LSTM诞生 2 如何用LSTM做股票预测2.1 算法构建流程2.2 部分代码 3 实现效果3.1 数据3.2 预测结果项目运行展示开发环境数据获取 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要…...

97.qt qml-自定义Table之实现ctrl与shift多选

我们之前实现了:93.qt qml-自定义Table优化(新增:水平拖拽/缩放自适应/选择使能/自定义委托)-CSDN博客 实现选择使能的时候,我们只能一行行去点击选中,非常麻烦,所以本章我们实现ctrl多选与shift多选、 所以在Table控件新增两个属性: 1.实现介绍 ctrl多选实现原理:当我…...

运行软件报错mfc140.dll丢失?分享mfc140.dll丢失的解决方法

小伙伴们&#xff0c;你是否也有过这样的经历&#xff1a;每当碰到诸如" mfc140.dll 丢失 "之类的烦人错误时&#xff0c;你是不是会一头雾水&#xff0c;完全不知道从何下手去解决&#xff1f;不要担心&#xff0c;接下来咱就给你提供这样一篇实用教程&#xff0c;教…...

milvus数据库-连接

Milvus 支持 19530 和 9091 两个端口&#xff1a; 端口 19530 用于 gRPC 和 RESTful API。 这是您使用不同 Milvus SDK 或 HTTP 客户端连接到 Milvus 服务器时的默认端口。 端口 9091 用于 Kubernetes 内的指标收集、pprof 分析和运行状况探测。 它用作管理端口。 1.连接到数…...

ios + vue3 Teleport + inset 兼容性问题

目录 1&#xff0c;问题表现2&#xff0c;解决步骤1&#xff0c;teleport 的问题2&#xff0c;inset 的问题3&#xff0c;teleport 的问题之二 1&#xff0c;问题表现 使用 vue3 的 Teleport 实现的 dialog 弹窗&#xff0c;但是在 ios app 中嵌套的 h5 中无法打开。 直接在io…...

计蒜客T1654 数列分段(C语言实现)

【题目描述】对于给定的一个长度为n的正整数数列ai&#xff0c;现要将其分成连续的若干段&#xff0c;并且每段和不超过m&#xff08;可以等于m&#xff09;&#xff0c;问最少能将其分成多少段使得满足要求。 【输入格式】第一行包含两个正整数n&#xff0c;m&#xff0c;表示…...

Linux进程——system函数、popen函数

system函数&#xff08;执行shell 命令&#xff09; 头文件 #include <stdlib.h> 函数定义 int system(const char * string); 函数说明 system()会调用fork()产生子进程&#xff0c;由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令&#xff0c;…...

【智能家居】5、主流程设计以及外设框架编写与测试

目录 一、主流程设计 1、工厂模式结构体定义 &#xff08;1&#xff09;指令工厂 inputCmd.h &#xff08;2&#xff09;外设工厂 controlDevices.h 二、外设框架编写 1、创建外设工厂对象bathroomLight 2、编写相关函数框架 3、将浴室灯相关操作插入外设工厂链表等待被调…...

详解ssh远程登录服务

华子目录 简介概念功能 分类文字接口图形接口 文字接口ssh连接服务器浅浅介绍一下加密技术凯撒加密加密分类对称加密非对称加密非对称加密方法&#xff08;也叫公钥加密&#xff09; ssh两大类认证方式&#xff1a;连接加密技术简介密钥解析 ssh工作过程版本协商阶段密钥和算法…...

LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄

接着前两节的Langchain&#xff0c;继续实现Langchain中的Agent LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字 代码实现 # 从langchain库中导入模块 from langchain.llms import OpenAI # 从langchain.l…...

wpf devexpress绑定grid到总计和分组统计

此主题描述了如何在gridcontrol中的视图模型和显示定义总计和分组统计 在视图模型中指定统计 1、创建 SummaryItemType 枚举你想要在GridControl中显示的统计类型&#xff1a; public enum SummaryItemType { Max, Count, None } 2、创建一个grid统计描述类 public class S…...

嵌入式 Linux 移植与系统启动方法

1、Linux系统启动与U-Boot 所谓移植就是把程序代码从一种运行环境转移到另一种运行环境。对于内核移植来说&#xff0c;主要是从一种硬件平台转移到另一种硬件平台上运行。 体系结构级别的移植是指在不同体系结构平台上Linux内核的移植&#xff0c;例如&#xff0c;在ARM、MI…...