noahware/hyper-reV

GitHub: noahware/hyper-reV

基于 Hyper-V 构建的内存自省与逆向工程虚拟机监控器,通过 UEFI 启动注入在 Ring -1 层实现隐蔽的内存操作与代码 Hook。

Stars: 571 | Forks: 91

## 目录 1. 简介 2. 启动前 3. 启动过程 - 3.1. 恢复 bootmgfw.efi - 3.2. 堆 - 3.3. 启动 bootmgfw.efi - 3.4. winload.efi - 3.5. hvloader.dll 与 Hyper-V 启动 Hook - 3.5.1. 加载 hyperv-attachment 4. hyperv-attachment/ Hyper-V 启动后信息 - 4.1. 如何支持不同的架构? - 4.2. 入口点(Hyper-V 启动前) - 4.3. 首次 VM exit - 4.4. APIC - 4.5. SLAT - 4.5.1. Intel 上的代码 Hook - 4.5.2. AMD 上的代码 Hook - 4.5.3. AMD 和 Intel 通用的代码 Hook 功能 - 4.5.3.1. 页面拆分/合并 - 4.5.3.2. 使用 NMI 进行同步 - 4.5.3.2.1. 主机状态下的 NMI - 4.5.3.2.2. 客户机状态下的 NMI - 4.5.3.3. Hyper-V SLAT CR3 的深拷贝 - 4.6. 将执行权交还 Hyper-V - 4.7. Hypercall - 4.7.1 Hypercall 列表与描述 5. 规避检测 6. 用户模式应用程序信息 - 6.1. 命令用法与描述列表 - 6.2. 内核 Hook - 6.3. 命令别名 - 6.4. 刷新日志 7. 如何编译/使用 - 7.1. 编译 'uefi-boot' - 7.2. 特定架构的编译 - 7.3. 加载脚本 - 7.4. 配合 Secure Boot 使用 - 7.5. 配合 TPM 使用 8. 源代码 9. 已测试的 Windows 版本 10. 致谢 # 1. 简介 hyper-reV 是一个[内存自省](https://hvmi.readthedocs.io/en/latest/chapters/1-overview.html)和逆向工程[虚拟机监控器](https://www.redhat.com/en/topics/virtualization/what-is-a-hypervisor),通过利用 [Hyper-V](https://en.wikipedia.org/wiki/Hyper-V) 提供支持。该项目为客户机——即被 Hyper-V 虚拟化的 Windows 操作系统——提供以下能力:读写客户机[虚拟内存](https://wiki.osdev.org/Memory_management#Virtual_Address_Space)和[物理内存](https://wiki.osdev.org/Memory_management#Physical_Address_Space),将客户机虚拟地址转换为相应的客户机物理地址,[SLAT](https://en.wikipedia.org/wiki/Second_Level_Address_Translation) 代码 Hook(也称为 EPT/NPT Hook),以及对客户机隐藏整页物理内存。由于它利用了 Hyper-V,这意味着它也能在受 [HVCI](https://learn.microsoft.com/en-us/windows/security/hardware-security/enable-virtualization-based-protection-of-code-integrity?tabs=security) 保护的系统上工作。 还有一个用户模式组件,它充当[内核调试器](https://en.wikipedia.org/wiki/Kernel_debugger)。 # 2. 启动前 'uefi-boot' 模块是 hyper-reV 的 UEFI 驱动程序。它会复制一份 bootmgfw.efi。然后,bootmgfw.efi 的内容被替换为 'uefi-boot' 模块,以便在下次启动时执行。hyperv-attachment(插入到 Hyper-V 中的模块)也被保存到磁盘上,与 bootmgfw.efi 位于同一目录中。 # 3. 启动过程 ![启动过程流程图](https://raw.githubusercontent.com/noahware/hyper-reV/main/images/boot_process.png) ## 3.1. 恢复 bootmgfw.efi 一旦 'uefi-boot' 模块启动,原始的 bootmgfw.efi 文件以及任何与时间戳相关的元数据都会被恢复,以隐藏文件被篡改的痕迹。 ## 3.2. 堆 还使用 [UEFI boot_services!AllocatePages](https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#efi-boot-services-allocatepages) [分配](https://wiki.osdev.org/Memory_Allocation)了一个[堆](https://wiki.osdev.org/Heap)(因此它是 4kB 对齐的,而不是使用不保证该对齐方式的 [boot_services!AllocatePool](https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#id16))。 此堆用于分配: - hyperv-attachment 运行时映像缓冲区(随后 hyperv-attachment 文件在加载到内存后立即从磁盘删除)。 - 稍后使用的[恒等映射](https://wiki.osdev.org/Identity_Paging)的 [PML4 和 PDPT](https://wiki.osdev.org/Page_Tables#48-bit_virtual_address_space)。 - hyperv-attachment 的内部堆。 - Hyper-V SLAT [CR3](https://wiki.osdev.org/CPU_Registers_x86#CR3) 的深拷贝(见 4.5)。 所有这些内存随后通过将客户机 SLAT 映射中的每一页指向一个虚拟页面来对客户机隐藏。 ## 3.3. 启动 bootmgfw.efi 然后通过 [UEFI boot_services!LoadImage](https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#id37) 加载原始 bootmgfw.efi 映像。在 bootmgfw.efi!ImgpLoadPEImage(一个加载可移植可执行映像的例程)上应用一个 Hook。使用 [UEFI boot_services!StartImage](https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html#id38) 启动 bootmgfw.efi。 ## 3.4. winload.efi 当 bootmgfw.efi 加载 winload.efi 映像时,它会被 bootmgfw.efi!ImgpLoadPEImage Hook 拦截。然后在应用 winload.efi!ImgpLoadPEImage Hook 之前,完全移除 bootmgfw.efi 的 Hook。这个 winload.efi!ImgpLoadPEImage Hook 允许在 winload.efi 加载 hvloader.dll 时拦截其加载过程。 ## 3.5. hvloader.dll 与 Hyper-V 启动 一旦通过 winload.efi!ImgpLoadPEImage Hook 拦截了 hvloader.dll 的加载,在移除 winload.efi 上的 Hook 后,会在 hvloader.dll 内部的 Hyper-V 启动例程深处应用一个 Hook。下面是被 Hook 的 Hyper-V 启动例程的精简反编译结果(它在 hvloader.dll!HvlLaunchHypervisor 内部被调用): ``` void __fastcall hv_launch(std::uint64_t hyperv_cr3, std::uint8_t* hyperv_entry_point, std::uint8_t* entry_point_gadget, std::uint64_t guest_kernel_cr3) { __writecr3(guest_kernel_cr3); __asm { jmp entry_point_gadget } } ``` 这个被 Hook 的 Hyper-V 启动例程将跳转到一个小片段(gadget),该片段最终会到达 Hyper-V 的入口点。参数如下: - rcx = 一个 CR3,Hyper-V 从中复制某些 PML4e。 - rdx = 重定位后的 Hyper-V 入口点的虚拟地址。 - r8 = 跳转到 Hyper-V 入口点的小片段的地址。 - r9 = 客户机的内核 CR3(例如 Windows 11 24H2 上的 0x1AE000)。 ## 3.5.1. 加载 hyperv-attachment 继续 Hook 的过程,一个 PML4e 被插入到 rcx 持有的 CR3 中。此 PML4e 包含主机物理内存的恒等映射。支持 hyperv-attachment 的物理内存也通过此恒等映射进行映射,因此在 Hyper-V 的地址空间中存在 hyperv-attachment 的有效虚拟映射。加载的 hyperv-attachment 映像通过(PML4e/恒等映射的虚拟基址 + hyperv-attachment 映像的物理基址)进行[重定位](https://sabotagesec.com/pe-relocation-table/)。这种重定位意味着 hyperv-attachment 一旦启动,将能够在 Hyper-V 的地址空间下执行。 正如 @Iraq1337 在[他的帖子](https://www.unknowncheats.me/forum/4323297-post8.html)中所描述的,物理内存中存在几份不受 SLAT 保护的 Hyper-V 副本(客户机可以读取它们)。只有最终的 Hyper-V 映像缓冲区对客户机是隐藏的。在虚拟机监控器启动 Hook 中,rdx 保存支持最终映像缓冲区的虚拟地址,该缓冲区**将**受 SLAT 保护,因此可以在不向客户机披露的情况下对其应用补丁。在启动例程 Hook 中,在 Hyper-V 的 VM exit 处理程序上放置了一个 Hook,将其指向一个代码洞(code cave),其中[远跳转/长跳转指令](https://www.felixcloutier.com/x86/jmp)将 detour(转向)到 hyperv-attachment 的 VM exit 处理程序。所有这些都是在移除 Hyper-V 启动例程 Hook 之后完成的。 # 4. hyperv-attachment/ Hyper-V 启动后信息 ## 4.1. 如何支持不同的架构? hyperv-attachment 同时支持 Intel 和 AMD。这通过对特定架构代码的抽象和一些 #ifdefs 实现,意味着在同一代码库中支持两种架构。针对 Intel 或 AMD 编译 hyperv-attachment 的步骤在“7. 如何编译/使用”中描述。 ## 4.2. 入口点(Hyper-V 启动前) hyperv-attachment 的入口点在 Hyper-V 启动 detour 中被调用,并执行以下操作: - 设置堆管理器。 - 设置初始 SLAT 上下文分配。 - 设置处理器状态日志上下文。 - 从 uefi-boot 映像模块获取一些信息以供后续使用(例如 uefi-boot 映像的物理基址和大小)。 hyperv-attachment 通过之前设置的恒等映射访问主机(在硬件上运行并控制客户机的虚拟机监控器)物理内存。 ## 4.3. 首次 VM exit 在首次 VM exit 中,hyperv-attachment 执行以下操作: - 在 Hyper-V 的全局 IDT([中断描述符表](https://wiki.osdev.org/Interrupt_Descriptor_Table))中设置 NMI([不可屏蔽中断](https://wiki.osdev.org/Non_Maskable_Interrupt))处理程序。 - 设置 APIC([高级可编程中断控制器](https://wiki.osdev.org/APIC)),稍后用于向所有主机逻辑处理器触发 NMI 以同步 SLAT 缓存。 - 将 uefi-boot 映像清零,以防止客户机搜索它。 某些进程无法在首次 VM exit 中完成(尤其是与 SLAT 相关的),因为 Hyper-V 尚未完全完成初始化。在发生一定数量的 VM exit 后,hyperv-attachment 通过 SLAT 隐藏堆(最初在 uefi-boot 映像中设置的那个)。隐藏的 uefi-boot 堆内存包括 hyperv-attachment 的内部堆、映像分配和已分配的恒等映射页表结构。这是通过将所有[页表项](https://wiki.osdev.org/Page_Tables)的页帧号设置为堆中的一个空闲虚拟页面来实现的。 ## 4.4. APIC 在首次 VM exit 时,通过位于 [MSR](https://wiki.osdev.org/Model_Specific_Registers) 0x1B 的 'APIC base' 获取 [APIC](https://wiki.osdev.org/APIC) 的基本信息。如果 APIC 已启用,则检查使用的是 xAPIC 还是 x2APIC。如果 APIC 尚未启用,则启用最高可能版本的 APIC。对于 xAPIC,可以通过其在 [Local APIC](https://wiki.osdev.org/APIC#Local_APIC_configuration) 的主机物理地址访问 ICR(中断命令寄存器)。对于 x2APIC,通过代表 Local APIC 的 MSR 访问 ICR。 通过 ICR,可以向 Local APIC 发送命令。这稍后用于通过构建 ICR 请求向除当前正在执行的处理器之外的所有处理器发送不可屏蔽中断。在文章后面,将描述这些 NMI 如何用于同步 SLAT 代码 Hook(EPT/NPT Hook)。为此项目内部开发并单独发布的 APIC 库[可以在这里找到](https://github.com/noahware/APIC)。 ## 4.5. SLAT SLAT CR3 是一个描述将客户机物理内存转换/翻译为主机物理内存的 CR3。在 AMD 上,这被称为嵌套 CR3。在 Intel 上,这被称为 [EPT](https://en.wikipedia.org/wiki/Second_Level_Address_Translation#Extended_Page_Tables) 指针。 Hyper-V SLAT CR3 是由 Hyper-V 创建的全局 SLAT CR3。在 Intel 和 AMD 上,它由 Hyper-V 中的所有逻辑处理器共享。 Hook SLAT CR3 在 4.5.3.3 中解释。它是 hyperv-attachment 通过对 Hyper-V SLAT CR3 进行深拷贝而创建的 SLAT CR3。 SLAT [代码 Hook](https://stackoverflow.com/questions/467557/what-is-meant-by-the-term-hook-in-programming) 的实现如下: ## 4.5.1. Intel 上的代码 Hook ![Intel SLAT 违规流程图](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/1bb73c307b185037.png) 在 Hyper-V 中,所有逻辑处理器共享唯一的 1 个指针(Intel 的 SLAT CR3)。 当前核心的 EPT 指针在 EPT 违规处理程序中根据需要在 Hook SLAT CR3 和 Hyper-V SLAT CR3 之间切换。这是因为这两个 SLAT CR3 都持有目标页面的映射,但具有不同的权限。 添加 Hook 时,目标页面会发生以下情况: - 在 Hyper-V SLAT CR3 中,页面被设置为 --x(不可读,不可写,但可执行)。 - 在 Hyper-V SLAT CR3 中,页面的页帧号被设置为影子页面的页帧号。 - 在 Hook SLAT CR3 中,页面被设置为 rw-(可读,可写,但不可执行)。 - 在 Hook SLAT CR3 中,页面的页帧号被设置为原始页面的页帧号。 当在 Hyper-V SLAT CR3 下执行目标页面时,执行的是影子页面。由于权限是 --x,因此也无法对目标页面读取或写入。 现在,如果客户机对被 Hook 的页面进行读取或写入,将引发 EPT 违规。在 EPT 违规处理程序中,当前核心的 EPT 指针被设置为 Hook SLAT CR3。这意味着现在可以读取/写入目标页面,但只是在原始页帧号下(因此影子页面的内容被隐藏)。 如果再次执行该页面,这些更改将被反转,反之亦然。 这非常高效,因为仅在需要更改页面权限时才会发生 EPT 违规。 ## 4.5.2. AMD 上的代码 Hook ![AMD SLAT 违规流程图](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/146d4b5198185039.png) AMD 的 SLAT 实现称为 [NPT](https://en.wikipedia.org/wiki/Second_Level_Address_Translation#Rapid_Virtualization_Indexing)。 由于页表项中没有“读取访问”位,因此在 AMD 上通过 NPT 无法实现仅执行页面。这使得实现性能较低,因为每次执行目标页面时都会发生[嵌套页面错误](https://tandasat.github.io/Hypervisor-101-in-Rust/memory-virtualization/nested-page-fault.html)。 在 Hook SLAT CR3 中,所有未 Hook 的页面都设置为不可执行,这意味着它只能执行被 Hook 的页面。 添加 Hook 时,目标页面会发生以下情况: - 在 Hyper-V SLAT CR3 中,页面设置为不可执行(因此它**不能**在 Hyper-V SLAT CR3 下执行)。 - 在 Hook SLAT CR3 中,页面设置为可执行(因此它**可以**在 Hook SLAT CR3 下执行)。 - 在 Hook SLAT CR3 中,页帧号设置为影子页面的页帧号。 当在 Hyper-V SLAT CR3 下执行被 Hook 的页面时,由于该页面在该 SLAT CR3 中不可执行,会引发嵌套页面错误。在嵌套页面错误处理程序中,当前核心的 SLAT CR3 被设置为 Hook SLAT CR3。当执行返回给客户机时,被 Hook 的页面现在是可执行的(并且正在执行包含“隐藏内容”的影子页面)。 一旦在 Hook SLAT CR3 下执行到达未 Hook 的页面(由执行任何未 Hook 页面导致的嵌套页面错误发出信号),当前核心的 SLAT CR3 将恢复为 Hyper-V SLAT CR3,执行可以正常继续,直到再次执行被 Hook 的页面。 当页面被 Hook 时,目标页面之前和之后的页面在 Hook SLAT CR3 中被设置为可执行。这可以防止指令跨越被 Hook 页面的页面边界时出现的问题(由 @papstuc 建议)。 关于获取 [VMCB](https://blog.back.engineering/04/08/2022/#virtual-machine-control-block-vmcb---parts),@Iraq1337 提供了他如何操作的示例代码以及 Hook SLAT CR3 的设置。他还解释了 NPT Hook 背后的逻辑。 ## 4.5.3. AMD 和 Intel 通用的代码 Hook 功能 ## 4.5.3.1. 页面拆分/合并 如果必须拆分[大页](https://wiki.osdev.org/Page_Tables#48-bit_virtual_address_space)([PDe 或 PDPTe](https://wiki.osdev.org/Page_Tables#48-bit_virtual_address_space)),以便获得一个 [PTe](https://wiki.osdev.org/Page_Tables#48-bit_virtual_address_space) 来表示要 Hook 的目标 4kB 客户机物理页,那么当移除 Hook 时(如果该合并范围内没有其他 Hook),这些条目将被重新合并。 这样做是为了节省一些用于分配那些较低分页结构的堆内存,并且还能提高 SLAT 性能。用于描述 SLAT 代码 Hook 的 hook_entry_t 链表结构仅占用 16 字节,允许仅用 4kB 描述 256 个 EPT/NPT Hook。 ## 4.5.3.2. 使用 NMI 进行同步 APIC 用于向所有主机逻辑处理器发送 NMI 以使 SLAT 缓存失效。这与一个位图配对,信号通知哪些逻辑处理器需要使其缓存失效。这样做是因为 Hook 同步存在问题(@papstuc 建议同步 SLAT 缓存)。一旦逻辑处理器接收到 NMI,根据处理器处于主机还是客户机状态,将发生以下情况: ## 4.5.3.2.1. 主机状态下的 NMI 如果 NMI 命中处理器时处理器处于主机状态,它将被交付给主机 IDT 中描述的处理程序。在进入中断时,[所有通用和 XMM 寄存器](https://wiki.osdev.org/CPU_Registers_x86-64)都保存在堆栈上,然后调用 NMI 处理器函数。如果位图中信号指示需要清除 SLAT 缓存,NMI 处理器函数将清除它。 并非每次 NMI 都清除 SLAT 缓存的原因是 [Hyper-V 使用 NMI 进行处理器间通信](https://forum.osdev.org/viewtopic.php?p=345109#p345109)。这就是为什么有一个位图来信号通知逻辑处理器是否仍然需要刷新缓存。 NMI 处理器返回后,所有通用和 XMM 寄存器都恢复到其原始值。此后,hyperv-attachment 的 NMI 处理程序跳转到 Hyper-V 在 IDT 中设置的原始 NMI 处理程序(或者如果 Hyper-V 由于某种原因没有设置处理程序,则直接从中断返回)。 目前 AMD 不处理主机 NMI,但这没有什么影响,因为有许多频繁的客户机 NMI 退出,hyperv-attachment 可以利用它们来刷新 SLAT 缓存。Intel 同时处理主机 NMI 和导致客户机退出的 NMI。 ## 4.5.3.2.2. 客户机状态下的 NMI 如果 NMI 命中处理器时处理器处于客户机状态,客户机将以“物理 NMI”为由进行 VM exit,并从 VM exit 处理程序中调用相同的 NMI 处理器函数,无需保留寄存器。 ## 4.5.3.3. Hyper-V SLAT CR3 的深拷贝 当添加第一个 SLAT Hook 时,会对 Hyper-V SLAT CR3 进行深拷贝。这意味着复制所有 SLAT 条目。这是在 Hook SLAT CR3 中完成的。 ## 4.6. 将执行权交还 Hyper-V 如果 VM exit 不由 hyperv-attachment 处理,则执行将转移到 Hyper-V 的原始 VM exit 处理程序。 ## 4.7. Hypercall hyperv-attachment 暴露了供客户机进行的 [hypercall](https://wiki.xenproject.org/wiki/Hypercall)。该项目监控 [CPUID 指令](https://wiki.osdev.org/CPUID)的使用,以处理来自客户机的 hypercall。当客户机中执行 CPUID 指令时,会发生 VM exit。在其 CPUID 处理程序中,hyperv-attachment 检查它是否是来自 hyper-reV 的有效 hypercall(通过寄存器中的某些唯一值)。如果不是来自 hyper-reV 的有效 hypercall,则执行返回给 Hyper-V(如 4.6 中所述)。 ## 4.7.1. Hypercall 列表与描述 guest_physical_memory_operation - 读/写客户机物理内存 guest_virtual_memory_operation - 读/写客户机虚拟内存 translate_guest_virtual_address - 将客户机虚拟地址转换为客户机物理地址 read_guest_cr3 - 获取当前客户机 CR3 add_slat_code_hook - 添加 SLAT 代码 Hook remove_slat_code_hook - 移除 SLAT 代码 Hook hide_guest_physical_page - 对客户机隐藏客户机物理页面 log_current_state - 在陷阱帧中记录当前处理器状态,日志稍后刷新 flush_logs - 将所有日志刷新到客户机虚拟缓冲区 get_heap_free_page_count - 获取 hyperv-attachment 堆中剩余的空闲页面数量 # 5. 规避检测 该项目的主要目标之一是规避其他类似项目具有的检测向量。通过独立于 Hyper-V 映像分配 hyperv-attachment,该项目避免了检测因插入映像的大小而导致 Hyper-V 映像分配之后的内存分配发生偏移的情况。 此外,该项目仅对受 SLAT 保护的最终 Hyper-V 映像应用 Hook。这避免了检测客户机搜索不受 SLAT 保护的 Hyper-V 映像副本(例如,从磁盘初始加载时的副本)。此外,bootmgfw.efi(Windows 的引导加载程序,在启动前被 uefi-boot.efi 模块替换)的原始文件元数据(例如修改时间)被恢复,以隐藏其被篡改的痕迹。 # 6. 用户模式应用程序信息 用户模式应用程序充当内核调试器,也是展示该项目能力可以实现的功能的一个示例。它有许多可以通过命令行界面执行的“命令”。该应用程序使用 [CLI11](https://github.com/CLIUtils/CLI11) 进行命令解析。 ## 6.1. 命令用法与描述列表 rgpm - 从给定的客户机物理地址读取内存。 用法:rgpm physical_address size wgpm - 将内存写入给定的客户机物理地址 用法:wgpm physical_address value size cgpm - 将内存从给定源复制到目标(客户机物理地址) 用法:cgpm destination_physical_address source_physical_address size gvat - 将客户机虚拟地址转换为其对应的客户机物理地址,使用给定的客户机 CR3 值 用法:gvat virtual_address CR3 rgvm - 从给定的客户机虚拟地址读取内存(给定相应的客户机 CR3 值时) 用法:rgvm virtual_address CR3 size wgvm - 将内存写入给定的客户机虚拟地址(给定相应的客户机 CR3 值时) 用法:wgvm virtual_address CR3 value size cgvm - 将内存从给定源复制到目标(客户机虚拟地址)(给定相应的客户机 CR3 值时) 用法:cgvm destination_virtual_address destination_CR3 source_virtual_address source_CR3 size akh - 在指定的内核代码上添加 Hook(给定客户机虚拟地址)(asmbytes 格式:0xE8 0x12 0x23 0x34 0x45) 用法:akh [OPTIONS] virtual_address 示例:akh ntoskrnl.exe!PsLookupProcessByProcessId --monitor --asmbytes 0x90 0x90 --post_original_asmbytes 0x90 选项: --asmbytes --post_original_asmbytes --monitor rkh - 移除指定内核代码上先前放置的 Hook(给定客户机虚拟地址) 用法:rkh virtual_address gva - 获取别名的数值 用法:gva alias_name hgpp - 对客户机隐藏物理页面的真实内容 用法:hgpp physical_address fl - 从 Hook 刷新陷阱帧日志 用法:fl hfpc - 获取 hyperv-attachment 的堆空闲页数 用法:hfpc lkm - 打印已加载内核模块的列表 用法:lkm kme - 列出已加载内核模块的导出项(给定名称时) 用法:kme module_name dkm - 将内核模块转储到磁盘上的文件 用法:dkm module_name output_directory ## 6.2. 内核 Hook 启动时,应用程序在 ntoskrnl.exe(主 Windows 内核映像)中查找合适的页面用作“detour 持有者”。这是影子页面上被[内联 Hook](https://www.codereversing.com/archives/592)覆盖的 Hook 例程的原始字节所在的位置。 通过使用预先存在的页面,不必分配新的内核可执行页面(如果发现未知的内核页面正在执行,这对某些安全工具来说会很可疑)。该页面被 SLAT Hook,因此可执行内容将对客户机隐藏。 来自 ntoskrnl.exe 的此页面可能通常不会被执行,因此加载一个不会被主动执行的未使用内核驱动程序,并使用其 .text 节中的随机页面作为 detour 持有者可能更明智。 利用 SLAT 代码 Hook,可以使用用户模式应用程序 Hook 内核例程。这是通过在影子页面上应用内联 Hook 来完成的,然后跳转到保存原始字节的位置,即典型的 detour。 该命令允许用户指定一些汇编代码(以十六进制形式),以便在原始字节执行之前/之后执行(通过命令参数中的 --asmbytes / --post_original_asmbytes),以及使 Hook 例程在每次执行时记录处理器状态(通过命令参数中的 --monitor)。 与大多数其他虚拟机辅助调试器不同,该项目完全将 [rip 相对操作数](https://wiki.osdev.org/X86-64_Instruction_Encoding#RIP/EIP-relative_addressing)解析为其绝对值。这意味着用户可以在原始字节具有 rip 相对操作数的例程上放置 Hook(例如 jz 50 或相对 call/jmp 到例程)。这也解析了 rip 相对内存访问(例如 cmp [rip+x], 0)。 ## 6.3. 命令别名 用户模式应用程序还解析所有已加载的内核模块,因此用户可以通过名称引用模块的导出或基址(例如 ntoskrnl.exe!PsLookupProcessByProcessId 或 ntoskrnl.exe)。此外,进程执行时使用的当前 CR3 可以使用 'current_cr3' 引用。 ## 6.4. 刷新日志 日志[捕获通用寄存器(从 rax-r15)](https://wiki.osdev.org/CPU_Registers_x86-64)、[rip](https://wiki.osdev.org/CPU_Registers_x86#Pointer_Registers)、CR3 以及[堆栈](https://wiki.osdev.org/Stack)的快照。 这些日志是通过在内核 Hook 命令上放置 '--monitor' 标志生成的。这样,当执行 Hook 时,它会调用虚拟机监控器以每次记录处理器状态。当在用户模式应用程序中使用 'fl' / flush logs 命令刷新日志时,如下所示: 0. rip=0xFFFFF8049B0BA5E6 rax=0x3 rcx=0x538 rdx=0xFFFFAF869BF2F260 rbx=0x0 rsp=0xFFFFAF869BF2F208 rbp=0AF869BF2F360 rsi=0x80 rdi=0x538 r8=0xFFFFAF869BF2F5A0 r9=0xFFFFE486C68FA800 r10=0xFFFFF8049B0BA5E0 r11=0xFFFFAF869BF2F458 r12=0xFFFFF8042D693000 r13=0xFFFFE486CD91E000 r14=0xFFFFAF869BF2F2A8 r15=0x0 CR3=0x1B473C000 stack data: 0xFFFFF8042D6A7724 0x0 0x8 0xFFFF3B4D56ACD14A 0x0 # 7. 如何编译/使用 ## 7.1. 编译 'uefi-boot' 要编译 uefi-boot 模块,必须安装 NASM (https://nasm.us) 并“检查环境变量 NASM_PREFIX 是否正确设置为 NASM 安装路径”(引自 https://github.com/ionescu007/VisualUefi/#Installation)。 必须克隆所有子模块(VisualUEFI / EDK2)。它们将位于 uefi-boot\ext 中。 克隆仓库(包括子模块)的命令: ``` git clone --recurse-submodules https://github.com/noahware/hyper-reV.git ``` 此外,必须通过打开 uefi-boot\ext\edk2\build\EDK-II.sln 并构建整个解决方案来构建 EDK2 库。 ## 7.2. 特定架构的编译 必须选择 hyperv-attachment 为 Intel 或 AMD 构建。 要为 Intel 编译 hyperv-attachment:在 arch_config.h(位于 hyperv-attachment src 目录中)中 #define _INTELMACHINE。 要为 AMD 编译,只需注释掉上述 #define 行并重新构建。 无论在 hyperv-attachment 中指定何种配置,uefi-boot 模块和用户模式应用程序的二进制文件都适用于 Intel 和 AMD。 ## 7.3. 加载脚本 项目根目录中有一个脚本,当以管理员身份在与 uefi-boot.efi 和 hyperv-attachment.dll 文件相同的目录中运行时,会将 uefi-boot 模块和 hyperv-attachment 放置在 EFI 分区中。一旦运行此脚本,hyper-reV 将在下次启动时加载。 ## 7.4. 配合 Secure Boot 使用 要在启用 [Secure Boot](https://access.redhat.com/articles/5254641) 的情况下加载项目,可以利用易受攻击的[引导加载程序](https://wiki.osdev.org/Bootloader),如[这篇文章](https://habr.com/articles/446238/)所述。 ## 7.5. 配合 TPM 使用 如果要规避高级安全工具,如果该安全工具执行[启动证明](https://learn.microsoft.com/en-us/azure/attestation/tpm-attestation-concepts),则不建议在启用 [TPM](https://en.wikipedia.org/wiki/Trusted_Platform_Module) 的情况下运行该项目。这是因为它可以看到加载了 uefi-boot 二进制文件(通过存储在 TPM PCR 中的信息或通过[度量启动](https://learn.microsoft.com/en-us/azure/security/fundamentals/measured-boot-host-attestation)日志)。此段落的信息来自 [Zepta 的帖子](https://www.unknowncheats.me/forum/anti-cheat-bypass/623028-measuredboot-tpm.html)。 # 8. 源代码 源代码可在本 [GitHub](https://github.com/noahware/hyper-reV) 上找到。 # 9. 已测试的 Windows 版本 该项目已在 Intel 和 AMD 的以下版本上进行了测试: Windows 10 21H2, Windows 10 22H2, Windows 11 22H2, Windows 11 23H2, Windows 11 24H2。 如果项目不起作用,请确保应用了这些主要 Windows 版本的最新次要更新。系统还必须能够运行 Hyper-V。 # 10. 致谢 [John / @Iraq1337](https://github.com/vmp38) - 宝贵的建议,特别是在 AMD 理论、嵌套 CR3 恒等映射示例以及获取 VMCB 的方法方面。此外,他还建议在 Hyper-V 启动之前对其应用补丁。 [@papstuc](https://github.com/papstuc) - 关键建议,特别是关于在所有处理器之间同步 EPT/NPT 缓存的建议,他关于页面拆分的 AMD NPT Hook 的想法,以及用户模式应用程序中使用的[他的 Windows 文件格式解析库](https://github.com/papstuc/portable_executable)。 mylostchristmas - 发现先前通过 winload.efi 的 SLAB 分配器分配受 SLAT 保护的 SLAB 页面的方法在 24H2 上无法以相同方式工作。然后从项目中移除了此功能,并替换为内部 SLAT 保护。
标签:DMA攻击, EPT, Hyper-V, Hypervisor, NPT, Rootkit, SecList, SLAT, UEFI, UML, Web报告查看器, Windows内部机制, Zeek, 云资产清单, 代码钩子, 内存取证, 内存管理, 内核调试, 内省技术, 启动安全, 攻防技术, 漏洞分析, 系统底层, 自动回退, 虚拟化, 虚拟化安全, 虚拟机逃逸, 路径探测, 逆向工程