CSAPP:BombLab

通过对主函数的观察发现里面有六个炸弹函数,先是获得输入input = read_line(); 接着运行 phase_1(input); 判断是否是正确答案,如果是正确答案,调用phase_defused(); 来获取接触炸弹的方式

输入"gdb -q bomb"进行调试

目录

前言

一、phase_1

二、phase_2

三、phase_3

四、phase_4

五、phase_5

六、phase_6

七、secret_phase

 


前言

  • 本实验是《深入理解计算机系统》一书中的附带实验——二进制炸弹实验。二进制炸弹是一个作为目标代码文件提供给学生们的程序。运行时,它提示用户输入6个不同的字符串。如果其中的任何一个不正确,炸弹就会“爆炸”,打印出一条错误信息,并且在一个分级服务器上记录事件日志。学生们必须通过对程序反汇编和逆向工程来测定应该是哪6个串,从而解除他们各自炸弹的雷管。
  • 该实验教会学生理解汇编语言,并且强制他们学习怎么使用调试器。
  • 本文用于记录之前做实验的一些信息,可能思路有些凌乱,谨慎参考

一、phase_1

进入调试模式之后,首先输入"disas phase_1"查看phase_1函数的汇编代码

汇编代码及其说明

Dump of assembler code for function phase_1:
   0x08048b50 <+0>:	sub    $0x1c,%esp    //栈指针%esp-0x1c,即该操作是为了给当前帧开辟一个大小为0x1c字节的空间
   0x08048b53 <+3>:	movl   $0x804a264,0x4(%esp)    //将0x804a264(用于比较的字符串在内存中存放的地址)赋给*(0x4+%esp)
   0x08048b5b <+11>:	mov    0x20(%esp),%eax     //将*(0x20+%esp)赋给寄存器%eax,其实就是输入的字符串 (字符串的理由:在后面的代码里,调用了一个strings_not_equal函数。我猜是字符串。用gdb读一下 果然是字符串。)
   0x08048b5f <+15>:	mov    %eax,(%esp)    //将寄存器%eax中的值赋给*(%esp)
   0x08048b62 <+18>:	call   0x8049084 <strings_not_equal>    //比较两字符串是否相等,根据结果给%eax赋值,可以反汇编这个函数 具体看其中值的具体变化)
   0x08048b67 <+23>:	test   %eax,%eax    
   0x08048b69 <+25>:	je     0x8048b70 <phase_1+32>    //如果eax=0,则进入phase2;否则爆炸
   0x08048b6b <+27>:	call   0x8049196 <explode_bomb>
   0x08048b70 <+32>:	add    $0x1c,%esp    //将%esp的值-0x1c,即栈指针往栈底方向移动0x1c字节,用于回收空间
   0x08048b73 <+35>:	ret    //返回
End of assembler dump.

根据汇编代码可以推出,%eax用于保存的是输入的字符串,地址0x804a264处存放的是用于比较的字符串(即我们应该输入的正确字符串)

输入"x/s 0x804a264"查看此处的信息,得到字符串"When a problem comes along, you must zip it!"

输入"run"运行该程序,输入字符串"When a problem comes along, you must zip it!"测试是否成功拆弹?!

OK!成功拆掉第一个炸弹!!让我们继续下一阶段的拆弹吧!

二、phase_2

汇编代码及其说明

Dump of assembler code for function phase_2:
   0x08048b74 <+0>:	push   %esi       //将程序的入口地址压入栈中
   0x08048b75 <+1>:	push   %ebx        //将被调用者保存寄存器中的值压入栈中,以便在返回前可以恢复它们
   0x08048b76 <+2>:	sub    $0x34,%esp    
   0x08048b79 <+5>:	lea    0x18(%esp),%eax    //将地址0x18+%esp赋给寄存器%eax(可以看出%eax用于保存变量)    
   0x08048b7d <+9>:	mov    %eax,0x4(%esp)
   0x08048b81 <+13>:	mov    0x40(%esp),%eax
   0x08048b85 <+17>:	mov    %eax,(%esp)
   0x08048b88 <+20>:	call   0x80492cb <read_six_numbers>    //根据该函数名称可以推测答案是输入六个数
   0x08048b8d <+25>:	cmpl   $0x1,0x18(%esp)        
   0x08048b92 <+30>:	je     0x8048b99 <phase_2+37>    //如果*(%esp+0x18)=0x1就跳转到phase+37,否则执行explode_bomb函数;所以可以推测*(%esp+0x18)=0x1
   0x08048b94 <+32>:	call   0x8049196 <explode_bomb>
   0x08048b99 <+37>:	lea    0x1c(%esp),%ebx    //将地址%esp+0x1c赋给寄存器%ebx
   0x08048b9d <+41>:	lea    0x30(%esp),%esi    //将地址%esp+0x30赋给寄存器%esi
   0x08048ba1 <+45>:	mov    -0x4(%ebx),%eax    //将*(%ebx-0x4)也就是*(%esp+0x18)也就是0x1赋给寄存器%eax
   0x08048ba4 <+48>:	add    %eax,%eax        //%eax中的值翻倍
   0x08048ba6 <+50>:	cmp    %eax,(%ebx)      //将%eax中的值与*(%ebx)也就是*(%esp+0x1c)作比较
   0x08048ba8 <+52>:	je     0x8048baf <phase_2+59>    //如果相等就跳转至<phase_2+59>,可以推测出*(%esp+0x1c)为0x2;否则会执行<explode_bomb>
   0x08048baa <+54>:	call   0x8049196 <explode_bomb>
   0x08048baf <+59>:	add    $0x4,%ebx        //%ebx加0x4
   0x08048bb2 <+62>:	cmp    %esi,%ebx        //循环条件,判断%esi是否等于%ebx
   0x08048bb4 <+64>:	jne    0x8048ba1 <phase_2+45>    如果不等于就跳转至<phase_2+45>
   0x08048bb6 <+66>:	add    $0x34,%esp    //栈顶指针往栈底方向移动0x34个字节
   0x08048bb9 <+69>:	pop    %ebx          //恢复%ebx原先的数据
   0x08048bba <+70>:	pop    %esi          //弹出程序的入口地址 
   0x08048bbb <+71>:	ret    //结束
End of assembler dump.

根据第10行中的" cmpl   $0x1,0x18(%esp) " 以及转移地址" je  0x8048b99 <phase_2+37>"得到地址为%esp+0x1中的值为0x1;根据第22行中的"jne  0x8048ba1 <phase_2+45>"得到程序进入了一个循环,循环终止的条件是第21行"%esi=%ebx",在循环中进行的是第16行%eax+=%eax后再将值保存到相应的地址单元中

故可以看出存储该六位输入数据的地址单元为%esp+0x18,%esp+0x1c,%esp+0x20,%esp+0x24,%esp+0x28,%esp+0x2c;存储的六位输入数据分别为0x1,0x2,0x4,0x8,0x10,0x20,对应十进制就是1,2,4,8,16,32

运行之后输入答案"1 2 4 8 16 32"之后没有爆炸说明拆弹成功!

三、phase_3

汇编代码及其说明

Dump of assembler code for function phase_3:
   0x08048bbc <+0>:	sub    $0x3c,%esp
   0x08048bbf <+3>:	lea    0x28(%esp),%eax
   0x08048bc3 <+7>:	mov    %eax,0x10(%esp)
   0x08048bc7 <+11>:	lea    0x2f(%esp),%eax
   0x08048bcb <+15>:	mov    %eax,0xc(%esp)
   0x08048bcf <+19>:	lea    0x24(%esp),%eax
   0x08048bd3 <+23>:	mov    %eax,0x8(%esp)
   0x08048bd7 <+27>:	movl   $0x804a2ba,0x4(%esp)
   0x08048bdf <+35>:	mov    0x40(%esp),%eax
   0x08048be3 <+39>:	mov    %eax,(%esp)
   0x08048be6 <+42>:	call   0x8048870 <__isoc99_sscanf@plt>
   0x08048beb <+47>:	cmp    $0x2,%eax
   0x08048bee <+50>:	jg     0x8048bf5 <phase_3+57>    //%eax>0x2,跳转至<phase_3+57>;否则跳转至<explode_bomb>,炸弹爆炸;因此推测输入中至少有一个数大于0x2
   0x08048bf0 <+52>:	call   0x8049196 <explode_bomb>
   0x08048bf5 <+57>:	cmpl   $0x7,0x24(%esp)
   0x08048bfa <+62>:	ja     0x8048cf2 <phase_3+310>    //*(0x24+esp)>0x7的话,跳转至<phase_3+310> 会出现炸弹爆炸,所以推测得到*(0x24+esp)<=0x7
   0x08048c00 <+68>:	mov    0x24(%esp),%eax
   0x08048c04 <+72>:	jmp    *0x804a2e0(,%eax,4)
   0x08048c0b <+79>:	mov    $0x78,%eax
   0x08048c10 <+84>:	cmpl   $0x3b8,0x28(%esp)
   0x08048c18 <+92>:	je     0x8048cfc <phase_3+320>    //由此推测出,*(0x28+esp)=0x3b8
   0x08048c1e <+98>:	call   0x8049196 <explode_bomb>
   0x08048c23 <+103>:	mov    $0x78,%eax
   0x08048c28 <+108>:	jmp    0x8048cfc <phase_3+320>
   0x08048c2d <+113>:	mov    $0x6d,%eax
   0x08048c32 <+118>:	cmpl   $0x69,0x28(%esp)
   0x08048c37 <+123>:	je     0x8048cfc <phase_3+320>    //由此推测出,*(0x28+esp)=0x69
   0x08048c3d <+129>:	call   0x8049196 <explode_bomb>
   0x08048c42 <+134>:	mov    $0x6d,%eax
   0x08048c47 <+139>:	jmp    0x8048cfc <phase_3+320>
   0x08048c4c <+144>:	mov    $0x78,%eax
   0x08048c51 <+149>:	cmpl   $0x33e,0x28(%esp)
   0x08048c59 <+157>:	je     0x8048cfc <phase_3+320>    //由此推测出,*(0x28+esp)=0x33e
   0x08048c5f <+163>:	call   0x8049196 <explode_bomb>
   0x08048c64 <+168>:	mov    $0x78,%eax
   0x08048c69 <+173>:	jmp    0x8048cfc <phase_3+320>
   0x08048c6e <+178>:	mov    $0x71,%eax
   0x08048c73 <+183>:	cmpl   $0x345,0x28(%esp)
   0x08048c7b <+191>:	je     0x8048cfc <phase_3+320>    //由此推测出,*(0x28+esp)=0x345
   0x08048c7d <+193>:	call   0x8049196 <explode_bomb>
   0x08048c82 <+198>:	mov    $0x71,%eax
   0x08048c87 <+203>:	jmp    0x8048cfc <phase_3+320>
   0x08048c89 <+205>:	mov    $0x70,%eax
   0x08048c8e <+210>:	cmpl   $0xca,0x28(%esp)          //由此推测出,*(0x28+esp)=0x70
   0x08048c96 <+218>:	je     0x8048cfc <phase_3+320>
   0x08048c98 <+220>:	call   0x8049196 <explode_bomb>
   0x08048c9d <+225>:	mov    $0x70,%eax
   0x08048ca2 <+230>:	jmp    0x8048cfc <phase_3+320>
   0x08048ca4 <+232>:	mov    $0x75,%eax
   0x08048ca9 <+237>:	cmpl   $0x14f,0x28(%esp)          //由此推测出,*(0x28+esp)=0x14f
   0x08048cb1 <+245>:	je     0x8048cfc <phase_3+320>
   0x08048cb3 <+247>:	call   0x8049196 <explode_bomb>
   0x08048cb8 <+252>:	mov    $0x75,%eax
   0x08048cbd <+257>:	jmp    0x8048cfc <phase_3+320>
   0x08048cbf <+259>:	mov    $0x69,%eax
   0x08048cc4 <+264>:	cmpl   $0x189,0x28(%esp)          //由此推测出,*(0x28+esp)=0x189
   0x08048ccc <+272>:	je     0x8048cfc <phase_3+320>
   0x08048cce <+274>:	call   0x8049196 <explode_bomb>
   0x08048cd3 <+279>:	mov    $0x69,%eax
   0x08048cd8 <+284>:	jmp    0x8048cfc <phase_3+320>
   0x08048cda <+286>:	mov    $0x62,%eax
   0x08048cdf <+291>:	cmpl   $0x32,0x28(%esp)           //由此推测出,*(0x28+esp)=0x32
   0x08048ce4 <+296>:	je     0x8048cfc <phase_3+320>
   0x08048ce6 <+298>:	call   0x8049196 <explode_bomb>
   0x08048ceb <+303>:	mov    $0x62,%eax
   0x08048cf0 <+308>:	jmp    0x8048cfc <phase_3+320>
   0x08048cf2 <+310>:	call   0x8049196 <explode_bomb>
   0x08048cf7 <+315>:	mov    $0x6c,%eax
   0x08048cfc <+320>:	cmp    0x2f(%esp),%al             //由此推测出,*(0x2f+esp)=%al   
   0x08048d00 <+324>:	je     0x8048d07 <phase_3+331>
   0x08048d02 <+326>:	call   0x8049196 <explode_bomb>
   0x08048d07 <+331>:	add    $0x3c,%esp
   0x08048d0a <+334>:	ret    
End of assembler dump.

 

根据第9行"movl   $0x804a2ba,0x4(%esp)"以及第12行"call   0x8048870 <__isoc99_sscanf@plt>"中的scanf提示,使用"x/s 0x804a2ba"将地址单元为0x804a2ba中的信息按照字符串的形式打印出来得到最终的答案输入形式应该是“整型,字符,整型”

根据第3行到第8行可以得到地址(0x24+esp)保存到(0x8+esp)中,地址(0x28+esp)保存到(0x10+esp)中,地址(0x2f+esp)保存到(0xc+esp)中;可以得到*(0x24+esp)<=0x7

令输入的*(0x24+esp)=0x1,输入"x/s *(0x804a2e0+1*4)"查看最终的跳转地址0x8048c2d <phase_3+113>,因为*(0x24+esp)对应了八种情况,所以答案有八种情况;

根据"0x08048c2d <+113>:    mov    $0x6d,%eax
        0x08048c32 <+118>:    cmpl   $0x69,0x28(%esp)
        0x08048c37 <+123>:    je     0x8048cfc <phase_3+320>"得到%eax=0x6d,*(0x28+esp)=0x69,紧接着跳转至

      "0x08048cfc <+320>:    cmp    0x2f(%esp),%al
       0x08048d00 <+324>:    je     0x8048d07 <phase_3+331>" 得到*(0x2f+esp)=%al=6d;

故最终三个输入的十六进制分别是0x1,0x6d,0x69,对应"%d %c %d"格式得到输入为1,m,105

让我们测试一下!Nice!又破解了一个!

四、phase_4

汇编代码如下:

Dump of assembler code for function phase_4:
   0x08048d74 <+0>:	sub    $0x2c,%esp
   0x08048d77 <+3>:	lea    0x1c(%esp),%eax
   0x08048d7b <+7>:	mov    %eax,0xc(%esp)
   0x08048d7f <+11>:	lea    0x18(%esp),%eax
   0x08048d83 <+15>:	mov    %eax,0x8(%esp)
   0x08048d87 <+19>:	movl   $0x804a4a3,0x4(%esp)
   0x08048d8f <+27>:	mov    0x30(%esp),%eax
   0x08048d93 <+31>:	mov    %eax,(%esp)
   0x08048d96 <+34>:	call   0x8048870 <__isoc99_sscanf@plt>    //根据scanf提示打印地址0x804a4a3中的值
   0x08048d9b <+39>:	cmp    $0x2,%eax
   0x08048d9e <+42>:	jne    0x8048dad <phase_4+57>    //根据跳转逻辑可以推测此时%eax=0x2
   0x08048da0 <+44>:	mov    0x18(%esp),%eax
   0x08048da4 <+48>:	test   %eax,%eax
   0x08048da6 <+50>:	js     0x8048dad <phase_4+57>    //根据跳转逻辑可以推测此时输入的第一个数是非负数
   0x08048da8 <+52>:	cmp    $0xe,%eax
   0x08048dab <+55>:	jle    0x8048db2 <phase_4+62>    //根据跳转逻辑可以推测此时%eax<=0xe,所以第一个输入是0-14之间某一个数
   0x08048dad <+57>:	call   0x8049196 <explode_bomb>
   0x08048db2 <+62>:	movl   $0xe,0x8(%esp)
   0x08048dba <+70>:	movl   $0x0,0x4(%esp)
   0x08048dc2 <+78>:	mov    0x18(%esp),%eax
   0x08048dc6 <+82>:	mov    %eax,(%esp)    //将%eax,0,14作为参数传给func4函数
   0x08048dc9 <+85>:	call   0x8048d0b <func4>
   0x08048dce <+90>:	cmp    $0x13,%eax
   0x08048dd1 <+93>:	jne    0x8048dda <phase_4+102>    //根据跳转逻辑可以推测此时函数func4的返回值%eax=0x13
   0x08048dd3 <+95>:	cmpl   $0x13,0x1c(%esp)   
   0x08048dd8 <+100>:	je     0x8048ddf <phase_4+107>    //根据跳转逻辑可以推测此时*(0x1c+%esp)=0x13,即输入的第二个数是0x13
   0x08048dda <+102>:	call   0x8049196 <explode_bomb>
   0x08048ddf <+107>:	add    $0x2c,%esp
   0x08048de2 <+110>:	ret    
End of assembler dump.

根据提示输入"x/s 0x804a4a3"打印地址0x804a4a3中的值,可以查看到输入为两个整型

查看func4函数的汇编代码如下:

Dump of assembler code for function func4:
   0x08048d0b <+0>:	sub    $0x1c,%esp
   0x08048d0e <+3>:	mov    %ebx,0x14(%esp)
   0x08048d12 <+7>:	mov    %esi,0x18(%esp)
   0x08048d16 <+11>:	mov    0x20(%esp),%eax
   0x08048d1a <+15>:	mov    0x24(%esp),%edx
   0x08048d1e <+19>:	mov    0x28(%esp),%esi    //三个参数,对应第一个输入,0,14(第二个参数为low,第三个参数为high)
   0x08048d22 <+23>:	mov    %esi,%ecx
   0x08048d24 <+25>:	sub    %edx,%ecx        //令high-low
   0x08048d26 <+27>:	mov    %ecx,%ebx
   0x08048d28 <+29>:	shr    $0x1f,%ebx    
   0x08048d2b <+32>:	add    %ebx,%ecx      //将寄存器ebx中的值逻辑右移0x1f位,用于判断high-low是否为负数,如果为负数的话右移31位得到1,再加到high-low中去;
   0x08048d2d <+34>:	sar    %ecx           //默认算术右移一位,即求(high-low)/2
   0x08048d2f <+36>:	lea    (%ecx,%edx,1),%ebx    //ebx我们现在有的low + (high - low) / 2也被称为两个数字的平均值。
   0x08048d32 <+39>:	cmp    %eax,%ebx
   0x08048d34 <+41>:	jle    0x8048d4d <func4+66>
   0x08048d36 <+43>:	lea    -0x1(%ebx),%ecx
   0x08048d39 <+46>:	mov    %ecx,0x8(%esp)
   0x08048d3d <+50>:	mov    %edx,0x4(%esp)
   0x08048d41 <+54>:	mov    %eax,(%esp)
   0x08048d44 <+57>:	call   0x8048d0b <func4>
   0x08048d49 <+62>:	add    %eax,%ebx        如果x<average,返回average+func4(x, low, average-1)
   0x08048d4b <+64>:	jmp    0x8048d66 <func4+91>
   0x08048d4d <+66>:	cmp    %eax,%ebx        //如果x=average,返回的是average
   0x08048d4f <+68>:	jge    0x8048d66 <func4+91>
   0x08048d51 <+70>:	mov    %esi,0x8(%esp)
   0x08048d55 <+74>:	lea    0x1(%ebx),%edx //
   0x08048d58 <+77>:	mov    %edx,0x4(%esp)
   0x08048d5c <+81>:	mov    %eax,(%esp)
   0x08048d5f <+84>:	call   0x8048d0b <func4>
   0x08048d64 <+89>:	add    %eax,%ebx    //如果x>average和计算则执行average+func4(x, average+1, high)
   0x08048d66 <+91>:	mov    %ebx,%eax    //可以根据phase_4得到函数返回值%eax=0x13
   0x08048d68 <+93>:	mov    0x14(%esp),%ebx
   0x08048d6c <+97>:	mov    0x18(%esp),%esi
   0x08048d70 <+101>:	add    $0x1c,%esp
   0x08048d73 <+104>:	ret    
End of assembler dump.

将函数func4中的寄存器看作变量将该汇编代码模拟转换成C语言代码如下:

#include<iostream>
using namespace std;
int func4(int x,int low,int high)
{
	int aver= low+(high-low+((high-low)<0?1:0))/2;
	if(x<aver)	
	{
		return aver+func4(x,low,aver-1); 	
	}
	else if(x>aver)
	{
		return aver+func4(x,aver+1,high);
	}
	else return aver;
 } 
 int main()
 {
	for(int i=0;i<=14;i++)
	{
		if(func4(i,0,14)==19)	cout<<i<<" ";
	}
	return 0;
 }
 

函数返回值通过寄存器%eax来保存,所以可以推测出函数func4返回值为0x13;通过C++模拟得到我们的第一个输入应该是4

第二个数可以直接根据phase_4的汇编代码推测为19(0x13)

测试一下!正确!

五、phase_5

汇编代码如下:

Dump of assembler code for function phase_5:
   0x08048de3 <+0>:	sub    $0x2c,%esp
   0x08048de6 <+3>:	lea    0x1c(%esp),%eax
   0x08048dea <+7>:	mov    %eax,0xc(%esp)
   0x08048dee <+11>:	lea    0x18(%esp),%eax
   0x08048df2 <+15>:	mov    %eax,0x8(%esp)
   0x08048df6 <+19>:	movl   $0x804a4a3,0x4(%esp)    //根据提示0x804a4a3是"%d %d"在内存中的地址
   0x08048dfe <+27>:	mov    0x30(%esp),%eax
   0x08048e02 <+31>:	mov    %eax,(%esp)
   0x08048e05 <+34>:	call   0x8048870 <__isoc99_sscanf@plt>
   0x08048e0a <+39>:	cmp    $0x1,%eax
   0x08048e0d <+42>:	jg     0x8048e14 <phase_5+49>    //根据跳转逻辑得到%eax=0x1
   0x08048e0f <+44>:	call   0x8049196 <explode_bomb>
   0x08048e14 <+49>:	mov    0x18(%esp),%eax
   0x08048e18 <+53>:	and    $0xf,%eax
   0x08048e1b <+56>:	mov    %eax,0x18(%esp)    //将0x18(esp)中的内容与上0xf
   0x08048e1f <+60>:	cmp    $0xf,%eax          //根据跳转逻辑,%eax不等于0xf
   0x08048e22 <+63>:	je     0x8048e4e <phase_5+107>
   0x08048e24 <+65>:	mov    $0x0,%ecx
   0x08048e29 <+70>:	mov    $0x0,%edx
   0x08048e2e <+75>:	add    $0x1,%edx
   0x08048e31 <+78>:	mov    0x804a300(,%eax,4),%eax    //长度为十六的数组中的值{0xa, 0x2, 0xe, 0x7, 0x8, 0xc, 0xf, 0xb, 0x0, 0x4, 0x1, 0xd, 0x3, 0x9, 0x6, 0x5}
   0x08048e38 <+85>:	add    %eax,%ecx
---Type <return> to continue, or q <return> to quit---
   0x08048e3a <+87>:	cmp    $0xf,%eax
   0x08048e3d <+90>:	jne    0x8048e2e <phase_5+75>
   0x08048e3f <+92>:	mov    %eax,0x18(%esp)
   0x08048e43 <+96>:	cmp    $0xf,%edx        //根据跳转逻辑得到%edx=0xf,说明循环了十五次
   0x08048e46 <+99>:	jne    0x8048e4e <phase_5+107>
   0x08048e48 <+101>:	cmp    0x1c(%esp),%ecx    //根据跳转逻辑得到%ecx=*(0x1c+esp),即第二个输入为%ecx
   0x08048e4c <+105>:	je     0x8048e53 <phase_5+112>
   0x08048e4e <+107>:	call   0x8049196 <explode_bomb>
   0x08048e53 <+112>:	add    $0x2c,%esp
   0x08048e56 <+115>:	ret    
End of assembler dump.

分析得到%eax不能取到0xf,%edx作为循环变量,控制循环次数为0xf次,每次循环%ecx都加上%eax的值

输入"p/x *(int *)(0x804a300)@16"查看以0x804a300为基址的长度为十六的数组中的值{0xa, 0x2, 0xe, 0x7, 0x8, 0xc, 0xf, 0xb, 0x0, 0x4, 0x1, 0xd, 0x3, 0x9, 0x6, 0x5},即数组

0 1 2 3 4 5 6 7 8 9 a b c d e f(索引)

a 2 e 7 8 c f b 0 4 1 d 3 9 6 5(值)

反推,如果%eax=0xf,说明索引为6,即上一个eax=6,那么上一个eax的索引为e;故得到eax的逆推序列为f 6 e 2 1 a 0 8 4 9 d b 7 3 c 5 

所以第一个输入等于最开始的%eax=5,第二个输入为%ecx=(c+…f)=115;

测试一下!下面只剩最后一个啦!!

六、phase_6

汇编代码如下:

Dump of assembler code for function phase_6:
   0x08048e57 <+0>:	push   %esi
   0x08048e58 <+1>:	push   %ebx
   0x08048e59 <+2>:	sub    $0x44,%esp
   0x08048e5c <+5>:	lea    0x10(%esp),%eax
   0x08048e60 <+9>:	mov    %eax,0x4(%esp)
   0x08048e64 <+13>:	mov    0x50(%esp),%eax
   0x08048e68 <+17>:	mov    %eax,(%esp)
   0x08048e6b <+20>:	call   0x80492cb <read_six_numbers>    //输入六个数字
   0x08048e70 <+25>:	mov    $0x0,%esi
   0x08048e75 <+30>:	mov    0x10(%esp,%esi,4),%eax
   0x08048e79 <+34>:	sub    $0x1,%eax
   0x08048e7c <+37>:	cmp    $0x5,%eax
   0x08048e7f <+40>:	jbe    0x8048e86 <phase_6+47>    //根据跳转逻辑eax<=0x5,故*(0x10+esp)<=0x6
   0x08048e81 <+42>:	call   0x8049196 <explode_bomb>
   0x08048e86 <+47>:	add    $0x1,%esi
   0x08048e89 <+50>:	cmp    $0x6,%esi
   0x08048e8c <+53>:	je     0x8048ec1 <phase_6+106>
   0x08048e8e <+55>:	mov    %esi,%ebx
   0x08048e90 <+57>:	mov    0x10(%esp,%ebx,4),%eax
   0x08048e94 <+61>:	cmp    %eax,0xc(%esp,%esi,4)
   0x08048e98 <+65>:	jne    0x8048e9f <phase_6+72>
   0x08048e9a <+67>:	call   0x8049196 <explode_bomb>
---Type <return> to continue, or q <return> to quit---
   0x08048e9f <+72>:	add    $0x1,%ebx
   0x08048ea2 <+75>:	cmp    $0x5,%ebx
   0x08048ea5 <+78>:	jle    0x8048e90 <phase_6+57>
   0x08048ea7 <+80>:	jmp    0x8048e75 <phase_6+30>    //通过观察得到上面表示的是六个输入均小于等于0x6
   0x08048ea9 <+82>:	mov    0x8(%edx),%edx
   0x08048eac <+85>:	add    $0x1,%eax
   0x08048eaf <+88>:	cmp    %ecx,%eax
   0x08048eb1 <+90>:	jne    0x8048ea9 <phase_6+82>    //查找结点
   0x08048eb3 <+92>:	mov    %edx,0x28(%esp,%esi,4)
   0x08048eb7 <+96>:	add    $0x1,%ebx
   0x08048eba <+99>:	cmp    $0x6,%ebx
   0x08048ebd <+102>:	jne    0x8048ec6 <phase_6+111>        
   0x08048ebf <+104>:	jmp    0x8048edd <phase_6+134>
   0x08048ec1 <+106>:	mov    $0x0,%ebx
   0x08048ec6 <+111>:	mov    %ebx,%esi
   0x08048ec8 <+113>:	mov    0x10(%esp,%ebx,4),%ecx
   0x08048ecc <+117>:	mov    $0x1,%eax
   0x08048ed1 <+122>:	mov    $0x804c13c,%edx        //第一个节点的地址为0x804c13c
   0x08048ed6 <+127>:	cmp    $0x1,%ecx
   0x08048ed9 <+130>:	jg     0x8048ea9 <phase_6+82>
   0x08048edb <+132>:	jmp    0x8048eb3 <phase_6+92>
   0x08048edd <+134>:	mov    0x28(%esp),%ebx
   0x08048ee1 <+138>:	mov    0x2c(%esp),%eax
---Type <return> to continue, or q <return> to quit---
   0x08048ee5 <+142>:	mov    %eax,0x8(%ebx)
   0x08048ee8 <+145>:	mov    0x30(%esp),%edx
   0x08048eec <+149>:	mov    %edx,0x8(%eax)
   0x08048eef <+152>:	mov    0x34(%esp),%eax
   0x08048ef3 <+156>:	mov    %eax,0x8(%edx)
   0x08048ef6 <+159>:	mov    0x38(%esp),%edx
   0x08048efa <+163>:	mov    %edx,0x8(%eax)
   0x08048efd <+166>:	mov    0x3c(%esp),%eax
   0x08048f01 <+170>:	mov    %eax,0x8(%edx)
   0x08048f04 <+173>:	movl   $0x0,0x8(%eax)    //链表尾指针设为空
   0x08048f0b <+180>:	mov    $0x5,%esi        //按照降序重建链表
   0x08048f10 <+185>:	mov    0x8(%ebx),%eax
   0x08048f13 <+188>:	mov    (%eax),%edx
   0x08048f15 <+190>:	cmp    %edx,(%ebx)    //判断第一个数是否大于第二个数
   0x08048f17 <+192>:	jge    0x8048f1e <phase_6+199>
   0x08048f19 <+194>:	call   0x8049196 <explode_bomb>
   0x08048f1e <+199>:	mov    0x8(%ebx),%ebx
   0x08048f21 <+202>:	sub    $0x1,%esi
   0x08048f24 <+205>:	jne    0x8048f10 <phase_6+185>
   0x08048f26 <+207>:	add    $0x44,%esp
   0x08048f29 <+210>:	pop    %ebx
   0x08048f2a <+211>:	pop    %esi
   0x08048f2b <+212>:	ret    
---Type <return> to continue, or q <return> to quit---
End of assembler dump.

输入"p/x *(0x804c13c)@3)"打印出节点的数据,序号和写一个节点的地址,可以看到第六个节点的尾指针为0;

分析出是按照降序排列的之后按降序得到得节点顺序为"5 1 3 6 4 2"

测试一下!成功排雷!!

七、secret_phase

通过对phase_defused的汇编代码的查看,只有经过正确触发(且已经拆完前六个雷)才能打开隐藏炸弹,输入正确答案进行排雷

<phase_defused>的汇编代码如下:

Dump of assembler code for function phase_defused:
   0x0804931b <+0>:	sub    $0x8c,%esp
   0x08049321 <+6>:	mov    %gs:0x14,%eax    
   0x08049327 <+12>:	mov    %eax,0x7c(%esp)
   0x0804932b <+16>:	xor    %eax,%eax
   0x0804932d <+18>:	cmpl   $0x6,0x804c3cc     //根据跳转逻辑,可以推测需要拆完六个雷才能打开隐藏函数   
   0x08049334 <+25>:	jne    0x80493a8 <phase_defused+141>
   0x08049336 <+27>:	lea    0x2c(%esp),%eax
   0x0804933a <+31>:	mov    %eax,0x10(%esp)
   0x0804933e <+35>:	lea    0x28(%esp),%eax
   0x08049342 <+39>:	mov    %eax,0xc(%esp)
   0x08049346 <+43>:	lea    0x24(%esp),%eax
   0x0804934a <+47>:	mov    %eax,0x8(%esp)
   0x0804934e <+51>:	movl   $0x804a4a9,0x4(%esp)    //按照x/s 0x804a4a9打印出来是%d %d %s
   0x08049356 <+59>:	movl   $0x804c4d0,(%esp)       
   0x0804935d <+66>:	call   0x8048870 <__isoc99_sscanf@plt>
   0x08049362 <+71>:	cmp    $0x3,%eax                //表明输入有三个参数,否则跳转到<+129>
   0x08049365 <+74>:	jne    0x804939c <phase_defused+129>
   0x08049367 <+76>:	movl   $0x804a4b2,0x4(%esp)    //打印出来是"DrEvil"
   0x0804936f <+84>:	lea    0x2c(%esp),%eax
   0x08049373 <+88>:	mov    %eax,(%esp)
   0x08049376 <+91>:	call   0x8049084 <strings_not_equal>
   0x0804937b <+96>:	test   %eax,%eax
---Type <return> to continue, or q <return> to quit---
   0x0804937d <+98>:	jne    0x804939c <phase_defused+129>    //在这一段进行了比较,如果你输入的字符串不符合要求,就无法开启隐藏关卡

   0x0804937f <+100>:	movl   $0x804a378,(%esp)        //"Curses, you've found the secret phase!"
   0x08049386 <+107>:	call   0x8048800 <puts@plt>
   0x0804938b <+112>:	movl   $0x804a3a0,(%esp)        //"But finding it and solving it are quite different..."
   0x08049392 <+119>:	call   0x8048800 <puts@plt>
   0x08049397 <+124>:	call   0x8048f7d <secret_phase>
   0x0804939c <+129>:	movl   $0x804a3d8,(%esp)        打印x/s 0x804a3d8得到的是 "Congratulations! You've defused the bomb!"
   0x080493a3 <+136>:	call   0x8048800 <puts@plt>
   0x080493a8 <+141>:	mov    0x7c(%esp),%eax
   0x080493ac <+145>:	xor    %gs:0x14,%eax
   0x080493b3 <+152>:	je     0x80493ba <phase_defused+159>
   0x080493b5 <+154>:	call   0x80487d0 <__stack_chk_fail@plt>
   0x080493ba <+159>:	add    $0x8c,%esp
   0x080493c0 <+165>:	ret    
End of assembler dump.

需要查看几个地址的值

可以知道,第四关和第五关前两个都是输入整型,测试一下发现必须要在第四关(第五关不必要)输入的两个整形后面输入"DrEvil"才能成功触发

下面查看<secret_phase>的汇编代码:

Dump of assembler code for function secret_phase:
   0x08048f7d <+0>:	push   %ebx
   0x08048f7e <+1>:	sub    $0x18,%esp
   0x08048f81 <+4>:	call   0x80491bd <read_line>    //先读入一行,返回值eax作为函数strtol@plt>的参数之一 
   0x08048f86 <+9>:	movl   $0xa,0x8(%esp)    //a
   0x08048f8e <+17>:	movl   $0x0,0x4(%esp) //4
   0x08048f96 <+25>:	mov    %eax,(%esp)
   0x08048f99 <+28>:	call   0x80488e0 <strtol@plt>     //将我们输入的数字当作字符串处理,然后转为十进制。简单地说就是输入什么十进制数参数就是那个十进制数本身
   0x08048f9e <+33>:	mov    %eax,%ebx
   0x08048fa0 <+35>:	lea    -0x1(%eax),%eax
   0x08048fa3 <+38>:	cmp    $0x3e8,%eax
   0x08048fa8 <+43>:	jbe    0x8048faf <secret_phase+50>    //根据跳转逻辑推测得到的输入的值小于等于0x3e8+1即小于等于1001
   0x08048faa <+45>:	call   0x8049196 <explode_bomb>
   0x08048faf <+50>:	mov    %ebx,0x4(%esp)    //a2
   0x08048fb3 <+54>:	movl   $0x804c088,(%esp)        //读出来的值为0x24,即36 第一个参数指针所指向的值为36
   0x08048fba <+61>:	call   0x8048f2c <fun7>
   0x08048fbf <+66>:	test   %eax,%eax        
   0x08048fc1 <+68>:	je     0x8048fc8 <secret_phase+75>    //根据跳转逻辑推测fun7的返回值是0
   0x08048fc3 <+70>:	call   0x8049196 <explode_bomb>
   0x08048fc8 <+75>:	movl   $0x804a294,(%esp)    //"Wow! You've defused the secret stage!"
   0x08048fcf <+82>:	call   0x8048800 <puts@plt>
   0x08048fd4 <+87>:	call   0x804931b <phase_defused>
   0x08048fd9 <+92>:	add    $0x18,%esp
---Type <return> to continue, or q <return> to quit---
   0x08048fdc <+95>:	pop    %ebx
   0x08048fdd <+96>:	ret    
End of assembler dump.

fun7的汇编代码如下:

Dump of assembler code for function fun7:
   0x08048f2c <+0>:	push   %ebx
   0x08048f2d <+1>:	sub    $0x18,%esp
   0x08048f30 <+4>:	mov    0x20(%esp),%edx    //第一个参数,传的是一个地址0x804c088
   0x08048f34 <+8>:	mov    0x24(%esp),%ecx    //第二个参数,我们所输入的那个数
   0x08048f38 <+12>:	test   %edx,%edx
   0x08048f3a <+14>:	je     0x8048f73 <fun7+71>        //如果传的地址所指向的值为0那么直接返回零
   0x08048f3c <+16>:	mov    (%edx),%ebx
   0x08048f3e <+18>:	cmp    %ecx,%ebx            //如果*p<=n则跳转
   0x08048f40 <+20>:	jle    0x8048f55 <fun7+41>
   0x08048f42 <+22>:	mov    %ecx,0x4(%esp)       
   0x08048f46 <+26>:	mov    0x4(%edx),%eax
   0x08048f49 <+29>:	mov    %eax,(%esp)
   0x08048f4c <+32>:	call   0x8048f2c <fun7>
   0x08048f51 <+37>:	add    %eax,%eax             //否则返回2*fun7(*(p+4),n)
   0x08048f53 <+39>:	jmp    0x8048f78 <fun7+76>
   0x08048f55 <+41>:	mov    $0x0,%eax
   0x08048f5a <+46>:	cmp    %ecx,%ebx
   0x08048f5c <+48>:	je     0x8048f78 <fun7+76>    //*p<=n后跳转若*p=n的话则返回0
   0x08048f5e <+50>:	mov    %ecx,0x4(%esp)
   0x08048f62 <+54>:	mov    0x8(%edx),%eax        
   0x08048f65 <+57>:	mov    %eax,(%esp)
   0x08048f68 <+60>:	call   0x8048f2c <fun7>
---Type <return> to continue, or q <return> to quit---
   0x08048f6d <+65>:	lea    0x1(%eax,%eax,1),%eax    //如果*p<n的话,返回2*fun7(*(p+8),n)+1;
   0x08048f71 <+69>:	jmp    0x8048f78 <fun7+76>
   0x08048f73 <+71>:	mov    $0xffffffff,%eax
   0x08048f78 <+76>:	add    $0x18,%esp
   0x08048f7b <+79>:	pop    %ebx
   0x08048f7c <+80>:	ret    
End of assembler dump.

fun7对应的C++代码为:

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

根据secret_phase的返回值为0得到输入的值与指针指向的值相等,即0x24,对应的十进制为36

  • 27
    点赞
  • 145
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值