二进制拆弹(20181023-20181026)

二进制拆弹一共设有七个关卡:

  • phase_1:字符串比较
  • phase_2:循环
  • phase_3:switch
  • phase_4:递归
  • phase_5:指针
  • phase_6:链表/指针/结构
  • secret_phase:隐藏关

Phase1-6通关截图

准备工作

  • 我的电脑是windows的,所以需要在虚拟机上安装VMware,并且安装gdb(百度寻找教程)。
  • 使用objdump反汇编bomb可执行程序,得到bomb的汇编代码asm.txt文件:objdump -d bomb > asm.txt
  • 使用gdb调试bomb程序,进入调试状态,如图1所示:gdb bomb
图1:进入调试状态

phase_1

08048b90 <phase_1>:
 8048b90:	83 ec 1c             	sub    $0x1c,%esp
 8048b93:	c7 44 24 04 e4 a1 04 	movl   $0x804a1e4,0x4(%esp)   //取0x804a1e4的内容,到%esp+0x4
 8048b9a:	08 
 8048b9b:	8b 44 24 20          	mov    0x20(%esp),%eax 
 8048b9f:	89 04 24             	mov    %eax,(%esp)            //把用户的输入放入%esp
 8048ba2:	e8 03 05 00 00       	call   80490aa <strings_not_equal>
 8048ba7:	85 c0                	test   %eax,%eax              //判断eax是否为0
 8048ba9:	74 05                	je     8048bb0 <phase_1+0x20> //为0跳转
 8048bab:	e8 05 06 00 00       	call   80491b5 <explode_bomb> //不为0爆炸
 8048bb0:	83 c4 1c             	add    $0x1c,%esp
 8048bb3:	c3                   	ret   

程序解读:

  • 取内存0x804a1e4处的内容
  • 取用户输入的内容
  • 比较两者的值,相同则进入下一关,不相同则爆炸

获取x804a1e4处的内容: p  (char *) 0x804a1e4

第一关运行结果
图2:phase_1​​​​​

 

可以看到,第一关的密码为:Houses will begat jobs, jobs will begat houses.

phase_2


08048bb4 <phase_2>:
 //程序第一部分
 8048bb4:	53                   	push   %ebx
 8048bb5:	83 ec 38             	sub    $0x38,%esp
 8048bb8:	8d 44 24 18          	lea    0x18(%esp),%eax
 8048bbc:	89 44 24 04          	mov    %eax,0x4(%esp)
 8048bc0:	8b 44 24 40          	mov    0x40(%esp),%eax
 8048bc4:	89 04 24             	mov    %eax,(%esp)
 8048bc7:	e8 10 06 00 00       	call   80491dc <read_six_numbers>   //读入6个数字
 8048bcc:	83 7c 24 18 00       	cmpl   $0x0,0x18(%esp)              //判断第一个数字是否为0
 8048bd1:	79 22                	jns    8048bf5 <phase_2+0x41>       //如果不为0,则跳转到8048bf5处
 8048bd3:	e8 dd 05 00 00       	call   80491b5 <explode_bomb>       //如果为0,爆炸!!!
 8048bd8:	eb 1b                	jmp    8048bf5 <phase_2+0x41>

 //程序第二部分,此处是一个循环
 8048bda:	89 d8                	mov    %ebx,%eax                    //%ebx的值赋值给%eax
 8048bdc:	03 44 9c 14          	add    0x14(%esp,%ebx,4),%eax       //取了0x14+%esp+4*%ebx的值加上%eax的值,存到%eax中
 8048be0:	39 44 9c 18          	cmp    %eax,0x18(%esp,%ebx,4)
 8048be4:	74 05                	je     8048beb <phase_2+0x37>
 8048be6:	e8 ca 05 00 00       	call   80491b5 <explode_bomb>
 8048beb:	83 c3 01             	add    $0x1,%ebx
 8048bee:	83 fb 06             	cmp    $0x6,%ebx
 8048bf1:	75 e7                	jne    8048bda <phase_2+0x26>

 //程序第三部分
 8048bf3:	eb 07                	jmp    8048bfc <phase_2+0x48>
 8048bf5:	bb 01 00 00 00       	mov    $0x1,%ebx                   //第一个数字不为0时跳转到这里来啦!!! %ebx里面存的是一个循环变量
 8048bfa:	eb de                	jmp    8048bda <phase_2+0x26>
 8048bfc:	83 c4 38             	add    $0x38,%esp
 8048bff:	5b                   	pop    %ebx
 8048c00:	c3                   	ret 

程序分析:

  • 通过对程序第一部分的分析,可以了解到是要输入6个数字,且第一个数字不能为0;
  • 第一个数字不为0时,跳转到8048bf5处。此处设置了一个新的变量,并且初始化为1。然后跳转到8048bda
  •  0x14(%esp,%ebx,4)是比例变址寻址方式,地址是 0x14+%esp+4*%ebx
  • 然后比较%eax0x18(%esp,%ebx,4)的操作数的值,如果相等的话,%eax的值++(循环变量++),如果不为6,则继续循环,如果循环体中没有explode_bomb的话,则会循环5次。
  • 循环体如果没有explode_bomb,则第二关破解。所以,关键是循环体8048be0处比较的是何处的值。实际比较的是后一个数字(0x18(%esp,%ebx,4))是否等于 当前数字+循环变量(%eax)。
  • 如果我们第一个输入的数字是1,则第二关的密码应该为 1  2(1+1)   4(2+2)   7(4+3)    1(7+4)   16(11+5) 即 1 2 4 7 11 16,答案不唯一,满足这个规律即可,但是第一个数字不能为0.

phase_3

08048c01 <phase_3>:
 8048c01:	83 ec 3c             	sub    $0x3c,%esp

 //输入
 8048c04:	8d 44 24 2c          	lea    0x2c(%esp),%eax
 8048c08:	89 44 24 10          	mov    %eax,0x10(%esp)
 8048c0c:	8d 44 24 27          	lea    0x27(%esp),%eax
 8048c10:	89 44 24 0c          	mov    %eax,0xc(%esp)
 8048c14:	8d 44 24 28          	lea    0x28(%esp),%eax
 8048c18:	89 44 24 08          	mov    %eax,0x8(%esp)
 8048c1c:	c7 44 24 04 3a a2 04 	movl   $0x804a23a,0x4(%esp)   //取出入内容,这里可以看出入的数值类型
 8048c23:	08 
 8048c24:	8b 44 24 40          	mov    0x40(%esp),%eax
 8048c28:	89 04 24             	mov    %eax,(%esp)
 8048c2b:	e8 30 fc ff ff       	call   8048860 <__isoc99_sscanf@plt>

 //输入的第一个数字要大于2,小于等于7,否则爆炸
 8048c30:	83 f8 02             	cmp    $0x2,%eax
 8048c33:	7f 05                	jg     8048c3a <phase_3+0x39>
 8048c35:	e8 7b 05 00 00       	call   80491b5 <explode_bomb>
 8048c3a:	83 7c 24 28 07       	cmpl   $0x7,0x28(%esp)    //后者的值是输入的第一个数字,假设输入的第一个数字是5
 8048c3f:	0f 87 fc 00 00 00    	ja     8048d41 <phase_3+0x140>
 8048c45:	8b 44 24 28          	mov    0x28(%esp),%eax

 //根据输入的数值进行跳转,这里可以通过调试,把所有可能的跳转地址都输出
 8048c49:	ff 24 85 60 a2 04 08 	jmp    *0x804a260(,%eax,4)

 8048c50:	b8 76 00 00 00       	mov    $0x76,%eax
 8048c55:	81 7c 24 2c f1 00 00 	cmpl   $0xf1,0x2c(%esp)
 8048c5c:	00 
 8048c5d:	0f 84 e8 00 00 00    	je     8048d4b <phase_3+0x14a>
 8048c63:	e8 4d 05 00 00       	call   80491b5 <explode_bomb>
 8048c68:	b8 76 00 00 00       	mov    $0x76,%eax
 8048c6d:	e9 d9 00 00 00       	jmp    8048d4b <phase_3+0x14a>
 8048c72:	b8 69 00 00 00       	mov    $0x69,%eax
 8048c77:	81 7c 24 2c aa 01 00 	cmpl   $0x1aa,0x2c(%esp)
 8048c7e:	00 
 8048c7f:	0f 84 c6 00 00 00    	je     8048d4b <phase_3+0x14a>
 8048c85:	e8 2b 05 00 00       	call   80491b5 <explode_bomb>
 8048c8a:	b8 69 00 00 00       	mov    $0x69,%eax
 8048c8f:	e9 b7 00 00 00       	jmp    8048d4b <phase_3+0x14a>
 8048c94:	b8 67 00 00 00       	mov    $0x67,%eax
 8048c99:	81 7c 24 2c 15 03 00 	cmpl   $0x315,0x2c(%esp)
 8048ca0:	00 
 8048ca1:	0f 84 a4 00 00 00    	je     8048d4b <phase_3+0x14a>
 8048ca7:	e8 09 05 00 00       	call   80491b5 <explode_bomb>
 8048cac:	b8 67 00 00 00       	mov    $0x67,%eax
 8048cb1:	e9 95 00 00 00       	jmp    8048d4b <phase_3+0x14a>
 8048cb6:	b8 62 00 00 00       	mov    $0x62,%eax
 8048cbb:	81 7c 24 2c fd 01 00 	cmpl   $0x1fd,0x2c(%esp)
 8048cc2:	00 
 8048cc3:	0f 84 82 00 00 00    	je     8048d4b <phase_3+0x14a>
 8048cc9:	e8 e7 04 00 00       	call   80491b5 <explode_bomb>
 8048cce:	b8 62 00 00 00       	mov    $0x62,%eax
 8048cd3:	eb 76                	jmp    8048d4b <phase_3+0x14a>
 8048cd5:	b8 74 00 00 00       	mov    $0x74,%eax
 8048cda:	81 7c 24 2c 7b 02 00 	cmpl   $0x27b,0x2c(%esp)
 8048ce1:	00 
 8048ce2:	74 67                	je     8048d4b <phase_3+0x14a>
 8048ce4:	e8 cc 04 00 00       	call   80491b5 <explode_bomb>
 8048ce9:	b8 74 00 00 00       	mov    $0x74,%eax
 8048cee:	eb 5b                	jmp    8048d4b <phase_3+0x14a>
 8048cf0:	b8 79 00 00 00       	mov    $0x79,%eax                //第一数字是5的时候,跳转到这里来啦!!!!
 8048cf5:	81 7c 24 2c 3a 01 00 	cmpl   $0x13a,0x2c(%esp)         //判断第二个数字的值是不是 0x13a,即十进制的314
 8048cfc:	00 
 8048cfd:	74 4c                	je     8048d4b <phase_3+0x14a>
 8048cff:	e8 b1 04 00 00       	call   80491b5 <explode_bomb>
 8048d04:	b8 79 00 00 00       	mov    $0x79,%eax
 8048d09:	eb 40                	jmp    8048d4b <phase_3+0x14a>
 8048d0b:	b8 70 00 00 00       	mov    $0x70,%eax
 8048d10:	81 7c 24 2c cf 02 00 	cmpl   $0x2cf,0x2c(%esp)
 8048d17:	00 
 8048d18:	74 31                	je     8048d4b <phase_3+0x14a>
 8048d1a:	e8 96 04 00 00       	call   80491b5 <explode_bomb>
 8048d1f:	b8 70 00 00 00       	mov    $0x70,%eax
 8048d24:	eb 25                	jmp    8048d4b <phase_3+0x14a>
 8048d26:	b8 71 00 00 00       	mov    $0x71,%eax
 8048d2b:	81 7c 24 2c 7b 01 00 	cmpl   $0x17b,0x2c(%esp)
 8048d32:	00 
 8048d33:	74 16                	je     8048d4b <phase_3+0x14a>
 8048d35:	e8 7b 04 00 00       	call   80491b5 <explode_bomb>
 8048d3a:	b8 71 00 00 00       	mov    $0x71,%eax
 8048d3f:	eb 0a                	jmp    8048d4b <phase_3+0x14a>
 8048d41:	e8 6f 04 00 00       	call   80491b5 <explode_bomb>

 //如果第二个数字是314,就会跳转到这里来啦, 判断输入的字符是不是 0x65,即字母y
 8048d46:	b8 65 00 00 00       	mov    $0x65,%eax
 8048d4b:	3a 44 24 27          	cmp    0x27(%esp),%al         
 8048d4f:	74 05                	je     8048d56 <phase_3+0x155> 
 8048d51:	e8 5f 04 00 00       	call   80491b5 <explode_bomb>
 8048d56:	83 c4 3c             	add    $0x3c,%esp
 8048d59:	c3                   	ret    

程序分析:

  • 查看输入的数据类型,可以看到输入为 整型数值、字符、整型数值
(gdb) p (char*)0x804a23a
$16 = 0x804a23a "%d %c %d"
  • 查看所有可能的跳转地址,假设输入的第一个数是5,则应该跳转到的地址是 p/x *(0x804a274)的结果,即跳转到 0x8048cf0
跳转入口地址查看

 

  • 然后顺着地址跳转就行了,我通过的密码是:5 y 314 解不唯一

phase_4

08048d5a <func4>:
 8048d5a:	56                   	push   %esi
 8048d5b:	53                   	push   %ebx
 8048d5c:	83 ec 14             	sub    $0x14,%esp
 8048d5f:	8b 54 24 20          	mov    0x20(%esp),%edx
 8048d63:	8b 44 24 24          	mov    0x24(%esp),%eax
 8048d67:	8b 5c 24 28          	mov    0x28(%esp),%ebx
 8048d6b:	89 d9                	mov    %ebx,%ecx
 8048d6d:	29 c1                	sub    %eax(0),%ecx(14)
 8048d6f:	89 ce                	mov    %ecx,%esi
 8048d71:	c1 ee 1f             	shr    $0x1f,%esi
 8048d74:	01 f1                	add    %esi,%ecx
 8048d76:	d1 f9                	sar    %ecx
 8048d78:	01 c1                	add    %eax,%ecx
 8048d7a:	39 d1                	cmp    %edx,%ecx
 8048d7c:	7e 17                	jle    8048d95 <func4+0x3b> //不大于则转移
 8048d7e:	83 e9 01             	sub    $0x1,%ecx
 8048d81:	89 4c 24 08          	mov    %ecx,0x8(%esp)
 8048d85:	89 44 24 04          	mov    %eax,0x4(%esp)
 8048d89:	89 14 24             	mov    %edx,(%esp)
 8048d8c:	e8 c9 ff ff ff       	call   8048d5a <func4>
 8048d91:	01 c0                	add    %eax,%eax
 8048d93:	eb 20                	jmp    8048db5 <func4+0x5b>
 8048d95:	b8 00 00 00 00       	mov    $0x0,%eax
 8048d9a:	39 d1                	cmp    %edx,%ecx
 8048d9c:	7d 17                	jge    8048db5 <func4+0x5b>//大于或等于则转移
 8048d9e:	89 5c 24 08          	mov    %ebx,0x8(%esp)
 8048da2:	83 c1 01             	add    $0x1,%ecx
 8048da5:	89 4c 24 04          	mov    %ecx,0x4(%esp)
 8048da9:	89 14 24             	mov    %edx,(%esp)
 8048dac:	e8 a9 ff ff ff       	call   8048d5a <func4>
 8048db1:	8d 44 00 01          	lea    0x1(%eax,%eax,1),%eax
 8048db5:	83 c4 14             	add    $0x14,%esp
 8048db8:	5b                   	pop    %ebx
 8048db9:	5e                   	pop    %esi
 8048dba:	c3                   	ret    

08048dbb <phase_4>:
 8048dbb:	83 ec 2c             	sub    $0x2c,%esp

 //输入两个值
 8048dbe:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 8048dc2:	89 44 24 0c          	mov    %eax,0xc(%esp)
 8048dc6:	8d 44 24 18          	lea    0x18(%esp),%eax
 8048dca:	89 44 24 08          	mov    %eax,0x8(%esp)
 8048dce:	c7 44 24 04 cf a3 04 	movl   $0x804a3cf,0x4(%esp)
 8048dd5:	08 

 8048dd6:	8b 44 24 30          	mov    0x30(%esp),%eax
 8048dda:	89 04 24             	mov    %eax,(%esp)

 //调用函数
 8048ddd:	e8 7e fa ff ff       	call   8048860 <__isoc99_sscanf@plt>

 //比较返回值是否为2,不是2则爆炸
 8048de2:	83 f8 02             	cmp    $0x2,%eax
 8048de5:	75 07                	jne    8048dee <phase_4+0x33>

 //比较输入的第一个数是否小于等于14,如果大于15则爆炸
 8048de7:	83 7c 24 18 0e       	cmpl   $0xe,0x18(%esp)
 8048dec:	76 05                	jbe    8048df3 <phase_4+0x38>
 8048dee:	e8 c2 03 00 00       	call   80491b5 <explode_bomb>

 //定义了两个变量,值分别为14 0
 8048df3:	c7 44 24 08 0e 00 00 	movl   $0xe,0x8(%esp)
 8048dfa:	00 
 8048dfb:	c7 44 24 04 00 00 00 	movl   $0x0,0x4(%esp)
 8048e02:	00 
 8048e03:	8b 44 24 18          	mov    0x18(%esp),%eax
 8048e07:	89 04 24             	mov    %eax,(%esp)

 //传入参数,调用函数fun4,传入的参数的顺序,从左到右为:输入的第一个数 0 14
 8048e0a:	e8 4b ff ff ff       	call   8048d5a <func4>

 //判断返回的值是否为7,不是7则爆炸
 8048e0f:	83 f8 07             	cmp    $0x7,%eax
 8048e12:	75 07                	jne    8048e1b <phase_4+0x60>
 
 //判断输入的第二个参数是否为7,不是则爆炸,所以可以确定第二个参数值为7
 8048e14:	83 7c 24 1c 07       	cmpl   $0x7,0x1c(%esp)
 8048e19:	74 05                	je     8048e20 <phase_4+0x65>
 8048e1b:	e8 95 03 00 00       	call   80491b5 <explode_bomb>
 8048e20:	83 c4 2c             	add    $0x2c,%esp
 8048e23:	c3                   	ret    

第四关是递归,核心是要写出递归函数。

我得到的递归函数是:

int func4(int a1, int a2, int a3)
{
  int v3; // ecx
  int result; // eax

  v3 = a2 + (a3 - a2) / 2;
  if ( v3 > a1 )
    return 2 * func4(a1, a2, v3 - 1);
  result = 0;
  if ( v3 < a1 )
    result = 2 * func4(a1, v3 + 1, a3) + 1;
  return result;
}
  • 第一次调用fun4的时候,传入的第二个参数值是0,第三个参数值是14
  • 分析phase_4,可以知道只有fun4的返回值是7的时候,才能不爆炸
  • 我们输入的第一个数不大于14,所以尝试求解,可以看到,当输入14的时候,返回值为7,所以第四关的密码是 14 7
代码和运行过程

 

phase_5

08048e24 <phase_5>:
 8048e24:	53                   	push   %ebx
 8048e25:	83 ec 18             	sub    $0x18,%esp
 8048e28:	8b 5c 24 20          	mov    0x20(%esp),%ebx
 8048e2c:	89 1c 24             	mov    %ebx,(%esp)
 8048e2f:	e8 57 02 00 00       	call   804908b <string_length>

 //输入字符串的长度不等于6,则爆炸
 //所以输入是长度为6的字符串
 8048e34:	83 f8 06             	cmp    $0x6,%eax
 8048e37:	74 05                	je     8048e3e <phase_5+0x1a>
 8048e39:	e8 77 03 00 00       	call   80491b5 <explode_bomb>

 8048e3e:	ba 00 00 00 00       	mov    $0x0,%edx
 8048e43:	b8 00 00 00 00       	mov    $0x0,%eax

 //这里是一个循环
 8048e48:	0f b6 0c 03          	movzbl (%ebx,%eax,1),%ecx
 //取低四位
 8048e4c:	83 e1 0f             	and    $0xf,%ecx                //这里是关键!!!
 //相加
 8048e4f:	03 14 8d 80 a2 04 08 	add    0x804a280(,%ecx,4),%edx  //这里是关键!!!
 8048e56:	83 c0 01             	add    $0x1,%eax
 8048e59:	83 f8 06             	cmp    $0x6,%eax
 8048e5c:	75 ea                	jne    8048e48 <phase_5+0x24>

 //判断%edx中的操作数是不是0x2b,即十进制的43
 8048e5e:	83 fa 2b             	cmp    $0x2b,%edx
 8048e61:	74 05                	je     8048e68 <phase_5+0x44>
 8048e63:	e8 4d 03 00 00       	call   80491b5 <explode_bomb>
 8048e68:	83 c4 18             	add    $0x18,%esp
 8048e6b:	5b                   	pop    %ebx
 8048e6c:	c3                   	ret    

程序分析:

  • 输入一个长度为6的字符串
  • 经过一个循环操作
  • 判断某个变量的值是不是43,如果是43则通关
  • 查看一下0x804a280(,%ecx,4)的内容,%ecx依次取0 1 2 3 4 ....
依次尝试查看地址中的值

 

  • 43 = 10+10+10+10+1+2 = 0xa + 0xa + 0xa + 0xa + 0x1 + 0x2
  • 0xa对应的%ecx中的值为1(0x804a280+4*1=0x804a284),所以只要 输入的字符的二进制表示&0x0f == 1 即可,例如a的ASCII码的二进制表示是01100001,01100001&0f =1
  • 0x1对应的%ecx中的值为3(0x804a280+4*3=0x804a28c),  01100011 & 0x0f 对应的十进制是3,01100011对应的字符是c
  • 0x2对应的%ecx中的值为0(0x804a280+4*0=0x804a280),  01110000 & 0x0f 对应的十进制是0,01110000对应的字符是p
  • 所以密码之一是:aaaacp(答案不唯一)

phase_6

08048e6d <phase_6>:
 8048e6d:	56                   	push   %esi
 8048e6e:	53                   	push   %ebx
 8048e6f:	83 ec 44             	sub    $0x44,%esp
 8048e72:	8d 44 24 10          	lea    0x10(%esp),%eax
 8048e76:	89 44 24 04          	mov    %eax,0x4(%esp)
 8048e7a:	8b 44 24 50          	mov    0x50(%esp),%eax
 8048e7e:	89 04 24             	mov    %eax,(%esp)
 8048e81:	e8 56 03 00 00       	call   80491dc <read_six_numbers>
 8048e86:	be 00 00 00 00       	mov    $0x0,%esi

//这是第一个循环,第一个循环里面还嵌入着一个小循环  这里的条件是数组中所有的元素都小于等于6,且不能存在两个数相同。所以输入的数字是1-6的排列组合之一
 8048e8b:	8b 44 b4 10          	mov    0x10(%esp,%esi,4),%eax
 8048e8f:	83 e8 01             	sub    $0x1,%eax
 8048e92:	83 f8 05             	cmp    $0x5,%eax
 8048e95:	76 05                	jbe    8048e9c <phase_6+0x2f>   //%eax-1 <= 5则继续,否则爆炸
 8048e97:	e8 19 03 00 00       	call   80491b5 <explode_bomb>   //爆炸
 8048e9c:	83 c6 01             	add    $0x1,%esi                //计数器加1,如果不等于6则循环
 8048e9f:	83 fe 06             	cmp    $0x6,%esi
 8048ea2:	75 07                	jne    8048eab <phase_6+0x3e>
 8048ea4:	bb 00 00 00 00       	mov    $0x0,%ebx
 8048ea9:	eb 38                	jmp    8048ee3 <phase_6+0x76>  //此处是第一个大循环的出口
 8048eab:	89 f3                	mov    %esi,%ebx
 8048ead:	8b 44 9c 10          	mov    0x10(%esp,%ebx,4),%eax  //取数组中的下一个值  小循环开始
 8048eb1:	39 44 b4 0c          	cmp    %eax,0xc(%esp,%esi,4)   //进行了一次比较,打印看看值是多少
 8048eb5:	75 05                	jne    8048ebc <phase_6+0x4f>
 8048eb7:	e8 f9 02 00 00       	call   80491b5 <explode_bomb>
 8048ebc:	83 c3 01             	add    $0x1,%ebx
 8048ebf:	83 fb 05             	cmp    $0x5,%ebx
 8048ec2:	7e e9                	jle    8048ead <phase_6+0x40>  //小于等于则跳转  小循环结束
 8048ec4:	eb c5                	jmp    8048e8b <phase_6+0x1e>

//这是第二个循环
 8048ec6:	8b 52 08             	mov    0x8(%edx),%edx
 8048ec9:	83 c0 01             	add    $0x1,%eax
 8048ecc:	39 c8                	cmp    %ecx,%eax
 8048ece:	75 f6                	jne    8048ec6 <phase_6+0x59>
 8048ed0:	eb 05                	jmp    8048ed7 <phase_6+0x6a>
 8048ed2:	ba 3c c1 04 08       	mov    $0x804c13c,%edx        //这里存的是一个地址值
 8048ed7:	89 54 b4 28          	mov    %edx,0x28(%esp,%esi,4) //把地址传给了一个结点
 8048edb:	83 c3 01             	add    $0x1,%ebx              //++
 8048ede:	83 fb 06             	cmp    $0x6,%ebx
 8048ee1:	74 17                	je     8048efa <phase_6+0x8d>   //
 8048ee3:	89 de                	mov    %ebx,%esi                //从此处进入第二个循环,%ebx寄存器内的初始值是0
 8048ee5:	8b 4c 9c 10          	mov    0x10(%esp,%ebx,4),%ecx   //取数组中的第一个元素
 8048ee9:	83 f9 01             	cmp    $0x1,%ecx
 8048eec:	7e e4                	jle    8048ed2 <phase_6+0x65>   //小于等于1则跳转
 8048eee:	b8 01 00 00 00       	mov    $0x1,%eax
 8048ef3:	ba 3c c1 04 08       	mov    $0x804c13c,%edx
 8048ef8:	eb cc                	jmp    8048ec6 <phase_6+0x59>

 8048efa:	8b 5c 24 28          	mov    0x28(%esp),%ebx
 8048efe:	8d 44 24 2c          	lea    0x2c(%esp),%eax
 8048f02:	8d 74 24 40          	lea    0x40(%esp),%esi
 8048f06:	89 d9                	mov    %ebx,%ecx
 8048f08:	8b 10                	mov    (%eax),%edx   //间接寻址
 8048f0a:	89 51 08             	mov    %edx,0x8(%ecx)
 8048f0d:	83 c0 04             	add    $0x4,%eax
 8048f10:	39 f0                	cmp    %esi,%eax
 8048f12:	74 04                	je     8048f18 <phase_6+0xab>
 8048f14:	89 d1                	mov    %edx,%ecx
 8048f16:	eb f0                	jmp    8048f08 <phase_6+0x9b>
 8048f18:	c7 42 08 00 00 00 00 	movl   $0x0,0x8(%edx)

 //这是第四个循环,前一个节点中的值要比后一个结点中的值大,从大到小排序
 8048f1f:	be 05 00 00 00       	mov    $0x5,%esi
 8048f24:	8b 43 08             	mov    0x8(%ebx),%eax
 8048f27:	8b 00                	mov    (%eax),%eax
 8048f29:	39 03                	cmp    %eax,(%ebx)
 8048f2b:	7d 05                	jge    8048f32 <phase_6+0xc5>
 8048f2d:	e8 83 02 00 00       	call   80491b5 <explode_bomb>
 8048f32:	8b 5b 08             	mov    0x8(%ebx),%ebx
 8048f35:	83 ee 01             	sub    $0x1,%esi
 8048f38:	75 ea                	jne    8048f24 <phase_6+0xb7>
 
 8048f3a:	83 c4 44             	add    $0x44,%esp
 8048f3d:	5b                   	pop    %ebx
 8048f3e:	5e                   	pop    %esi
 8048f3f:	c3                   	ret    

程序分析:如下如,各节点存储的值分别为:0x4d<1>  0x11a<2>  0x223<3>   0x3d9<4>   0x106<5>   0x202<6> ,然后由大到小排序:4 3 6 2 5 1

结点中的值

 

secret_phase

这一关是隐藏关卡,我在asm.txt(反汇编得到的文件)中搜索 secret_phase,发现只有phase_defused函数中使用到了secret_phase函数,所以推测phase_defused是隐藏关卡的入口。

问:phase_defused函数什么时候会被调用???

答:每次密码输对的时候会调用phase_defused函数。所以说,如果调用了phase_defused函数,就意味着通过了一个普通关卡(不包括隐藏关卡)

0x1 分析phase_defused函数

08049326 <phase_defused>:
 8049326:	81 ec 8c 00 00 00    	sub    $0x8c,%esp
 804932c:	65 a1 14 00 00 00    	mov    %gs:0x14,%eax
 8049332:	89 44 24 7c          	mov    %eax,0x7c(%esp)
 8049336:	31 c0                	xor    %eax,%eax

 //判断 0x804c3c8 中的值是否为6,不是6则跳转,跳转后,phase_defused函数会顺序执行完毕,无法进入隐藏关卡
 8049338:	83 3d c8 c3 04 08 06 	cmpl   $0x6,0x804c3c8
 804933f:	75 72                	jne    80493b3 <phase_defused+0x8d>

//调用 <__isoc99_sscanf@plt>
 8049341:	8d 44 24 2c          	lea    0x2c(%esp),%eax
 8049345:	89 44 24 10          	mov    %eax,0x10(%esp)
 8049349:	8d 44 24 28          	lea    0x28(%esp),%eax
 804934d:	89 44 24 0c          	mov    %eax,0xc(%esp)
 8049351:	8d 44 24 24          	lea    0x24(%esp),%eax
 8049355:	89 44 24 08          	mov    %eax,0x8(%esp)
 8049359:	c7 44 24 04 29 a4 04 	movl   $0x804a429,0x4(%esp) //"%d %d %s"
 8049360:	08 
 8049361:	c7 04 24 d0 c4 04 08 	movl   $0x804c4d0,(%esp)
 8049368:	e8 f3 f4 ff ff       	call   8048860 <__isoc99_sscanf@plt>
 
 //判断<__isoc99_sscanf@plt>的返回值是否为3,不是3则跳转
 804936d:	83 f8 03             	cmp    $0x3,%eax   //sscanf返回参数,表示输入的参数个数
 8049370:	75 35                	jne    80493a7 <phase_defused+0x81>
 8049372:	c7 44 24 04 32 a4 04 	movl   $0x804a432,0x4(%esp)  //DrEvil
 8049379:	08 
 804937a:	8d 44 24 2c          	lea    0x2c(%esp),%eax
 804937e:	89 04 24             	mov    %eax,(%esp)
 8049381:	e8 24 fd ff ff       	call   80490aa <strings_not_equal>
 8049386:	85 c0                	test   %eax,%eax
 8049388:	75 1d                	jne    80493a7 <phase_defused+0x81>
 804938a:	c7 04 24 f8 a2 04 08 	movl   $0x804a2f8,(%esp)   //0x804a2f8中的值:Curses, you've found the secret phase!"
 8049391:	e8 5a f4 ff ff       	call   80487f0 <puts@plt>
 8049396:	c7 04 24 20 a3 04 08 	movl   $0x804a320,(%esp)   //But finding it and solving it are quite different...
 804939d:	e8 4e f4 ff ff       	call   80487f0 <puts@plt>
 80493a2:	e8 ea fb ff ff       	call   8048f91 <secret_phase> //调用secret_phase函数
 80493a7:	c7 04 24 58 a3 04 08 	movl   $0x804a358,(%esp)  //Congratulations! You've defused the bomb!
 80493ae:	e8 3d f4 ff ff       	call   80487f0 <puts@plt>
 80493b3:	8b 44 24 7c          	mov    0x7c(%esp),%eax
 80493b7:	65 33 05 14 00 00 00 	xor    %gs:0x14,%eax
 80493be:	74 05                	je     80493c5 <phase_defused+0x9f>
 80493c0:	e8 fb f3 ff ff       	call   80487c0 <__stack_chk_fail@plt>
 80493c5:	81 c4 8c 00 00 00    	add    $0x8c,%esp
 80493cb:	c3                   	ret    
 80493cc:	66 90                	xchg   %ax,%ax
 80493ce:	66 90                	xchg   %ax,%ax

程序分析:

  • 8049338:    83 3d c8 c3 04 08 06     cmpl   $0x6,0x804c3c8 函数首先判断0x804c3c8中的值是不是6,如果不是6的话则跳转,不会进入隐藏关卡。我们可以在0x8049338处设置断点(b *0x8049338),每次查看0x804c3c8中的值(p *(int *)0x804c3c8)。查看值的时候发现,每通过一个普通关卡,0x804c3c8中的值加1,例如第三关的密码正确,就会调用phase_defused函数,此时0x804c3c8中的值为3。所以当0x804c3c8中的值为6的时候,才有可能进入隐藏关卡。
  • 当通过了前6关以后,0x804c3c8中的值为6,此时函数不会跳转,会继续执行。
  • 8049359:    c7 44 24 04 29 a4 04     movl   $0x804a429,0x4(%esp) ,输出0x804a429中的内容,发现是%d %d %s
  • 8049361:    c7 04 24 d0 c4 04 08     movl   $0x804c4d0,(%esp),输出0x804c4d0中的内容,发现是14 7,是第四关输入的内容。但是上一步我们分析了输入是%d %d %s,这样的话还缺少一个%s,也就是说,隐藏关卡的调用可能需要在第四关多输入一个字符串。
  • 继续分析,我们发现sscanf函数调用完后,对其返回值进行了判断:804936d:    83 f8 03   cmp    $0x3,%eax,即判断 输入的参数是不是有3个,如果不是的话,就会跳转,无法继续执行。这也进一步验证了我们上一步的猜测。
  • 那,输入的字符串是啥呢?类似第一关,我们在phase_defused中发现了<strings_not_equal>,所以前面应该有个程序直接设定好的字符串,找一找,果然8049372:    c7 44 24 04 32 a4 04     movl   $0x804a432,0x4(%esp) ,输出0x804a432的内容,发现是DrEvil,这不就是一个字符串么!!
  • 如果第六关通过、第四关的输入是14 7 DrEvil,那么我们就能进入隐藏关卡了!!
  • 继续分析secret_phase函数吧!!

0x2 分析secret_phase函数

08048f91 <secret_phase>:
 8048f91:	53                   	push   %ebx
 8048f92:	83 ec 18             	sub    $0x18,%esp
 8048f95:	e8 92 02 00 00       	call   804922c <read_line>
 8048f9a:	c7 44 24 08 0a 00 00 	movl   $0xa,0x8(%esp)
 8048fa1:	00 
 8048fa2:	c7 44 24 04 00 00 00 	movl   $0x0,0x4(%esp)
 8048fa9:	00 
 8048faa:	89 04 24             	mov    %eax,(%esp)
 8048fad:	e8 1e f9 ff ff       	call   80488d0 <strtol@plt>
 8048fb2:	89 c3                	mov    %eax,%ebx
 8048fb4:	8d 40 ff             	lea    -0x1(%eax),%eax
 8048fb7:	3d e8 03 00 00       	cmp    $0x3e8,%eax
 8048fbc:	76 05                	jbe    8048fc3 <secret_phase+0x32>
 8048fbe:	e8 f2 01 00 00       	call   80491b5 <explode_bomb>
 8048fc3:	89 5c 24 04          	mov    %ebx,0x4(%esp)   //我们输入的数字
 8048fc7:	c7 04 24 88 c0 04 08 	movl   $0x804c088,(%esp) //程序设定的数组
 8048fce:	e8 6d ff ff ff       	call   8048f40 <fun7>

 8048fd3:	83 f8 01             	cmp    $0x1,%eax   //查看fun7返回值是否等于1,不等于则引爆
 8048fd6:	74 05                	je     8048fdd <secret_phase+0x4c>
 8048fd8:	e8 d8 01 00 00       	call   80491b5 <explode_bomb>
 8048fdd:	c7 04 24 14 a2 04 08 	movl   $0x804a214,(%esp)
 8048fe4:	e8 07 f8 ff ff       	call   80487f0 <puts@plt>
 8048fe9:	e8 38 03 00 00       	call   8049326 <phase_defused>
 8048fee:	83 c4 18             	add    $0x18,%esp
 8048ff1:	5b                   	pop    %ebx
 8048ff2:	c3                   	ret    
 8048ff3:	66 90                	xchg   %ax,%ax
 8048ff5:	66 90                	xchg   %ax,%ax
 8048ff7:	66 90                	xchg   %ax,%ax
 8048ff9:	66 90                	xchg   %ax,%ax
 8048ffb:	66 90                	xchg   %ax,%ax
 8048ffd:	66 90                	xchg   %ax,%ax
 8048fff:	90                   	nop

程序分析

  • 初步分析,应该是需要我们输入一个数字(假设是x),然后判断x是不是小于0x3e9,如果大于的话,就抱歉地爆炸了,如果小于的话,调用函数fun7,函数fun7的参数是一个指针(或者理解为数组的首地址,程序设定好的:0x804c088)和一个数字(输入的x),也就是说fun7的是这样的:fun7(int *a,int x)。
  • fun7调用完后,判断fun7的返回值,如果不是1,则爆炸,如果是1,恭喜,你过关了!!!
  • 最后,我们来愉快地分析一波fun7吧,开森!!

0x2 分析fun7函数

08048f40 <fun7>:
 8048f40:	53                   	push   %ebx
 8048f41:	83 ec 18             	sub    $0x18,%esp
 8048f44:	8b 54 24 20          	mov    0x20(%esp),%edx
 8048f48:	8b 4c 24 24          	mov    0x24(%esp),%ecx
 8048f4c:	85 d2                	test   %edx,%edx
 8048f4e:	74 37                	je     8048f87 <fun7+0x47>
 8048f50:	8b 1a                	mov    (%edx),%ebx
 8048f52:	39 cb                	cmp    %ecx,%ebx
 8048f54:	7e 13                	jle    8048f69 <fun7+0x29>
 8048f56:	89 4c 24 04          	mov    %ecx,0x4(%esp)
 8048f5a:	8b 42 04             	mov    0x4(%edx),%eax
 8048f5d:	89 04 24             	mov    %eax,(%esp)
 8048f60:	e8 db ff ff ff       	call   8048f40 <fun7>
 8048f65:	01 c0                	add    %eax,%eax   //,2*%eax ,eax中存储的是函数的返回值
 8048f67:	eb 23                	jmp    8048f8c <fun7+0x4c>
 8048f69:	b8 00 00 00 00       	mov    $0x0,%eax
 8048f6e:	39 cb                	cmp    %ecx,%ebx
 8048f70:	74 1a                	je     8048f8c <fun7+0x4c>
 8048f72:	89 4c 24 04          	mov    %ecx,0x4(%esp)
 8048f76:	8b 42 08             	mov    0x8(%edx),%eax
 8048f79:	89 04 24             	mov    %eax,(%esp)
 8048f7c:	e8 bf ff ff ff       	call   8048f40 <fun7>
 8048f81:	8d 44 00 01          	lea    0x1(%eax,%eax,1),%eax
 8048f85:	eb 05                	jmp    8048f8c <fun7+0x4c>
 8048f87:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
 8048f8c:	83 c4 18             	add    $0x18,%esp
 8048f8f:	5b                   	pop    %ebx
 8048f90:	c3                   	ret    

程序分析:

  • 初步分析,这是一个简单的递归函数
  • 根据递归函数,写出伪代码,然后进行程序分析
fun7的伪代码

 

  • 你看了可能会有个问题:指针a1里面是啥,里面是啥,里面是啥???a[1]、a[2]里面居然还是个地址???我们来倒推一波吧!
  • 分析secret_phase函数,我们知道只有fun7的返回值是1,才能过关。如果fun7的返回值是1,则意味着在第一次递归的时候,我们进入的分支是if(*a1 != a2)的分支,且2 * fun7(a1[2], a2) 的结果是0,即 fun7(a1[2], a2) 是0。同时我们还能知道,第一次递归的时候a2 > *a1.
  • fun7(a1[2], a2) 的返回值是0,意味着啥呢?意味着 *a[2] = a2,嘻嘻,所以现在的关键是研究一波第一次递归传入的指针。
  • 第一次递归,传入的指针,实际是个地址,的值是0x804c088,查看其附近的值。

  • 从上图,得到输入的值应该是32H,即十进制的50,至于为啥,仔细看前几步的分析呀!
  • 至此,二进制炸弹拆完了!!!
  • 16
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值