CSAPP LAB Binary bombs实验报告

CSAPP LAB Binary bombs实验报告

更好的阅读体验

一、实验目的:

逆向工程拆除“二进制炸弹”程序

增强对程序机器级表示、汇编语言、调试器和逆向工程等 理解。

一个“Binary Bombs”(二进制炸弹,简称炸弹)是一个 Linux可执行C程序,包含phase1~phase6共6个阶段。

炸弹运行各阶段要求输入一个字符串,若输入符合程序预 期,该阶段炸弹被“拆除”,否则“爆炸”。

你需要拆除尽可能多的炸弹

二、实验要求

拆弹装备:

熟练使用gdb调试器和objdump;

单步跟踪调试每一阶段的机器代码;

理解汇编语言代码的行为或作用;

“推断”拆除炸弹所需的目标字符串。

在各阶段的开始代码前和引爆炸弹函数前设置断点,便于调试。

每个炸弹阶段考察机器级语言程序不同方面,难度递增

阶段1:字符串比较

阶段2:循环

阶段3:条件/分支:含switch语句

阶段4:递归调用和栈

阶段5:指针

阶段6:链表/指针/结构

隐藏阶段,第4阶段之后附加特定字符串后出现

三、实验内容(所修改函数代码,功能以及重要代码的解释):

phase_1

前期准备

objdump –d bomb > asm.txt

进入asm.txt文件,找到main函数和phase_1函数,代码和我的注释如下

080489db <main>:
....
 8048a6d:	c7 04 24 48 a0 04 08 	movl   $0x804a048,(%esp)
 8048a74:	e8 47 fd ff ff       	call   80487c0 <puts@plt>
 8048a79:	e8 2e 07 00 00       	call   80491ac <read_line>   //读入字符串
 8048a7e:	89 04 24             	mov    %eax,(%esp)
 8048a81:	e8 aa 00 00 00       	call   8048b30 <phase_1>  //调用phase_1
 8048a86:	e8 24 08 00 00       	call   80492af <phase_defused>
....


08048b30 <phase_1>:
 8048b30:	55                   	push   %ebp
 8048b31:	89 e5                	mov    %esp,%ebp
 8048b33:	83 ec 10             	sub    $0x10,%esp
 8048b36:	68 c4 a0 04 08       	push   $0x804a0c4               //将某个字符串的地址压栈
 8048b3b:	ff 75 08             	pushl  0x8(%ebp)               //将我们输入的字符串地址压栈
 8048b3e:	e8 04 05 00 00       	call   8049047 <strings_not_equal>  //比较两个字符串
 8048b43:	83 c4 10             	add    $0x10,%esp                   
 8048b46:	85 c0                	test   %eax,%eax             //eax必须为0,说明两个字符串应该相等
 8048b48:	74 05                	je     8048b4f <phase_1+0x1f>
 8048b4a:	e8 fb 05 00 00       	call   804914a <explode_bomb>
 8048b4f:	c9                   	leave  
 8048b50:	c3                   	ret    

所以我们直接用gdb调试查看0x804a0c4处的字符串

屏幕截图 2021-12-25 192247.JPG

通过

image-20211225192514179.png

phase_2

phase_2主要考察循环,具体代码和注释如下

08048b51 <phase_2>:
 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             	pushl  0x8(%ebp)
 8048b6a:	e8 03 06 00 00       	call   8049172 <read_six_numbers> //字面意思,说明是读入六个数
 8048b6f:	83 c4 10             	add    $0x10,%esp 
 8048b72:	83 7d dc 00          	cmpl   $0x0,-0x24(%ebp)  
 8048b76:	79 05                	jns    8048b7d <phase_2+0x2c>   //非负跳转,不能跳,所以第一个数不能是负数
 8048b78:	e8 cd 05 00 00       	call   804914a <explode_bomb>   
 8048b7d:	bb 01 00 00 00       	mov    $0x1,%ebx      
 8048b82:	89 d8                	mov    %ebx,%eax                 //循环从这里开始,eax的值为现在是第几次循环
 8048b84:	03 44 9d d8          	add    -0x28(%ebp,%ebx,4),%eax  //输入第i-1个的值加到eax
 8048b88:	39 44 9d dc          	cmp    %eax,-0x24(%ebp,%ebx,4)  //第i个值与eax比较
 8048b8c:	74 05                	je     8048b93 <phase_2+0x42>   //相等跳
 8048b8e:	e8 b7 05 00 00       	call   804914a <explode_bomb>
 8048b93:	83 c3 01             	add    $0x1,%ebx                  //ebx从1加到6
 8048b96:	83 fb 06             	cmp    $0x6,%ebx
 8048b99:	75 e7                	jne    8048b82 <phase_2+0x31>
 8048b9b:	8b 45 f4             	mov    -0xc(%ebp),%eax                                      
 8048b9e:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax//这六个数需要满足,第一个数非负,第i+1个数减去第i个数的值为i
 8048ba5:	74 05                	je     8048bac <phase_2+0x5b>               //2 3 5 8 12 17为一组答案
 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    

不难看出,输入的六个数字需满足:1. 第一个数非负 2. 第i+1个数减去第i个数的值为i

2 3 5 8 12 17为一组答案

image-20211225193003874.png

phase_3

phase_3主要考察switch语句,具体代码和注释如下

08048bb1 <phase_3>:
 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 1e a1 04 08       	push   $0x804a11e
 8048bd3:	ff 75 08             	pushl  0x8(%ebp)
 8048bd6:	e8 35 fc ff ff       	call   8048810 <__isoc99_sscanf@plt>  //gdb调试发现是读入两个数,一个字符
 8048bdb:	83 c4 20             	add    $0x20,%esp                   //读入的第一个数在-0x14(%ebp)里面
 8048bde:	83 f8 02             	cmp    $0x2,%eax
 8048be1:	7f 05                	jg     8048be8 <phase_3+0x37>      
 8048be3:	e8 62 05 00 00       	call   804914a <explode_bomb>    
 8048be8:	83 7d ec 07          	cmpl   $0x7,-0x14(%ebp) 
 8048bec:	0f 87 e9 00 00 00    	ja     8048cdb <phase_3+0x12a>   //将第一个数和7比较,大于跳转到炸弹,说明的值小于7
 8048bf2:	8b 45 ec             	mov    -0x14(%ebp),%eax        
 8048bf5:	ff 24 85 40 a1 04 08 	jmp    *0x804a140(,%eax,4)    //相当于Switch语句,跳转到0x804a140+%eax*4
 8048bfc:	b8 7a 00 00 00       	mov    $0x7a,%eax       //eax的值是根据第一个值跳转到不同的 case 得到的
 8048c01:	81 7d f0 71 02 00 00 	cmpl   $0x271,-0x10(%ebp)   //第三个输入的值,应该与0x271相等 625
 8048c08:	0f 84 d7 00 00 00    	je     8048ce5 <phase_3+0x134>
 8048c0e:	e8 37 05 00 00       	call   804914a <explode_bomb>
 8048c13:	b8 7a 00 00 00       	mov    $0x7a,%eax
 8048c18:	e9 c8 00 00 00       	jmp    8048ce5 <phase_3+0x134>
 8048c1d:	b8 62 00 00 00       	mov    $0x62,%eax
 8048c22:	81 7d f0 a7 02 00 00 	cmpl   $0x2a7,-0x10(%ebp)
 8048c29:	0f 84 b6 00 00 00    	je     8048ce5 <phase_3+0x134>
 8048c2f:	e8 16 05 00 00       	call   804914a <explode_bomb>
 8048c34:	b8 62 00 00 00       	mov    $0x62,%eax
 8048c39:	e9 a7 00 00 00       	jmp    8048ce5 <phase_3+0x134>
 8048c3e:	b8 70 00 00 00       	mov    $0x70,%eax
 8048c43:	83 7d f0 4a          	cmpl   $0x4a,-0x10(%ebp)
 8048c47:	0f 84 98 00 00 00    	je     8048ce5 <phase_3+0x134>
 8048c4d:	e8 f8 04 00 00       	call   804914a <explode_bomb>
 8048c52:	b8 70 00 00 00       	mov    $0x70,%eax
 8048c57:	e9 89 00 00 00       	jmp    8048ce5 <phase_3+0x134>
 8048c5c:	b8 67 00 00 00       	mov    $0x67,%eax
 8048c61:	81 7d f0 a8 00 00 00 	cmpl   $0xa8,-0x10(%ebp)
 8048c68:	74 7b                	je     8048ce5 <phase_3+0x134>
 8048c6a:	e8 db 04 00 00       	call   804914a <explode_bomb>
 8048c6f:	b8 67 00 00 00       	mov    $0x67,%eax
 8048c74:	eb 6f                	jmp    8048ce5 <phase_3+0x134>
 8048c76:	b8 73 00 00 00       	mov    $0x73,%eax
 8048c7b:	81 7d f0 e6 02 00 00 	cmpl   $0x2e6,-0x10(%ebp)
 8048c82:	74 61                	je     8048ce5 <phase_3+0x134>
 8048c84:	e8 c1 04 00 00       	call   804914a <explode_bomb>
 8048c89:	b8 73 00 00 00       	mov    $0x73,%eax
 8048c8e:	eb 55                	jmp    8048ce5 <phase_3+0x134>
 8048c90:	b8 76 00 00 00       	mov    $0x76,%eax
 8048c95:	81 7d f0 0e 03 00 00 	cmpl   $0x30e,-0x10(%ebp)
 8048c9c:	74 47                	je     8048ce5 <phase_3+0x134>
 8048c9e:	e8 a7 04 00 00       	call   804914a <explode_bomb>
 8048ca3:	b8 76 00 00 00       	mov    $0x76,%eax
 8048ca8:	eb 3b                	jmp    8048ce5 <phase_3+0x134>
 8048caa:	b8 6f 00 00 00       	mov    $0x6f,%eax
 8048caf:	81 7d f0 e7 03 00 00 	cmpl   $0x3e7,-0x10(%ebp)
 8048cb6:	74 2d                	je     8048ce5 <phase_3+0x134>
 8048cb8:	e8 8d 04 00 00       	call   804914a <explode_bomb>
 8048cbd:	b8 6f 00 00 00       	mov    $0x6f,%eax
 8048cc2:	eb 21                	jmp    8048ce5 <phase_3+0x134>
 8048cc4:	b8 71 00 00 00       	mov    $0x71,%eax
 8048cc9:	83 7d f0 4a          	cmpl   $0x4a,-0x10(%ebp)
 8048ccd:	74 16                	je     8048ce5 <phase_3+0x134>
 8048ccf:	e8 76 04 00 00       	call   804914a <explode_bomb>
 8048cd4:	b8 71 00 00 00       	mov    $0x71,%eax
 8048cd9:	eb 0a                	jmp    8048ce5 <phase_3+0x134>
 8048cdb:	e8 6a 04 00 00       	call   804914a <explode_bomb>            
 8048ce0:	b8 7a 00 00 00       	mov    $0x7a,%eax
 8048ce5:	3a 45 eb             	cmp    -0x15(%ebp),%al          //switch语句,最后把ebp-0x15的值(就是读到的字符)与al比较,需要相等
 8048ce8:	74 05                	je     8048cef <phase_3+0x13e>     //这道题我跳转到了第一个选项中,所以al为1111010,ascii为z
 8048cea:	e8 5b 04 00 00       	call   804914a <explode_bomb>
 8048cef:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048cf2:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048cf9:	74 05                	je     8048d00 <phase_3+0x14f>
 8048cfb:	e8 90 fa ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048d00:	c9                   	leave  
 8048d01:	c3                   	ret    

注意到:

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

gdb 调试,找到0x804a11e指向的字符串,发现是读入两个数,一个字符

屏幕截图 2021-12-15 000350.JPG

又因为:

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

将第一个数和7比较,大于跳转到炸弹,说明第一个值小于7

 8048bf2:	8b 45 ec             	mov    -0x14(%ebp),%eax        
 8048bf5:	ff 24 85 40 a1 04 08 	jmp    *0x804a140(,%eax,4)    //相当于Switch语句,跳转到0x804a140+%eax*4
 8048bfc:	b8 7a 00 00 00       	mov    $0x7a,%eax       

相当于Switch语句,跳转到0x804a140+%eax4,所以查看0x804a140+%eax4的内容

image-20211225194112977.png

不妨让第一个数为0,跳到0x8048bfc

 8048bfc:	b8 7a 00 00 00       	mov    $0x7a,%eax       
 8048c01:	81 7d f0 71 02 00 00 	cmpl   $0x271,-0x10(%ebp)   
 8048c08:	0f 84 d7 00 00 00    	je     8048ce5 <phase_3+0x134>
 8048c0e:	e8 37 05 00 00       	call   804914a <explode_bomb>

现在eax为0x7a。第三个输入的值,应该与0x271相等,为625

最后:

 8048ce5:	3a 45 eb             	cmp    -0x15(%ebp),%al                
 8048ce8:	74 05                	je     8048cef <phase_3+0x13e>        
 8048cea:	e8 5b 04 00 00       	call   804914a <explode_bomb>

最后把读到的字符与al比较,需要相等(之前我们的eax等于0x7a),对应ascii为z

答案:0 z 625

image-20211225194948737.png

phase_4

phase_4主要考察递归调用和栈,还是挺费劲的TwT

08048d02 <func4>:
 8048d02:	55                   	push   %ebp
 8048d03:	89 e5                	mov    %esp,%ebp     //建立新的栈帧
 8048d05:	56                   	push   %esi
 8048d06:	53                   	push   %ebx        
 8048d07:	8b 55 08             	mov    0x8(%ebp),%edx   //edx=第一个数记为a
 8048d0a:	8b 4d 0c             	mov    0xc(%ebp),%ecx    //ecx=0(首次调用),其余记为b
 8048d0d:	8b 75 10             	mov    0x10(%ebp),%esi   //esi=14(首次调用),其余记为c
 8048d10:	89 f0                	mov    %esi,%eax   //eax=c
 8048d12:	29 c8                	sub    %ecx,%eax   //eax=b+c
 8048d14:	89 c3                	mov    %eax,%ebx  //ebx=b+c
 8048d16:	c1 eb 1f             	shr    $0x1f,%ebx  //逻辑右移31位,相当于去b+c符号,记为s
 8048d19:	01 d8                	add    %ebx,%eax  //eax=b+c+s
 8048d1b:	d1 f8                	sar    %eax      //算数右移一位,eax=(b+c+s)/2
 8048d1d:	8d 1c 08             	lea    (%eax,%ecx,1),%ebx  //ebx=(b+c+s)/2+b,记为f
 8048d20:	39 d3                	cmp    %edx,%ebx    //比较edx和ebx
 8048d22:	7e 15                	jle    8048d39 <func4+0x37>  //如果ebx<=edx,即f<=a,跳到第三块,否则继续第二块

 8048d24:	83 ec 04             	sub    $0x4,%esp   
 8048d27:	8d 43 ff             	lea    -0x1(%ebx),%eax  //eax=f-1
 8048d2a:	50                   	push   %eax 
 8048d2b:	51                   	push   %ecx
 8048d2c:	52                   	push   %edx
 8048d2d:	e8 d0 ff ff ff       	call   8048d02 <func4> //递归调用,参数:a,b,f-1
 8048d32:	83 c4 10             	add    $0x10,%esp   
 8048d35:	01 d8                	add    %ebx,%eax   //eax=f+返回值
 8048d37:	eb 19                	jmp    8048d52 <func4+0x50>//跳到第四块

 8048d39:	89 d8                	mov    %ebx,%eax  //eax=f
 8048d3b:	39 d3                	cmp    %edx,%ebx 
 8048d3d:	7d 13                	jge    8048d52 <func4+0x50>//如果f>=a,跳转第四块
 8048d3f:	83 ec 04             	sub    $0x4,%esp  
 8048d42:	56                   	push   %esi   
 8048d43:	8d 43 01             	lea    0x1(%ebx),%eax
 8048d46:	50                   	push   %eax
 8048d47:	52                   	push   %edx
 8048d48:	e8 b5 ff ff ff       	call   8048d02 <func4>//递归调用,参数:a,f,c
 8048d4d:	83 c4 10             	add    $0x10,%esp
 8048d50:	01 d8                	add    %ebx,%eax  //eax=f+返回值

 8048d52:	8d 65 f8             	lea    -0x8(%ebp),%esp  //
 8048d55:	5b                   	pop    %ebx
 8048d56:	5e                   	pop    %esi  //c
 8048d57:	5d                   	pop    %ebp
 8048d58:	c3                   	ret      

08048d59 <phase_4>:
 8048d59:	55                   	push   %ebp
 8048d5a:	89 e5                	mov    %esp,%ebp
 8048d5c:	83 ec 18             	sub    $0x18,%esp
 8048d5f:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8048d65:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048d68:	31 c0                	xor    %eax,%eax
 8048d6a:	8d 45 f0             	lea    -0x10(%ebp),%eax
 8048d6d:	50                   	push   %eax
 8048d6e:	8d 45 ec             	lea    -0x14(%ebp),%eax
 8048d71:	50                   	push   %eax
 8048d72:	68 af a2 04 08       	push   $0x804a2af
 8048d77:	ff 75 08             	pushl  0x8(%ebp)
 8048d7a:	e8 91 fa ff ff       	call   8048810 <__isoc99_sscanf@plt>     //读入两个数,第一个在-0x14(%ebp),二-0x10(%ebp)
 8048d7f:	83 c4 10             	add    $0x10,%esp
 8048d82:	83 f8 02             	cmp    $0x2,%eax                 
 8048d85:	75 06                	jne    8048d8d <phase_4+0x34>   //需要相等,eax=2
 8048d87:	83 7d ec 0e          	cmpl   $0xe,-0x14(%ebp)
 8048d8b:	76 05                	jbe    8048d92 <phase_4+0x39>  //-0x14(%ebp)小于等于15
 8048d8d:	e8 b8 03 00 00       	call   804914a <explode_bomb>
 8048d92:	83 ec 04             	sub    $0x4,%esp
 8048d95:	6a 0e                	push   $0xe        //14
 8048d97:	6a 00                	push   $0x0       //0
 8048d99:	ff 75 ec             	pushl  -0x14(%ebp) //调用函数传参 14 0 第一个数
 8048d9c:	e8 61 ff ff ff       	call   8048d02 <func4> 
 8048da1:	83 c4 10             	add    $0x10,%esp  
 8048da4:	83 f8 12             	cmp    $0x12,%eax    //返回值为18
 8048da7:	75 06                	jne    8048daf <phase_4+0x56>
 8048da9:	83 7d f0 12          	cmpl   $0x12,-0x10(%ebp) //18相等于-0x10(%ebp)(第二个数)
 8048dad:	74 05                	je     8048db4 <phase_4+0x5b>
 8048daf:	e8 96 03 00 00       	call   804914a <explode_bomb>
 8048db4:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048db7:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax          
 8048dbe:	74 05                	je     8048dc5 <phase_4+0x6c>  
 8048dc0:	e8 cb f9 ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048dc5:	c9                   	leave  
 8048dc6:	c3                   	ret    

屏幕截图 2021-12-15 003051.JPG

大概意思就是读入两个数,第一个数是func4()函数的一个参数,要求func4()返回值为18;第二个数为18就行了。

具体的细节我写到注释里面了。

需要把func4()函数用高级语言写出来,再枚举输入来得到答案:

#include<bits/stdc++.h>
using namespace std;
int wzynb(int c,int b,int a){
    int s;
    int f=b+c;
    if(f<0){
        f-=1;
    }else{
        f+=1;
    }
    f/=2;

    if(f<a){
        int na=f;
        int rt=wzynb(c,f+1,a);
        f+=rt;
    }else if(f==a){
        
    }else{
        int nw=f-1;
        int rt=wzynb(nw,b,a);
        f+=rt;
    }
    return f;
}
int main()
{
    for(int i=0;i<=15;i++){
        if(wzynb(14,0,i)==18){
            cout<<i<<endl;
        }
    }
}

运行结果:11

image-20211225195630241.png

答案:11 18 (DrEvil是后面进入隐藏关的内容)

image-20211225195740024.png

phase_5

phase_5主要考察指针相关知识

08048dc7 <phase_5>:
 8048dc7:	55                   	push   %ebp
 8048dc8:	89 e5                	mov    %esp,%ebp
 8048dca:	53                   	push   %ebx
 8048dcb:	83 ec 10             	sub    $0x10,%esp  
 8048dce:	8b 5d 08             	mov    0x8(%ebp),%ebx  //ebx为读入数据的地址
 8048dd1:	53                   	push   %ebx  
 8048dd2:	e8 4e 02 00 00       	call   8049025 <string_length>
 8048dd7:	83 c4 10             	add    $0x10,%esp
 8048dda:	83 f8 06             	cmp    $0x6,%eax  //读入字符串长度为6
 8048ddd:	74 05                	je     8048de4 <phase_5+0x1d>
 8048ddf:	e8 66 03 00 00       	call   804914a <explode_bomb>
 8048de4:	89 d8                	mov    %ebx,%eax   //eax为读入数据的地址
 8048de6:	83 c3 06             	add    $0x6,%ebx  //ebx+6
 8048de9:	b9 00 00 00 00       	mov    $0x0,%ecx //ecx=0

 8048dee:	0f b6 10             	movzbl (%eax),%edx  //将edx后面四位 = eax字符数组的第i个 i为第几次循环
 8048df1:	83 e2 0f             	and    $0xf,%edx  //取edx后四位
 8048df4:	03 0c 95 60 a1 04 08 	add    0x804a160(,%edx,4),%ecx //ecx+= (edx*4+0x804a160)
 8048dfb:	83 c0 01             	add    $0x1,%eax  //eax++  
 8048dfe:	39 d8                	cmp    %ebx,%eax   //循环六次
 8048e00:	75 ec                	jne    8048dee <phase_5+0x27>

 8048e02:	83 f9 37             	cmp    $0x37,%ecx  //ecx=55  一种凑法:edx=2,3,4,5,6,C
 8048e05:	74 05                	je     8048e0c <phase_5+0x45> //一个答案是:    
                                                           //b(0x62),c(0x63),d(0x64),e(0x65),f(0x66),l(0x6C)
 8048e07:	e8 3e 03 00 00       	call   804914a <explode_bomb>  //bcdefl
 8048e0c:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048e0f:	c9                   	leave  
 8048e10:	c3                   	ret    

大概意思是读入六个字符,将它们的后面四位作为变址,使得所对应的六个数加起来等于55,一种凑法如下。

屏幕截图 2021-12-21 150626.JPG

后四位:2,3,4,5,6,C

一个答案是:b(0x62),c(0x63),d(0x64),e(0x65),f(0x66),l(0x6C)

image.png

phase_6

phase_6主要涉及链表

08048e11 <phase_6>:
//一
 8048e11:	55                   	push   %ebp
 8048e12:	89 e5                	mov    %esp,%ebp
 8048e14:	56                   	push   %esi
 8048e15:	53                   	push   %ebx
 8048e16:	83 ec 48             	sub    $0x48,%esp
 8048e19:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8048e1f:	89 45 f4             	mov    %eax,-0xc(%ebp)
 8048e22:	31 c0                	xor    %eax,%eax
 8048e24:	8d 45 c4             	lea    -0x3c(%ebp),%eax
 8048e27:	50                   	push   %eax
 8048e28:	ff 75 08             	pushl  0x8(%ebp)
 8048e2b:	e8 42 03 00 00       	call   8049172 <read_six_numbers>  //读入六个数
 8048e30:	83 c4 10             	add    $0x10,%esp  
 8048e33:	be 00 00 00 00       	mov    $0x0,%esi  //esi=0
//二
 8048e38:	8b 44 b5 c4          	mov    -0x3c(%ebp,%esi,4),%eax
 8048e3c:	83 e8 01             	sub    $0x1,%eax   
 8048e3f:	83 f8 05             	cmp    $0x5,%eax
 8048e42:	76 05                	jbe    8048e49 <phase_6+0x38>  //数据全部<=6
 8048e44:	e8 01 03 00 00       	call   804914a <explode_bomb>
 8048e49:	83 c6 01             	add    $0x1,%esi   //esi++
 8048e4c:	83 fe 06             	cmp    $0x6,%esi  
 8048e4f:	74 33                	je     8048e84 <phase_6+0x73>//esi==6时跳出循环,到第五块
 8048e51:	89 f3                	mov    %esi,%ebx //ebx=esi
 8048e53:	8b 44 9d c4          	mov    -0x3c(%ebp,%ebx,4),%eax   //eax装入第i个数
 8048e57:	39 44 b5 c0          	cmp    %eax,-0x40(%ebp,%esi,4) //a[i]与a[i-1]比较
 8048e5b:	75 05                	jne    8048e62 <phase_6+0x51>  //不能相等
 8048e5d:	e8 e8 02 00 00       	call   804914a <explode_bomb>
 8048e62:	83 c3 01             	add    $0x1,%ebx  //ebx=i+1
 8048e65:	83 fb 05             	cmp    $0x5,%ebx
 8048e68:	7e e9                	jle    8048e53 <phase_6+0x42>  //i+1小于或等于5
 8048e6a:	eb cc                	jmp    8048e38 <phase_6+0x27>  //这是一个双重for循环,要求每个数组元素不相同
//三
 8048e6c:	8b 52 08             	mov    0x8(%edx),%edx  //下一个节点的首地址
 8048e6f:	83 c0 01             	add    $0x1,%eax  //eax+1(从1开始)
 8048e72:	39 c8                	cmp    %ecx,%eax  //ecx=eax
 8048e74:	75 f6                	jne    8048e6c <phase_6+0x5b>  //不相等继续,直到eax=a[i],相当于进入了链表第a[i]个元素
//四
 8048e76:	89 54 b5 dc          	mov    %edx,-0x24(%ebp,%esi,4)  //记录下地址
 8048e7a:	83 c3 01             	add    $0x1,%ebx //i++
 8048e7d:	83 fb 06             	cmp    $0x6,%ebx
 8048e80:	75 07                	jne    8048e89 <phase_6+0x78>  //i不等于6跳五块
 8048e82:	eb 1c                	jmp    8048ea0 <phase_6+0x8f>  //否则跳六块
 //五
 8048e84:	bb 00 00 00 00       	mov    $0x0,%ebx  //ebx=esi=0,从零开始
 8048e89:	89 de                	mov    %ebx,%esi
 8048e8b:	8b 4c 9d c4          	mov    -0x3c(%ebp,%ebx,4),%ecx //ecx=a[i]
 8048e8f:	b8 01 00 00 00       	mov    $0x1,%eax  //eax=1
 8048e94:	ba 3c c1 04 08       	mov    $0x804c13c,%edx  //链表头地址
 8048e99:	83 f9 01             	cmp    $0x1,%ecx
 8048e9c:	7f ce                	jg     8048e6c <phase_6+0x5b>  //a[i]>1跳第三块
 8048e9e:	eb d6                	jmp    8048e76 <phase_6+0x65> //a[i]<=1跳第四块
//六
 8048ea0:	8b 5d dc             	mov    -0x24(%ebp),%ebx //ebx链表地址
 8048ea3:	8d 45 dc             	lea    -0x24(%ebp),%eax //eax头地址
 8048ea6:	8d 75 f0             	lea    -0x10(%ebp),%esi //esi尾地址
 8048ea9:	89 d9                	mov    %ebx,%ecx //ecx链表地址
 8048eab:	8b 50 04             	mov    0x4(%eax),%edx 
 8048eae:	89 51 08             	mov    %edx,0x8(%ecx)
 8048eb1:	83 c0 04             	add    $0x4,%eax
 8048eb4:	89 d1                	mov    %edx,%ecx
 8048eb6:	39 f0                	cmp    %esi,%eax  
 8048eb8:	75 f1                	jne    8048eab <phase_6+0x9a>
//七
 8048eba:	c7 42 08 00 00 00 00 	movl   $0x0,0x8(%edx)
 8048ec1:	be 05 00 00 00       	mov    $0x5,%esi
 8048ec6:	8b 43 08             	mov    0x8(%ebx),%eax
 8048ec9:	8b 00                	mov    (%eax),%eax
 8048ecb:	39 03                	cmp    %eax,(%ebx)
 8048ecd:	7e 05                	jle    8048ed4 <phase_6+0xc3>   //如果前面的数更大,就爆炸
 8048ecf:	e8 76 02 00 00       	call   804914a <explode_bomb>   
 8048ed4:	8b 5b 08             	mov    0x8(%ebx),%ebx              //检测现在的链表是否从小到大排序
 8048ed7:	83 ee 01             	sub    $0x1,%esi
 8048eda:	75 ea                	jne    8048ec6 <phase_6+0xb5>
 8048edc:	8b 45 f4             	mov    -0xc(%ebp),%eax
 8048edf:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8048ee6:	74 05                	je     8048eed <phase_6+0xdc>
 8048ee8:	e8 a3 f8 ff ff       	call   8048790 <__stack_chk_fail@plt>
 8048eed:	8d 65 f8             	lea    -0x8(%ebp),%esp
 8048ef0:	5b                   	pop    %ebx
 8048ef1:	5e                   	pop    %esi
 8048ef2:	5d                   	pop    %ebp
 8048ef3:	c3                   	ret    

一步步分析发现:读入六个数,首先是一个二重循环,保证要求每个数组元素不相同,有一个链表,根据我们输入的数字进行排序,最后需要保证节点中的数字从小到大。

屏幕截图 2021-12-21 192315.JPG

通过调试得到:每个节点有三个数据,第一个是改节点中的数字,第二个是初始编号,第三个是指向下一个节点的指针。按照从小到大的顺序为:4 1 2 5 3 6

image.png

secret_phase

Ctrl+f搜索secret_phase,发现是从phase_defused函数进入的,而phase_defused就是我们关过后调用的函数。具体分析写在注释里了

080492af <phase_defused>:
 80492af:	55                   	push   %ebp
 80492b0:	89 e5                	mov    %esp,%ebp
 80492b2:	83 ec 68             	sub    $0x68,%esp
 80492b5:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 80492bb:	89 45 f4             	mov    %eax,-0xc(%ebp)
 80492be:	31 c0                	xor    %eax,%eax
 80492c0:	83 3d cc c3 04 08 06 	cmpl   $0x6,0x804c3cc                     //比较输入的字符串数目是否等于6,不等于则跳转至程序结束
 80492c7:	75 6f                	jne    8049338 <phase_defused+0x89>
 80492c9:	83 ec 0c             	sub    $0xc,%esp
 80492cc:	8d 45 a4             	lea    -0x5c(%ebp),%eax
 80492cf:	50                   	push   %eax
 80492d0:	8d 45 a0             	lea    -0x60(%ebp),%eax
 80492d3:	50                   	push   %eax
 80492d4:	8d 45 9c             	lea    -0x64(%ebp),%eax
 80492d7:	50                   	push   %eax
 80492d8:	68 09 a3 04 08       	push   $0x804a309      //%d %d %s  前两个数是第四关的答案,后面的字符串是进入隐藏关卡的关键
 80492dd:	68 d0 c4 04 08       	push   $0x804c4d0       //print (char*)0x804c4d0
 80492e2:	e8 29 f5 ff ff       	call   8048810 <__isoc99_sscanf@plt>
 80492e7:	83 c4 20             	add    $0x20,%esp
 80492ea:	83 f8 03             	cmp    $0x3,%eax  //判断返回值eax(即正确匹配的通配符个数)是否为3
 80492ed:	75 39                	jne    8049328 <phase_defused+0x79>
 80492ef:	83 ec 08             	sub    $0x8,%esp
 80492f2:	68 12 a3 04 08       	push   $0x804a312  //DrEvil
 80492f7:	8d 45 a4             	lea    -0x5c(%ebp),%eax
 80492fa:	50                   	push   %eax
 80492fb:	e8 47 fd ff ff       	call   8049047 <strings_not_equal>
 8049300:	83 c4 10             	add    $0x10,%esp        //输入的字符串应和“DrEvil”相等
 8049303:	85 c0                	test   %eax,%eax    
 8049305:	75 21                	jne    8049328 <phase_defused+0x79>
 8049307:	83 ec 0c             	sub    $0xc,%esp
 804930a:	68 d8 a1 04 08       	push   $0x804a1d8       //Curses, you've found the secret phase!
 804930f:	e8 ac f4 ff ff       	call   80487c0 <puts@plt>
 8049314:	c7 04 24 00 a2 04 08 	movl   $0x804a200,(%esp)
 804931b:	e8 a0 f4 ff ff       	call   80487c0 <puts@plt>
 8049320:	e8 21 fc ff ff       	call   8048f46 <secret_phase>
 8049325:	83 c4 10             	add    $0x10,%esp
 8049328:	83 ec 0c             	sub    $0xc,%esp
 804932b:	68 38 a2 04 08       	push   $0x804a238 //Congratulations! You've defused the bomb!
 8049330:	e8 8b f4 ff ff       	call   80487c0 <puts@plt>
 8049335:	83 c4 10             	add    $0x10,%esp
 8049338:	8b 45 f4             	mov    -0xc(%ebp),%eax
 804933b:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 8049342:	74 05                	je     8049349 <phase_defused+0x9a>
 8049344:	e8 47 f4 ff ff       	call   8048790 <__stack_chk_fail@plt>
 8049349:	c9                   	leave  
 804934a:	c3                   	ret    

gdb查看使用到的内容:

屏幕截图 2021-12-21 212022.JPG

屏幕截图 2021-12-21 212318.JPG

屏幕截图 2021-12-21 212345.JPG

屏幕截图 2021-12-21 214120.JPG

可以看出进入secret_phase需要以下条件:

  • 通过前六关

  • 在第四关后面再输入一个字符串:DrEvil

进入隐藏关:

image.png

08048ef4 <fun7>:
 8048ef4:	55                   	push   %ebp
 8048ef5:	89 e5                	mov    %esp,%ebp
 8048ef7:	53                   	push   %ebx
 8048ef8:	83 ec 04             	sub    $0x4,%esp
 8048efb:	8b 55 08             	mov    0x8(%ebp),%edx
 8048efe:	8b 4d 0c             	mov    0xc(%ebp),%ecx
 8048f01:	85 d2                	test   %edx,%edx
 8048f03:	74 37                	je     8048f3c <fun7+0x48> //测试传入的edx是否为0,是就跳转至结束并返回0xffffffff
 8048f05:	8b 1a                	mov    (%edx),%ebx //取出edx地址的值,即该节点的值,赋给ebx
 8048f07:	39 cb                	cmp    %ecx,%ebx //比较
 8048f09:	7e 13                	jle    8048f1e <fun7+0x2a> //ebx <= ecx(读入的数字),跳转
 8048f0b:	83 ec 08             	sub    $0x8,%esp
 8048f0e:	51                   	push   %ecx              //还是读入的那个数
 8048f0f:	ff 72 04             	pushl  0x4(%edx)        //地址+4,相当于前往右节点
 8048f12:	e8 dd ff ff ff       	call   8048ef4 <fun7>   //执行递归
 8048f17:	83 c4 10             	add    $0x10,%esp
 8048f1a:	01 c0                	add    %eax,%eax  //eax*2
 8048f1c:	eb 23                	jmp    8048f41 <fun7+0x4d>  //跳转到返回的地方,直接eax
 8048f1e:	b8 00 00 00 00       	mov    $0x0,%eax  //eax=0
 8048f23:	39 cb                	cmp    %ecx,%ebx
 8048f25:	74 1a                	je     8048f41 <fun7+0x4d> //如果ecx,ebx相等就跳转并返回0
 8048f27:	83 ec 08             	sub    $0x8,%esp   
 8048f2a:	51                   	push   %ecx   //还是读入的那个数
 8048f2b:	ff 72 08             	pushl  0x8(%edx)   //地址+8,相当于前往左节点
 8048f2e:	e8 c1 ff ff ff       	call   8048ef4 <fun7>
 8048f33:	83 c4 10             	add    $0x10,%esp
 8048f36:	8d 44 00 01          	lea    0x1(%eax,%eax,1),%eax  //eax=eax*2+1
 8048f3a:	eb 05                	jmp    8048f41 <fun7+0x4d>
 8048f3c:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
 8048f41:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048f44:	c9                   	leave  
 8048f45:	c3                   	ret    
 
 
 
 

08048f46 <secret_phase>:
 8048f46:	55                   	push   %ebp
 8048f47:	89 e5                	mov    %esp,%ebp
 8048f49:	53                   	push   %ebx
 8048f4a:	83 ec 04             	sub    $0x4,%esp
 8048f4d:	e8 5a 02 00 00       	call   80491ac <read_line>  //读入一行
 8048f52:	83 ec 04             	sub    $0x4,%esp   
 8048f55:	6a 0a                	push   $0xa  //10进制
 8048f57:	6a 00                	push   $0x0
 8048f59:	50                   	push   %eax
 8048f5a:	e8 21 f9 ff ff       	call   8048880 <strtol@plt>  //用于字符串转long     
                        //声明: long int strtol(const char *str, char **endptr, int base)
 8048f5f:	89 c3                	mov    %eax,%ebx    //ebx=eax
 8048f61:	8d 40 ff             	lea    -0x1(%eax),%eax  //eax--
 8048f64:	83 c4 10             	add    $0x10,%esp   //
 8048f67:	3d e8 03 00 00       	cmp    $0x3e8,%eax
 8048f6c:	76 05                	jbe    8048f73 <secret_phase+0x2d>  //结果必须<=1000
 8048f6e:	e8 d7 01 00 00       	call   804914a <explode_bomb>
 8048f73:	83 ec 08             	sub    $0x8,%esp
 8048f76:	53                   	push   %ebx
 8048f77:	68 88 c0 04 08       	push   $0x804c088   
 8048f7c:	e8 73 ff ff ff       	call   8048ef4 <fun7>//传入一个地址,一个输入的数字
 8048f81:	83 c4 10             	add    $0x10,%esp
 8048f84:	85 c0                	test   %eax,%eax
 8048f86:	74 05                	je     8048f8d <secret_phase+0x47>  //fun7返回值需要为0
 8048f88:	e8 bd 01 00 00       	call   804914a <explode_bomb>
 8048f8d:	83 ec 0c             	sub    $0xc,%esp
 8048f90:	68 f8 a0 04 08       	push   $0x804a0f8
 8048f95:	e8 26 f8 ff ff       	call   80487c0 <puts@plt>
 8048f9a:	e8 10 03 00 00       	call   80492af <phase_defused>
 8048f9f:	83 c4 10             	add    $0x10,%esp
 8048fa2:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048fa5:	c9                   	leave  
 8048fa6:	c3                   	ret    

隐藏关是输入一个数,传入fun7()中,使得fun7()返回值为0。

一步步分析fun7(),用高级语言写出对于程序:

struct Node
{
    int val;
    Node* left;
    Node* right;
};

int fun7(Node *root,int ans){
    if(root->val<=ans){
        if(root->val==ans){
            return 0;
        }else{
            return 2*fun7(root->left,ans);
        }
    }else{
        return 2*fun7(root->right,ans)+1;
    }
}

2019112409332931.png

这是一颗二叉树,准确来说是一棵BST二叉搜索树,即满足右子节点<根节点<左子节点。

这道题虽然涉及的数据结构较复杂,但是要求比较仁慈,因为只需要输入的数字对于根节点的值就可以返回0啦。

屏幕截图 2021-12-21 223346.JPG

image.png

image.png

四、实验总结

ebp),%ebx
8048fa5: c9 leave
8048fa6: c3 ret


隐藏关是输入一个数,传入fun7()中,使得fun7()返回值为0。

一步步分析fun7(),用高级语言写出对于程序:

```c++
struct Node
{
    int val;
    Node* left;
    Node* right;
};

int fun7(Node *root,int ans){
    if(root->val<=ans){
        if(root->val==ans){
            return 0;
        }else{
            return 2*fun7(root->left,ans);
        }
    }else{
        return 2*fun7(root->right,ans)+1;
    }
}

[外链图片转存中…(img-bIqbIZmE-1654332562085)]

这是一颗二叉树,准确来说是一棵BST二叉搜索树,即满足右子节点<根节点<左子节点。

这道题虽然涉及的数据结构较复杂,但是要求比较仁慈,因为只需要输入的数字对于根节点的值就可以返回0啦。

[外链图片转存中…(img-IpRzgoCD-1654332562085)]

[外链图片转存中…(img-ilaIBuPZ-1654332562086)]

[外链图片转存中…(img-XwQcG3Wp-1654332562086)]

四、实验总结

很开心,学到了很多东西,加深了对汇编语言的理解和掌握,掌握了gdb调试的各种方法。

  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值