Windows用户态hook技术总结

IAT/EAI hook

属于地址hook,通过修改PE文件中导入地址表(IAT)或导出地址表(EAT)中的函数地址,将对目标API的调用重定向到自定义函数。

实现原理:

  1. 解析目标模块的PE头,定位到导入表(IMAGE_IMPORT_DESCRIPTOR)
  2. 遍历导入表,找到目标函数对应的IAT条目
  3. 修改IAT中该函数地址为自定义函数的地址(需注意内存页保护属性,通常需要VirtualProtect修改为可写)
  4. 当程序调用该API时,实际跳转到自定义函数执行

适用情况:

  • 适用于拦截通过显式导入(静态链接)调用的API
  • 常用于监控文件操作(CreateFile、ReadFile)、注册表操作、进程创建等
  • 对同一模块内所有调用统一生效,无需逐个修改调用点

注意事项:

  • 无法拦截通过LoadLibrary + GetProcAddress动态获取的函数调用
  • 修改IAT后,原始函数地址丢失,需保存原始地址以便在自定义函数中调用原函数
  • 需考虑DLL加载顺序问题,若目标DLL尚未加载则无法hook
  • 某些安全软件会检测IAT是否被篡改

inline hook

通过修改目标函数起始处的几个字节,跳转到自定义函数处。

实现原理:

  1. 保存目标函数开头的若干字节(通常5-14字节,取决于跳转指令长度)
  2. 在函数开头写入跳转指令(如x86的jmp rel32占5字节,x64需考虑地址空间问题)
  3. 自定义函数执行完毕后,执行被覆盖的原始指令,再跳回原函数剩余部分继续执行
  4. 通常使用jmp + 绝对地址或push ret方式实现跳转

适用情况:

  • 通用性强,可hook任意函数(无论静态导入还是动态获取)
  • 适用于需要深度监控或修改函数行为的场景
  • 可hook未导出的内部函数(需知道函数地址)

注意事项:

  • 多线程环境下需确保修改指令时的原子性,避免部分修改导致崩溃
  • 被hook函数若正在执行,修改可能导致不可预知的行为
  • 需处理指令边界对齐问题,避免覆盖不完整的指令
  • x64下需注意相对跳转范围(32位偏移仅±2GB),需使用jmp [rip+offset]push rax; mov rax, addr; jmp rax等方式
  • 某些函数开头较短(如只有2字节的ret),需特殊处理
  • 需绕过Windows的PatchGuard(仅内核态)和用户态的安全软件检测

异常hook

主要利用了Windows用户态异常处理机制,在触发异常时控制权转到我们设置的异常处理器(如调试器、VEH、SEH)中执行。

int3 hook

实现原理:

  1. 将目标函数开头的第一个字节替换为0xCC(int3指令)
  2. 当程序执行到该处时触发断点异常(EXCEPTION_BREAKPOINT)
  3. 通过VEH(Vectored Exception Handler)或调试器捕获该异常
  4. 在异常处理器中修改上下文(CONTEXT结构),将EIP/RIP指向自定义函数
  5. 恢复执行后程序跳转到自定义函数

适用情况:

  • 适合临时调试、动态分析场景
  • 可hook任意函数,包括动态获取的函数
  • 对原始函数代码修改极小(仅1字节)

注意事项:

  • 性能开销较大(每次调用都会触发异常处理流程)
  • 需处理异常处理器的递归调用问题
  • 多线程环境下需考虑同步
  • 某些反调试技术会检测0xCC断点
  • 需在异常处理器中正确恢复执行上下文

硬件断点hook

实现原理:

  1. 利用CPU的调试寄存器(DR0-DR3)设置硬件断点
  2. 将目标函数地址写入DR0-DR3,设置断点类型为执行断点
  3. 当CPU执行到该地址时触发异常(EXCEPTION_SINGLE_STEP)
  4. 通过VEH捕获异常,修改上下文实现hook
  5. 设置DR6标志位清除断点状态,DR7控制断点启用

适用情况:

  • 无需修改目标函数代码,隐蔽性较好
  • 适合监控关键API调用
  • 可设置读写断点监控内存访问

注意事项:

  • 最多只能设置4个硬件断点(DR0-DR3)
  • 线程相关:调试寄存器是线程上下文的一部分,需为每个线程单独设置
  • 性能开销较大(每次命中都会触发异常)
  • 需处理异常处理器的递归和嵌套
  • 某些虚拟化环境可能不支持硬件断点

内存断点hook

实现原理:

  1. 通过VirtualProtect修改目标函数所在内存页的保护属性(如改为PAGE_NOACCESS或PAGE_GUARD)
  2. 当程序访问该内存页时触发访问违例异常(EXCEPTION_ACCESS_VIOLATION)
  3. 在VEH中捕获异常,判断是否为目标地址
  4. 修改上下文将执行流转到自定义函数
  5. 临时恢复内存属性后单步执行,再重新设置断点

适用情况:

  • 可监控大范围内存区域的访问
  • 适合逆向分析和调试场景
  • 可同时监控读、写、执行操作

注意事项:

  • 性能开销极大(每次内存访问都触发异常)
  • 内存页粒度为4KB,同一页内的其他代码也会受影响
  • 需处理递归异常(恢复属性后单步执行再重新设置)
  • 多线程环境下需谨慎处理内存属性修改
  • 某些安全软件会监控内存属性变化

虚函数表hook

实现原理:

  1. 获取目标对象的虚函数表指针(vptr),通常位于对象内存布局的前4/8字节
  2. 定位到目标虚函数在虚函数表中的索引位置
  3. 修改虚函数表中的函数指针,指向自定义函数
  4. 当通过基类指针或引用调用虚函数时,实际执行自定义函数

适用情况:

  • 适用于C++虚函数调用的拦截
  • 常用于COM接口hook、MFC框架hook
  • 可hook特定对象实例或全局修改(修改虚函数表本身)

注意事项:

  • 需了解目标类的虚函数表布局(虚函数顺序)
  • 修改虚函数表会影响该类的所有实例(若修改的是共享虚函数表)
  • 若只修改某个对象的vptr,则只影响该对象
  • 需考虑多继承、虚继承等复杂场景
  • 某些编译器优化(如COMDAT折叠)可能导致虚函数表被合并
  • 需注意内存页保护属性,虚函数表通常位于只读段

消息钩子

通过SetWindowsHookEx函数安装系统级或线程级的消息过滤器实现,只能拦截消息事件。

实现原理:

  1. 调用SetWindowsHookEx注册钩子过程(Hook Procedure)
  2. 指定钩子类型(如WH_KEYBOARD、WH_MOUSE、WH_CBT等)
  3. 当指定事件发生时,系统调用钩子链中的钩子过程
  4. 钩子过程可监控、修改或阻止消息传递
  5. 全局钩子需将钩子过程放在DLL中,由系统注入到目标进程

适用情况:

  • 适合键盘记录、鼠标监控、窗口消息拦截
  • 可用于自动化测试、辅助功能开发
  • 系统级钩子可监控所有进程的消息

注意事项:

  • 全局钩子会降低系统性能(所有消息都需经过钩子链)
  • 钩子DLL会被注入到所有满足条件的进程中,需确保DLL兼容性
  • 需在DLL中正确处理钩子过程,避免死锁
  • 64位系统需注意32位和64位钩子的区别(不能混用)
  • UIPI(用户界面特权隔离)会阻止低权限进程钩取高权限进程的消息
  • 某些杀毒软件会检测SetWindowsHookEx的调用
  • 钩子过程应尽快返回,避免阻塞消息处理
相关推荐
caimouse13 小时前
Reactos 第 10 章 网络操作 — 10.3.1 NIC驱动
网络·windows
初圣魔门首席弟子14 小时前
Node.js 详细介绍(知识库版)
windows·qt·node.js·知识库
kingbal16 小时前
Windows:flutter环境搭建
windows·flutter
CodeKwang16 小时前
Windows 环境 OCCT 8.0 编译构建及与 Qt6 项目集成
windows·qt·opencascade
vx-Biye_Design17 小时前
springboot安阳地区研学旅游服务小程序-计算机毕业设计源码12785
java·vue.js·windows·spring boot·tomcat·maven·mybatis
gc_229917 小时前
学习在Windows中基于Docker部署Dify的步骤
windows·docker·dify
caimouse17 小时前
Reactos 第 10 章 网络操作 — 10.3.2 LAN驱动模块
服务器·网络·windows
A尘埃17 小时前
批处理命令(Linux/Mac、Windows项目启动脚本)
linux·windows·macos
染指11101 天前
26.RAG进阶(Advanced RAG)-假设性问题索引
人工智能·windows·agent·rag·advanced rag
就改了1 天前
Windows 环境 SkyWalking 完整实操教程
windows·微服务·skywalking