CSAPP Lab2 BombLab流程记录

CSAPP BombLab详解

实验环境

OS: Ubuntu18.04LTS 64位

实验开始前的准备

本次实验需要大家熟悉GDB的用法,使用如下命令调试bomb二进制程序

当打算使用标准输入(Stdin)作为输入调试时
gdb ./bomb		
当打算使用文件做输入调试时,使用如下命令(文件输入的文件名记得换成自己的)
gdb --args ./bomb input.txt

关于GDB的使用,只需要掌握基本的即可,在Lab以及实践中常用的命令如break,print,display,examine,info register。具体可见GDB Tutorial

使用如下命令生成bomb二进制文件的反汇编代码。

objdump -d bomb >> ass.txt 

在GDB中同样可以用disassemble 函数名生成函数对应的反汇编代码:
在这里插入图片描述

从什么地方开始

使用objdump命令生成的汇编文件中许多内容是库函数相关的指令代码,我们无需关注。我们需要找到main函数的入口。浏览后可知main函数调用了函数phase_1 ~ phase_6,这6个phase就是我们需要去解的。
找到main函数入口

phase1

顺腾摸瓜,找到了phase_1的反汇编代码。
phase1汇编代码
根据汇编,可以看出phase1要做的是比较两个字符串是否相同。那么这两个字符串是从何而来?

在main函数中有指令如下:
  400e32:	e8 67 06 00 00       	callq  40149e <read_line>
  400e37:	48 89 c7             	mov    %rax,%rdi
  400e3a:	e8 a1 00 00 00       	callq  400ee0 <phase_1>

在开始前复习知识点:
函数调用
首先在main函数中,我们先找到这个片段,这个代码片段实际上做了2件事。

  1. 调用read_line,读入你输入的字符串。并把字符串的首地址放到%rax中,
  2. 为了调用只有一个参数的phase_1函数,需要把参数放到%rdi中。最后进行函数调用。

进入到phase_1中,此时%rdi中存放的是我们输入的字符串首地址(回忆书中内容。strings_not_equal函数是比较两个字符串是否相等,自然应该有两个参数,且在C语言中,更具体来说,参数是字符串的首地址。那么另一个字符串首地址就应当在%rsi(在x86架构中,若对寄存器的低4字节执行mov指令,则高四字节自动设置为0) 中。

在gdb中输入:
p (char *)$rsi

可得到:Border relations with Canada have never been better.
至此,只需要把我们的输入调整到和它一样,就可以完成phase1。

phase_2

假设我在phase_2的输入是:

1 2 4 8 16 32

在这里插入图片描述
在左边的汇编代码注意看这条:

0x0000000000400efe <+2>:     sub    $0x28,%rsp

它将栈顶指针向下移动0x28,也就是40字节,可以理解成开辟了一块空间,<read_six_number>函数将读取到的数字放到这块空间里。
当<read_six_number>执行完毕后,使用x(实际上也就是examine命令)查看内存布局。发现数字确实都读入进去了。读取的每个数字是int类型占四字节
在这里插入图片描述
接着分析下面的汇编代码,如果第一个数字是1,那么跳转到<+52>处。
<+52> 与 <+57> 使用lea指令计算地址。取一条来分析其含义:

lea    0x4(%rsp),%rbx

它的意思就是将%rsp寄存器中存储的地址值+4后,再存入%rbx。
下一条指令:

lea    0x18(%rsp),%rbp 

看到%rbp的值被设置为%rsp + 24, 正好是我们读取到的6个数字所占字节数,这不是巧合,通过设置%rbp,可理解为设置了循环的边界
接着向下执行,函数跳转到<+27>处。这是关键的几步代码。

   0x0000000000400f17 <+27>:    mov    -0x4(%rbx),%eax                                             
   0x0000000000400f1a <+30>:    add    %eax,%eax                                                   
   0x0000000000400f1c <+32>:    cmp    %eax,(%rbx)                                                 
   0x0000000000400f1e <+34>:    je     0x400f25 <phase_2+41>                                       
   0x0000000000400f20 <+36>:    callq  0x40143a <explode_bomb> 

注意%rbx实际上现在指向的是我们输入序列中的第二个数字(2)
实际上做了这么几件事情:

  1. 找到%rbx所指向数字的前一个输入数字,放入%rax中
  2. 自己+自己,在没溢出的情况下相当于乘2倍
  3. 比较%rbx所指向的数字与第二步得到的乘积
  4. 相等则跳转到<+41>:%rbx += 4为接下来判断后面的数字做准备。不相等则引爆炸弹,程序终止。

看到现在,我们可以确定:这六个数字是一个首项为1,公比为2 的等比数字序列。 这就是phase_2的全部内容。

phase_3

假设我在phase_3的输入是:
1 311

phase_3反汇编代码如图所示在这里插入图片描述
发现调用了sscanf函数,去查了sscanf的函数原型:

int sscanf(const char *str, const char *format, ...)

结合函数传参使用寄存器的规则,可知:

  • 形参str存放在%rdi中
  • 形参format存在%rsi中
  • 第一个可变参数放在%rdx中
  • 第二个可变参数放在%rcx中

验证一下:
在这里插入图片描述
在这里插入图片描述
执行完sscanf后的结果,可以看出确实在phase_3中我们要输入两个数字进行拆弹。
在(%rsp+8)的地址处存放的是输入的第一个数字,在(%rsp+12)的地方存放的是第二个数字。
下面这段代码告诉我们第一个数应当一定是处于[0, 7]之间。

   0x0000000000400f6a <+39>:    cmpl   $0x7,0x8(%rsp)                                              
   0x0000000000400f6f <+44>:    ja     0x400fad <phase_3+106> 

接着向下执行遇到这条指令,顺腾摸瓜我们看看这个地址里放的是什么东西

0x0000000000400f75 <+50>:    jmpq   *0x402470(,%rax,8)

在这里插入图片描述

仔细看,在地址0x402478中存放的内容是:
指令mov $0x137,%eax 的地址 0x0000000000400fb9。

跳转后,将我们输入的第二个数字与0x137比较。发现相等,成功解决phase_3。
划重点:成段汇编程序的结构,明显对应着课上讲过的Switch-case语句汇编后的格式。Switch-case语句的底层实现构造了跳转表。jmpq *0x402470(,%rax,8)中的0x402470就是跳转表的首地址。
我们输入的数字相当于是一个数组下标,用来选出跳转表的表项,并把表项当成一个地址,进行进一步的跳转。

在phase_3中,需要保证第一个数字在[0,7]之间,第二个数字是什么取决于第一个数字,故本phase答案不唯一。读者可另作尝试。

phase_4

在这里插入图片描述
phase_4的输入部分phase_3很像,同样是输入两个数字。
在(%rsp+8)的地址处存放的是输入的第一个数字,在(%rsp+12)的地方存放的是第二个数字。

jbe指令是用于无符号数小于等于则跳转的意思。故下面的代码段透露的信息是第一个数字应该属于[0,14]。

0x000000000040102e <+34>:    cmpl   $0xe,0x8(%rsp)                                              
0x0000000000401033 <+39>:    jbe    0x40103a <phase_4+46>

接着执行,phase_4函数调用了func_4,func_4汇编代码如下:
在这里插入图片描述
func4函数具体没有什么确切的含义,只是一系列操作。
它以我们输入的第一个数为第一个参数参数,常数14为第二个参数,常数0为第三个参数。
注意到phase_4函数中有

   0x0000000000401048 <+60>:    callq  0x400fce <func4>                                            
   0x000000000040104d <+65>:    test   %eax,%eax                                                   
   0x000000000040104f <+67>:    jne    0x401058 <phase_4+76>                                       
   0x0000000000401051 <+69>:    cmpl   $0x0,0xc(%rsp) 
   0x0000000000401056 <+74>:    je     0x40105d <phase_4+81>                                       
   0x0000000000401058 <+76>:    callq  0x40143a <explode_bomb>                                     
   0x000000000040105d <+81>:    add    $0x18,%rsp   

说明函数func4的返回值存入%rax中,且其值需要是0才能不会引爆炸弹。考虑到func4它的确没有什么具体的含义,且我们输入的第一个数是[0,14]可以一个一个去试。
在func4中要学会的是:

  • SHL,SHR 对应着逻辑左移,逻辑右移
  • SAL, SAL 对应着算数左移,算数右移

此外,我们输入的第二个数一定得是0。如果不是0,在<+74>处就不会跳转,从而引爆炸弹。

phase_5

假设我的输入是:
ionefg

在这里插入图片描述
大致浏览后可以发现,本次任务又是要比较字符串是否相等。不过不同于phase_1,这次的字符串被“加密”过,不能直接从%rsi中获取。
核心代码在这段:

0x000000000040108b <+41>:    movzbl (%rbx,%rax,1),%ecx                                          
0x000000000040108f <+45>:    mov    %cl,(%rsp)                                                  
0x0000000000401092 <+48>:    mov    (%rsp),%rdx                                                 
0x0000000000401096 <+52>:    and    $0xf,%edx                                                   
0x0000000000401099 <+55>:    movzbl 0x4024b0(%rdx),%edx                                         
0x00000000004010a0 <+62>:    mov    %dl,0x10(%rsp,%rax,1)                                       
0x00000000004010a4 <+66>:    add    $0x1,%rax                                                   
0x00000000004010a8 <+70>:    cmp    $0x6,%rax                                                   
0x00000000004010ac <+74>:    jne    0x40108b <phase_5+41>                                       
0x00000000004010ae <+76>:    movb   $0x0,0x16(%rsp)                                             
0x00000000004010b3 <+81>:    mov    $0x40245e,%esi                                              
0x00000000004010b8 <+86>:    lea    0x10(%rsp),%rdi                                             
0x00000000004010bd <+91>:    callq  0x401338 <strings_not_equal>  

在这里插入图片描述
%rbx寄存器中存放的是我们输入字符串的首地址,%rax存放的是相对于该输入字符串的下标偏移量,用人话解释就是如果把我们的输入看作是一个字符数组,%rbx中存放的是“数组名”,%rax中存放的是数组下标。我们看看上述核心代码做了什么?

  1. 从输入的字符数组中取出一个字符,将其ascii二进制表示写入%rcx中
  2. 将%rcx的低8位写入%rsp所指向内存空间,把该数也写道%rdx中
  3. 把除了%rdx中的低4位全都置0,保留低4位
  4. 把(0x4024b0+%rdx) 地址处的值放入%rdx中。这里为什么突然来一个0x4024b0呢?检查一下这块地址。看起来一头雾水
    在这里插入图片描述
    没办法,接着向下执行,模仿phase_1的操作,看看调用string_not_equal前%rsi寄存器,它应该保存了正确的“密码”
    在这里插入图片描述
    OK,聪明的读者应该已经看出来了,我们所输入的字符串中的字符,并不是“最终的字符”而是要把这个字符的低4位看作是一个数组下标,在以0x4024b0为首地址的字符数组用这个下标找到新的字符。我们在0x4024b0这个字符数组中分别找字符f、l、y、e、r、s相对于0x4024b0的偏移量,偏移量分别是:9(1001 二进制表示,下同)、15(1111)、14(1110)、5(0101)、6(0111)、7(0111)。最后,我们只需要去网上找一个ascii字母表,看哪个字母它的二进制表示的后4位能和上面五个数字之一匹配。这个phase答案不唯一,读者可自行尝试。

phase_6

(gdb) disassemble phase_6                                                                          
Dump of assembler code for function phase_6:                                                       
=> 0x00000000004010f4 <+0>:     push   %r14                                                        
   0x00000000004010f6 <+2>:     push   %r13                                                        
   0x00000000004010f8 <+4>:     push   %r12                                                        
   0x00000000004010fa <+6>:     push   %rbp                                                        
   0x00000000004010fb <+7>:     push   %rbx                                                        
   0x00000000004010fc <+8>:     sub    $0x50,%rsp                                                  
   0x0000000000401100 <+12>:    mov    %rsp,%r13                                                   
   0x0000000000401103 <+15>:    mov    %rsp,%rsi                                                   
   0x0000000000401106 <+18>:    callq  0x40145c <read_six_numbers>                                 
   0x000000000040110b <+23>:    mov    %rsp,%r14                                                   
   0x000000000040110e <+26>:    mov    $0x0,%r12d                                                  
   0x0000000000401114 <+32>:    mov    %r13,%rbp                                                   
   0x0000000000401117 <+35>:    mov    0x0(%r13),%eax 
   0x000000000040111b <+39>:    sub    $0x1,%eax                                                   
   0x000000000040111e <+42>:    cmp    $0x5,%eax                                                   
   0x0000000000401121 <+45>:    jbe    0x401128 <phase_6+52>                                       
   0x0000000000401123 <+47>:    callq  0x40143a <explode_bomb>                                     
   0x0000000000401128 <+52>:    add    $0x1,%r12d                                                  
   0x000000000040112c <+56>:    cmp    $0x6,%r12d                                                  
   0x0000000000401130 <+60>:    je     0x401153 <phase_6+95>                                       
   0x0000000000401132 <+62>:    mov    %r12d,%ebx                                                  
   0x0000000000401135 <+65>:    movslq %ebx,%rax                                                   
   0x0000000000401138 <+68>:    mov    (%rsp,%rax,4),%eax                                          
   0x000000000040113b <+71>:    cmp    %eax,0x0(%rbp)                                              
   0x000000000040113e <+74>:    jne    0x401145 <phase_6+81>                                       
   0x0000000000401140 <+76>:    callq  0x40143a <explode_bomb>                                     
   0x0000000000401145 <+81>:    add    $0x1,%ebx                                                   
   0x0000000000401148 <+84>:    cmp    $0x5,%ebx                                                   
   0x000000000040114b <+87>:    jle    0x401135 <phase_6+65>                                       
   0x000000000040114d <+89>:    add    $0x4,%r13                                                   
   0x0000000000401151 <+93>:    jmp    0x401114 <phase_6+32>                                       
   0x0000000000401153 <+95>:    lea    0x18(%rsp),%rsi                                             
   0x0000000000401158 <+100>:   mov    %r14,%rax                                                   
   0x000000000040115b <+103>:   mov    $0x7,%ecx                                                   
   0x0000000000401160 <+108>:   mov    %ecx,%edx                                                   
   0x0000000000401162 <+110>:   sub    (%rax),%edx                                                 
   0x0000000000401164 <+112>:   mov    %edx,(%rax)                                                 
   0x0000000000401166 <+114>:   add    $0x4,%rax                                                   
   0x000000000040116a <+118>:   cmp    %rsi,%rax                                                                                            
   0x000000000040116d <+121>:   jne    0x401160 <phase_6+108>                                      
   0x000000000040116f <+123>:   mov    $0x0,%esi                                                   
   0x0000000000401174 <+128>:   jmp    0x401197 <phase_6+163>                                      
   0x0000000000401176 <+130>:   mov    0x8(%rdx),%rdx                                              
   0x000000000040117a <+134>:   add    $0x1,%eax                                                   
   0x000000000040117d <+137>:   cmp    %ecx,%eax                                                   
   0x000000000040117f <+139>:   jne    0x401176 <phase_6+130>                                      
   0x0000000000401181 <+141>:   jmp    0x401188 <phase_6+148>                                      
   0x0000000000401183 <+143>:   mov    $0x6032d0,%edx                                              
   0x0000000000401188 <+148>:   mov    %rdx,0x20(%rsp,%rsi,2)                                      
   0x000000000040118d <+153>:   add    $0x4,%rsi                                                   
   0x0000000000401191 <+157>:   cmp    $0x18,%rsi
   0x0000000000401191 <+157>:   cmp    $0x18,%rsi                                                  
   0x0000000000401195 <+161>:   je     0x4011ab <phase_6+183>                                      
   0x0000000000401197 <+163>:   mov    (%rsp,%rsi,1),%ecx                                          
   0x000000000040119a <+166>:   cmp    $0x1,%ecx                                                   
   0x000000000040119d <+169>:   jle    0x401183 <phase_6+143>                                      
   0x000000000040119f <+171>:   mov    $0x1,%eax                                                   
   0x00000000004011a4 <+176>:   mov    $0x6032d0,%edx                                              
   0x00000000004011a9 <+181>:   jmp    0x401176 <phase_6+130>                                      
   0x00000000004011ab <+183>:   mov    0x20(%rsp),%rbx                                             
   0x00000000004011b0 <+188>:   lea    0x28(%rsp),%rax                                             
   0x00000000004011b5 <+193>:   lea    0x50(%rsp),%rsi                                             
   0x00000000004011ba <+198>:   mov    %rbx,%rcx                                                   
   0x00000000004011bd <+201>:   mov    (%rax),%rdx                                                 
   0x00000000004011c0 <+204>:   mov    %rdx,0x8(%rcx)                                              
   0x00000000004011c4 <+208>:   add    $0x8,%rax                                                   
   0x00000000004011c8 <+212>:   cmp    %rsi,%rax                                                   
   0x00000000004011cb <+215>:   je     0x4011d2 <phase_6+222>                                      
   0x00000000004011cd <+217>:   mov    %rdx,%rcx                                                   
   0x00000000004011d0 <+220>:   jmp    0x4011bd <phase_6+201>                                      
   0x00000000004011d2 <+222>:   movq   $0x0,0x8(%rdx)                                              
   0x00000000004011da <+230>:   mov    $0x5,%ebp                                                   
   0x00000000004011df <+235>:   mov    0x8(%rbx),%rax                                              
   0x00000000004011e3 <+239>:   mov    (%rax),%eax                                                 
   0x00000000004011e5 <+241>:   cmp    %eax,(%rbx)                                                 
   0x00000000004011e7 <+243>:   jge    0x4011ee <phase_6+250>                                      
   0x00000000004011e9 <+245>:   callq  0x40143a <explode_bomb>                                     
   0x00000000004011ee <+250>:   mov    0x8(%rbx),%rbx                                              
   0x00000000004011f2 <+254>:   sub    $0x1,%ebp                                                   
   0x00000000004011f5 <+257>:   jne    0x4011df <phase_6+235>                                                                                
   0x00000000004011f7 <+259>:   add    $0x50,%rsp                                                  
   0x00000000004011fb <+263>:   pop    %rbx                                                        
   0x00000000004011fc <+264>:   pop    %rbp                                                        
   0x00000000004011fd <+265>:   pop    %r12                                                        
   0x00000000004011ff <+267>:   pop    %r13                                                        
   0x0000000000401201 <+269>:   pop    %r14                                                        
   0x0000000000401203 <+271>:   retq                                                               
End of assembler dump.                         

phase_6是最麻烦也是最难的一个phase了,首先本phase读取6个数字。

下面代码都是对这读取到的六个数字做检查,比如这6个数字的范围需要在[1,6],数字两两不能相同。

   0x000000000040110b <+23>:    mov    %rsp,%r14                                                   
   0x000000000040110e <+26>:    mov    $0x0,%r12d                                                  
   0x0000000000401114 <+32>:    mov    %r13,%rbp                                                   
   0x0000000000401117 <+35>:    mov    0x0(%r13),%eax 
   0x000000000040111b <+39>:    sub    $0x1,%eax                                                   
   0x000000000040111e <+42>:    cmp    $0x5,%eax                                                   
   0x0000000000401121 <+45>:    jbe    0x401128 <phase_6+52>                                       
   0x0000000000401123 <+47>:    callq  0x40143a <explode_bomb>                                     
   0x0000000000401128 <+52>:    add    $0x1,%r12d                                                  
   0x000000000040112c <+56>:    cmp    $0x6,%r12d                                                  
   0x0000000000401130 <+60>:    je     0x401153 <phase_6+95>                                       
   0x0000000000401132 <+62>:    mov    %r12d,%ebx                                                  
   0x0000000000401135 <+65>:    movslq %ebx,%rax                                                   
   0x0000000000401138 <+68>:    mov    (%rsp,%rax,4),%eax                                          
   0x000000000040113b <+71>:    cmp    %eax,0x0(%rbp)                                              
   0x000000000040113e <+74>:    jne    0x401145 <phase_6+81>                                       
   0x0000000000401140 <+76>:    callq  0x40143a <explode_bomb>                                     
   0x0000000000401145 <+81>:    add    $0x1,%ebx                                                   
   0x0000000000401148 <+84>:    cmp    $0x5,%ebx                                                   
   0x000000000040114b <+87>:    jle    0x401135 <phase_6+65>                                       
   0x000000000040114d <+89>:    add    $0x4,%r13 

接下来作者来了一个小把戏,下面汇编代码的意思是输入数字顺序不变,使用7 - 输入的数字。
比如我的输入序列为1 2 3 4 5 6。经过这个小把戏的处理后我的数字序列就变成了6 5 4 3 2 1。
这串数字序列下面分析会用到。

   0x0000000000401153 <+95>:    lea    0x18(%rsp),%rsi                                             
   0x0000000000401158 <+100>:   mov    %r14,%rax                                                   
   0x000000000040115b <+103>:   mov    $0x7,%ecx                                                   
   0x0000000000401160 <+108>:   mov    %ecx,%edx                                                   
   0x0000000000401162 <+110>:   sub    (%rax),%edx                                                 
   0x0000000000401164 <+112>:   mov    %edx,(%rax)                                                 
   0x0000000000401166 <+114>:   add    $0x4,%rax                                                   
   0x000000000040116a <+118>:   cmp    %rsi,%rax                                                                                            
   0x000000000040116d <+121>:   jne    0x401160 <phase_6+108>                                      
   0x000000000040116f <+123>:   mov    $0x0,%esi                                           

剩下这段代码是什么意思呢?这是本phase的核心代码段

   0x000000000040116f <+123>:   mov    $0x0,%esi                                                   
   0x0000000000401174 <+128>:   jmp    0x401197 <phase_6+163>                                      
   0x0000000000401176 <+130>:   mov    0x8(%rdx),%rdx                                              
   0x000000000040117a <+134>:   add    $0x1,%eax                                                   
   0x000000000040117d <+137>:   cmp    %ecx,%eax                                                   
   0x000000000040117f <+139>:   jne    0x401176 <phase_6+130>                                      
   0x0000000000401181 <+141>:   jmp    0x401188 <phase_6+148>                                      
   0x0000000000401183 <+143>:   mov    $0x6032d0,%edx                                              
   0x0000000000401188 <+148>:   mov    %rdx,0x20(%rsp,%rsi,2)                                      
   0x000000000040118d <+153>:   add    $0x4,%rsi                                                   
   0x0000000000401191 <+157>:   cmp    $0x18,%rsi
   0x0000000000401191 <+157>:   cmp    $0x18,%rsi                                                  
   0x0000000000401195 <+161>:   je     0x4011ab <phase_6+183>                                      
   0x0000000000401197 <+163>:   mov    (%rsp,%rsi,1),%ecx                                          
   0x000000000040119a <+166>:   cmp    $0x1,%ecx                                                   
   0x000000000040119d <+169>:   jle    0x401183 <phase_6+143>                                      
   0x000000000040119f <+171>:   mov    $0x1,%eax                                                   
   0x00000000004011a4 <+176>:   mov    $0x6032d0,%edx                                              
   0x00000000004011a9 <+181>:   jmp    0x401176 <phase_6+130>                                      
 

建议小伙伴们在调试这一段时一定要细扣函数的内存布局,你会清楚很多。
注意到这条指令引用了一个奇怪的地址,打印出来看看:

 0x00000000004011a4 <+176>:   mov    $0x6032d0,%edx

在这里插入图片描述
这段代码执行完是什么效果呢,就是按着6 5 4 3 2 1的顺序,从(%rsp+32)的位置开始,分别记录node6 node5 node4 node3 node2 node1 的地址。
我举得例子是这个顺序,换个例子比如此时经过作者恶作剧处理过后的数字序列为3 4 5 6 1 2。
那么自(%rsp+32)的位置开始,分别记录node3 node4 node5 node6 node1 node2 的地址。检查下内存布局,看看是不是我们想的这样:
(这里举一个额外的例子,下面我们还用6 5 4 3 2 1为例)
在这里插入图片描述
可以看到和我们想的一样,0x7fffffffe2b0处所存储的地址正是node6的地址

接着执行,处理下面的代码段:

   0x00000000004011ab <+183>:   mov    0x20(%rsp),%rbx                                             
   0x00000000004011b0 <+188>:   lea    0x28(%rsp),%rax                                             
   0x00000000004011b5 <+193>:   lea    0x50(%rsp),%rsi                                             
   0x00000000004011ba <+198>:   mov    %rbx,%rcx                                                   
   0x00000000004011bd <+201>:   mov    (%rax),%rdx                                                 
   0x00000000004011c0 <+204>:   mov    %rdx,0x8(%rcx)                                              
   0x00000000004011c4 <+208>:   add    $0x8,%rax                                                   
   0x00000000004011c8 <+212>:   cmp    %rsi,%rax                                                   
   0x00000000004011cb <+215>:   je     0x4011d2 <phase_6+222>                                      
   0x00000000004011cd <+217>:   mov    %rdx,%rcx                                                   
   0x00000000004011d0 <+220>:   jmp    0x4011bd <phase_6+201>                                         

回顾上面的对node1 ~ node6处内存检查,<node + 8>这前面一直没有用到,在这里终于用到了。
回顾上一代码段我们得到自(%rsp + 32)开始,内存中分别放入了node6 node5 node4 node3 node2 node1 的地址。我们可以把这个地址序列,当成是逻辑上的序列,即我们希望node的排布顺序是这样的
但是实际上我们检查内存看到了,物理上node们的排布顺序是实打实地按1 2 3 4 5 6的顺序。
怎么办?**联系链表的思想,我们在<node+8>处放后继元素的地址就好了!**实际上上述代码段做的就是这个工作。至此node的含义也搞清楚了,node中含有两个元素,一个元素是自身的value,另一个元素是后继元素的地址。

   0x00000000004011d2 <+222>:   movq   $0x0,0x8(%rdx)                                              
   0x00000000004011da <+230>:   mov    $0x5,%ebp                                                   
   0x00000000004011df <+235>:   mov    0x8(%rbx),%rax                                              
   0x00000000004011e3 <+239>:   mov    (%rax),%eax                                                 
   0x00000000004011e5 <+241>:   cmp    %eax,(%rbx)                                                 
   0x00000000004011e7 <+243>:   jge    0x4011ee <phase_6+250>                                      
   0x00000000004011e9 <+245>:   callq  0x40143a <explode_bomb>                                     
   0x00000000004011ee <+250>:   mov    0x8(%rbx),%rbx                                              
   0x00000000004011f2 <+254>:   sub    $0x1,%ebp                                                   
   0x00000000004011f5 <+257>:   jne    0x4011df <phase_6+235>                                                                                
   0x00000000004011f7 <+259>:   add    $0x50,%rsp                                                  
   0x00000000004011fb <+263>:   pop    %rbx                                                        
   0x00000000004011fc <+264>:   pop    %rbp                                                        
   0x00000000004011fd <+265>:   pop    %r12                                                        
   0x00000000004011ff <+267>:   pop    %r13                                                        
   0x0000000000401201 <+269>:   pop    %r14                                                        
   0x0000000000401203 <+271>:   retq                       

最后一段代码,比较简单了,按前面的分析,我们直到node们实际上是链表中的节点。这部分代码段的功能是判断相邻两个节点中,靠近头节点的节点要比靠近尾节点的value大。
用人话解释就是,假设链表头在左,链表尾在右,对相邻节点来说,左边value域中的值要比右边value域中的值要大。答案是序列4 3 2 1 6 5
至此phase_6已经全部分析完成。

  • 15
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值