windows钩子、SetWindowsHookEx函数的使用

Windows钩子简介

Windows钩子技术是一种Windows操作系统提供的机制,允许程序在特定事件发生时拦截并处理操作系统消息、事件或其他程序的输入和输出。钩子可以被用来监视和记录用户的操作、增加或修改某些功能,或者对某些事件作出反应,以此来实现各种自动化和定制化的需求。

钩子技术可以通过两种方式实现:全局钩子和局部钩子。全局钩子会拦截所有应用程序的消息和事件,而局部钩子则只拦截指定的应用程序或线程的消息和事件。

如果是全局钩子或者跨进程钩子,钩子的回调函数需要写在.dll动态链接库文件中,在使用SetWindowsHookEx函数为进程挂载钩子后,被挂载钩子的进程会去加载内存中的.dll文件,这时.dll文件中的函数可以共享该进程中的所有资源(dll注入原理)。

钩子是依附于线程的,而消息循环是线程处理消息的机制,因此必须在安装钩子的主程序所在的线程中创建消息循环。在安装钩子后,当Windows操作系统检测到需要被钩子处理的事件时,将会把事件信息封装成消息发送到安装钩子的主程序的消息队列中,安装钩子的主程序需要在消息循环中接收并处理这些消息,这样才能够完成钩子的处理过程。

此外,安装钩子的主程序需要运行在一个独立的线程中,这样可以防止钩子处理逻辑阻塞其他线程的执行。在这种情况下,线程的主循环必须调用 GetMessagePeekMessage 函数以等待消息的到来。因此,必须在主程序中创建一个消息循环以便及时接收和处理钩子的消息,从而完成钩子的功能。

微软Hook介绍


下面是一个跨进程钩子的举例

dll文件

#include <windows.h>
#include <iostream>

#define MY_API __declspec(dllexport)
extern "C" MY_API HOOKPROC getDllHookProc(VOID);

BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH: 
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	// 所有小于零的是系统消息不做处理如果不放行系统消息会卡死
	if (nCode >= 0)
	{
		std::cout << "nCode:" << nCode << std::endl;
		/*
		return 1;
		如果返回一个非零的数则该消息无法被捕获
		*/
	}
	
	// 调用下一个钩子一定要调用下一个钩子否则会卡死
	return CallNextHookEx(NULL, nCode, wParam, lParam);
}

HOOKPROC getDllHookProc(VOID) {
	// 返回钩子回调函数
	return HookProc;
}

钩子回调函数通常有三个参数:

nCode:表示钩子代码,是一个整数值。如果小于0,则是系统消息,需要将钩子信息传递给下一个钩子,如果大于等于0,则可拦截。

wParam:表示一个消息或一个系统事件的参数,通常是一个虚拟键码或者一个句柄等。

lParam:表示一个消息或一个系统事件的参数,通常是一个指向 MSG 或 KBDLLHOOKSTRUCT 等结构体的指针。

这三个参数中,最重要的是 lParam 参数,因为它包含了所有的消息和事件信息,通常需要对其进行解析,以便获取具体的信息,lParam指向的结构体不确定,和SetWindowsHookEx设置的监听类型有关。


.exe文件

#include<Windows.h>
#include<iostream>

using namespace std;

typedef HOOKPROC(*func)(void);

int main() {

	HINSTANCE hDll; //DLL句柄
	// 加载dll
	hDll = LoadLibrary(L"testDll.dll");

	if (hDll != NULL)
	{
		// 这里通过窗口标题获取窗口句柄,注意FindWindow只能获取顶级窗口
		HWND hwnd = FindWindow(NULL, L"记事本");
		// 获取钩子回调函数
		func proc = (func)GetProcAddress(hDll, "getDllHookProc");
		// 安装钩子
		HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, proc(), hDll, 
						GetWindowThreadProcessId(hwnd, NULL));
		
		// 一定要有消息循环不然消息会卡死
		MSG msg;
		while (GetMessage(&msg, NULL, 0, 0)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		
		// 卸载钩子
		UnhookWindowsHookEx(hook);

	}
	return 0;
}

SetWindowsHookEx函数
参数解释如下:

idHook: 要安装的钩子的类型。可以是下列值之一:
WH_CALLWNDPROC: 监听全局或局部的窗口消息,每次消息被发送到窗口过程时触发。
WH_CALLWNDPROCRET: 监听全局或局部的窗口消息,每次窗口过程处理完消息后触发。
WH_CBT: 监听全局或局部的窗口事件,如窗口创建、销毁、移动、大小调整等。
WH_KEYBOARD: 监听全局或局部的键盘输入事件。
WH_MOUSE: 监听全局或局部的鼠标事件。
WH_MSGFILTER: 监听全局或局部的窗口消息,包括按键、鼠标、定时器等。
WH_SHELL: 监听全局的 shell 事件。
WH_SYSMSGFILTER: 监听全局的窗口消息,包括键盘、鼠标、定时器等。
WH_FOREGROUNDIDLE: 监听全局的空闲时间。
WH_JOURNALRECORD: 监听全局的系统事件记录。
WH_JOURNALPLAYBACK: 监听全局的系统事件回放。
WH_DEBUG: 监听全局的调试事件。
WH_MOUSE_LL: 监听全局的鼠标事件,使用鼠标低级别钩子。
WH_KEYBOARD_LL: 监听全局的键盘事件,使用键盘低级别钩子。
等等

lpfn: 指向钩子处理函数的指针。钩子过程的参数和返回值的类型取决于 idHook 的值。

hMod: 包含钩子处理函数的 DLL 的句柄。如果钩子过程在钩子处理函数所在的进程中,则为 NULL。

dwThreadId: 线程标识符,标识接收钩子信息的线程。如果为 0,则钩子过程与所有线程共享。

SetWindowsHookEx 函数成功后返回钩子的句柄。可以使用返回的句柄来卸载钩子。

需要注意的是,钩子处理函数应该尽快完成处理,以免拖慢系统性能。此外,由于全局钩子会监视所有进程的事件,可能会产生安全风险,因此应该谨慎使用。

微软参考

消息循环

GetMessage():从消息队列中取得一个消息,如果队列为空,则等待直到有消息到来。

TranslateMessage():将一个虚拟键消息翻译为字符消息,并将其放回消息队列中。

DispatchMessage():将一个消息派发给相应的窗口过程函数或者钩子函数。

这三个函数通常一起使用,构成了一个消息循环。当应用程序启动后,会创建一个消息队列,然后调用 GetMessage() 函数等待消息到来,一旦消息到来,就将其交给 TranslateMessage() 函数进行翻译,最后交给 DispatchMessage() 函数进行处理。这样循环进行,直到 GetMessage() 函数返回 0,表示退出消息循环。在消息循环中,每个消息都会依次被处理,直到消息队列为空。


说在最后

安全申明:本人才疏学浅,若有任何谬误,欢迎指正

我的博客:ひかりの博客
csdn主页:csdn博客主页

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows钩子函数是一种Windows API机制,它允许程序在操作系统中拦截和监视特定事件或消息。这些事件或消息可以是键盘、鼠标、消息队列等。钩子函数通常用于记录用户输入,或者在特定条件下触发自定义操作。 实现Windows钩子函数需要以下步骤: 1. 定义钩子函数 钩子函数是一个回调函数,当特定事件或消息发生时,操作系统将调用该函数钩子函数需要根据钩子类型和事件类型进行定义,例如键盘钩子函数可以监视按键事件,鼠标钩子函数可以监视鼠标事件等。 2. 安装钩子 安装钩子需要使用`SetWindowsHookEx`函数。该函数需要三个参数:钩子类型、钩子函数地址、以及钩子函数所属进程的句柄。钩子类型可以是全局钩子或局部钩子,具体取决于监视的事件或消息。 3. 卸载钩子 卸载钩子需要使用`UnhookWindowsHookEx`函数。该函数需要一个参数,即之前安装钩子时返回的句柄。 下面是一个示例键盘钩子函数的实现: ```c++ LRESULT CALLBACK KeyboardHook(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0) { // 拦截到键盘事件 PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)lParam; if (wParam == WM_KEYDOWN) { // 按键按下事件 // 处理按键事件 } } return CallNextHookEx(NULL, nCode, wParam, lParam); // 调用下一个钩子 } int main() { HHOOK hook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHook, NULL, 0); // 安装键盘钩子 // ... UnhookWindowsHookEx(hook); // 卸载键盘钩子 return 0; } ``` 在上面的示例中,`KeyboardHook`函数是一个键盘钩子函数,它拦截键盘事件并进行处理。在`main`函数中,使用`SetWindowsHookEx`函数安装了一个全局键盘钩子,并使用`UnhookWindowsHookEx`函数卸载了该钩子
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值