x64 内核(一)IA-32e 寄存器研究

😄

1 IA-32 和 IA-32e 架构

1.1 IA-32e 架构

  1. 从 32 位系统过渡到 64 位系统初期,Intel 开发了 IA-64,仅支持 64 位指令集,不支持原先的 x86(IA-32)指令集。而 AMD 开发了能支持 IA-32 指令集的 CPU,称之为 x86-64 或 AMD64(简称 x64)。之后由于市场的因素,Intel 跟着 AMD 也在自己的 x86 基础上开发出兼容 IA-32的 CPU,称 IA-32e 或者 Intel64。

    • x86:IA-32

    • AMD64

    • Intel64:IA-32e

  2. IA-32e 模式下支持两种子模式的软件运行。

    • 64-bit mode:Run 64 bit Apps。
    • Compatibility mode(兼容模式):Run 16/32 bit Apps,run legacy software。(Legacy Mode:Real mode、Protected mode、Virtual 8086 mode,称为传统模式)
  3. 64 位模式。

    • AMD:Long Mode
    • Intel:IA-32e 下 64-bit Mode
  4. 32、64 位。

    • Legacy Mode(传统模式):内核 32 位,用户 32 位。
    • 64-bit mode:内核 64 位,用户 64 位。
    • Compatibility mode(兼容模式):内核 64 位,用户 16/32 位。

IA-32e 模式下,系统级的寄存器如下:

  • Model-specific registers(MSR):用于 R0 代码的寄存器(驱动可访问),用于控制——调试扩展、性能监控计数器、机器检查架构和内存类型范围 (MTRRs) 等。MSR 很复杂,Intel 专门出了手册卷 4 来进行说明。
  • 描述符寄存器:GDTR、IDTR、LDTR 和 TR。
  • 标志寄存器:RFLAGS,64 位中高 32 位保留未使用。R0、R3 都可直接访问和修改(R3仅可修改部分位,如ZF)。
  • 调试寄存器:DR0~DR7,64 位。
  • 控制寄存器:CR0~CR4,CR8,都是 64 位。
  • CPUID 指令,不是寄存器。

开机后,模式启动顺序:实模式 — 保护模式 — IA-32e 模式。

下图 2-1 表示 IA-32,图 2-2 表示 IA-32e。

1

2.png

1.2 IA-32e 门的支持

IA-32e 取消了任务门, TSS 还存在(取消了大部分成员),但是系统调用时不会从 TSS 中获取 RSP0。

调用门促进 64 位模式和兼容模式之间的转换。 IA-32e 模式不支持任务门。

支持:调用门、中断门、陷阱门。

最新的手册:Intel® 64 and IA-32 Architectures Software Developer Manuals。

1
2
3
4
5
6
7
8
9
10
11
12
//0x68 bytes (sizeof)
struct _KTSS64
{
ULONG Reserved0; //0x0
ULONGLONG Rsp0; //0x4
ULONGLONG Rsp1; //0xc
ULONGLONG Rsp2; //0x14
ULONGLONG Ist[8]; //0x1c
ULONGLONG Reserved1; //0x5c
USHORT Reserved2; //0x64
USHORT IoMapBase; //0x66
};

1.3 IA-32e 运行模式

x86 支持三种模式:实模式、保护模式、虚拟8086模式。

Intel 64 可以切换到 x86 的模式中,同时还支持 IA-32e 模式。在 IA-32e 模式下,处理器支持两种子模式(Sub-modes):

  1. 64-bit 模式(不支持运行32位程序)
  2. Compatibility mode,兼容模式(支持运行32位程序)。

在 IA-32e 下区分 64 位模式和还是兼容模式:查看 CS 段寄存器的 L 位。(代码段描述符增加一个L属性位,用于表明这个是要一个64位代码段)

  • L = 1 : 64位代码段
  • L = 0:
    • D = 1 32位代码段
    • D = 0 16位代码段

注意:兼容模式(Compatibility mode)并不是切换到保护模式,是在 IA-32e 模式下。同时 IA-32e 不支持虚拟 8086 模式。

关于运行模式的切换需要看 Intel 手册卷2 9.9:

3.png

1.4 IA32_EFER 寄存器

MSR 是包含控制寄存器和状态寄存器两类功能的一组寄存器。它即可以控制CPU的一些行为,也存储一些CPU的状态。MSR每个寄存器是64位宽的,MSR是一组寄存器,他们没有自己的名字,他们的访问方式和使用 IN/OUT 指令的 I/O 操作是一样的都是使用地址来进行访问。

使用 RDMSR 和 WRMSR 两个指令来进行读写,相当于 I/O 操作的 IN/OUT 指令,

MSR 寄存器中提供了一些特殊的寄存器,如 IA32_EFER 寄存器:它提供了几个与 IA-32e 的模式启用与操作相关的字段(控制着是否进入64位模式)。 它还提供了一个与页面访问权限修改相关的字段 IA32_EFER MSR 的布局下图所示。

4.png

  • Bit 11:控制每级页表项的最高位 XD 位,使用 或(||) 关系。

1.5 激活 IA-32e 模式

操作系统应遵循以下一般顺序来激活 IA-32e 模式:

  1. 从启用分页保护模式开始,通过设置 CRO.PG 三 O 禁用分页。使用 MOV CRO 指令禁用分页(必须位于标识映射页中)。

IA-32e 模式的启用条件是:

  1. 启用段内存管理进入保护模式 CR0.PE = 1

  2. 启用PAE内存分页管理模式 CR4.PAE = 1

  3. 启用64位模式 EFER.LME = 1

  4. 启用页内存管理 CR0.PG = 1

  5. 上述四点必须按照上面的顺序启用

  如果条件全部满足,CPU会置 EFER.LMA = 1表示 IA-32e 位模式启动了 。然后在 IA-32e 下区分 64 位模式和还是兼容模式:查看 CS 段寄存器的 L 位。

IA-32e 模式下寄存器的设置:

25.png

可参考:

1.6 64-bit模式和兼容模式

概括来说:64-bit 模式和兼容模式在 IA-32e 激活后并存。

  • 64-bit 模式用来运行 64 位的程序。
  • 兼容模式,用来运行16/32 位的程序。

兼容模式:IA-32e 模式中的兼容模式保持与传统 IA-32 16 位和 32 位应用程序的二进制兼容性。 (兼容模式不支持以虚拟 8086 模式运行或使用硬件任务管理的旧版 16 位或 32 位应用程序)。 兼容模式是在代码段描述符的基础上选择的。 它允许遗留应用程序在 64 位操作系统下与在 64 位模式下运行的 64 位应用程序共存。 在 IA-32e 模式下运行的操作系统可以通过将其代码段描述符的 CS.L 位清除为 0 来执行现有的 16 位和 32 位应用程序。

  1. IA-32e 模式只允许 64 位操作系统进入。
  2. 操作系统基于代码段描述符的 L 位来决定是否启用 64 位模式。 64 位模式用来运行 64 位的程序,兼容模式可以运行 32 位/16 位程序。
  3. 64 位模式下,线性地址为 64 位,操作数默认是 32 位大小,但是可以使用 64 位硬编码前缀将其转换为 64 位操作数。
  4. 兼容模式允许传统的 16 位和 32 位应用程序在 64 位操作系统下运行,无需重新编译(但是不允许 Virtual 8086 模式应用程序或使用硬件任务管理运行)。 与 64 位模式一样,兼容模式由操作系统在单个代码段的基础上启用。 这意味着 64 位应用程序可以在处理器上运行(以 64 位模式),同时传统的 32 位应用程序(未针对 64 位重新编译)以兼容模式运行。

兼容模式类似于传统(legacy)的保护模式,应用程序只能访问前 4GB 线性地址空间,只能使用标准 IA-32 的指令和寄存器。但是兼容模式属于 IA-32e 模式下的子模式,需要遵循以下:

  1. 兼容模式下,虚拟 8086 模式、任务门、堆栈参数复制功能不可用。

  2. 在内核中,内核数据结构、地址转译、中断和异常处理使用 64-bit 模式机制而不是 32-bit 机制来处理。

    • 兼容模式下,线性到物理地址转换使用 64 位模式扩展页面转换机制。

    • 使用 64 位模式机制处理中断和异常。

    • 系统调用(通过调用门和 SYSENTER/SYSEXIT 调用)使用 IA-32e 模式机制处理。

兼容模式和 64-bit 模式是同时运行并存的,兼容模式应用程序不知道 64-bit 模式的存在,其根据代码段描述符的 L 位来进行运行。

在 IA-32e 模式下,所有 16 位和 32 位地址计算都进行零扩展以形成 64 位地址。 地址计算首先被截断为当前模式(64 位模式或兼容模式)的有效地址大小,被任何地址大小前缀覆盖。 结果是零扩展的完整 64 位地址宽度。 因此,在兼容模式下运行的 16 位和 32 位应用程序只能访问 64 位模式有效地址的低 4GBytes。 同样,在 64 位模式下生成的 32 位地址只能访问 64 位模式有效地址的低 4GBytes。

参考《64-Bit Extension Technology Software Developer’s Guide》。

2 CPUID 指令

CPUID 是一个指令,用来得到 CPU 处理器的身份信息(如型号、家族等)、特征和状态,还能得到 CPU 的一些其他扩展信息。

可以在用户模式下使用。

Opcode Instruction Op/En 64-Bit Mode Compat/Leg Mode Description
0F A2 CPUID ZO Valid Valid 根据输入的 EAX(在某些情况下,ECX 也作为输入)的不同,将不同的身份/特征信息返回到 EAX、EBX、ECX、EDX 中。

指令操作数:

Op/En Operand 1 Operand 2 Operand 3 Operand 4
ZO NA NA NA NA

2.1 CPUID 指令的支持

  1. CPUID 指令从 Intel 486 开始引入支持。

  2. 判断 CPU 是否支持 CPUID 指令:使用 RFLAGS 寄存器的 Bit-21(ID位)。

    在代码中,如果能设置或者清除 rflags.ID 位的值,则表示 CPU 支持 CPUID 指令。

  3. 在 IA-32e 模式下,CPUID 指令会清除 rax/rbx/rcx/rdx 的高 32 位,仅使用低 32 位。

2.2 CPUID 的使用

Intel 将要查询的功能号放入 RAX 寄存器中(对于较复杂的信息查询,会使用到 RCX 作为输入寄存器)。

输入分为两部分,主叶为必须值。

  • RAX:主叶,leaf 或 main-leaf
  • RCX:子叶,sub-leaf
1
2
3
mov rax, 0Bh;
mov rcx, 0;
cpuid;

指令语法:

5.png

CPUID 返回的信息分为两大类:基本信息、扩展信息。每类信息都有最大功能号(main)限制。

目前 Intel 手册上这两类信息的输入功能号范围:

  • 基本功能号:0~0x1F
  • 扩展功能号:0x80000000~0x80000008

下面分别查询 Intel(R) Core(TM) i5-8279U CPU @ 2.40GHz 2.40 GHz——MacBook Pro (13-inch, 2019, Four Thunderbolt 3 ports) 支持的最大基本功能号和最大扩展功能号:

  1. 查询最大基本功能号:使用 00h leaf 来查询。

    1
    2
    3
    4
    5
    6
    7
    8
    mov rax, 0h;
    cpuid;

    // 输出如下
    RAX 0000000000000016 //最大基本功能号0x16
    RBX 00007FFA756E6547 //Genu
    RCX 00007FFA6C65746E //ntel
    RDX 0000000049656E69 //inel

    RBX+RDX+RCX:Genuntelinel。AMD为 AuthenticAMD。

    6.png

  2. 查询最大扩展功能号:使用 80000000h leaf 来查询。

    1
    2
    3
    4
    5
    6
    7
    8
    mov rax, 80000000h;
    cpuid;

    // 输出如下
    RAX 0000000080000008 //最大扩展功能号0x80000008
    RBX 00007FFA00000000 //Reserved
    RCX 00007FFA00000000 //Reserved
    RDX 0000000000000000 //Reserved

    7.png

如果输入:功能号 <= 最大功能号,但是 CPU 不支持该功能号,则所有寄存器的返回值为 0。

如果输入:功能号 > 最大功能号,则返回 CPU 支持的最大功能号的信息。如上面实验 CPU 的 0x16/0x80000008 的信息。

除此之外还有几个常用的指令:

  1. 查询处理器型号(Family, model, stepping):CPUID.01H:EAX。

  2. 最大的物理地址和线性地址:CPUID.80000008H:EAX[bits 7-0:Physical,bits 15-8:Linear]。

    1
    2
    3
    4
    5
    6
    7
    8
    mov rax, 80000008h;
    cpuid;

    // 输出如下
    RAX 0000000000003024 //Physical:0x24=36(64GB), Linear:0x30=48(256TB)
    RBX 00007FFA00000000 //Reserved
    RCX 00007FFA00000000 //Reserved
    RDX 0000000000000000 //Reserved

    8.png

  3. 查询 MONOTOR/MWAIT 信息:CPUID.01H:ECX.[3]。

    monitor/mwait 指令的使用情形有些与 pause 指令类似,在 Idle-Loop(空闲的循环)中用来提高处理器的性能和减少能耗。pause 常用于 OS 的自旋锁,monitor/mwait 对某个地址范围做监控,这个地址范围内没发生写操作就进入优化(空闲)的状态,由 mwait 等待某些事件而退出,因地址 store 操作而退出或者因某些中断类的事件发生。由 IA32_MISC_ENABLE 开启这个功能。

  4. 查看 syscall/sysret 指令是否可用:CPUID.80000001H:EDX.[11] = 1。

更多信息参考:

  • Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 2 (2A, 2B, 2C & 2D): Instruction Set Reference, A-Z
  • x86/x64 体系探索及编程

2.3 CPUID.01H, CPUID.80000001H

15.png

16.png

3 CRx 寄存器

控制着 CPU 处理器的工作模式,限制着 CPU 的功能。

IA-32e 模式下 CR0~CR15 控制寄存器被扩展为 64 位,但是仅使用 CR0、CR2、CR3、CR4,还有专用于 IA-32e 模式下的 CR8。

9.png

  • CR0:包含系统控制位、操作模式位、处理器状态位。
  • CR1:保留。
  • CR2:发生页面异常(Page-fault)时,用来存放页面异常的线性地址。注意:mov cr2,xx 指令并不会检查地址 xx 是否规范。
  • CR3:唯一存放物理地址的寄存器。保存的物理地址为 PML4/PML5 表基址。
  • CR4:提供一些扩展的控制功能。
  • CR8:仅在 IA-32e 模式下可用。控制着对 Task Priority Register (TPR) 的读写。

注意:通用寄存器的访问和写入仅在内核模式下才可以使用。

3.1 CR0

10.png

标志 位 描述
PE 0 启用保护模式(Protection Enable)
PE = 1,开启保护模式。
PE = 0,实模式。
要开启分页模式需要 CR0.PG = 1。
PG = 0 且 PE = 0 ,处理器工作在实地址模式下。
PG = 0 且 PE = 1,处理器工作在没有开启分页机制的保护模式下。(目前没有这样的操作系统)
PG = 1 且 PE = 0 ,在PE没有开启的情况下,无法开启PG。(没有这样的状态)
PG = 1 且 PE = 1,处理器工作在开启了分页机制的保护模式下。
MP, EM, TS, NE 1, 2, 3, 5 用于 x87 FPU 浮点运算。
ET 4 对齐类型(Extension Type)。在 Pentium 4、Intel Xeon、P6 系列和 Pentium 处理器中保留。 在 Pentium 4 Intel Xeon 和 P6 系列处理器中,此标志被硬编码为 1。在 Intel386 和 Intel486 处理器中,此标志在设置时表示支持 Intel 387 DX 数学协处理器指令。手册中没提到其他使用方式。
WP 16 写保护(Write Protect)
WP = 1,supervisor 权限的程序禁止修改 only-read 页面。
WP = 0,supervisor 权限的程序允许修改 only-read 页面。
页面 supervisor 权限指 R0~R2,user 指 R3。
在 CR4.CET 置1前必须先将该为置1,当 CR4.CET 置1后不允许将该位清零。
AM 18 对齐掩码(Alignment Mask)
只有 CPL == 3,运行在用户模式或者虚拟8086模式,才会对地址进行边界对齐检查。不对齐产生#AC异常。
AM = 1 && rflags.AC = 1,启用自动对齐检查。
AM = 0 || rflags.AC = 0,禁用自动对齐检查。
NW, CD 29, 30 NW:Not Write-through(不可直写), CD:Cache Disable。Write-back 回写。跟 Memory Cache 有关。
NW 和 CD 标志被清零时,回写(对于奔腾4,英特尔至强,P6系列和奔腾处理器)或直写(对于Intel486处理器)被启用以用于命中缓存和失效周期的写入。
PG 31 分页(Paging)。
开启页式管理前必须要打开保护模式 PE 位,否则将产生 #GP 异常。
PG = 0 且 PE = 0 ,处理器工作在实地址模式下。
PG = 0 且 PE = 1,处理器工作在没有开启分页机制的保护模式下。(目前没有这样的操作系统)
PG = 1 且 PE = 0 ,在PE没有开启的情况下,无法开启PG。(没有这样的状态)
PG = 1 且 PE = 1,处理器工作在开启了分页机制的保护模式下。

本机测试:CR0 = c0050033。

参考:

3.2 CR2

11.png

CR2 没有控制位,当页面发生异常(缺页、页面访问权限错误)是,用来存放引发的 #PF 异常线性地址。

注意:mov cr2,xx 指令并不会检查地址 xx 是否规范。

3.3 CR3

12.png

CR3 寄存器中的值为物理地址。低 12 位中仅有两个标志位 PCD、PWT。

CR3 的值指向 PML4 或 PML5 时需要将低 12 位置 0,然后 CR3 的 64 位指向的物理地址就是 PML4 或 PML5 表的基地址。

注意:在使用 CR3 寻找 PML4 或 PML5 时,低 12 位需要置 0,所以存放 PML4 或 PML5 的页面必须与上一个页面 4KB 对齐。

PCD 和 PWT 标志控制处理器内部数据缓存中该分页结构的缓存(它们不控制页面目录信息的TLB缓存)。

  • PCD(Page-level Cache Disable):页级缓存禁用。控制用于访问当前分页结构层次的第一个分页结构的内存类型。如果禁用分页、或使用 PAE 分页、或者在 CR4.PCIDE = 1 时使用 4 级分页或 5 级分页,则不使用该位。
  • PWT(Page-level Write-Through):页级直写。控制用于访问当前分页结构层次的第一个分页结构的内存类型。如果禁用分页、或使用 PAE 分页、或者在 CR4.PCIDE = 1 时使用 4 级分页或 5 级分页,则不使用该位。

PWT:Page Write Through

  • PWT = 1,写Cache的时候也要将数据写入内存中。
  • PWT = 0,写Cache时由CPU控制寄存器决定是否写入内存。

PCD:Page Cache Disable

  • PCD = 1,禁止某个页写入缓存,直接写内存。
  • PCD = 0,可以写入Cache。

14.png

3.4 CR4

13.png

CR4 目前(2022.4)共 23 个标志位,这些标志可以实现一些架构扩展,并表明操作系统或对特定处理器功能的执行支持。在 CR4 中,部分的控制位必须要通过 CPUID.EAX=01H 查询是否获得支持。

标志 位 描述
VME, PVI 0, 1 VME:虚拟 8086 模式扩展(Virtual-8086 Mode Extensions),置 1 时启用虚拟 8086 模式下的中断和异常处理扩展。还提供了对虚拟中断标志(VIF)的硬件支持。
PVI:保护模式虚拟中断(Protected Mode Virtual Interrupts),置 1 启用保护模式下对虚拟中断标志(VIF)的硬件支持,置 0 时禁用保护模式下的 VIF 标志。
TSD 2 禁用时间戳指令(Time Stamp Disable)
TSD = 1,将 RDTSC 指令的执行限制 R0 运行的程序中。
TSD = 0,允许 RDTSC 指令在任何权限级别下执行。
类似于 CPUID.80000001H:EDX[27] = 1:RDTSCP and IA32_TSC_AUX are available。CPUID 查询 leaf 中有两个与 TSD ( Time Stamp Disable)相关:CPUID.EAX-01H 中 返回的 EDX[4].TSC 标志位用来测试是否支持 CR4.TSD 控制位:CPUID.EAX-80000001H 中返回的 EDX[27].RDTSCP 位用来测试是否支持 RDTSCP 指令。
DE 3 调试扩展(Debugging Extensions)
DE = 1,引用 DR4 和 DR5 时,由于 DR4 和 DR5 是被保留的,会产生一个 #UD 异常。
DE = 0,处理器会对寄存器 DR4 和 DR5 的引用进行别名,DR4 和 DR5 被映射到 DR6 和 DR7。以便于写在早期 IA-32 处理器上运行的软件兼容。
PSE 4 PSE:分页扩展(Page Size Extension),仅适用于 32 位的分页模式中。PDE.PS 位受 CR.PSE 位影响:
54.png
PAE 5 PAE:物理地址扩展(Physical Address Extension)
IA-32:PAE = 1,启用 2-9-9-12 分页,使分页产生超过 32 位的物理地址。PAE = 0,使用 10-10-12 分页,使用 32 位的物理地址。
IA-32e:在进入 IA-32e 模式之前必须设置 PAE。
MCE 6 机器异常检查(Machine-Check Exception)
置 1 时开启 Machine-Check 机制,清 0 时关闭 Machine-Check 机制。
CR4.DE 和 CR4.MCE 功能需要处理器支持,可以从 CPUID.EAX=01H.EDX 寄存器里获得,返回的 EDX[2]是 DE 支持位,EDX[7]是 MCE 支持位。
PGE 7 全局分页使能(Page Global Enable),该位影响 TLB 的刷新。启用该位前必须保证 CR0.PE = 1, CR0.PG = 1,即必须先启用保护模式和分页模式。
PGE = 1,PDE.G 和 PTE.G 才被启用。(CR4.PGE = 1) && (PDE.G = 1 \|\| PTE.G = 1) 条件下进程 CR3 切换时不会刷新 TLB。
PGE = 0,PDE.G 和 PTE.G 被禁用。
PCE 8 性能监控计数使能(Performance-Monitoring Counter Enable )位
PCE = 1,RDPMC 指令可以在任何权限下执行。
PCE = 0,RDPMC 指令只能在 R0 级权限下执行。
这和 CR4.TSD 位的用法是相反的。说明 CR4.PCE 比 CR4.TSD 需要更谨慎地去使用。
OSFXSR, OSXMMEXCPT 9, 10 OSFXSR:和 x87 浮点计算相关
OSFXSR = 1,指示 SSE 系列指令是可用的,FXSAVE 和 FXRSTOR 指令可以保存 XMM 寄存器和 MXCSR 的状态。
OSFXSR = 0,FXSAVE 和 FXRSTOR 指令只能保存 x87 和 MMX 寄存器的状态,使用 SSE 系列指令会产生 #UD 异常。
CR4.OSFXSR 功能需要处理器支持,可以从 CPUID.EAX=01H.EDX 寄存器里获得,返回的 EDX[24] 是 FXSR 支持位。
OSXMMEXCPT:表示操作系统支持通过在 SIMD 浮点异常时调用的异常处理程序来处理未屏蔽 SIMD 浮点异常(# XM) 生成。 SIMD 浮点异常仅由 SSE/SSE2/SSE3/SSE4.1 SIMD 浮点指令生成。
UMIP 11 UMIP = 1,禁止在 CPL>0 时使用 SGDT, SIDT, SLDT, SMSW, 和STR 指令。
LA57 12 仅在 IA-32e模式下使用(57-bit linear addresses)
LA57 = 1,启用 5 级分页模式转换 57 位线性地址。
LA57 = 0,启用 4 级分页模式转换 48 位线性地址。
VMXE 13 启用 VT 模式
VMXE = 1,开启 VMX(Virtual Machine Extensions)功能,由 VMXON 指令进入VMX 模式,VMXOFF 指令退出 VMX 模式。
VMXE = 0,关闭 VMX 功能。将 CR4.VMXE 清 0 只能在VMX模式以外。
CR4.VMXE 需要处理器支持,可检查从 CPUID.EAX=01H 返回的 ECXI[5].VMX 看是否得到支持。
SMXE 14 SMXE = 1,表示开启 SMX(Safer Mode Extensions)功 能(”上帝模式“)。可以使用 GETSEC 指令查询 SMX 功能。
VMXE 与 SMXE 控制位仅在 Intel 上使用,AMD 机器上是保留位。
FSGSBASE 16 FSGSBASE = 1,启用 RDFSBASE, RDGSBASE, WRFSBASE, WRGSBASE指令。
PCIDE 17 PCIDE = 1,启用 Process-context identifiers (PCIDs)。仅适用于 IA-32e 模式(如果 IA32_EFER.LMA = 1)。
OSXSAVE 18 和 x87 FPU 的 XSAVE/XRSTOR 指令以及 XSETBV/XGETBV 指令有关。
KL 19 Key-Locker-Enable Bit
KL = 1,表示启用 LOADIWKEY 指令。如果系统固件已激活对 AES Key Locker 指令的支持,此时使用 CPUID 指令查询结果为 CPUID.19H:EBX.AESKLE[bit0] = 1 ,就表示已启用 AES Key Locker 指令。
KL = 0,此时使用 CPUID 指令查询结果为 CPUID.19H:EBX.AESKLE[bit0] = 0 ,此时执行任何 Key Locker指令会导致 #UD 异常。
SMEP 20 Supervisor Mode Executions Protection Enable
SMEP = 1,在 R0 权限的程序不可以执行 R3 中 PTE.XD = 0 的页面上的代码。
SMEP = 0,在 R0 权限的程序可以执行 R3 中 PTE.XD = 0 的页面上的代码。
SMAP 21 Supervisor Mode Access Protection Enable
SMEP = 1,在 R0 权限的程序不可以访问 R3 中 PTE.XD = 0 的页面上的代码。
SMEP = 0,在 R0 权限的程序可以访问 R3 中 PTE.XD = 0 的页面上的代码。
PKE, PKS 22, 24 PKE(Enable protection keys for user-mode pages)。4 级分页和 5 级分页将每个用户模式的线性地址与一个保护密钥联系起来。使用 PKRU 寄存器可以指定每个保护密钥关联的用户模式线性地址是否可以被读取或写入。可以通过 CPUID.(EAX=07H,ECX=0H):ECX.OSPKE[bit4]) 查看改位是否已置 1。
PKE 还控制是否允许使用 RDPKRU 和 WRPKRU 指令来访问 PKRU 寄存器。

PKS(Enable protection keys for supervisor-mode pages)。4 级分页和 5 级分页将 R0 模式的线性地址与一个保护密钥联系起来。PKS = 1 时,可以使用 MSR 的 IA32_PKRS 可以指定每个保护密钥关联的内核模式线性地址是否可以被读取或写入。
CET 23 控制流强制技术(Control-flow Enforcement Technology )
CET = 1,启用控制流强制技术,只有在 CR0.Wp = 1 时才可以将 CET = 1。
必须在 CR0.WP 被清零之前将 CET = 0。

先试用 CPUID 查询 CRx 寄存的位是否被启用,CRx 位启用后,PDE、PTE 中的控制位才被启用。

快速计算内存大小:

bits 大小
10 1 KB,2^10^ * 1 Byte = 1KB。
20 1 MB,2^20^ = 2^10^ * 1 KB = 1 MB。
30 1 GB,2^30^ = 2^10^ * 1 MB = 1 GB。
32 4 GB,2^32^ = 2^2^ * 1 GB = 4 GB。
40 1 TB,2^40^ = 2^10^ * 1 GB = 1 TB。
48 256 TB,2^48^ = 2^8^ * 1 TB = 256 TB。
50 1 PB,2^50^ = 2^10^ * 1TB = 1 PB。
57 128 PB,2^57^ = 2^7^ * 1TB = 128 PB。

4 MSR

4.1 MSR 格式

MSR(Model-Specific Register)特定模式寄存器,MSR 提供对硬件和软件一些相关功能的控制和设置。跟 CPU 的Model(型号)相关。MSR 在 AMD 和 Intel 的处理器上实现不同,并且在 Intel 不同的架构上实现也不完全相同。所以在查询 MSR功能表之前,需要先使用 CPUID.01H:EAX[bits 27-20,bits 19-16] 然后到卷4查看一下当前CPU型号(Family, Model)。

每个 MSR 都有自己的地址值(或者称为编号),且每个 MSR 都有一个用于助记的名称,如 IA32_EFER(0xC0000080)。由于 MSR 是从 IA-32 延续而来,所以每个 MSR的名称都是以 IA32_ 为前缀。 MSR的读写使用 rdmsr、wrmsr 指令。MSR在 IA-32 和 IA-32e 都是64位的。

类似于 CPUID 指令,MSR 的格式如下 IA32_MISC_ENABLE.ENABLEFOPCODE[bit 2] = 1 。

27.png

使用到的三个 MSR 寄存器:

  • 0xC0000081:IA32_STAR
  • 0xC0000082:IA32_LSTAR
  • 0xC0000084:IA32_FMASK

4.2 rdmsr、wrmsr 指令

特权指令。

MSR在 IA-32 和 IA-32e 都是64位的。在 64 位环境,会将 RCX、RDX 高 32 位清 0,使用 RAX,也只会取 EAX 中的 32 位。

  1. rdmsr 指令使用。

    • ECX:指出要读 MSR 哪个地址的值。
    • EDX:EAX:读出来的高低 32 位值。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 输入
    mov ecx, 0C0000082h; // IA32 LSTAR,IA-32e Mode System Call Target Address
    rdmsr;

    // 输出
    RDX = 0xFFFFF800
    RAX = 0x67427180

    // 等同于
    kd> rdmsr C0000082
    msr[c0000082] = fffff800`67427180
  2. wrmsr 指令使用。

    • ECX:指出要往 MSR 哪个地址写入值。
    • EDX:EAX:指出要写入的高低 32 位值。
    1
    2
    3
    4
    5
    mov ecx, 176h;	//IA32_SYSENTER_EIP 寄存器的地址
    mov rdx, 0xFFFF123412345678;
    mov rax, rdx;
    shr rdx, 32;
    wrmsr; //运行后蓝屏

4.3 IA32_EFER 寄存器

它提供了几个与 IA-32e 的模式启用与操作相关的字段(控制着是否进入64位模式)。 它还提供了一个与页面访问权限修改相关的字段 IA32_EFER(0xC0000080) MSR 的布局下图所示。

4.png

Bit 11:控制每级页表项的最高位 XD 位,使用 或(||) 关系。

IA-32e 模式的启用条件是:

   1. 启用段内存管理进入保护模式 CR0.PE = 1
   2. 启用PAE内存分页管理模式 CR4.PAE = 1
   3. 启用64位模式 EFER.LME = 1 
   4. 启用页内存管理 CR0.PG = 1 

上述四点必须按照上面的顺序启用

如果条件全部满足,CPU会置 EFER.LMA = 1表示 IA-32e 位模式启动了 。然后在 IA-32e 下区分 64 位模式和还是兼容模式:查看 CS 段寄存器的 L 位。

IA-32e 模式下寄存器的设置:

25.png

4.4 支持 sysenter/sysexit 指令的 MSR

64 位 Windows 并不是使用 sysenter/sysexit 指令进行 R3/R0 权限的切换,使用 syscall/sysret 指令。

sysenter/sysexit 在 IA-32 和 IA-32e 下都可以使用,但是不支持在 AMD 的 Long mode 下使用。

与之相关的 3 个 MSR 寄存器:

  • 0x174:IA32_SYSENTER_CS
  • 0x175:IA32_SYSENTER_ESP
  • 0x176:IA32_SYSENTER_EIP

28.png

一、IA-32

R3 进入 R0:使用 sysenter 进入 0 环时,CS0 寄存器的段选择子来自 IA32_SYSENTER_CS,SS = CS.Selector + 8。EIP0 来自于 IA32_SYSENTER_EIP(0x176)。但是 ESP0 是来自于 TSS.ESP0,不是来自于 IA32_SYSENTER_ESP(0x175)。

R0 返回 R3:使用 sysexit 返回 3 环时,EDX 存放 EIP3,ECX 存放 ESP3。

二、IA-32e

R3 进入 R0:使用 sysenter 进入 0 环时,CS 寄存器的段选择子 来自 IA32_SYSENTER_CS,SS = CS.Selector + 8。RIP0 来自于 IA32_SYSENTER_EIP(0x176)。但是 RSP0 是来自于 TSS.RSP0,不是来自于 IA32_SYSENTER_ESP(0x175)。

SYSEXIT 指令返回时不同,具体见《Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3 (3A, 3B, 3C & 3D): System Programming Guide 5.8.7.1》。

4.5 支持 syscall/sysret 指令的 MSR

SYSCALL 和 SYSRET 指令专为使用平坦内存模式且不使用分段的操作系统而设计。 这些指令(连同 SYSENTER/SYSEXIT)非常适合 IA-32e 模式操作。 兼容模式不支持 SYSCALL 和 SYSRET。 CPUID.80000001H.EDX[bit 11] = 1 表示该 CPU 支持 SYSCALL 和 SYSRET。

SYSCALL 和 SYSRET 指定 64 位代码偏移量。 在 IA-32e 模式下,清除 EFLAGS 中的位是可编程的,而不是固定的; SYSCALL 和 SYSRET 会保存和恢复 EFLAGS 寄存器。

  1. 64 位 Windows 使用 syscall/sysret 指令进行系统调用和返回,对于 32 位的程序,会在 Wow64 进行转换后,从 64 位的 ntdll.dll 调用 syscall 进入 0 环。

  2. 内核一直都是运行 64-bit 模式,32 位程序的运行环境是模拟出来的。在 64 位系统中,如果此时程序 CS = 0x23,则该段选择子对应的段描述符 CS.L = 0,说明系统此时以兼容模式来运行该程序,以 32 位指令集来运行。

  3. 但是不管是 32 位程序还是 64 位程序,只要发生系统调用,都必须以 64-bit 模式进入,因为内核以 64-bit 模式运行。

  4. 所以, 32 位的驱动程序无法在 64 位系统下运行,因为 IA-32e 的兼容模式是用来运行用户模式的程序的。

一、syscall 步骤

  1. 将 rflags 存入 r11 寄存器,将 3 换的返回地址保存到 rcx 寄存器。
  2. 获取以下 msr 寄存器中的值进行提权跳转。

29.png

二、sysret 返回(返回到64位程序/32位程序)

30.png

使用到的三个 MSR 寄存器:

  • 0xC0000081:IA32_STAR
  • 0xC0000082:IA32_LSTAR
  • 0xC0000084:IA32_FMASK

31.png

4.6 支持 swapgs 指令的 MSR

  • 0xC0000101:IA32_GS_BASE —— 用户模式 GS:[0],GS.Base。
  • 0xC0000102:IA32_KERNEL_GS_BASE —— 内核模式 GS:[0],GS.Base。
1
2
3
4
5
6
kd> rdmsr 0xC0000100	//IA32_FS_BASE
msr[c0000100] = 00000000`00000000
kd> rdmsr 0xC0000101 //IA32_GS_BASE
msr[c0000101] = fffff800`65642000
kd> rdmsr 0xC0000102 //IA32_KERNEL_GS_BASE
msr[c0000102] = 0000004d`0668b000

初始状态时,IA32_GS_BASE 保存用户态的 GS:[0],这个时候将其加载到 GS.Base,IA32_KERNEL_GS_BASE 为内核态的 GS:[0]。

当系统调用时,进入 0 环后,会将 IA32_KERNEL_GS_BASE 的值加载到 GS.Base,并与 IA32_GS_BASE 的值进行交换。

如上 IA32_KERNEL_GS_BASE = 0000004d'0668b000 就是已经进入内核态发生过一次交换了。swapgs 指令如下:

32.png