Windows 10 x86/wow64用户态堆
引言
在过去几周中,我多次收到亲友的"紧急"求助,称其计算机"出现问题"、“界面异常"或"疑似被黑”。经排查发现,这些计算机均被升级至Windows 10。尽管可讨论强制升级策略,但显然Win10市场份额正在增长,使其成为值得深入研究的技术目标。
本文汇集了关于Windows 10中32位进程用户态堆管理器行为的笔记,重点记录与Windows 7的异同,并探讨如何操纵堆行为以提高可预测性。具体关注以下问题:
- 后端分配器行为特征
- 激活LFH(低碎片堆)的条件
- Win7与Win10的LFH行为差异
- 创建特定堆布局(同尺寸/不同尺寸对象相邻)的方法
- 实现可靠精确堆喷洒的可能性
需要说明的是,本文基于观测记录而非ntdll.dll逆向工程,且测试样本有限,结论未必完全准确。希望这些笔记能激励更多人进行测试和代码逆向。
测试环境
- Windows 10 Enterprise x64(VirtualBox虚拟机,2CPU/1.8GB内存)
- Visual Studio Express 2015桌面版
- WinDBG for Windows 10(配置符号路径:_NT_SYMBOL_PATH=srvc:\symbolshttp://msdl.microsoft.com/download/symbols)
测试代码详见:https://github.com/corelan/win10_heap
堆结构分析
与早期版本类似,Windows 10堆位于ASLR随机化地址,以头部结构起始。关键字段包括:
- EncodeFlagMask (0x4c) 和 Encoding (0x50):块头编码信息(每个进程/堆使用独立XOR密钥)
- VirtualMemoryThreshold (0x5c):值为0xfe00(0x7F000字节),超过此值的分配将触发VirtualAllocdBlocks
- FrontEndHeapType (0xd6):值0x2表示使用LFH前端分配器
LFH头部结构(_LFH_HEAP)包含桶(Bucket)、子段(SubSegment)等管理单元。与Win7不同,Win10的LFH头部通常位于首段之外。
后端分配器(BEA)行为
BEA_Alloc1测试
分配两个0x300字节块观察相邻性:
|
|
确认从0xba0字节空闲块分割产生,剩余0x590字节空闲块:
|
|
BEA_Alloc2测试
分配10个0x300字节块(避免LFH激活),释放最后一块后分配两个0x100字节块:
|
|
BEA优先重用精确尺寸空闲块,其次分割大块。释放块未立即重用因其与相邻空闲空间合并。
BEA_Alloc3测试
创建0x100-0x58-0x100相邻布局,释放中间块后重新分配:
|
|
释放0x58字节块后,新分配成功重用相同地址并填充’B’。
BEA_Alloc4测试
尝试用0x80字节块覆盖0x58字节块的前4双字(仅控制0x80块最后4双字)。通过释放相邻块触发合并:
|
|
但数学计算偏差导致覆盖偏移误差8字节,证明原理可行但需精确计算。
前端分配器(LFH)行为
LFH激活条件
- 0x1500字节块:第18次分配激活LFH(!heap -x显示LFH标记)
- 插入异尺寸分配:中途分配0x300字节不影响0x2100字节块的LFH激活(仍需18次)
- 中途释放异尺寸块:不影响0x3000字节块的LFH激活阈值
- 释放同尺寸块:释放0x800字节块后,仍需18次分配激活LFH
结论:LFH激活阈值保持18次,且不受异尺寸操作影响。
LFH分配特性
- LIFO行为消失:释放后立即分配不一定返回相同地址
- 取回概率:统计显示最多需约50次分配才能取回释放块
- 非相邻分配:同子段内块不再连续排列
- 最大尺寸限制:仍为0x4000字节(验证0x4008字节分配未触发LFH)
LFH块替换测试
通过释放整个子段(包含漏洞块及其他可控块),可用不同尺寸LFH块替换原空间:
|
|
成功替换但需控制整个子段释放。
大块分配与堆喷洒
VirtualAllocdBlocks分配
分配0x7ffb0字节(超过阈值)触发VirtualAllocdBlocks:
|
|
地址对齐页边界,但间隔大于Win7,降低喷洒连续性。
精确堆喷洒
避免LFH和VirtualAllocdBlocks,使用对齐尺寸(如0x20000-8)创建连续分配:
|
|
通过每0x1000字节重复结构(垃圾数据+ROP+shellcode)实现精确定位:
|
|
建议先分配大块(0x1ff00字节)并保留小块防止合并,再释放大块创建"安静"喷洒环境。
结论
Windows 10堆管理在保持基础机制的同时引入重要变化:
- LFH激活:保持18次阈值但取消LIFO行为,降低可预测性
- 地址布局:LFH块不再连续,增加布局操控难度
- 堆喷洒:VirtualAllocdBlocks间隔增大,但通过尺寸控制仍可实现精确喷洒
测试代码提供实际验证方法,建议结合具体应用场景调整策略。