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;

地址转换过程:

  1. 从CR3寄存器获取PML4表基地址
  2. 使用PML4索引在PML4表中找到PDPT表项
  3. 使用PDPT索引在PDPT表中找到PD表项
  4. 使用PD索引在PD表中找到PT表项
  5. 使用PT索引在PT表中找到物理页框号(PFN)
  6. 将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. 自我引用技术

自我引用技术允许通过虚拟地址直接访问分页表结构,原理是在分页表的某一层创建一个指向自身的条目。

实现原理

  1. 将PML4表中的某个条目指向PML4表本身的物理地址
  2. 当CPU解析这个条目时,会反复使用该条目,形成自我引用循环
  3. 通过特定虚拟地址可以直接访问分页表结构

示例实现

低端虚拟地址示例

PML4[0] = CR3;  // 第0个条目指向自身

访问虚拟地址0x00000000000000000x0000000000000FFF实际上访问的是PML4表的前4KB。

高端虚拟地址示例

PML4[0x1FF] = CR3;  // 最高条目指向自身

访问虚拟地址0xFFFFF80000000000开始的地址范围实际上访问的是PML4表。

弱点

  • 自我引用条目缺乏随机性
  • 攻击者可以通过计算找到其他条目的地址
  • 条目间的偏移量固定(如0x100和0x101条目间相差512GB)

7. PTE后门技术

PTE(Page Table Entry)后门技术通过修改PTE来实现对物理内存的间接访问和控制。

实现步骤

  1. 找到目标虚拟地址的PTE条目(使用WinDbg的!pte命令)
  2. 修改PTE内容:
    • 修改权限位(如设置为读写)
    • 修改指向的物理地址
  3. 刷新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为例:

  1. 分解地址:

    • PML4_index=0x0E
    • PDPT_index=0x40
    • PD_index=0
    • PT_index=0
    • Offset=0
  2. 查找PML4条目:0x38a0000040653867

    • 低12位:0x867(标志位)
    • PFN:0x40653
    • 物理地址:0x40653000(PDPT表基地址)
  3. 查找PDPT条目(索引0x40)

    • 获取PFN:0x41cd7
    • 物理地址:0x41cd7000(PD表基地址)
  4. 查找PD条目(索引0x00)

    • 获取PFN:0x3d7d9
    • 物理地址:0x3d7d9000(最终物理地址)

因此,虚拟地址0x71000000000映射到物理地址0x3d7d9000

Windows内核:虚拟内存分页系统与自我引用技术 1. 虚拟内存与分页机制概述 虚拟地址是现代计算机操作系统中非常重要的概念,尤其是在x86_ 64架构中。操作系统通过分页机制实现虚拟内存,具有以下关键特性: 将内存分解为固定大小的块(页面,通常4KB) 通过页表进行地址映射 提供内存隔离与保护 允许多个进程共享物理内存同时保持隔离 提供比物理内存更大的地址空间 2. x86_ 64架构下的虚拟地址结构 x86_ 64架构使用四级分页机制,虚拟地址结构如下: 地址转换过程: 从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物理地址的代码示例: 6. 自我引用技术 自我引用技术允许通过虚拟地址直接访问分页表结构,原理是在分页表的某一层创建一个指向自身的条目。 实现原理 将PML4表中的某个条目指向PML4表本身的物理地址 当CPU解析这个条目时,会反复使用该条目,形成自我引用循环 通过特定虚拟地址可以直接访问分页表结构 示例实现 低端虚拟地址示例 访问虚拟地址 0x0000000000000000 到 0x0000000000000FFF 实际上访问的是PML4表的前4KB。 高端虚拟地址示例 访问虚拟地址 0xFFFFF80000000000 开始的地址范围实际上访问的是PML4表。 弱点 自我引用条目缺乏随机性 攻击者可以通过计算找到其他条目的地址 条目间的偏移量固定(如0x100和0x101条目间相差512GB) 7. PTE后门技术 PTE(Page Table Entry)后门技术通过修改PTE来实现对物理内存的间接访问和控制。 实现步骤 找到目标虚拟地址的PTE条目(使用WinDbg的 !pte 命令) 修改PTE内容: 修改权限位(如设置为读写) 修改指向的物理地址 刷新TLB(通过触发页错误、上下文切换或 invlpg 指令) WinDbg操作示例 应用场景 访问内核空间(通过修改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 。