百度百科segmentfault
鉤子(Hook),是Windows消息處理機制的一個平臺,應用程序能夠在上面設置子程以監視指定窗口的某種消息,並且所監視的窗口能夠是其餘進程所建立的。當消息到達後,在目標窗口處理函數以前處理它。鉤子機制容許應用程序截獲處理Windows消息或特定事件
瞭解 Windows 窗口程序的朋友都知道-窗口程序是靠消息驅動的,一切過程皆是消息
,本來的消息傳遞過程是:系統接收到消息-將消息放入系統消息隊列-將消息放入線程消息隊列-線程中處理該消息
,而鉤子函數SetWindowsHookEx
的做用就是攔截消息,用自定義的函數處理消息windows
注意:當攔截當前線程消息時能夠在當前代碼中寫回調函數,當攔截其它線程消息時則必須調用dll中回調函數
函數
參數含義spa
HHOOK WINAPI SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId); /* idHook:鉤子的類型,即它處理的消息類型 lpfn:指向dll中的函數或者指向當前進程中的一段代碼 hMod:dll的句柄 dwThreadId:線程ID,當此參數爲0時表示設置全局鉤子 */
注意事項
設置全局鉤子只須要將SetWindowsHookEx
的第四個參數設爲0便可,全局鉤子會攔截每個窗口線程的消息,而後自動加載該 dll 並調用 dll 中的回調函數。注意32位程序加載32位dll注入32位進程,64位同理
線程
完整代碼
下面是一個包含 DllMain 函數的標準 dll 模板,咱們先編譯一個 dll 方便以後注入code
#include <windows.h> #include <tchar.h> BOOL test() { MessageBox(NULL, TEXT("SetWindowsHookEx Success!"), TEXT("Tips"), MB_OK); return 0; } BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpvRevered) { // MessageBox(NULL, TEXT("This is DllMain!"), TEXT("Tips"), MB_OK); switch (dwReason) { case DLL_PROCESS_ATTACH: /* 只有第一次會進到這裏,以後再次Attach不會產生DLL_PROCESS_ATTACH,只會增長DLL的調用次數 */ MessageBox(NULL, TEXT("Process Load Dll Success!"), TEXT("Tips"), MB_OK); break; case DLL_PROCESS_DETACH: // MessageBox(NULL, TEXT("Process Unload Dll Success!"), TEXT("Tips"), MB_OK); break; case DLL_THREAD_ATTACH: // MessageBox(NULL, TEXT("Thread load Dll Success!"), TEXT("Tips"), MB_OK); break; case DLL_THREAD_DETACH: // MessageBox(NULL, TEXT("Thread Unload Dll Success!"), TEXT("Tips"), MB_OK); break; } return TRUE; }
使用命令gcc test2.c -shared -o test2.dll
編譯成 dll 文件,而後設置全局鉤子向其它線程注入該 dll,鉤子代碼以下隊列
#include <windows.h> int main(int argc, char *argv) { int i; HMODULE hdll = LoadLibrary("test2.dll"); HOOKPROC hproc = (HOOKPROC)GetProcAddress(hdll, "test"); HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, hproc, hdll, 0); for(i=0;i<30;i++){ Sleep(2000); } UnhookWindowsHookEx(hook); return 0; }
使用命令gcc Inject_dll1_1.c -o Inject_dll1_1
編譯成可執行文件,Sleep
的做用是保證當前進程存活,進程結束全局鉤子就會被卸載進程
運行截圖
雙擊程序運行,顯示當前進程已加載該dll事件
而後在桌面按下回車鍵ip
在任意窗口按下回車鍵
注意事項
安裝全局鉤子不太穩定,由於攔截了全部窗口的消息,處理不當就會形成系統崩潰!因此爲了穩定性咱們可使用特定的線程ID
來安裝線程消息鉤子,鉤子代碼和上面的代碼差很少,核心就是要獲取線程的ID
我主要是用CreateToolhelp32Snapshot
建立進程快照而後配合TH32CS_SNAPPROCESS
TH32CS_SNAPTHREAD
這兩個結構體獲取線程ID!固然還能夠用GetWindowsThreadProcessId
配合窗口句柄快速獲取進程ID而後獲得線程ID
完整代碼
dll 和上面全局鉤子的 dll 相同,核心的鉤子代碼以下
#include <stdio.h> #include <windows.h> #include <tlhelp32.h> int getThreadID(int pid) { THREADENTRY32 te32; te32.dwSize = sizeof(te32); HANDLE Snapshot_thread = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (Snapshot_thread != INVALID_HANDLE_VALUE) { if (Thread32First(Snapshot_thread, &te32)) { do { if (te32.th32OwnerProcessID == pid) { return te32.th32ThreadID; } } while (Thread32Next(Snapshot_thread, &te32)); } } CloseHandle(Snapshot_thread); return 0; } int getPID(char *target) { PROCESSENTRY32 pe32; MODULEENTRY32 me32; HANDLE hProcess, hSnapshot_proc, hSnapshot_mod; pe32.dwSize = sizeof(pe32); hSnapshot_proc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (Process32First(hSnapshot_proc, &pe32)) { do { if (!strcmp(pe32.szExeFile, target)) { return pe32.th32ProcessID; } } while (Process32Next(hSnapshot_proc, &pe32)); } CloseHandle(hSnapshot_proc); return 0; } int main(int argc, char *argv) { int i; HMODULE hdll = LoadLibrary("test2.dll"); HOOKPROC hproc = (HOOKPROC)GetProcAddress(hdll, "test"); HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, hproc, hdll, getThreadID(getPID("calc.exe"))); for (i = 0; i < 30; i++) { Sleep(2000); } UnhookWindowsHookEx(hook); return 0; }
編譯命令gcc Inject_dll1_2.c -o Inject_dll1_2
運行截圖
我注入的是計算器的線程,因此須要先打開計算器
而後運行該鉤子程序
到計算器窗口按下回車鍵