Windows堆溢出浅讲
Windows堆溢出浅讲
堆表
堆表分为空表free
和快表lookaside
组成。
空表
free
数组共有$128$项,每一项是个双向列表的下一项地址。free[1]
链接了所有大小为$8$字节的堆块,free[2]
链接大小为$16$字节的,以此类推到free[127]
链接大小为$1016$字节的堆块。free[0]
从小到大链接剩余的大于$1016$字节大小的堆块。
快表
这玩意儿跟空表差不多,但每一项最多链接$4$个堆块,且都被设置为占用态,不能被合并。
堆块分配
快表分配时必须精确分配。普通空表最优大小查找失败时会查找次优堆块。零号空表free[0]
从大到小倒着找合适大小。
堆块合并
小块大小小于$1\mathrm{KB}$,大块大小在$[1,512)\mathrm{KB}$之间,巨块在$512\mathrm{KB}$及以上。
小块分配:快表、普通空表、堆缓存、零号空表、内存紧缩、NULL
小块释放:快表、空表
大块分配:堆缓存、零号空表
大块释放:堆缓存、零号空表
巨快分配:虚分配
巨快释放:直接释放
堆结构
这里使用HeapCreate
创建堆,创建堆底层都为RtlAllocateHeap
。调试堆时不能直接在调试态运行,否则策略差别很大,应手动插入int 3
终端后Attach上。HeapCreate
和malloc
、GetProcessHeap
都分别返回各自设定的堆区。堆区偏移0x178是空表索引区。当堆区刚刚初始化后,只有一个空闲态的大块叫“尾块”,零号空表指向这玩意儿。这第一个“尾块”位于堆区偏移0x688处,当堆设定为可扩展的HeapCreate(0,0,0);
时启用快表,快表就在0x688处。
占用态堆块结构:
1 | 8byte Block head |
空闲态在$8$字节的Block head后面还有$8$字节,分别是一个$4$字节的Flink in freelist和一个$4$字节的Blink in freelist。
堆块分配
堆块大小为申请的大小加上$8$字节的Block head,一个堆单位为$8$字节,不足的按$8$字节算。
DWORD SHOOT
当某堆块要被从双向链表中卸下来时,使用堆溢出方式改写该块的Flink和Blink。进行卸载操作时会向Flink指向的地址写入$4$字节的Blink的数据,造成任意内存位置$4$字节写操作。