pwndbg arenas 命令详解:在 GDB/LLDB 中枚举 glibc ptmalloc2 全部分配区(Arena)
【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB & LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg
导读
本文聚焦 pwndbg 的arenas命令——一条用于**枚举当前进程中 glibc ptmalloc2 全部内存分配区(malloc arena)**的调试命令。在漏洞利用(exploit development)与逆向工程中,多线程程序的堆布局远比单线程复杂:每个 arena 各自维护独立的 bins、fastbins 与 top chunk,而arenas命令能帮你一眼看清进程内到底存在哪些 arena、每个 arena 归属哪个堆映射。读完本文,你将掌握arenas与配套的arena命令的用法、输出字段含义,以及 pwndbg 从符号与启发式两条路径解析 arena 链表的底层原理。
arenas命令:快速定位进程内全部分配区
用法与参数
arenas是 pwndbg 内置的 glibc 堆调试命令之一,用法极其简洁:
usage: arenas [-h]它不接收任何位置参数,唯一的可选项是标准的帮助参数:
| Short | Long | Help |
|---|---|---|
| -h | --help | show this help message and exit |
命令的官方描述只有一句话:"List this process's arenas."(列出当前进程的所有分配区)。命令入口定义在 pwndbg/commands/glibc_malloc.py,通过@pwndbg.commands.Command(parser, category=CommandCategory.GLIBC_MALLOC)注册到GLIBC_MALLOC命令分类,并带有@OnlyForSaneHeap装饰器——这意味着当目标进程的堆状态异常(无法构建堆对象)时,命令会安全退出而不是崩溃。
适用前提:
arenas针对 glibc 的 ptmalloc2 分配器实现,适用于动态链接了标准 glibc 的目标进程。若目标使用 jemalloc、musl 或 mallocng 等其他分配器,arenas不会列出其内部结构(这些分配器由 pwndbg 的jemalloc、musl、mallocng等命令族负责)。
命令调用链
在 GDB 或 LLDB 会话中,当程序运行到断点处时直接执行:
pwndbg> arenas命令内部的核心逻辑是:
allocator = pwndbg.aglib.heap.glibc.get_allocator() arenas = allocator.arenas即通过get_allocator()获取当前进程对应的 glibc 分配器实例(详见 pwndbg/aglib/heap/glibc.py),再由分配器的arenas属性取得全部 arena 列表。
输出格式与字段解读
arenas的输出是一张右对齐的表格,表头包含 9 个字段:
arena type arena address heap address map start map end perm size offset file| 字段 | 含义 |
|---|---|
| arena type | main_arena(主分配区)或non-main arena(非主分配区) |
| arena address | malloc_state结构体在内存中的地址 |
| heap address | 该 arena 关联的堆段起始地址 |
| map start / map end | 堆内存映射区间的起止地址 |
| perm | 映射区权限(如rw-p) |
| size | 映射区间大小 |
| offset | 映射相对文件偏移 |
| file | 映射来源文件 |
关键实现细节(见 pwndbg/commands/glibc_malloc.py):
- arena type 着色区分:主分配区
main_arena用成功色(绿色)标注,非主分配区non-main arena用提示色(黄色)标注,便于一眼区分。 - heap 地址取第一个堆段:
first_heap = arena.heaps[0],展示该 arena 的heaps列表中的第一个堆段起始地址。 - vmmap 信息联查:
pwndbg.aglib.vmmap.find(first_heap.start)会去查询该堆地址所属的内存映射,返回的映射文本按空格拆分后逐列填入map start / map end / perm / size / offset / file,与 pwndbg 的vmmap命令共用同一套映射信息。 - 多堆段折叠展示:如果一个非主 arena 拥有多个堆段(例如堆通过
mmap扩展出第二个 segment),则后续堆段以空 arena type 单元格加 Unicode 箭头↳的子行展示,箭头指示该堆段归属于上方同一 arena。
这一输出设计让你在多线程 + 堆溢出场景中,能快速判断:当前进程有几个分配区、哪个是主 arena、各 arena 的堆在哪里、堆映射的权限与大小是否异常(例如权限位被篡改、堆越界扩展)。
与arena命令的分工:看单个 vs 看全部
arenas展示全局,而配套命令arena展示单个分配区的完整内部状态,两者共同构成 arena 调试的完整闭环。
arena的用法为:
usage: arena [-h] [addr]- 不带参数时,默认打印当前线程所属的 arena,并先输出提示信息:
Arena for thread <tid> is located at: 0x7ffff7fa2c80- 带地址参数时,直接打印该地址处
malloc_state结构体的完整内容:
pwndbg> arena 0x7ffff7fa2c80其完整字段输出包括mutex、flags、have_fastchunks、fastbinsY、top、bins、binmap、next、next_free、system_mem等 glibcmalloc_state的全部字段(字段解析见 pwndbg/aglib/heap/glibc.py 的Arena类)。
两者的典型配合用法:
- 先用
arenas看到进程内全部 arena 及其堆位置; - 再用
arena <address>深入某个目标分配区,检查其fastbinsY/bins/top/system_mem等内部状态; - 结合 vis-heap-chunks.md、bins.md、top-chunk.md 等命令完成堆布局的进一步可视化。
底层原理:pwndbg 如何枚举全部 arena
arenas命令只是视图层,真正的枚举逻辑位于分配器的arenas属性(pwndbg/aglib/heap/glibc.py):
@property @pwndbg.lib.cache.cache_until("stop") def arenas(self) -> tuple[Arena, ...]: """Return a tuple of all current arenas.""" arenas: list[Arena] = [] main_arena = self.main_arena if main_arena: arenas.append(main_arena) arena = main_arena addr = arena.next while addr is not None and addr != main_arena.address: arena = Arena(addr) arenas.append(arena) addr = arena.next self._arenas = tuple(arenas) return self._arenas枚举算法清晰地复刻了 glibc 的 arena 链表结构:
- 先取
main_arena:主分配区是链表头; - 沿
next指针遍历:glibc 中每个非主 arena 的malloc_state.next指向下一个 arena,直到回到main_arena自身(环形链表); - 缓存到 "stop" 事件:
@pwndbg.lib.cache.cache_until("stop")表示结果缓存在程序停止(stop)事件上,程序继续运行后缓存自动失效,避免陈旧堆信息。
两条解析路径:DebugSymsHeap 与 HeuristicHeap
pwndbg 的 glibc 分配器分为两种实现,main_arena与thread_arena的解析方式也因此不同:
路径一:DebugSymsHeap(libc 带调试符号)
当 libc 带有调试符号时(pwndbg.libc.has_debug_info()为真),直接通过符号表定位:
@property def main_arena(self) -> Arena | None: self._main_arena_addr = pwndbg.aglib.symbol.lookup_symbol_addr( "main_arena", prefer_static=True ) ... return self._main_arena即从符号main_arena得到其地址(prefer_static=True优先取静态符号,避免被 GOT/PLT 干扰),再以该地址构建Arena对象。thread_arena同理,通过thread_arena符号读取指针值(可能为 NULL,表示该线程尚未分配任何内存,见 pwndbg/aglib/heap/glibc.py)。
路径二:HeuristicHeap(无调试符号,启发式)
当 libc 没有调试符号但能确定版本时,pwndbg 使用运行时生成的 C 结构体(glibc_structs)做启发式解析。main_arena的定位策略是:
- 优先用符号
main_arena(静态链接或已 unstrip 的场景); - 找不到符号时,在
.data段中按 glibc 默认main_arena的初始化模式做模式匹配——glibc 中main_arena初始为{ .mutex = _LIBC_LOCK_INITIALIZER, .next = &main_arena, .attached_threads = 1 },pwndbg 按此特征(next自指 +attached_threads = 1)在数据段中搜索匹配的malloc_state结构(见 pwndbg/aglib/heap/glibc.py)。
此外,Arena.is_main_arena的判断也很直接:self.address == _allocator.main_arena.address(pwndbg/aglib/heap/glibc.py),这与命令输出中arena type列的着色逻辑一一对应。
多堆段的heaps组织
Arena.heaps(pwndbg/aglib/heap/glibc.py)负责收集一个 arena 关联的全部堆段:
- 主 arena 优先用 sbrk 堆区(
get_sbrk_heap_region()),若top不在 sbrk 区域内则额外补一个堆段; - 非主 arena 沿
Heap.prev指针向前回溯,把各历史 mmap 堆段全部收进列表; - 列表最后反转,保证堆段按地址/时间顺序排列,
heaps[0]即最早建立的堆段。
这解释了arenas输出中第一行取arena.heaps[0]、后续堆段用↳子行展示的行为。
实战场景与使用建议
场景一:多线程程序堆布局侦察
对一个多线程二进制,程序运行一段时间后执行:
pwndbg> arenas如果看到多个non-main arena行,说明进程已为多个线程创建了独立分配区(glibc 默认 arena 数量上限约为核数的 8 倍)。此时配合thread apply all bt可把"哪些线程持有哪些 arena"对应起来,定位堆风水(heap feng shui)布局的目标 arena。
场景二:堆溢出后定位被破坏的 arena
在堆利用(如 fastbin attack、unsorted bin attack)中,常需要篡改某个 arena 的top、bins或fastbinsY。先arenas拿到目标 arena 地址,再arena <addr>查看字段,最后用 xinfo 或 search 反向验证篡改效果。
场景三:堆映射权限检查
arenas输出的perm列直接来自 vmmap 映射信息。若发现堆段权限异常(如堆被 mprotect 成r-x),可快速定位是哪一段堆映射被改,再结合 vmmap 的详细映射列表交叉验证。
注意事项
arenas依赖@OnlyForSaneHeap,当堆结构损坏到无法构建堆对象时命令会静默返回,此时应先修复堆状态或用heap命令诊断(见 heap.md);- 结果缓存在 stop 事件上:若你在一次停止中反复调用
malloc(例如通过gdb.execute('call malloc(...)')执行 inferior call),堆可能因brk/mmap扩展而超出缓存的内存映射范围。pwndbg 已通过监听inferior_call事件在 inferior 函数调用完成后清理 stop 缓存来解决此问题,对应回归测试见 tests/library/gdb/tests/heap/test_inferior_call_cache.py(该测试正是验证thread_arena在多次 inferiormalloc后仍可访问); arenas列出的是 arena 的malloc_state结构,而非单个 chunk;chunk 级视图请使用 malloc-chunk.md 与 vis-heap-chunks.md。
小结
arenas命令以一张 9 列表格,把 glibc ptmalloc2 中散落在各处的 arena 及其堆映射汇总到眼前,是分析多线程堆布局的第一站。其背后是对 glibc arena 环形链表、malloc_state结构、vmmap 映射信息的系统解析,并同时支持带调试符号的DebugSymsHeap路径与无符号的HeuristicHeap启发式路径。配合单分配区查看命令arena(见 arena.md)以及 bins.md、fastbins.md、unsortedbin.md 等命令,即可在 GDB/LLDB 中完成从"进程有几个堆分配区"到"某个 bins 里挂着哪些 chunk"的全链路堆调试。
【免费下载链接】pwndbgExploit Development and Reverse Engineering with GDB & LLDB Made Easy项目地址: https://gitcode.com/GitHub_Trending/pw/pwndbg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考