https://www.douban.com/note/318793892/架構
本文主要介紹按鍵消息是如何傳遞到窗口並轉化爲具體的按鍵消息的。
Windows系統是事件驅動的多任務系統,其中按鍵和鼠標是主要的事件。按鍵是由鍵盤驅動得到並轉換,而後廣播給各個窗口。
整個架構的核心是csrss.exe這個進程,對於「通常」的窗口,收到的消息都是由這個任務產生的。該任務負責用CreateFile方式打開鍵盤設備並讀取信息,得到對應的鍵碼併發送給特定的進程,csrss.exe的啓動的輸入線程爲win32k!RawInputThread,具體要把鍵盤消息發送給哪一個線程,以及如何發送,是由csrss來控制的。
因此,每一個進程的MessageLoop其實是從這裏讀取的,因而SetWindowHook之類的函數有些是掛到各個進程裏,有些其實是掛到了csrss裏,達到監控鍵盤的目的。但對於加密的控件(如網銀等),SetWindowHook無論用,但若是hook到RawInpuThread裏,那麼仍是有可能讀到信息的。
csrss訪問的是鍵盤設備,具體這個設備是一個kbdclass設備,如下部分就是內核的代碼空間了。
由於一個系統裏可能不僅一個鍵盤(同理,鼠標也是),因此須要一個統一的驅動去管理全部的鍵盤設備,這個驅動就是kbdclass.sys,鍵盤設備類驅動,對應的設備名是L"\\Driver\\Kdbclass",RawInpuThread實際上訪問的是這個設備,而對於每一個鍵盤,則是有各自的驅動,對於PS/2鍵盤是i8042prt.sys,對於USB鍵盤,這是另外的驅動,這些驅動稱爲port驅動,是真正的設備驅動,對應的設備是KeyboadClass0、KeyboardClass1等等(有幾個鍵盤,數字就排到多少),RawInpuThread把請求發送到kbdclass,kbdclass把請求(IRP)pend到這一層,等待下層port驅動返回。
這裏須要特別提出的一個函數是KeyboardClassServiceCallback,這個函數是kbdclass一層的callback,下層設備驅動的全部返回值都須要通過它。因此,若是鉤子掛到這裏,那麼理論上全部的輸入均可以被攔截的,已經親自測試過網銀控件的密碼會在此被泄露,但QQ不會,QQ2013會啓動失敗,估計是檢查了這個驅動。
順便說一句,早期的QQ加密沒那麼恐怖,但它是啓動一個線程不停的SetWindowHook,使得本身的hook永遠在第一個,而後過濾掉全部的消息防止被人監聽。後來的QQ多是修改IDT裏的鍵盤中斷實現的。
固然病毒也能夠經過filter驅動注入到kbdclass和port驅動之間,來實現監聽鍵盤,這也是一種常見的狀況。
因此,一個按鍵的消息產生流程以下:
1)硬件中斷/硬件端口數據
//WinIO能模擬,或者修改IDT是在這一層
2)鍵盤Port驅動(USB or PS/2)
//Filter驅動在此
//KeyboardClassServiceCallback也在這一層被調用
3)kbdclass驅動
//處理鍵盤佈局和鍵盤語言
4)Windows內核邊界(zwCreate/zwReadFile)
----------------------(系統調用)----------------------
5)Windows內核邊界(zwCreate/zwReadFile)
6)csrss.exe的win32k!RawInputThread讀取,完成scancode和vk的轉換
//SetWindowHook工做在這裏(全局)
//kbd_event工做在這裏
7)csrss.exe調用DispatchMessage等函數分發消息
//SetWindowHook工做在這裏(進程)
//PostMessage和SendMessage在這裏
8)各個進程處理消息
WinIO這個驅動比較特殊,它提供接口能夠容許應用層直接寫端口,但只能寫PS/2端口,因此有些模擬按鍵程序經過WinIO來模擬按鍵。
對於標準的程序,PostMessage等函數能夠完成模擬,但對於不太標準的軟件,只能用kbd_event來模擬,但有些軟件(如網銀控件)就只能在更靠近內核的區域模擬了。
同時,有一種輸入是很特殊的,就是DirectInput,這是DirectX提供的一種方法,大型遊戲中很常見的用法,由於DirectInput的輸入速度很快,繞過了消息層。但對於這種軟件,在kbdclass一層甚至都沒法模擬。目前還不肯定DirectInput工做在哪一層,猜想多是在kbdclass和port驅動之間,也許是一種filter驅動。對於這種輸入方法,能夠WinIO來模擬,但對於USB鍵盤則沒有辦法。
Windows提供了一套API:SendInput,這個驅動發送按鍵消息時有兩種類型,一種是VK模式的,實際上跟kbd_event同樣,工做在csrss這一層,而另外一種是ScanCode模式,MSDN裏有這樣的描述:
Windows 2000/XP: Set the KEYEVENTF_SCANCODE flag to define keyboard input in terms of the scan code. This is useful to simulate a physical keystroke regardless of which keyboard is currently being used.
能夠看出,這是可以模擬更底層的按鍵的,理論上說是能夠模擬DirectInput的按鍵的,實際測試也是這樣,但文檔中沒有說明在vista之後的版本是什麼狀態,因此暫時也沒法知道在WIN7裏的工做狀況。併發