Windows 10 x86/wow64用户态堆
引言
近期收到多起紧急求助,发现用户计算机被强制升级至Windows 10。随着市场占有率提升,Windows 10已成为值得深入研究的操作系统版本。本文记录了32位进程在Windows 10用户态堆管理器的行为特征,重点分析其与Windows 7的异同,并探讨如何通过堆操作提高行为可预测性。
核心研究问题:
- 后端分配器的行为特征
- 激活LFH(低碎片堆)的条件
- Win7与Win10的LFH行为差异
- 创建特定堆布局的技术方案
- 实现可靠精确堆喷洒的方法
测试环境
- Windows 10 Enterprise x64(VirtualBox虚拟机)
- Visual Studio Express 2015
- WinDBG for Windows 10
- 符号服务器配置:
srv*c:\symbols*http://msdl.microsoft.com/download/symbols
测试代码库:https://github.com/corelan/win10_heap
堆结构分析
Windows 10堆基址受ASLR影响,头部包含以下关键字段(通过WinDBG的dt _HEAP
命令查看):
EncodeFlagMask
(0x4c)和Encoding
(0x50):堆块头编码密钥VirtualMemoryThreshold
(0x5c):值为0xfe00(0x7F000字节),超过此值的分配将触发VirtualAllocdBlocksFrontEndHeapType
(0xd6):值为0x2表示使用LFH前端分配器
LFH堆头结构(dt _LFH_HEAP
)包含桶管理、子段分配等信息。与Win7不同,Win10的LFH堆头通常位于首段之外。
后端分配器(BEA)行为
BEA_Alloc1测试
分配两个0x300字节块时,从段尾空闲块分割获得连续内存空间:
|
|
BEA_Alloc2测试
分配10个0x300字节块后释放最后一个,该块与相邻空闲空间合并为0x12c8字节大块。后续0x100字节分配优先复用精确匹配的空闲块(009b2b08),其次使用较大空闲块(009bc338)。
BEA_Alloc3测试
通过包围策略控制堆布局:
- 分配0x100字节围栏块
- 分配填充’A’的0x58字节目标块
- 分配另一个0x100字节围栏块 释放目标块后,新0x58字节分配成功复用原地址(内容变为’B’)。
BEA_Alloc4测试
尝试通过不同尺寸块覆盖目标内存:
- 使用0x80字节块覆盖0x58字节块的前4双字
- 需要精确计算偏移量(实际测试存在8字节偏差)
- 证明通过尺寸差异实现内存控制的可行性
前端分配器(LFH)行为
LFH激活条件测试
- 标准触发:连续18次同尺寸分配后激活LFH(第18次分配显示
LFH;busy
标志) - 干扰测试:插入不同尺寸分配/释放操作不影响18次阈值
- 同桶释放:释放同尺寸块不影响激活计数
LFH分配特性
- 最大尺寸:仍限制为0x4000字节
- LIFO行为:不再严格遵循后进先出,回收时间在0-50次分配间波动
- 地址随机化:同子段内块不再连续排列
LFH回收测试(LFH_TakeBack2)
通过释放整个子段的所有块,可以实现跨桶尺寸的内存复用:
- 原0x58字节块被释放
- 分配0x88字节块成功覆盖原内存区域
- 验证了子段级回收的可行性
大块分配与堆喷洒
VirtualAllocdBlocks分配
分配超过0x7F000字节的块会触发独立虚拟内存分配:
|
|
与Win7相比,分配间隔更大且不可预测,降低了堆喷洒可靠性。
精确堆喷洒技术
关键避免LFH和VirtualAllocdBlocks,使用对齐尺寸(如0x1fff8字节)在普通段内实现连续分配:
- 前几个分配可能未对齐
- 新段首分配实现页对齐(地址结尾为0048)
- 通过每0x1000字节重复结构(垃圾数据+ROP+shellcode)实现地址无关性
测试成功在0x0c0c0c0c地址放置标记值:
|
|
结论
Windows 10堆管理在保持基础架构的同时引入重要变化:
- LFH激活阈值保持18次但分配随机性增强
- 后端分配器行为基本一致,可通过包围策略控制布局
- 大块分配间距增大,需要调整堆喷洒策略
- 精确堆喷洒仍可通过段内对齐分配实现
建议在实际利用前进行针对性测试,并考虑多线程环境中的噪声影响。通过预分配大块再释放的策略可以隔离堆喷洒区域。
免责声明:本文仅用于安全研究目的,请勿用于非法用途。