Heap 与 Bins 命令
1. heap 命令的原理:顺藤摸瓜(线性遍历)
pwndbg 的 heap 命令是个“老实人”。它解析堆的方法是从堆的最开头(Heap Base)开始,挨个读取每一个 chunk 的 Size 字段。
- 读到 Chunk A,Size 是
0x290,于是它跳到Addr + 0x290去找 Chunk B。 - 读到 Chunk B,Size 是
0x20,于是它跳到Addr + 0x20去找 Chunk C。 - 依此类推,直到找到 Top Chunk 为止。
2. 致命的断链:为什么卡在 0x...9d20?
在你执行 free(3) 触发 向后合并(Backward Consolidation) 之前,堆的结构是完好的,heap 命令能一路顺藤摸瓜。
但是,free(3) 引发了一场“大地震”。 glibc 把从 0x...9d00(原先伪造头部附近)一直到被释放的 Chunk 3 结尾,这一大片长达 0xc90 字节的内存,强行捏合成了一个巨型 Unsorted Bin 块。
来看看 heap 命令执行到这里时遭遇了什么:
- 它读到了
0x5634b33a98d0这里的 chunk,Size 是0x450。 - 它顺理成章地加上
0x450,跳到了下一个地址:0x5634b33a9d20。 - 它试图读取
0x5634b33a9d20这里的 Size,结果发现是0x00!
为什么会是 0x00? 因为在这个由 free(3) 触发的巨型合并中,这块区域已经被新的堆结构覆盖,或者原来的伪造 Size 已经被破坏了。对于 pwndbg 的 heap 命令来说,读到 Size: 0x00 是一个致命错误——这意味着它没法继续往下跳了(如果往下跳就会原地踏步,死循环)。为了防止 GDB 崩溃,heap 命令只能无奈选择在此刻停止输出。
所以,这实际上说明正常的堆块单向链表已经被你的攻击完全破坏了,出现了严重的空间重叠。
3. bins 命令的原理:直捣黄龙(直接读 main_arena)
既然 heap 命令瞎了,为什么 bins 还能准确报出 Unsorted Bin 的位置?
因为 bins 命令是个“走捷径”的聪明人。它根本不看堆上的 Chunk 链表是怎么连的,它直接去读 Libc 里面的 main_arena 结构体!
glibc 在完成大合并后,会把这个巨型空闲块的地址挂在 main_arena.bins[1](也就是 Unsorted Bin 的链表头)上。 bins 命令读取了 libc 的内存,发现 main_arena 指向了堆上的 0x5634b33a9d00,于是它就直接把这个地址打印出来了: all: 0x5634b33a9d00 —▸ 0x7fd37e0aec00
更新: 2026-04-26 19:20:43
原文: https://www.yuque.com/idcm/wnemg9/nkan4n9g6fpokq9w