Windows 10 x86/wow64用户态堆
引言
在过去几周中,我接到一些亲戚的"紧急"电话,要求我检查他们的电脑,因为"东西坏了"、“看起来不一样了"和"我觉得电脑被黑了”。我很快意识到他们的电脑已升级到Windows 10。
本文收集了关于Windows 10中32位进程用户态堆管理器行为的笔记。调查主要目的是记录与Windows 7的相似性和差异性,并希望提出一些操作堆以增加行为可预测性的方法。
测试环境
- Windows 10 Enterprise x64,完全补丁
- Visual Studio Express 2015 for desktop
- WinDBG for Windows 10
堆结构
与之前的Windows版本类似,Windows 10堆位于受ASLR影响的地址,并以头结构开始。堆管理单元的基地址位于非静态地址。
堆头分析
|
|
偏移0x4c和0x50用于存储堆中块头编码信息。VirtualMemoryThreshold字段包含值0xfe00,表示块数,需要乘以8得到实际字节数(0x7F000字节)。
后端分配器
后端分配器是用于管理空闲块的默认/活动机制。它使用段内的可用块,开始时为空。当块被释放时,它会以某种列表形式"记住"这些空闲块。
BEA行为测试
通过一系列测试应用程序评估其在Windows 10上的行为:
BEA_Alloc1: 分配2个0x300字节的块,检查它们是否相邻放置,释放后是否合并,以及新分配时是否分割。
BEA_Alloc2: 创建一系列0x300字节分配,释放最后一个,然后导致2个0x100字节分配,检查这些分配的位置。
BEA_Alloc3: 释放0x58字节块,目标是使用0x58字节分配重新占据其位置。
BEA_Alloc4: 释放0x58字节块,尝试使用0x80字节分配控制原始0x58字节块的前4个双字。
前端分配器 - LFH
LFH激活条件
测试表明,在Windows 10中,仍然需要18次连续分配才能激活LFH:
- 分配1-17:非LFH管理
- 分配18+:LFH管理
测试还表明:
- 在分配系列中插入不同大小的分配不会影响LFH触发
- 释放不同大小的块不会影响LFH触发
- 释放相同桶大小的块不会影响LFH触发
LFH行为变化
与Windows 7相比,Windows 10中的LFH行为有所变化:
- 块不再在子段内连续分配
- LIFO行为不再一致
- 取回已释放块所需的分配次数变化较大(0-50次)
LFH大小限制
与Windows 7一样,LFH仍然限制为最大0x4000字节块。
大块分配
VirtualAllocdBlocks
通过分配大于堆头中VirtualMemoryThreshold值的大小,仍然可以触发VirtualAllocdBlocks分配。
精确堆喷射
通过避免LFH和VirtualAllocdBlocks,使用"合适"的大小和分配数量,仍然可以在普通段内获得对齐的连续分配。
关键技术:
- 使用对齐的块大小(页大小的倍数)
- 在每个分配中每0x1000字节重复相同结构
- 处理应用程序中的"噪音"分配
结论
Windows 10堆管理在保持与之前版本基本架构的同时,引入了重要的行为变化,特别是在LFH方面。了解这些变化对于开发可靠的利用技术至关重要。
通过精心设计的堆布局和分配策略,仍然可以在Windows 10上实现可预测的堆操作和精确的堆喷射。