HyperGuard深度解析:SKPG扩展类型与系统保护机制

本文深入探讨HyperGuard的SKPG扩展类型,包括MSR与控制寄存器扩展、安全虚拟地址转换扩展及处理器特定扩展,详细解析其如何通过拦截恶意修改保护系统关键组件。

HyperGuard Part 3 – 更多SKPG扩展

大家好!欢迎来到HyperGuard系列的第3部分! 在上一篇博客中,我介绍了SKPG扩展——这些数据结构描述了应由HyperGuard监控的内存范围和系统组件。到目前为止,我只涵盖了初始化扩展和各种类型的内存扩展,但这些只是开始。在本文中,我将介绍其余的扩展类型,并展示HyperGuard如何使用它们来保护系统的其他区域。

接下来要研究的扩展组是MSR和控制寄存器扩展:

MSR和控制寄存器扩展

该组包含以下扩展类型:

  • 0x1003: SkpgExtentMsr
  • 0x1006: SkpgExtentControlRegister
  • 0x100C: SkpgExtentExtendedControlRegister

这些扩展类型从普通内核接收,但从未添加到SKPG_CONTEXT末尾的数组中,也不会在我将在后续文章中描述的运行时检查中进行验证。相反,它们用于SKPG初始化的另一部分。

在SkpgInitializeContext中初始化SKPG_CONTEXT后,SkpgConnect执行IPI(处理器间中断)。它通过调用SkeGenericIpiCall并传入目标函数和输入数据来执行此IPI,该函数将在每个处理器上调用目标函数并发送请求的数据。在这种情况下,目标函数是SkpgxInstallIntercepts,输入数据包含输入扩展的数量和匹配数组:

我将在未来的博客文章中更详细地介绍拦截,但为了提供必要的背景:SKPG可以要求虚拟机监控程序拦截系统中的某些操作,如内存访问、寄存器访问或指令。HyperGuard使用该能力拦截对某些MSR和控制寄存器(以及其他内容,我稍后会讨论)的访问,以防止恶意修改。HyperGuard使用输入扩展从接受的选项列表中选择要拦截的MSR和控制寄存器。

由于每个处理器都有自己的一组MSR和寄存器,HyperGuard需要在所有处理器上拦截请求的MSR或寄存器。因此,通过IPI调用SkpgxInstallIntercepts,以确保它在每个处理器的上下文中被调用。

一旦进入SkpgxInstallIntercepts,该函数会遍历输入扩展数组,并根据其中提供的数据处理此组中的三种类型。如果您记得,每个扩展包含0x18字节的类型特定数据。对于此组,此数据包含要拦截的MSR/寄存器编号以及应在其上拦截的处理器编号。这意味着每个MSR或控制寄存器可能有多个输入扩展,每个用于不同的处理器编号。或者,如果普通内核请求,MSR和控制寄存器可能仅在某些处理器上拦截,而在其他处理器上不拦截。输入扩展中MSR和控制寄存器扩展的数据结构如下所示:

1
2
3
4
5
6
7
typedef struct _MSR_CR_DATA
{
    ULONG64 Mask;
    ULONG64 Value;
    ULONG RegisterNumber;
    ULONG ProcessorNumber;
} MSR_CR_DATA, *PMSR_CR_DATA;

在遍历扩展时,函数检查扩展类型是否属于此组中的三种之一,如果是,则检查扩展中的处理器编号是否与当前处理器匹配。如果匹配,则检查MSR或控制寄存器的编号是否与接受的之一匹配。如果扩展匹配接受的寄存器之一,则从SKPRCB中的数组获取掩码——此数组包含所有接受的MSR和控制寄存器所需的掩码,因此可以要求虚拟机监控程序拦截它们。所有掩码被收集,当所有扩展都被检查后,最终掩码被发送到ShvlSetRegisterInterceptMasks进行安装。用于安装拦截的掩码是联合HV_REGISTER_CR_INTERCEPT_CONTROL。它有文档记录,可以在此处找到。

现在覆盖了一般过程,我们可以研究接受的MSR和控制寄存器,并理解为什么HyperGuard可能希望保护它们免受修改,从MSR开始:

SkpgExtentMsr

修补某些MSR是漏洞利用和rootkit的常见操作,允许它们执行诸如挂钩系统调用或禁用安全功能之类的操作。其中一些MSR已经由PatchGuard定期监控,但通过HyperGuard拦截它们有好处,我将在后面介绍。可以拦截的MSR列表随着时间的推移不断增长,并随着新功能和寄存器添加到CPU而接收新添加,例如CET的实现添加了多个可能成为攻击者目标的MSR。截至Windows 11 build 22598,可以由SKPG拦截的MSR是:

  • IA32_EFER (0xC0000080) – 除其他外,此MSR包含NX位,强制执行一种缓解措施,不允许在未明确标记为可执行的地址中执行代码。它还包含与虚拟化支持相关的标志。
  • IA32_STAR (0xC0000081) – 包含x86系统调用处理程序的地址。
  • IA32_LSTAR (0xC0000082) – 包含x64系统调用处理程序的地址——通常应指向nt!KiSystemCall64。
  • IA32_CSTAR (0xC0000083) – 包含在兼容模式下运行时x64系统调用处理程序的地址——通常应指向nt!KiSystemCall32。
  • IA32_SFMASK (0xC0000084) – 系统调用标志掩码。当执行系统调用时,此处设置的任何位将从EFLAGS中清除。
  • IA32_TSC_AUX (0xC0000103) – 用法取决于操作系统,但此MSR通常用于存储签名,与时间戳一起读取。
  • IA32_APIC_BASE (0x1B) – 包含APIC基地址。
  • IA32_SYSENTER_CS (0x174) – 包含使用SYSENTER执行系统调用时环0代码的CS值。
  • IA32_SYSENTER_ESP (0x175) – 包含使用SYSENTER执行系统调用时内核堆栈的堆栈指针。
  • IA32_SYSENTER_EIP (0x176) – 包含使用SYSENTER执行系统调用时环0入口的EIP值。
  • IA32_MISC_ENABLE (0x1A0) – 控制多个处理器功能,如快速字符串禁用、性能监控和XD(不可执行)位禁用。
  • MSR_IA32_S_CET (0x6A2) – 控制内核模式CET设置。
  • IA32_PL0_SSP (0x6A4) – 包含环0影子堆栈指针。
  • IA32_PL1_SSP (0x6A5) – 包含环1影子堆栈指针。
  • IA32_PL2_SSP (0x6A6) – 包含环2影子堆栈指针。
  • IA32_INTERRUPT_SSP_TABLE_ADDR (0x6A8) – 包含指向中断影子堆栈表的指针。
  • IA32_XSS (0xDA0) – 包含在内核模式下调用XSAVE和XRESTOR指令时使用的掩码。例如,它控制由Intel处理器跟踪(IPT)使用的寄存器的保存和加载。

SkpgExtentControlRegister

通过修改某些控制寄存器,攻击者可以禁用安全功能或获得执行控制。目前SKPG支持拦截两个控制寄存器:

  • CR0 – 控制某些硬件配置,如分页、保护模式和写保护。
  • CR4 – 控制不同硬件功能的配置。例如,驱动程序签名强制、SMEP和UMIP位控制安全功能,使CR4成为使用任意写入漏洞的攻击者的有趣目标。

SkpgExtentExtendedControlRegister

目前仅存在一个扩展控制寄存器——XCR0。它用于切换扩展寄存器(如AVX、ZMM和CET寄存器)的存储或加载,可以由SKPG拦截和保护。

安装拦截

现在我们知道寄存器可以被拦截以及为什么,我们可以返回并通过ShvlSetRegisterInterceptMasks查看拦截的安装。该函数接收HV_REGISTER_CR_INTERCEPT_CONTROL掩码以了解要安装哪些拦截,以及一些被拦截寄存器的值——CR0、CR4和IA32_MISC_ENABLE MSR。这些都放置在一个传递给函数的结构中,该结构如下所示:

1
2
3
4
5
6
7
struct _REGISTER_INTERCEPT_INFORMATION
{
    HV_REGISTER_CR_INTERCEPT_CONTROL InterceptControl;
    ULONG64 Cr0Value;
    ULONG64 Cr4Value;
    ULONG64 Ia32MiscEnableValue;
} REGISTER_INTERCEPT_INFORMATION, *PREGISTER_INTERCEPT_INFORMATION;

InterceptControl掩码在遍历输入扩展时构建,CR0、CR4和IA32_MISC_ENABLE的值从SKPRCB读取(它们的值,以及所有其他可能被拦截的寄存器的值,在SkeInitSystem中放置在那里,由代码SECURESERVICE_PHASE3_INIT的安全调用触发)。

此结构被发送到ShvlSetRegisterInterceptMasks,该函数又对输入结构中的四个值中的每一个调用ShvlSetVpRegister以注册拦截。通过启动代码为HvCallSetVpRegisters (0x51)的快速超级调用设置寄存器值,发送四个参数(对于感兴趣的人,所有超级调用值都有文档记录)。最后两个参数是HV_REGISTER_NAME和HV_REGISTER_VALUE类型——这些类型有文档记录,因此很容易看到正在设置哪些寄存器:

查看函数,我们看到它正在设置CR0、CR4和IA32_MISC_ENABLE所需的值,最后设置拦截控制的掩码,因此从此时起,所有请求的寄存器都由虚拟机监控程序拦截,对它们的任何访问都将转发到SKPG拦截例程。

安全虚拟地址转换扩展

在上一篇文章中,我介绍了安全扩展——指示要保护的VTL1内存或数据结构的扩展。我还涵盖了内存扩展,包括安全内存扩展。这里是另一种安全扩展,它们在安全内核内部初始化,而不使用来自VTL0的输入扩展。它们称为安全虚拟地址转换扩展,并在SkpgCreateSecureVaTranslationExtents内部初始化。这些扩展用于保护不同页面或内存区域的虚拟->物理地址转换,这些是攻击的常见目标:

  • 0x100B: SkpgExtentProcessorMode
  • 0x100E: SkpgExtentLoadedModule
  • 0x100F: SkpgExtentProcessorState
  • 0x1010: SkpgExtentKernelCfgBitmap
  • 0x1011: SkpgExtentZeroPage
  • 0x1012: SkpgExtentAlternateInvertedFunctionTable
  • 0x1015: SkpgExtentSecureExtensionTable
  • 0x1017: SkpgExtentKernelVAProtection
  • 0x1019: SkpgExtentSecurePool

尽管它们被称为安全扩展,但它们保护的数据大多是VTL0数据,例如KCFG位图的VTL0映射或倒函数表。完成的精确验证因类型而异:例如,零页永远不应映射,因此零页的成功虚拟->物理地址转换不应可接受,而内核CFG位图应具有有效转换,但这些页面的VTL0映射应始终为只读。

查看SkpgCreateSecureVaTranslationExtents,我们可以看到扩展初始化时没有输入数据或内存范围:

这是因为所有这些扩展都与特定数据结构相关,这些数据结构都在其他地方初始化,因此数据不需要是扩展本身的一部分,因此类型是唯一需要设置的部分。我们还可以看到,其中一些扩展仅在启用KCFG时初始化,因为如果没有它,它们就不需要。我将在后续的博客文章中介绍对每个扩展完成的检查,该文章将描述SKPG扩展验证。

最后,如果启用HotPatching,则添加另外两个扩展,两者类型均为SkpgExtentExtensionTable:

这些扩展保护SkpgSecureExtension和SkpgNtExtension变量,这些变量跟踪HotPatching数据。

每处理器扩展

还有两个扩展是处理器特定的,因为它们保护的数据在每个处理器中单独存在。然而,与MSR和控制寄存器扩展不同,不需要安装拦截,也不需要

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计