Skip to main content

CSAPP:BombLab

3141 words·15 mins· loading · loading ·
mio
Author
mio
I’m Just A Student…

CSAPP:BombLab
#

[!NOTE]

本文主要参考博客:arthals.ink,如果你要学习方法,你只要看TA写的就可以了,我只看了前两层, 只是做个记录,我认为对于我来说很好的解决问题方式就是写注释(所以我这里有逐行的注释)。

gdb指令

p $rax  			# 打印寄存器 rax 的值
p $rsp  			# 打印栈指针的值
p/x $rsp  			# 打印栈指针的值,以十六进制显示
p/d $rsp  			# 打印栈指针的值,以十进制显示

x/2x $rsp  			# 以十六进制格式查看栈指针 %rsp 指向的内存位置 M[%rsp] 开始的两个单位。
x/2d $rsp 			# 以十进制格式查看栈指针 %rsp 指向的内存位置 M[%rsp] 开始的两个单位。
x/2c $rsp 			# 以字符格式查看栈指针 %rsp 指向的内存位置 M[%rsp] 开始的两个单位。
x/s $rsp 			# 把栈指针指向的内存位置 M[%rsp] 当作 C 风格字符串来查看。

x/b $rsp 			# 检查栈指针指向的内存位置 M[%rsp] 开始的 1 字节。
x/h $rsp 			# 检查栈指针指向的内存位置 M[%rsp] 开始的 2 字节(半字)。
x/w $rsp 			# 检查栈指针指向的内存位置 M[%rsp] 开始的 4 字节(字)。
x/g $rsp 			# 检查栈指针指向的内存位置 M[%rsp] 开始的 8 字节(双字)。

info registers  	# 打印所有寄存器的值
info breakpoints  	# 打印所有断点的信息

delete breakpoints 1  # 删除第一个断点,可以简写为 d 1

phase1:
#

比较简单,就是对比一下字符串,熟悉一下。

00000000000015ab <phase_1>:
    15ab:	f3 0f 1e fa          	endbr64 
    15af:	48 83 ec 08          	sub    $0x8,%rsp
    15b3:	48 8d 35 f2 1a 00 00 	lea    0x1af2(%rip),%rsi        # 这很简单,你只要查看rsi里面存放了什么东西就可以
    15ba:	e8 f3 05 00 00       	call   1bb2 <strings_not_equal>
    15bf:	85 c0                	test   %eax,%eax
    15c1:	75 05                	jne    15c8 <phase_1+0x1d>
    15c3:	48 83 c4 08          	add    $0x8,%rsp
    15c7:	c3                   	ret    
    15c8:	e8 f9 06 00 00       	call   1cc6 <explode_bomb>
    15cd:	eb f4                	jmp    15c3 <phase_1+0x18>

phase2:
#

先看phase_2的代码,这是典型的循环

00000000000015cf <phase_2>:
    15cf:	f3 0f 1e fa          	endbr64                         # 用来防止ROP攻击 
    15d3:	55                   	push   %rbp                     # 两个局部变量
    15d4:	53                   	push   %rbx
    15d5:	48 83 ec 28          	sub    $0x28,%rsp               # 分配了40个字节
    15d9:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax            # 金丝雀值,用来防止恶意修改
    15e0:	00 00 
    15e2:	48 89 44 24 18       	mov    %rax,0x18(%rsp)          # 把金丝雀值保存起来,这样被修改的时候就会提醒
    15e7:	31 c0                	xor    %eax,%eax                # ???
    15e9:	48 89 e6             	mov    %rsp,%rsi                # 栈指针的值赋给了rsi
    15ec:	e8 2d 07 00 00       	call   1d1e <read_six_numbers>  # 调用一个读取6个数字的函数
    15f1:	83 3c 24 01          	cmpl   $0x1,(%rsp)              # 第一个数字为1
    15f5:	75 0a                	jne    1601 <phase_2+0x32>
    15f7:	48 89 e3             	mov    %rsp,%rbx                # rbx为当前栈顶的地址
    15fa:	48 8d 6c 24 14       	lea    0x14(%rsp),%rbp          # rbp存放rsp + 20bytes的地址 0 4 8 12 16 20刚好六个数字用栈传递
    15ff:	eb 10                	jmp    1611 <phase_2+0x42>      # 无条件跳转1611
    1601:	e8 c0 06 00 00       	call   1cc6 <explode_bomb>
    1606:	eb ef                	jmp    15f7 <phase_2+0x28>
    1608:	48 83 c3 04          	add    $0x4,%rbx                # 相等的情况下考察第二个参数的情况
    160c:	48 39 eb             	cmp    %rbp,%rbx                # 循环终止条件
    160f:	74 10                	je     1621 <phase_2+0x52>
    1611:	8b 03                	mov    (%rbx),%eax              # 取第一个参数到eax
    1613:	01 c0                	add    %eax,%eax                # eax = eax * 2
    1615:	39 43 04             	cmp    %eax,0x4(%rbx)           # 和第二个参数作比较
    1618:	74 ee                	je     1608 <phase_2+0x39>      # 相等继续,不相等爆炸
    161a:	e8 a7 06 00 00       	call   1cc6 <explode_bomb>
    161f:	eb e7                	jmp    1608 <phase_2+0x39>
    1621:	48 8b 44 24 18       	mov    0x18(%rsp),%rax
    1626:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax
    162d:	00 00 
    162f:	75 07                	jne    1638 <phase_2+0x69>
    1631:	48 83 c4 28          	add    $0x28,%rsp
    1635:	5b                   	pop    %rbx
    1636:	5d                   	pop    %rbp
    1637:	c3                   	ret    
    1638:	e8 13 fc ff ff       	call   1250 <__stack_chk_fail@plt>

再看它调用的读取六个数字的function

0000000000001d1e <read_six_numbers>:
    1d1e:	f3 0f 1e fa          	endbr64 
    1d22:	48 83 ec 08          	sub    $0x8,%rsp                # 再分配8个字节
    1d26:	48 89 f2             	mov    %rsi,%rdx                # 记住rsi是上一层栈顶的位置放到rdx这里
    1d29:	48 8d 4e 04          	lea    0x4(%rsi),%rcx           # 把这些参数用寄存器向下一个sscanf传递
    1d2d:	48 8d 46 14          	lea    0x14(%rsi),%rax
    1d31:	50                   	push   %rax
    1d32:	48 8d 46 10          	lea    0x10(%rsi),%rax
    1d36:	50                   	push   %rax
    1d37:	4c 8d 4e 0c          	lea    0xc(%rsi),%r9
    1d3b:	4c 8d 46 08          	lea    0x8(%rsi),%r8
    1d3f:	48 8d 35 b6 15 00 00 	lea    0x15b6(%rip),%rsi        # 32fc <array.0+0x1fc> 这里应该是我们输入的数字 int sscanf(const char *str, const char *format, ...);
    1d46:	b8 00 00 00 00       	mov    $0x0,%eax
    # 分析一下sscanf的参数 rdi:就是我们输入的string,rsi是格式,就是"%d %d %d %d %d %d",rdx是第一个数,rcx是第二个数,r8是第三个数,r9是第四个数,现在寄存器不够用,用栈传递参数,并且是从右向左的这就很好理解了
    1d4b:	e8 b0 f5 ff ff       	call   1300 <__isoc99_sscanf@plt> #这里要调用sscanf函数
    1d50:	48 83 c4 10          	add    $0x10,%rsp
    1d54:	83 f8 05             	cmp    $0x5,%eax
    1d57:	7e 05                	jle    1d5e <read_six_numbers+0x40>
    1d59:	48 83 c4 08          	add    $0x8,%rsp
    1d5d:	c3                   	ret    
    1d5e:	e8 63 ff ff ff       	call   1cc6 <explode_bomb>

phase3:
#

本层就是关于一些条件的判断(大概就是switch语句)(理解提升了,之前自己肯定没办法做出来的):

000000000000163d <phase_3>:                                         # 提醒是关于switch语句,不是哥们是否有些太长了
    163d:	f3 0f 1e fa          	endbr64                         # 我们按照线性的方法先走一遍程序
    1641:	48 83 ec 28          	sub    $0x28,%rsp               # 分配了40个字节
    1645:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax            # 金丝雀值
    164c:	00 00 
    164e:	48 89 44 24 18       	mov    %rax,0x18(%rsp)          # 金丝雀值放在24个字节开始的位置
    1653:	31 c0                	xor    %eax,%eax                # 检查
    1655:	48 8d 4c 24 0f       	lea    0xf(%rsp),%rcx           # rcx = rsp + 15 占一个字节 第四个参数 a2
    165a:	48 8d 54 24 10       	lea    0x10(%rsp),%rdx          # rdx = rsp + 16 占四个字节 第三个参数 a1
    165f:	4c 8d 44 24 14       	lea    0x14(%rsp),%r8           # r8 = rsp + 20  占四个字节 第五个参数 a3
    1664:	48 8d 35 5e 1a 00 00 	lea    0x1a5e(%rip),%rsi        # 30c9 <_IO_stdin_used+0xc9> 这里的rsi是"%d %c %d"
    166b:	e8 90 fc ff ff       	call   1300 <__isoc99_sscanf@plt>
    1670:	83 f8 02             	cmp    $0x2,%eax                # sscanf的返回值是读取的参数的个数,若参数小于2错
    1673:	7e 20                	jle    1695 <phase_3+0x58>
    1675:	83 7c 24 10 07       	cmpl   $0x7,0x10(%rsp)          # a1大于7就爆炸
    167a:	0f 87 0a 01 00 00    	ja     178a <phase_3+0x14d>     
    1680:	8b 44 24 10          	mov    0x10(%rsp),%eax          # rax = a1(我们先假设a1 = 6)
    1684:	48 8d 15 55 1a 00 00 	lea    0x1a55(%rip),%rdx        # 30e0 <_IO_stdin_used+0xe0> rdx中加载了一个-68?
    168b:	48 63 04 82          	movslq (%rdx,%rax,4),%rax       # rax = 4 * rax + rdx 
    168f:	48 01 d0             	add    %rdx,%rax                # rax = rax + rdx(可能是跳表位置的计算?)
    1692:	3e ff e0             	notrack jmp *%rax               # 其作用是 跳转到 RAX 寄存器存储的地址,并且不记录 return address 到 影子调用栈(Shadow Stack
    1695:	e8 2c 06 00 00       	call   1cc6 <explode_bomb>
    169a:	eb d9                	jmp    1675 <phase_3+0x38>
    169c:	b8 77 00 00 00       	mov    $0x77,%eax
    16a1:	81 7c 24 14 a8 01 00 	cmpl   $0x1a8,0x14(%rsp)
    16a8:	00 
    16a9:	0f 84 e5 00 00 00    	je     1794 <phase_3+0x157>
    16af:	e8 12 06 00 00       	call   1cc6 <explode_bomb>
    16b4:	b8 77 00 00 00       	mov    $0x77,%eax
    16b9:	e9 d6 00 00 00       	jmp    1794 <phase_3+0x157>
    16be:	b8 70 00 00 00       	mov    $0x70,%eax
    16c3:	81 7c 24 14 bc 00 00 	cmpl   $0xbc,0x14(%rsp)
    16ca:	00 
    16cb:	0f 84 c3 00 00 00    	je     1794 <phase_3+0x157>
    16d1:	e8 f0 05 00 00       	call   1cc6 <explode_bomb>
    16d6:	b8 70 00 00 00       	mov    $0x70,%eax
    16db:	e9 b4 00 00 00       	jmp    1794 <phase_3+0x157>
    16e0:	b8 78 00 00 00       	mov    $0x78,%eax
    16e5:	81 7c 24 14 40 03 00 	cmpl   $0x340,0x14(%rsp)
    16ec:	00 
    16ed:	0f 84 a1 00 00 00    	je     1794 <phase_3+0x157>
    16f3:	e8 ce 05 00 00       	call   1cc6 <explode_bomb>
    16f8:	b8 78 00 00 00       	mov    $0x78,%eax
    16fd:	e9 92 00 00 00       	jmp    1794 <phase_3+0x157>
    1702:	b8 6e 00 00 00       	mov    $0x6e,%eax
    1707:	83 7c 24 14 39       	cmpl   $0x39,0x14(%rsp)
    170c:	0f 84 82 00 00 00    	je     1794 <phase_3+0x157>
    1712:	e8 af 05 00 00       	call   1cc6 <explode_bomb>
    1717:	b8 6e 00 00 00       	mov    $0x6e,%eax
    171c:	eb 76                	jmp    1794 <phase_3+0x157>
    171e:	b8 74 00 00 00       	mov    $0x74,%eax
    1723:	81 7c 24 14 c4 03 00 	cmpl   $0x3c4,0x14(%rsp)
    172a:	00 
    172b:	74 67                	je     1794 <phase_3+0x157>
    172d:	e8 94 05 00 00       	call   1cc6 <explode_bomb>
    1732:	b8 74 00 00 00       	mov    $0x74,%eax
    1737:	eb 5b                	jmp    1794 <phase_3+0x157>
    1739:	b8 67 00 00 00       	mov    $0x67,%eax
    173e:	81 7c 24 14 95 03 00 	cmpl   $0x395,0x14(%rsp)
    1745:	00 
    1746:	74 4c                	je     1794 <phase_3+0x157>
    1748:	e8 79 05 00 00       	call   1cc6 <explode_bomb>
    174d:	b8 67 00 00 00       	mov    $0x67,%eax
    1752:	eb 40                	jmp    1794 <phase_3+0x157>
    1754:	b8 71 00 00 00       	mov    $0x71,%eax           # a1小于7会跳转到这里 eax = 71,这里已经重新赋值了
    1759:	81 7c 24 14 f2 01 00 	cmpl   $0x1f2,0x14(%rsp)    # 看第三个参数的值,不等于498就爆炸
    1760:	00 
    1761:	74 31                	je     1794 <phase_3+0x157>
    1763:	e8 5e 05 00 00       	call   1cc6 <explode_bomb>
    1768:	b8 71 00 00 00       	mov    $0x71,%eax
    176d:	eb 25                	jmp    1794 <phase_3+0x157>
    176f:	b8 6e 00 00 00       	mov    $0x6e,%eax
    1774:	81 7c 24 14 83 03 00 	cmpl   $0x383,0x14(%rsp)
    177b:	00 
    177c:	74 16                	je     1794 <phase_3+0x157>
    177e:	e8 43 05 00 00       	call   1cc6 <explode_bomb>
    1783:	b8 6e 00 00 00       	mov    $0x6e,%eax
    1788:	eb 0a                	jmp    1794 <phase_3+0x157>
    178a:	e8 37 05 00 00       	call   1cc6 <explode_bomb>
    178f:	b8 6e 00 00 00       	mov    $0x6e,%eax
    1794:	38 44 24 0f          	cmp    %al,0xf(%rsp)        # 等于498的情况下来到这里,看输入的字符和al的值是否相等,查ASCII这里应该是G
    1798:	75 15                	jne    17af <phase_3+0x172> # 不相等爆炸
    179a:	48 8b 44 24 18       	mov    0x18(%rsp),%rax      # 第二个是q,OK结束,完全不知道具体的switch但是能做
    179f:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax
    17a6:	00 00 
    17a8:	75 0c                	jne    17b6 <phase_3+0x179>
    17aa:	48 83 c4 28          	add    $0x28,%rsp
    17ae:	c3                   	ret    
    17af:	e8 12 05 00 00       	call   1cc6 <explode_bomb>
    17b4:	eb e4                	jmp    179a <phase_3+0x15d>
    17b6:	e8 95 fa ff ff       	call   1250 <__stack_chk_fail@plt>

phase4:
#

关于递归函数?

这是phase4中调用的方法:

00000000000017bb <func4>:                                       # 开始的参数 edi:a1(x1) esi:0(x2) edx:14(x3) 设返回值为x
    17bb:	f3 0f 1e fa          	endbr64                     # 这应该是一个递归函数
    17bf:	53                   	push   %rbx                 # 临时变量 (temp)
    17c0:	89 d0                	mov    %edx,%eax            # x = x3
    17c2:	29 f0                	sub    %esi,%eax            # x -= x2
    17c4:	89 c3                	mov    %eax,%ebx            # temp = x
    17c6:	c1 eb 1f             	shr    $0x1f,%ebx           # 这里相当于是取了temp的符号
    17c9:	01 c3                	add    %eax,%ebx            # temp += x
    17cb:	d1 fb                	sar    %ebx                 # temp /= 2(这里就是默认省略了1,愚蠢)
    17cd:	01 f3                	add    %esi,%ebx            # temp += x2
    17cf:	39 fb                	cmp    %edi,%ebx            # 比较和x1相不相等
    17d1:	7f 06                	jg     17d9 <func4+0x1e>    # 如果大于>
    17d3:	7c 10                	jl     17e5 <func4+0x2a>    # 如果小于<
    17d5:	89 d8                	mov    %ebx,%eax            # x = temp
    17d7:	5b                   	pop    %rbx
    17d8:	c3                   	ret    
    17d9:	8d 53 ff             	lea    -0x1(%rbx),%edx      # 大于x1的情况 x3 = temp - 1
    17dc:	e8 da ff ff ff       	call   17bb <func4>         # 递归调用
    17e1:	01 c3                	add    %eax,%ebx            # temp += x
    17e3:	eb f0                	jmp    17d5 <func4+0x1a>    # 返回
    17e5:	8d 73 01             	lea    0x1(%rbx),%esi       # 小于的情况 x1 = temp + 1
    17e8:	e8 ce ff ff ff       	call   17bb <func4>         # 递归调用
    17ed:	01 c3                	add    %eax,%ebx            # temp += x
    17ef:	eb e4                	jmp    17d5 <func4+0x1a>    # 返回
00000000000017f1 <phase_4>:
    17f1:	f3 0f 1e fa          	endbr64 
    17f5:	48 83 ec 18          	sub    $0x18,%rsp               # 分配24个字节
    17f9:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax            # 金丝雀值
    1800:	00 00 
    1802:	48 89 44 24 08       	mov    %rax,0x8(%rsp)           # 把金丝雀值放到了stack上面
    1807:	31 c0                	xor    %eax,%eax
    1809:	48 8d 4c 24 04       	lea    0x4(%rsp),%rcx           # 栈上参数传递 第四个参数 4字节 a2
    180e:	48 89 e2             	mov    %rsp,%rdx                # 第三个参数 4字节 a1
    1811:	48 8d 35 f0 1a 00 00 	lea    0x1af0(%rip),%rsi        # 参数格式:"%d %d"
    1818:	e8 e3 fa ff ff       	call   1300 <__isoc99_sscanf@plt>
    181d:	83 f8 02             	cmp    $0x2,%eax                # 是否读取正确
    1820:	75 06                	jne    1828 <phase_4+0x37>      # 不正确爆炸
    1822:	83 3c 24 0e          	cmpl   $0xe,(%rsp)              # a1 <= 14不然爆炸
    1826:	76 05                	jbe    182d <phase_4+0x3c>
    1828:	e8 99 04 00 00       	call   1cc6 <explode_bomb>      # a1 <= 14跳到这里 这里大概是为调用f4做准备
    182d:	ba 0e 00 00 00       	mov    $0xe,%edx                # 第三个参数是14
    1832:	be 00 00 00 00       	mov    $0x0,%esi                # 第二个参数是0
    1837:	8b 3c 24             	mov    (%rsp),%edi              # 第一个参数是a1
    183a:	e8 7c ff ff ff       	call   17bb <func4>             # 调用了f4,那就是说我们根据f4的逻辑来设置a1的输入
    183f:	83 f8 12             	cmp    $0x12,%eax               # 将返回值和18作比较
    1842:	75 07                	jne    184b <phase_4+0x5a>      # 不相同就爆炸
    1844:	83 7c 24 04 12       	cmpl   $0x12,0x4(%rsp)          # 把a2和18作比较
    1849:	74 05                	je     1850 <phase_4+0x5f>      # 不相同爆炸,相同结束
    184b:	e8 76 04 00 00       	call   1cc6 <explode_bomb>
    1850:	48 8b 44 24 08       	mov    0x8(%rsp),%rax
    1855:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax
    185c:	00 00 
    185e:	75 05                	jne    1865 <phase_4+0x74>
    1860:	48 83 c4 18          	add    $0x18,%rsp
    1864:	c3                   	ret    
    1865:	e8 e6 f9 ff ff       	call   1250 <__stack_chk_fail@plt>

我对于fun4做了逆向:

#include<stdio.h>
//手动逆向代码fun4
int fun4(int num1, int num2, int num3){
    int x = num3 - num2;
    int temp = x;
    if(temp < 0){
        ++temp;
    }
    temp /= 2;
    temp += num2;
    if(temp > num1){
        //要注意调用完成之后获取的rax的使用(因为这里只调用但没有获取值浪费了很长时间)
       return fun4(num1, num2, temp - 1) + temp;
    }else if(temp < num1){
       return fun4(num1, temp + 1, num3) + temp;
    }else{
        return temp;
    } 
}
//就是给一个输入,使得返回值为0x12
int main(){
    int num1;
    //scanf("%d", &num1);
    //当输入11时,答案为18,也就是answer
    int value = fun4(11, 0, 0xe);
    printf("%d\n", value);
}

phase5:
#

hint:我的输入和array之间的转换关系,也不是很难

phase5:

000000000000186a <phase_5>:
    186a:	f3 0f 1e fa          	endbr64 
    186e:	53                   	push   %rbx                     # 一个局部变量
    186f:	48 83 ec 10          	sub    $0x10,%rsp               # 开了16字节空间
    1873:	48 89 fb             	mov    %rdi,%rbx                # 局部变量存放rdi,rdi就是字符串的首地址
    1876:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax            # 金丝雀值
    187d:	00 00 
    187f:	48 89 44 24 08       	mov    %rax,0x8(%rsp)           # 放在栈上,也就是说有8字节的可用空间
    1884:	31 c0                	xor    %eax,%eax                # 校验
    1886:	e8 06 03 00 00       	call   1b91 <string_length>     # 调用string_length,这里应该是rdi作为参数读入了一个string
    188b:	83 f8 06             	cmp    $0x6,%eax                # 返回值和6比较,不相等爆炸,输入的字符串的长度要是6才可以
    188e:	75 55                	jne    18e5 <phase_5+0x7b>
    1890:	b8 00 00 00 00       	mov    $0x0,%eax                # eax = 0?下面大概是为strings_not_equal做准备,不相等爆炸
    1895:	48 8d 0d 64 18 00 00 	lea    0x1864(%rip),%rcx        # maduiersnfotvbylWow! You've defused the secret stage!
    189c:	0f b6 14 03          	movzbl (%rbx,%rax,1),%edx       # 就是我输入数字,从上面这个stirng中找值,构造一个rdi                                                             
    18a0:	83 e2 0f             	and    $0xf,%edx
    18a3:	0f b6 14 11          	movzbl (%rcx,%rdx,1),%edx
    18a7:	88 54 04 01          	mov    %dl,0x1(%rsp,%rax,1)
    18ab:	48 83 c0 01          	add    $0x1,%rax
    18af:	48 83 f8 06          	cmp    $0x6,%rax                # rax就是一个index作为循环控制量
    18b3:	75 e7                	jne    189c <phase_5+0x32>
    18b5:	c6 44 24 07 00       	movb   $0x0,0x7(%rsp)           # 最后为我们构造的字符串添加了一个结束符号
    18ba:	48 8d 7c 24 01       	lea    0x1(%rsp),%rdi
    18bf:	48 8d 35 0c 18 00 00 	lea    0x180c(%rip),%rsi        # *rsi = "bruins" 通过上面的操作,*rdi要等于"bruins"怎么操作?
    18c6:	e8 e7 02 00 00       	call   1bb2 <strings_not_equal> # 意思就是两个字符串不相同就爆炸
    18cb:	85 c0                	test   %eax,%eax
    18cd:	75 1d                	jne    18ec <phase_5+0x82>
    18cf:	48 8b 44 24 08       	mov    0x8(%rsp),%rax
    18d4:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax
    18db:	00 00 
    18dd:	75 14                	jne    18f3 <phase_5+0x89>
    18df:	48 83 c4 10          	add    $0x10,%rsp
    18e3:	5b                   	pop    %rbx
    18e4:	c3                   	ret    
    18e5:	e8 dc 03 00 00       	call   1cc6 <explode_bomb>
    18ea:	eb a4                	jmp    1890 <phase_5+0x26>
    18ec:	e8 d5 03 00 00       	call   1cc6 <explode_bomb>
    18f1:	eb dc                	jmp    18cf <phase_5+0x65>
    18f3:	e8 58 f9 ff ff       	call   1250 <__stack_chk_fail@plt>

我们不必细究它调用的两个方法的具体实现了,就和函数名字一样。我输入的string是"M63487",因为实际上会和0xf作与运算,所以每个字符都是可选的。

phase6:
#

[!CAUTION]

应该是最难的一层了,hint:链表,那就要用到结构体了吧。(做完:其实还好,只要你理解它在干什么。)

00000000000018f8 <phase_6>:
    18f8:	f3 0f 1e fa          	endbr64                        # 关于链表操作,最逆天的一层,孩子们
    18fc:	41 57                	push   %r15                    # 6个局部变量,都是拿来干嘛的???
    18fe:	41 56                	push   %r14
    1900:	41 55                	push   %r13
    1902:	41 54                	push   %r12
    1904:	55                   	push   %rbp
    1905:	53                   	push   %rbx
    1906:	48 83 ec 78          	sub    $0x78,%rsp              # 分配120个bytes
    190a:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax           # 金丝雀值
    1911:	00 00 
    1913:	48 89 44 24 68       	mov    %rax,0x68(%rsp)         # 放到栈上,有104个bytes是可用的
    1918:	31 c0                	xor    %eax,%eax               # 检测金丝雀值
    191a:	4c 8d 74 24 10       	lea    0x10(%rsp),%r14         # 此时r14存放的是rsp + 16的地址 
    191f:	4c 89 74 24 08       	mov    %r14,0x8(%rsp)          # 把rsp + 16的地址放在rsp + 8的位置
    1924:	4c 89 f6             	mov    %r14,%rsi               # 把rsp + 16的地址作为第二个参数
    1927:	e8 f2 03 00 00       	call   1d1e <read_six_numbers> # 读取了六个数字 rsp + 16 20 24 28 32 36放在这六个位置
    192c:	4d 89 f4             	mov    %r14,%r12               # r12中放 rsp + 16的地址
    192f:	41 bf 01 00 00 00    	mov    $0x1,%r15d              # r15 = 1
    1935:	4d 89 f5             	mov    %r14,%r13               # r13中放 rsp + 16的地址
    1938:	e9 c6 00 00 00       	jmp    1a03 <phase_6+0x10b>    # 跳转
    193d:	e8 84 03 00 00       	call   1cc6 <explode_bomb>
    1942:	e9 ce 00 00 00       	jmp    1a15 <phase_6+0x11d>
    1947:	48 83 c3 01          	add    $0x1,%rbx               # rbx刚刚为1,这里就是作为一个循环控制变量 ++index(第二层循环)
    194b:	83 fb 05             	cmp    $0x5,%ebx               # 和5比较
    194e:	0f 8f a7 00 00 00    	jg     19fb <phase_6+0x103>    # 如果大于5跳转
    1954:	41 8b 44 9d 00       	mov    0x0(%r13,%rbx,4),%eax   # r15小于5的情况:eax中存放 *(rsp + 4 * index)
    1959:	39 45 00             	cmp    %eax,0x0(%rbp)          # 和首元素做比较
    195c:	75 e9                	jne    1947 <phase_6+0x4f>     # 不相等跳转,相等直接爆炸
    195e:	e8 63 03 00 00       	call   1cc6 <explode_bomb>
    1963:	eb e2                	jmp    1947 <phase_6+0x4f>
    1965:	48 8b 54 24 08       	mov    0x8(%rsp),%rdx          # 至此输入检查已经结束,rdx = rsp + 16(不要想错了,这里存放的值是rsp + 16)
    196a:	48 83 c2 18          	add    $0x18,%rdx              # rdx = rsp + 36
    196e:	b9 07 00 00 00       	mov    $0x7,%ecx               # rcx = 7
    1973:	89 c8                	mov    %ecx,%eax               # eax = 7
    1975:	41 2b 04 24          	sub    (%r12),%eax             # eax为7减去数组中的元素
    1979:	41 89 04 24          	mov    %eax,(%r12)             # 再把这个减了之后的值加载回去
    197d:	49 83 c4 04          	add    $0x4,%r12               # 下一个数字
    1981:	4c 39 e2             	cmp    %r12,%rdx               # 检查终止条件
    1984:	75 ed                	jne    1973 <phase_6+0x7b>
    1986:	be 00 00 00 00       	mov    $0x0,%esi               # 现在输入的每个数字都成了它对于7的补 rsi = 0,假设输入2 6 1 5 4 3 此时的值就是 5 1 6 2 3 4 rsi = 0
    198b:	8b 4c b4 10          	mov    0x10(%rsp,%rsi,4),%ecx  # rcx = *(rsp + 16 + 4 * rsi) 为数组的第一个值
    198f:	b8 01 00 00 00       	mov    $0x1,%eax               # eax = 1
    1994:	48 8d 15 75 38 00 00 	lea    0x3875(%rip),%rdx       # gdb查看内存这里就是把一个链表的node1的地址加载给了rdx,尝试用gdb去查看链表的具体结构,大概就是结构体{value + key + nextAddress}
    199b:	83 f9 01             	cmp    $0x1,%ecx               # rcx处的值和1比较
    199e:	7e 0b                	jle    19ab <phase_6+0xb3>     # 小于等于1就跳转
    19a0:	48 8b 52 08          	mov    0x8(%rdx),%rdx          # rdx此时应该为节点指向的节点的地址
    19a4:	83 c0 01             	add    $0x1,%eax               # ++eax
    19a7:	39 c8                	cmp    %ecx,%eax               # rcx和 eax比较
    19a9:	75 f5                	jne    19a0 <phase_6+0xa8>     # 不相等跳转,直到数组的第一个值和链表第一个节点的值相等就跳转
    19ab:	48 89 54 f4 30       	mov    %rdx,0x30(%rsp,%rsi,8)  # *(rsp + 48 + 8 * rsi) = rdx 把这个地址存放在stack上面
    19b0:	48 83 c6 01          	add    $0x1,%rsi               # ++rsi
    19b4:	48 83 fe 06          	cmp    $0x6,%rsi               # 循环终止条件
    19b8:	75 d1                	jne    198b <phase_6+0x93>     # 不相等继续
    19ba:	48 8b 5c 24 30       	mov    0x30(%rsp),%rbx         # 现在我们已经把按照输入数字顺序节点指向的地址放在了栈上(人话?)rbx为第一个地址
    19bf:	48 8b 44 24 38       	mov    0x38(%rsp),%rax         # rax是第二个地址
    19c4:	48 89 43 08          	mov    %rax,0x8(%rbx)          # 以下就是把链表按照我们输入的顺序连接在一起,看不明白就画图
    19c8:	48 8b 54 24 40       	mov    0x40(%rsp),%rdx
    19cd:	48 89 50 08          	mov    %rdx,0x8(%rax)
    19d1:	48 8b 44 24 48       	mov    0x48(%rsp),%rax
    19d6:	48 89 42 08          	mov    %rax,0x8(%rdx)
    19da:	48 8b 54 24 50       	mov    0x50(%rsp),%rdx
    19df:	48 89 50 08          	mov    %rdx,0x8(%rax)
    19e3:	48 8b 44 24 58       	mov    0x58(%rsp),%rax
    19e8:	48 89 42 08          	mov    %rax,0x8(%rdx)
    19ec:	48 c7 40 08 00 00 00 	movq   $0x0,0x8(%rax)          # 0就是null节点
    19f3:	00 
    19f4:	bd 05 00 00 00       	mov    $0x5,%ebp                # rbp = 5
    19f9:	eb 35                	jmp    1a30 <phase_6+0x138>     # 连接完了之后跳转
    19fb:	49 83 c7 01          	add    $0x1,%r15                # 这是应该是第一层循环
    19ff:	49 83 c6 04          	add    $0x4,%r14                # 下一个
    1a03:	4c 89 f5             	mov    %r14,%rbp                # 在成功读取六个数字之后跳转到这里,rbp存放rsp + 16地址(第一次)
    1a06:	41 8b 06             	mov    (%r14),%eax              # 读取的第一个数字
    1a09:	83 e8 01             	sub    $0x1,%eax                # 读取的数字-1
    1a0c:	83 f8 05             	cmp    $0x5,%eax                # 和5作比较
    1a0f:	0f 87 28 ff ff ff    	ja     193d <phase_6+0x45>      # 大于5爆炸(这意味着不能输入大于6的数字)
    1a15:	41 83 ff 05          	cmp    $0x5,%r15d               # r15刚刚赋值为1,现在和5作比较
    1a19:	0f 8f 46 ff ff ff    	jg     1965 <phase_6+0x6d>      # 大于5跳转(到这里为止,经过了一个类似于冒泡排序的比较,这意味着我们输入的数字不能有重复的也不能大于6)
    1a1f:	4c 89 fb             	mov    %r15,%rbx                # rbx = 1   
    1a22:	e9 2d ff ff ff       	jmp    1954 <phase_6+0x5c>      
    1a27:	48 8b 5b 08          	mov    0x8(%rbx),%rbx
    1a2b:	83 ed 01             	sub    $0x1,%ebp
    1a2e:	74 11                	je     1a41 <phase_6+0x149>		
    1a30:	48 8b 43 08          	mov    0x8(%rbx),%rax           # 连接之后在这里 
    1a34:	8b 00                	mov    (%rax),%eax
    1a36:	39 03                	cmp    %eax,(%rbx)
    1a38:	7d ed                	jge    1a27 <phase_6+0x12f>		# 也就是说链表必须是递增还是递减的一个顺序?
    1a3a:	e8 87 02 00 00       	call   1cc6 <explode_bomb>
    1a3f:	eb e6                	jmp    1a27 <phase_6+0x12f>
    1a41:	48 8b 44 24 68       	mov    0x68(%rsp),%rax
    1a46:	64 48 2b 04 25 28 00 	sub    %fs:0x28,%rax
    1a4d:	00 00 
    1a4f:	75 0f                	jne    1a60 <phase_6+0x168>
    1a51:	48 83 c4 78          	add    $0x78,%rsp
    1a55:	5b                   	pop    %rbx
    1a56:	5d                   	pop    %rbp
    1a57:	41 5c                	pop    %r12
    1a59:	41 5d                	pop    %r13
    1a5b:	41 5e                	pop    %r14
    1a5d:	41 5f                	pop    %r15
    1a5f:	c3                   	ret    
    1a60:	e8 eb f7 ff ff       	call   1250 <__stack_chk_fail@plt>

不容易,终于写完了。