Hook深度研究:监视WOW64程序在系统中的执行情况
字数 2140 2025-08-18 17:33:08

WoW64程序深度监控:挂钩64位NTDLL的技术研究

1. 背景与概述

WoW64 (Windows 32-bit on Windows 64-bit) 是Windows系统允许32位应用程序在64位系统上运行的兼容层。在WoW64进程中,存在两个版本的NTDLL:

  1. 32位NTDLL:负责将系统调用转换到WoW64环境,并调整以适应x64 ABI
  2. 64位NTDLL:由WoW64环境调用,最终负责用户模式到内核模式的转换

传统安全产品通常只挂钩32位模块,这容易被攻击者绕过。通过挂钩64位NTDLL,安全产品可以获得更全面的进程行为监控能力。

2. 64位DLL注入技术

2.1 wow64log.dll劫持

原理

  • WoW64环境初始化时会尝试从system32目录加载wow64log.dll
  • 默认情况下该DLL不存在,可被利用作为注入点

实现步骤

  1. 创建64位DLL并命名为wow64log.dll
  2. 将其放入system32目录
  3. 系统会自动加载到所有WoW64进程中

优缺点

  • 优点:简单易实现,兼容所有Windows版本
  • 缺点:无法控制注入时机和进程选择

2.2 天堂之门(Heaven's Gate)技术

原理

  • 利用CS段寄存器切换(0x33)将执行模式从32位切换到64位
  • 直接调用64位NTDLL的LdrLoadDll函数

实现步骤

  1. 在目标进程执行32位代码
  2. 通过天堂之门切换到64位模式
  3. 调用64位LdrLoadDll加载目标DLL

关键代码

; 切换到64位模式
push 0x33                ; 64位代码段选择子
call $+5                 ; 将返回地址压栈
add [rsp], 5             ; 调整返回地址
retf                     ; 远返回切换到64位模式

; 64位代码开始
; 调用LdrLoadDll...

2.3 APC注入技术

原理

  • 使用异步过程调用(APC)在目标线程上下文中执行代码
  • 通过适配器thunk加载DLL

实现步骤

  1. 分配内存并写入适配器thunk代码
  2. 初始化APC并设置NormalRoutine为thunk地址
  3. 将APC插入目标线程队列

适配器thunk结构

typedef struct {
    PVOID LdrLoadDll;    // 64位LdrLoadDll地址
    PWSTR DllPath;       // DLL路径
} APC_CONTEXT, *PAPC_CONTEXT;

CFG问题

  • 适配器thunk位于4GB以下,被标记在WoW64 CFG位图
  • 但KiUserApcDispatcher会检查本机CFG位图,导致验证失败

2.4 解决CFG问题的方案

方案1:临时清除WoW64Process标志

原理

  • 修改EPROCESS的Wow64Process成员(偏移量0x428)
  • 使系统将进程视为原生64位进程

实现伪代码

// 保存原始值
OriginalWow64Process = *(PVOID*)(EProcess + 0x428);

// 清除Wow64Process标志
*(PVOID*)(EProcess + 0x428) = NULL;

// 分配内存 - 现在会标记在本机CFG位图
AllocateMemoryForThunk();

// 恢复原始值
*(PVOID*)(EProcess + 0x428) = OriginalWow64Process;

缺点

  • EPROCESS结构不稳定,偏移可能变化
  • 多线程环境下有风险

方案2:无thunk APC注入

原理

  • 直接使用LdrLoadDll作为APC例程
  • 利用KiUserApcDispatcher传递的隐藏参数

关键发现

  • KiUserApcDispatcher会将上下文结构指针放入R9
  • LdrLoadDll的ModuleHandle参数恰好使用R9
  • 上下文结构前几个成员在APC执行后不再需要

实现步骤

  1. 初始化APC,NormalRoutine设为LdrLoadDll地址
  2. NormalContext包含DLL路径等信息
  3. 系统自动处理参数传递

3. 挂钩64位NTDLL

3.1 准备工作

依赖处理

  • 只依赖原生NTDLL,避免其他64位DLL
  • 替换所有Win32 API为NTDLL对应函数:
    • VirtualProtect → NtProtectVirtualMemory
    • memcpy → RtlCopyMemory
    • memset → RtlFillMemory

项目配置

  • 链接器设置/NODEFAULTLIB
  • 显式添加NTDLL.lib
  • 禁用运行时检查(/RTC)
  • 禁用缓冲区安全检查(/GS-)
  • 指定DllMain为入口点

3.2 内联挂钩实现

标准挂钩流程

  1. 分配"蹦床"内存
  2. 复制目标函数前几条指令到蹦床
  3. 在蹦床中添加跳回原函数的指令
  4. 修改目标函数开头为跳转到蹦床
  5. 蹦床中跳转到detour函数

WoW64特殊问题

  • 64位NTDLL位于4GB以上地址空间
  • 蹦床位于4GB以下,距离超过2GB限制
  • 标准相对跳转(E9)无法使用

解决方案

; 6字节长跳转方案
push low_32_bits        ; 将低32位地址压栈
ret                     ; 弹出地址并跳转

3.3 递归问题处理

问题描述

  • detour函数中调用被挂钩函数会导致无限递归
  • 传统TLS方案不可用(缺少CRT/kernel32)

解决方案

  • 利用WoW64.dll未使用的TEB TLS插槽
  • 硬编码插槽位置存储递归深度

实现代码

// 获取当前线程TEB
PTEB teb = NtCurrentTeb();

// 使用预定义的未使用插槽
LONG* depthCounter = (LONG*)&teb->TlsSlots[10]; 

if (InterlockedIncrement(depthCounter) == 1) {
    // 实际detour逻辑
}

InterlockedDecrement(depthCounter);

4. 完整实现流程

  1. 注入64位DLL

    • 选择适合的注入方法(APC/天堂之门等)
    • 处理CFG验证问题
  2. 初始化挂钩引擎

    • 准备只依赖NTDLL的环境
    • 实现必要的内存操作函数
  3. 设置挂钩

    • 为目标函数创建蹦床
    • 安装长距离跳转
    • 处理递归控制
  4. 监控与处理

    • 在detour函数中分析调用参数
    • 执行自定义逻辑
    • 必要时调用原始函数

5. 限制与注意事项

  1. 系统版本差异

    • Windows 8.1 Update 3后NTDLL位置变化
    • EPROCESS结构偏移可能不同
  2. 安全缓解措施

    • 动态代码限制可能阻止注入
    • CFG导出抑制会影响挂钩
    • 代码完整性保护需要特殊处理
  3. 稳定性考虑

    • 避免关键系统函数无限递归
    • 确保内存操作原子性
    • 处理多线程竞争条件

附录:关键数据结构

APC_CONTEXT结构

typedef struct _APC_CONTEXT {
    PVOID LdrLoadDll;      // 64位LdrLoadDll地址
    PWSTR DllPath;         // 要加载的DLL路径
    ULONG Flags;           // 加载标志
    PHANDLE ModuleHandle;  // 输出模块句柄
} APC_CONTEXT, *PAPC_CONTEXT;

蹦床布局

+---------------------+
| 原始函数前几条指令   |
+---------------------+
| JMP 回原始函数+5    |
+---------------------+
| JMP 到detour函数    | (可选)
+---------------------+

CFG位图选择逻辑

PVOID MiSelectCfgBitMap(PEPROCESS Process, PVOID Address) {
    if (Process->Wow64Process == NULL) {
        return Process->CfgBitMap; // 原生进程
    }
    if (Address >= (PVOID)0x100000000) {
        return Process->CfgBitMap; // 高地址空间
    }
    return Process->Wow64CfgBitMap; // WoW64低地址空间
}
WoW64程序深度监控:挂钩64位NTDLL的技术研究 1. 背景与概述 WoW64 (Windows 32-bit on Windows 64-bit) 是Windows系统允许32位应用程序在64位系统上运行的兼容层。在WoW64进程中,存在两个版本的NTDLL: 32位NTDLL :负责将系统调用转换到WoW64环境,并调整以适应x64 ABI 64位NTDLL :由WoW64环境调用,最终负责用户模式到内核模式的转换 传统安全产品通常只挂钩32位模块,这容易被攻击者绕过。通过挂钩64位NTDLL,安全产品可以获得更全面的进程行为监控能力。 2. 64位DLL注入技术 2.1 wow64log.dll劫持 原理 : WoW64环境初始化时会尝试从system32目录加载wow64log.dll 默认情况下该DLL不存在,可被利用作为注入点 实现步骤 : 创建64位DLL并命名为wow64log.dll 将其放入system32目录 系统会自动加载到所有WoW64进程中 优缺点 : 优点:简单易实现,兼容所有Windows版本 缺点:无法控制注入时机和进程选择 2.2 天堂之门(Heaven's Gate)技术 原理 : 利用CS段寄存器切换(0x33)将执行模式从32位切换到64位 直接调用64位NTDLL的LdrLoadDll函数 实现步骤 : 在目标进程执行32位代码 通过天堂之门切换到64位模式 调用64位LdrLoadDll加载目标DLL 关键代码 : 2.3 APC注入技术 原理 : 使用异步过程调用(APC)在目标线程上下文中执行代码 通过适配器thunk加载DLL 实现步骤 : 分配内存并写入适配器thunk代码 初始化APC并设置NormalRoutine为thunk地址 将APC插入目标线程队列 适配器thunk结构 : CFG问题 : 适配器thunk位于4GB以下,被标记在WoW64 CFG位图 但KiUserApcDispatcher会检查本机CFG位图,导致验证失败 2.4 解决CFG问题的方案 方案1:临时清除WoW64Process标志 原理 : 修改EPROCESS的Wow64Process成员(偏移量0x428) 使系统将进程视为原生64位进程 实现伪代码 : 缺点 : EPROCESS结构不稳定,偏移可能变化 多线程环境下有风险 方案2:无thunk APC注入 原理 : 直接使用LdrLoadDll作为APC例程 利用KiUserApcDispatcher传递的隐藏参数 关键发现 : KiUserApcDispatcher会将上下文结构指针放入R9 LdrLoadDll的ModuleHandle参数恰好使用R9 上下文结构前几个成员在APC执行后不再需要 实现步骤 : 初始化APC,NormalRoutine设为LdrLoadDll地址 NormalContext包含DLL路径等信息 系统自动处理参数传递 3. 挂钩64位NTDLL 3.1 准备工作 依赖处理 : 只依赖原生NTDLL,避免其他64位DLL 替换所有Win32 API为NTDLL对应函数: VirtualProtect → NtProtectVirtualMemory memcpy → RtlCopyMemory memset → RtlFillMemory 项目配置 : 链接器设置/NODEFAULTLIB 显式添加NTDLL.lib 禁用运行时检查(/RTC) 禁用缓冲区安全检查(/GS-) 指定DllMain为入口点 3.2 内联挂钩实现 标准挂钩流程 : 分配"蹦床"内存 复制目标函数前几条指令到蹦床 在蹦床中添加跳回原函数的指令 修改目标函数开头为跳转到蹦床 蹦床中跳转到detour函数 WoW64特殊问题 : 64位NTDLL位于4GB以上地址空间 蹦床位于4GB以下,距离超过2GB限制 标准相对跳转(E9)无法使用 解决方案 : 3.3 递归问题处理 问题描述 : detour函数中调用被挂钩函数会导致无限递归 传统TLS方案不可用(缺少CRT/kernel32) 解决方案 : 利用WoW64.dll未使用的TEB TLS插槽 硬编码插槽位置存储递归深度 实现代码 : 4. 完整实现流程 注入64位DLL : 选择适合的注入方法(APC/天堂之门等) 处理CFG验证问题 初始化挂钩引擎 : 准备只依赖NTDLL的环境 实现必要的内存操作函数 设置挂钩 : 为目标函数创建蹦床 安装长距离跳转 处理递归控制 监控与处理 : 在detour函数中分析调用参数 执行自定义逻辑 必要时调用原始函数 5. 限制与注意事项 系统版本差异 : Windows 8.1 Update 3后NTDLL位置变化 EPROCESS结构偏移可能不同 安全缓解措施 : 动态代码限制可能阻止注入 CFG导出抑制会影响挂钩 代码完整性保护需要特殊处理 稳定性考虑 : 避免关键系统函数无限递归 确保内存操作原子性 处理多线程竞争条件 附录:关键数据结构 APC_ CONTEXT结构 蹦床布局 CFG位图选择逻辑