vHeap 是一个面向 GDB/pwndbg 的 glibc ptmalloc 堆可视化工具。它从正在调试的进程中读取堆状态,将 bin、chunk、allocator 管理结构和任意地址的内存内容展示在浏览器中,适合 CTF heap 题分析、调试和教学。
当前版本由两部分组成:
vheap.py:运行在 GDB/pwndbg 中的 Python 后端,负责采集堆信息和读取目标内存。frontend/:Vite + React + TypeScript 前端,负责图布局、结构体解析和内存视图。
- 显示 tcache、fastbin、unsortedbin、smallbin、largebin 和 allocated chunk。
- 显示
malloc_state、heap_info、tcache 等 ptmalloc 管理结构(取决于目标和调试符号)。 - 用指针边连接 chunk、管理结构和其他内存视图。
- 在 Inspector 中按
malloc_chunk、_IO_FILE、_IO_FILE_plus、_IO_jump_t、_IO_wide_data重新解释数据。 - 输入任意地址直接创建 raw memory dump;需要时再选择结构体类型进行 typed interpretation。
- 在底部 memory dump dock 中按调试器风格查看绝对地址、四组十六进制字节和 ASCII;不可读取的字节显示为
--。 - 所有读取操作均为只读,不会修改被调试进程。
推荐在 Linux 环境使用:
- GDB 11 或更高版本。
- 已安装并能正常工作的 pwndbg。
- Python 3.9 或更高版本,以及 pwndbg 自带的
.venv虚拟环境。 - Node.js 20.19 或更高版本(系统未安装时
setup.sh会在项目的.tools/中准备 Node.js 22)。 - pnpm 10(系统未安装时
setup.sh会在项目的.tools/中准备)。 - 使用 glibc/ptmalloc 的正在运行的用户态程序。
后端依赖位于 requirements.txt,包括 python-socketio、aiohttp 和 requests。
git clone https://github.com/XxingGoD/vheap.git
cd vheap先按照 pwndbg 官方文档完成安装。脚本会自动查找常见的 ~/pwndbg、~/pwndbg-dev 和 /opt/pwndbg 目录;也可以显式传入 pwndbg 根目录:
PWNDBG_PATH/.venv/bin/python3
直接执行(pwndbg 位于常见目录时):
./setup.sh如果 pwndbg 位于 /opt/pwndbg,执行:
./setup.sh /opt/pwndbgsetup.sh 会完成以下操作:
- 检查 GDB 版本并找到 pwndbg 的
.venv。 - 使用该虚拟环境安装并验证 Python 依赖。
- 检查 Node.js;缺少或版本过低时,在项目
.tools/中下载并校验 Node.js 22。 - 准备 pnpm 10,执行
pnpm install --frozen-lockfile和pnpm build。 - 将当前仓库的
vheap.py以幂等方式加入~/.gdbinit。
脚本不会用 sudo 修改系统包。GDB 和 pwndbg 仍需先按官方文档安装;Node/pnpm、Python 依赖、前端构建和 GDB 配置由脚本完成。首次自动下载 Node.js 时需要访问 nodejs.org。常用选项:
./setup.sh --help
./setup.sh --skip-frontend # 只安装后端依赖,不下载 Node/pnpm
./setup.sh --no-gdbinit # 不修改 ~/.gdbinit默认写入 GDB 已存在的 ~/.gdbinit;如果只使用 XDG 配置文件 ~/.config/gdb/gdbinit,脚本会选择该文件。也可以通过 GDBINIT=/path/to/gdbinit ./setup.sh 指定位置。
重新打开 GDB 后,使用下面的命令确认 vHeap 已加载:
(gdb) help vhserv
(gdb) help vhstate
(gdb) help vhstop如果没有使用 setup.sh,可以分别执行:
/opt/pwndbg/.venv/bin/python3 -m pip install -r requirements.txt
pnpm install --frozen-lockfile
pnpm build构建结果位于 vheapViews/dist/,vhserv 会优先提供该目录中的前端资源。
gdb ./challenge在 GDB 中运行程序,直到堆已经初始化。例如:
(gdb) start对于需要输入的题目,也可以使用 run、断点或 pwndbg 的其他命令停在合适的位置。
在 GDB 中执行:
(gdb) vhserv localhost 8080 --data-bytes 128然后在浏览器打开:
http://127.0.0.1:8080
参数说明:
| 参数 | 说明 |
|---|---|
host |
Web 服务监听地址,默认 localhost。 |
port |
Web 服务端口,默认 8080。 |
--data-bytes N |
每个 chunk 最多读取的 payload 字节数,默认 64,范围为 0 到 65536。 |
--no-auto-update |
不在每次 GDB stop 时自动刷新堆状态。 |
--no-structures |
不采集 ptmalloc 管理结构。 |
例如:
# 读取更多 chunk payload,便于查看 _IO_FILE
(gdb) vhserv localhost 1337 --data-bytes 256
# 关闭自动更新并隐藏管理结构
(gdb) vhserv localhost 8080 --no-auto-update --no-structures停止服务:
(gdb) vhstop服务启动时会自动采集一次状态。程序发生 malloc/free 或 GDB 停止后,可以手动执行:
(gdb) vhstate也可以在刷新时调整 payload 和管理结构设置:
(gdb) vhstate --data-bytes 256
(gdb) vhstate --data-bytes 0
(gdb) vhstate --structures
(gdb) vhstate --no-structures--data-bytes 0 只会关闭普通 chunk payload 的采集;前端的任意地址 memory view 仍可以单独发起内存读取。
- 左侧栏可以按 bin、地址、字段和关键字筛选节点。
flow和stack两种布局分别对应横向和纵向排列。- chunk、management structure、memory view 和 bin head 使用不同颜色区分。
- 点击节点可以在右侧 Inspector 查看字段、指针和原始 JSON。
- 指针字段会自动尝试连接到匹配地址的 chunk 或结构体。
选中一个 chunk 后,在 Inspector 的 reinterpret payload 中选择类型:
| 类型 | 用途 |
|---|---|
malloc_chunk |
查看 ptmalloc chunk 头、size、fd/bk 和 large-bin 链。 |
_IO_FILE |
按 glibc FILE 布局解析字段和 _IO flags。 |
_IO_FILE_plus |
在 _IO_FILE 后继续解析 vtable 指针。 |
_IO_jump_t |
查看 FILE 虚函数表指针。 |
_IO_wide_data |
查看 wide stream 的常用前缀字段。 |
结构体布局依赖目标架构和 libc 版本。64 位常见 glibc 中 _IO_FILE_plus 的 vtable 通常位于 0xd8,但制作 payload 前必须用目标环境的 ptype、pahole 或 libc 源码确认。
如果字段被标记为 unavailable,增加采集长度:
(gdb) vhstate --data-bytes 256- 在左侧
memory views区域输入十六进制或十进制地址;支持0x7ffff...、裸十六进制(如7ffff7dd18c0)和十进制。 - 可选填写
name,名称会显示在主图、memory 列表、Dump 标题和 Inspector 中,也可以在 Inspector 内后改名。 interpretation默认是raw memory dump,不需要选择或创建结构体;这会直接查看地址范围内的原始字节。- 可选地填写读取字节数;raw dump 默认读取
0x100字节,选择结构体时默认使用该布局的预期大小。 - 点击
open dump。只有主动选择malloc_chunk、_IO_FILE等布局时,按钮才会变为parse address并显示字段解释。
raw dump 和 typed view 都会在主图创建一个 memory 节点并打开底部 memory panel;raw dump 不解析结构字段,也不会生成 typed pointer 边。同一个地址和类型再次提交时会更新原节点,不会创建重复节点。实时读取发出后会立即打开 memory panel;GDB 超时、断线或拒绝读取时,错误会保留在该视图中并可以重新刷新。
选中 memory 节点后,画布底部会打开 memory region dock:
- 每行固定 16 个字节,左侧显示 64 位/32 位格式化地址。
- 中间按 4 字节分组显示
00到0f,右侧显示可打印 ASCII,控制字符显示为.。 - 点击地址栏的跳转按钮,或使用左右翻页按钮,可以复用当前结构体类型浏览相邻内存页。
- 点击字节会高亮并显示该字节的绝对地址和值,支持复制值。
- 目标内存不可读或返回不完整时,对应单元格显示
--,视图保留错误和范围状态。 - dock 支持切换已有 memory view、刷新、折叠和关闭。
单次任意地址读取最多 0x10000 字节,且只读目标内存。
TypeScript 前端自带 demo snapshot,可以先验证界面和内存视图:
pnpm install --frozen-lockfile
pnpm dev打开:
http://127.0.0.1:5173/?demo=1
在左侧输入以下示例:
address: 0x2000
type: _IO_FILE_plus
bytes: 224
点击 parse address 后,可以同时查看 typed memory 节点、Inspector 字段和底部原始内存区域。Demo 不连接 GDB,也不会读取或修改本机内存。
在仓库根目录执行:
# 安装依赖
pnpm install --frozen-lockfile
# 启动 Vite 开发服务器,默认端口 5173
pnpm dev
# 严格 TypeScript 检查
pnpm typecheck
# 生成生产构建到 vheapViews/dist
pnpm build
# 预览生产构建
pnpm preview开发服务器会将 /socket.io 代理到 127.0.0.1:8080。因此要在开发模式测试真实 GDB 数据,先在 GDB 中运行:
(gdb) vhserv localhost 8080确认目标程序已经运行到堆初始化之后,并在 GDB 中执行:
(gdb) vhstate同时检查浏览器地址中的端口是否与 vhserv 使用的端口一致。
确认 GDB 中的服务仍在运行,并让目标程序停在断点或停止状态后再读取。任意地址读取通过 GDB post_event 在 GDB 主线程执行;目标程序正在运行、没有选中的 inferior、地址未映射或服务端口不一致时,视图会显示具体错误。可以点击 memory panel 的刷新按钮重试。
当前前端支持基于 Chromium 的 Microsoft Edge。旧版 EdgeHTML 不支持前端使用的 BigInt 地址处理,不在支持范围内。
升级后首次访问如果仍是旧界面,执行一次强制刷新;新版本服务会对入口页面发送 Cache-Control: no-store,后续构建更新不会继续复用旧入口。管理结构数量为 0 时,在 GDB 中重新采集:
(gdb) vhstate --structures管理结构依赖 pwndbg 版本和目标 libc 调试符号。命令执行成功但仍为 0 时,普通 chunk 视图仍可使用;这通常表示当前目标没有暴露可解析的 malloc_state、heap_info 或 tcache 对象。
普通 chunk payload 受 --data-bytes 限制。增大限制后重新采集:
(gdb) vhstate --data-bytes 256如果使用了 --data-bytes 0,普通 chunk payload 会被明确标记为 disabled。
确认 setup.sh 使用的是正确的 pwndbg 根目录,并重新启动 GDB。也可以临时手动加载:
(gdb) source /absolute/path/to/vheap/vheap.py换一个端口启动服务,并使用新端口访问:
(gdb) vhserv localhost 18080http://127.0.0.1:18080
IO 结构体是 ABI 相关的。使用目标 libc 的调试信息确认字段偏移,不要直接把其他发行版或其他版本 libc 的偏移用于利用。
vheap/
├── vheap.py # GDB/pwndbg 后端和 Socket.IO 服务
├── setup.sh # 安装 Python 依赖、构建前端并接入 gdbinit
├── requirements.txt # Python 依赖
├── frontend/
│ └── src/
│ ├── App.tsx # 应用状态和主布局
│ ├── graph.ts # React Flow 图模型和 ELK 布局
│ ├── data.ts # 快照、地址和内存字节处理
│ ├── structViews.ts # malloc_chunk/IO 结构体布局
│ ├── Inspector.tsx # 节点字段检查器
│ └── MemoryRegionView.tsx # 16 字节内存区域视图
├── testHeaps/ # 示例 heap 测试程序
└── EXTENDING.md # 数据协议和扩展说明
- 后端依赖 pwndbg 的 ptmalloc 解析器和目标进程状态。
- 管理结构采集是 best-effort;缺少符号或不兼容的 pwndbg 版本时,部分结构可能不会出现。
- 内存读取有大小上限,避免一次请求阻塞 GDB。
- vHeap 只读取目标内存,不提供写内存、修改 chunk 或执行利用的功能。
- 请只在自己拥有或获准调试的程序上使用。
数据模型、Socket.IO 事件和新增结构体类型的说明见 EXTENDING.md。
本项目使用 BSD 2-Clause License,详见 LICENSE。
