关于Windows消息钩子的理解与测试项目
前奏
近來一直在自學Windows Hook相關的知識,已經嘗試多種注入方式。尤其對消息鉤子方式很感興趣,因為看到Spy++能夠截獲系統中絕大多數應用的消息流,就很想知道它的工作原理,打算制作屬于自己的Spy++。
消息鉤子簡介
消息鉤子簡言之就是Windows處理消息的一個平臺,用戶可以在此平臺獲取或過濾所需應用的消息(例如某鼠標位置,鍵盤擊下等),這些被監控的消息都會在目標窗口處理函數之前被截獲。系統在拿到消息后會問你:這是不是你想要的消息?如果是,則恭喜你,你可以在回調函數里做相應操作了。
消息鉤子函數背景
消息鉤子主要由三部分組成:
- 鉤子安裝函數原型
| 123456 | HHOOK WINAPI SetWindowsHookEx(??_In_??int?idHook,??_In_? HOOKPROC lpfn,??_In_? HINSTANCE hMod,??_In_? DWORD dwThreadId); |
參數1-idHook:這個函數代表了你要安裝鉤子的種類,比如鍵盤鉤子,鼠標鉤子,窗體鉤子等等。以下我列了一張表,結合MSDN供大家參考。
參數2-HOOKPROC lpfn:此參數是鉤子回調函數的地址,對應上述不同種類的鉤子類型,其鉤子回調函數原型基本是一致的。請見下(需要注意的是,wParam和lParam針對不同類型的鉤子,傳遞參數所代表意義不同,因種類繁多,請各位查閱MSDN):
| 1234567891011 | LRESULT CALLBACK HookProc(??int?nCode,??WPARAM wParam,??LPARAM lParam){???//?process event???...???return?CallNextHookEx(NULL, nCode, wParam, lParam);} |
nCode:int,此參數指示Hook例程是否需要處理消息,如果nCode為HC_ACTION,則需要處理;如果小于0,不予處理,須調用CallNextHookEx函數返回。 wParam與lParam:針對不同類型的鉤子,代表的意義不同,但總體是針對當前類型鉤子的消息。后面以例子做出驗證。 參數3-HINSTANCE hMod:包含Hook例程的Dll所在模塊基址。 參數4-DWORD dwThreadId:所要注入程序的主線程ID。如果此項填0,則代表鉤子為系統鉤子,基本所有在運行的進程都會被注入。如果此項指定線程ID,則是有針對性的線程鉤子。
返回值:HHOOK類型的句柄。
- 鉤子回調函數
- 鉤子卸載函數
| 123 | BOOL?WINAPI UnhookWindowsHookEx(??_In_? HHOOK hhk); |
參數:SetWindowsHookEx的返回值。
鍵盤消息鉤子及CBT鉤子程序代碼
首先我們要明確下,鉤子從安裝到回顯的流程
編寫Dll,在Dll中實現我們所需要的鉤子回調函數及安裝函數。
Dll中除了要編寫與鉤子相關的處理函數,也要編寫與顯示程序通信的模塊,以便驗證鉤子消息的正確性。
Dll需要導出鉤子啟動函數與卸載函數。
回顯程序處理已安裝鉤子發送的信息。
至此,框架已經明了,我們分步拆解完成這個小項目。(整體代碼請見附件,VS2013編譯)
- 結構體及枚舉的定義
| 12345678910111213141516171819202122 | typedef struct COMMPACK{????int?nType;//Hook類型????WPARAM wParam;//參數1????LPARAM lParam;//參數2????TCHAR szMsg[16];//額外的字符串信息,取決于具體鉤子} COMMPACK,?*PCOMMPACK;//與回顯程序通訊的包結構typedef struct HOOKSTC{????int?nType;//Hook類型????HOOKPROC hkproc;//Hook回調????HHOOK hhook;//Hook句柄} HOOKSTC,?*PHOOKSTC;HOOKSTC m_Array4Hooks[NHOOKNUMS]?=?{?0?};//創建多鉤子類型的結構體數組enum { M_CALLWNDPROC, M_CBT, M_DEBUG, M_GETMESSAGE, M_KEYBOARD, M_MOUSE, M_MSGFILTER };//鉤子類型枚舉enum { IDHCBT_ACTIVATE, IDHCBT_CLICKSKIPPED, IDHCBT_CREATEWND, IDHCBT_DESTROYWND, IDHCBT_KEYSKIPPED,????IDHCBT_MINMAX, IDHCBT_MOVESIZE, IDHCBT_QS, IDHCBT_SETFOCUS, IDHCBT_SYSCOMMAND, IDUnknown};//CBT鉤子的消息群 |
- CBT鉤子的回調函數
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 | LRESULT WINAPI m_CBTProc(int?nCode, WPARAM wParam, LPARAM lParam){????if?(nCode <?0)return?CallNextHookEx(m_Array4Hooks[M_CBT].hhook, nCode, wParam, lParam);????CHAR szCode[16]?=?{?0?};????PCOMMPACK pCP?=?new COMMPACK;????pCP->nType?=?M_CBT;????switch (nCode)????{????case HCBT_ACTIVATE://系統要激活一個窗口????????pCP->lParam?=?(LPARAM)IDHCBT_ACTIVATE;????????pCP->wParam?=?IDHCBT_ACTIVATE;????????break;????case HCBT_CLICKSKIPPED://系統已經從系統消息隊列中刪除了一個鼠標消息????????pCP->lParam?=?(LPARAM)IDHCBT_CLICKSKIPPED;????????pCP->wParam?=?IDHCBT_CLICKSKIPPED;????????break;????case HCBT_CREATEWND://一個窗口將要被創建????????pCP->lParam?=?(LPARAM)IDHCBT_CREATEWND;????????pCP->wParam?=?IDHCBT_CREATEWND;????????break;????case HCBT_DESTROYWND://一個窗口將要被銷毀????????pCP->lParam?=?(LPARAM)IDHCBT_DESTROYWND;????????pCP->wParam?=?IDHCBT_DESTROYWND;????????break;????case HCBT_KEYSKIPPED://系統已從系統消息隊列中刪除了一個鍵盤消息????????pCP->lParam?=?(LPARAM)IDHCBT_KEYSKIPPED;????????pCP->wParam?=?IDHCBT_KEYSKIPPED;????????break;????case HCBT_MINMAX://一個窗口將被最小化或最大化????????pCP->lParam?=?(LPARAM)IDHCBT_MINMAX;????????pCP->wParam?=?IDHCBT_MINMAX;????????break;????case HCBT_MOVESIZE://一個窗口將被移動或改變尺寸????????pCP->lParam?=?(LPARAM)IDHCBT_MOVESIZE;????????pCP->wParam?=?IDHCBT_MOVESIZE;????????break;????case HCBT_QS://系統已從系統消息隊列中取到一個WM_QUEUESYNC 消息????????pCP->lParam?=?(LPARAM)IDHCBT_QS;????????pCP->wParam?=?IDHCBT_QS;????????break;????case HCBT_SETFOCUS://一個窗口將要獲得鍵盤焦點????????pCP->lParam?=?(LPARAM)IDHCBT_SETFOCUS;????????pCP->wParam?=?IDHCBT_SETFOCUS;????????break;????case HCBT_SYSCOMMAND://一個系統命令將被執行????????pCP->lParam?=?(LPARAM)IDHCBT_SYSCOMMAND;????????pCP->wParam?=?IDHCBT_SYSCOMMAND;????????break;????default://其他????????pCP->lParam?=?(LPARAM)IDUnknown;????????pCP->wParam?=?IDUnknown;????????break;????}????m_Com(pCP);//與回顯程序通信????delete pCP;????return?CallNextHookEx(m_Array4Hooks[M_CBT].hhook, nCode, wParam, lParam);} |
- 鍵盤鉤子的回調函數
| 1234567891011 | LRESULT WINAPI m_KeyboardProc(int?nCode, WPARAM wParam, LPARAM lParam){????if?((nCode <?0?|| !((DWORD)lParam &?0x40000000)))return?CallNextHookEx(m_Array4Hooks[M_KEYBOARD].hhook, nCode, wParam, lParam);????PCOMMPACK pCP?=?new COMMPACK;????pCP->lParam?=?lParam;????pCP->wParam?=?wParam;//這個值就是按鍵的VirtualKey????pCP->nType?=?M_KEYBOARD;????m_Com(pCP);//與回顯程序通訊????delete pCP;????return?CallNextHookEx(m_Array4Hooks[M_KEYBOARD].hhook, nCode, wParam, lParam);} |
- 與回顯程序通訊的函數
| 123456789 | void m_Com(PCOMMPACK& pCP,?int?addi){????hTarget?=?::FindWindow(NULL, L"HookApp");//尋找回顯程序窗口標題????COPYDATASTRUCT stcCDS;//采用WM_COPYDATA消息方式進程間通訊????stcCDS.cbData?=?sizeof(COMMPACK);????stcCDS.dwData?=?addi;????stcCDS.lpData?=?pCP;//打包消息結構體????::SendMessage(hTarget, WM_COPYDATA,?0, (LPARAM)&stcCDS);} |
- 鉤子初始化函數
| 12345678910111213141516171819202122 | void InitHooks(){????hTarget?=?::FindWindow(NULL, L"HookApp");????m_Array4Hooks[M_CALLWNDPROC].hkproc?=?m_CallWndProc;????m_Array4Hooks[M_CALLWNDPROC].nType?=?WH_CALLWNDPROC;????m_Array4Hooks[M_CBT].hkproc?=?m_CBTProc;????m_Array4Hooks[M_CBT].nType?=?WH_CBT;????//m_Array4Hooks[M_DEBUG]->hkproc?=?m_DebugProc;????//m_Array4Hooks[M_DEBUG]->nType?=?WH_DEBUG;????//m_Array4Hooks[M_GETMESSAGE]->hkproc?=?m_GetMsgProc;????//m_Array4Hooks[M_GETMESSAGE]->nType?=?WH_GETMESSAGE;????m_Array4Hooks[M_KEYBOARD].hkproc?=?m_KeyboardProc;????m_Array4Hooks[M_KEYBOARD].nType?=?WH_KEYBOARD;????m_Array4Hooks[M_MOUSE].hkproc?=?m_MouseProc;????m_Array4Hooks[M_MOUSE].nType?=?WH_MOUSE;????//m_Array4Hooks[M_MSGFILTER]->hkproc?=?m_MessageFilterProc;????//m_Array4Hooks[M_MSGFILTER]->nType?=?WH_MSGFILTER;????for?(int?i?=?0; i < NHOOKNUMS;?++i)????{????????m_Array4Hooks[i].hhook?=?0;????}} |
- 鉤子安裝及卸載函數
| 1234567891011121314151617181920 | void InstallHook(DWORD dwProcessID){????g_dwThreadID?=?GetThreadInfo(dwProcessID);//由注入器傳入指定注入進程ID,遍歷獲得主線程ID????InitHooks();//初始化鉤子????for?(int?i?=?0; i < NHOOKNUMS;?++i)????{????????if?(m_Array4Hooks[i].hkproc?==?NULL)continue;????????m_Array4Hooks[i].hhook?=?SetWindowsHookEx(m_Array4Hooks[i].nType, m_Array4Hooks[i].hkproc, g_Module, g_dwThreadID);????}}void UnInstallHook(){????for?(int?i?=?0; i < NHOOKNUMS;?++i)????{????????UnhookWindowsHookEx(m_Array4Hooks[i].hhook);????}} |
- 線程遍歷函數
| 123456789101112131415161718192021222324 | DWORD GetThreadInfo(DWORD dwProcessID){????HANDLE hSnThread?=?CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,?0);????if?(hSnThread?==?INVALID_HANDLE_VALUE) {????????return?0;????}????//?開始遍歷線程????THREADENTRY32 threadEntry?=?{ sizeof(THREADENTRY32) };????//?獲取快照中的第一個線程的信息????Thread32First(hSnThread, &threadEntry);????DWORD dwSuspendCount?=?0;????do {????????//?判斷遍歷到的線程是否屬于這個進程的.????????if?(threadEntry.th32OwnerProcessID?==?dwProcessID) {????????????return?threadEntry.th32ThreadID;????????}????????//?獲取快照中的下一個線程信息????}?while?(Thread32Next(hSnThread, &threadEntry));????return?1;} |
- 函數導出
| 12 | EXTERN_C _declspec(dllexport) void InstallHook(DWORD dwProcessID);EXTERN_C _declspec(dllexport) void UnInstallHook(); |
至此DLL部分結束,開始回顯程序的消息處理
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 | BOOL?CHookAppDlg::OnCopyData(CWnd*?pWnd, COPYDATASTRUCT*?pCopyDataStruct){????//?TODO:? 在此添加消息處理程序代碼和/或調用默認值????static?int?nIndex?=?0;????PCOMMPACK pCP?=?new COMMPACK;????pCP?=?(PCOMMPACK)pCopyDataStruct->lpData;????switch (pCP->nType)//根據消息的類型進行定義????{????case M_CALLWNDPROC:????{????}break;????case M_CBT:????{????????static?int?nIndex?=?0;????????CString strCallWndProc;????????switch (pCP->wParam)????????{????????case IDHCBT_ACTIVATE:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"HCBT_ACTIVATE", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????case IDHCBT_CLICKSKIPPED:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"HCBT_CLICKSKIPPED", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????case IDHCBT_CREATEWND:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"HCBT_CLICKSKIPPED", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????case IDHCBT_DESTROYWND:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"HCBT_DESTROYWND", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????case IDHCBT_KEYSKIPPED:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"HCBT_KEYSKIPPED", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????case IDHCBT_MINMAX:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"HCBT_MINMAX", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????case IDHCBT_MOVESIZE:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"HCBT_MOVESIZE", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????case IDHCBT_QS:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"HCBT_QS", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????case IDHCBT_SETFOCUS:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"HCBT_SETFOCUS", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????case IDHCBT_SYSCOMMAND:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"HCBT_SYSCOMMAND", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????case IDUnknown:????????{????????????strCallWndProc.Format(L"CBT [%d] - nCode: %s, tsk: %ld? ", nIndex, L"Unknown", pCP->wParam);????????????strCallWndProc?+=?L"\r\n";????????????m_StrCBT?+=?strCallWndProc;????????????UpdateData(FALSE);????????????++nIndex;????????}break;????????default:????????????break;????????}????}break;????case M_DEBUG:????{????}break;????case M_GETMESSAGE:????{????}break;????case M_KEYBOARD:????{????????static?int?nIndex?=?0;????????CString strCallWndProc;????????strCallWndProc.Format(L"KEYBOARD [%d] - VK: %c? ", nIndex, char(pCP->wParam));????????strCallWndProc?+=?L"\r\n";????????m_StrKeyBoard?+=?strCallWndProc;????????UpdateData(FALSE);????????++nIndex;?????????????}break;????case M_MOUSE:????{?????????????}break;????case M_MSGFILTER:????{????}break;????default:????????break;????}????return?CDialogEx::OnCopyData(pWnd, pCopyDataStruct);} |
注入器部分代碼:
| 1234567891011121314151617181920212223 | BOOL?_Inject_::m_WindowsHook(PWCHAR pszDllName, LPCSTR pszDllProc, DWORD dwPID){????typedef void*?(*HookOnProto)(DWORD);//聲明導出函數原型????HookOnProto HookOn;????hInstDll?=?LoadLibrary(pszDllName);????if?(hInstDll?==?NULL)return?FALSE;????HookOn?=?(HookOnProto)GetProcAddress(hInstDll, pszDllProc);//從指定Dll導出所需函數????if?(HookOn?==?NULL)return?FALSE;????HookOn(dwPID);//鉤子安裝????return?TRUE;}BOOL?_Inject_::m_UnWinHook(PWCHAR pszDllName, LPCSTR pszDllProc){????typedef void*?(*HookOffProto)(void);????HookOffProto HookOff;????HookOff?=?(HookOffProto)GetProcAddress(hInstDll, pszDllProc);????if?(HookOff?==?NULL)return?FALSE;????HookOff(); |
效果演示:(以注入notepad++為例)
安裝消息鉤子前
安裝消息鉤子后
由于鉤子種類繁多,我暫時沒有全部實現,有興趣的各位可以在我的基礎上試試其他的,代碼寫的不好,讓各位見笑了,如有錯誤,也懇請各位指正。
總結
以上是生活随笔為你收集整理的关于Windows消息钩子的理解与测试项目的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二进制枚举
- 下一篇: 3-6:常见任务和主要工具之正则表达式