从Kdmapper到高级Rootkit检测:PTE Walk与NMI在反Rootkit中的应用
字数 2920 2025-10-01 14:05:52

高级内核Rootkit攻防技术:从Kdmapper到PTE Walk与NMI检测

前言

内核Rootkit以其高权限、高隐蔽性而著称,传统的防御手段效率低下,不适合实战攻防。本文从内核Rootkit攻击原理出发,剖析著名的Kdmapper手动映射驱动项目,并从内存扫描、堆栈遥测、通信检测三个角度阐述先进的防御手段,帮助读者理解现代内核安全领域的攻防实践。

1. Kdmapper 原理剖析

Kdmapper是利用Intel驱动中的任意内核地址读写漏洞,手动将驱动映射至内核地址空间执行,从而绕过Windows驱动签名验证(DSE)的经典工具。

1.1 核心流程:MapDriver

  1. 内核内存分配
    根据模式选择分配内存。AllocateIndependentPages模式防止分配出大页内存,避免某些检测。

    ULONG64 kernel_image_base = 0;
    if (mode == AllocationMode::AllocateIndependentPages) {
        kernel_image_base = intel_driver::MmAllocateIndependentPagesEx(image_size);
    } else { // AllocatePool by default
        kernel_image_base = intel_driver::AllocatePool(nt::POOL_TYPE::NonPagedPool, image_size);
    }
    
  2. PE文件处理
    在用户态解析PE文件,执行重定位等操作,与DLL反射加载过程类似。

  3. 写入内核内存
    将处理好的PE镜像数据写入到分配的内核内存地址。

    if (!intel_driver::WriteMemory(realBase, (PVOID)((uintptr_t)local_image_base + (destroyHeader ? TotalVirtualHeaderSize : 0)), image_size)) {
        // 错误处理
    }
    
  4. 设置内存保护属性
    将内存页设置为可执行(PAGE_EXECUTE_READ等)。

    if (!intel_driver::MmSetPageProtection(secAddr, secSize, prot)) {
        // 错误处理
    }
    
  5. 调用DriverEntry
    通过Hook NtAddAtom等函数,跳转执行驱动入口点。

    NTSTATUS status = 0;
    if (!intel_driver::CallKernelFunction(&status, address_of_entry_point, (PassAllocationAddressAsFirstParam ? realBase : param1), param2)) {
        // 错误处理
    }
    

1.2 痕迹清除

Kdmapper会抹除以下列表中的加载痕迹,增强隐蔽性:

  • PiDDBCacheTable:驱动加载记录表。
  • g_KernelHashBucketList:内核模块哈希缓存列表。
  • MmUnloadedDrivers:未加载驱动列表。

项目参考:FiYHer/system_trace_tool - 专门用于清理驱动加载/卸载痕迹。

2. 高级检测技术:PTE Walk

传统线性地址或物理地址扫描效率极低。PTE Walk通过遍历页表,快速定位实际映射的内存页,极大提升检测效率。

2.1 x64页表结构

Windows x64采用4级页表结构:

  1. PML4E (Page Map Level 4)
  2. PDPTE (Page Directory Pointer Table)
  3. PDE (Page Directory)
  4. PTE (Page Table)

2.2 页表项(PTE)结构

typedef union _HARDWARE_PTE_X64 {
    ULONG64 value;
    struct {
        ULONG64 present : 1;        // 页是否存在于物理内存
        ULONG64 write : 1;          // 是否可写
        ULONG64 owner : 1;          // 用户/内核模式
        // ... 其他标志位 ...
        ULONG64 large_page : 1;     // 是否为大页 (1GB or 2MB)
        ULONG64 global : 1;
        // ... 其他标志位 ...
        ULONG64 page_frame_number : 36; // 物理页帧号 (PFN)
        // ... 其他标志位 ...
        ULONG64 noExecute : 1;      // 执行禁止位 (NX)
    } Bits;
} HARDWARE_PTE_X64, *PHARDWARE_PTE_X64;

2.3 PTE Walk 算法步骤

  1. 获取CR3:读取CR3寄存器获取当前进程的PML4物理地址。
  2. 映射CR3:将PML4物理地址映射到内核虚拟地址空间。
  3. 遍历页表:从PML4开始逐级向下遍历:
    • 检查Present位,快速跳过未使用的虚拟地址范围。
    • 处理大页(1GB或2MB),直接获取大页属性。
    • 遍历到最后一级PTE,获取4KB页的属性。
  4. 记录内存属性:收集所有已映射页的虚拟地址、大小和属性(R/W/X)。

2.4 检测原理

Kdmapper等工具分配的内存通常具有可执行但未关联合法内核模块的特性。通过PTE Walk快速扫描整个内核地址空间(耗时<1秒),可以高效定位这些异常的可执行页,进而进行针对性扫描或特征提取。

3. 高级检测技术:NMI中断堆栈检查

另一种思路是通过中断触发,检查CPU各核心上正在执行线程的堆栈,寻找异常返回地址。

3.1 为何选择NMI?

  • 不可屏蔽中断 (Non-Maskable Interrupt):拥有最高优先级,Rootkit无法屏蔽此中断。
  • 其他方式(如IPI、DPC、APC)可能被绕过。

3.2 实现原理

  1. 发送NMI:向所有CPU核心发送NMI中断。
  2. 注册NMI回调:在每个CPU上触发回调函数。
  3. 安全采集堆栈
    • 关键点:NMI运行在极高IRQL,代码必须极简且快速,否则易导致系统蓝屏。
    • 方法:参考可靠方法(如周旋久师傅的方法),避免直接在回调中调用RtlWalkFrameChain
    • 步骤
      • 通过KPCR结构获取CPU上下文。
      • 通过TSS获取异常处理栈地址。
      • 解析machineFrame结构,捕获被中断线程的RIP(返回地址)和RSP
  4. 分析堆栈
    • 在NMI回调之外的较低IRQL环境下,分析采集到的RIP
    • 检查RIP是否指向已知合法内核模块(ntoskrnl.exe, win32k.sys等)的地址范围内。
    • RIP不在任何合法模块中,则判定为Rootkit线程。

3.3 优缺点

  • 优点:能有效检测无模块化Rootkit的执行线程。
  • 缺点:需要Rootkit线程相对活跃才能命中。对游戏外挂等频繁运行的Rootkit效果较好。

4. 通信与检测:.data ptr Hijacking

当攻击者使用自家Rootkit驱动时,会避免使用标准通信方式(如IRP),转而采用更隐蔽的技术,如.data段函数指针劫持。

4.1 原理

  • 背景:Windows图形子系统(如win32kbase.sys)包含许多用户态可触发、内核态执行的函数指针。
  • 漏洞:Patch Guard对内核.data段的指针监控并非全覆盖。
  • 攻击:找到这些函数指针,将其替换为攻击者控制的恶意函数地址。

4.2 利用步骤(以Win11 21H2为例)

  1. 定位目标函数

    • 目标函数:ApiSetEditionCreateWindowStationEntryPoint(未导出)。
    • 方法:先定位其上层导出函数NtUserCreateWindowStation
    PVOID funcAddr = GetSystemRoutineAddress(L"win32kbase.sys", "NtUserCreateWindowStation");
    
    • NtUserCreateWindowStation函数体中,通过特征码48 8B 05 ?? ?? ?? ?? 48 85 C0定位到ApiSetEditionCreateWindowStationEntryPoint中的函数指针。
  2. 挂靠进程上下文

    • win32kbase.sys在会话空间加载。需使用KeStackAttachProcess挂靠到GUI进程(如winlogon.exe)的上下文中才能操作其内存。
  3. 替换指针

    • 备份原函数指针。
    • 将指针替换为恶意Hook函数地址。
  4. 用户态触发

    • 用户态调用CreateWindowStationA,触发执行被替换的函数指针,从而执行内核态恶意代码。
    • 可实现通信、强杀安全软件、连接C2等功能。

4.3 检测方法

  • 检测难度:困难。因Windows内核中此类指针数量庞大,且随版本变化。
  • 现有方案
    1. 行为监控:捕捉异常高频的特定系统调用(如NtUserCreateWindowStation)。
    2. 指针验证:追踪到调用的内核函数,检查其.data指针是否指向预期模块内部。若指针指向win32kbase.sys等模块外部,则判定为篡改。
    3. 特征扫描:对已知易受攻击的指针进行定期扫描和校验。

5. 参考资源

  1. 反反 Rootkit 技术 - 第三部分:劫持指针
  2. Kernel-Adventures/DataPtrHijack
  3. 看雪论坛:简述常规的 "驱动.data通信" 如何利用/查找
  4. 看雪论坛:使用NMI中断检测无模块驱动
  5. FiYHer/system_trace_tool

高级内核Rootkit攻防技术:从Kdmapper到PTE Walk与NMI检测 前言 内核Rootkit以其高权限、高隐蔽性而著称,传统的防御手段效率低下,不适合实战攻防。本文从内核Rootkit攻击原理出发,剖析著名的Kdmapper手动映射驱动项目,并从内存扫描、堆栈遥测、通信检测三个角度阐述先进的防御手段,帮助读者理解现代内核安全领域的攻防实践。 1. Kdmapper 原理剖析 Kdmapper是利用Intel驱动中的任意内核地址读写漏洞,手动将驱动映射至内核地址空间执行,从而绕过Windows驱动签名验证(DSE)的经典工具。 1.1 核心流程:MapDriver 内核内存分配 : 根据模式选择分配内存。 AllocateIndependentPages 模式防止分配出大页内存,避免某些检测。 PE文件处理 : 在用户态解析PE文件,执行重定位等操作,与DLL反射加载过程类似。 写入内核内存 : 将处理好的PE镜像数据写入到分配的内核内存地址。 设置内存保护属性 : 将内存页设置为可执行(PAGE_ EXECUTE_ READ等)。 调用DriverEntry : 通过Hook NtAddAtom 等函数,跳转执行驱动入口点。 1.2 痕迹清除 Kdmapper会抹除以下列表中的加载痕迹,增强隐蔽性: PiDDBCacheTable :驱动加载记录表。 g_ KernelHashBucketList :内核模块哈希缓存列表。 MmUnloadedDrivers :未加载驱动列表。 项目参考:FiYHer/system_ trace_ tool - 专门用于清理驱动加载/卸载痕迹。 2. 高级检测技术:PTE Walk 传统线性地址或物理地址扫描效率极低。PTE Walk通过遍历页表,快速定位实际映射的内存页,极大提升检测效率。 2.1 x64页表结构 Windows x64采用4级页表结构: PML4E (Page Map Level 4) PDPTE (Page Directory Pointer Table) PDE (Page Directory) PTE (Page Table) 2.2 页表项(PTE)结构 2.3 PTE Walk 算法步骤 获取CR3 :读取CR3寄存器获取当前进程的PML4物理地址。 映射CR3 :将PML4物理地址映射到内核虚拟地址空间。 遍历页表 :从PML4开始逐级向下遍历: 检查 Present 位,快速跳过未使用的虚拟地址范围。 处理大页(1GB或2MB),直接获取大页属性。 遍历到最后一级PTE,获取4KB页的属性。 记录内存属性 :收集所有已映射页的虚拟地址、大小和属性(R/W/X)。 2.4 检测原理 Kdmapper等工具分配的内存通常具有 可执行但未关联合法内核模块 的特性。通过PTE Walk快速扫描整个内核地址空间(耗时 <1秒),可以高效定位这些异常的可执行页,进而进行针对性扫描或特征提取。 3. 高级检测技术:NMI中断堆栈检查 另一种思路是通过中断触发,检查CPU各核心上正在执行线程的堆栈,寻找异常返回地址。 3.1 为何选择NMI? 不可屏蔽中断 (Non-Maskable Interrupt) :拥有最高优先级,Rootkit无法屏蔽此中断。 其他方式(如IPI、DPC、APC)可能被绕过。 3.2 实现原理 发送NMI :向所有CPU核心发送NMI中断。 注册NMI回调 :在每个CPU上触发回调函数。 安全采集堆栈 : 关键点 :NMI运行在极高IRQL,代码必须极简且快速,否则易导致系统蓝屏。 方法 :参考可靠方法(如周旋久师傅的方法),避免直接在回调中调用 RtlWalkFrameChain 。 步骤 : 通过 KPCR 结构获取CPU上下文。 通过 TSS 获取异常处理栈地址。 解析 machineFrame 结构,捕获被中断线程的 RIP (返回地址)和 RSP 。 分析堆栈 : 在NMI回调之外的较低IRQL环境下,分析采集到的 RIP 。 检查 RIP 是否指向已知合法内核模块( ntoskrnl.exe , win32k.sys 等)的地址范围内。 若 RIP 不在任何合法模块中,则判定为Rootkit线程。 3.3 优缺点 优点 :能有效检测无模块化Rootkit的执行线程。 缺点 :需要Rootkit线程相对活跃才能命中。对游戏外挂等频繁运行的Rootkit效果较好。 4. 通信与检测:.data ptr Hijacking 当攻击者使用自家Rootkit驱动时,会避免使用标准通信方式(如IRP),转而采用更隐蔽的技术,如.data段函数指针劫持。 4.1 原理 背景 :Windows图形子系统(如 win32kbase.sys )包含许多用户态可触发、内核态执行的函数指针。 漏洞 :Patch Guard对内核 .data 段的指针监控并非全覆盖。 攻击 :找到这些函数指针,将其替换为攻击者控制的恶意函数地址。 4.2 利用步骤(以Win11 21H2为例) 定位目标函数 : 目标函数: ApiSetEditionCreateWindowStationEntryPoint (未导出)。 方法:先定位其上层导出函数 NtUserCreateWindowStation 。 在 NtUserCreateWindowStation 函数体中,通过特征码 48 8B 05 ?? ?? ?? ?? 48 85 C0 定位到 ApiSetEditionCreateWindowStationEntryPoint 中的函数指针。 挂靠进程上下文 : win32kbase.sys 在会话空间加载。需使用 KeStackAttachProcess 挂靠到GUI进程(如 winlogon.exe )的上下文中才能操作其内存。 替换指针 : 备份原函数指针。 将指针替换为恶意Hook函数地址。 用户态触发 : 用户态调用 CreateWindowStationA ,触发执行被替换的函数指针,从而执行内核态恶意代码。 可实现通信、强杀安全软件、连接C2等功能。 4.3 检测方法 检测难度 :困难。因Windows内核中此类指针数量庞大,且随版本变化。 现有方案 : 行为监控 :捕捉异常高频的特定系统调用(如 NtUserCreateWindowStation )。 指针验证 :追踪到调用的内核函数,检查其 .data 指针是否指向预期模块内部。若指针指向 win32kbase.sys 等模块外部,则判定为篡改。 特征扫描 :对已知易受攻击的指针进行定期扫描和校验。 5. 参考资源 反反 Rootkit 技术 - 第三部分:劫持指针 Kernel-Adventures/DataPtrHijack 看雪论坛:简述常规的 "驱动.data通信" 如何利用/查找 看雪论坛:使用NMI中断检测无模块驱动 FiYHer/system_ trace_ tool