CTF Pwn模块系列分享(二):汇编基础+Linux内存模型拆解

今天进入Pwn学习的关键前置关——汇编基础+Linux进程内存模型。

今天我不会讲复杂的底层原理,只挑Pwn解题必须用到的核心内容,用大白话+实操案例拆解,保证新手也能看懂、会用。毕竟这两块是后续学栈溢出、ROP的基础,打好基础,后面的学习会事半功倍!

在这里插入图片描述

一、先明确:为什么必须学汇编和内存模型?

在讲具体内容前,先解决大家的疑惑:Pwn是二进制漏洞利用,为什么非要学这些?

今天我们重点讲“x86_64架构”的汇编(CTF Pwn题90%以上是x86_64,也就是64位Linux环境),不用纠结32位,先把64位核心内容吃透。

二、汇编基础:新手必懂的核心寄存器+关键指令

汇编的核心是“寄存器”和“指令”——寄存器是CPU的“临时仓库”,指令是CPU的“操作命令”。新手不用记所有寄存器和指令,掌握下面这些Pwn高频内容就够了。

1. 高频寄存器:6个核心寄存器及其用途

x86_64有很多寄存器,但Pwn中最常用的是这6个,记住它们的分工:

在这里插入图片描述
小提醒:寄存器名前加“r”是64位(比如rax),加“e”是32位(比如eax),Pwn中重点关注64位。

2. 关键指令:5个高频指令,理解程序核心操作

指令是CPU的操作命令,Pwn中最常用的5个指令,拆解如下:

1. mov 目标, 源:

功能:把“源”的数据复制到“目标”(比如把内存中的数据放到寄存器,或寄存器之间传数据)。

例子:mov rax, 0x1 → 把数字1放到rax寄存器;mov rdi, [rbp+0x8] → 把rbp+0x8地址处的内存数据放到rdi。

2. push 数据:

功能:把数据压入栈中(栈是“先进后出”的结构),同时rsp会减8(64位系统,一个数据占8字节)。

例子:push rbp → 把rbp寄存器的值压入栈,rsp = rsp - 8。

3. pop 目标:

功能:把栈顶的数据弹出到“目标”,同时rsp会加8。

例子:pop rbp → 把栈顶数据放到rbp,rsp = rsp + 8。

4. call 函数地址:

功能:调用函数,核心操作有两个——① 把当前rip的值(下一条要执行的指令地址)压入栈(这就是“返回地址”);② 把rip指向函数地址。

关键:Pwn中栈溢出的核心,就是修改这个“返回地址”,让程序执行我们想执行的代码!

5. ret:

功能:函数执行结束后返回,核心操作——把栈顶的返回地址弹出到rip。

例子:函数执行完call后,执行ret,就会把之前压入栈的返回地址弹到rip,程序回到call之后的指令继续执行。

三、Linux进程内存模型:找到漏洞的内存地图

当程序运行时,操作系统会给它分配一块独立的内存空间,这块空间按功能分成不同区域——这就是“进程内存模型”。Pwn的漏洞都和“栈”相关,所以重点讲栈,其他区域简单了解即可。

1. 内存四大核心分区(从低地址到高地址)

  • 代码段(.text):存放程序的指令(比如我们刚才讲的mov、call指令),只读(不能修改)。

  • 数据段(.data/.bss):存放程序的全局变量、静态变量(比如程序里定义的int a=10)。

  • 堆(Heap):动态分配内存的区域(比如程序里用malloc申请的内存),从低地址向高地址生长。

  • 栈(Stack):存放函数的局部变量、参数、返回地址,从高地址向低地址生长(核心!Pwn漏洞高发区)。

关键记住:栈的生长方向是“从高地址到低地址”,而push指令是“往栈里加东西”,所以push会让rsp减小;pop是“从栈里拿东西”,会让rsp增大。

2. 栈帧结构:Pwn解题的核心重点

每个函数执行时,都会在栈上开辟一块独立的空间,这就是“栈帧”。栈帧由rbp和rsp界定:rbp指向栈帧底部(固定),rsp指向栈帧顶部(动态)。

一个完整的栈帧(函数调用时)结构如下(从高地址到低地址,也就是从栈底到栈顶):

  1. 函数参数(比如调用func(a,b),a是第1参数,存在rdi;b是第2参数,存在rsi,同时会压入栈)。

  2. 返回地址(call指令压入的,是函数执行完后要回到的指令地址)。

  3. 老rbp(函数开头会执行push rbp,把上一个栈帧的rbp压入栈,方便返回后恢复)。

  4. 局部变量(函数里定义的变量,比如int x=5,会存在栈帧的这个区域)。

用通俗的图理解(简化版):

[高地址] 函数参数 → 返回地址 → 老rbp → 局部变量 [低地址]

         ↑          ↑        ↑               

        rbp+0x10   rbp+0x8    rbp

Pwn核心逻辑:栈溢出漏洞,就是让“局部变量”的输入超出分配的内存空间,覆盖到“返回地址”——把返回地址改成我们想执行的代码地址,程序就会按我们的意愿执行!

四、实战小任务:用GDB看栈帧,直观理解(手把手教)

光说不练假把式,我们用一个简单的C程序,通过GDB调试,直观看看栈帧结构:

1. 准备简单C程序(保存为test.c)

#include <stdio.h>

void func(int a, int b) {
   int x = 10; // 局部变量
   printf("a=%d, b=%d\n", a, b);
   }

int main() {
func(1, 2); // 调用func,参数1和2
return 0;
 }
  }

2. 编译程序(关闭保护,方便调试)

终端执行命令:gcc -g -fno-stack-protector -z execstack -o test test.c

参数说明:-g加调试信息;-fno-stack-protector关闭栈保护;-z execstack允许栈执行(新手调试用)。

3. GDB调试,查看栈帧

启动GDB:gdb ./test

在func函数处下断点:b func

运行程序:r

查看栈帧信息:info frame → 能看到rbp、rsp的地址,以及返回地址;

查看栈内数据:x/20wx $rsp → 查看rsp开始的20个4字节数据,能看到局部变量x、老rbp、返回地址、函数参数a和b。

通过这个操作,你能直观看到栈里的返回地址、局部变量的位置——这就是后续栈溢出漏洞利用的基础!

五、新手避坑&学习建议

不要死记硬背:汇编指令和栈帧结构,结合GDB调试理解,比死记快。

先掌握64位:CTF中64位Pwn题占绝大多数,32位可以后续再补。

多调试简单程序:把刚才的test.c改一改(比如加更多局部变量、多嵌套几个函数),用GDB看栈帧变化,加深理解。

推荐工具:除了GDB,还可以用pwndbg(GDB插件,更友好),终端执行git clone https://github.com/pwndbg/pwndbg && cd pwndbg && ./setup.sh即可安装。

六、下期预告

今天我们了解了汇编基础和栈帧结构,这是后续学习栈溢出的核心前提。下期我们将进入最关键的实战环节:栈溢出基础与ret2text实战,第一次亲手构造payload,控制程序执行流!

如果今天的内容对你有帮助,别忘了点赞、在看,转发给一起学CTF的小伙伴~

CTF学习资源

网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我们和网安大厂360共同研发的的网安视频教程,内容涵盖了入门必备的操作系统、计算机网络和编程语言等初级知识,而且包含了中级的各种渗透技术,并且还有后期的CTF对抗、区块链安全等高阶技术。总共200多节视频,100多本网安电子书,最新学习路线图和工具安装包都有,不用担心学不全。
在这里插入图片描述

适合学习的人群

一、基础适配人群

  1. 零基础转型者‌:适合计算机零基础但愿意系统学习的人群,资料覆盖从网络协议、操作系统到渗透测试的完整知识链‌;
  2. 开发/运维人员‌:具备编程或运维基础者可通过资料快速掌握安全防护与漏洞修复技能,实现职业方向拓展‌或者转行就业;
  3. 应届毕业生‌:计算机相关专业学生可通过资料构建完整的网络安全知识体系,缩短企业用人适应期‌;

二、能力提升适配

1、‌技术爱好者‌:适合对攻防技术有强烈兴趣,希望掌握漏洞挖掘、渗透测试等实战技能的学习者‌;

2、安全从业者‌:帮助初级安全工程师系统化提升Web安全、逆向工程等专项能力‌;

3、‌合规需求者‌:包含等保规范、安全策略制定等内容,适合需要应对合规审计的企业人员‌;

因篇幅有限,仅展示部分资料,完整版的网络安全学习资料已经上传,戳下面拿:

在这里插入图片描述

🐵这些东西我都可以免费分享给大家,需要的可以点这里自取👉:网安入门到进阶资源

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐