【计算机系统】Bomblab实验

前言

内容仅作记录,请谨慎参考,思路可能差不多,但每个人的炸弹是不一样的

第一个

08048b90 <phase_1>:
 8048b90:	83 ec 1c		sub    $0x1c,%esp
//栈顶指针%esp-0x1c,即开辟一个大小为0x1c字节的栈空间
 8048b93:	c7 44 24 04 5c a1 04		movl   $0x804a15c,0x4(%esp)
//将地址0x804a15c中的值赋给*(%esp+0x4)
 8048b9a:	08 
 8048b9b:	8b 44 24 20		mov    0x20(%esp),%eax
//将*(%esp+0x20)赋给寄存器%eax
 8048b9f:	89 04 24		mov    %eax,(%esp)
//将寄存器%eax中的值赋给*(esp)
 8048ba2:	e8 93 04 00 00		call   804903a <strings_not_equal>
//执行函数,比较两字符串是否相等
 8048ba7:	85 c0		test   %eax,%eax
//test即为做“与”运算
 8048ba9:	74 05		je     8048bb0 <phase_1+0x20>
//结果为0则跳转,即%eax=0,则跳转到8048bb0
 8048bab:	e8 95 05 00 00		call   8049145 <explode_bomb>
//结果为1则执行这一句,炸弹爆炸
 8048bb0:	83 c4 1c		add    $0x1c,%esp
//将栈顶指针往栈底移动0x1c,回收栈空间
 8048bb3:	c3		ret  

通过分析汇编代码,我们可以推出寄存器%eax存放的是我们所输入的字符串,同时我们可以看到,$0x804a15c是一个立即数,那么很有可能该地址处存放的就是用于比较的字符串,使用gdb进行调试,输入x/s 0x804a15c查看地址中的值可以看到其内容为:
And they have no disregard for human life.
运行bomb程序,输入这段字符串可以看到第一个炸弹被成功拆除:
在这里插入图片描述

第二个

08048bb4 <phase_2>:
 8048bb4:	56		push   %esi
 8048bb5:	53		push   %ebx
 8048bb6:	83 ec 34		sub    $0x34,%esp
//栈顶指针%esp-0x34,即开辟一个大小为0x34字节的栈空间
 8048bb9:	8d 44 24 18		lea    0x18(%esp),%eax
//将地址%esp+0x18赋给%eax
 8048bbd:	89 44 24 04		mov    %eax,0x4(%esp)
//将寄存器%eax中的值赋给*(%esp+0x4)
 8048bc1:	8b 44 24 40		mov    0x40(%esp),%eax
//将*(%esp+40)赋给寄存器%eax
 8048bc5:	89 04 24		mov    %eax,(%esp)
//将寄存器%eax中的值赋给*(esp)
 8048bc8:	e8 9f 05 00 00		call   804916c <read_six_numbers>
//执行函数
 8048bcd:	83 7c 24 18 01		cmpl   $0x1,0x18(%esp)
//将0x1与*(%esp+0x18)进行比较
 8048bd2:	74 1e		je     8048bf2 <phase_2+0x3e>
//如果相等,则跳转到8048bf2
 8048bd4:	e8 6c 05 00 00		call   8049145 <explode_bomb>
//如果不相等则执行这一句,炸弹爆炸
 8048bd9:	eb 17		jmp    8048bf2 <phase_2+0x3e>
//跳转到8048bf2
 8048bdb:	8b 43 fc		mov    -0x4(%ebx),%eax
//将*(%ebx-0x4)赋给寄存器%eax
 8048bde:	01 c0		add    %eax,%eax
//将寄存器%eax中的值的二倍赋给%eax
 8048be0:	39 03		cmp    %eax,(%ebx)
//将%eax与*(%ebx)进行比较
 8048be2:	74 05		je     8048be9 <phase_2+0x35>
//如果相等,则跳转到8048be9
 8048be4:	e8 5c 05 00 00		call   8049145 <explode_bomb>
//如果不相等则执行这一句,炸弹爆炸
 8048be9:	83 c3 04		add    $0x4,%ebx
//将寄存器%ebx中的值加上0x4结果放到寄存器%ebx中
 8048bec:	39 f3		cmp    %esi,%ebx
//将%esi与%ebx中的值进行比较
 8048bee:	75 eb		jne    8048bdb <phase_2+0x27>
//如果不相等,则跳转到8048bdb
 8048bf0:	eb 0a		jmp    8048bfc <phase_2+0x48>
//跳转到8048bfc
 8048bf2:	8d 5c 24 1c		lea    0x1c(%esp),%ebx
//将地址%esp+0x1c赋给%ebx
 8048bf6:	8d 74 24 30		lea    0x30(%esp),%esi
//将地址%esp+0x30赋给%esi
 8048bfa:	eb df		jmp    8048bdb <phase_2+0x27>
//跳转到8048bdb
 8048bfc:	83 c4 34		add    $0x34,%esp
//将寄存器%esp中的值加上0x34结果放到寄存器%esp中
 8048bff:	5b		pop    %ebx
 8048c00:	5e		pop    %esi
//弹栈
 8048c01:	c3		ret   

通过分析汇编代码,开辟栈空间之后结合<read_six_numbers>我们可以推出%esp+0x18、
%esp+0x1c、%esp+0x20、%esp+0x24、%esp+0x28、%esp+0x2c中存放的是要进行比较的六个数。同时,通过cmpl $0x1,0x18(%esp)可以知道第一个数为1,之后将会进行一个循环,通过add %eax,%eax可以知道每次循环的结果为原来的数的二倍,故最终得到的六个数为:1、2、4、8、16、32
运行bomb,输入这六个数可以看到第二个炸弹被拆除:
在这里插入图片描述

第三个

08048c02 <phase_3>:
 8048c02:	83 ec 2c		sub    $0x2c,%esp
//栈顶指针%esp-0x2c,即开辟一个大小为0x2c字节的栈空间
 8048c05:	8d 44 24 1c		lea    0x1c(%esp),%eax
//将地址%esp+0x1c赋给%eax
 8048c09:	89 44 24 0c		mov    %eax,0xc(%esp)
//将寄存器%eax中的值赋给*(%esp+0xc)
 8048c0d:	8d 44 24 18		lea    0x18(%esp),%eax
//将地址%esp+0x18赋给寄存器%eax
 8048c11:	89 44 24 08		mov    %eax,0x8(%esp)
//将寄存器%eax中的值赋给*(%esp+0x8)
 8048c15:	c7 44 24 04 f7 a2 04		movl   $0x804a2f7,0x4(%esp)
//将立即数0x804a2f7赋给*(%esp+0x4)
 8048c1c:	08 
 8048c1d:	8b 44 24 30		mov    0x30(%esp),%eax
//将*(%esp+0x30)赋给寄存器%eax
 8048c21:	89 04 24		mov    %eax,(%esp)
//将寄存器%eax中的值赋给*(%esp)
 8048c24:	e8 37 fc ff ff		call   8048860 <__isoc99_sscanf@plt>
//执行函数
 8048c29:	83 f8 01		cmp    $0x1,%eax
//比较0x1与寄存器%eax中的值的大小
 8048c2c:	7f 05		jg     8048c33 <phase_3+0x31>
//%eax中的值大于0x1则跳转到8048c33
 8048c2e:	e8 12 05 00 00		call   8049145 <explode_bomb>
//没有跳转成功则炸弹爆炸
 8048c33:	83 7c 24 18 07		cmpl   $0x7,0x18(%esp)
//将0x7与*(%esp+0x18)中的值进行比较
 8048c38:	77 3c		ja     8048c76 <phase_3+0x74>
//如果*(%esp+0x18)中的值大于0x7则跳转到8048c76
 8048c3a:	8b 44 24 18		mov    0x18(%esp),%eax
//将*(%esp+0x18)中的值赋给寄存器%eax
 8048c3e:	ff 24 85 b8 a1 04 08		jmp    *0x804a1b8(,%eax,4)
//跳转到%eax*4+*0x804a1b8(使用x/s查看得到为8048c45)
 8048c45:	b8 f1 00 00 00		mov    $0xf1,%eax
//将立即数0xf1赋给寄存器%eax
 8048c4a:	eb 3b		jmp    8048c87 <phase_3+0x85>
//跳转到8048c87
 8048c4c:	b8 8c 01 00 00		mov    $0x18c,%eax
//将立即数0x18c赋给寄存器%eax
 8048c51:	eb 34		jmp    8048c87 <phase_3+0x85>
//跳转到8048c87
 8048c53:	b8 c2 00 00 00		mov    $0xc2,%eax
//将立即数0xc2赋给寄存器%eax
 8048c58:	eb 2d		jmp    8048c87 <phase_3+0x85>
//跳转到8048c87
 8048c5a:	b8 bd 02 00 00		mov    $0x2bd,%eax
//将立即数0x2bd赋给寄存器%eax
 8048c5f:	eb 26		jmp    8048c87 <phase_3+0x85>
//跳转到8048c87
 8048c61:	b8 09 03 00 00		mov    $0x309,%eax
//将立即数0x309赋给寄存器%eax
 8048c66:	eb 1f		jmp    8048c87 <phase_3+0x85>
//跳转到8048c87
 8048c68:	b8 92 02 00 00		mov    $0x292,%eax
//将立即数0x292赋给寄存器%eax
 8048c6d:	eb 18		jmp    8048c87 <phase_3+0x85>
//跳转到8048c87
 8048c6f:	b8 53 02 00 00		mov    $0x253,%eax
//将立即数0x253赋给寄存器%eax
 8048c74:	eb 11		jmp    8048c87 <phase_3+0x85>
//跳转到8048c87
 8048c76:	e8 ca 04 00 00		call   8049145 <explode_bomb>
//炸弹爆炸
 8048c7b:	b8 00 00 00 00		mov    $0x0,%eax
//将立即数0x0赋给寄存器%eax
 8048c80:	eb 05		jmp    8048c87 <phase_3+0x85>
//跳转到8048c87
 8048c82:	b8 fc 02 00 00		mov    $0x2fc,%eax
//将立即数0x2fc赋给寄存器%eax
 8048c87:	3b 44 24 1c		cmp    0x1c(%esp),%eax
//比较*(%esp+0x1c)与寄存器%eax中的值的大小
 8048c8b:	74 05		je     8048c92 <phase_3+0x90>
//如果相等则跳转到8048c92
 8048c8d:	e8 b3 04 00 00		call   8049145 <explode_bomb>
//炸弹爆炸
 8048c92:	83 c4 2c		add    $0x2c,%esp
//将寄存器%esp加0x2c,用于回收开辟的栈空间
 8048c95:	c3		ret    

分析汇编代码,我们注意到,movl $0x804a2f7,0x4(%esp)这里传递了一个立即数,使用gdb调试,x/s 0x804a2f7查看该地址的内容可以得到:
在这里插入图片描述
结合后面的call 8048860 <__isoc99_sscanf@plt>可以推出输入的为两个数,后面的将0x1与%eax进行比较也说明了要输入两个参数
接着,将0x7与%esp+0x18中的值进行了比较,只有当这个值不大于7时炸弹才不会爆炸
通过jmp * 0x804a1b8(,%eax,4)可以知道这是一个跳转表,结合刚才的分析可以推出第一个输入即为跳转的参数0、1、2、3、4、5、6、7,对应8个不同的地址
使用gdb查看*0x804a1b8+0、+4、+8、+12、+16、+20、+24、+28中的内容:
在这里插入图片描述
可以看到对应了8个不同的跳转地址,进而得到8组输入为:
在这里插入图片描述
运行bomb,输入结果:
在这里插入图片描述
可以看到确实是8组答案均可通过(还真有人这么闲都试一遍啊)

第四个

第四关代码

08048cf3 <phase_4>:
 8048cf3:	83 ec 2c		sub    $0x2c,%esp
//栈顶指针%esp-0x2c,即开辟一个大小为0x2c字节的栈空间
 8048cf6:	8d 44 24 1c		lea    0x1c(%esp),%eax
//将地址%esp+0x1c赋给寄存器%eax
 8048cfa:	89 44 24 0c		mov    %eax,0xc(%esp)
//将%eax中的值赋给*(%esp+0xc)
 8048cfe:	8d 44 24 18		lea    0x18(%esp),%eax
//将地址%esp+0x18赋给寄存器%eax
 8048d02:	89 44 24 08		mov    %eax,0x8(%esp)
//将寄存器%eax中的值赋给*(%esp+0x8)
 8048d06:	c7 44 24 04 f7 a2 04		movl   $0x804a2f7,0x4(%esp)
//将立即数0x804a2f7赋给*(%esp+0x4)
 8048d0d:	08 
 8048d0e:	8b 44 24 30		mov    0x30(%esp),%eax
//将*(%esp+0x30)赋给寄存器%eax
 8048d12:	89 04 24		mov    %eax,(%esp)
//将寄存器%eax中的值赋给*(%esp)
 8048d15:	e8 46 fb ff ff		call   8048860 <__isoc99_sscanf@plt>
//执行函数,可以推出是进行输入
 8048d1a:	83 f8 02		cmp    $0x2,%eax
//比较0x2与寄存器%eax中的值
 8048d1d:	75 07		jne    8048d26 <phase_4+0x33>
//如果不相等则跳转到8048d26
 8048d1f:	83 7c 24 18 0e		cmpl   $0xe,0x18(%esp)
//比较0xe与*(%esp+0x18)
 8048d24:	76 05		jbe    8048d2b <phase_4+0x38>
//如果小于等于则跳转到8048d2b
 8048d26:	e8 1a 04 00 00		call   8049145 <explode_bomb>
//炸弹爆炸
 8048d2b:	c7 44 24 08 0e 00 00		movl   $0xe,0x8(%esp)
//将0xe赋给*(%esp+0x8),即函数的第三个参数
 8048d32:	00 
 8048d33:	c7 44 24 04 00 00 00		movl   $0x0,0x4(%esp)
//将0x0赋给*(%esp+0x4),即函数的第二个参数
 8048d3a:	00 
 8048d3b:	8b 44 24 18		mov    0x18(%esp),%eax
//将*(%esp+0x18)赋给寄存器%eax,即函数的第一个参数(我们的第一个输入)
 8048d3f:	89 04 24		mov    %eax,(%esp)
//将寄存器%eax中的值赋给*(%esp)
 8048d42:	e8 4f ff ff ff		call   8048c96 <func4>
//执行函数func4
 8048d47:	83 f8 2b		cmp    $0x2b,%eax
//比较0x2b与寄存器%eax中的值的大小
 8048d4a:	75 07		jne    8048d53 <phase_4+0x60>
//如果不相等则跳转到8048d53
 8048d4c:	83 7c 24 1c 2b		cmpl   $0x2b,0x1c(%esp)
//比较0x2b与*(%esp+0x1c)的大小
 8048d51:	74 05		je     8048d58 <phase_4+0x65>
//如果相等则跳转到8048d58
 8048d53:	e8 ed 03 00 00		call   8049145 <explode_bomb>
//炸弹爆炸
 8048d58:	83 c4 2c		add    $0x2c,%esp
//将寄存器%esp加上0x2c用于回收栈空间
 8048d5b:	c3		ret

func4函数

func4:
08048c96 <func4>:
 8048c96:	56		push   %esi
 8048c97:	53		push   %ebx
 8048c98:	83 ec 14		sub    $0x14,%esp
 8048c9b:	8b 54 24 20		mov    0x20(%esp),%edx
 8048c9f:	8b 44 24 24		mov    0x24(%esp),%eax
 8048ca3:	8b 74 24 28		mov    0x28(%esp),%esi
 8048ca7:	89 f1		mov    %esi,%ecx
//可以推出以上传递函数用到的三个参数(假设为x、y、z)给寄存器
 8048ca9:	29 c1		sub    %eax,%ecx
//将%ecx中的值减去%eax中的值,即z-y
 8048cab:	89 cb		mov    %ecx,%ebx
 8048cad:	c1 eb 1f		shr    $0x1f,%ebx
//将z-y的值右移31位,即取其符号位
 8048cb0:	01 d9		add    %ebx,%ecx
//z-y为负则加上1,否则加上0
 8048cb2:	d1 f9		sar    %ecx
//算术左移一位,即%ecx中的值/2
 8048cb4:	8d 1c 01		lea    (%ecx,%eax,1),%ebx
//y+(z-y+(1))/2赋给寄存器%ebx
 8048cb7:	39 d3		cmp    %edx,%ebx
//将x与寄存器%ebx中的值进行比较
 8048cb9:	7e 17		jle    8048cd2 <func4+0x3c>
//小于等于则跳转到8048cd2
 8048cbb:	8d 4b ff		lea    -0x1(%ebx),%ecx
 8048cbe:	89 4c 24 08		mov    %ecx,0x8(%esp)
 8048cc2:	89 44 24 04		mov    %eax,0x4(%esp)
 8048cc6:	89 14 24		mov    %edx,(%esp)
 8048cc9:	e8 c8 ff ff ff		call   8048c96 <func4>
 8048cce:	01 d8		add    %ebx,%eax
//如果x<result则返回result+func4(x,y,result-1)
 8048cd0:	eb 1b		jmp    8048ced <func4+0x57>
//跳转到8048ced
 8048cd2:	89 d8		mov    %ebx,%eax
//x=result则返回result
 8048cd4:	39 d3		cmp    %edx,%ebx
//比较x与result的大小
 8048cd6:	7d 15		jge    8048ced <func4+0x57>
//大于等于则跳转到8048ced
 8048cd8:	89 74 24 08		mov    %esi,0x8(%esp)
 8048cdc:	8d 43 01		lea    0x1(%ebx),%eax
 8048cdf:	89 44 24 04		mov    %eax,0x4(%esp)
 8048ce3:	89 14 24		mov    %edx,(%esp)
 8048ce6:	e8 ab ff ff ff		call   8048c96 <func4>
 8048ceb:	01 d8		add    %ebx,%eax
//如果x>result则执行result+func4(x,result+1,z)
 8048ced:	83 c4 14		add    $0x14,%esp
 8048cf0:	5b		pop    %ebx
 8048cf1:	5e		pop    %esi
 8048cf2:	c3		ret    

通过查看gdb查看0x804a2f7中的值可以得到:
在这里插入图片描述
结合之后的函数名的特点,以及cmp $0x2 %eax可以推出输入的是两个数,然后传递了函数func4的三个参数:输入、0、14执行函数func4
通过分析func4的汇编代码我们可以推出其对应的c程序:
int func4(int x,int y,int z)
{
int result=y+(z-y)/2;
if(x<result)
return result+func4(x,y,result-1);
else if(x>result)
return result+func4(x,result+1,z);
else
return result;
}
其中参数x为我们的输入,y=0,z=0xe(14)
函数执行完之后,通过汇编代码可以看到,程序将函数返回结果与0x2b(43)进行了比较,同时这个比较的值也是我们的第二个输入,也就是说第一个输入要使函数返回结果为43,编写一段程序,不难得到当函数第一个参数为12时,函数返回结果为43,输入12、43:
在这里插入图片描述
可以看到结果正确

第五个

08048d5c <phase_5>:
 8048d5c:	53		push   %ebx
//将寄存器%ebx压栈
 8048d5d:	83 ec 28		sub    $0x28,%esp
//将栈顶指针%esp-0x28,即开辟大小为0x28的栈空间
 8048d60:	8b 5c 24 30		mov    0x30(%esp),%ebx
//将*(%esp+0x30)赋给寄存器%ebx
 8048d64:	65 a1 14 00 00 00		mov    %gs:0x14,%eax
 8048d6a:	89 44 24 1c		mov    %eax,0x1c(%esp)
//将寄存器%eax中的值赋给*(%esp+0x1c)
 8048d6e:	31 c0		xor    %eax,%eax
//将寄存器%eax中的值进行异或
 8048d70:	89 1c 24		mov    %ebx,(%esp)
//将寄存器%ebx中的值赋给*(%esp)
 8048d73:	e8 a3 02 00 00		call   804901b <string_length>
//执行函数
 8048d78:	83 f8 06		cmp    $0x6,%eax
//比较0x6与寄存器%eax中的值的大小
 8048d7b:	74 45		je     8048dc2 <phase_5+0x66>
//如果相等则跳转到8048dc2
 8048d7d:	e8 c3 03 00 00		call   8049145 <explode_bomb>
//炸弹爆炸
 8048d82:	eb 3e		jmp    8048dc2 <phase_5+0x66>
//跳转到8048dc2
 8048d84:	0f b6 14 03		movzbl (%ebx,%eax,1),%edx
//将寄存器%eax和寄存器%ebx中的值相加作0扩展赋给寄存器%edx
 8048d88:	83 e2 0f		and    $0xf,%edx
//将0xf与寄存器%edx进行与运算
 8048d8b:	0f b6 92 d8 a1 04 08		movzbl 0x804a1d8(%edx),%edx
//将寄存器%edx中的值加上0x804ald8作0扩展赋给寄存器%edx
 8048d92:	88 54 04 15		mov    %dl,0x15(%esp,%eax,1)
//将寄存器%edx最后一个字节的值赋给*(%esp+%eax+0x15)
 8048d96:	83 c0 01		add    $0x1,%eax
//寄存器%eax中的值加1
 8048d99:	83 f8 06		cmp    $0x6,%eax
//比较0x6与寄存器%eax的值的大小
 8048d9c:	75 e6		jne    8048d84 <phase_5+0x28>
//如果不相等则跳转到8048d84
 8048d9e:	c6 44 24 1b 00		movb   $0x0,0x1b(%esp)
//将0x0赋给*(%esp+0x1b)
 8048da3:	c7 44 24 04 ae a1 04		movl   $0x804a1ae,0x4(%esp)
//将0x804a1ae赋给*(%esp+0x4)
 8048daa:	08 
 8048dab:	8d 44 24 15		lea    0x15(%esp),%eax
//将地址%esp+0x15赋给寄存器eax
 8048daf:	89 04 24		mov    %eax,(%esp)
//将寄存器%eax中的值赋给*(esp)
 8048db2:	e8 83 02 00 00		call   804903a <strings_not_equal>
//执行函数
 8048db7:	85 c0		test   %eax,%eax
//做与运算
 8048db9:	74 0e		je     8048dc9 <phase_5+0x6d>
//结果为0则跳转到8048dc9
 8048dbb:	e8 85 03 00 00		call   8049145 <explode_bomb>
//炸弹爆炸
 8048dc0:	eb 07		jmp    8048dc9 <phase_5+0x6d>
//跳转到8048dc9
 8048dc2:	b8 00 00 00 00		mov    $0x0,%eax
//将0x0赋给寄存器%eax
 8048dc7:	eb bb		jmp    8048d84 <phase_5+0x28>
//跳转到8048d84
 8048dc9:	8b 44 24 1c		mov    0x1c(%esp),%eax
//将*(%esp+0x1c)赋给寄存器%eax
 8048dcd:	65 33 05 14 00 00 00		xor    %gs:0x14,%eax
 8048dd4:	74 05		je     8048ddb <phase_5+0x7f>
//如果相等则跳转到8048ddb
 8048dd6:	e8 e5 f9 ff ff		call   80487c0 <__stack_chk_fail@plt>
//执行函数
 8048ddb:	83 c4 28		add    $0x28,%esp
//将%esp加上0x28,用于回收栈空间
 8048dde:	5b		pop    %ebx
 8048ddf:	90		nop
 8048de0:	c3		ret

通过分析汇编代码call 804901b <string_length>以及cmp $0x6,%eax可以推出输入为一个长度为6的字符串,由之后的movzbl (%ebx,%eax,1),%edx和and $0xf,%edx可以推出是取的每个字符的低4位(二进制表示的话),由movzbl 0x804a1d8(%edx),%edx可以知道以0x804a1d8为基址进行了查找,使用gdb输入x/s 0x804a1d8得到:
在这里插入图片描述
可以知道0x804a1d8为一个字符串的首地址,以每个字符的低4为下标查找对应的字符,循环6次,将最终结果放到了寄存器%edx中
之后比较了0x804a1ae中的值与刚刚所得到的结果,使用gdb输入x/s 0x804a1ae得到:
在这里插入图片描述
通过以上分析可以知道,该程序的大致执行过程为:
输入一个长度为6的字符串,将每个字符的低4位(二进制表示)作为索引在字符串
maduiersnfotvbyl中查找对应的字符,最终得到“flames”
字符串flames在字符串maduiersnfotvbyl中对应的位置:
在这里插入图片描述
可以知道答案是并不唯一的,输入一组答案:
在这里插入图片描述

第六个

08048de1 <phase_6>:
 8048de1:	56		push   %esi
 8048de2:	53		push   %ebx
 8048de3:	83 ec 44		sub    $0x44,%esp
 8048de6:	8d 44 24 10		lea    0x10(%esp),%eax
 8048dea:	89 44 24 04		mov    %eax,0x4(%esp)
 8048dee:	8b 44 24 50		mov    0x50(%esp),%eax
 8048df2:	89 04 24		mov    %eax,(%esp)
 8048df5:	e8 72 03 00 00		call   804916c <read_six_numbers>
//输入6个数	
 8048dfa:	be 00 00 00 00		mov    $0x0,%esi
 8048dff:	8b 44 b4 10		mov    0x10(%esp,%esi,4),%eax
 8048e03:	83 e8 01		sub    $0x1,%eax
 8048e06:	83 f8 05		cmp    $0x5,%eax
 8048e09:	76 05		jbe    8048e10 <phase_6+0x2f>
 8048e0b:	e8 35 03 00 00		call   8049145 <explode_bomb>
 8048e10:	83 c6 01		add    $0x1,%esi
 8048e13:	83 fe 06		cmp    $0x6,%esi
 8048e16:	74 1b		je     8048e33 <phase_6+0x52>
//这里可以看到对输入的六个数的每个数进行了判断,每个数的大小不能大于6	
 8048e18:	89 f3		mov    %esi,%ebx
 8048e1a:	8b 44 9c 10		mov    0x10(%esp,%ebx,4),%eax
 8048e1e:	39 44 b4 0c		cmp    %eax,0xc(%esp,%esi,4)
 8048e22:	75 05		jne    8048e29 <phase_6+0x48>
 8048e24:	e8 1c 03 00 00		call   8049145 <explode_bomb>
 8048e29:	83 c3 01		add    $0x1,%ebx
 8048e2c:	83 fb 05		cmp    $0x5,%ebx
 8048e2f:	7e e9		jle    8048e1a <phase_6+0x39>
 8048e31:	eb cc		jmp    8048dff <phase_6+0x1e>
//这是内层循环要求输入的六个数的大小不能相等	
 8048e33:	8d 44 24 10		lea    0x10(%esp),%eax
 8048e37:	8d 5c 24 28		lea    0x28(%esp),%ebx
 8048e3b:	b9 07 00 00 00		mov    $0x7,%ecx
 8048e40:	89 ca		mov    %ecx,%edx
 8048e42:	2b 10		sub    (%eax),%edx
 8048e44:	89 10		mov    %edx,(%eax)
 8048e46:	83 c0 04		add    $0x4,%eax
 8048e49:	39 d8		cmp    %ebx,%eax
 8048e4b:	75 f3		jne    8048e40 <phase_6+0x5f>
//这里对输入的每个数做了处理7-a[i]	
 8048e4d:	bb 00 00 00 00		mov    $0x0,%ebx
 8048e52:	eb 1d		jmp    8048e71 <phase_6+0x90>
//开始一个循环
 8048e54:	8b 52 08		mov    0x8(%edx),%edx
//每次偏移0x8
 8048e57:	83 c0 01		add    $0x1,%eax
 8048e5a:	39 c8		cmp    %ecx,%eax
//?
 8048e5c:	75 f6		jne    8048e54 <phase_6+0x73>
 8048e5e:	eb 05		jmp    8048e65 <phase_6+0x84>
 8048e60:	ba 3c c1 04 08		mov    $0x804c13c,%edx
 8048e65:	89 54 b4 28		mov    %edx,0x28(%esp,%esi,4)
//将链表中的每个值放到一块新的空间
 8048e69:	83 c3 01		add    $0x1,%ebx
 8048e6c:	83 fb 06		cmp    $0x6,%ebx
 8048e6f:	74 17		je     8048e88 <phase_6+0xa7>
 8048e71:	89 de		mov    %ebx,%esi
 8048e73:	8b 4c 9c 10		mov    0x10(%esp,%ebx,4),%ecx
//依次获得输入的6个数
 8048e77:	83 f9 01		cmp    $0x1,%ecx
 8048e7a:	7e e4		jle    8048e60 <phase_6+0x7f>
//<=1则跳回开头
 8048e7c:	b8 01 00 00 00		mov    $0x1,%eax
 8048e81:	ba 3c c1 04 08		mov    $0x804c13c,%edx
 8048e86:	eb cc		jmp    8048e54 <phase_6+0x73>
//?
 8048e88:	8b 5c 24 28		mov    0x28(%esp),%ebx //链表的第一个
 8048e8c:	8d 44 24 2c		lea    0x2c(%esp),%eax  //链表的第二个
 8048e90:	8d 74 24 40		lea    0x40(%esp),%esi   //链表的第七个(边界)
 8048e94:	89 d9		mov    %ebx,%ecx
//开始循环
 8048e96:	8b 10		mov    (%eax),%edx    //相当于节点->next
 8048e98:	89 51 08		mov    %edx,0x8(%ecx) //n[i]->next=n[i+1]
 8048e9b:	83 c0 04		add    $0x4,%eax     //增加4
 8048e9e:	39 f0		cmp    %esi,%eax
 8048ea0:	74 04		je     8048ea6 <phase_6+0xc5>
//%eax为链表最后一个节点的地址时停止
 8048ea2:	89 d1		mov    %edx,%ecx
 8048ea4:	eb f0		jmp    8048e96 <phase_6+0xb5>
 8048ea6:	c7 42 08 00 00 00 00		movl   $0x0,0x8(%edx)
//将各个节点按照我们的输入的7-a[i]的顺序链接起来	
 8048ead:	be 05 00 00 00		mov    $0x5,%esi
 8048eb2:	8b 43 08		mov    0x8(%ebx),%eax
 8048eb5:	8b 00		mov    (%eax),%eax
 8048eb7:	39 03		cmp    %eax,(%ebx)
//比较前一个节点与后一个节点的值的大小
 8048eb9:	7d 05		jge    8048ec0 <phase_6+0xdf>
 8048ebb:	e8 85 02 00 00		call   8049145 <explode_bomb>
//前面的值比后面的值大则炸弹爆炸
 8048ec0:	8b 5b 08		mov    0x8(%ebx),%ebx  //移动指针
 8048ec3:	83 ee 01		sub    $0x1,%esi
 8048ec6:	75 ea		jne    8048eb2 <phase_6+0xd1>//循环
 8048ec8:	83 c4 44		add    $0x44,%esp
 8048ecb:	5b		pop    %ebx
 8048ecc:	5e		pop    %esi
 8048ecd:	c3		ret    

这个的汇编代码较为复杂,采用了逐段分析的方式,程序大致执行过程如下:
首先由我们输入6个数,这6个数必须满足不能大于6同时互不相等,假设为a[]
接着对每个输入的数进行运算7-a[i]
然后我们从一个地址拷贝了数据,这个地址为0x804c13c,使用gdb查看:
在这里插入图片描述
可以看到,以三个为一组可以分成6组数据:
在这里插入图片描述
结合后面的分析可以得知这是一个链表的数据结构
之后通过调整next的指向将链表按照7-a[i]的顺序进行了链接
在调整链表顺序之后,其值的大小必须满足降序排列
综上,我们可以知道,7-a[0]、7-a[1]…顺序排列的链表必须满足降序
将链表降序排列得到:
在这里插入图片描述
得到7-a[i]的序列为:5、3、6、4、1、2
则a[i]的序列为:2、4、1、3、6、5
输入该序列:
在这里插入图片描述

隐藏关

寻找隐藏关

080492b6 <phase_defused>:
 80492b6:	81 ec 8c 00 00 00		sub    $0x8c,%esp
 80492bc:	65 a1 14 00 00 00		mov    %gs:0x14,%eax
 80492c2:	89 44 24 7c		mov    %eax,0x7c(%esp)
 80492c6:	31 c0		xor    %eax,%eax
 80492c8:	83 3d c8 c3 04 08 06		cmpl   $0x6,0x804c3c8
 80492cf:	75 72		jne    8049343 <phase_defused+0x8d>
 80492d1:	8d 44 24 2c		lea    0x2c(%esp),%eax
 80492d5:	89 44 24 10		mov    %eax,0x10(%esp)
 80492d9:	8d 44 24 28		lea    0x28(%esp),%eax
 80492dd:	89 44 24 0c		mov    %eax,0xc(%esp)
 80492e1:	8d 44 24 24		lea    0x24(%esp),%eax
 80492e5:	89 44 24 08		mov    %eax,0x8(%esp)
 80492e9:	c7 44 24 04 51 a3 04		movl   $0x804a351,0x4(%esp)
 80492f0:	08 
 80492f1:	c7 04 24 d0 c4 04 08		movl   $0x804c4d0,(%esp)
 80492f8:	e8 63 f5 ff ff		call   8048860 <__isoc99_sscanf@plt>
 80492fd:	83 f8 03		cmp    $0x3,%eax
//该输入函数同样出现在第四关,这里判断第四关输入了几个值
 8049300:	75 35		jne    8049337 <phase_defused+0x81>
 8049302:	c7 44 24 04 5a a3 04		movl   $0x804a35a,0x4(%esp)
//使用gdb查看该地址中的内容为”DrEvil”
 8049309:	08 
 804930a:	8d 44 24 2c		lea    0x2c(%esp),%eax
 804930e:	89 04 24		mov    %eax,(%esp)
 8049311:	e8 24 fd ff ff		call   804903a <strings_not_equal>
 8049316:	85 c0		test   %eax,%eax
 8049318:	75 1d		jne    8049337 <phase_defused+0x81>
 804931a:	c7 04 24 20 a2 04 08		movl   $0x804a220,(%esp)
//使用gdb查看该地址中的内容“Curses, you've found the secret phase!”
 8049321:	e8 ca f4 ff ff		call   80487f0 <puts@plt>
 8049326:	c7 04 24 48 a2 04 08		movl   $0x804a248,(%esp)
//使用gdb查看该地址中的内容“But finding it and solving it are quite different...”
 804932d:	e8 be f4 ff ff		call   80487f0 <puts@plt>
 8049332:	e8 e8 fb ff ff		call   8048f1f <secret_phase>
//成功进入隐藏关卡
 8049337:	c7 04 24 80 a2 04 08		movl   $0x804a280,(%esp)
 804933e:	e8 ad f4 ff ff		call   80487f0 <puts@plt>
 8049343:	8b 44 24 7c		mov    0x7c(%esp),%eax
 8049347:	65 33 05 14 00 00 00		xor    %gs:0x14,%eax
 804934e:	74 05		je     8049355 <phase_defused+0x9f>
 8049350:	e8 6b f4 ff ff		call   80487c0 <__stack_chk_fail@plt>
 8049355:	81 c4 8c 00 00 00		add    $0x8c,%esp
 804935b:	c3		ret    
 804935c:	66 90		xchg   %ax,%ax
 804935e:	66 90		xchg   %ax,%ax

通过分析这一段汇编代码我们可以得出,在第四关输入值的时候,我们是可以输入三个值的,而且我们输入的第三个值将会作为我们能否进入隐藏关的钥匙,通过gdb查看用来比较的值:
在这里插入图片描述
我们可以猜测,这把钥匙就是字符串“DrEvil”,在第四关额外输入该字符串:
在这里插入图片描述果然成功进入了隐藏关!

隐藏关代码

08048f1f <secret_phase>:
 8048f1f:	53		push   %ebx
 8048f20:	83 ec 18		sub    $0x18,%esp
 8048f23:	e8 94 02 00 00		call   80491bc <read_line>
 8048f28:	c7 44 24 08 0a 00 00		movl   $0xa,0x8(%esp)
 8048f2f:	00 
 8048f30:	c7 44 24 04 00 00 00		movl   $0x0,0x4(%esp)
 8048f37:	00 
 8048f38:	89 04 24		mov    %eax,(%esp)
//这里传递函数的三个参数
 8048f3b:	e8 90 f9 ff ff		call   80488d0 <strtol@plt>
//了解函数strtol的使用可以知道其标准格式为strtol(char*ch1,char*ch2,int base)
//具体作用就是将*ch1字符串当作base进制的返回,这里传入0xa说明输入一个十进制数
 8048f40:	89 c3		mov    %eax,%ebx
 8048f42:	8d 40 ff		lea    -0x1(%eax),%eax
 8048f45:	3d e8 03 00 00		cmp    $0x3e8,%eax
 8048f4a:	76 05		jbe    8048f51 <secret_phase+0x32>
//这里控制输入的值不能大于0x3e8,这里其实与后面是有关联的
 8048f4c:	e8 f4 01 00 00		call   8049145 <explode_bomb>
 8048f51:	89 5c 24 04		mov    %ebx,0x4(%esp)
 8048f55:	c7 04 24 88 c0 04 08		movl   $0x804c088,(%esp)
//传入函数参数,第二个为我们的输入,第一个为一个固定值
 8048f5c:	e8 6d ff ff ff		call   8048ece <fun7>
//调用函数func7
 8048f61:	83 f8 04		cmp    $0x4,%eax
 8048f64:	74 05		je     8048f6b <secret_phase+0x4c>
 8048f66:	e8 da 01 00 00		call   8049145 <explode_bomb>
 8048f6b:	c7 04 24 88 a1 04 08		movl   $0x804a188,(%esp)
 8048f72:	e8 79 f8 ff ff		call   80487f0 <puts@plt>
 8048f77:	e8 3a 03 00 00		call   80492b6 <phase_defused>
 8048f7c:	83 c4 18		add    $0x18,%esp
 8048f7f:	5b		pop    %ebx
 8048f80:	c3		ret    
 8048f81:	66 90		xchg   %ax,%ax
 8048f83:	66 90		xchg   %ax,%ax
 8048f85:	66 90		xchg   %ax,%ax
 8048f87:	66 90		xchg   %ax,%ax
 8048f89:	66 90		xchg   %ax,%ax
 8048f8b:	66 90		xchg   %ax,%ax
 8048f8d:	66 90		xchg   %ax,%ax
 8048f8f:	90		nop

func7函数

08048ece <fun7>:
 8048ece:	53		push   %ebx
 8048ecf:	83 ec 18		sub    $0x18,%esp
 8048ed2:	8b 54 24 20		mov    0x20(%esp),%edx
 8048ed6:	8b 4c 24 24		mov    0x24(%esp),%ecx
//传入函数的两个参数,假设叫*p和n
 8048eda:	85 d2		test   %edx,%edx
 8048edc:	74 37		je     8048f15 <fun7+0x47>
//为零则直接结束
 8048ede:	8b 1a		mov    (%edx),%ebx
 8048ee0:	39 cb		cmp    %ecx,%ebx
 8048ee2:	7e 13		jle    8048ef7 <fun7+0x29>
//*p<=n跳转到8048ef7
 8048ee4:	89 4c 24 04		mov    %ecx,0x4(%esp)
 8048ee8:	8b 42 04		mov    0x4(%edx),%eax
//传入函数的两个参数*(p+4)、n
 8048eeb:	89 04 24		mov    %eax,(%esp)
 8048eee:	e8 db ff ff ff		call   8048ece <fun7>
 8048ef3:	01 c0		add    %eax,%eax
//返回2*func7(*(p+4), n),这里对应*p>n的情况
 8048ef5:	eb 23		jmp    8048f1a <fun7+0x4c>
 8048ef7:	b8 00 00 00 00		mov    $0x0,%eax
 8048efc:	39 cb		cmp    %ecx,%ebx
 8048efe:	74 1a		je     8048f1a <fun7+0x4c>
//如果*p=n则跳转到8048f1a(结束),这里对应*p==n的情况
 8048f00:	89 4c 24 04		mov    %ecx,0x4(%esp)
 8048f04:	8b 42 08		mov    0x8(%edx),%eax
//传入函数的两个参数*(p+8)、n
 8048f07:	89 04 24		mov    %eax,(%esp)
 8048f0a:	e8 bf ff ff ff		call   8048ece <fun7>
 8048f0f:	8d 44 00 01		lea    0x1(%eax,%eax,1),%eax
//返回2*func7(*(p+8), n)+1,这里对应*p<n的情况
 8048f13:	eb 05		jmp    8048f1a <fun7+0x4c>
 8048f15:	b8 ff ff ff ff		mov    $0xffffffff,%eax
 8048f1a:	83 c4 18		add    $0x18,%esp
 8048f1d:	5b		pop    %ebx
 8048f1e:	c3		ret    

这时可以大致分析出func7函数的程序代码:

int  func7(? *p, int n)
{
if(*p==0) return 0;
if(*p==n) return 0;
else if(*p<n) return 2*func7(*(p+8), n)+1;
else return 2*func7(*(p+4), n);
}

这时我们会疑惑* p到底是什么,* (p+4)、* (p+8)作为参数是什么意思?
回到隐藏关代码,使用gdb查看以地址0x804c088开始的内容:
在这里插入图片描述
可以看到,将他们以三个为一组,可以分成15组,而且都是数据,地址,地址的格式
不断的寻找这些地址之间的关系,可以发现:
在这里插入图片描述
这时这个数据结构就一目了然了,它居然是一个二叉树!
那么也就可以解释为什么输入不能大于0x3e9了,因为该值就是这个二叉树所有值中的最大值了,其次,我们也可以解释*(p+4)、*(p+8)的含义了,这两个写法正对应了二叉树查找左子树和右子树的过程。
之后我们可以看到,将返回结果与0x4进行了比较,相等的话可以通过这个隐藏关
如何使返回结果为0x4? 分析func7函数我们知道,查找到目标值时将会返回0,查找一次左子树返回结果的2倍,查找一次右子树返回结果的2倍加1。
由于最多查找3次,那么就是0->2x0+1->2x1->2x2得到4
也就是查找两次左子树,一次右子树,对应值为0x7,由于输入为10进制,输入7:
在这里插入图片描述
炸弹成功拆除!

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼树C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值