想像一下 : 有一群壞人想帶著武器去劫機,但是他們在過層層安檢的時候總會被檢查到並被驅離。
但是,這群壞人有一天賄絡了某一層的警察,使他們可以直接走自己的流程,這樣就不會被抓了
那麼,在程式上賄絡的行為是在安裝 ”Hook
” ,而這樣透過"鉤子”來獲取或改變程序的行為稱為 “Hooking
”。
來試試看對記事本進行 Hooking
,使他收不到來自鍵盤的信息。
在 Hooking
記事本之前,我們要瞭解把文字輸入到 notepad.exe 時電腦會發生甚麼事?
在應用程式中,我們常常會用到許多硬體消息。主要會發生這些事 :
System message quene
( 系統訊息佇列
)Thread message quene
( 執行緒的訊息佇列
)官方文件
https://learn.microsoft.com/zh-tw/windows/win32/inputdev/about-keyboard-input
那如果要接上 hook 後,打算不讓筆記本不要收到訊息則是這樣 :
系統訊息佇列
。系統訊息佇列
的訊息分給執行緒訊息佇列
。這邊有一個程式,請在 x64 環境下執行它 ( 我在windows10 環境下沒問題 )。
https://github.com/Dinlon5566/IT_Reverse_Engineering/tree/main/Dx17
HookMain.exe 來執行程式
打開記事本,會發現不能打字,但其他程式可以。
這就是因為在 KeyHook.dll 中有中斷文字傳輸的功能,導致記事本收不到訊息。
簡單看一下 HookMain.exe 主要程式碼
// HookMain.cpp
HookStart();
printf("press 'q' to quit!\n");
while (_getch() != 'q');
HookStop();
其中,HookStart()
與 HookStop()
是 KeyHook.dll 的函數。
mainDll.dll 主要程式碼
//dllMain.cpp
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
char szPath[MAX_PATH] = { 0, };
char* p = NULL;
if (nCode >= 0)
{
// 若鍵盤有狀態,第31位元會是 1。
if (!(lParam & 0x80000000))
{
//取得並比較是否為 "notepad.exe"
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');
//如果是就直接退出
if (!_stricmp(p + 1, DEF_PROCESS_NAME))
return 1;
}
}
//其他程序則接到下一個鉤子或程式。
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
__declspec(dllexport) void HookStart()
{
//接上 Hook
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
}
__declspec(dllexport) void HookStop()
{
//拔掉 Hook
if (g_hHook)
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
其中 SetWindowsHookEx
可以用來設置勾點,這次使用 WH_KEYBOARD 來監視鍵盤系統事件。
解說一下這個功能在 winuser.h
的定義
HHOOK SetWindowsHookExA(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);
WH_KEYBOARD
屬於這邊,值為2Handle
PID
,若是 0 則是全部。MS 說明書 : 使用勾點
https://learn.microsoft.com/zh-tw/windows/win32/winmsg/using-hooks
MS 說明書 :SetWindowsHookExA function
https://learn.microsoft.com/zh-tw/windows/win32/api/winuser/nf-winuser-setwindowshookexa
當程式被勾上後,只要鍵盤有操作系統就會把 dll 注入到程序哩,然後啟動 KeyboardProc()
這個 DLL程序。