全局钩子注入
前言:
windows中大部分的应用程序都是基于消息机制的,它们都有一个消息过程函数,根据不同的消息完成不同的功能。而消息钩子是windows提供的一种消息过滤和预处理机制,可以用来截获和监视系统中的消息。按照钩子作用范围不同,又可以分为局部钩子和全局钩子。局部钩子是针对某个线程的,而全局钩子是作用于整个系统的基于消息的应用。全局钩子需要使用DLL文件,在DLL文件中实现相应的钩子函数。
函数介绍:
HHOOK WINAPI SetWindowsHookExW( _In_ int idHook, _In_ HOOKPROC lpfn, _In_opt_ HINSTANCE hmod, _In_ DWORD dwThreadId);
(1).idHook 要安装的钩子(HOOK)的类型,它决定了 HOOKPROC 被调用的时机,可选参数如下。
值 | 含义 |
WH_MSGFILTER = -1 | 线程级,截获用户与控件交互的消息 |
WH_JOURNALRECORD = 0 | 系统级,记录所有消息队列送出的输入消息 |
WH_JOURNALPLAYBACK = 1 | 系统级,回放由WH_JOURNALRECORD记录的消息 |
WH_KEYBOARD = 2 | 系统级或线程级,截获键盘消息 |
WH_GETMESSAGE = 3 | 系统级或线程级,截获从消息队列送出的消息 |
WH_CALLWNDPROC = 4 | 系统级或线程级,截获发送到目标窗口的消息 |
WH_CBT = 5 |
系统级或线程级,截获系统基本消息 例如:窗口的创建,激活,关闭,最大/最小化,移动等 |
WH_SYSMSGFILTER = 6 | 系统级,截获系统范围内用户与控件交互的消息 |
WH_MOUSE = 7 | 系统级或线程级,截获鼠标消息 |
WH_HARDWARE = 8 | 系统级或线程级,截获非标准硬件(非鼠标,键盘)的消息 |
WH_DEBUG = 9 | 系统级或线程级,在其它钩子调用前调用,用于调试钩子 |
WH_SHELL = 10 | 系统级或线程级,截获发给外壳应用程序的消息 |
WH_FOREGROUNDIDLE = 11 | 系统级或线程级,在程序前台线程空闲时调用 |
WH_CALLWNDPROCRET = 12 |
系统级或线程级,截获目标窗口处理完的消息 在SendMessage被调用后发生 |
WH_KEYBOARD_LL = 13 | 系统级,截获全局键盘消息 |
WH_MOUSE_LL = 14 |
系统级,截获全局鼠标消息 |
(2).lpfn 指向钩子回调函数的指针。如果最后一个参数 dwThreadId 为0或者是其它进程创建的线程标识符,则 lpfn 参数必须指向DLL中的钩子回调函数,即 HOOKPROC 函数必须在DLL中实现。否则,lpfn 可以指向与当前进程相关联的代码中的钩子过程。
(3).hmod 包含由 lpfn 参数指向的钩子回调函数的DLL句柄。如果 dwThreadId 参数指定由当前进程创建线程,并且钩子回调函数位于当前进程关联的代码中,则 hmod 参数必须设置为 NULL。
(4).dwThreadId 与钩子程序关联的线程标识符(指定要 HOOK 的线程 ID)。如果此参数为0,则钩子过程与系统中所有线程相关联,即全局消息钩子
实现原理:
通过 SetWindowsHookExW 函数安装一个用于过滤特定类型消息的钩子函数(经实验,钩子WH_GETMESSAGE 可以立即被触发,而某些类型的钩子需要在处理指定类型的消息时才能被触发),当其它进程中产生了我们过滤的消息,就会调用 HOOKPROC ,如果发现目标DLL(HOOKPROC实现的DLL)尚未加载,就会使用KeUserModeCallback 函数回调 User32.dll 的 __ClientLoadLibrary() 函数,由 User32.dll把这个DLL加载到目标进程中(低级键盘和鼠标钩子的加载有所不同),从而实现DLL注入其它进程的目的。
注意:此法可以对所有有消息循环的程序进行注入,但对那些没有消息循环的纯后台程序无能为力
主要代码如下:
//共享内存 #pragma data_seg("mydata") HHOOK g_hHook = NULL; //必须赋初值,否则微软编译器会把没有初始化的数据放到普通的未初始化数据段中 //而不是放在shared中,从而导致多个进程之间的共享行为失败 #pragma data_seg() #pragma comment(linker,"/SECTION:mydata,RWS") HMODULE g_hModule; //钩子句柄 extern "C" _declspec(dllexport) //卸载钩子 BOOL UnsetGlobalHook() { if (g_hHook) { UnhookWindowsHookEx(g_hHook); } return TRUE; } //钩子的回调函数 LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam) { MessageBox(NULL, L"你被监视了", L"提示", MB_ICONWARNING | MB_OKCANCEL); //CallNextHookEx函数表示将当前钩子传递给钩子链中的下一个钩子 //第一个参数当前钩子的句柄。如果直接返回0,则表示中断钩子传递 //对钩子进行拦截 return CallNextHookEx(g_hHook, code, wParam, lParam); } extern "C" _declspec(dllexport) //设置全局钩子 BOOL SetGlobalHook() { //SetWindowsHookEx,第一个参数表示钩子的类型,WH_GETMESSAGE表示安装消息队列的消息钩子 //可以监视发送到消息队列的消息。第二个参数表示钩子回调函数,第三个参数表示包含钩子回调 //函数的DLL模块句柄,如果要设置全局钩子,则该参数必须指定DLL模块句柄,第四个参数表示与 //钩子关联的线程ID,0表示为全局钩子,关联所有线程。 g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hModule, 0); if (g_hHook == NULL) { return FALSE; } return TRUE; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { g_hModule = hModule; break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
//定义函数指针和函数指针变量 typedef BOOL(*ty_SetGlobalHook)(); ty_SetGlobalHook fpSetGlobalHook = NULL; //获取要注入的DLL的加载基址 m_hDll= LoadLibrary(m_Edit_DllName); if (!m_hDll) { MessageBox(L"Dll加载失败"); } //获取设置全局钩子函数的函数地址。赋值给函数指针变量 fpSetGlobalHook = (ty_SetGlobalHook)GetProcAddress(m_hDll, "SetGlobalHook"); if (!fpSetGlobalHook) { MessageBox(L"加载设置全局钩子函数地址失败"); } //设置全局钩子 BOOL bRet = fpSetGlobalHook(); if (bRet) { MessageBox(L"全局钩子设置成功"); } else { MessageBox(L"全局钩子设置失败"); }