【无标题】

【无标题】_第1张图片

寄存器相关的知识

【无标题】_第2张图片

【无标题】_第3张图片

【无标题】_第4张图片

phase1

// 对bomb可执行文件进行调试
gdb bomb
// 通过disas指令可以通过反汇编操作查看phase_1这个函数的汇编代码
disas phase_1
// 或者可以直接通过这个指令生成整个反汇编文件
objdump -d bomb > bomb.s
  1. 通过下面这个文件可以发现,read_line函数的结果rax放入了rdi寄存器,也就是作为phase_1函数的第一个参数,然后调用了phase_1函数
  400e32:	e8 67 06 00 00       	callq  40149e 
  400e37:	48 89 c7             	mov    %rax,%rdi
  400e3a:	e8 a1 00 00 00       	callq  400ee0 
  1. 在phase_1函数中,又将0x402400放入esi寄存器,调用了strings_not_equal函数。
  2. 此时rdi寄存器存的是readline函数的结果,esi寄存器存的是一个地址,分别作为strings_not_equal函数第一个和第二个参数。
  3. 因此可以猜测,我们需要输入的就是0x402400这个地方存的字符串。通过x/s 0x402400即可获得这个字符串
Dump of assembler code for function phase_1:
   // 函数调用时的压栈操作
   0x0000000000400ee0 <+0>:     sub    $0x8,%rsp
   // 将0x402400这个地址放入esi寄存器
   // 作为strings_not_equal的一个参数
   0x0000000000400ee4 <+4>:     mov    $0x402400,%esi
   0x0000000000400ee9 <+9>:     callq  0x401338 
   // 将eax和eax寄存器进行按位与操作,并把结果放入标志寄存器
   // eax是上面那个函数的返回值,如果eax为0,说明这两个字符是相同的
   0x0000000000400eee <+14>:    test   %eax,%eax
   // 如果零标志位为1,则je成功运行,即躲过explode_bomb
   // 也就是说零标志位如果是0,代表eax不是0,那么就会爆炸
   0x0000000000400ef0 <+16>:    je     0x400ef7 
   // 爆炸
   0x0000000000400ef2 <+18>:    callq  0x40143a 
   0x0000000000400ef7 <+23>:    add    $0x8,%rsp
   0x0000000000400efb <+27>:    retq
End of assembler dump.

phase2

反汇编代码如下
这个phase的大意是通过sccanf读取我们输入的6个数字,如果能够满足是1 2 4 8 16 32的话,那就可以通过。
结合着汇编代码去看,应该不难理解这个phase的意思。

0000000000400efc :
  400efc:	55                   	push   %rbp
  400efd:	53                   	push   %rbx
  400efe:	48 83 ec 28          	sub    $0x28,%rsp
  400f02:	48 89 e6             	mov    %rsp,%rsi
  400f05:	e8 52 05 00 00       	callq  40145c 
  400f0a:	83 3c 24 01          	cmpl   $0x1,(%rsp) // 栈指针指向的内存的值应该是1
  400f0e:	74 20                	je     400f30 
  400f10:	e8 25 05 00 00       	callq  40143a 
  400f15:	eb 19                	jmp    400f30 
  400f17:	8b 43 fc             	mov    -0x4(%rbx),%eax // 将rbx-4的位置的内存值给到eax
  400f1a:	01 c0                	add    %eax,%eax // eax乘2
  400f1c:	39 03                	cmp    %eax,(%rbx) // 即eax乘2后,要等于rbx指向的值,eax实际上是rbx下面的一个值
  400f1e:	74 05                	je     400f25 
  400f20:	e8 15 05 00 00       	callq  40143a 
  400f25:	48 83 c3 04          	add    $0x4,%rbx // 再给rbx+4
  400f29:	48 39 eb             	cmp    %rbp,%rbx
  400f2c:	75 e9                	jne    400f17  // 重复
  400f2e:	eb 0c                	jmp    400f3c  //结束当前程序
  400f30:	48 8d 5c 24 04       	lea    0x4(%rsp),%rbx // 栈顶指针+4,给到rbx
  400f35:	48 8d 6c 24 18       	lea    0x18(%rsp),%rbp // 栈顶指针+24,给到rbp,即栈底指针
  400f3a:	eb db                	jmp    400f17 
  400f3c:	48 83 c4 28          	add    $0x28,%rsp
  400f40:	5b                   	pop    %rbx
  400f41:	5d                   	pop    %rbp
  400f42:	c3                   	retq   

但是有个问题就是,我们输入的应该是正序还是倒序的?这个需要结合read_six_numbers来看了。

  1. 这个函数其实关键是调用了sscanf函数,解析我们输入的字符串
  2. 而sccanf的参数为int sscanf(const char *str, const char *format, ...),其中后面的省略号就是通过str解析出来的结果,可能有多个,在这里,就是6个数字。
  3. 可以发现,这里将0x4025c3放入了esi,即作为sscanf的第二个参数,那么说明这个地址存放的东西就是解析的format,通过打印这个地址的值可以发现就是"%d %d %d %d %d %d",说明除了str和format,我们还需要传递六个参数。
  4. 可以发现,第一个参数rdi,这里没有显式地设置,因为这个rdi从phase_2函数开始就没有变过,一直都是我们输入的字符串。
  5. read_six_numbers在调用sscanf之前的那么多行,其实就是在传递后面的六个参数,也就是第3个到第8个参数。
  6. 通过前几行就可以发现,第1个值被放在了rsi处,而rsi其实就是phase_2的栈顶位置,也就是说第一个被解析出来的值放在栈底,也就是1。因此我们的字符串应该是从1开始增长,而不是从32开始逆序
000000000040145c :
  40145c:	48 83 ec 18          	sub    $0x18,%rsp
  401460:	48 89 f2             	mov    %rsi,%rdx // 第3个参数,也是被解析的第一个值
  401463:	48 8d 4e 04          	lea    0x4(%rsi),%rcx // 第4个参数,也是被解析的第二个值
  401467:	48 8d 46 14          	lea    0x14(%rsi),%rax
  40146b:	48 89 44 24 08       	mov    %rax,0x8(%rsp)
  401470:	48 8d 46 10          	lea    0x10(%rsi),%rax
  401474:	48 89 04 24          	mov    %rax,(%rsp)
  401478:	4c 8d 4e 0c          	lea    0xc(%rsi),%r9
  40147c:	4c 8d 46 08          	lea    0x8(%rsi),%r8
  401480:	be c3 25 40 00       	mov    $,%esi
  401485:	b8 00 00 00 00       	mov    $0x0,%eax
  40148a:	e8 61 f7 ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  40148f:	83 f8 05             	cmp    $0x5,%eax
  401492:	7f 05                	jg     401499 
  401494:	e8 a1 ff ff ff       	callq  40143a 
  401499:	48 83 c4 18          	add    $0x18,%rsp
  40149d:	c3                   	retq   

phase3

这个phase会让你输入两个数字

  • 第一个数字是介于0-6之间的数,小于7即可
  • 第二个数字根据第一个数字,通过switch指令进行跳转
    一开始做的时候还没看懂那个switch的跳转指令。通过gdb调试不断往下走才确定了我输入的6对应的是哪个位置。
0000000000400f43 :
  400f43:	48 83 ec 18          	sub    $0x18,%rsp // 减小栈指针,挪出24字节的空间
  400f47:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx // 栈指针+12 给rcx
  400f4c:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx // 栈指针+8 给rdx
  400f51:	be cf 25 40 00       	mov    $0x4025cf,%esi // %d %d,sscanf的参数
  400f56:	b8 00 00 00 00       	mov    $0x0,%eax
  400f5b:	e8 90 fc ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  400f60:	83 f8 01             	cmp    $0x1,%eax // 匹配成功的数目要大于1,即大于等于2,应该是2
  400f63:	7f 05                	jg     400f6a 
  400f65:	e8 d0 04 00 00       	callq  40143a 
  400f6a:	83 7c 24 08 07       	cmpl   $0x7,0x8(%rsp) 
  400f6f:	77 3c                	ja     400fad  // 7如果大于rsp+8指向的值,则直接爆炸
  400f71:	8b 44 24 08          	mov    0x8(%rsp),%eax // rsp+8指向的值给eax
  400f75:	ff 24 c5 70 24 40 00 	jmpq   *0x402470(,%rax,8) // 间接寻址0x402470+8*rax,其实这里就是个switch操作,根据你输入的第一个数字是0-6,给你传送到下面对应的mov
  400f7c:	b8 cf 00 00 00       	mov    $0xcf,%eax // 将eax置为0xcf,即11001111 15+64+128=207
  400f81:	eb 3b                	jmp    400fbe 
  400f83:	b8 c3 02 00 00       	mov    $0x2c3,%eax
  400f88:	eb 34                	jmp    400fbe 
  400f8a:	b8 00 01 00 00       	mov    $0x100,%eax
  400f8f:	eb 2d                	jmp    400fbe 
  400f91:	b8 85 01 00 00       	mov    $0x185,%eax
  400f96:	eb 26                	jmp    400fbe 
  400f98:	b8 ce 00 00 00       	mov    $0xce,%eax
  400f9d:	eb 1f                	jmp    400fbe 
  400f9f:	b8 aa 02 00 00       	mov    $0x2aa,%eax
  400fa4:	eb 18                	jmp    400fbe 
  400fa6:	b8 47 01 00 00       	mov    $0x147,%eax
  400fab:	eb 11                	jmp    400fbe 
  400fad:	e8 88 04 00 00       	callq  40143a 
  400fb2:	b8 00 00 00 00       	mov    $0x0,%eax
  400fb7:	eb 05                	jmp    400fbe 
  400fb9:	b8 37 01 00 00       	mov    $0x137,%eax
  400fbe:	3b 44 24 0c          	cmp    0xc(%rsp),%eax // 比较eax是否等于rsp+12指向的值
  400fc2:	74 05                	je     400fc9  // 等于的话,直接退出程序
  400fc4:	e8 71 04 00 00       	callq  40143a  // 不等于的话,就爆炸
  400fc9:	48 83 c4 18          	add    $0x18,%rsp
  400fcd:	c3                   	retq   

phase4

phase_4反汇编的代码如下
要求我们两个数字,其中第一个数字要小于等于14,第二个数字必须为0。其中第一个数字要通过func4函数,并且使其返回0。

000000000040100c :
  40100c:	48 83 ec 18          	sub    $0x18,%rsp // 栈指针下移,挪出24字节的位置
  401010:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx // 将rcx置为栈顶指针+12
  401015:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx // 将rdx置为栈顶指针+8
  40101a:	be cf 25 40 00       	mov    $0x4025cf,%esi // 
  40101f:	b8 00 00 00 00       	mov    $0x0,%eax
  401024:	e8 c7 fb ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  401029:	83 f8 02             	cmp    $0x2,%eax
  40102c:	75 07                	jne    401035  // 如果输入参数个数不是两个,直接爆炸
  40102e:	83 7c 24 08 0e       	cmpl   $0xe,0x8(%rsp) // 将14 和 栈顶指针+8指向的值 比较
  401033:	76 05                	jbe    40103a  // 如果小于等于,则跳过炸弹继续执行
  401035:	e8 00 04 00 00       	callq  40143a  // 如果大于则直接爆炸
  40103a:	ba 0e 00 00 00       	mov    $0xe,%edx // 将edx置为14,func4的第3个参数
  40103f:	be 00 00 00 00       	mov    $0x0,%esi // 将esi置为0,func4的第2个参数
  401044:	8b 7c 24 08          	mov    0x8(%rsp),%edi // 将rsp+8指向的值给edi,func4的第1个参数
  401048:	e8 81 ff ff ff       	callq  400fce  // 调用一个函数
  40104d:	85 c0                	test   %eax,%eax // 如果eax不为0,则直接爆炸
  40104f:	75 07                	jne    401058  // eax不为0,跳到炸弹那里去
  401051:	83 7c 24 0c 00       	cmpl   $0x0,0xc(%rsp) // 比较0和rsp+12指向的值
  401056:	74 05                	je     40105d  // 如果rsp+12指向的值就是0,则通过
  401058:	e8 dd 03 00 00       	callq  40143a 
  40105d:	48 83 c4 18          	add    $0x18,%rsp
  401061:	c3                   	retq

func4函数的反汇编的代码

  1. func4是一个递归程序,这个代码如果要完全弄清楚,非常复杂,有一些大佬将其一步步分析成C语言代码,但是依然很恶心。不过如果只是想要通过,就没那么难。
  2. 首先,func4有3个参数,第一个参数就是我们输入的第一个数字,第二个参数固定为0,第三个参数固定为14
  3. 在func4的第一次跳转之前,ecx一定是7。然后我们用第一个数字和ecx即7比较,如果小于等于7就会跳转。
  4. 跳转之后,又用我们第一个数字和ecx即7进行比较,如果小于等于7,就会直接结束函数,并且返回0
  5. 因此,可以发现,7就是一个答案。并且是最简单的那个,都不会触发递归。
0000000000400fce :
  400fce:	48 83 ec 08          	sub    $0x8,%rsp
  400fd2:	89 d0                	mov    %edx,%eax // eax被置为14
  400fd4:	29 f0                	sub    %esi,%eax // eax被置为14
  400fd6:	89 c1                	mov    %eax,%ecx // ecx被置为14
  400fd8:	c1 e9 1f             	shr    $0x1f,%ecx // ecx被置为0
  400fdb:	01 c8                	add    %ecx,%eax // eax被置为14 0b1110
  400fdd:	d1 f8                	sar    %eax // eax被置为0b111,即7
  400fdf:	8d 0c 30             	lea    (%rax,%rsi,1),%ecx // ecx被置为7
  
  400fe2:	39 f9                	cmp    %edi,%ecx // x和7比较
  400fe4:	7e 0c                	jle    400ff2  // 如果x<=7,跳转
  400fe6:	8d 51 ff             	lea    -0x1(%rcx),%edx
  400fe9:	e8 e0 ff ff ff       	callq  400fce 
  400fee:	01 c0                	add    %eax,%eax
  400ff0:	eb 15                	jmp    401007 
  
  400ff2:	b8 00 00 00 00       	mov    $0x0,%eax // 将eax置为0
  400ff7:	39 f9                	cmp    %edi,%ecx // 将x和7比较
  400ff9:	7d 0c                	jge    401007  // 如果大于等于7,则直接跳出程序
  400ffb:	8d 71 01             	lea    0x1(%rcx),%esi // 将esi置为8
  400ffe:	e8 cb ff ff ff       	callq  400fce  // 递归
  401003:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax
  401007:	48 83 c4 08          	add    $0x8,%rsp
  40100b:	c3                   	retq   

我刚开始做phase4的时候,陷入了func4这个函数里,完全搞不清楚它在干啥。这也有了一个教训,看汇编代码的时候,不要陷进去汇编的细节,要能够提取它的作用,用C语言的形式去想一下,或者想一想它具体在完成什么动作。

phase5

这个炸弹很有意思

  1. 首先阅读汇编代码,搞清楚每个语句的作用,然后按照代码的逻辑给它划分,看的更加有层次感,最后搞清楚每个层次都干了什么
  2. 首先,这个phase需要我们输入一个长度为6的字符串
  3. 然后根据我们这些字符串的ascii码的二进制表示的最低4位的值为偏移,去系统给出的一个字符串取字符
  4. 根据输入的6个字符取出来的6个字符需要刚好和系统给定的另一个字符串相同
    做的时候踩了些坑
  5. 第一遍阅读汇编代码的时候,就可以尝试去划分和理解,不用等到完全读了一遍之后再来划分。这样效率其实很低。耐下心来正确地分析代码,还是可以在第一遍读代码的时候就理解代码的逻辑的。这样就算没有很清楚,也可以通过第二轮去专门划分
  6. 有的时候各种寄存器一波操作,其实就是完成了一个很简单的逻辑,不要迷失在细节里了
  7. 分清楚传的是寄存器的值还是这个寄存器指向的内存的值
0000000000401062 :
// 将fs:0x28的值放到rsp+24字节的位置
  401062:	53                   	push   %rbx
  401063:	48 83 ec 20          	sub    $0x20,%rsp // 给栈挪出32字节的位置
  401067:	48 89 fb             	mov    %rdi,%rbx // 将我们的输入给rbx
  40106a:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax // 将段寄存器偏移40字节的值给rax
  401071:	00 00 
  401073:	48 89 44 24 18       	mov    %rax,0x18(%rsp) // 将rax给栈顶指针+24字节的位置

// 判断输入字符串的长度是否是6,如果不是6则直接爆炸
  401078:	31 c0                	xor    %eax,%eax // eax清零
  40107a:	e8 9c 02 00 00       	callq  40131b  // 获取我们输入的字符串的长度
  40107f:	83 f8 06             	cmp    $0x6,%eax // 判断是否是6
  401082:	74 4e                	je     4010d2  // 如果是6的话,则跳过炸弹
  401084:	e8 b1 03 00 00       	callq  40143a  // 如果不是6,则直接爆炸
  401089:	eb 47                	jmp    4010d2 

// 这一段就是把我们输入的字符串通过一些奇怪的处理,放置在栈上rsp+16开始的位置,16-21
// 这个奇怪的处理应该是根据我们输入的字符,用它们的ascii码为偏移,去0x4024b0地址取字符放到rsp+16开始的位置
  40108b:	0f b6 0c 03          	movzbl (%rbx,%rax,1),%ecx // 将rbx+rax指向的值放入ecx,这里的rbx就是我们的输入,这里应该就是依次把字符给ecx
  40108f:	88 0c 24             	mov    %cl,(%rsp) // 将ecx的值为栈顶指向的位置
  401092:	48 8b 14 24          	mov    (%rsp),%rdx // 把栈顶的值给edx
  401096:	83 e2 0f             	and    $0xf,%edx // 用0b1111与edx按位与
  401099:	0f b6 92 b0 24 40 00 	movzbl 0x4024b0(%rdx),%edx // 将0x4024b0+edx指向的值给edx
  4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%rax,1) // 将edx的值放到16+rsp+rax指向的位置
  4010a4:	48 83 c0 01          	add    $0x1,%rax // 给rax+1
  4010a8:	48 83 f8 06          	cmp    $0x6,%rax // 6和rax比较
  4010ac:	75 dd                	jne    40108b  // 如果不同,则继续循环

// 判断我们放在栈上的字符串是否和0x40245e这个地址的字符一样
  4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp) // 将rsp+22指向的值置为0
  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi // 给esi一个地址0x40245e // "flyers"
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi // 给rdi一个地址rsp+16 // "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"
  4010bd:	e8 76 02 00 00       	callq  401338  // 比较这两个地址指向的字符串
  4010c2:	85 c0                	test   %eax,%eax 
  4010c4:	74 13                	je     4010d9  // 如果字符串相同,则跳过炸弹
  4010c6:	e8 6f 03 00 00       	callq  40143a  // 如果字符串不相同,则爆炸
  4010cb:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)
  4010d0:	eb 07                	jmp    4010d9 

  4010d2:	b8 00 00 00 00       	mov    $0x0,%eax // 将eax置0
  4010d7:	eb b2                	jmp    40108b  // 跳转

// 比较%fs:0x2和栈上0x18(%rsp)开始的值是否一样,一样则成功。这个测试正常来说不会有问题。
  4010d9:	48 8b 44 24 18       	mov    0x18(%rsp),%rax // 将rsp+24指向的值给rax
  4010de:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax // 将rax和一个地址的值异或
  4010e5:	00 00 
  4010e7:	74 05                	je     4010ee  // 如果等于0,则跳过下面这个函数,成功返回
  4010e9:	e8 42 fa ff ff       	callq  400b30 <__stack_chk_fail@plt>
  4010ee:	48 83 c4 20          	add    $0x20,%rsp
  4010f2:	5b                   	pop    %rbx
  4010f3:	c3                   	retq   

phase6

这个lab也很有意思啊

  1. 首先,它需要我们输入六个数字
  2. 其次,它要求我们这个六个数字都小于等于6,并且不重复
  3. 然后,它会把我们这六个数字替换为7-x,即如果输入是123456,就会被替换成654321
  4. 然后,它会根据现在的数字,去一个链表里取6个数字。比如我们现在的数字被变成了6454321,那么它就会去这个链表里取出从第六个到第一个结点的地址。再将这个地址放到栈中从rsp+0x20开始的地方
  5. 然后,它会依次把取出来的六个结点串成一个链表
  6. 最后,它会检查这个链表是否是一个单调递减链表
    因此,我们输入的六个数字其实就相当于是一个排序的方法,如果我们输入214356,那就代表着我们把原链表里的顺序从123456改为214356,然后判断修改之后是否是单调递减的。注意,这里说的123456指的是链表的结点编号,排序的依据是这个节点的val。
00000000004010f4 :
// 准备操作
  4010f4:	41 56                	push   %r14
  4010f6:	41 55                	push   %r13
  4010f8:	41 54                	push   %r12
  4010fa:	55                   	push   %rbp
  4010fb:	53                   	push   %rbx
// 开始操作
  4010fc:	48 83 ec 50          	sub    $0x50,%rsp // 开辟了80个字节的栈空间
  401100:	49 89 e5             	mov    %rsp,%r13 // 将栈顶指针给r13
  401103:	48 89 e6             	mov    %rsp,%rsi // 将栈顶指针给了rsi,即函数的第二个参数
  401106:	e8 51 03 00 00       	callq  40145c  // 将我们输入的6个数字读入栈中
  40110b:	49 89 e6             	mov    %rsp,%r14 // 把栈指针给了r14
  40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d // 将r12置为0

// 下面这一波就是判断,是否六个数字都不同,并且六个数字都小于等于6
  401114:	4c 89 ed             	mov    %r13,%rbp // 把r13给rbp,rbp是栈底指针啊
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax // 把r13指向的东西赋给了eax,r13现在是栈底,应该就是我们输入的第一个数字
  40111b:	83 e8 01             	sub    $0x1,%eax // 将输入的第一个数字减1
  40111e:	83 f8 05             	cmp    $0x5,%eax // 5和输入的数字进行比较
  401121:	76 05                	jbe    401128  // 输入的数字减1后小于等于5,则跳过炸弹,否则爆炸
  401123:	e8 12 03 00 00       	callq  40143a 
  401128:	41 83 c4 01          	add    $0x1,%r12d // 给r12加1,r12在这之前刚被置为0
  40112c:	41 83 fc 06          	cmp    $0x6,%r12d // 6和r12对比,如果一样则跳转,第一次到这里应该是不会跳转
  401130:	74 21                	je     401153 
  401132:	44 89 e3             	mov    %r12d,%ebx // 将r12给ebx,即ebx置为1
// 下面这一段是判断输入的第2到第6数是否都不和第1个数相同,如果存在相同的,则直接爆炸
  401135:	48 63 c3             	movslq %ebx,%rax // 将ebx给rax,rax=1
  401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax // eax=rsp+4*rax指向的值,第一次运行到这里应该是rsp+4,访问的是输入的第2个数字
  40113b:	39 45 00             	cmp    %eax,0x0(%rbp) // eax和rbp指向的值比较,即第一个数和第二个数进行比较
  40113e:	75 05                	jne    401145  // 如果不同,则跳转,否则就爆炸
  401140:	e8 f5 02 00 00       	callq  40143a 
  401145:	83 c3 01             	add    $0x1,%ebx // ebx+=1
  401148:	83 fb 05             	cmp    $0x5,%ebx // ebx和5进行对比,如果ebx小于等于5,则跳转
  40114b:	7e e8                	jle    401135 
// 给r13加4
  40114d:	49 83 c5 04          	add    $0x4,%r13
  401151:	eb c1                	jmp    401114 

  401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi // 将rsi指向rsp+24的位置
  401158:	4c 89 f0             	mov    %r14,%rax // 把r14给rax,第一次运行到这里的时候r14这时候就是rsp
  40115b:	b9 07 00 00 00       	mov    $0x7,%ecx // ecx置为7
// 这是一个小循环,循环6次,将我们输入的数,都换成7-x
  401160:	89 ca                	mov    %ecx,%edx // edx置为7
  401162:	2b 10                	sub    (%rax),%edx // 7-rsp指向的值
  401164:	89 10                	mov    %edx,(%rax) // 再将7-rsp指向的值放到rsp指向的位置
  401166:	48 83 c0 04          	add    $0x4,%rax // rax+4
  40116a:	48 39 f0             	cmp    %rsi,%rax // rsi和rax比较
  40116d:	75 f1                	jne    401160  // 如果不一样,则继续循环
// 现在栈顶的6个数字分别是,6 5 4 3 2 1
// 下面的操作是根据栈顶的六个数字,去一个链表里取结点
  40116f:	be 00 00 00 00       	mov    $0x0,%esi // esi置为0
  401174:	eb 21                	jmp    401197  // 跳转

  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx // rdx+8指向的值放到rdx中
  40117a:	83 c0 01             	add    $0x1,%eax // eax+1
  40117d:	39 c8                	cmp    %ecx,%eax // ecx中存放的是我们输入的数字,
  40117f:	75 f5                	jne    401176  // 如果不同的话,就不断循环

  401181:	eb 05                	jmp    401188 
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx

  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2) // 6304544 6304528 6304512 6304496 6304480 0x6032d0
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab 

  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx // 取出栈顶的元素放到ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx // 元素和1相比
  40119d:	7e e4                	jle    401183  // 如果元素小于等于1,则跳转
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax // eax置为1
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx // edx置为0x6032d0
  4011a9:	eb cb                	jmp    401176 

// 上面的操作花了这么大牛劲,就是把6304544 6304528 6304512 6304496 6304480 0x6032d0这六个地址放在了rsp+32 +0 +8 +16 +24 +32 +40的位置 好像刚好放满了这个栈
  4011ab:	48 8b 5c 24 20       	mov    0x20(%rsp),%rbx // 把rbx置为rsp+32指向的值
  4011b0:	48 8d 44 24 28       	lea    0x28(%rsp),%rax // 把rax置为rsp+40,即上述操作栈的第二个元素的位置
  4011b5:	48 8d 74 24 50       	lea    0x50(%rsp),%rsi // 把rsi置为rsp+80,即整个栈的终点
  4011ba:	48 89 d9             	mov    %rbx,%rcx // 把rcx置为第一个值
  // 这里好像是循环着把一个链表给串起来
  4011bd:	48 8b 10             	mov    (%rax),%rdx // 把rdx置为第二个值
  4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx) // 把第二个值放到rcx+8指向的位置
  4011c4:	48 83 c0 08          	add    $0x8,%rax // rax+=1,即指向下一个值
  4011c8:	48 39 f0             	cmp    %rsi,%rax // 比较是否到了终点
  4011cb:	74 05                	je     4011d2  // 到了终点则跳转
  4011cd:	48 89 d1             	mov    %rdx,%rcx // 否则将rcx变成rdx,即变成第二个点
  4011d0:	eb eb                	jmp    4011bd 
 // 将链表最后一个节点的next指针置为0
  4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx) 
  4011d9:	00 
// 链表降序排序 443 477 691 924 168 332
  4011da:	bd 05 00 00 00       	mov    $0x5,%ebp // ebp置为5
  4011df:	48 8b 43 08          	mov    0x8(%rbx),%rax // 此时rbx是第一个结点的地址,因此rax是第二个结点的地址
  4011e3:	8b 00                	mov    (%rax),%eax  // 将eax置为第二个结点
  4011e5:	39 03                	cmp    %eax,(%rbx) // 比较第二个结点和第一个结点的值
  4011e7:	7d 05                	jge    4011ee  // 第一个如果要大于等于第二个,那么就跳过炸弹,否则就爆炸
  4011e9:	e8 4c 02 00 00       	callq  40143a 
  4011ee:	48 8b 5b 08          	mov    0x8(%rbx),%rbx
  4011f2:	83 ed 01             	sub    $0x1,%ebp
  4011f5:	75 e8                	jne    4011df 
  4011f7:	48 83 c4 50          	add    $0x50,%rsp
  4011fb:	5b                   	pop    %rbx
  4011fc:	5d                   	pop    %rbp
  4011fd:	41 5c                	pop    %r12
  4011ff:	41 5d                	pop    %r13
  401201:	41 5e                	pop    %r14
  401203:	c3                   	retq   

你可能感兴趣的:(CSAPP,linux)