windows钩子、SetWindowsHookEx函数的使用

Windows钩子简介

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

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

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

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

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

微软Hook介绍


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

dll文件

#include 
#include 

#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
#include

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博客主页

你可能感兴趣的:(学习笔记,windows,microsoft,c++)