Post

浅谈 | Intel MPK

引言

Intel MPK(Memory Protection Keys) 是 Intel 推出的一种内存保护机制,可以在不修改页表的情况下,来控制线性地址所关联物理页的访问权限,本篇将说明一些基本用法和用途,在阅读本篇内容前,需要读者对 x86/x64 架构有基本的了解。

说明

  • CPUID.(EAX=07H,ECX=0H):ECX.OSPKE [bit 4])

    • 判断系统是否支持 PKE
  • CPUID.(EAX=07H,ECX=0H):ECX.OSPKE [bit 4])

    • 判断系统是否支持 PKS
  • CR4.PKE

    • 控制是否启用 User-Mode(用户模式)页面的 Protection Keys
  • CR4.PKS

    • 控制是否启用 Supervisor-Mode(管理模式)页面的 Protection Keys
  • PKRU

    • 一个 32 位的内部寄存器,控制 User-Mode Protection Keys 所关联的页面权限
  • IA32_PKRS

    • 一个 32 位的 MSR,控制 Supervisor-Mode Protection Keys 所关联的页面权限
  • RDPKRU

    • 读取 PKRU 内部寄存器
  • WRPKRU

    • 写入 PKRU 内部寄存器

Protection Key

在禁用 CR4.PKE 的情况下,用户模式页表中的 Bit 59 ~ Bit62 是保留位

在启用 CR4.PKE 的情况下,用户模式页表中的 Bit 59 ~ Bit62 是 Protection Key

在禁用 CR4.PKS 的情况下,管理模式页表中的 Bit 59 ~ Bit62 是保留位

在启用 CR4.PKS 的情况下,管理模式页表中的 Bit 59 ~ Bit62 是 Protection Key

这 4 位的 Protection Key 范围是 0~15,相当于是一个索引,被关联到 PKRU 或 PKRS

PKRU

这个寄存器类似于一个位图,存放着 16 对 ADi 与 WDi,用于控制与 User-Mode Protection Key 关联的页面权限,这个寄存器需要通过RDPKRU WRPKRU XSAVE XRSTOR 进行控制。

  • ADi = 1,不允许任何数据访问
  • WDi = 1,不允许用户模式数据写入(在 CR0.WP 复位的情况下管理模式可以写入)
  • 取指操作不受 PKRU 影响

IA32_PKRS

这个寄存器结构与 PKRU 极其相似,区别在于它所控制的是 Supervisor-Mode Protection Keys,并且此寄存器是一个 MSR 寄存器,高 32 位为保留位,需要通过RDMSR WRMSR 进行控制。

  • ADi = 1,不允许任何数据访问
  • WDi = 1,不允许用户模式数据写入(在 CR0.WP 复位的情况下管理模式可以写入)
  • 取指操作不受 PKRS 影响

PageFault

如果是由 Protection Keys 引发的#PF,那么 PFEC 的 Bit 5 则置位。

流程

  • 首先,判断处理器是否支持 PKE 或 PKS
  • 如果支持,则置位 CR4.PKE 或 PKS
  • 通过 WRPKRU 将权限写入到 PKRU,或者通过 WRMSR 将权限写入到 PKRS
    • 例子:如果 Protection Key 设置为 0Fh(1111b) ,那么所对应的掩码就是 0C0000000h
  • 然后修改页表条目中的 Protection Key,将它与 PKRU 或 PKRS 关联

结尾

聪明的挂哥,可能已经看到加粗的几个字了,取指操作不受 Protection Key 影响,意味着可以分离读写与执行,可以分离读写就意味着,在使用所谓的 无痕HOOK的时候,性能可以大幅度提高。

聪明的挂哥都知道,Intel VT-x 提供的 EPT 可以轻松的完成这项任务,但是 AMD-V 提供的 NPT 并没有这样的功能,那么结合 Protection Key 是否可以间接的为 AMD-V 提供这样的功能来平替 EPT,来完成所谓的 无痕HOOK 呢?

答案就在下面,诸位细品。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nt!_HARDWARE_PTE
   +0x000 Valid            : Pos 0, 1 Bit
   +0x000 Write            : Pos 1, 1 Bit
   +0x000 Owner            : Pos 2, 1 Bit
   +0x000 WriteThrough     : Pos 3, 1 Bit
   +0x000 CacheDisable     : Pos 4, 1 Bit
   +0x000 Accessed         : Pos 5, 1 Bit
   +0x000 Dirty            : Pos 6, 1 Bit
   +0x000 LargePage        : Pos 7, 1 Bit
   +0x000 Global           : Pos 8, 1 Bit
   +0x000 CopyOnWrite      : Pos 9, 1 Bit
   +0x000 Prototype        : Pos 10, 1 Bit
   +0x000 reserved0        : Pos 11, 1 Bit
   +0x000 PageFrameNumber  : Pos 12, 40 Bits
   +0x000 SoftwareWsIndex  : Pos 52, 11 Bits
   +0x000 NoExecute        : Pos 63, 1 Bit
This post is licensed under CC BY 4.0 by the author.