【学习记录】Week9(一):glibc堆结构精读与堆风水方法论——堆利用的基石

📅 2026/7/3 3:53:10 👁️ 阅读次数 📝 编程学习
【学习记录】Week9(一):glibc堆结构精读与堆风水方法论——堆利用的基石

写在前面:在Week8中,我们探讨了整数漏洞如何导致堆溢出。本周,我们将深入glibc堆管理的核心机制,并学习堆风水这一高级技术。堆风水是所有堆漏洞利用稳定性的前提,也是CTF比赛中堆题的必杀技。今天,我们先从glibc的堆结构精读开始,逐步建立堆利用的完整知识体系。

📑 目录

  1. glibc堆结构精读:chunk与bin的奥秘
  2. 堆风水方法论:精确控制内存布局的艺术
  3. 实战演练:堆风水基础应用
  4. 总结与预告

1. glibc堆结构精读:chunk与bin的奥秘

1.1 chunk结构解析

在glibc的ptmalloc2中,堆内存被组织为多个连续的chunk。每个chunk包含头部和用户数据区csdn.net:

struct malloc_chunk { size_t prev_size; // 前一个chunk的大小(如果空闲) size_t size; // 当前chunk的大小,包括头部(低3位为标志位) union { struct { malloc_chunk* fd; // 前向指针(仅在空闲时使用) malloc_chunk* bk; // 后向指针(仅在空闲时使用) }; char user_data[0]; // 用户数据区(已分配时) }; };

关键标志位

  • PREV_INUSE(0x1): 前一个chunk是否在使用中
  • IS_MMAPPED(0x2): 是否通过mmap分配
  • NON_MAIN_ARENA(0x4): 是否属于非主arena

1.2 bin类型详解

glibc通过bin来管理空闲chunk,不同大小的chunk被放入不同的bin中csdn.net+1:

0x20-0x80

0x90-0x3F8

≥0x400

其他

Chunk被释放

大小判断

Fastbin

Small Bin

Large Bin

Unsorted Bin

单链表LIFO

双链表FIFO

双链表按大小排序

双链表FIFO临时存储

1.2.1 Fastbin
  • 范围:0x20到0x80字节(64位系统)deepwiki.com
  • 结构:单链表,后进先出(LIFO)csdn.net
  • 特点
    • 不会自动合并相邻chunkcsdn.net
    • PREV_INUSE位始终为1,防止合并csdn.net
    • 分配速度最快
1.2.2 Small Bin
  • 范围:0x20到0x3F8字节(64位系统)csdn.net
  • 结构:双链表,先进先出(FIFO)csdn.net
  • 特点
    • 同一small bin中的chunk大小相同csdn.net
    • 精确匹配大小分配
1.2.3 Large Bin
  • 范围:0x400字节及以上csdn.net
  • 结构:双链表,按大小排序csdn.net
  • 特点
    • 每个bin包含一定大小范围内的chunkcsdn.net
    • 使用fd_nextsizebk_nextsize指针加速检索csdn.net
1.2.4 Unsorted Bin
  • 作用:临时存放释放的chunk,等待整理csdn.net
  • 结构:双链表,先进先出(FIFO)csdn.net
  • 特点
    • chunk大小可能不同csdn.net
    • malloc时优先检查unsorted bincsdn.net

1.3 Tcache机制(glibc 2.26+)

Tcache是glibc 2.26引入的线程缓存机制,旨在提高多线程环境下的分配性能csdn.net+1。

1.3.1 Tcache结构
typedef struct tcache_entry { struct tcache_entry *next; // 指向下一个相同大小的空闲chunk } tcache_entry; typedef struct tcache_perthread_struct { char counts[TCACHE_MAX_BINS]; // 每个bin的chunk计数 tcache_entry *entries[TCACHE_MAX_BINS]; // 单链表指针数组 } tcache_perthread_struct;
1.3.2 Tcache特性
  • 线程隔离:每个线程独立维护tcache,无锁竞争csdn.net
  • 容量限制:每个size最多7个chunkdeepwiki.com
  • 优先分配:malloc时优先从tcache获取csdn.net
  • 优先释放:free时优先放入tcache(如果未满)csdn.net
1.3.3 Tcache与Fastbin对比
特性TcacheFastbin
线程隔离
数量限制每个size最多7个无硬性限制
安全检查几乎无基础检查(如double free)
分配优先级最高次之
链表操作单链表单链表

<details> <summary>🔧 Tcache安全机制演进</summary>

  • glibc 2.26-2.27:几乎无安全检查,可轻易double freecsdn.net
  • glibc 2.29+:引入key字段检测double freecsdn.net
  • glibc 2.32+:引入Safe linking机制,next指针加密csdn.net
  • 现代glibc:对齐检查、size验证等
// glibc 2.29+的tcache_entry结构 typedef struct tcache_entry { struct tcache_entry *next; struct tcache_perthread_struct *key; // 用于检测double free } tcache_entry;

</details>

2. 堆风水方法论:精确控制内存布局的艺术

2.1 什么是堆风水?

堆风水是一种通过精确控制堆内存布局,为漏洞利用创造可预测环境的技术csdn.net+1。它本身不是漏洞,而是漏洞利用的使能技术。

核心思想:通过精心安排堆块的分配和释放顺序,使目标数据(如函数指针、GOT表项)位于可控区域csdn.net。

2.2 为什么需要堆风水?

现代操作系统和编译器引入了多种防护机制:

  • ASLR:地址空间布局随机化
  • PIE:位置无关可执行文件
  • NX:栈不可执行
  • Canary:栈溢出保护

堆风水通过控制内存布局,帮助攻击者绕过这些防护机制csdn.net。

2.3 堆风水基本步骤

步骤1:分析漏洞利用需求

明确需要控制哪些内存区域(如GOT表、函数指针、堆元数据等)csdn.net。

步骤2:设计堆布局

根据需求设计理想的堆内存布局,包括:

  • 目标地址的预期位置
  • 需要的chunk大小和数量
  • 分配和释放顺序
步骤3:实现堆操控

通过以下技术实现堆布局控制:

  • 堆喷射:大量分配特定大小的chunk,填充目标地址区间csdn.net
  • 堆整形:通过分配特定大小的chunk,影响堆管理器行为csdn.net
  • 碎片化:释放部分chunk制造空闲块,重新分配以控制排列csdn.net
步骤4:验证布局

使用GDB等工具验证堆布局是否符合预期,调整分配释放策略。

2.4 堆风水实战技巧

技巧1:覆盖特定地址范围
from pwn import * # 目标:覆盖0x56000000~0x58000000区间 # 策略:申请0x100个0x20008字节的内存块 for i in range(0x100): malloc(0x20008) # 每个chunk大小为0x20000,间隔0x20000 # 填充数据为0x57,使地址0x57575757的内容可预测 edit(i, p64(0x57575757) * (0x20008 // 8))
技巧2:泄露堆地址

利用unsorted bin残留的main_arena地址泄露堆基址csdn.net:

# 释放一个chunk进入unsorted bin free(0) # 利用UAF或输出功能读取fd/bk指针,获取main_arena地址 # main_arena地址 = 泄露的地址 - 96 # libc基址 = main_arena地址 - 0x10
技巧3:控制chunk排列

通过交替分配不同大小的chunk,控制堆块排列顺序csdn.net:

# 分配两组不同大小的chunk,制造可控排列 malloc(0x20) # chunk0 malloc(0x80) # chunk1 malloc(0x20) # chunk2 malloc(0x80) # chunk3 # 释放chunk0和chunk2,它们会进入fastbin # 此时堆布局:[free chunk0] [chunk1] [free chunk2] [chunk3] # fastbin: chunk2 -> chunk0 -> NULL

<details> <summary>📖 堆风水高级应用:babyfengshui案例分析</summary>

题目背景:清华蓝莲花战队的babyfengshui题目,存在堆溢出漏洞csdn.net。

漏洞分析

  • 程序管理用户描述和名称,使用两个堆块:一个存储描述,一个存储用户结构
  • update功能中检查描述长度不能覆盖到名称堆块,但假设两个堆块连续csdn.net
  • 漏洞:如果描述堆块大小可控,可以使其不与名称堆块连续,从而溢出覆盖下一个用户结构

堆风水利用

  1. 分配多个小描述堆块(0x20),制造fastbin空闲块
  2. 释放部分用户,在堆中制造间隔
  3. 分配新用户,描述大小为0x80,此时描述堆块与名称堆块不连续
  4. 通过溢出修改下一个用户的描述指针为GOT表地址
  5. 显示功能泄露GOT表内容,获取libc地址
  6. 修改free的GOT表为system,释放"/bin/sh"字符串获取shell

关键点:通过堆风水控制堆块排列,使溢出能够覆盖目标数据csdn.net。

</details>

3. 实战演练:堆风水基础应用

3.1 环境准备

# 安装pwntools和pwndbg pip3 install pwntools git clone https://github.com/pwndbg/pwndbg cd pwndbg ./setup.sh

3.2 基础堆风水脚本

from pwn import * # 初始化环境 context.arch = 'amd64' context.os = 'linux' context.log_level = 'debug' # 加载目标程序 p = process('./heap_fengshui') elf = ELF('./heap_fengshui') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') def malloc(size, content): p.sendlineafter(b'choice: ', b'1') p.sendlineafter(b'size: ', str(size).encode()) p.sendlineafter(b'content: ', content) def free(index): p.sendlineafter(b'choice: ', b'2') p.sendlineafter(b'index: ', str(index).encode()) def show(index): p.sendlineafter(b'choice: ', b'3') p.sendlineafter(b'index: ', str(index).encode()) # 1. 堆风水布局:创建特定排列的堆块 # 目标:在堆中创建一个可控区域,用于后续溢出覆盖 # 分配多个小chunk,填充fastbin for i in range(7): malloc(0x20, b'A' * 0x20) # 释放部分chunk,制造空闲块 for i in range(0, 7, 2): free(i) # 此时fastbin状态:[6] -> [4] -> [2] -> [0] -> NULL # 堆布局:[free 0] [1] [free 2] [3] [free 4] [5] [free 6] # 2. 分配目标chunk,控制其位置 # 我们希望目标chunk位于两个空闲chunk之间,便于溢出 malloc(0x80, b'B' * 0x80) # 这个chunk会从unsorted bin分配,位置可控 # 3. 利用漏洞(假设有堆溢出) # 通过溢出修改相邻chunk的元数据 # 这里模拟一个溢出漏洞 payload = b'C' * 0x20 # 填充当前chunk payload += p64(0) + p64(0x31) # 伪造下一个chunk的头部 payload += p64(0x4141414141414141) # 伪造fd指针 payload += p64(0x4242424242424242) # 伪造bk指针 # 发送溢出payload p.sendlineafter(b'choice: ', b'4') p.sendlineafter(b'index: ', b'7') p.sendlineafter(b'size: ', str(len(payload)).encode()) p.sendlineafter(b'content: ', payload) # 4. 验证堆布局 # 使用GDB检查堆状态 # gdb.attach(p) # pause()

3.3 调试与验证

使用GDB + pwndbg检查堆布局:

# 在GDB中 pwndbg> heap pwndbg> vis pwndbg> bins

预期输出

Heap Dump: 0x555555559000: 0x0000000000000000 0x0000000000000031 [chunk0] 0x555555559010: 0x4141414141414141 0x4141414141414141 0x555555559020: 0x0000000000000000 0x0000000000000031 [chunk1] 0x555555559030: 0x4242424242424242 0x4242424242424242 ...

4. 总结与预告

4.1 核心知识点总结

  1. glibc堆结构是堆利用的基础,不同bin类型管理不同大小的chunkcsdn.net+1
  2. Tcache机制改变了堆漏洞利用格局,提供了新的攻击面csdn.net+1
  3. 堆风水是所有堆题稳定利用的前提,通过控制内存布局为漏洞利用创造条件csdn.net+1
  4. 堆风水核心是精确控制堆块分配释放顺序,使目标数据位于可控区域csdn.net

4.2 易错点与注意事项

  1. 不同glibc版本差异:Tcache行为、安全检查机制不同csdn.net
  2. 堆布局不可预测:ASLR、堆随机化使堆地址难以预测,需信息泄露csdn.net
  3. chunk对齐:64位系统chunk大小按0x10对齐csdn.net
  4. 合并机制:释放chunk时可能合并相邻空闲chunkcsdn.net

4.3 进阶学习路径

<details> <summary>📚 推荐学习路径</summary>

  1. 源码分析:精读glibc malloc源码,理解分配释放流程csdn.net
  2. 漏洞利用技术:学习UAF、Double Free、Fastbin Attack等具体技术csdn.net+1
  3. 高级利用技术:掌握House of系列、Tcache Poisoning等高级技术csdn.net+1
  4. 实战练习:通过CTF题目巩固知识,如babyfengshui、heapcreator等csdn.net

</details>

4.4 下篇预告

下一篇我们将深入学习UAF漏洞利用,包括:

  • UAF漏洞原理与利用条件
  • Double Free传统版与Tcache版
  • 伪造堆块技术
  • Tcache Poisoning原理与实战
  • 实战案例:利用UAF获取shell

📊 知识图谱总结

glibc堆结构与堆风水堆结构精读chunk结构:prev_size:size:fd/bk指针bin类型0x20-0x800x20-0x3F8≥0x400:Unsorted BinTcache机制:线程缓存:每个size最多7个:优先分配释放堆风水方法论核心思想:控制内存布局:创造可预测环境基本步骤:分析利用需求:设计堆布局:实现堆操控:验证布局实战技巧:堆喷射:堆整形:碎片化实战应用环境准备:pwntools:pwndbg基础脚本:堆布局控制:漏洞触发调试验证:GDB检查:堆可视化

最终结论:glibc堆结构和堆风水是堆漏洞利用的基石。理解堆管理机制和内存布局控制技术,是掌握堆利用的关键。在接下来的学习中,我们将深入探讨具体的漏洞利用技术,逐步构建完整的堆利用知识体系csdn.net+1。

参考文献

  1. 掌握Glibc 2.26 tcache:内存管理新利用与攻防策略
  2. 堆风水:内存布局操控与漏洞利用的高级技术
  3. Heap Exploitation
  4. glibc堆概述(笔记2)
  5. 堆深入学习记录2 | 青训营
  6. 关于堆的一些数据结构(个人笔记)
  7. 别再只盯着Fastbin了:聊聊glibc 2.26引入Tcache后,堆漏洞利用格局的’新常态’与防御思路
  8. 【学习笔记】CTF PWN选手的养成(三)