前置共识:你懂Web安全,**Web漏洞本质:输入
Web:请求体、参数、Cookie → 解析引擎执行;
二进制:标准输入、文件内容、网络数据包 → CPU执行;
所有内存漏洞,都离不开两件东西:内存布局+程序控制流。
一、底层基础模块(漏洞挖掘刚需,非必要内容砍掉)
1. x86 / x64 汇编基础(只学漏洞相关,不搞DOS中断、浮点指令)
寄存器
x86(32位)
通用寄存器:EAX EBX ECX EDX ESI EDI EBP ESP
EAX:函数返回值,类比Web接口return的结果ECX/EDX:临时变量,循环计数ESI/EDI:内存拷贝(rep movs,大量溢出场景高频)- EBP:栈基址,标记当前函数栈帧底部
- ESP:栈指针,永远指向栈顶,栈是向下增长(地址变小)!👉 这个点Web人最容易记错:栈往低地址长。
x64(64位)
寄存器名字加R:RAX RBX RCX RDX RSI RDI RBP RSP
新增8个寄存器:R8~R15
Windows x64调用约定:前4个参数RCX,RDX,R8,R9寄存器传参,剩余放栈;
Linux System V AMD64:前6参数RDI,RSI,RDX,RCX,R8,R9,剩余放栈。
✅ 重点区别:32位几乎全部参数压栈;64位优先寄存器传参,栈只存后备参数+栈帧+返回地址。
核心指令清单(漏洞挖掘高频)
mov dst, src:数据拷贝,mov eax, 0x1234push val:压栈,ESP -=4(x86)/8(x64),把val写入ESP指向内存pop reg:出栈,把ESP指向的数据读入reg,ESP +=4/8call 0x401000:函数调用- 自动:把下一条指令地址(返回地址return address)压栈,然后跳转到目标地址
ret:函数返回- 自动:从栈取出返回地址,弹出,跳转到该地址。栈溢出劫持的核心!
cmp a,b:比较,修改标志位,不修改a,b本身je/jne/jg/jl:条件跳转,对应代码if else分支rep movsb:内存批量拷贝,很多堆溢出、缓冲区溢出发生在这里lea reg, [mem]:加载有效地址,不是读内存!取地址,经常用来做简单算术计算,新手极易踩坑。
学习建议:王爽汇编只看寄存器、内存寻址、栈章节。不用学BIOS、中断。
2. PE / ELF 文件格式 & 程序加载到内存的过程
PE = Windows可执行文件(exe/dll);ELF = Linux(elf可执行、so动态库)
程序保存在磁盘上是PE/ELF文件,操作系统加载器把它映射到虚拟内存,不是原样复制。
核心段(Section)
.text:代码段,存放汇编指令,只读+可执行。程序正常执行代码在这里。.data:已初始化全局变量(int g=10;),可读可写。.bss:未初始化全局变量(int g;),不占用磁盘空间,加载后在内存分配,可读可写。.rdata:只读数据,字符串常量放这里。
两个不属于PE/ELF段,但最重要的内存区域
- 栈 Stack
- 操作系统创建线程时分配;每个线程独立栈
- 大小固定(默认Windows 1MB),向下增长
- 存放:局部变量、函数参数、栈帧基址EBP、函数返回地址
- 生命周期:函数进入开辟栈帧,函数退出回收
- 漏洞主战场:栈溢出(stack buffer overflow)
- 堆 Heap
- 动态内存,
malloc()/new申请,free()释放 - 大小不固定,向上增长;多个块相邻排布
- 存放:程序运行时动态的数据、缓冲区
- 漏洞主战场:堆溢出、UAF、Double Free
- 动态内存,
Web类比:
.text=后端源码;全局变量=全局config;栈=单次请求临时上下文;堆=动态上传缓存。
3. 栈帧结构 & 调用约定(栈溢出原理基石)
32位典型函数栈帧
高地址 +----------------+ | 上一层函数栈帧 | | return address | <-- call指令压入!劫持目标 | saved EBP | <-- push ebp;mov ebp,esp 保存旧栈底 | local buffer[] | <-- 用户输入写入这里!缓冲区 | 局部变量 | +----------------+ 低地址函数序言 prologue(函数开头固定汇编)
push ebp ;保存上层栈底 mov ebp, esp ;当前ebp作为新栈帧底部 sub esp, 0x20 ;栈空间开辟0x20字节放局部变量(缓冲区)函数尾声 epilogue
mov esp, ebp pop ebp ret ;从栈取出返回地址跳转!👉栈溢出原理一句话:缓冲区buffer在栈上,程序没有校验输入长度,超长输入覆盖buffer之后,继续往后写,覆盖栈上的return address。当函数执行ret时,CPU跳转到我们可控的地址,实现控制流劫持。
调用约定 Calling Convention
约定三件事:参数放哪里、谁清理栈、名字修饰。
cdecl:C默认,参数从右往左压栈;调用者清理栈(C语言可变参数printf用这个)stdcall:参数从右往左压栈;被调用函数ret n 自动清理栈(Windows API)fastcall:优先寄存器传参,剩余压栈(x64全部属于fastcall风格)
格式化字符串漏洞:
printf(user_input);,用户输入直接传入格式化函数,%p %x %n。类比Web XSS,输入当成解析指令执行,可读取栈内存,甚至写内存。
二、漏洞保护机制(二进制“WAF”,Web类比)
编译/操作系统开启,用来对抗内存漏洞,挖洞必须逐个理解:
- ASLR 地址空间布局随机化
模块基址随机,每次启动程序dll/so加载地址不一样。Web类比:每次请求随机CSRF Token,硬编码地址的Payload失效。- 绕过思路:信息泄露,泄露一个模块基址,算出所有函数地址。
- DEP / NX 数据执行保护
栈、堆内存标记为不可执行。就算你把shellcode写到栈上,CPU也不能执行这片内存。类比WAF拦截恶意脚本执行。- 绕过:ROP(Return-Oriented Programming,返回导向编程),复用程序本身.text段里已有的指令片段,拼接gadget,不需要在栈执行shellcode。
- Stack Canary 栈金丝雀
在buffer和返回地址之间插入一个随机哨兵值。函数ret前检查canary,如果被篡改,程序直接崩溃终止。类比:在输入缓冲区末尾放校验码。- 绕过:栈内存泄露,读出canary值,Payload带上原canary,覆盖时不破坏它。
- PIE 位置无关可执行文件
整个程序.text代码段基址随机,相当于ASLR作用于主程序。关闭PIE地址固定。 - RELRO 重定位表保护
Partial RELRO / Full RELRO:保护GOT表,阻止GOT劫持。GOT劫持是Linux下经典利用手段。
新手入门练习靶机:全部关闭保护(
-fno-stack-protector -z execstack -no-pie),先跑通无保护栈溢出,再逐个学习绕过保护。
三、工具链深度讲解(IDA/x64dbg/gdb/010/pwntools)
1. IDA Free(静态分析,不运行程序)
静态分析:不开程序,直接反汇编二进制文件,查看伪代码(F5)。类比Web代码审计,读源码。
- 核心操作:
- F5:生成C伪代码,最常用!
- Shift+F12:查看字符串,快速定位功能点、报错信息
- X:交叉引用,查看这个函数/变量被谁调用(找危险函数调用点)
- 搜索:
strcpygetssprintfscanf无边界函数,快速找潜在溢出点
局限:静态只能看代码路径,无法知道运行时内存真实情况,必须搭配调试器。
2. 动态调试器(运行程序,实时观察内存)
Windows:x64dbg(推荐替代OllyDbg);Linux:gdb + pwndbg插件(必装)
核心操作统一理解:
- F2:下断点 break,到指定地址暂停程序
- F7:单步步入 step into,进入call函数内部
- F8:单步跳过 step over,call当做一行,不进去
- F9:运行直到下一个断点
- 调试窗口:寄存器窗口、栈窗口、内存窗口。随时改内存、改寄存器值。
调试目标:
- 跟踪用户输入写入内存的位置
- 观察缓冲区边界,看多少字节输入会覆盖返回地址(计算偏移)
- 验证漏洞触发,看EIP/RIP(指令寄存器)是否被可控数据劫持。
EIP/RIP:指令指针寄存器,CPU下一条要执行指令地址。漏洞成功劫持 = EIP/RIP被我们控制。Web类比:成功控制跳转url。
3. 010 Editor:二进制编辑器
解析PE/ELF模板,可视化查看文件头、节表。用来分析文件结构、恶意样本原始字节。
4. pwntools(Python漏洞利用库,Linux首选)
类比Web的requests库,自动化和程序交互,构造payload,远程打服务。
基础骨架示例:
frompwnimport*p=process("./vuln")#本地启动程序# p = remote("127.0.0.1",9999) #远程服务payload=b'A'*offset+p64(0x401146)#填充垃圾字节 + 目标跳转地址p.sendline(payload)p.interactive()#拿到交互shell四、经典内存漏洞原理(对照Web漏洞横向理解)
1. 栈缓冲区溢出 Stack Overflow
成因:栈上局部缓冲区,输入长度无校验,覆盖返回地址。入门第一个漏洞。
Web对标:SQL注入,输入超出预期边界,修改控制流。
2. 格式化字符串漏洞 Format String
printf(buf);用户输入直接作为格式化参数,%n可以写内存。
Web对标:XSS,输入被解析为引擎指令。
3. 堆溢出 Heap Overflow
堆块内缓冲区溢出,覆盖相邻堆块的块头metadata。堆结构复杂,利用难度高于栈溢出。
Web对标:文件上传漏洞,写入数据污染相邻文件元数据。
4. UAF Use-After-Free 释放后重用
内存free释放,但指针没有置NULL,程序继续使用这块内存。
Web对标:会话固定,会话销毁后未清空,依然可以复用session。
5. Double Free 重复释放
同一块堆内存被free两次,破坏堆管理结构。
五、完整栈溢出实战流程(详细展开,Web对照)
目标:找到漏洞 → 确定崩溃偏移 → 构造payload → 劫持RIP/EIP
- 审计找危险函数
IDA静态分析,找到strcpy(buffer, user_input),没有长度判断。
Web动作:找接口发现直接拼接参数到SQL,无过滤。
- 确认可控输入
调试器,输入字符串,确认输入写入栈上buffer。 - 计算偏移量(offset)
生成唯一模式字符串(pwntoolscyclic),发送给程序,程序崩溃。查看RIP的值,反查得到从buffer起始位置到返回地址的字节数。
Web动作:union注入猜列数。
- 构造payload
payload = 垃圾填充( offset字节 ) + 目标地址
垃圾填充填满缓冲区,刚好覆盖到return address,把return address替换成后门函数地址。 - 发送payload,验证劫持
运行程序,payload送入,函数ret时跳转到后门函数,拿到shell。
Web动作:发送注入语句,成功执行SQL。
六、学习路线 & 避坑指南(针对Web安全专家)
✅ 推荐阅读精简清单
- 《汇编语言》王爽:寄存器、栈、内存寻址(前5章)
- 《程序员的自我修养》:PE/ELF、程序加载,只读3、6章
- 《0day安全:软件漏洞分析技术》:栈溢出章节,入门圣经
- pwndbg官方文档、pwntools文档
✅ 练习平台
- pwnable.kr(入门Pwn靶场,栈溢出、格式化字符串)
- HackTheBox Pwn、BUUCTF Pwn题库
❌ Web专家最容易踩的坑
- 误以为栈向上增长:栈是向下增长,地址变小,这个是高频踩坑点。
- 混淆虚拟地址和物理地址:所有调试看到地址都是虚拟内存地址,操作系统内存隔离。
- 跳过保护机制直接学堆漏洞:顺序:无保护栈溢出 → ROP绕过NX → canary绕过 → 堆漏洞。堆难度远高于栈溢出,不要一开始就啃堆。
- 只看书不上调试器:二进制是动手学科,看汇编必须跟着调试器单步看寄存器和内存变化。
七、知识体系总图(可以存下来)
底层基础:汇编指令+寄存器 → 栈帧/调用约定 → PE/ELF内存布局
↓
漏洞保护机制 ASLR/DEP/Canary/PIE
↓
工具链:IDA静态分析 + x64dbg/gdb动态调试 + pwntools写EXP
↓
漏洞类型:栈溢出 → 格式化字符串 → 堆溢出/UAF
↓
EXP利用:ROP、信息泄露、GOT劫持等绕过保护手法
↓
实战:CTF Pwn靶场 → 真实程序审计、漏洞复现、挖洞