Abusing Windows Physical
字数 2481 2025-08-24 10:10:13
Windows 物理地址滥用技术深入解析
引言
本文档详细解析了Windows系统中物理地址(Physical Address)的利用技术,这种技术在多个内核漏洞利用中发挥了关键作用,如SMBleeding (CVE-2020-0796)、CVE-2020-1206和CVE-2018-1038等。通过深入理解Windows内存管理机制,攻击者可以绕过多种安全防护措施。
Windows内核安全机制背景
Windows Vista以来引入的多项安全机制:
- NX/DEP:内核模式下区分data和code,通过保护模式内存分段实现内存属性标记
- KALSR:内核地址空间布局随机化,随机化Windows模块基地址
- Integrity Levels:可信级别机制限制高风险程序执行敏感操作
- SMEP:超级模式执行保护,禁止内核空间执行用户空间代码
Windows内存分页单位
- 1KB = 0x400B
- 1MB = 1KB * 0x400
- 1GB = 1MB * 0x400
- 1TB = 1GB * 0x400
Intel分页机制
x86架构(32位)
- 两级寻址机制:PAGE DIRECTORY和PAGE TABLE
- CR3寄存器指向当前PAGE DIRECTORY的物理地址
- 每个PAGE DIRECTORY有1024个PDE(Page Directory Entry)
- 每个PDE指向一个PAGE TABLE的物理地址
- 每个PAGE TABLE有1024个PTE(Page Table Entry)
- 每个PTE映射4KB物理地址
特殊情况下,PDE可以指向4MB的Large Page(PS标记)
PAE(物理地址扩展)
- 引入PDPT(Page Directory Pointer Table)
- 物理地址扩展到36位(64GB寻址空间)
- PDPT有512个PDPTE,每个指向1GB空间
- 引入NX bit区分data和code
x64架构(64位)
- 使用48位物理地址(64位虚拟地址)
- 引入Canonical Address概念(0-0x7FFFFFFFFFFFF和0xFFFF800000000000-0xFFFFFFFFFFFFFFFF)
- 四级页表寻址:PML4、PDPT、PAGE DIRECTORY、PAGE TABLE
- 寻址计算:512PML4Es * 512PDPTEs * 512PDEs * 512PTEs * 4KB = 256TB
自引用条目技术(Self-ref Entry)
技术原理
将最高级别页表中的某一entry指向该页表自身:
- 32位:self-entry存在于PAGE DIRECTORY
- 64位:self-entry存在于PML4
实现方式
假设:
- entry 0x00 → User Space
- entry 0x100 → self-ref
- entry 0x1ff → Kernel Space
内存分布:
- User Space: 0 ~ 0x7fffffffffff
- Memory Management: 0xffff800000000000 ~ 0xffff800000000000
- Kernel Space: 0xffffff8000000000 ~ 0xffffffffffffffff
计算物理地址的Python函数
def calc_physical_64(virtual_addr):
entry_size = 0x8
shift_address = virtual_addr >> 12 # 4kb
pte_offset = shift_address & 0x1ff
shift_address = shift_address >> 9 # 512
pde_offset = shift_address & 0x1ff
shift_address = shift_address >> 9 # 512
pdpt_offset = shift_address & 0x1ff
shift_address = shift_address >> 9
pml_offset = shift_address & 0x1ff
print("entry: PML4: 0x%x PDPT: 0x%x PD: 0x%x PT: 0x%x" %
(pml_offset, pdpt_offset, pde_offset, pte_offset))
print("offset: PML4: 0x%x PDPT: 0x%x PD: 0x%x PT: 0x%x" %
(pml_offset*entry_size, pdpt_offset*entry_size,
pde_offset*entry_size, pte_offset*entry_size))
def calc_physical_32(virtual_addr):
entry_size = 0x8
shift_address = virtual_addr >> 12 # 4kb
pte_offset = shift_address & 0x1ff
shift_address = shift_address >> 9 # 512
pde_offset = shift_address & 0x1ff
print("entry: PD: 0x%x PT: 0x%x" % (pde_offset, pte_offset))
print("offset: PD: 0x%x PT: 0x%x" % (pde_offset*entry_size, pte_offset*entry_size))
Windows实现细节
- 64位系统中:前256个PML4条目用于用户空间,后256个用于内核空间
- PML4的self-ref entry是0x1ED(内核空间)
- 对应的虚拟地址空间:(512G + 1G + 2M + 4K)*0x1ED = 0xf6fb7dbed000
- 加上canonical address:0xfffff6fb7dbed000
安全弱点
- Windows为所有进程使用固定的PML4 self-ref entry
- 攻击者可计算Page Table/entries
- 可修改或添加entries
- 影响KALSR,可用于SMEP绕过
Windows 1607更新
引入了随机化Self-Ref Entry的措施
Windows HAL堆
- HAL.dll(硬件抽象层)是开机时第一个加载的模块
- HAL堆地址在启动时创建,总是映射到固定虚拟地址:
- 32位:0xffd00000
- 64位:0xfffffffffd000000
- 这一特性可用于绕过KALSR
实践验证
测试代码
#include <Windows.h>
#include <wchar.h>
int main() {
PVOID addr = (PVOID)0x1000000;
//Allocate Memory
addr = VirtualAlloc(addr, 0x1000, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
wprintf(L"address = 0x%llx\n", addr);
// Setting Memory
memset(addr, 0x41, 0x1000);
// Debug
__debugbreak();
}
Windows 7 32位实验结果
- 成功分配虚拟地址并写入数据
- 32位系统只有PDE和PTE
- PTE用物理地址0x61cd5000映射虚拟地址0x1000000
- 修改PTE可改变虚拟地址内容
- 通过修改PTE可实现两个虚拟地址共享同一物理地址
- 计算出的PTE偏移正确,但缺少PTE基地址
- Windows 7中Self-Ref没有随机化
Windows 10 1903 64位实验结果
- 四级页表寻址机制
- PTE保存虚拟地址对应的物理地址内容
- 修改PTE效果与Win7相同
- 计算出的Page Table偏移正确
- Windows 10及以上版本存在:
- nt!MiGetPteAddress(Pte基地址编码在固定偏移0x13)
- MiGetPdeAddress(偏移0xc)
- Windows 10中PTE基地址是随机的
利用技术总结
- 任意地址写利用:通过修改可控虚拟地址的PTE指向目标物理地址
- 共享物理地址:使两个虚拟地址指向同一物理地址,通过可控地址修改不可控地址
- 绕过KALSR:利用HAL堆固定地址或计算Page Table地址
- SMEP绕过:通过物理地址操作在内核空间执行用户空间代码
相关链接
- CSW2016会议资料
- Windows内核文档
- Intel架构手册
防御建议
- 保持系统更新(特别是1607及以上版本)
- 启用所有内存保护机制
- 监控异常的内存操作
- 限制对关键内存区域的访问
通过深入理解这些技术,安全研究人员可以更好地防御相关攻击,同时也能开发更有效的漏洞利用方法。