Windows 10 x86/wow64用户态堆内存管理
引言
在过去的几周里,我接到了一些亲戚的"紧急"电话,要求我检查他们的电脑,因为"系统出现问题"、“界面看起来不同"和"我认为电脑被黑了”。我很快意识到他们的电脑已升级到Windows 10。
我们可以就强制升级策略进行长篇讨论,但无论如何,Windows 10正在获得市场份额。这也意味着它已成为一个值得研究的Windows版本。
在本文中,我收集了关于Windows 10中32位进程用户态堆管理器行为的一些笔记。我调查的主要重点是记录与Windows 7的相似之处和差异,并希望提出一些操作堆以增加其行为可预测性的方法。
测试环境
我的测试环境包含以下组件:
- Windows 10 Enterprise x64,完全补丁(在VirtualBox中作为虚拟机运行),配备2个CPU和1.8GB RAM
- Visual Studio Express 2015 for desktop
- WinDBG(Windows 10版本)
堆结构
与之前的Windows版本类似,Windows 10堆位于受ASLR影响的(“随机”)地址,并以头结构开始。
转储堆头内容,我们可以观察到以下字段:
|
|
偏移量0x0d6(FrontEndHeapType)指示正在使用的前端分配器。值0x2指LFH(低碎片堆)。
后端分配器
在Windows 7上,后端分配器(BEA)是用于管理空闲块的默认/活动机制。根据我的观察,这在Windows 10上仍然如此。
BEA行为测试
在第一个测试应用程序中,我们分配2个0x300字节的块。这在我的简单测试应用程序中不应该是常见的对象大小,并且应该足够大以:
- 避免后端分配器在其空闲列表中已有该大小的块
- 避免后端分配器在其空闲列表中已有比该大小更大的块
- 避免LFH已对包含该大小的块的桶激活
测试结果显示,当没有确切大小的空闲块时,较大的空闲块会被分割以满足分配请求。
前端分配器 - LFH
LFH激活条件
测试表明,在Windows 10中,仍然需要18次连续分配才能激活特定大小的LFH。这种触发似乎不受以下因素影响:
- 在分配系列中发生不同桶大小的分配
- 在分配期间释放不同大小的块
- 在分配系列中释放相同桶的块
LFH分配行为
在Windows 10中,LFH管理的块在子段内不再连续分配。与Windows 7相比,这增加了创建特定布局/对象序列的复杂性。
测试还证实,LFH仍然限制为最大0x4000字节的块。
大块分配
VirtualAllocdBlocks
通过触发大于堆头中VirtualMemoryThreshold值(0x7F000字节)的HeapAlloc/RtlAllocateHeap分配,我们仍然可以创建VirtualAllocdBlock块。
然而,两个分配之间的间隙似乎比Windows 7下更大,这使得在堆喷射中使用此类分配来填充较大内存区域变得更加困难。
精确堆喷射
是的,在Windows 10下仍然可以进行精确堆喷射。关键是避免LFH和VirtualAllocdBlocks。
使用"合适"的大小和"合适"的分配数量,可以在普通段内获得对齐的连续分配(起始于????0048)。通过在每个分配中每0x1000字节重复相同的结构(垃圾数据+ROP+shellcode+垃圾数据),您应该能够将内容放置在可预测的地址。
结论
Windows 10堆管理器在保持与Windows 7相似架构的同时,引入了一些重要的行为变化:
- LFH激活条件保持不变(18次连续分配)
- LFH块在子段内不再连续分配
- 后端分配器行为基本保持不变
- 大块分配(VirtualAllocdBlocks)的间隙更大
- 通过精心选择块大小和分配策略,仍然可以实现精确堆喷射
这些发现对于在Windows 10环境下进行漏洞利用开发和堆相关研究具有重要意义。