利用jemalloc内存分配,控制Firefox堆。
字数 1769 2025-08-20 18:18:04
jemalloc内存分配器利用技术详解
1. jemalloc概述
jemalloc是一个高性能的用户空间内存分配器,被广泛应用于多个重要项目中:
- Mozilla Firefox在Windows、Mac OS X和Linux平台上使用
- FreeBSD和NetBSD操作系统的默认系统分配器
- Facebook在其Web服务组件中使用
jemalloc的设计重点在于:
- 基于局部性原则(分配在一起的项目一起使用),尽量连续分配内存
- 通过多arena设计减少多线程环境下的锁竞争问题
- 提高从RAM中检索数据的性能,而非最小化页面利用率
2. jemalloc核心架构
2.1 主要数据结构
Chunks
- 大块虚拟内存区域,大小固定(Firefox中为1MB,FreeBSD libc中为2MB)
- 由
arena_chunk_t结构描述 - 用于存储所有其他数据结构及用户请求的内存
Arenas
- 管理chunks和底层页面的内存区域
- 用于缓解多线程间的锁竞争问题
- 线程的分配/释放总是在同一个arena中进行
- 数量取决于系统配置(单CPU系统1个,SMP系统为CPU核心数的2-4倍)
- Firefox中只有一个arena
Runs
- chunks的进一步划分单位
- 一组连续页面(至少1页),与页面大小倍数对齐
- 负责跟踪regions(用户内存分配)的状态(空闲/使用)
- 使用位掩码跟踪状态(位掩码是run元数据的一部分)
Regions
- malloc()返回给用户的内存区域
- 按大小分为三类:
- Small/medium:小于页面大小
- Large:大于页面但小于chunk大小减去头部
- Huge:大于chunk大小减去头部
Bins
- 组织空闲regions的结构
- 通过runs管理空闲regions
- 每个bin关联特定大小类,管理该大小的regions
- 包含"current run"(最近使用的run)和空闲regions的runs树
2.2 数据结构关系
Arenas → Chunks → Runs → Regions
↑
Bins
3. 利用原语
jemalloc没有使用传统的"unlinking"或"frontlinking"机制,因此需要开发新的利用方法:
3.1 相邻区域覆盖
原理:
- 相同大小类的regions放在同一bin的同一run中,且之间无元数据
- 通过堆风水(Heap Feng Shui)控制堆布局
- 将目标对象与易受攻击对象放在同一run中相邻位置
步骤:
- 执行大量分配以覆盖现有run中的空闲区域或获取新run
- 执行受控分配(目标对象)
- 每隔一个释放目标对象,在run中创建"洞"
- 触发堆溢出,使易受攻击对象溢出到目标对象
3.2 Run头损坏
场景:
- 溢出run的最后一项,覆盖下一个run的头部
- 溢出run应服务于任何可用bins的regions
利用:
- 溢出run#1的最后一项,覆盖run#2头部
- 分配大小等于run#2服务的大小的内存
- 返回的指针将指向run#1的内存区域
3.3 Chunk头损坏
场景:
- 通过控制堆分配强制分配新arena
- 在前一个arena的最后一个region(与新arena相邻)触发溢出
- 破坏chunk头的元数据
效果:
- 释放新arena的任何region时,堆管理信息被更改
- 下次malloc()返回的region将指向前一个arena的已分配空间
4. Firefox案例研究
4.1 调试工具unmask_jemalloc
- 基于GDB Python脚本实现
- 支持Linux 32/64位Firefox目标
- 功能包括查看arenas、chunks、runs、bins等内部状态
4.2 堆操作技术
JavaScript堆喷示例:
function jemalloc_spray(blocks, size) {
var block_size = size / 2;
var marker = unescape("%ubeef%udead");
marker += marker;
var content = unescape("%u6666%u6666");
while(content.length < (block_size/2)) { content += content; }
var arr = [];
for(i=0; i<blocks; i++) {
// 构造随机填充块
var rndstr = "%u" + rnd1.toString() + rnd2.toString();
rndstr += "%u" + rnd3.toString() + rnd4.toString();
var padding = unescape(rndstr);
// 构造块并喷入堆
var block = marker + content + padding;
arr[i] = block.substr(0);
}
// 每隔一个释放块
for(i=0; i<blocks; i+=2) {
delete(arr[i]);
arr[i] = null;
}
// 触发垃圾回收
var ret = trigger_gc();
// 重新填充释放的块
// ...
return arr;
}
关键点:
- 使用未转义字符串和数组进行受控分配
- 实现随机填充以绕过传统堆喷缓解
- 通过delete和null显式释放内存
- 强制垃圾回收以稳定堆状态
5. 结论
jemalloc利用技术的关键在于:
- 理解其多arena、chunk-run-region的层次结构
- 通过堆风水控制内存布局
- 利用相邻区域覆盖、run头损坏或chunk头损坏实现利用
- 在Firefox中结合JavaScript堆操作技术实现可靠利用
unmask_jemalloc工具可帮助研究人员深入分析jemalloc内部状态,辅助漏洞利用开发。