CASPP_Lab2 二进制炸弹

文章目录

  • 二进制炸弹
    • 一、拆弹
      • Phase_1
      • Phase_2
      • Phase_3
      • Phase_4
      • Phase_5
      • Phase_6
      • Secret Phase
    • 二、课前预习
      • 知道大家不爱看预习,我把预习放后面
      • 该部分为循环、链表等对应的汇编,帮助熟悉一下,可直接跳过,进入拆弹环节
      • / ∗ 字 符 串 比 较 ∗ / {\color{blue}{/* 字符串比较 */}} //
      • / ∗ 循 环 ∗ / {\color{blue}{/* 循环 */}} //
        • 一层
        • 两层
      • / ∗ 分 支 语 句 ∗ / {\color{blue}{/* 分支语句 */}} //
        • if else
        • switch case
        • go to
      • / ∗ 函 数 调 用 ∗ / {\color{blue}{/* 函数调用 */}} //
      • / ∗ 指 针 ∗ / {\color{blue}{/* 指针 */}} //
      • / ∗ 结 构 体 ∗ / {\color{blue}{/* 结构体 */}} //
      • / ∗ 链 表 ∗ / {\color{blue}{/* 链表 */}} //

二进制炸弹

一、拆弹

Phase_1

首先安装 gdb

sudo apt install gdb

在bomb.c所在文件夹下打开命令行

gdb bomb

在gdb环境下调试
先在phase_1处打断点,然后运行

break phase_1
run

看到提示
CASPP_Lab2 二进制炸弹_第1张图片
此时,我们对答案一无所知,随便输入一个字符串

Phase_1 Come on!

Enter后,看到了程序的汇编代码
CASPP_Lab2 二进制炸弹_第2张图片

[-------------------------------------code-------------------------------------]
   0x4013dd <main+311>:		call   0x401120 <__printf_chk@plt>
   0x4013e2 <main+316>:		mov    edi,0x8
   0x4013e7 <main+321>:		call   0x401150 <exit@plt>
=> 0x4013ec <phase_1>:		sub    rsp,0x8
   0x4013f0 <phase_1+4>:	mov    esi,0x403150
   0x4013f5 <phase_1+9>:	call   0x4018b8 <strings_not_equal>
   0x4013fa <phase_1+14>:	test   eax,eax
   0x4013fc <phase_1+16>:	jne    0x401403 <phase_1+23>
[------------------------------------stack-------------------------------------]

此时看到了,结合test eax,eax可知phase_1是一个字符串的比对,只要输入的字符串和原字符串相等即可.
函数比对需要两个字符串,第一个字符串由我们输入,第二个参数存在esi中,由mov esi,0x403150可知原字符串的存在0x403150中

输入命令查看

x/s 0x403150

请添加图片描述
即phase_1的答案为I am not part of the problem. I am a Republican.
炸弹炸了,退出后重新开启即可

q

请添加图片描述

Phase_2

重新进入

gdb bomb

给 phase_2添加断点

break phase_2
run

中途输入phase_1的答案

CASPP_Lab2 二进制炸弹_第3张图片
回车
请添加图片描述
查看phase_2汇编代码

disas phase_2

CASPP_Lab2 二进制炸弹_第4张图片分解该部分汇编代码:

申请栈空间,读取六个数

Dump of assembler code for function phase_2:
=> 0x000000000040140a <+0>:	push   rbx
   0x000000000040140b <+1>:	sub    rsp,0x20
   0x000000000040140f <+5>:	mov    rsi,rsp
   0x0000000000401412 <+8>:	call   0x4019be <read_six_numbers>

第一个数必须为0,否则爆炸

   0x0000000000401417 <+13>:	cmp    DWORD PTR [rsp],0x0
   0x000000000040141b <+17>:	jne    0x401424 <phase_2+26>
   0x0000000000401424 <+26>:	call   0x40199a <explode_bomb>

第二数必须为1,否则爆炸

   0x000000000040141d <+19>:	cmp    DWORD PTR [rsp+0x4],0x1
   0x0000000000401422 <+24>:	je     0x401429 <phase_2+31>
   0x0000000000401424 <+26>:	call   0x40199a <explode_bomb>

接下来是一个循环,实现的功能是 f[n] = f[n-1] + f[n-2] (n从第三个数开始)
细节如下
rax = rbx-0x1
rcx = rbx-0x1
mov eax,DWORD PTR [rsp+rax4]
add eax,DWORD PTR [rsp+rcx4]

   0x0000000000401429 <+31>:	mov    ebx,0x2
   0x000000000040142e <+36>:	jmp    0x401438 <phase_2+46>
   0x0000000000401430 <+38>:	call   0x40199a <explode_bomb>
   0x0000000000401435 <+43>:	add    ebx,0x1
   0x0000000000401438 <+46>:	cmp    ebx,0x5
   0x000000000040143b <+49>:	jg     0x401458 <phase_2+78>
   0x000000000040143d <+51>:	movsxd rdx,ebx
   0x0000000000401440 <+54>:	lea    ecx,[rbx-0x2]
   0x0000000000401443 <+57>:	movsxd rcx,ecx
   0x0000000000401446 <+60>:	lea    eax,[rbx-0x1]
   0x0000000000401449 <+63>:	cdqe   
   0x000000000040144b <+65>:	mov    eax,DWORD PTR [rsp+rax*4]
   0x000000000040144e <+68>:	add    eax,DWORD PTR [rsp+rcx*4]
   0x0000000000401451 <+71>:	cmp    DWORD PTR [rsp+rdx*4],eax
   0x0000000000401454 <+74>:	je     0x401435 <phase_2+43>
   0x0000000000401456 <+76>:	jmp    0x401430 <phase_2+38>
   0x0000000000401458 <+78>:	add    rsp,0x20
   0x000000000040145c <+82>:	pop    rbx
   0x000000000040145d <+83>:	ret    

由上面的推理可知,答案为0 1 1 2 3 5

Phase_3

同上,重新运行gdb,打断点,运行

CASPP_Lab2 二进制炸弹_第5张图片
查看phase_3汇编代码

disas phase_3
Dump of assembler code for function phase_3:
=> 0x000000000040145e <+0>:		sub    rsp,0x18
   0x0000000000401462 <+4>:		lea    r8,[rsp+0x8]
   0x0000000000401467 <+9>:		lea    rcx,[rsp+0x7]
   0x000000000040146c <+14>:	lea    rdx,[rsp+0xc]
   0x0000000000401471 <+19>:	mov    esi,0x4031ae
   0x0000000000401476 <+24>:	mov    eax,0x0
   0x000000000040147b <+29>:	call   0x401110 <__isoc99_sscanf@plt>
   0x0000000000401480 <+34>:	cmp    eax,0x2
   0x0000000000401483 <+37>:	jle    0x40149b <phase_3+61>
   0x0000000000401485 <+39>:	mov    eax,DWORD PTR [rsp+0xc]
   0x0000000000401489 <+43>:	cmp    eax,0x7
   0x000000000040148c <+46>:	ja     0x40159f <phase_3+321>
   0x0000000000401492 <+52>:	mov    eax,eax
   0x0000000000401494 <+54>:	jmp    QWORD PTR [rax*8+0x4031c0]
   0x000000000040149b <+61>:	call   0x40199a <explode_bomb>
   0x00000000004014a0 <+66>:	jmp    0x401485 <phase_3+39>
   0x00000000004014a2 <+68>:	cmp    DWORD PTR [rsp+0x8],0x201
   0x00000000004014aa <+76>:	jne    0x4014b6 <phase_3+88>
   0x00000000004014ac <+78>:	mov    eax,0x67
   0x00000000004014b1 <+83>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014b6 <+88>:	call   0x40199a <explode_bomb>
   0x00000000004014bb <+93>:	mov    eax,0x67
   0x00000000004014c0 <+98>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014c5 <+103>:	cmp    DWORD PTR [rsp+0x8],0x1bd
   0x00000000004014cd <+111>:	jne    0x4014d9 <phase_3+123>
   0x00000000004014cf <+113>:	mov    eax,0x6a
   0x00000000004014d4 <+118>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014d9 <+123>:	call   0x40199a <explode_bomb>
   0x00000000004014de <+128>:	mov    eax,0x6a
   0x00000000004014e3 <+133>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014e8 <+138>:	cmp    DWORD PTR [rsp+0x8],0x1e8
   0x00000000004014f0 <+146>:	jne    0x4014fc <phase_3+158>
   0x00000000004014f2 <+148>:	mov    eax,0x74
   0x00000000004014f7 <+153>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014fc <+158>:	call   0x40199a <explode_bomb>
   0x0000000000401501 <+163>:	mov    eax,0x74
   0x0000000000401506 <+168>:	jmp    0x4015a9 <phase_3+331>
   0x000000000040150b <+173>:	cmp    DWORD PTR [rsp+0x8],0x167
   0x0000000000401513 <+181>:	jne    0x40151f <phase_3+193>
   0x0000000000401515 <+183>:	mov    eax,0x76
   0x000000000040151a <+188>:	jmp    0x4015a9 <phase_3+331>
   0x000000000040151f <+193>:	call   0x40199a <explode_bomb>
   0x0000000000401524 <+198>:	mov    eax,0x76
   0x0000000000401529 <+203>:	jmp    0x4015a9 <phase_3+331>
   0x000000000040152b <+205>:	cmp    DWORD PTR [rsp+0x8],0x3ac
   0x0000000000401533 <+213>:	jne    0x40153c <phase_3+222>
   0x0000000000401535 <+215>:	mov    eax,0x65
   0x000000000040153a <+220>:	jmp    0x4015a9 <phase_3+331>
   0x000000000040153c <+222>:	call   0x40199a <explode_bomb>
   0x0000000000401541 <+227>:	mov    eax,0x65
   0x0000000000401546 <+232>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401548 <+234>:	cmp    DWORD PTR [rsp+0x8],0x177
   0x0000000000401550 <+242>:	jne    0x401559 <phase_3+251>
   0x0000000000401552 <+244>:	mov    eax,0x71
   0x0000000000401557 <+249>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401559 <+251>:	call   0x40199a <explode_bomb>
   0x000000000040155e <+256>:	mov    eax,0x71
   0x0000000000401563 <+261>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401565 <+263>:	cmp    DWORD PTR [rsp+0x8],0x3bc
   0x000000000040156d <+271>:	jne    0x401576 <phase_3+280>
   0x000000000040156f <+273>:	mov    eax,0x72
   0x0000000000401574 <+278>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401576 <+280>:	call   0x40199a <explode_bomb>
   0x000000000040157b <+285>:	mov    eax,0x72
   0x0000000000401580 <+290>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401582 <+292>:	cmp    DWORD PTR [rsp+0x8],0x334
   0x000000000040158a <+300>:	jne    0x401593 <phase_3+309>
   0x000000000040158c <+302>:	mov    eax,0x75
   0x0000000000401591 <+307>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401593 <+309>:	call   0x40199a <explode_bomb>
   0x0000000000401598 <+314>:	mov    eax,0x75
   0x000000000040159d <+319>:	jmp    0x4015a9 <phase_3+331>
   0x000000000040159f <+321>:	call   0x40199a <explode_bomb>
   0x00000000004015a4 <+326>:	mov    eax,0x68
   0x00000000004015a9 <+331>:	cmp    BYTE PTR [rsp+0x7],al
   0x00000000004015ad <+335>:	jne    0x4015b4 <phase_3+342>
   0x00000000004015af <+337>:	add    rsp,0x18
   0x00000000004015b3 <+341>:	ret    
   0x00000000004015b4 <+342>:	call   0x40199a <explode_bomb>
   0x00000000004015b9 <+347>:	jmp    0x4015af <phase_3+337>
End of assembler dump.

有点长,没事,分解开来,一部分一部分看

第一部分,申请栈空间,数据读入

=> 0x000000000040145e <+0>:		sub    rsp,0x18
   0x0000000000401462 <+4>:		lea    r8,[rsp+0x8]
   0x0000000000401467 <+9>:		lea    rcx,[rsp+0x7]
   0x000000000040146c <+14>:	lea    rdx,[rsp+0xc]
   0x0000000000401471 <+19>:	mov    esi,0x4031ae
   0x0000000000401476 <+24>:	mov    eax,0x0
   0x000000000040147b <+29>:	call   0x401110 <__isoc99_sscanf@plt>

注意到调用了一个函数名为__isoc99_sscanf@plt,实际上它是一个库函数,在RUNOOB.COM查阅相关文档得知,sscanf的声明如下:

C 库函数 
int sscanf(const char *str, const char *format, ...) 
从字符串读取格式化输入。

意思就是根据指定的格式(format)提取字符串s中的内容,返回读取成功的内容个数,其中…就是提取内容存储的地址。
参数的要求很复杂,

下面的实例演示了 sscanf() 函数的用法。

#include 
#include 
#include 

int main()
{
   int day, year;
   char weekday[20], month[20], dtm[100];

   strcpy( dtm, "Saturday March 25 1989" );
   sscanf( dtm, "%s %s %d  %d", weekday, month, &day, &year );

   printf("%s %d, %d = %s\n", month, day, year, weekday );
    
   return(0);
}

让我们编译并运行上面的程序,这将产生以下结果:

March 25, 1989 = Saturday

通过对sscanf的了解,可以知道,需要输入三部分参数

   0x0000000000401462 <+4>:		lea    r8,[rsp+0x8]
   0x0000000000401467 <+9>:		lea    rcx,[rsp+0x7]
   0x000000000040146c <+14>:	lea    rdx,[rsp+0xc]
   0x0000000000401471 <+19>:	mov    esi,0x4031ae
   0x0000000000401476 <+24>:	mov    eax,0x0
   0x000000000040147b <+29>:	call   0x401110 <__isoc99_sscanf@plt>

第一部分参数 $rdi 我们输入的字符串的地址

第二部分参数 $esi 输入字符串的格式

第三部分参数 $rdx $rcx $r8 用来存放内容的地址,从占用栈空间大小可知,一个char两个int

查看输入格式mov esi,0x4031ae

x/s 0x4031ae

查看输入的字符串

x/s $rdi

CASPP_Lab2 二进制炸弹_第6张图片
证明上述分析正确,我们只需要输入%d %c% %d即可

下面这部分表示,输入小于等于2个时爆炸,输入的第一个(栈底)数大于7时,爆炸

   0x0000000000401480 <+34>:	cmp    eax,0x2
   0x0000000000401483 <+37>:	jle    0x40149b <phase_3+61>
   0x0000000000401485 <+39>:	mov    eax,DWORD PTR [rsp+0xc]
   0x0000000000401489 <+43>:	cmp    eax,0x7
   0x000000000040148c <+46>:	ja     0x40159f <phase_3+321>

下面这段代码表示,输入的第一个整数的范围为<7,然后根据第一个整数的值依次进行跳转到不同的case,后面可知case只有8个,该整数的范围为0~7

   0x0000000000401485 <+39>:	mov    eax,DWORD PTR [rsp+0xc]
   0x0000000000401489 <+43>:	cmp    eax,0x7
   0x000000000040148c <+46>:	ja     0x40159f <phase_3+321>
   0x0000000000401492 <+52>:	mov    eax,eax
   0x0000000000401494 <+54>:	jmp    QWORD PTR [rax*8+0x4031c0]

接着是一个典型的switch case 循环分支,数cmp的个数可知,共8个case

   0x00000000004014a2 <+68>:	cmp    DWORD PTR [rsp+0x8],0x201
   0x00000000004014aa <+76>:	jne    0x4014b6 <phase_3+88>
   0x00000000004014ac <+78>:	mov    eax,0x67
   0x00000000004014b1 <+83>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014b6 <+88>:	call   0x40199a <explode_bomb>
   0x00000000004014bb <+93>:	mov    eax,0x67
   0x00000000004014c0 <+98>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014c5 <+103>:	cmp    DWORD PTR [rsp+0x8],0x1bd
   0x00000000004014cd <+111>:	jne    0x4014d9 <phase_3+123>
   0x00000000004014cf <+113>:	mov    eax,0x6a
   0x00000000004014d4 <+118>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014d9 <+123>:	call   0x40199a <explode_bomb>
   0x00000000004014de <+128>:	mov    eax,0x6a
   0x00000000004014e3 <+133>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014e8 <+138>:	cmp    DWORD PTR [rsp+0x8],0x1e8
   0x00000000004014f0 <+146>:	jne    0x4014fc <phase_3+158>
   0x00000000004014f2 <+148>:	mov    eax,0x74
   0x00000000004014f7 <+153>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014fc <+158>:	call   0x40199a <explode_bomb>
   0x0000000000401501 <+163>:	mov    eax,0x74
   0x0000000000401506 <+168>:	jmp    0x4015a9 <phase_3+331>
   0x000000000040150b <+173>:	cmp    DWORD PTR [rsp+0x8],0x167
   0x0000000000401513 <+181>:	jne    0x40151f <phase_3+193>
   0x0000000000401515 <+183>:	mov    eax,0x76
   0x000000000040151a <+188>:	jmp    0x4015a9 <phase_3+331>
   0x000000000040151f <+193>:	call   0x40199a <explode_bomb>
   0x0000000000401524 <+198>:	mov    eax,0x76
   0x0000000000401529 <+203>:	jmp    0x4015a9 <phase_3+331>
   0x000000000040152b <+205>:	cmp    DWORD PTR [rsp+0x8],0x3ac
   0x0000000000401533 <+213>:	jne    0x40153c <phase_3+222>
   0x0000000000401535 <+215>:	mov    eax,0x65
   0x000000000040153a <+220>:	jmp    0x4015a9 <phase_3+331>
   0x000000000040153c <+222>:	call   0x40199a <explode_bomb>
   0x0000000000401541 <+227>:	mov    eax,0x65
   0x0000000000401546 <+232>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401548 <+234>:	cmp    DWORD PTR [rsp+0x8],0x177
   0x0000000000401550 <+242>:	jne    0x401559 <phase_3+251>
   0x0000000000401552 <+244>:	mov    eax,0x71
   0x0000000000401557 <+249>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401559 <+251>:	call   0x40199a <explode_bomb>
   0x000000000040155e <+256>:	mov    eax,0x71
   0x0000000000401563 <+261>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401565 <+263>:	cmp    DWORD PTR [rsp+0x8],0x3bc
   0x000000000040156d <+271>:	jne    0x401576 <phase_3+280>
   0x000000000040156f <+273>:	mov    eax,0x72
   0x0000000000401574 <+278>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401576 <+280>:	call   0x40199a <explode_bomb>
   0x000000000040157b <+285>:	mov    eax,0x72
   0x0000000000401580 <+290>:	jmp    0x4015a9 <phase_3+331>
   0x0000000000401582 <+292>:	cmp    DWORD PTR [rsp+0x8],0x334
   0x000000000040158a <+300>:	jne    0x401593 <phase_3+309>
   0x000000000040158c <+302>:	mov    eax,0x75
   0x0000000000401591 <+307>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004015a9 <+331>:	cmp    BYTE PTR [rsp+0x7],al
   0x00000000004015ad <+335>:	jne    0x4015b4 <phase_3+342>
   0x00000000004015af <+337>:	add    rsp,0x18
   0x00000000004015b3 <+341>:	ret    
   0x00000000004015b4 <+342>:	call   0x40199a <explode_bomb>
   0x00000000004015b9 <+347>:	jmp    0x4015af <phase_3+337>
End of assembler dump.

以其中一个为例

   0x00000000004014a2 <+68>:	cmp    DWORD PTR [rsp+0x8],0x201
   0x00000000004014aa <+76>:	jne    0x4014b6 <phase_3+88>
   0x00000000004014ac <+78>:	mov    eax,0x67
   0x00000000004014b1 <+83>:	jmp    0x4015a9 <phase_3+331>
   0x00000000004014b6 <+88>:	call   0x40199a <explode_bomb>
   0x00000000004014bb <+93>:	mov    eax,0x67
   0x00000000004014c0 <+98>:	jmp    0x4015a9 <phase_3+331>

如果输入的%d %c %d的第二个整数值不为0x201 (十进制下的 513)则寻找下一个case
否则第二个整数是0x201$rax=0x67,跳转到 ,如果输入的字符不等于$al($rax=0x67),则爆炸,否则弹栈结束,该字符为g

综上所述,其中一个可行的答案为 0 g 513
共有8个可行的答案

Phase_4

同上操作
CASPP_Lab2 二进制炸弹_第7张图片
查看phase_4的汇编代码

disas phase_4
gdb-peda$ disas phase_4
Dump of assembler code for function phase_4:
=> 0x00000000004015f8 <+0>:		sub    rsp,0x18
   0x00000000004015fc <+4>:		lea    rcx,[rsp+0x8]
   0x0000000000401601 <+9>:		lea    rdx,[rsp+0xc]
   0x0000000000401606 <+14>:	mov    esi,0x40331f
   0x000000000040160b <+19>:	mov    eax,0x0
   0x0000000000401610 <+24>:	call   0x401110 <__isoc99_sscanf@plt>
   0x0000000000401615 <+29>:	cmp    eax,0x2
   0x0000000000401618 <+32>:	jne    0x401627 <phase_4+47>
   0x000000000040161a <+34>:	mov    eax,DWORD PTR [rsp+0xc]
   0x000000000040161e <+38>:	test   eax,eax
   0x0000000000401620 <+40>:	js     0x401627 <phase_4+47>
   0x0000000000401622 <+42>:	cmp    eax,0xe
   0x0000000000401625 <+45>:	jle    0x40162c <phase_4+52>
   0x0000000000401627 <+47>:	call   0x40199a <explode_bomb>
   0x000000000040162c <+52>:	mov    edx,0xe
   0x0000000000401631 <+57>:	mov    esi,0x0
   0x0000000000401636 <+62>:	mov    edi,DWORD PTR [rsp+0xc]
   0x000000000040163a <+66>:	call   0x4015bb <func4>
   0x000000000040163f <+71>:	cmp    eax,0x2
   0x0000000000401642 <+74>:	jne    0x40164b <phase_4+83>
   0x0000000000401644 <+76>:	cmp    DWORD PTR [rsp+0x8],0x2
   0x0000000000401649 <+81>:	je     0x401650 <phase_4+88>
   0x000000000040164b <+83>:	call   0x40199a <explode_bomb>
   0x0000000000401650 <+88>:	add    rsp,0x18
   0x0000000000401654 <+92>:	ret    
End of assembler dump.

突然来个短的,松了一口气
还是分块看

申请空间,输入参数,输入参数个数要为2

=> 0x00000000004015f8 <+0>:		sub    rsp,0x18
   0x00000000004015fc <+4>:		lea    rcx,[rsp+0x8]
   0x0000000000401601 <+9>:		lea    rdx,[rsp+0xc]
   0x0000000000401606 <+14>:	mov    esi,0x40331f
   0x000000000040160b <+19>:	mov    eax,0x0
   0x0000000000401610 <+24>:	call   0x401110 <__isoc99_sscanf@plt>
   0x0000000000401615 <+29>:	cmp    eax,0x2
   0x0000000000401618 <+32>:	jne    0x401627 <phase_4+47>

这不是sscanf嘛,老熟人了,直接查看输入参数的格式

x/s 0x40331f

请添加图片描述
只需要输入两个整数

输入的第一个数a[0], 要求0<=a[0]<=14

   0x000000000040161a <+34>:	mov    eax,DWORD PTR [rsp+0xc]
   0x000000000040161e <+38>:	test   eax,eax
   0x0000000000401620 <+40>:	js     0x401627 <phase_4+47>
   0x0000000000401622 <+42>:	cmp    eax,0xe
   0x0000000000401625 <+45>:	jle    0x40162c <phase_4+52>
   0x0000000000401627 <+47>:	call   0x40199a <explode_bomb>

下面这部分表示,调用了func4($edi=第一个输入参数, $esi=0x0, $edx=0xe),且该函数返回值要为0x2
并且输入的第二个参数等于0x2

   0x000000000040162c <+52>:	mov    edx,0xe
   0x0000000000401631 <+57>:	mov    esi,0x0
   0x0000000000401636 <+62>:	mov    edi,DWORD PTR [rsp+0xc]
   0x000000000040163a <+66>:	call   0x4015bb <func4>
   0x000000000040163f <+71>:	cmp    eax,0x2
   0x0000000000401642 <+74>:	jne    0x40164b <phase_4+83>
   0x0000000000401644 <+76>:	cmp    DWORD PTR [rsp+0x8],0x2
   0x0000000000401649 <+81>:	je     0x401650 <phase_4+88>
   0x000000000040164b <+83>:	call   0x40199a <explode_bomb>
   0x0000000000401650 <+88>:	add    rsp,0x18
   0x0000000000401654 <+92>:	ret    

下面我们来看的细节

func4($edi=第一个输入参数, $esi=0x0, $edx=0xe)

disas func4
Dump of assembler code for function func4:
   0x00000000004015bb <+0>:		sub    rsp,0x8
   0x00000000004015bf <+4>:		mov    ecx,edx
   0x00000000004015c1 <+6>:		sub    ecx,esi
   0x00000000004015c3 <+8>:		mov    eax,ecx
   0x00000000004015c5 <+10>:	shr    eax,0x1f
   0x00000000004015c8 <+13>:	add    eax,ecx
   0x00000000004015ca <+15>:	sar    eax,1
   0x00000000004015cc <+17>:	add    eax,esi
   0x00000000004015ce <+19>:	cmp    eax,edi
   0x00000000004015d0 <+21>:	jg     0x4015de <func4+35>
   0x00000000004015d2 <+23>:	jl     0x4015ea <func4+47>
   0x00000000004015d4 <+25>:	mov    eax,0x0
   0x00000000004015d9 <+30>:	add    rsp,0x8
   0x00000000004015dd <+34>:	ret    
   0x00000000004015de <+35>:	lea    edx,[rax-0x1]
   0x00000000004015e1 <+38>:	call   0x4015bb <func4>
   0x00000000004015e6 <+43>:	add    eax,eax
   0x00000000004015e8 <+45>:	jmp    0x4015d9 <func4+30>
   0x00000000004015ea <+47>:	lea    esi,[rax+0x1]
   0x00000000004015ed <+50>:	call   0x4015bb <func4>
   0x00000000004015f2 <+55>:	lea    eax,[rax+rax*1+0x1]
   0x00000000004015f6 <+59>:	jmp    0x4015d9 <func4+30>
End of assembler dump.

拆开看, func4的第一部分是对输入的三个参数进行操作,不妨令三个参数为x, y, z
func4(int x, int y, int z)

   0x00000000004015bb <+0>:		sub    rsp,0x8
   0x00000000004015bf <+4>:		mov    ecx,edx
   0x00000000004015c1 <+6>:		sub    ecx,esi
   0x00000000004015c3 <+8>:		mov    eax,ecx
   0x00000000004015c5 <+10>:	shr    eax,0x1f
   0x00000000004015c8 <+13>:	add    eax,ecx
   0x00000000004015ca <+15>:	sar    eax,1
   0x00000000004015cc <+17>:	add    eax,esi

此部分对应的C语言代码为

int temp = z - y;
temp = (z - y) >> 31 + temp;
temp >>= 1;
temp += y;

上面的操作相当于求mid = (x + y) / 2, 但是由于 x + y可能会溢出,于是作出了一步优化,防止溢出
你可能会困惑为什么要(z - y) >> 31int除过符号位一共31位有效数字,符号位不动,移动之后+0 or -0,相当于啥也没加,嘿嘿,恶心你
上面的操作可写为如下形式

int mid = y + (z - y) / 2;

func4的第二部分

   0x00000000004015ce <+19>:	cmp    eax,edi
   0x00000000004015d0 <+21>:	jg     0x4015de <func4+35>
   0x00000000004015d2 <+23>:	jl     0x4015ea <func4+47>
   0x00000000004015d4 <+25>:	mov    eax,0x0
   0x00000000004015d9 <+30>:	add    rsp,0x8
   0x00000000004015dd <+34>:	ret    
   0x00000000004015de <+35>:	lea    edx,[rax-0x1]
   0x00000000004015e1 <+38>:	call   0x4015bb <func4>
   0x00000000004015e6 <+43>:	add    eax,eax
   0x00000000004015e8 <+45>:	jmp    0x4015d9 <func4+30>
   0x00000000004015ea <+47>:	lea    esi,[rax+0x1]
   0x00000000004015ed <+50>:	call   0x4015bb <func4>
   0x00000000004015f2 <+55>:	lea    eax,[rax+rax*1+0x1]
   0x00000000004015f6 <+59>:	jmp    0x4015d9 <func4+30>

此部分用C语言表示为

if (x > mid) {
		func4(x, mid + 1, z);
		edx = edx * 2;
}
else if (x < mid) {
		func4(x, y, mid - 1);
		edx = edx * 2 + 1;
}
else {
		edx = 0;
		return edx;
}

好家伙,真是难识汇编真面目,这不就是可爱的二分查找嘛
此处注意找到x时,edx=0,从二叉树右枝返回时edx = edx * 2 + 1;,从二叉树左子树返回时edx = edx * 2

再联系我们需要的返回的结果为0x2,那么就需要

  1. edx: 0->1->2,即找到后edx = 0,再从右子树返回edx = edx*2 + 1 = 1,再从左子树返回edx = edx*2 = 2
  2. 或者edx: 0->0->1->2,即找到后edx = 0,先从左子树返回edx = 0 * 2 = 0,再从右子树返回edx = edx*2 + 1 = 1,再从左子树返回edx = edx*2 = 2

由于该题的查找范围是0~14,所以二叉树的高度只有4,最多往上回溯3次,即
第一次向左找,查找范围为 0~7
第二次向右找,查找范围为3~7
这样回溯前两次查找,即可实现最终edx = 2

  1. 第三次找到了,即该参数为5
  2. 第三次没找到,只能到左子树才能使回溯的结果正确,查找范围3~5
    第四次找到,该参数为4

附上一颗二叉树,及画流程图的方法

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14

两个答案的查找方式如下
CASPP_Lab2 二进制炸弹_第8张图片
CASPP_Lab2 二进制炸弹_第9张图片

综上所述,phase_4的答案为5 2或者4 2

Phase_5

继续打断点,查看phase_5的汇编代码

gdb-peda$ disas phase_5
Dump of assembler code for function phase_5:
=> 0x0000000000401655 <+0>:		push   rbx
   0x0000000000401656 <+1>:		sub    rsp,0x10
   0x000000000040165a <+5>:		mov    rbx,rdi
   0x000000000040165d <+8>:		call   0x4018a4 <string_length>
   0x0000000000401662 <+13>:	cmp    eax,0x6
   0x0000000000401665 <+16>:	jne    0x40168b <phase_5+54>
   0x0000000000401667 <+18>:	mov    eax,0x0
   0x000000000040166c <+23>:	cmp    eax,0x5
   0x000000000040166f <+26>:	jg     0x401692 <phase_5+61>
   0x0000000000401671 <+28>:	movsxd rcx,eax
   0x0000000000401674 <+31>:	movzx  edx,BYTE PTR [rbx+rcx*1]
   0x0000000000401678 <+35>:	and    edx,0xf
   0x000000000040167b <+38>:	movzx  edx,BYTE PTR [rdx+0x403200]
   0x0000000000401682 <+45>:	mov    BYTE PTR [rsp+rcx*1+0x9],dl
   0x0000000000401686 <+49>:	add    eax,0x1
   0x0000000000401689 <+52>:	jmp    0x40166c <phase_5+23>
   0x000000000040168b <+54>:	call   0x40199a <explode_bomb>
   0x0000000000401690 <+59>:	jmp    0x401667 <phase_5+18>
   0x0000000000401692 <+61>:	mov    BYTE PTR [rsp+0xf],0x0
   0x0000000000401697 <+66>:	mov    esi,0x4031b7
   0x000000000040169c <+71>:	lea    rdi,[rsp+0x9]
   0x00000000004016a1 <+76>:	call   0x4018b8 <strings_not_equal>
   0x00000000004016a6 <+81>:	test   eax,eax
   0x00000000004016a8 <+83>:	jne    0x4016b0 <phase_5+91>
   0x00000000004016aa <+85>:	add    rsp,0x10
   0x00000000004016ae <+89>:	pop    rbx
   0x00000000004016af <+90>:	ret    
   0x00000000004016b0 <+91>:	call   0x40199a <explode_bomb>
   0x00000000004016b5 <+96>:	jmp    0x4016aa <phase_5+85>
End of assembler dump.

第一部分,输入一个长度为6的字符串

=> 0x0000000000401655 <+0>:	push   rbx
   0x0000000000401656 <+1>:	sub    rsp,0x10
   0x000000000040165a <+5>:	mov    rbx,rdi
   0x000000000040165d <+8>:	call   0x4018a4 <string_length>
   0x0000000000401662 <+13>:	cmp    eax,0x6
   0x0000000000401665 <+16>:	jne    0x40168b <phase_5+54>

第二部分,是一个典型的循环结构,遍历字符数组str[0]~str[5],进行判断

   0x0000000000401667 <+18>:	mov    eax,0x0
   0x000000000040166c <+23>:	cmp    eax,0x5
   0x000000000040166f <+26>:	jg     0x401692 <phase_5+61>
   0x0000000000401671 <+28>:	movsxd rcx,eax
   0x0000000000401674 <+31>:	movzx  edx,BYTE PTR [rbx+rcx*1]
   0x0000000000401678 <+35>:	and    edx,0xf
   0x000000000040167b <+38>:	movzx  edx,BYTE PTR [rdx+0x403200]
   0x0000000000401682 <+45>:	mov    BYTE PTR [rsp+rcx*1+0x9],dl
   0x0000000000401686 <+49>:	add    eax,0x1
   0x0000000000401689 <+52>:	jmp    0x40166c <phase_5+23>
   0x000000000040168b <+54>:	call   0x40199a <explode_bomb>
   0x0000000000401690 <+59>:	jmp    0x401667 <phase_5+18>
   0x0000000000401692 <+61>:	mov    BYTE PTR [rsp+0xf],0x0

上面这段代码进行的操作是

遍历字符数组movzx edx,BYTE PTR [rbx+rcx*1]
取出其ASCII码的低四位and edx,0xf,不妨设为m
利用m作为索引,取出在地址0x403200下存的对应字符movzx edx,BYTE PTR [rdx+0x403200]
请添加图片描述设计个字符串还不忘嘲讽,非拆不可
将对应的6个字符取出,后面要用mov BYTE PTR [rsp+rcx*1+0x9],dl

第三部分,将上面得到的字符串与$esi中的字符串进行比较,相同即可

   0x0000000000401692 <+61>:	mov    BYTE PTR [rsp+0xf],0x0
   0x0000000000401697 <+66>:	mov    esi,0x4031b7
   0x000000000040169c <+71>:	lea    rdi,[rsp+0x9]
   0x00000000004016a1 <+76>:	call   0x4018b8 <strings_not_equal>
   0x00000000004016a6 <+81>:	test   eax,eax
   0x00000000004016a8 <+83>:	jne    0x4016b0 <phase_5+91>
   0x00000000004016aa <+85>:	add    rsp,0x10
   0x00000000004016ae <+89>:	pop    rbx
   0x00000000004016af <+90>:	ret    

首先查看$esi中的字符串

x/s 0x4031b7

请添加图片描述
即在maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?中选出flyers即可
即输入字符低四位对应的十进制数m应为9 15 14 5 6 7
即对应的ASCII码的低四位为1001 1111 1110 0101 0110 0111
查表可知

str[0]&0xf == 1001可以是) y Y i I

str[1]&0xf == 1111可以是/ ? _ o O

str[2]&0xf == 1110可以是. > N ^ n ~

str[3]&0xf == 0101可以是% 5 E e U u

str[4]&0xf == 0110可以是& 6 F f V v

str[5]&0xf == 0111可以是’ 7 G g W w

总上所述,phase_5的答案可为上述6个字符的任意组合,ionefg是一个答案

Phase_6

`Phase_6`有点复杂,先说一下整体思路:

最初存在一个链表,而且链表中有乱序的数值,按照我们输入的6个数字对链表进行重新排序,要求排序之后的链表中的数值按降序(从大到小)排列

再次运行,查看汇编代码

gdb-peda$ disas phase_6
Dump of assembler code for function phase_6:
=> 0x00000000004016b7 <+0>:		push   r12
   0x00000000004016b9 <+2>:		push   rbp
   0x00000000004016ba <+3>:		push   rbx
   0x00000000004016bb <+4>:		sub    rsp,0x50
   0x00000000004016bf <+8>:		lea    rsi,[rsp+0x30]
   0x00000000004016c4 <+13>:	call   0x4019be <read_six_numbers>
   0x00000000004016c9 <+18>:	mov    ebp,0x0
   0x00000000004016ce <+23>:	jmp    0x4016f9 <phase_6+66>
   0x00000000004016d0 <+25>:	call   0x40199a <explode_bomb>
   0x00000000004016d5 <+30>:	jmp    0x40170d <phase_6+86>
   0x00000000004016d7 <+32>:	add    ebx,0x1
   0x00000000004016da <+35>:	cmp    ebx,0x5
   0x00000000004016dd <+38>:	jg     0x4016f6 <phase_6+63>
   0x00000000004016df <+40>:	movsxd rax,ebp
   0x00000000004016e2 <+43>:	movsxd rdx,ebx
   0x00000000004016e5 <+46>:	mov    edi,DWORD PTR [rsp+rdx*4+0x30]
   0x00000000004016e9 <+50>:	cmp    DWORD PTR [rsp+rax*4+0x30],edi
   0x00000000004016ed <+54>:	jne    0x4016d7 <phase_6+32>
   0x00000000004016ef <+56>:	call   0x40199a <explode_bomb>
   0x00000000004016f4 <+61>:	jmp    0x4016d7 <phase_6+32>
   0x00000000004016f6 <+63>:	mov    ebp,r12d
   0x00000000004016f9 <+66>:	cmp    ebp,0x5
   0x00000000004016fc <+69>:	jg     0x401716 <phase_6+95>
   0x00000000004016fe <+71>:	movsxd rax,ebp
   0x0000000000401701 <+74>:	mov    eax,DWORD PTR [rsp+rax*4+0x30]
   0x0000000000401705 <+78>:	sub    eax,0x1
   0x0000000000401708 <+81>:	cmp    eax,0x5
   0x000000000040170b <+84>:	ja     0x4016d0 <phase_6+25>
   0x000000000040170d <+86>:	lea    r12d,[rbp+0x1]
   0x0000000000401711 <+90>:	mov    ebx,r12d
   0x0000000000401714 <+93>:	jmp    0x4016da <phase_6+35>
   0x0000000000401716 <+95>:	mov    esi,0x0
   0x000000000040171b <+100>:	jmp    0x401724 <phase_6+109>
   0x000000000040171d <+102>:	mov    QWORD PTR [rsp+rcx*8],rdx
   0x0000000000401721 <+106>:	add    esi,0x1
   0x0000000000401724 <+109>:	cmp    esi,0x5
   0x0000000000401727 <+112>:	jg     0x401745 <phase_6+142>
   0x0000000000401729 <+114>:	mov    eax,0x1
   0x000000000040172e <+119>:	mov    edx,0x4052d0
   0x0000000000401733 <+124>:	movsxd rcx,esi
   0x0000000000401736 <+127>:	cmp    DWORD PTR [rsp+rcx*4+0x30],eax
   0x000000000040173a <+131>:	jle    0x40171d <phase_6+102>
   0x000000000040173c <+133>:	mov    rdx,QWORD PTR [rdx+0x8]
   0x0000000000401740 <+137>:	add    eax,0x1
   0x0000000000401743 <+140>:	jmp    0x401733 <phase_6+124>
   0x0000000000401745 <+142>:	mov    rbx,QWORD PTR [rsp]
   0x0000000000401749 <+146>:	mov    rcx,rbx
   0x000000000040174c <+149>:	mov    eax,0x1
   0x0000000000401751 <+154>:	jmp    0x401764 <phase_6+173>
   0x0000000000401753 <+156>:	movsxd rdx,eax
   0x0000000000401756 <+159>:	mov    rdx,QWORD PTR [rsp+rdx*8]
   0x000000000040175a <+163>:	mov    QWORD PTR [rcx+0x8],rdx
   0x000000000040175e <+167>:	add    eax,0x1
   0x0000000000401761 <+170>:	mov    rcx,rdx
   0x0000000000401764 <+173>:	cmp    eax,0x5
   0x0000000000401767 <+176>:	jle    0x401753 <phase_6+156>
   0x0000000000401769 <+178>:	mov    QWORD PTR [rcx+0x8],0x0
   0x0000000000401771 <+186>:	mov    ebp,0x0
   0x0000000000401776 <+191>:	jmp    0x40177f <phase_6+200>
   0x0000000000401778 <+193>:	mov    rbx,QWORD PTR [rbx+0x8]
   0x000000000040177c <+197>:	add    ebp,0x1
   0x000000000040177f <+200>:	cmp    ebp,0x4
   0x0000000000401782 <+203>:	jg     0x401795 <phase_6+222>
   0x0000000000401784 <+205>:	mov    rax,QWORD PTR [rbx+0x8]
   0x0000000000401788 <+209>:	mov    eax,DWORD PTR [rax]
   0x000000000040178a <+211>:	cmp    DWORD PTR [rbx],eax
   0x000000000040178c <+213>:	jge    0x401778 <phase_6+193>
   0x000000000040178e <+215>:	call   0x40199a <explode_bomb>
   0x0000000000401793 <+220>:	jmp    0x401778 <phase_6+193>
   0x0000000000401795 <+222>:	add    rsp,0x50
   0x0000000000401799 <+226>:	pop    rbx
   0x000000000040179a <+227>:	pop    rbp
   0x000000000040179b <+228>:	pop    r12
   0x000000000040179d <+230>:	ret   
    
End of assembler dump.

好家伙,令人窒息的长度,加油,逐个击破

第一部分,先读了6个数

=> 0x00000000004016b7 <+0>:		push   r12
   0x00000000004016b9 <+2>:		push   rbp
   0x00000000004016ba <+3>:		push   rbx
   0x00000000004016bb <+4>:		sub    rsp,0x50
   0x00000000004016bf <+8>:		lea    rsi,[rsp+0x30]
   0x00000000004016c4 <+13>:	call   0x4019be <read_six_numbers>

第二部分,一个二重循环

   0x00000000004016c9 <+18>:	mov    ebp,0x0
   0x00000000004016ce <+23>:	jmp    0x4016f9 <phase_6+66>
   0x00000000004016d0 <+25>:	call   0x40199a <explode_bomb>
   0x00000000004016d5 <+30>:	jmp    0x40170d <phase_6+86>
   0x00000000004016d7 <+32>:	add    ebx,0x1
   0x00000000004016da <+35>:	cmp    ebx,0x5
   0x00000000004016dd <+38>:	jg     0x4016f6 <phase_6+63>
   0x00000000004016df <+40>:	movsxd rax,ebp
   0x00000000004016e2 <+43>:	movsxd rdx,ebx
   0x00000000004016e5 <+46>:	mov    edi,DWORD PTR [rsp+rdx*4+0x30]
   0x00000000004016e9 <+50>:	cmp    DWORD PTR [rsp+rax*4+0x30],edi
   0x00000000004016ed <+54>:	jne    0x4016d7 <phase_6+32>
   0x00000000004016ef <+56>:	call   0x40199a <explode_bomb>
   0x00000000004016f4 <+61>:	jmp    0x4016d7 <phase_6+32>
   0x00000000004016f6 <+63>:	mov    ebp,r12d
   0x00000000004016f9 <+66>:	cmp    ebp,0x5
   0x00000000004016fc <+69>:	jg     0x401716 <phase_6+95>
   0x00000000004016fe <+71>:	movsxd rax,ebp
   0x0000000000401701 <+74>:	mov    eax,DWORD PTR [rsp+rax*4+0x30]
   0x0000000000401705 <+78>:	sub    eax,0x1
   0x0000000000401708 <+81>:	cmp    eax,0x5
   0x000000000040170b <+84>:	ja     0x4016d0 <phase_6+25>
   0x000000000040170d <+86>:	lea    r12d,[rbp+0x1]
   0x0000000000401711 <+90>:	mov    ebx,r12d
   0x0000000000401714 <+93>:	jmp    0x4016da <phase_6+35>
   0x0000000000401716 <+95>:	mov    esi,0x0

该二重循环实现了两个功能,我们分开来看
1. 输入的6个数都不大于5

   0x00000000004016fe <+71>:	movsxd rax,ebp
   0x0000000000401701 <+74>:	mov    eax,DWORD PTR [rsp+rax*4+0x30]
   0x0000000000401705 <+78>:	sub    eax,0x1
   0x0000000000401708 <+81>:	cmp    eax,0x5
   0x000000000040170b <+84>:	ja     0x4016d0 <phase_6+25>

2. 输入的6个数各不相同

   0x00000000004016d7 <+32>:	add    ebx,0x1
   0x00000000004016da <+35>:	cmp    ebx,0x5
   0x00000000004016dd <+38>:	jg     0x4016f6 <phase_6+63>
   0x00000000004016df <+40>:	movsxd rax,ebp
   0x00000000004016e2 <+43>:	movsxd rdx,ebx
   0x00000000004016e5 <+46>:	mov    edi,DWORD PTR [rsp+rdx*4+0x30]
   0x00000000004016e9 <+50>:	cmp    DWORD PTR [rsp+rax*4+0x30],edi
   0x00000000004016ed <+54>:	jne    0x4016d7 <phase_6+32>
   0x00000000004016ef <+56>:	call   0x40199a <explode_bomb>
上面的汇编相当于以下C代码
for (int i = 0; i <= 5; i++) {
		for (int j = i + 1; j <= 5; j++) {
				if (num[i] == num[j]) explode_bomb();
		}
}

第三部分,将输入的6个数字放入链表节点中,按照降序重组链表

又一个二重循环,根据输入的6个整数,将对应的链表节点的地址存储在新数组order[]

   0x0000000000401716 <+95>:	mov    esi,0x0
   0x000000000040171b <+100>:	jmp    0x401724 <phase_6+109>
   0x000000000040171d <+102>:	mov    QWORD PTR [rsp+rcx*8],rdx
   0x0000000000401721 <+106>:	add    esi,0x1
   0x0000000000401724 <+109>:	cmp    esi,0x5
   0x0000000000401727 <+112>:	jg     0x401745 <phase_6+142>
   0x0000000000401729 <+114>:	mov    eax,0x1
   0x000000000040172e <+119>:	mov    edx,0x4052d0
   0x0000000000401733 <+124>:	movsxd rcx,esi
   0x0000000000401736 <+127>:	cmp    DWORD PTR [rsp+rcx*4+0x30],eax
   0x000000000040173a <+131>:	jle    0x40171d <phase_6+102>
   0x000000000040173c <+133>:	mov    rdx,QWORD PTR [rdx+0x8]
   0x0000000000401740 <+137>:	add    eax,0x1
   0x0000000000401743 <+140>:	jmp    0x401733 <phase_6+124>
   0x0000000000401745 <+142>:	mov    rbx,QWORD PTR [rsp]

查看0x4052d0,可以发现这是一个由6个节点链接而成的链表

x/12xg 0x4052d0

CASPP_Lab2 二进制炸弹_第10张图片
相当于以下C代码

for (int i = 0; i <= 5; i++) {
		node *p = head;
		for (int count = 1; count <= 5; count++, p = p->next) {
				if (count == arr[i]) {	// arr[]为输入数组
						order[i] = p;	
				}
		}
}

一重循环,将链表按照order[]重组,重组之后的链表中的值按照降序排列

   0x0000000000401745 <+142>:	mov    rbx,QWORD PTR [rsp]
   0x0000000000401749 <+146>:	mov    rcx,rbx
   0x000000000040174c <+149>:	mov    eax,0x1
   0x0000000000401751 <+154>:	jmp    0x401764 <phase_6+173>
   0x0000000000401753 <+156>:	movsxd rdx,eax
   0x0000000000401756 <+159>:	mov    rdx,QWORD PTR [rsp+rdx*8]
   0x000000000040175a <+163>:	mov    QWORD PTR [rcx+0x8],rdx
   0x000000000040175e <+167>:	add    eax,0x1
   0x0000000000401761 <+170>:	mov    rcx,rdx
   0x0000000000401764 <+173>:	cmp    eax,0x5
   0x0000000000401767 <+176>:	jle    0x401753 <phase_6+156>

第四部分,判断,如果链表中的数字不是降序的,则炸弹爆炸

   0x0000000000401769 <+178>:	mov    QWORD PTR [rcx+0x8],0x0
   0x0000000000401771 <+186>:	mov    ebp,0x0
   0x0000000000401776 <+191>:	jmp    0x40177f <phase_6+200>
   0x0000000000401778 <+193>:	mov    rbx,QWORD PTR [rbx+0x8]
   0x000000000040177c <+197>:	add    ebp,0x1
   0x000000000040177f <+200>:	cmp    ebp,0x4
   0x0000000000401782 <+203>:	jg     0x401795 <phase_6+222>
   0x0000000000401784 <+205>:	mov    rax,QWORD PTR [rbx+0x8]
   0x0000000000401788 <+209>:	mov    eax,DWORD PTR [rax]
   0x000000000040178a <+211>:	cmp    DWORD PTR [rbx],eax
   0x000000000040178c <+213>:	jge    0x401778 <phase_6+193>
   0x000000000040178e <+215>:	call   0x40199a <explode_bomb>
   0x0000000000401793 <+220>:	jmp    0x401778 <phase_6+193>
   0x0000000000401795 <+222>:	add    rsp,0x50
   0x0000000000401799 <+226>:	pop    rbx
   0x000000000040179a <+227>:	pop    rbp
   0x000000000040179b <+228>:	pop    r12
   0x000000000040179d <+230>:	ret   

在看一下原来链表中数值的排列情况

gdb-peda$ x/12xg 0x4052d0
0x4052d0 <node1>:	0x0000000100000062	0x00000000004052e0
0x4052e0 <node2>:	0x0000000200000350	0x00000000004052f0
0x4052f0 <node3>:	0x0000000300000379	0x0000000000405300
0x405300 <node4>:	0x0000000400000230	0x0000000000405310
0x405310 <node5>:	0x00000005000001fd	0x0000000000405320
0x405320 <node6>:	0x00000006000000da	0x0000000000000000
<node>: val 	next

对其进行排序,从大到小

node1: 		0x62		order:6
node2:		0x350		order:2
node3: 		0x379		order:1
node4:		0x230		order:3
node5: 		0x1fd		order:4
node6:		0xda		order:5

总上所述,phase_6的答案为3 2 4 5 6 1

Secret Phase

在bomb.c的最后有一句提示,说明还有隐藏关卡

    /* Wow, they got it!  But isn't something... missing?  Perhaps
     * something they overlooked?  Mua ha ha ha ha! */

是的,这个secret_phase藏在函数phase_defused
查看phase_defused

disas phase_defused
gdb-peda$ disas phase_defused
Dump of assembler code for function phase_defused:
   0x0000000000401b2b <+0>:		cmp    DWORD PTR [rip+0x3c3a],0x6        # 0x40576c 
   0x0000000000401b32 <+7>:		je     0x401b35 <phase_defused+10>
   0x0000000000401b34 <+9>:		ret    
   0x0000000000401b35 <+10>:	sub    rsp,0x68
   0x0000000000401b39 <+14>:	lea    r8,[rsp+0x10]
   0x0000000000401b3e <+19>:	lea    rcx,[rsp+0x8]
   0x0000000000401b43 <+24>:	lea    rdx,[rsp+0xc]
   0x0000000000401b48 <+29>:	mov    esi,0x403369
   0x0000000000401b4d <+34>:	mov    edi,0x405870
   0x0000000000401b52 <+39>:	mov    eax,0x0
   0x0000000000401b57 <+44>:	call   0x401110 <__isoc99_sscanf@plt>
   0x0000000000401b5c <+49>:	cmp    eax,0x3
   0x0000000000401b5f <+52>:	je     0x401b70 <phase_defused+69>
   0x0000000000401b61 <+54>:	mov    edi,0x4032a8
   0x0000000000401b66 <+59>:	call   0x401060 <puts@plt>
   0x0000000000401b6b <+64>:	add    rsp,0x68
   0x0000000000401b6f <+68>:	ret    
   0x0000000000401b70 <+69>:	mov    esi,0x403372
   0x0000000000401b75 <+74>:	lea    rdi,[rsp+0x10]
   0x0000000000401b7a <+79>:	call   0x4018b8 <strings_not_equal>
   0x0000000000401b7f <+84>:	test   eax,eax
   0x0000000000401b81 <+86>:	jne    0x401b61 <phase_defused+54>
   0x0000000000401b83 <+88>:	mov    edi,0x403248
   0x0000000000401b88 <+93>:	call   0x401060 <puts@plt>
   0x0000000000401b8d <+98>:	mov    edi,0x403270
   0x0000000000401b92 <+103>:	call   0x401060 <puts@plt>
   0x0000000000401b97 <+108>:	mov    eax,0x0
   0x0000000000401b9c <+113>:	call   0x4017db <secret_phase>
   0x0000000000401ba1 <+118>:	jmp    0x401b61 <phase_defused+54>
End of assembler dump.

该部分汇编表示,需要用sscanf()读入,并利用函数与0x403372中的字符串作比较,相同才能触发隐藏关卡

那么问题来了,在那里输入字符串呢?(入口呢?)

首先查看sscanf()的读入格式

x/s 0x403369

请添加图片描述
由此我们猜测,该字符串是在读入两个整数之后读取的
回忆之前几个关卡,只有phase_3phase_4用到了sscanf(),而且其读入格式分别为

phase_3	:	%d	%c	%d
phase_4	:	%d	%d

那么,触发secret_phase的字符串就应该是在phase_4读入两个整数之后读入的
查看得知,需要读入的字符串是

   0x0000000000401b70 <+69>:	mov    esi,0x403372
   0x0000000000401b75 <+74>:	lea    rdi,[rsp+0x10]
   0x0000000000401b7a <+79>:	call   0x4018b8 <strings_not_equal>
gdb-peda$ x/s 0x403372
0x403372:	"DrEvil"

于是我们在phase_4读入两个整数之后加上DrEvil,即可进入secret_phase

下面我们来看secret_phase

disas secret_phase
gdb-peda$ disas secret_phase
Dump of assembler code for function secret_phase:
   0x00000000004017db <+0>:		push   rbx
   0x00000000004017dc <+1>:		call   0x4019fd <read_line>
   0x00000000004017e1 <+6>:		mov    rdi,rax
   0x00000000004017e4 <+9>:		call   0x401140 <atoi@plt>
   0x00000000004017e9 <+14>:	mov    ebx,eax
   0x00000000004017eb <+16>:	lea    eax,[rax-0x1]
   0x00000000004017ee <+19>:	cmp    eax,0x3e8
   0x00000000004017f3 <+24>:	ja     0x401817 <secret_phase+60>
   0x00000000004017f5 <+26>:	mov    esi,ebx
   0x00000000004017f7 <+28>:	mov    edi,0x4050f0
   0x00000000004017fc <+33>:	call   0x40179e <fun7>
   0x0000000000401801 <+38>:	cmp    eax,0x5
   0x0000000000401804 <+41>:	jne    0x40181e <secret_phase+67>
   0x0000000000401806 <+43>:	mov    edi,0x403188
   0x000000000040180b <+48>:	call   0x401060 <puts@plt>
   0x0000000000401810 <+53>:	call   0x401b2b <phase_defused>
   0x0000000000401815 <+58>:	pop    rbx
   0x0000000000401816 <+59>:	ret    
   0x0000000000401817 <+60>:	call   0x40199a <explode_bomb>
   0x000000000040181c <+65>:	jmp    0x4017f5 <secret_phase+26>
   0x000000000040181e <+67>:	call   0x40199a <explode_bomb>
   0x0000000000401823 <+72>:	jmp    0x401806 <secret_phase+43>
End of assembler dump.

首先需要我们输入一个参数,该参数要小于等于0x3e8
下面代码说明,在调用fun7之前,准备了两个参数,$esi是我们输入的一个参数,$edi是一个地址0x4050f0
且fun7返回的结果要等于0x5

   0x00000000004017f5 <+26>:	mov    esi,ebx
   0x00000000004017f7 <+28>:	mov    edi,0x4050f0
   0x00000000004017fc <+33>:	call   0x40179e <fun7>
   0x0000000000401801 <+38>:	cmp    eax,0x5
   0x0000000000401804 <+41>:	jne    0x40181e <secret_phase+67>

查看0x4050f0地址下的内容

x/60xg 0x4050f0
gdb-peda$ x/60xg 0x4050f0
0x4050f0 <n1>:	0x0000000000000024	0x0000000000405110
0x405100 <n1+16>:	0x0000000000405130	0x0000000000000000
0x405110 <n21>:	0x0000000000000008	0x0000000000405190
0x405120 <n21+16>:	0x0000000000405150	0x0000000000000000
0x405130 <n22>:	0x0000000000000032	0x0000000000405170
0x405140 <n22+16>:	0x00000000004051b0	0x0000000000000000
0x405150 <n32>:	0x0000000000000016	0x0000000000405270
0x405160 <n32+16>:	0x0000000000405230	0x0000000000000000
0x405170 <n33>:	0x000000000000002d	0x00000000004051d0
0x405180 <n33+16>:	0x0000000000405290	0x0000000000000000
0x405190 <n31>:	0x0000000000000006	0x00000000004051f0
0x4051a0 <n31+16>:	0x0000000000405250	0x0000000000000000
0x4051b0 <n34>:	0x000000000000006b	0x0000000000405210
0x4051c0 <n34+16>:	0x00000000004052b0	0x0000000000000000
0x4051d0 <n45>:	0x0000000000000028	0x0000000000000000
0x4051e0 <n45+16>:	0x0000000000000000	0x0000000000000000
0x4051f0 <n41>:	0x0000000000000001	0x0000000000000000
0x405200 <n41+16>:	0x0000000000000000	0x0000000000000000
0x405210 <n47>:	0x0000000000000063	0x0000000000000000
0x405220 <n47+16>:	0x0000000000000000	0x0000000000000000
0x405230 <n44>:	0x0000000000000023	0x0000000000000000
0x405240 <n44+16>:	0x0000000000000000	0x0000000000000000
0x405250 <n42>:	0x0000000000000007	0x0000000000000000
0x405260 <n42+16>:	0x0000000000000000	0x0000000000000000
0x405270 <n43>:	0x0000000000000014	0x0000000000000000
0x405280 <n43+16>:	0x0000000000000000	0x0000000000000000
0x405290 <n46>:	0x000000000000002f	0x0000000000000000
0x4052a0 <n46+16>:	0x0000000000000000	0x0000000000000000
0x4052b0 <n48>:	0x00000000000003e9	0x0000000000000000
0x4052c0 <n48+16>:	0x0000000000000000	0x0000000000000000

有了phase_6的经验,可以看出这是一棵二叉树

n1=0x24
n21=0x8
n22=0x32
n31=0x6
n32=0x16
n33=0x2d
n34=0x6b
n41=0x1
n42=0x7
n43=0x14
n44=0x23
n45=0x28
n46=0x2f
n47=0x63
n48=0x3e9

从二叉树可以看出,左儿子小于父节点,右儿子大于父节点,又是一棵二叉查找树,于是可以猜测fun7的功能可能又是二分查找

下面查看fun7()

disas fun7
gdb-peda$ disas fun7
Dump of assembler code for function fun7:
   0x000000000040179e <+0>:		test   rdi,rdi
   0x00000000004017a1 <+3>:		je     0x4017d5 <fun7+55>
   0x00000000004017a3 <+5>:		sub    rsp,0x8
   0x00000000004017a7 <+9>:		mov    eax,DWORD PTR [rdi]
   0x00000000004017a9 <+11>:	cmp    eax,esi
   0x00000000004017ab <+13>:	jg     0x4017b9 <fun7+27>
   0x00000000004017ad <+15>:	jne    0x4017c6 <fun7+40>
   0x00000000004017af <+17>:	mov    eax,0x0
   0x00000000004017b4 <+22>:	add    rsp,0x8
   0x00000000004017b8 <+26>:	ret    
   0x00000000004017b9 <+27>:	mov    rdi,QWORD PTR [rdi+0x8]
   0x00000000004017bd <+31>:	call   0x40179e <fun7>
   0x00000000004017c2 <+36>:	add    eax,eax
   0x00000000004017c4 <+38>:	jmp    0x4017b4 <fun7+22>
   0x00000000004017c6 <+40>:	mov    rdi,QWORD PTR [rdi+0x10]
   0x00000000004017ca <+44>:	call   0x40179e <fun7>
   0x00000000004017cf <+49>:	lea    eax,[rax+rax*1+0x1]
   0x00000000004017d3 <+53>:	jmp    0x4017b4 <fun7+22>
   0x00000000004017d5 <+55>:	mov    eax,0xffffffff
   0x00000000004017da <+60>:	ret    
End of assembler dump.

果然是典型的二分查找,只不过是利用二叉查找树了而已
fun7(int val, node* root)用C语言表示

int fun7(int val, node* root) {
		if (root == NULL) return -1;
		if (val < root->val) {
				return 2 * fun7(val, root->left);
		} else if (val > root->val) {
				return 2 * fun7(val, root->right) + 1;
		} else {
				return 0;
		}
}

由此可见,只需要反推出返回值为0x5时的输入val即可
能够产生5的过程有很多,但是综合考虑该二叉树高度为4,只能作四次运算,所以产生5的方式只能是0 -> 1 -> 2 -> 5,即

  1. 找到val
  2. 从右子树回溯一次,eax = 0 * 2 + 1 = 1
  3. 从左子树回溯一次,eax = 1 * 2 = 2
  4. 从右子树回溯一次,eax = 2 * 2 + 1 = 5

如图所示
CASPP_Lab2 二进制炸弹_第11张图片
所以secret_phase的答案为0x2f == 47

CASPP_Lab2 二进制炸弹_第12张图片

拆弹成功!请叫我不那么专业的拆弹专家

二、课前预习

知道大家不爱看预习,我把预习放后面

该部分为循环、链表等对应的汇编,帮助熟悉一下,可直接跳过,进入拆弹环节

LInux 常见命令
C 汇编 Linux 手册
gedit便捷插件配置

以下语句导入插件库

sudo apt-get install gedit-plugins
  • 请写出C语言下包含字符串比较、循环、分支(含switch)、函数调用、递归、指针、结构、链表等的例子程序sample.c。
  • 生成执行程序sample.out。
  • 用gcc –S或CodeBlocks或GDB或OBJDUMP等,反汇编,比较。
    列出每一部分的C语言对应的汇编语言。
  • 修改编译选项-O (缺省2)、O0、O1、O3、Og、-m32/m64。再次查看生成的汇编语言与原来的区别。
  • 注意O1之后缺省无栈帧,RBP为普通寄存器。用 -fno-omit-frame-pointer加上栈指针。
  • GDB命令详解 –tui模式 ^XA切换 layout改变等等
  • 有目的地学习: 看VS的功能,GDB命令用什么?

生 成 s a m p l e . c 文 件 {\color{blue}{生成sample.c文件}} sample.c

gedit sample.c

生 成 s a m p l e . o u t 可 执 行 文 件 {\color{blue}{生成sample.out可执行文件}} sample.out

直接生成 a.out

gcc sample.c

自命名 sample.out 文件

gcc sample.c -o sample.out

sample.c 如下

#include 
#include 
#include 

typedef struct node *link;
struct node {
	unsigned char item;
	link next;
};

static link head = NULL;	// 头指针

link make_node(unsigned char item) {
	link p = malloc(sizeof *p);
	p->item = item;
	p->next = NULL;
	return p;
}

void insert(link p) {
	p->next = head;
	head = p;
}

void free_node(link p) {
	free(p);
}

link pop(void) {
	if (head == NULL) {
		return NULL;
	} else {
		link p = head;
		head = head->next;
		return p;
	}
}

void print_item(link p) {
	printf("%d->", p->item);
}

void fighting_function() {
	printf("I can do it!\n");
}

int *swap(int *px, int *py) {
	int temp;
	temp = *px;
	*px = *py;
	*py = temp;
	return px;
}

int main() {
	
	/* 字符串比较 */
	printf("请输入等待比较的字符串:\n");
	char str1[50] = "Full hope full energy!";
	char str2[50] = {0};
	
	gets(str2);
	printf("result = %d\n", strcmp(str1, str2));

	/* 循环 */
	// 一层
	for (int i = 0; i < 5; i ++) {
		printf("%c", 'h');
	}
	printf("\n");
	
	// 两层
	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			printf("%d %d\n", i, j);
		}
	}
	
	/* 分支语句 */
	// if else
	int k = 1;
	if (k == 1) {
		printf("if\n");
	} else if (k > 2) {
		printf("else if\n");
	} else {
		printf("else\n");
	}
	
	// switch case
	int love = 1;
	printf("请输入 1~3");
	scanf("%d", &love);
	switch (love) {
	case 1:
		printf("I love you~\n");
		break;
	case 2:
		printf("You love me~\n");
		break;
	default:
		printf("Mixue ice cream and tea\n");
		break;
	}
		
	/* 函数调用 */
	fighting_function();
	
	/* go to */
	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (i >= 2) goto date;
		}
	}
	
	date:
		printf("Let's have a date~\n");
	
	/* 指针 */
	int sw_a = 10, sw_b = 20;
	int *sp = swap(&sw_a, &sw_b);
	printf("a = %d, b = %d, *sp = %d\n", sw_a, sw_b, *sp);
	
/******************\
* 解锁新的注释方式  *
*       	   *
\******************/

	/* 结构体 */
	struct Map_Pos {
		int x, y;
	};
	struct Map_Pos a = {1, 2};
	printf("x = %d, y = %d\n", a.x, a.y);
	
	/* 链表 */
	link p = make_node(0);
	insert(p);
	p = make_node(2);
	insert(p);
	p = make_node(5);
	insert(p);
	while (p = pop()) {
		print_item(p);
		free_node(p);
	}
	
	return 0;
}

得到 sample.s

gcc sample.c -S

汇编代码 sample.s 如下,划分还是比较清晰的

/ ∗ 字 符 串 比 较 ∗ / {\color{blue}{/* 字符串比较 */}} //

	/* 字符串比较 */
	printf("请输入等待比较的字符串:\n");
	char str1[50] = "Full hope full energy!";
	char str2[50] = {0};
	
	gets(str2);
	printf("result = %d\n", strcmp(str1, str2));
/* 字符串比较 */
	call	puts@PLT
	movabsq	$8101808743130101062, %rax
	movabsq	$7286943410167357541, %rdx
	movq	%rax, -128(%rbp)
	movq	%rdx, -120(%rbp)
	movabsq	$36805310309742, %rax
	movl	$0, %edx
	movq	%rax, -112(%rbp)
	movq	%rdx, -104(%rbp)
	movq	$0, -96(%rbp)
	movq	$0, -88(%rbp)
	movw	$0, -80(%rbp)
	movq	$0, -64(%rbp)
	movq	$0, -56(%rbp)
	movq	$0, -48(%rbp)
	movq	$0, -40(%rbp)
	movq	$0, -32(%rbp)
	movq	$0, -24(%rbp)
	movw	$0, -16(%rbp)
	leaq	-64(%rbp), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	gets@PLT
	leaq	-64(%rbp), %rdx
	leaq	-128(%rbp), %rax
	movq	%rdx, %rsi
	movq	%rax, %rdi
	call	strcmp@PLT
	movl	%eax, %esi
	leaq	.LC3(%rip), %rdi
	movl	$0, %eax
	call	printf@PLT
	movl	$0, -176(%rbp)
	jmp	.L13

/ ∗ 循 环 ∗ / {\color{blue}{/* 循环 */}} //

一层

	/* 循环 */
	// 一层
	for (int i = 0; i < 5; i ++) {
		printf("%c", 'h');
	}
	printf("\n");

/* 循环 */
	// 一层
	subq	$16, %rsp
	movl	$0, -4(%rbp)
	jmp	.L13
.L14:
	movl	$104, %edi
	call	putchar@PLT
	addl	$1, -4(%rbp)
.L13:
	cmpl	$4, -4(%rbp)
	jle	.L14
	movl	$10, %edi
	call	putchar@PLT
	movl	$0, %eax
	

两层

	// 两层
	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			printf("%d %d\n", i, j);
		}
	}
	// 两层
	subq	$16, %rsp
	movl	$0, -8(%rbp)
	jmp	.L13
.L16:
	movl	$0, -4(%rbp)
	jmp	.L14
.L15:
	movl	-4(%rbp), %edx
	movl	-8(%rbp), %eax
	movl	%eax, %esi
	leaq	.LC2(%rip), %rdi
	movl	$0, %eax
	call	printf@PLT
	addl	$1, -4(%rbp)
.L14:
	cmpl	$4, -4(%rbp)
	jle	.L15
	addl	$1, -8(%rbp)
.L13:
	cmpl	$4, -8(%rbp)
	jle	.L16
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc

/ ∗ 分 支 语 句 ∗ / {\color{blue}{/* 分支语句 */}} //

if else

	/* 分支语句 */
	// if else
	int k = 1;
	if (k == 1) {
		printf("if\n");
	} else if (k > 2) {
		printf("else if\n");
	} else {
		printf("else\n");
	}
	subq	$16, %rsp
	movl	$1, -4(%rbp)
	cmpl	$1, -4(%rbp)
	jne	.L13
	leaq	.LC2(%rip), %rdi
	call	puts@PLT
	jmp	.L14
.L13:
	cmpl	$2, -4(%rbp)
	jle	.L15
	leaq	.LC3(%rip), %rdi
	call	puts@PLT
	jmp	.L14
.L15:
	leaq	.LC4(%rip), %rdi
	call	puts@PLT
.L14:
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret

switch case

	// switch case
	int love = 1;
	printf("请输入 1~3");
	scanf("%d", &love);
	switch (love) {
	case 1:
		printf("I love you~\n");
		break;
	case 2:
		printf("You love me~\n");
		break;
	default:
		printf("Mixue ice cream and tea\n");
		break;
	}
	subq	$16, %rsp
	movq	%fs:40, %rax
	movq	%rax, -8(%rbp)
	xorl	%eax, %eax
	movl	$1, -12(%rbp)
	leaq	.LC2(%rip), %rdi
	movl	$0, %eax
	call	printf@PLT
	leaq	-12(%rbp), %rax
	movq	%rax, %rsi
	leaq	.LC3(%rip), %rdi
	movl	$0, %eax
	call	__isoc99_scanf@PLT
	movl	-12(%rbp), %eax
	cmpl	$1, %eax
	je	.L13
	cmpl	$2, %eax
	je	.L14
	jmp	.L19
.L13:
	leaq	.LC4(%rip), %rdi
	call	puts@PLT
	jmp	.L16
.L14:
	leaq	.LC5(%rip), %rdi
	call	puts@PLT
	jmp	.L16
.L19:
	leaq	.LC6(%rip), %rdi
	call	puts@PLT
	nop
.L16:
	movl	$0, %eax
	movq	-8(%rbp), %rdx
	xorq	%fs:40, %rdx
	je	.L18
	call	__stack_chk_fail@PLT
.L18:
	leave
	.cfi_def_cfa 7, 8
	ret

go to

	/* go to */
	for (int i = 0; i < 5; i++) {
		for (int j = 0; j < 5; j++) {
			if (i >= 2) goto date;
		}
	}
	
	date:
		printf("Let's have a date~\n");
	subq	$16, %rsp
	movl	$0, -8(%rbp)
	jmp	.L13
.L18:
	movl	$0, -4(%rbp)
	jmp	.L14
.L17:
	cmpl	$1, -8(%rbp)
	jg	.L20
	addl	$1, -4(%rbp)
.L14:
	cmpl	$4, -4(%rbp)
	jle	.L17
	addl	$1, -8(%rbp)
.L13:
	cmpl	$4, -8(%rbp)
	jle	.L18
	jmp	.L16
.L20:
	nop
.L16:
	leaq	.LC2(%rip), %rdi
	call	puts@PLT
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc

/ ∗ 函 数 调 用 ∗ / {\color{blue}{/* 函数调用 */}} //

void fighting_function() {
	printf("I can do it!\n");
}

	/* 函数调用 */
	fighting_function();
fighting_function:
.LFB11:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	leaq	.LC1(%rip), %rdi
	call	puts@PLT
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE11:
	.size	fighting_function, .-fighting_function
	.globl	swap
	.type	swap, @function

	movl	$0, %eax
	call	fighting_function
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret

/ ∗ 指 针 ∗ / {\color{blue}{/* 指针 */}} //

int *swap(int *px, int *py) {
	int temp;
	temp = *px;
	*px = *py;
	*py = temp;
	return px;
}

	/* 指针 */
	int sw_a = 10, sw_b = 20;
	int *sp = swap(&sw_a, &sw_b);
	printf("a = %d, b = %d, *sp = %d\n", sw_a, sw_b, *sp);
swap:
.LFB12:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	%rdi, -24(%rbp)
	movq	%rsi, -32(%rbp)
	movq	-24(%rbp), %rax
	movl	(%rax), %eax
	movl	%eax, -4(%rbp)
	movq	-32(%rbp), %rax
	movl	(%rax), %edx
	movq	-24(%rbp), %rax
	movl	%edx, (%rax)
	movq	-32(%rbp), %rax
	movl	-4(%rbp), %edx
	movl	%edx, (%rax)
	movq	-24(%rbp), %rax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE12:
	.size	swap, .-swap
	.section	.rodata
.LC2:
	.string	"a = %d, b = %d, *sp = %d\n"
	.text
	.globl	main
	.type	main, @function

	subq	$32, %rsp
	movq	%fs:40, %rax
	movq	%rax, -8(%rbp)
	xorl	%eax, %eax
	movl	$10, -24(%rbp)
	movl	$20, -20(%rbp)
	leaq	-20(%rbp), %rdx
	leaq	-24(%rbp), %rax
	movq	%rdx, %rsi
	movq	%rax, %rdi
	call	swap
	movq	%rax, -16(%rbp)
	movq	-16(%rbp), %rax
	movl	(%rax), %ecx
	movl	-20(%rbp), %edx
	movl	-24(%rbp), %eax
	movl	%eax, %esi
	leaq	.LC2(%rip), %rdi
	movl	$0, %eax
	call	printf@PLT
	movl	$0, %eax
	movq	-8(%rbp), %rsi
	xorq	%fs:40, %rsi
	je	.L14
	call	__stack_chk_fail@PLT
.L14:
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc

/ ∗ 结 构 体 ∗ / {\color{blue}{/* 结构体 */}} //

这里可以看出结构体内部定义的数据是在申请的栈空间上连续排列的

	/* 结构体 */
	struct Map_Pos {
		int x, y;
	};
	struct Map_Pos a = {1, 2};
	printf("x = %d, y = %d\n", a.x, a.y);

	subq	$16, %rsp
	movl	$1, -8(%rbp)
	movl	$2, -4(%rbp)
	movl	-4(%rbp), %edx
	movl	-8(%rbp), %eax
	movl	%eax, %esi
	leaq	.LC2(%rip), %rdi
	movl	$0, %eax
	call	printf@PLT
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc

/ ∗ 链 表 ∗ / {\color{blue}{/* 链表 */}} //

	/* 链表 */
	link p = make_node(0);
	insert(p);
	p = make_node(2);
	insert(p);
	p = make_node(5);
	insert(p);
	while (p = pop()) {
		print_item(p);
		free_node(p);
	}
	subq	$16, %rsp
	movl	$0, %edi
	call	make_node
	movq	%rax, -8(%rbp)
	movq	-8(%rbp), %rax
	movq	%rax, %rdi
	call	insert
	movl	$2, %edi
	call	make_node
	movq	%rax, -8(%rbp)
	movq	-8(%rbp), %rax
	movq	%rax, %rdi
	call	insert
	movl	$5, %edi
	call	make_node
	movq	%rax, -8(%rbp)
	movq	-8(%rbp), %rax
	movq	%rax, %rdi
	call	insert
	jmp	.L13
.L14:
	movq	-8(%rbp), %rax
	movq	%rax, %rdi
	call	print_item
	movq	-8(%rbp), %rax
	movq	%rax, %rdi
	call	free_node
.L13:
	call	pop
	movq	%rax, -8(%rbp)
	cmpq	$0, -8(%rbp)
	jne	.L14
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc

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