浅谈c hook 钩子的使用介绍

浅谈c hook 钩子的使用介绍

首页休闲益智连接圈更新时间:2024-07-28

一、基本概念:

hook是Windows平台的消息处理机制。 应用程序可以在其上设置子例程来监视指定窗口的某些消息,并且被监视的窗口可以由其他进程创建。 当消息到达时,它会在目标窗口处理函数之前被处理。 钩子机制允许应用程序拦截并处理窗口消息或特定事件。

钩子实际上是处理消息的程序段。 它通过系统调用挂接到系统中。 每当发送特定消息时,钩子程序在到达目标窗口之前捕获该消息,即钩子函数首先获得控制权。 此时,钩子函数可以处理(改变)消息,不处理而继续投递消息,或者强制结束消息投递。

2、运行机制:

1、hook列表和hook子流程:

每个 Hook 都有一个与其关联的指针列表,称为钩子列表,由系统维护。 该列表的指针指向Hook子例程调用的指定的、应用程序定义的回调函数,即Hook的各个处理子例程。 当与指定的 Hook 类型关联的消息发生时,系统将消息传递给 Hook 子例程。 有些Hook子进程只能监听消息,或者修改消息,或者停止消息的进程,以防止这些消息被传递到下一个Hook子进程或者目标窗口。 最近安装的钩子放在链的开头,最早安装的钩子放在链的末尾,即后添加的先获得控制权。

Windows 不要求钩子子进程的卸载顺序必须与安装顺序相反。 每当卸载一个钩子时,Windows都会释放它占用的内存并更新整个钩子列表。 如果程序安装了一个钩子,但在卸载该钩子之前结束,系统会自动为其卸载该钩子。

挂钩子例程是应用程序定义的回调函数(CALL 编程Inn BACK 函数)。 它不能定义为某个类的成员函数,只能定义为普通的C函数。 用于监视系统或特定类型的事件。 这些事件可以与特定线程相关联,也可以是系统中所有线程的事件。

钩子子例程必须遵循以下语法:

LRESULT CALLBACK HookProc int nCode, ARAM wParam, LPARAM lParam );

HookProc 是应用程序定义的名称。

nCode参数是Hook代码,Hook子程序通过这个参数来确定任务。 该参数的值取决于 Hook 类型。 每种类型的Hook都有自己的Hook代码特征字符集。

安卓

wParam和lParam参数的值取决于Hook代码,但它们的典型值包含有关发送或接收消息的信息。

2、Hook安装与释放:

使用 API 函数 SetWindowsHookEx() 将应用程序定义的挂钩子例程安装到挂钩列表中。 SetWindowsHookEx 函数始终将 Hook 子例程安装在 Hook 链的开头。 当指定类型的Hook监听的事件发生时,系统会调用与该Hook关联的Hook链开头的Hook子例程。 Hook链中的每个Hook子进程决定是否将此事件传递给下一个Hook子进程。 Hook子例程需要调用CallNextHookEx函数将事件传递给下一个Hook子例程。

HHOOK SetWindowsHookEx(      int idHook, // 钩子的类型,即它处理的消息类型      HOOKPROC lpfn, // 钩子子程的地址指针。如果ThreadId参数为0 // 或是一个由别的进程创建的线程的标识, // lpfn必须指向DLL中的钩子子程。 // 除此以外,lpfn可以指向当前进程的一段钩子子程代码。 // 钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数。      HINSTANCE hMod, // 应用程序实例的句柄。标识包含lpfn所指的子程的DLL。 // 如果dwThreadId 标识当前进程创建的一个线程, // 而且子程代码位于当前进程,hMod必须为NULL。 // 可以很简单的设定其为本应用程序的实例句柄。      DWORD dwThreadId // 与安装的钩子子程相关联的线程的标识符。 // 如果为0,钩子子程与所有的线程关联,即为全局钩子。       );

如果函数成功,则返回钩子子进程的句柄,如果失败,则返回NULL。

上述钩子子进程与线程的关联是指发送给钩子链中的线程的消息同时发送给钩子子进程并由钩子子进程首先处理。

调用钩子子例程中已获得控制权的钩子函数。 消息处理完成后,如果想让消息继续投递,那么就必须调用另一个SDK中的API函数CallNextHookEx来传递给它执行钩子列表。 下一个钩子子程序。 成功时,该函数返回钩子链中下一个钩子进程的返回值。 返回值的类型取决于钩子的类型。 该函数的原型如下:

LRESULT CallNextHookEx ( HHOOK hhk; int nCode; WPARAM wParam; LPARAM lParam; );

hhk是当前钩子的句柄编程栈,编程栈由SetWindowsHookEx()函数返回。

NCode 是传递给钩子进程的事件代码。

wParam和lParam分别是传递给hook子进程的wParam值,其具体含义与hook类型有关。

钩子函数还可以通过直接返回TRUE来丢弃消息并阻止消息的传递。 否则,其他挂钩的应用程序将不会收到挂钩通知,并且可能会产生不正确的结果。

该钩子使用后需要用UnHookWindowsHookEx()卸载,否则会造成麻烦。 释放钩子比较简单,UnHookWindowsHookEx()只有一个参数。 函数原型如下:

UnHookWindowsHookEx ( HHOOK hhk; );

如果成功,该函数返回 TRUE,否则返回 FALSE。

3、部分运行机制:

在Win16环境下,DLL的全局数据对于每个加载它的进程都是相同的; 在Win32环境中,情况发生了变化,DLL函数中的代码创建的任何对象(包括变量)都由调用它的线程或进程拥有。 当进程加载DLL时,操作系统自动将DLL地址映射到进程的私有空间,即进程的虚拟地址空间,同时还将DLL的全局数据复制一份到进程空间。 也就是说,每个进程拥有的同一个DLL的全局数据具有相同的名称,但其值不一定相同,并且它们之间互不干扰。

因此,如果要在Win32环境下的多个进程之间共享数据,就必须进行必要的设置。 访问同一个Dll的进程之间共享内存是通过内存映射文件技术实现的。 还可以将需要共享的数据分开,放在一个独立的数据段中,并将该段的属性设置为共享。 这些变量必须被赋予初始值,否则编译器会将没有初始值的变量放置在一个称为uninitialized的数据段中。

#pragma data_seg 预处理指令用于设置共享数据段。 例如:

#pragma data_seg("SharedDataName") HHOOK hHook=NULL; #pragma data_seg()

#pragma data_seg("SharedDataName") 和 #pragma data_seg() 之间的所有变量都将被访问 Dll 的所有进程看到和共享。

当进程隐式或显式调用动态库中的函数时,系统必须将动态库映射到进程的虚拟地址空间(以下简称“地址空间”)。 这使得 DLL 成为进程的一部分,作为该进程执行,并使用该进程的堆栈。

要创建全局钩子,我们需要使用DLL注入。 Hook的DLL工程主要代码如下:

#include #pragma data_seg("HookWnd") HHOOK g_HookWnd = NULL; //钩子句柄 HWND g_DestWnd = NULL; //目的窗口句柄,就是截获的消息要发往什么窗口 HINSTANCE g_hInst = NULL; #pragma data_seg() #pragma comment( linker, "/section:HookWnd,RWS" ) __declspec(dllexport) BOOL SetHook(HWND hWnd); __declspec(dllexport) void DestroyHook(); #define WM_MYMESSAGE (WM_USER 1) BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) { g_hInst = hinstDll;//保存应用程序实例 return TRUE; } LRESULT CALLBACK MyWndProc(int nCode, WPARAM wParam, LPARAM lParam) { CWPSTRUCT *pCwp = reinterpret_cast(lParam); if (pCwp->message == WM_SHOWWINDOW) { ::SendMessage(g_DestWnd, WM_MYMESSAGE, wParam, reinterpret_cast(pCwp->hwnd)); return 0; } else return CallNextHookEx(g_HookWnd, nCode, wParam, lParam); } BOOL SetHook(HWND hWnd) { if (NULL == hWnd) return FALSE; g_DestWnd = hWnd; //第三个参数还可通过些方法获得:GetModuleHandle("HookTest.dll") return (NULL != (g_HookWnd = ::SetWindowsHookEx(WH_CALLWNDPROC, MyWndProc, g_hInst, 0))); } void DestroyHook() { if (NULL != g_HookWnd) { UnhookWindowsHookEx(g_HookWnd); g_DestWnd = NULL; } }

4.系统钩子和线程钩子:

SetWindowsHookEx()函数的最后一个参数决定这个钩子是系统钩子还是线程钩子。

线程钩子用于监视指定线程的事件消息。 线程钩子通常位于当前线程或从当前线程派生的线程内。

系统挂钩监视来自系统中所有线程的事件消息。 由于系统钩子影响系统中的所有应用程序,因此钩子函数必须放置在单独的动态链接库(DLL)中。 系统自动将包含“钩子回调函数”的DLL映射到所有受钩子函数影响的进程的地址空间中,即将这个DLL注入到那些进程中。

一些注意事项:

(1)如果针对同一个事件(如鼠标消息)同时安装了线程钩子和系统钩子,系统会自动先调用线程钩子,然后调用系统钩子。

(2)对于同一个事件消息可以安装多个钩子处理进程,这些钩子处理进程形成钩子链。 当前钩子处理完成后,应该将钩子信息传递给下一个钩子函数。

(3) Hooks,尤其是系统Hooks,会消耗消息处理时间,降低系统性能。 仅在必要时安装挂钩,并在使用后立即卸载。

3. 挂钩式

每种类型的 Hook 都使应用程序能够监视不同类型的系统消息处理机制。 所有可用的 Hook 类型如下所述。

1. WH_CALLWNDPROC 和 WH_CALLWNDPROCRET 钩子

WH_CALLWNDPROC 和 WH_CALLWNDPROCRET 挂钩允许您监视发送到窗口过程的消息。 系统在消息发送到接收窗口过程之前调用WH_CALLWNDPROC Hook子例程,并在窗口过程处理完消息之后调用WH_CALLWNDPROCRET Hook子例程。

WH_CALLWNDPROCRET Hook 将指针传递给 CWPRETSTRUCT 结构,然后将其传递给 Hook 子例程。 CWPRETSTRUCT 结构包含处理该消息的窗口过程的返回值,以及与该消息关联的消息参数。

2.WH_CBT钩子

系统将在以下事件之前调用 WH_CBT Hook 子例程,其中包括:

1.激活、创建、销毁、最小化、最大化、移动、改变大小等窗口事件;

2、完整的系统使用说明;

3.来自系统消息队列的鼠标移动和键盘事件;

4.设置输入焦点事件;

5. 同步系统消息队列事件。

Hook 子例程的返回值决定系统是否允许或阻止其中一项操作。

3.WH_DEBUG钩子

在系统调用与系统中其他Hook关联的Hook子例程之前,系统会调用WH_DEBUG Hook子例程。 您可以使用此 Hook 来决定是否允许系统调用与其他 Hook 关联的 Hook 子例程。

4. WH_FOREGROUNDIDLE 钩子

当应用程序的前台线程空闲时,可以使用 WH_FOREGROUNDIDLE Hook 来执行低优先级任务。 当应用程序的前台线程即将空闲时,系统将调用WH_FOREGROUNDIDLE Hook子例程。

5. WH_GETMESSAGE 钩子

应用程序使用 WH_GetMessage Hook 来监视从 GetMessage 或 PeekMessage 函数返回的消息。 您可以使用 WH_GETMESSAGE Hook 来监视鼠标和键盘输入,以及发送到消息队列的其他消息。

6. WH_JOURNALPLAYBACK 钩子

WH_JOURNALPLAYBACK Hook 使应用程序能够将消息插入到系统消息队列中。 您可以使用此 Hook 来回放使用 WH_JOURRNALRECORD Hook 记录的连续鼠标和键盘事件。 只要安装了WH_JOURNALPLAYBACK Hook,普通的鼠标和键盘事件就无效。 WH_JOURNALPLAYBACK Hook 是全局 Hook,不能像线程特定 Hook 一样使用。 WH_JOURNALPLAYBACK Hook 返回一个超时值,该值告诉系统在处理来自播放 Hook 的当前消息之前需要等待多长时间(以毫秒为单位)。 这使得 Hooks 可以控制实时事件的播放。 WH_JOURNALPLAYBACK 是系统范围的本地挂钩,它们不会注入任何行程地址空间。

7. WH_JOURNALRECORD 钩子

WH_JOURNALRECORD Hook 用于监视和记录输入事件。 通常,您可以使用此 Hook 记录连续的鼠标和键盘事件,然后使用 WH_JOURNALPLAYBACK Hook 回放它们。 WH_JOURNALRECORD Hook 是全局 Hook,不能像线程特定 Hook 一样使用。 WH_JOURNALRECORD 是系统范围的本地挂钩,它们不会注入任何行程地址空间。

8.WH_KEYBOARD挂钩

在应用程序中,WH_KEYBOARD Hook用于监视WM_KEYDOWN和WM_KEYUP消息,这些消息通过GetMessage或PeekMessage函数返回。 您可以使用此 Hook 来监视输入到消息队列中的键盘消息。

9. WH_KEYBOARD_LL 钩子

WH_KEYBOARD_LL Hook 监视输入到线程消息队列的键盘消息。

10.WH_MOUSE钩子

WH_MOUSE Hook 监视从 GetMessage 或 PeekMessage 函数返回的鼠标消息。 使用此 Hook 可以监视输入到消息队列的鼠标消息。

11. WH_MOUSE_LL 钩子

WH_MOUSE_LL Hook 监视输入到线程消息队列的鼠标消息。

12. WH_MSGFILTER 和 WH_SYSMSGFILTER 钩子

WH_MSGFILTER 和 WH_SYSMSGFILTER Hooks 允许我们监视菜单、滚动条、消息框、对话框消息并发现用户使用 ALT TAB 或 ALT ESC 组合键来切换窗口。 WH_MSGFILTER Hook 只能监视传递到菜单、滚动条、消息框的消息以及传递到由安装了 Hook 子例程的应用程序创建的对话框的消息。 WH_SYSMSGFILTER Hook 监视所有应用程序消息。

WH_MSGFILTER 和 WH_SYSMSGFILTER Hook 允许我们在模式循环期间过滤消息,这相当于在主消息循环中过滤消息。

WH_MSGFILTER Hook可以通过调用CallMsgFilter函数直接调用。 通过使用此函数,应用程序可以在模式循环期间使用与主消息循环中相同的代码来过滤消息。

13.WH_Shell钩子

Shell 应用程序可以使用 WH_SHELL Hook 来接收重要通知。 当 shell 应用程序处于活动状态并且顶层窗口被创建或销毁时,系统将调用 WH_SHELL Hook 子例程。

WH_SHELL一共有5种情况:

1. 只要一个顶级的、无主的窗口被创建、激活或销毁;

2、当任务栏需要重绘按钮时;

3、当系统需要在任务栏上显示程序的最小化形式时;

4.当前键盘布局状态发生变化时;

5、当用户按Ctrl Esc执行任务管理器(或同级程序)时。

按照惯例,没有 shell 应用程序会接收 WH_SHELL 消息。 因此,在应用程序可以接收WH_SHELL消息之前,应用程序必须调用SystemParametersInfo函数来注册自己。

以上就是本文的全部内容。 希望对大家的学习有所帮助,也希望大家支持我们。

本文标题:浅谈c hooks的使用

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved