☰
二进制漏洞挖掘入门
2026/10/15 19:05:18 网站建设 项目流程

前置共识:你懂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位优先寄存器传参,栈只存后备参数+栈帧+返回地址。

核心指令清单(漏洞挖掘高频)
  1. mov dst, src:数据拷贝,mov eax, 0x1234
  2. push val:压栈,ESP -=4(x86)/8(x64),把val写入ESP指向内存
  3. pop reg:出栈,把ESP指向的数据读入reg,ESP +=4/8
  4. call 0x401000:函数调用
    • 自动:把下一条指令地址(返回地址return address)压栈,然后跳转到目标地址
  5. ret:函数返回
    • 自动:从栈取出返回地址,弹出,跳转到该地址。栈溢出劫持的核心!
  6. cmp a,b:比较,修改标志位,不修改a,b本身
  7. je/jne/jg/jl:条件跳转,对应代码if else分支
  8. rep movsb:内存批量拷贝,很多堆溢出、缓冲区溢出发生在这里
  9. lea reg, [mem]:加载有效地址,不是读内存!取地址,经常用来做简单算术计算,新手极易踩坑。

学习建议:王爽汇编只看寄存器、内存寻址、栈章节。不用学BIOS、中断。

2. PE / ELF 文件格式 & 程序加载到内存的过程

PE = Windows可执行文件(exe/dll);ELF = Linux(elf可执行、so动态库)
程序保存在磁盘上是PE/ELF文件,操作系统加载器把它映射到虚拟内存,不是原样复制。

核心段(Section)
  1. .text:代码段,存放汇编指令,只读+可执行。程序正常执行代码在这里。
  2. .data:已初始化全局变量(int g=10;),可读可写。
  3. .bss:未初始化全局变量(int g;),不占用磁盘空间,加载后在内存分配,可读可写。
  4. .rdata:只读数据,字符串常量放这里。
两个不属于PE/ELF段,但最重要的内存区域
  1. 栈 Stack
    • 操作系统创建线程时分配;每个线程独立栈
    • 大小固定(默认Windows 1MB),向下增长
    • 存放:局部变量、函数参数、栈帧基址EBP、函数返回地址
    • 生命周期:函数进入开辟栈帧,函数退出回收
    • 漏洞主战场:栈溢出(stack buffer overflow)
  2. 堆 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

约定三件事:参数放哪里、谁清理栈、名字修饰。

  1. cdecl:C默认,参数从右往左压栈;调用者清理栈(C语言可变参数printf用这个)
  2. stdcall:参数从右往左压栈;被调用函数ret n 自动清理栈(Windows API)
  3. fastcall:优先寄存器传参,剩余压栈(x64全部属于fastcall风格)

格式化字符串漏洞:printf(user_input);,用户输入直接传入格式化函数,%p %x %n。类比Web XSS,输入当成解析指令执行,可读取栈内存,甚至写内存。

二、漏洞保护机制(二进制“WAF”,Web类比)

编译/操作系统开启,用来对抗内存漏洞,挖洞必须逐个理解:

  1. ASLR 地址空间布局随机化
    模块基址随机,每次启动程序dll/so加载地址不一样。Web类比:每次请求随机CSRF Token,硬编码地址的Payload失效。
    • 绕过思路:信息泄露,泄露一个模块基址,算出所有函数地址。
  2. DEP / NX 数据执行保护
    栈、堆内存标记为不可执行。就算你把shellcode写到栈上,CPU也不能执行这片内存。类比WAF拦截恶意脚本执行。
    • 绕过:ROP(Return-Oriented Programming,返回导向编程),复用程序本身.text段里已有的指令片段,拼接gadget,不需要在栈执行shellcode。
  3. Stack Canary 栈金丝雀
    在buffer和返回地址之间插入一个随机哨兵值。函数ret前检查canary,如果被篡改,程序直接崩溃终止。类比:在输入缓冲区末尾放校验码。
    • 绕过:栈内存泄露,读出canary值,Payload带上原canary,覆盖时不破坏它。
  4. PIE 位置无关可执行文件
    整个程序.text代码段基址随机,相当于ASLR作用于主程序。关闭PIE地址固定。
  5. 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:运行直到下一个断点
  • 调试窗口:寄存器窗口、栈窗口、内存窗口。随时改内存、改寄存器值。

调试目标:

  1. 跟踪用户输入写入内存的位置
  2. 观察缓冲区边界,看多少字节输入会覆盖返回地址(计算偏移)
  3. 验证漏洞触发,看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

  1. 审计找危险函数
    IDA静态分析,找到strcpy(buffer, user_input),没有长度判断。

Web动作:找接口发现直接拼接参数到SQL,无过滤。

  1. 确认可控输入
    调试器,输入字符串,确认输入写入栈上buffer。
  2. 计算偏移量(offset)
    生成唯一模式字符串(pwntoolscyclic),发送给程序,程序崩溃。查看RIP的值,反查得到从buffer起始位置到返回地址的字节数。

Web动作:union注入猜列数。

  1. 构造payload
    payload = 垃圾填充( offset字节 ) + 目标地址
    垃圾填充填满缓冲区,刚好覆盖到return address,把return address替换成后门函数地址。
  2. 发送payload,验证劫持
    运行程序,payload送入,函数ret时跳转到后门函数,拿到shell。

Web动作:发送注入语句,成功执行SQL。

六、学习路线 & 避坑指南(针对Web安全专家)

✅ 推荐阅读精简清单

  1. 《汇编语言》王爽:寄存器、栈、内存寻址(前5章)
  2. 《程序员的自我修养》:PE/ELF、程序加载,只读3、6章
  3. 《0day安全:软件漏洞分析技术》:栈溢出章节,入门圣经
  4. pwndbg官方文档、pwntools文档

✅ 练习平台

  • pwnable.kr(入门Pwn靶场,栈溢出、格式化字符串)
  • HackTheBox Pwn、BUUCTF Pwn题库

❌ Web专家最容易踩的坑

  1. 误以为栈向上增长:栈是向下增长,地址变小,这个是高频踩坑点。
  2. 混淆虚拟地址和物理地址:所有调试看到地址都是虚拟内存地址,操作系统内存隔离。
  3. 跳过保护机制直接学堆漏洞:顺序:无保护栈溢出 → ROP绕过NX → canary绕过 → 堆漏洞。堆难度远高于栈溢出,不要一开始就啃堆。
  4. 只看书不上调试器:二进制是动手学科,看汇编必须跟着调试器单步看寄存器和内存变化。

七、知识体系总图(可以存下来)

底层基础:汇编指令+寄存器 → 栈帧/调用约定 → PE/ELF内存布局
↓
漏洞保护机制 ASLR/DEP/Canary/PIE
↓
工具链:IDA静态分析 + x64dbg/gdb动态调试 + pwntools写EXP
↓
漏洞类型:栈溢出 → 格式化字符串 → 堆溢出/UAF
↓
EXP利用:ROP、信息泄露、GOT劫持等绕过保护手法
↓
实战:CTF Pwn靶场 → 真实程序审计、漏洞复现、挖洞

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询