利用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中相邻位置

步骤

  1. 执行大量分配以覆盖现有run中的空闲区域或获取新run
  2. 执行受控分配(目标对象)
  3. 每隔一个释放目标对象,在run中创建"洞"
  4. 触发堆溢出,使易受攻击对象溢出到目标对象

3.2 Run头损坏

场景

  • 溢出run的最后一项,覆盖下一个run的头部
  • 溢出run应服务于任何可用bins的regions

利用

  1. 溢出run#1的最后一项,覆盖run#2头部
  2. 分配大小等于run#2服务的大小的内存
  3. 返回的指针将指向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利用技术的关键在于:

  1. 理解其多arena、chunk-run-region的层次结构
  2. 通过堆风水控制内存布局
  3. 利用相邻区域覆盖、run头损坏或chunk头损坏实现利用
  4. 在Firefox中结合JavaScript堆操作技术实现可靠利用

unmask_jemalloc工具可帮助研究人员深入分析jemalloc内部状态,辅助漏洞利用开发。

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 数据结构关系 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堆喷示例 : 关键点 : 使用未转义字符串和数组进行受控分配 实现随机填充以绕过传统堆喷缓解 通过delete和null显式释放内存 强制垃圾回收以稳定堆状态 5. 结论 jemalloc利用技术的关键在于: 理解其多arena、chunk-run-region的层次结构 通过堆风水控制内存布局 利用相邻区域覆盖、run头损坏或chunk头损坏实现利用 在Firefox中结合JavaScript堆操作技术实现可靠利用 unmask_ jemalloc工具可帮助研究人员深入分析jemalloc内部状态,辅助漏洞利用开发。