WH_KEYBOARD_LL和WH_KEYBOARD

最近自己碰到一個(gè)需求: 想通過按鍵給某個(gè)非焦點(diǎn)窗口發(fā)送鍵盤指令. 正好復(fù)習(xí)了一下Windows編程和Hook的相關(guān)知識, 寫了一個(gè)可以用的鉤子工具, 微軟的官方文檔入口: https://docs.microsoft.com/en-us/windows/win32/winmsg/hooks

Windows消息機(jī)制和Hook

  • 首先, Windows操作系統(tǒng)負(fù)責(zé)維護(hù)兩個(gè)消息隊(duì)列(Message Queue): 系統(tǒng)消息隊(duì)列和線程消息隊(duì)列. 操作系統(tǒng)會(huì)為每一個(gè)有窗口的線程創(chuàng)建消息隊(duì)列, 包括控制臺程序. 當(dāng)用戶有一個(gè)輸入以后, 比如按鍵或者鼠標(biāo), 系統(tǒng)會(huì)創(chuàng)建一個(gè)事件消息首先壓入到系統(tǒng)消息隊(duì)列, 然后分發(fā)到對應(yīng)的線程消息隊(duì)列.

  • 每一個(gè)線程需要維護(hù)一個(gè)消息循環(huán)(Message Loop), 用來輪詢獲取消息隊(duì)列中的相關(guān)消息和調(diào)用對應(yīng)的窗口處理函數(shù), 對應(yīng)的API是GetMessageA(...)

  • 每一個(gè)線程只能監(jiān)聽和響應(yīng)跟自己相關(guān)的事件. 如果這個(gè)線程需要獲取全局的事件響應(yīng), 這個(gè)時(shí)候就要用到Hook了. 基本過程如下:

  1. 通過SetWindowsHookExA(...)設(shè)置鉤子(local或者DLL)
  2. 系統(tǒng)注入DLL或者直接發(fā)送底層消息到Hook線程
  3. Hook回調(diào)函數(shù)對目標(biāo)事件做出響應(yīng), 在DLL中就發(fā)送消息到Hook線程或者local直接響應(yīng)
  4. local或者DLL的不同由Hook的種類決定, 有些Hook只能用DLL, 下面會(huì)詳細(xì)介紹
  • Windows提供兩種鍵盤鉤子: WH_KEYBOARD_LLWH_KEYBOARD. 援引MSDN的解釋如下:

    鍵盤鉤子.PNG

    WH_KEYBOARD_LL是一個(gè)Low-Level的鉤子, 當(dāng)Raw Input Thread(RIT)決定從系統(tǒng)消息隊(duì)列中分發(fā)消息之前, 就已經(jīng)截獲了這個(gè)消息進(jìn)行了處理. 所以WH_KEYBOARD_LL甚至?xí)缬谙到y(tǒng)線程來處理消息, 比如ctrl + alt + del都可以截獲, 并且WH_KEYBOARD_LL讓系統(tǒng)不需要通過DLL來動(dòng)態(tài)注入所有進(jìn)程了,系統(tǒng)只會(huì)把消息發(fā)送到Hook線程. WH_KEYBOARD的層級比較高, 屬于應(yīng)用程序級別的, 所以系統(tǒng)線程會(huì)早于這個(gè)Hook響應(yīng), 并且只能截獲系統(tǒng)發(fā)送到應(yīng)用線程messag queue的消息, 但是優(yōu)先執(zhí)行該Hook的回調(diào)函數(shù),如果Hook回調(diào)返回false才會(huì)繼續(xù)處理當(dāng)前應(yīng)用的消息響應(yīng)函數(shù). 所以總結(jié)以下幾點(diǎn):

    • 這兩種鉤子都可以作為全局鉤子使用, 只需要設(shè)置鉤子的時(shí)候注入線程設(shè)成 0
    • WH_KEYBOARD_LL 在消息發(fā)送之前就已經(jīng)處理了, 而 WH_KEYBOARD是發(fā)送到注入線程以后
    • WH_KEYBOARD_LL 無需系統(tǒng)注入DLL執(zhí)行, 而 WH_KEYBOARD 需要DLL來注入所有進(jìn)程空間

定位需要響應(yīng)事件的窗口句柄

  • 首先獲取窗口所屬進(jìn)程的PID:
    快照系統(tǒng)進(jìn)程, 然后輪詢進(jìn)程列表,根據(jù)進(jìn)程名稱找到對應(yīng)的ID
DWORD getPIDFromProcessName(string sProcessName)
{
    PROCESSENTRY32 pe;
    pe.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (!Process32First(hSnapshot, &pe))
    {
        return false;
    }

    string sCur(pe.szExeFile);
    do
    {
        if (GetLastError() == ERROR_NO_MORE_FILES)
        {
            break;
        }
        sCur = pe.szExeFile;
        cout << sCur.c_str() << endl;
        if (sCur == sProcessName)
        {
            printf("Found");
            return pe.th32ProcessID;
        }
    } while (Process32Next(hSnapshot, &pe));

    return 0;
}
  • 鎖定目標(biāo)進(jìn)程需要響應(yīng)事件的窗口句柄:
EnumWindows(YourCallBack, (LPARAM)pid);

設(shè)置鉤子

  • 如果是使用WH_KEYBOARD_LL, 直接寫在Hook線程就可以了:
//HookLocal.cpp

LRESULT CALLBACK HookCallback(int code, WPARAM wParam, LPARAM lParam)
...
SetWindowsHookExA(WH_KEYBOARD_LL,HookCallback,0,0);
...
  • 如果是使用WH_KEYBOARD, 要寫在DLL中, 通過注入線程來調(diào)用, 這里要注意傳入Hook線程的窗口句柄,用來Post一個(gè)消息通知Hook線程來處理消息:
//HookDLL.cpp

DWORD g_hwnd;

LRESULT CALLBACK HookCallback(int code, WPARAM wParam, LPARAM lParam)
{
    PostMessage(g_hwnd, ...); //發(fā)送消息到Hook線程的消息隊(duì)列
    return true;
}
extern "C" _declspec(dllexport) void setHook(HWND hookWindow)
{
    HMODULE mod = GetModuleHandle(L"YourDLL.dll");
    //注入hook
    SetWindowsHookExA(WH_KEYBOARD, HookCallback, mod, 0);
    //保存Hook線程窗口句柄
    g_hwnd = hookWindow;
}

消息循環(huán)

在Hook線程中, 無論是用的哪種鉤子,都需要一個(gè)消息循環(huán)輪詢當(dāng)前線程中的消息隊(duì)列:

MSG msg;
while(GetMessageA(&msg,0,0,0))
{
   TranslateMessage(&msg);
   DispatchMessage(&msg);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嘲恍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子撩幽,更是在濱河造成了極大的恐慌职抡,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眯分,死亡現(xiàn)場離奇詭異拌汇,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)弊决,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門噪舀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人飘诗,你說我怎么就攤上這事与倡。” “怎么了昆稿?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵纺座,是天一觀的道長。 經(jīng)常有香客問我溉潭,道長净响,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任喳瓣,我火速辦了婚禮馋贤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘畏陕。我一直安慰自己配乓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扰付,像睡著了一般堤撵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上羽莺,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天实昨,我揣著相機(jī)與錄音,去河邊找鬼盐固。 笑死荒给,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的刁卜。 我是一名探鬼主播志电,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛔趴!你這毒婦竟也來了挑辆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤孝情,失蹤者是張志新(化名)和其女友劉穎鱼蝉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體箫荡,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡魁亦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了羔挡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洁奈。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖绞灼,靈堂內(nèi)的尸體忽然破棺而出利术,到底是詐尸還是另有隱情,我是刑警寧澤镀赌,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布氯哮,位于F島的核電站,受9級特大地震影響商佛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜姆打,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一良姆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幔戏,春花似錦玛追、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽韩玩。三九已至,卻和暖如春陆馁,著一層夾襖步出監(jiān)牢的瞬間找颓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工叮贩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留击狮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓益老,卻偏偏與公主長得像彪蓬,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子捺萌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Hooks 本文翻譯自 MSDN档冬。 Hook 是系統(tǒng)消息處理機(jī)制中的一個(gè)點(diǎn),在這個(gè)點(diǎn)上桃纯,程序可以裝載一個(gè)子程序酷誓。利...
    董噠噠閱讀 1,168評論 0 0
  • SetWindowsHookEx 將應(yīng)用程序定義的鉤子過程安裝到鉤子鏈中.您將安裝一個(gè)掛鉤程序來監(jiān)視系統(tǒng)的某些類型...
    f675b1a02698閱讀 5,884評論 0 0
  • hook是WINDOWS提供的一種消息處理機(jī)制,它使得程序員可以使用子過程來監(jiān)視系統(tǒng)消息慈参,并在消息到達(dá)目標(biāo)過程前得...
    a_code閱讀 1,873評論 0 0
  • 看到同事拉我進(jìn)入的學(xué)校合育群中的老師和家長們的鼓勵(lì)呛牲,我感到很有動(dòng)力,非常感謝有這么一個(gè)好的平臺讓我能夠?qū)W習(xí)驮配,...
    徒步旅行2018閱讀 327評論 3 1
  • 徐樂琦~詠輝房產(chǎn)營銷有限公司 【日精進(jìn)打卡第76天】 【知~學(xué)習(xí)】 《六項(xiàng)精進(jìn)》1遍 共50 《大學(xué)》1遍 共4...
    f03548fbee23閱讀 125評論 0 1