Windows内核:虚拟内存分页系统与自我引用技术
字数 1822 2025-08-20 18:18:05
Windows内核:虚拟内存分页系统与自我引用技术
1. 虚拟内存与分页机制概述
虚拟地址是现代计算机操作系统中非常重要的概念,尤其是在x86_64架构中。操作系统通过分页机制实现虚拟内存,具有以下关键特性:
- 将内存分解为固定大小的块(页面,通常4KB)
- 通过页表进行地址映射
- 提供内存隔离与保护
- 允许多个进程共享物理内存同时保持隔离
- 提供比物理内存更大的地址空间
2. x86_64架构下的虚拟地址结构
x86_64架构使用四级分页机制,虚拟地址结构如下:
typedef union _virt_addr_t {
void* value;
struct {
std::uint64_t offset : 12; // 页面内偏移
std::uint64_t pt_index : 9; // 页表索引
std::uint64_t pd_index : 9; // 页目录索引
std::uint64_t pdpt_index : 9; // 页目录指针表索引
std::uint64_t pml4_index : 9; // PML4表索引
std::uint64_t reserved : 16; // 保留位
};
} virt_addr_t, *pvirt_addr_t;
地址转换过程:
- 从CR3寄存器获取PML4表基地址
- 使用PML4索引在PML4表中找到PDPT表项
- 使用PDPT索引在PDPT表中找到PD表项
- 使用PD索引在PD表中找到PT表项
- 使用PT索引在PT表中找到物理页框号(PFN)
- 将PFN与offset组合得到物理地址
3. 大页面机制
x86_64支持两种大页面:
2MB大页面
- 跳过PT级别的查找
- Offset占21位(2^21=2MB)
- 虚拟地址直接通过PD索引指向物理内存块
1GB大页面
- 跳过PT和PD级别的查找
- Offset占30位(2^30=1GB)
- 虚拟地址直接通过PDPT索引指向物理内存块
4. 页表访问与KPTI机制
由于Spectre/Meltdown漏洞的影响,操作系统引入了KPTI(Kernel Page Table Isolation)机制:
- 每个进程有两个不同的PML4表:
- 用户模式和内核模式映射表(传统映射)
- 仅用户模式映射表(用于用户模式执行时隔离内核空间)
在Windows中,进程内存管理由KPROCESS结构体管理,其中包含指向PML4表的页框号(PFN)。
5. PFN与物理地址转换
PFN(Page Frame Number)是物理页面的编号:
- 物理地址 = PFN << 12(或PFN * 4096)
- 通过CR3寄存器获取PML4物理地址的代码示例:
typedef union _cr3 {
std::uint64_t flags;
struct {
std::uint64_t reserved1 : 3;
std::uint64_t page_level_write_through : 1;
std::uint64_t page_level_cache_disable : 1;
std::uint64_t reserved2 : 7;
std::uint64_t dirbase : 36; // PML4基地址
std::uint64_t reserved3 : 16;
};
} cr3;
const auto cr3_value = CR3{ __readcr3() }; // 读取CR3寄存器
const auto pml4_physical_address = cr3_value.dirbase << 12; // 获取PML4物理地址
6. 自我引用技术
自我引用技术允许通过虚拟地址直接访问分页表结构,原理是在分页表的某一层创建一个指向自身的条目。
实现原理
- 将PML4表中的某个条目指向PML4表本身的物理地址
- 当CPU解析这个条目时,会反复使用该条目,形成自我引用循环
- 通过特定虚拟地址可以直接访问分页表结构
示例实现
低端虚拟地址示例
PML4[0] = CR3; // 第0个条目指向自身
访问虚拟地址0x0000000000000000到0x0000000000000FFF实际上访问的是PML4表的前4KB。
高端虚拟地址示例
PML4[0x1FF] = CR3; // 最高条目指向自身
访问虚拟地址0xFFFFF80000000000开始的地址范围实际上访问的是PML4表。
弱点
- 自我引用条目缺乏随机性
- 攻击者可以通过计算找到其他条目的地址
- 条目间的偏移量固定(如0x100和0x101条目间相差512GB)
7. PTE后门技术
PTE(Page Table Entry)后门技术通过修改PTE来实现对物理内存的间接访问和控制。
实现步骤
- 找到目标虚拟地址的PTE条目(使用WinDbg的
!pte命令) - 修改PTE内容:
- 修改权限位(如设置为读写)
- 修改指向的物理地址
- 刷新TLB(通过触发页错误、上下文切换或
invlpg指令)
WinDbg操作示例
!pte 0x7FF000123000 // 查看PTE条目
ed 0xFFFFF6FB7DBED000 0x0000000000000003 // 修改为读写权限
ed 0xFFFFF6FB7DBED000 0x12345003 // 修改指向的物理地址
应用场景
- 访问内核空间(通过修改PTE指向内核物理页面)
- 遍历物理内存
- 绕过正常地址映射机制
8. 页表条目结构
x86_64系统中,每个页表条目为64位(8字节),结构如下:
- 位0-11:标志位(存在位、读写位、用户/内核位等)
- 位12-51:物理页框号(PFN)
- 位52-63:保留位
9. 地址转换示例
以虚拟地址0x71000000000为例:
-
分解地址:
- PML4_index=0x0E
- PDPT_index=0x40
- PD_index=0
- PT_index=0
- Offset=0
-
查找PML4条目:
0x38a0000040653867- 低12位:0x867(标志位)
- PFN:0x40653
- 物理地址:0x40653000(PDPT表基地址)
-
查找PDPT条目(索引0x40)
- 获取PFN:0x41cd7
- 物理地址:0x41cd7000(PD表基地址)
-
查找PD条目(索引0x00)
- 获取PFN:0x3d7d9
- 物理地址:0x3d7d9000(最终物理地址)
因此,虚拟地址0x71000000000映射到物理地址0x3d7d9000。