【CSAPP】二进制炸弹 实验分析

【CSAPP】二进制炸弹 实验分析LAB2二进制炸弹实验是CSAPP配套实验中最有名的一个,实验一共分为6关,每关难度依次递增。独立完成还是需要很大一块时间的。做完这个实验能够让我们理解汇编语言的原理,GDB调试器的使用,以及进一步了解一个HELLOWORLD程序背后到底发生了什么。【必要的知识储备】objdump-t  bomb > name.txt 列出可执行文件bomb包含的所有函数名字和全局变

LAB2 二进制炸弹实验是CSAPP配套实验中最有名的一个,实验一共分为6关,每关难度依次递增。独立完成还是需要很大一块时间的。

做完这个实验能够让我们理解汇编语言的原理,GDB调试器的使用,以及进一步了解一个HELLO WORLD程序背后到底发生了什么。

必要的知识储备】

objdump -t   bomb  >  name.txt  列出可执行文件bomb包含的所有函数名字和全局变量名等等信息。>重定向到name.txt文件

objdump -d  bomb  >  bomb.txt反汇编bomb文件,将生成的汇编文件重定向到bomb.txt文件中

GDB相关的几个常用的操作:

r 1 2 3:进入gdb并导入程序文件后,以命令行参数1 2 3运行程序

b *0x40046b :在地址0x40046b处设置断点

d :删除所有断点

stepi :执行一条汇编指令

s:执行一条C语句,跳转到相应函数

n:执行一条C语句,但是直接跳过跳转函数,到达下一跳C语句

p /d $edx:以十进制方式打印出%edx内容,/x就对应16进制

p *(int *) ($ebp-0x32) :这对于查看寄存器对应地址所对应内容很有帮助

p (char*)0xbfff890:检查以该地址开头的字符串

x/3w $esp:从寄存器%esp地址开始,所对应内容打印2个字

bt:打印当前堆栈信息

info registers:查看寄存器信息

info frame:查看帧,栈指针信息


【实验分析】

实验材料只有一个可执行文件,先把他用objdump -d反汇编得到汇编文件。打开文件发现对应6个phase,对应着6关。

一:phase_1(字符串比较)

这一关是最容易的,先按照逻辑过一边第一段的代码,其中比较重要的几句是:

<span style="font-size:14px;"> 8048b29:    83 c4 f8                 add    $0xfffffff8,%esp
 8048b2c:    68 c0 97 04 08           push   $0x80497c0         
 8048b31:    50                       push   %eax    
 8048b32:    e8 f9 04 00 00           call   8049030 <strings_not_equal>
 8048b37:    83 c4 10                 add    $0x10,%esp
 8048b3a:    85 c0                    test   %eax,%eax
 8048b3c:    74 05                    je     8048b43 <phase_1+0x23>
 8048b3e:    e8 b9 09 00 00           call   80494fc <explode_bomb> 
</span>

我们看到push了两个东西后,调用了一个<strings_not_equal>函数,根据名字判断极有可能是我判断两个字符串是否相等。

我们跳转到这个函数来验证我们的猜想是否正确:

首先在<strings_not_equal>中调用了<string_length>,很明显,稍加分析就可以得知这是返回字符串长度的函数。

<span style="font-size:14px;"> 8049039:	8b 75 08             	mov    0x8(%ebp),%esi  
 804903c:	8b 7d 0c             	mov    0xc(%ebp),%edi   
 804903f:	83 c4 f4             	add    $0xfffffff4,%esp
 8049042:	56                   	push   %esi
 8049043:	e8 d0 ff ff ff       	call   8049018 <string_length>
 8049048:	89 c3                	mov    %eax,%ebx
 804904a:	83 c4 f4             	add    $0xfffffff4,%esp
 804904d:	57                   	push   %edi
 804904e:	e8 c5 ff ff ff       	call   8049018 <string_length>
 8049053:	39 c3                	cmp    %eax,%ebx
 8049055:	74 09                	je     8049060 <strings_not_equal+0x30></span>

因此我们可以在*0x804903f处设置一个断点,再使用命令:p  (char*)($esi)可以看出,esi储存我们输入的字符,同理显示%edi寄存器的内容:“Public speaking is very easy.”这个字符串属于系统的硬编码。

接下来的几条指令分别计算了二个字符串的长度,返回值存放在%eax和%ebx寄存器中,再cmp比较两者长度,不等就爆炸。

<strings_not_equal>的剩余语句其实不用看我们基本可以猜测出答案了,当该函数%eax返回1表示不相等就爆炸,返回0表示相等,因此这一关我们的答案:“Public speaking is very easy.”


二: phase_2(循环语句)

第二关我们先看调用的名为<read_six_numbers>函数:

<span style="font-size:14px;"><span style="font-size:12px;"> 8048fe7:	50                   	push   %eax
 8048fe8:	8d 42 10             	lea    0x10(%edx),%eax
 8048feb:	50                   	push   %eax
 8048fec:	8d 42 0c             	lea    0xc(%edx),%eax
 8048fef:	50                   	push   %eax
 8048ff0:	8d 42 08             	lea    0x8(%edx),%eax
 8048ff3:	50                   	push   %eax
 8048ff4:	8d 42 04             	lea    0x4(%edx),%eax
 8048ff7:	50                   	push   %eax
 8048ff8:	52                   	push   %edx
 8048ff9:	68 1b 9b 04 08       	push   $0x8049b1b      //格式
 8048ffe:	51                   	push   %ecx
 8048fff:	e8 5c f8 ff ff       	call   8048860 <sscanf@plt></span></span>

我们先看看sscanf函数的原型:int sscanf(const char *buffer,const char *format,[argument ]…);

sscanf会从buffer里读进数据,依照format的格式将数据写入到argument里。因此我们很容易联想到,前面push的一系列数据正式调用sscanf的参数,从中我们也可以发现,调用函数时,参数是从右向左入栈的。

在gdb中输入命令:p (char*)(0x8048ff9),我们得到输出“%d %d %d %d %d”。因此我们确定了输入6个数据的格式。

从<read_six_numbers>回来后,首先比较了第一个数:

<span style="font-size:14px;"> 8048b63:	83 7d e8 01          	cmpl   $0x1,-0x18(%ebp)  </span>

因此输入6个数据的第一个数就是1。

接着进入了一个循环,关键语句是

<span style="font-size:14px;"> 8048b7e:	39 04 9e             	cmp    %eax,(%esi,%ebx,4)</span>

%eax根据前一条语句计算出来,值为1*2=2,而(%esi,%ebx,4)储存的是sscanf格式化输出后的6个数中的其中一个(也就是我们的输入),比较两者,如果不相等就爆炸。%ebx寄存器的值用来计数,inc之后表示当前验证第几个数,当其大于5时就退出循环。依次比较完6个数都相等后本关才通过。

根据规律我们很容易得到:第n个数的值=第n-1个数的值*n

因此本关的答案1 2 6 24 120 720


三:phase_3(switch语句)

这个片段代码很长,我们先分析一下前面一小段,直接看代码后面的注释

<span style="font-size:14px;">08048b98 <phase_3>:
 8048b98:	55                   	push   %ebp
 8048b99:	89 e5                	mov    %esp,%ebp
 8048b9b:	83 ec 14             	sub    $0x14,%esp
 8048b9e:	53                   	push   %ebx
 8048b9f:	8b 55 08             	mov    0x8(%ebp),%edx
 8048ba2:	83 c4 f4             	add    $0xfffffff4,%esp
 8048ba5:	8d 45 fc             	lea    -0x4(%ebp),%eax
 8048ba8:	50                   	push   %eax           //压入第三个参数c
 8048ba9:	8d 45 fb             	lea    -0x5(%ebp),%eax 
 8048bac:	50                   	push   %eax             //压入第二个参数b
 8048bad:	8d 45 f4             	lea    -0xc(%ebp),%eax 
 8048bb0:	50                   	push   %eax         //压入第一个参数a
 8048bb1:	68 de 97 04 08       	push   $0x80497de//得出输入格式“"%d %c %d" (a b c)
 8048bb6:	52                   	push   %edx
 8048bb7:	e8 a4 fc ff ff       	call   8048860 <sscanf@plt>
 8048bbc:	83 c4 20             	add    $0x20,%esp
 8048bbf:	83 f8 02             	cmp    $0x2,%eax  //%eax返回元素的个数  
 8048bc2:	7f 05                	jg     8048bc9 <phase_3+0x31>
 8048bc4:	e8 33 09 00 00       	call   80494fc <explode_bomb>//小于2个爆炸
 8048bc9:	83 7d f4 07          	cmpl   $0x7,-0xc(%ebp)   
 8048bcd:	0f 87 b5 00 00 00    	ja     8048c88 <phase_3+0xf0>// a>7爆炸 
</span>

上面这段代码和第二关有点类似,我们可以得出输入数据的格式,紧接着就是本题的核心语句了:

<span style="font-size:14px;"> 8048bd3:	8b 45 f4             	mov    -0xc(%ebp),%eax  /*开始switch分支*/
 8048bd6:	ff 24 85 e8 97 04 08 	jmp    *0x80497e8(,%eax,4)//选择跳转点</span>

前一条表明switch分支的开始,而后一条的意思是根据我们输入的参数a(必须小于等于7)选择跳转点,之后对应不同的跳转点有不同的cmp语句,如果不相等就爆炸。

因此可以看出本题的答案不止1个。在这里假设输入的第一个参数为1,之后可以按照顺序推导出结果。

eax为1跳转到如下位置:

<span style="font-size:14px;"> 8048c00:	b3 62                	mov    $0x62,%bl
 8048c02:	81 7d fc d6 00 00 00 	cmpl   $0xd6,-0x4(%ebp)       //c=0xd6,%bl=0x62
 8048c09:    0f 84 80 00 00 00        je     8048c8f <phase_3+0xf7>
</span>

  得出c的值为0xd6,接着继续跳转:

<span style="font-size:14px;"> 8048c8f:	3a 5d fb             	cmp    -0x5(%ebp),%bl //b=0x62,即b
 8048c92:	74 05                	je     8048c99 <phase_3+0x101>
</span>

我们得出了b=0x62,即字母b。值得注意的一点是:当想查看偏移ebp一定两的内存对应的数据时比如-0x4(%ebp),但是他并没有对应的数据寄存器。我们可以在gdb中用命令: p *(int*)($ebp-0x4)查看。

因此本关的一个答案:1 b 214


四:phase_4(递归)

这一关考察的主要是一个递归的知识,我们只要找到了模式,能够逆推出c代码这道题就解决了。

<span style="font-size:14px;">08048ce0 <phase_4>:
 8048cef:	50                   	push   %eax
 8048cf0:	68 08 98 04 08       	push   $0x8049808 //字符格式"%d"
 8048cf5:	52                   	push   %edx
 8048cf6:	e8 65 fb ff ff       	call   8048860 <sscanf@plt>
 8048cfb:	83 c4 10             	add    $0x10,%esp
 8048cfe:	83 f8 01             	cmp    $0x1,%eax  //是1个参数就继续否则爆炸
 8048d01:	75 06                	jne    8048d09 <phase_4+0x29>
 8048d03:	83 7d fc 00          	cmpl   $0x0,-0x4(%ebp)
 8048d07:	7f 05                	jg     8048d0e <phase_4+0x2e>//且参数大于0
 8048d09:	e8 ee 07 00 00       	call   80494fc <explode_bomb>
 8048d0e:	83 c4 f4             	add    $0xfffffff4,%esp
 8048d11:	8b 45 fc             	mov    -0x4(%ebp),%eax
 8048d14:	50                   	push   %eax
 8048d15:	e8 86 ff ff ff       	call   8048ca0 <func4>
 8048d1a:	83 c4 10             	add    $0x10,%esp
 8048d1d:	83 f8 37             	cmp    $0x37,%eax //返回值必须等于0x37即55
 8048d20:	74 05                	je     8048d27 <phase_4+0x47>
 8048d22:	e8 d5 07 00 00       	call   80494fc <explode_bomb>
 8048d27:	89 ec                	mov    %ebp,%esp
 8048d29:	5d                   	pop    %ebp
 8048d2a:	c3                   	ret    
 8048d2b:	90                   	nop</span>

同理,得出输入字符格式为“%d“,要求一个参数且大于0,之后进入一个<func4>,后一句看出要求返回值%eax必须等于0x37,我们进入func4看看究竟发生了什么:

<span style="font-size:14px;">08048ca0 <func4>:
 8048ca0:	55                   	push   %ebp
 8048ca1:	89 e5                	mov    %esp,%ebp
 8048ca3:	83 ec 10             	sub    $0x10,%esp
 8048ca6:	56                   	push   %esi
 8048ca7:	53                   	push   %ebx  //$ebx储存输入参数假设为n
 8048ca8:	8b 5d 08             	mov    0x8(%ebp),%ebx
 8048cab:	83 fb 01             	cmp    $0x1,%ebx
 8048cae:	7e 20                	jle    8048cd0 <func4+0x30> //n<=1,返回1
 8048cb0:	83 c4 f4             	add    $0xfffffff4,%esp
 8048cb3:	8d 43 ff             	lea    -0x1(%ebx),%eax //n>0,先n-1
 8048cb6:	50                   	push   %eax
 8048cb7:	e8 e4 ff ff ff       	call   8048ca0 <func4>//调用func4(n-1)
 8048cbc:	89 c6                	mov    %eax,%esi  //%esi保存返回值
 8048cbe:	83 c4 f4             	add    $0xfffffff4,%esp
 8048cc1:	8d 43 fe             	lea    -0x2(%ebx),%eax //fun4(n-1)调用完后,n变成n-2
 8048cc4:	50                   	push   %eax
 8048cc5:	e8 d6 ff ff ff       	call   8048ca0 <func4>//调用func4(n-1)
 8048cca:	01 f0                	add    %esi,%eax  //相加两次调用的结果并返回
 8048ccc:	eb 07                	jmp    8048cd5 <func4+0x35>
 8048cce:	89 f6                	mov    %esi,%esi
 8048cd0:	b8 01 00 00 00       	mov    $0x1,%eax
 8048cd5:	8d 65 e8             	lea    -0x18(%ebp),%esp
 8048cd8:	5b                   	pop    %ebx
 8048cd9:	5e                   	pop    %esi
 8048cda:	89 ec                	mov    %ebp,%esp
 8048cdc:	5d                   	pop    %ebp
 8048cdd:	c3                   	ret    
 8048cde:	89 f6                	mov    %esi,%esi</span>

因此不难得出c实现:

<span style="font-size:14px;">inf func4(int n) {  
    if (n <= 1)  
        return 1;  
    else  
        return func4(n-1) + func4(n-2);  
}   </span>

因此经过计算后得出n=9时满足返回值为0x37即55


五:phase_5(字符串操作)

本题主要的分析都在注释中:

<span style="font-size:14px;"> 8048d34:    8b 5d 08                 mov    0x8(%ebp),%ebx //储存输入字符串
 8048d37:    83 c4 f4                 add    $0xfffffff4,%esp
 8048d3a:    53                       push   %ebx
 8048d3b:    e8 d8 02 00 00           call   8049018 <string_length>
 8048d40:    83 c4 10                 add    $0x10,%esp
 8048d43:    83 f8 06                 cmp    $0x6,%eax
 8048d46:    74 05                    je     8048d4d <phase_5+0x21>
 8048d48:    e8 af 07 00 00           call   80494fc <explode_bomb>
 8048d4d:    31 d2                    xor    %edx,%edx
 8048d4f:    8d 4d f8                 lea    -0x8(%ebp),%ecx
 8048d52:    be 20 b2 04 08           mov    $0x804b220,%esi    //作为模式串:"isrveawhobpnutfg\260\001"
 8048d57:    8a 04 1a                 mov    (%edx,%ebx,1),%al //循环开始位置,依次取输入字符串的第1个到第5个字符(%edx:0-5)存入%al
 8048d5a:    24 0f                    and    $0xf,%al      //取%al最后四位   
 8048d5c:    0f be c0                 movsbl %al,%eax     //作符号扩展
 8048d5f:    8a 04 30                 mov    (%eax,%esi,1),%al //根据符号扩展后的索引选取模式串中的字符
 8048d62:    88 04 0a                 mov    %al,(%edx,%ecx,1) //按照0-5的顺序存入一个新数组%ecx
 8048d65:    42                       inc    %edx
 8048d66:    83 fa 05                 cmp    $0x5,%edx
 8048d69:    7e ec                    jle    8048d57 <phase_5+0x2b>
 8048d6b:    c6 45 fe 00              movb   $0x0,-0x2(%ebp)
 8048d6f:    83 c4 f8                 add    $0xfffffff8,%esp
 8048d72:    68 0b 98 04 08           push   $0x804980b       //字符串:“giants”
 8048d77:    8d 45 f8                 lea    -0x8(%ebp),%eax  //得到的新数组
 8048d7a:    50                       push   %eax
 8048d7b:    e8 b0 02 00 00           call   8049030 <strings_not_equal>//比较,两者要一致,逆推即可(低四位作索引:15 0 5 11 13 1,高四位任意)
 8048d80:    83 c4 10                 add    $0x10,%esp                   //任意取一组对应的字符串为?@EKMA
 8048d83:    85 c0                    test   %eax,%eax
 8048d85:    74 05                    je     8048d8c <phase_5+0x60></span>

总之,本关分为2大块:

第一块:取输入字符串的每一个字符的低四位符号扩展后,作为索引选取模式串中的字符,形成一个新数组。

第二块:调用<string_not_equeal>比较得到的新数组和本身硬编码的”giants”,二者要一致。

本关需要逆推,先根据“giants”逆推出在模式串中的索引:15 0 11 13 1 。索引作为低四位,高四位任取。

因此本关的其中一个答案是:?@EKMA


六:phase_6(链表)

这一关难度很大,研究了半天,最后参考了以下别人的才弄清楚。

总的来说可以分成四部分来解读:

1).读入6个数并检验输入数据是否满足要求  

2).根据输入重新排列链表并存入另一块内存。  

3).重新连接重排后的链表。 

4).检测链表值是否满足要求。

(假设我们已经)

首先是部分一:检验输入是否合法。

汇编和C代码解释一起看更容易懂。

<span style="font-size:14px;"> 8048da1:       8b 55 08                mov    0x8(%ebp),%edx 
 8048da4:       c7 45 cc 6c b2 04 08    movl   $0x804b26c,-0x34(%ebp) 
 8048dab:       83 c4 f8                add    $0xfffffff8,%esp
 8048dae:	8d 45 e8             	lea    -0x18(%ebp),%eax  //数组arr首地址,gdb中通过p *(int*)($ebp-0x18)可得出
 8048db1:	50                   	push   %eax
 8048db2:	52                   	push   %edx
 8048db3:	e8 20 02 00 00       	call   8048fd8 <read_six_numbers> //读入6个数字存入数组arr
 8048db8:	31 ff                	xor    %edi,%edi     //%edi清零,表示i=0
 8048dba:	83 c4 10             	add    $0x10,%esp
 8048dbd:	8d 76 00             	lea    0x0(%esi),%esi
 8048dc0:	8d 45 e8             	lea    -0x18(%ebp),%eax   //得到数组的首地址
 8048dc3:	8b 04 b8             	mov    (%eax,%edi,4),%eax  //edi范围从0-5,改变数组下标
 8048dc6:	48                   	dec    %eax
 8048dc7:	83 f8 05             	cmp    $0x5,%eax  //每个元素必须小于等于6,否则爆炸
 8048dca:	76 05                	jbe    8048dd1 <phase_6+0x39>
 8048dcc:	e8 2b 07 00 00       	call   80494fc <explode_bomb>
 8048dd1:	8d 5f 01             	lea    0x1(%edi),%ebx  //ebx=edi+1,ebx表示为j,相当于j=i+1
 8048dd4:	83 fb 05             	cmp    $0x5,%ebx        //j <= 5进行內循环,如果大于就进行外循环
 8048dd7:	7f 23                	jg     8048dfc <phase_6+0x64> 
 8048dd9:	8d 04 bd 00 00 00 00 	lea    0x0(,%edi,4),%eax  //%eax表示距离数组首地址的偏移量
 8048de0:	89 45 c8             	mov    %eax,-0x38(%ebp)   //暂存在此地址
 8048de3:	8d 75 e8             	lea    -0x18(%ebp),%esi  //数组首地址
 8048de6:	8b 55 c8             	mov    -0x38(%ebp),%edx  //偏移量,用于得到对应下标的数组元素地址
 8048de9:	8b 04 32             	mov    (%edx,%esi,1),%eax //首地址+偏移量=arr[i]
 8048dec:	3b 04 9e             	cmp    (%esi,%ebx,4),%eax //同理,得到arr[j]
 8048def:	75 05                	jne    8048df6 <phase_6+0x5e> //要求arr[i] != arr[j],否则爆炸
 8048df1:	e8 06 07 00 00       	call   80494fc <explode_bomb>
 8048df6:	43                   	inc    %ebx       //j++
 8048df7:	83 fb 05             	cmp    $0x5,%ebx    // j <= 5 继续內循环
 8048dfa:	7e ea                	jle    8048de6 <phase_6+0x4e>
 8048dfc:	47                   	inc    %edi       //i++   
 8048dfd:	83 ff 05             	cmp    $0x5,%edi    //i <= 5  继续外循环
 8048e00:	7e be                	jle    8048dc0 <phase_6+0x28></span>

这是这段汇编代码对应的C代码:

<span style="font-size:14px;">    for (int i =0; i<=5; i++){  
        if (arr[i]>= 6)  
            explode_bomb();  
        for (int j = i+1; j<=5; j++){  
            if (arr[i]== arr[j])  
                explode_bomb();  
        }  
    }  </span>


然后是部分二:根据输入的6个数字,重新排列链表并存入新的地址。

<span style="font-size:14px;">    8048e02:        31 ff               xor    %edi,%edi                  // i = 0  
    8048e04:        8d4d e8             lea    -0x18(%ebp),%ecx           //数组arr首地址存入%ecx
    8048e07:        8d45 d0             lea    -0x30(%ebp),%eax            
    8048e0a:        8945 c4             mov    %eax,-0x3c(%ebp)           
    8048e0d:        8d76 00             lea    0x0(%esi),%esi            
    8048e10:        8b75 cc             mov    -0x34(%ebp),%esi           //%esi存放链表首地址(稍后解释为什么)  
    8048e13:        bb01 00 00 00       mov    $0x1,%ebx                  // j = 1  
    8048e18:        8d04 bd 00 00 00 00 lea    0x0(,%edi,4),%eax          //%eax 存放偏移地址
    8048e1f:         89c2               mov    %eax,%edx  
    8048e21:        3b1c 08             cmp    (%eax,%ecx,1),%ebx       
    8048e24:        7d12                jge    8048e38 <phase_6+0xa0>    // j - arr[i] <= 0继续循环 
    8048e26:        8b04 0a             mov    (%edx,%ecx,1),%eax        //%eax = arr[i]  
    8048e29:        8db4 26 00 00 00 00        lea    0x0(%esi,%eiz,1),%esi     
    8048e30:        8b76 08             mov    0x8(%esi),%esi           //相当于node=node->next 
    8048e33:        43                  inc    %ebx                     //j++  
    8048e34:        39c3                cmp    %eax,%ebx                // j < arr[i] 继续內循环
    8048e36:        7cf8                jl     8048e30 <phase_6+0x98>  
    8048e38:        8b55 c4             mov    -0x3c(%ebp),%edx         //%edx存放新数组a首地址    
    8048e3b:        8934 ba             mov    %esi,(%edx,%edi,4)        //esi存入新数组对应下标中  
    8048e3e:        47                  inc    %edi  
    8048e3f:         83ff 05            cmp    $0x5,%edi  
    8048e42:        7ecc                jle    8048e10 <phase_6+0x78>  //i <= 5 继续外循环</span>

首先关注一条核心语句(也告诉了我们如何推到出硬编码的地址表示一个链表):

<span style="font-size:14px;"> 8048da4:       c7 45 cc 6c b2 04 08    movl   $0x804b26c,-0x34(%ebp) </span>

看起来似乎是一个字符串,尝试着:

【CSAPP】二进制炸弹 实验分析

打印处理了node1,可以猜测很有可能是个结构体,再看这一条语句:

<span style="font-size:14px;">048e30:     8b 76 08   mov   0x8(%esi),%esi  //esi值为0x804b26c,这是一个结构体的操作
</span>

因此,我们可以尝试着打印:

【CSAPP】二进制炸弹 实验分析

这是一个首位相接的链表。且结构体含有3个值。为2个整型和1个指针。

C代码如下:

<span style="font-size:14px;">    for (int i =0; i<=5; i++){  
        for (int j =0; j<arr[i]; j++){  
            node = node->next;  
        }  
        a[i]= node;  //a[i]是一个新数组
    }  </span>

假设为们输入的是1 2 3 4 5 6 ,经过这段代码之后就变成了2 3 4 5 6 1。

这段代码的功能就是:假设arr[i]=x,找出arr中第x个数存入新数组a[i](x从0开始,到6后变为1)。


部分三:重新连接链表

<span style="font-size:14px;">    8048e44:        8b 75 d0            mov    -0x30(%ebp),%esi    //取数组a首地址  
    8048e47:        8975 cc             mov    %esi,-0x34(%ebp)    //更新原链表首地址
      
    8048e4a:        bf01 00 00 00       mov    $0x1,%edi               // i = 1        
    8048e4f:        8d55 d0             lea    -0x30(%ebp),%edx         //数组a首地址  
    8048e52:        8b04 ba             mov    (%edx,%edi,4),%eax       //ptr[i] => eax  
    8048e55:        8946 08             mov    %eax,0x8(%esi)          //ptr[i-1]->next = ptr[i]        
    8048e58:        89c6                mov    %eax,%esi              //esi = ptr[i]  
    8048e5a:        47                  inc    %edi  
    8048e5b:        83ff 05             cmp    $0x5,%edi  
    8048e5e:        7ef2                jle    8048e52 <phase_6+0xba>  
      
    8048e60:        c746 08 00 00 00 00        movl   $0x0,0x8(%esi)  
    8048e67:        8b75 cc             mov    -0x34(%ebp),%esi     //最后一个结点的next赋0  
</span>


部分四:检验链表值

<span style="font-size:14px;">    8048e6a:        31 ff               xor    %edi,%edi                   // i = 0  
    8048e6c:        8d74 26 00          lea    0x0(%esi,%eiz,1),%esi       
    8048e70:        8b56 08             mov    0x8(%esi),%edx              //esi+8 的值为下一个结点地址
    8048e73:        8b06                mov    (%esi),%eax                 //取当前结点第一个元素值  
    8048e75:        3b02                cmp    (%edx),%eax                  
    8048e77:        7d05                jge    8048e7e <phase_6+0xe6>      //取当前结点第一个元素值和下一个结点第一个元素值比较,必须大于,否则爆炸
    8048e79:        e87e 06 00 00       call   80494fc<explode_bomb>  
    8048e7e:        8b76 08             mov    0x8(%esi),%esi  
    8048e81:        47                  inc    %edi  
    8048e82:        83ff 04             cmp    $0x4,%edi  
    8048e85:        7ee9                jle    8048e70 <phase_6+0xd8>  </span>


从这部分看出,要求重建后的链表每个结点的第一个元素都要大于后面一个结点的第一个元素。

第一个元素的大小分别为:fd 2d5  12d   3e5  d4   1b0

从大到小排列后:3e5 2d5 1b0 12d fd d4

参考第二个元素对应的输入值为:4 2 6 3 1 5


参考资料:spch2008大神的博客,最后一关帮助很大!http://blog.csdn.net/spch2008/article/details/29393915

今天的文章【CSAPP】二进制炸弹 实验分析分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/33281.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注