Windows API钩子

news2025/1/12 0:55:25

原文链接:https://www.cnblogs.com/zhaotianff/p/18073138

有前面的文章中,我介绍了一个Windows API的监控工具(API Monitor)的使用。它内部就是使用了Hook机制,能Hook Windows API,能在我们钩选的API函数被调用时,进入中断。

这里我们借助github上一个库来自己实现一下类似API Monitor的功能。

MinHook

项目地址:https://github.com/TsudaKageyu/minhook

The Minimalistic x86/x64 API Hooking Library for Windows

完整的介绍可以看到:MinHook - The Minimalistic x86/x64 API Hooking Library - CodeProject

下面的内容来自作者codeproject原文,感兴趣的可以自行阅读,就不作翻译了。

项目背景

As you who are interested in Windows API hooking know, there is an excellent library for it by Microsoft Research named Detours. It's really useful, but its free edition (called 'Express') doesn't support the x64 environment. Though its commercial edition (called 'Professional') supports x64, it's too expensive for me to afford. It costs around US$10,000!

So I decided to write my own library or "poorman's Detours" from scratch. But I haven't designed my library as the perfect clone of Detours. It has just the API hooking functionality because that's all I want.

As of April 2016, this library is used in some projects: 7+ Taskbar TweakerBetter ExplorerConEmuDxWndMumbleNonVisual Desktop AccessOpen Broadcaster SoftwareQTTabBarx360ce, mods for some games and more. I am happy finding that this project is helpful to people.

实现原理

The basic concept of this software is the same as Microsoft Detours and Daniel Pistelli's Hook-Engine. It replaces the prologue of the target function with x86's JMP (unconditional jump) instruction to the detour function. It's safe, stable, and a proven method.

Overwriting the Target Function

In the x64/x86 instruction set, there are some forms of the JMP instruction. I decided to always use a 32 bit relative JMP of 5 bytes. It's the shortest form that can be used in reality. In this case, shorter is better.

In x86 mode, 32bit relative JMP covers the whole address space. Because the overflowed bits are just ignored in the relative address arithmetic, in x86 mode, the function addresses don't matter.

ASM

; x86 mode (assumed that the target function is at 0x40000000)

; 32bit relative JMPs of 5 bytes cover whole address space
0x40000000:  E9 FBFFFFBF      JMP 0x0        (EIP+0xBFFFFFFB)
0x40000000:  E9 FAFFFFBF      JMP 0xFFFFFFFF (EIP+0xBFFFFFFA)

; Shorter forms are useless in this case
; 8bit JMPs of 2 bytes cover -126 ~ +129 bytes
0x40000000:  EB 80            JMP 0x3FFFFF82 (EIP-0x80)
0x40000000:  EB 7F            JMP 0x40000081 (EIP+0x7F)
; 16bit JMPs of 4 bytes cover -32764 ~ +32771 bytes
0x40000000:  66E9 0080        JMP 0x3FFF8004 (EIP-0x8000)
0x40000000:  66E9 FF7F        JMP 0x40008003 (EIP+0x7FFF)

But, in x64 mode, it's a problem. It only covers the very narrow range in comparison with the whole address space. So I introduced a new function called 'Relay Function' which just has a 64 bit jump to the detour function and is placed near the target function. Fortunately, the VirtualAlloc() API function can accept the address to allocate, and it's an easy job to look for unallocated regions near the target function.

ASM

; x64 mode (assumed that the target function is at 0x140000000)

; 32bit relative JMPs of 5 bytes cover about -2GB ~ +2GB
0x140000000: E9 00000080      JMP 0xC0000005  (RIP-0x80000000)
0x140000000: E9 FFFFFF7F      JMP 0x1C0000004 (RIP+0x7FFFFFFF)

; Target function (Jump to the Relay Function)
0x140000000: E9 FBFF0700      JMP 0x140080000 (RIP+0x7FFFB)

; Relay function (Jump to the Detour Function)
0x140080000: FF25 FAFF0000    JMP [0x140090000 (RIP+0xFFFA)]
0x140090000: xxxxxxxxxxxxxxxx ; 64bit address of the Detour Function
Building the Trampoline Function

The target function is overwritten to detour. And, how do we call the original target function? In many cases, we have to call the original function from within the detour function. MinHook has a function called "Trampoline Function" for the purpose of calling the original function (and Daniel Pistelli call it "Bridge Function"). This is a clone of the prologue of the original function with the trailing unconditional jump for resuming into the original function. The real world examples are here. They are what MinHook actually creates.

We should disassemble the original function to know the instructions boundary and the instructions to be copied. I adopted Vyacheslav Patkov's Hacker Disassembler Engine (HDE) as a disassembler. It's small, light-weight and suitable for my purpose. I disassembled thousands of API functions on Windows XP, Vista, and 7 for examination purposes, and built the trampoline function for them.

ASM

; Original "USER32.dll!MessageBoxW" in x64 mode
0x770E11E4: 4883EC 38         SUB RSP, 0x38
0x770E11E8: 4533DB            XOR R11D, R11D
; Trampoline
0x77064BD0: 4883EC 38         SUB RSP, 0x38
0x77064BD4: 4533DB            XOR R11D, R11D
0x77064BD7: FF25 5BE8FEFF     JMP QWORD NEAR [0x77053438 (RIP-0x117A5)]
; Address Table
0x77053438: EB110E7700000000  ; Address of the Target Function +7 (for resuming)

; Original "USER32.dll!MessageBoxW" in x86 mode
0x7687FECF: 8BFF              MOV EDI, EDI
0x7687FED1: 55                PUSH EBP
0x7687FED2: 8BEC              MOV EBP, ESP
; Trampoline
0x0014BE10: 8BFF              MOV EDI, EDI
0x0014BE12: 55                PUSH EBP
0x0014BE13: 8BEC              MOV EBP, ESP
0x0014BE15: E9 BA407376       JMP 0x7687FED4

What if the original function contains the branch instructions? Of course, they should be modified to point to the same address as the original.

ASM

Shrink ▲   

; Original "kernel32.dll!IsProcessorFeaturePresent" in x64 mode
0x771BD130: 83F9 03           CMP ECX, 0x3
0x771BD133: 7414              JE 0x771BD149
; Trampoline
; (Became a little complex, because 64 bit version of JE doesn't exist)
0x77069860: 83F9 03           CMP ECX, 0x3
0x77069863: 74 02             JE 0x77069867
0x77069865: EB 06             JMP 0x7706986D
0x77069867: FF25 1BE1FEFF     JMP QWORD NEAR [0x77057988 (RIP-0x11EE5)]
0x7706986D: FF25 1DE1FEFF     JMP QWORD NEAR [0x77057990 (RIP-0x11EE3)]
; Address Table
0x77057988: 49D11B7700000000  ; Where the original JE points.
0x77057990: 35D11B7700000000  ; Address of the Target Function +5 (for resuming)

; Original "gdi32.DLL!GdiFlush" in x86 mode
0x76479FF4: E8 DDFFFFFF       CALL 0x76479FD6
; Trampoline
0x00147D64: E8 6D223376       CALL 0x76479FD6
0x00147D69: E9 8B223376       JMP 0x76479FF9

; Original "kernel32.dll!CloseProfileUserMapping" in x86 mode
0x763B7918: 33C0              XOR EAX, EAX
0x763B791A: 40                INC EAX
0x763B791B: C3                RET
0x763B791C: 90                NOP
; Trampoline (Additional jump is not required, because this is a perfect function)
0x0014585C: 33C0              XOR EAX, EAX
0x0014585E: 40                INC EAX
0x0014585F: C3                RET 

The RIP relative addressing mode is also a problem in the x64 mode. Their relative addresses should be modified to point to the same addresses.

ASM

; Original "kernel32.dll!GetConsoleInputWaitHandle" in x64 mode
0x771B27F0: 488B05 11790C00   MOV RAX, [0x7727A108 (RIP+0xC7911)]
; Trampoline
0x77067EB8: 488B05 49222100   MOV RAX, [0x7727A108 (RIP+0x212249)]
0x77067EBF: FF25 4BE3FEFF     JMP QWORD NEAR [0x77056210 (RIP-0x11CB5)]
; Address Table
0x77056210: F7271B7700000000  ; Address of the Target Function +7 (for resuming)

; Original "user32.dll!TileWindows" in x64 mode
0x770E023C: 4883EC 38         SUB RSP, 0x38
0x770E0240: 488D05 71FCFFFF   LEA RAX, [0x770DFEB8 (RIP-0x38F)]
; Trampoline
0x77064A80: 4883EC 38         SUB RSP, 0x38
0x77064A84: 488D05 2DB40700   LEA RAX, [0x770DFEB8 (RIP+0x7B42D)]
0x77064A8B: FF25 CFE8FEFF     JMP QWORD NEAR [0x77053360 (RIP-0x11731)]
; Address Table
0x77053360: 47020E7700000000 ; Address of the Target Function +11 (for resuming)

这个项目已经有很多年了,我大概是21年,通过github上的一个项目了解到这个库,

github上其实也有一些类似的库,如

EasyHook

https://github.com/EasyHook/EasyHook

Detours

https://github.com/microsoft/Detours

本文介绍的是minHook使用

使用前准备

将代码从github导下来(也可以使用动态库和静态库的方式,这里不作介绍了。我这里直接将源码放到项目里一起编译。)

另外,也可以使用nuget包管理器进行下载:NuGet Gallery | minhook 1.3.3

使用步骤

1、声明要HOOK的函数指针,如这里以MessageBoxW为例

typedef int (WINAPI* MESSAGEBOXW)(HWND, LPCWSTR, LPCWSTR, UINT);

2、定义函数指针变量,这个指针将会指向原始的API调用

MESSAGEBOXW fpMessageBoxW = NULL;

3、定义hook后的函数(也就是在调用MessageBoxW时,不会再调用原始函数,而是调用我们定义的函数)

这里我们在hook的函数里还是调用原始函数,但是将消息框内容改成了"Hooked"

 int WINAPI DetourMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
 {
      return fpMessageBoxW(hWnd, L"Hooked!", lpCaption, uType);
 }

4、初始化minHook

  
  if (MH_Initialize() != MH_OK)
  {
      return ;
  }

5、创建apihook(创建好后处理禁用的状态,需要启用才能生效)

   if (MH_CreateHookEx(&MessageBoxW, &DetourMessageBoxW, &fpMessageBoxW) != MH_OK)
   {
       return;
   }

6、启用apihook

    if (MH_EnableHook(&MessageBoxW) != MH_OK)
    {
        return;
    }

7、使用完成后,禁用apihook,并卸载minHook

     if (MH_DisableHook(&MessageBoxW) != MH_OK)
     {
         return ;
     }
 
     if (MH_Uninitialize() != MH_OK)
     {
         return ;
     }

另外minHook提供了一个可以指定模块和函数名的版本MH_CreateHookApi

    MH_STATUS WINAPI MH_CreateHookApi(
        LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal);

在前面的使用步骤中,调用的是MH_CreateHookEx,直接将函数以参数传递,MH_CreateHookApi函数可以指定模块路径和模块中导出的函数名。

还可以使用C++的模板功能进行一些简化,如下:

MH_CreateHook函数的简化

  template <typename T>
  inline MH_STATUS MH_CreateHookEx(LPVOID pTarget, LPVOID pDetour, T** ppOriginal)
  {
      return MH_CreateHook(pTarget, pDetour, reinterpret_cast<LPVOID*>(ppOriginal));
  }
  
  //原始调用
  if (MH_CreateHook(&MessageBoxW, &DetourMessageBoxW,
      reinterpret_cast<LPVOID*>(&fpMessageBoxW)) != MH_OK)
  {
      return ;
  }
  
  //简化后的调用
  if (MH_CreateHookEx(&MessageBoxW, &DetourMessageBoxW, &fpMessageBoxW) != MH_OK)
  {
      return;
  }

MH_CreateHookApi函数的简化

  template <typename T>
  inline MH_STATUS MH_CreateHookApiEx(
      LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, T** ppOriginal)
  {
      return MH_CreateHookApi(
          pszModule, pszProcName, pDetour, reinterpret_cast<LPVOID*>(ppOriginal));
  }
  
      //原始调用
      if (MH_CreateHookApi(L"user32","MessageBoxW", &DetourMessageBoxW,
          reinterpret_cast<LPVOID*>(&fpMessageBoxW)) != MH_OK)
      {
          return;
      }
  
      //简化后的调用
      if (MH_CreateHookApiEx(
          L"user32", "MessageBoxW", &DetourMessageBoxW, &fpMessageBoxW) != MH_OK)
      {
          return;
      }

使用示例

创建一个C++控制台程序,将前面导下来的src和include里面的文件复制到工程中,添加如下代码(来自codeproject):

这段代码的作用就是先在Hook的情况下,调用MessageBoxW函数,然后在未Hook的情况下,再调用MessageBoxW函数

#include <iostream>
#include "minHook/MinHook.h"

template <typename T>
inline MH_STATUS MH_CreateHookEx(LPVOID pTarget, LPVOID pDetour, T** ppOriginal)
{
     return MH_CreateHook(pTarget, pDetour, reinterpret_cast<LPVOID*>(ppOriginal));
 }
 
 template <typename T>
 inline MH_STATUS MH_CreateHookApiEx(
     LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, T** ppOriginal)
 {
     return MH_CreateHookApi(
         pszModule, pszProcName, pDetour, reinterpret_cast<LPVOID*>(ppOriginal));
 }
 
 typedef int (WINAPI* MESSAGEBOXW)(HWND, LPCWSTR, LPCWSTR, UINT);
 
 // Pointer for calling original MessageBoxW.
 MESSAGEBOXW fpMessageBoxW = NULL;
 
 // Detour function which overrides MessageBoxW.
 int WINAPI DetourMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
 {
     return fpMessageBoxW(hWnd, L"Hooked!", lpCaption, uType);
 }
 
 
 
 int main()
 {
     // Initialize MinHook.
     if (MH_Initialize() != MH_OK)
     {
         return 1;
     }
 
     // Create a hook for MessageBoxW, in disabled state.
     if (MH_CreateHookEx(&MessageBoxW, &DetourMessageBoxW, &fpMessageBoxW) != MH_OK)
     {
         return 1;
     }
     // Enable the hook for MessageBoxW.
     if (MH_EnableHook(&MessageBoxW) != MH_OK)
     {
         return 1;
     }
 
     // Expected to tell "Hooked!".
     MessageBoxW(NULL, L"Not hooked...", L"MinHook Sample", MB_OK);
 
     // Disable the hook for MessageBoxW.
     if (MH_DisableHook(&MessageBoxW) != MH_OK)
     {
         return 1;
     }
 
     // Expected to tell "Not hooked...".
     MessageBoxW(NULL, L"Not hooked...", L"MinHook Sample", MB_OK);
 
     // Uninitialize MinHook.
     if (MH_Uninitialize() != MH_OK)
     {
         return 1;
     }
 
     return 0;
 }

运行效果:

示例代码 

参考资料:

MinHook - The Minimalistic x86/x64 API Hooking Library - CodeProject

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1959655.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++必备知识--类和对象

类的定义 格式 class是类的关键字&#xff0c;Stack是类的名字(自己定义的)&#xff0c;在{}中定义类的主体。C中类的出现是为了替代C语言中的结构体&#xff0c;所以类的结构与结构体类似&#xff0c;{}之后需要加分号。类体中的内容称为类的成员&#xff0c;类中(声明)的变量…

国内优秀的消防报警设备企业「三江电子」×企企通启动采购数字化项目,共筑消防电子产品数字化新篇章

近日&#xff0c;深圳市高新投三江电子股份有限公司&#xff08;以下简称“三江电子”&#xff09;与企企通成功召开SRM采购供应链管理项目启动会。三江电子副总经理黄总、副总经理沈总、企企通高级副总裁徐总&#xff0c;以及双方项目负责人和项目组成员一同出席本次启动会。 …

AlmaLinux9安装中文语言包_zabbix没有中文语言包

更新你的系统包&#xff0c;如果系统最新可以忽略&#xff1a; sudo dnf update安装中文简体语言包 sudo yum install langpacks-zh_CN安装繁体中文包 sudo dnf install kde-l10n-Chinese-traditional安装完成后重启系统&#xff0c;以确保语言设置生效 设置系统为简体中文&…

《昇思25天学习打卡营第25天|第28天》

今天是打卡的第二十八天&#xff0c;实践应用篇中的计算机视觉中Vision Transformer图像分类。 从Vision Transformer&#xff08;ViT&#xff09;简介开始了解&#xff0c;模型结构&#xff0c;模型特点&#xff0c;实验的环境准备和数据读取&#xff0c;模型解析&#xff08…

Vue学习---vue 项目结构

这只是一个基础的目录结构&#xff0c;根据项目的复杂性和规模&#xff0c;可能会有所不同。 node_modules 项目依赖文件&#xff0c;其中包括很多基础依赖和自己安装的依赖。 public 存放公共资源和项目的主入口文件inde…

详细分析Sql Server索引的创建、查询、删除等基本知识(附Demo)

目录 前言1. 基本知识2. 索引2.1 创建2.2 删除2.3 查询 3. 总结 前言 原先分析过Sql的基本知识&#xff0c;感兴趣也可阅读&#xff1a; Mysql的两种存储引擎详细分析及区别&#xff08;全&#xff09;Mysql优化高级篇&#xff08;全&#xff09; 1. 基本知识 索引是在数据…

论文阅读-《Distant Supervision for Relation Extraction beyond the Sentence Boundary》

文章提出了首个将远程监督应用于跨句子关系提取的方法&#xff0c;通过整合句内和句间关系&#xff0c;利用图表示和多路径特征提取提高了准确性和鲁棒性。 摘要 文章提出了一种新的方法&#xff0c;用于在远程监督下进行跨句子的关系抽取。这种方法利用图表示来整合依赖和话…

滤芯材料做CE认证,按照什么EN标准测试?

要对滤芯材料进行CE认证&#xff0c;您需要遵循欧洲联盟的相关法规和标准。滤芯材料通常与产品安全性、电磁兼容性和环保性有关。以下是可能适用于滤芯材料的一些标准、要求和指令&#xff1a; 低电压指令(Low Voltage Directive&#xff0c;LVD)&#xff1a;滤芯材料通常包含电…

Vue3自研开源Tree组件:人性化的拖拽API设计

针对Element Plus Tree组件拖拽功能API用的麻烦&#xff0c;小卷开发了一个API使用简单的JuanTree组件。拖拽功能用起来非常简单&#xff01; 文章目录 使用示例allowDragallowDrop支持节点勾选支持dirty检测后台API交互 源码实现 使用示例 组件的使用很简单&#xff1a; 通过…

Kubernetes—二进制部署k8s集群搭建单机matser和etcd集群(1)

目录 组件部署 一、操作系统初始化配置 二、升级Liunx内核 三、部署docker引擎 四、部署etcd集群 1.在 master01 节点上操作 2.在 node节点上操作 检查etcd群集状态 增删查键 备份还原 etcd&#xff08;拓展&#xff09; 五.部署 Master 组件 1.上传解压 2.创建…

git查看记录详解

文章目录 git查看记录查看文件修改列表查看修改差异友好的查看修改记录结合多个选项查看记录示例输出 git查看记录 使用 git log 你不仅可以查看提交记录&#xff0c;还可以通过一些选项查看文件的修改列表、修改差异&#xff0c;并以更友好的方式查看修改记录。以下是一些常用…

ROS2入门到精通—— 2-14 ROS2实战:远程可视化实车上的文件

0 前言 ROS车的外接屏幕很小&#xff0c;在上面鼠标操作和键盘操作都比较麻烦&#xff0c;想要远程可视化ROS车上的文件&#xff0c;可以在本文学到一些用到实际工作中 1 ssh 终端操作远程桌面 ssh -Y pi192.168.33.200输入密码登陆&#xff0c;这样还是不能可视化文件&…

CS224W—02 Node Embeddings

CS224W—02 Node Embeddings Node Embeddings概念 传统的图机器学习&#xff1a; 给定一个输入图&#xff0c;抽取节点、边和图级别特征, 学习一个能将这些特征映射到标签的模型(像SVM&#xff0c; NN…)这个抽取不同级别特征的过程&#xff0c;就是特征工程feature enginee…

华为NetEngine AR1000V虚拟路由器 nfv

华为NetEngine AR1000V虚拟路由器 华为NetEngine AR1000V虚拟路由器彩页 - 华为企业业务 华为NetEngine AR1000V虚拟路由器 NetEngine AR1000V是华为公司在传统企业网络向SD-WAN转型的趋势下推出的虚拟路由器。 AR1000V基于NFV技术&#xff0c;可以部署在X86硬件平台的服务…

我出一道面试题,看看你能拿 3k 还是 30k!

大家好&#xff0c;我是程序员鱼皮。欢迎屏幕前的各位来到今天的模拟面试现场&#xff0c;接下来我会出一道经典的后端面试题&#xff0c;你只需要进行 4 个简单的选择&#xff0c;就能判断出来你的水平是新手&#xff08;3k&#xff09;、初级&#xff08;10k&#xff09;、中…

HarmonyOs~ArkUI进阶 之 状态管理

合理选择装饰器 最小化状态共享范围 在没有强烈的业务需求下&#xff0c;尽可能按照状态需要共享的最小范围选择合适的装饰器。 应用开发过程中&#xff0c;按照组件颗粒度&#xff0c;状态一般分为组件内独享的状态和组件间需要共享的状态 组件间需要共享的状态 组件间需…

细说MCU的DAC1和DAC2各自输出一通道模拟信号的方法

目录 一、参考硬件 二、 建立新工程 1.配置DAC1 2.配置DAC1的DMA 3.配置DAC2 4.配置DAC2的DMA 5.配置定时器 6.配置时钟和Debug 三、修改代码 四、 查看结果 一、参考硬件 本项目依赖的软件和硬件工程参考本文作者写的文章&#xff1a; 细说MCU的DAC1实现…

【中项第三版】系统集成项目管理工程师 | 第 11 章 规划过程组⑧ | 11.21 - 11.22

前言 第11章对应的内容选择题和案例分析都会进行考查&#xff0c;这一章节属于10大管理的内容&#xff0c;学习要以教材为准。本章上午题分值预计在15分。 目录 11.21 实施定量风险分析 11.21.1 主要输入 11.21.2 主要工具与技术 11.21.3 主要输出 11.22 规划风险应对 …

UPLOAD-LABS靶场[超详细通关教程,通关攻略]

---------------------------------------- 靶场环境&#xff1a; 下载链接&#xff1a; https://codeload.github.com/c0ny1/upload-labs/zip/refs/heads/master 使用小皮集成环境来完成这个靶场 将文件放到WWW目录下就可以进行访问 ------------------------------------…

【JavaEE】HTTP(3)

&#x1f921;&#x1f921;&#x1f921;个人主页&#x1f921;&#x1f921;&#x1f921; &#x1f921;&#x1f921;&#x1f921;JavaEE专栏&#x1f921;&#x1f921;&#x1f921; 文章目录 1.HTTP响应1.2状态码 2.HTTPS协议2.1概念2.2引入对称加密2.2.1概念2.2.2图解…