iT邦幫忙

2021 iThome 鐵人賽

DAY 14
0
Security

打通任督二脈奇幻之旅 - 用 30 天探索 Windows 底層運作原理系列 第 14

【Day 14】- 今天來實作一個鍵盤監聽器

Agenda

  • 資安宣言
  • 測試環境與工具
  • 技術原理與程式碼
  • References
  • 下期預告

資安宣言


撰寫本系列文章目的在於提升資訊安全之實務能力,
並透過實作體悟到資訊安全領域的重要性,
本系列所有文章之內容皆有一定技術水平,
不得從事非法行為、惡意攻擊等非法活動,
「一切不合法規之行為皆受法律所約束」,
為了避免造成公司、廠商或玩家之間困擾,
所有實作不會拿已上市產品、Online Game 等等來作範例學習,
且部分具有深度、價值之內容,將會提升一定閱讀門檻(不對該技術做分析、解說),
請勿透過本系列文章所學,從事任何非法活動,請不要以身試法!!!


測試環境與工具

技術原理與程式碼

首先開始前要先說一下,
小弟我目前還屬於菜鳥階段,正不斷努力學習中,
若有發現錯誤或不妥之處還請不吝賜教。
歡迎大家多多留言,互相交流交流。

那就開始今天的主題吧~~
/images/emoticon/emoticon07.gif
/images/emoticon/emoticon39.gif

今天來講講關於 PS/2 的鍵盤過濾驅動,
簡稱為:「鍵盤監聽/側錄器」

首先,按下鍵盤按鍵後的流程大致上是這樣的:

  1. 按下實體鍵盤按鍵
  2. 產生 IRQL
  3. 鍵盤驅動:i8042prt
  4. 處理訊息:KbdClass
  5. 開始進入 User mode,發送訊號到對應的視窗中

中斷要求層級 Interrupt Request Level (IRQL)
在電腦系統中,無時無刻都在大量處理著資訊、訊息、程式...,
這個中斷要求層級就是要讓一些更重要的事情來「插隊的」,
意思就是說,當有一個優先順序較高的「中斷要求」來臨時,
它就能被優先執行,因此優先順序較低的就會被打斷,
所以你有想到什麼東西中斷層級是最高的?當然是按下電源按鈕、拔插頭...。


圖片內容取自 WIKI:https://zh.wikipedia.org/wiki/IRQL

當按下鍵盤,會產生「硬體中斷」,中斷層級很高,也是因為這樣系統才能捕捉到訊息,
所以當我們按下鍵盤,User mode 是有那麼一瞬間都停了下來,在等待鍵盤訊息的處理,
同時 IRP 訊息正在由下而上一層一層的傳遞,最終會到 User mode 應用程式中,
也就是大家打出來的文字。

所以~
要如何攔截鍵盤訊息呢?

有兩種方法:
作法一:
自己建立一個虛擬的 Device,讓這個虛擬的 Device Attach 上去 KbdClass。

作法二:
想辦法拿到鍵盤所使用的虛擬設備(KbdClass),然後 HOOK 它!

先來講第一種較腳踏實地的作法,
使用 IoCreateDevice 建立虛擬的 Device,
要怎麼知道現在有幾個虛擬的 Device?
使用 WinObj.exe
然後你就會看到:KeyboardClass0

這是在 VMWare 上的結果,若在實體機結果就不相同。

所以將建立好的虛擬 Device Attach 上去 KbdClass0,
當然,鍵盤可能很多個,所以 KbdClass0 可能不只一個,
KbdClass0、KbdClass1... ...要自己列舉,然後通通 Attach 上去!!
Attach 函數就用 IoAttachDevice

再來,我們不應該在讀取 IRP 時做一些耗時的動作,例如更改按鍵...等,
因為這樣會耽誤 IRP 傳遞的速度,所以要在讀 IRP 這個動作完之後,
再來做一些其他操作,不然會完蛋!

大概就是設定 IoSetCompletionRoutine

最後拿到 IRP,此時就是設一個 FUNC 來做想做的事情拉~
把 IRP 讀進來放進指向 PKEYBOARD_INPUT_DATA 結構的變數中,

typedef struct _KEYBOARD_INPUT_DATA {
  USHORT UnitId;
  USHORT MakeCode;
  USHORT Flags;
  USHORT Reserved;
  ULONG  ExtraInformation;
} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;

來源:KEYBOARD_INPUT_DATA

其中的 MakeCode 就是鍵盤按鍵編號了,可以把它改成別的,也可以直接印出來...等等,

效果圖:

再來講一下第二種作法,這種作法相當簡單,實作起來也很容易,
直接想辦法拿到鍵盤所使用的虛擬設備(KbdClass0),
然後把 IRP_MJ_READ 改成自己寫好的 FUNC,

使用 ObReferenceObjectByName 拿到虛擬設備(KbdClass0),
保存一份原本的 IRP_MJ_READ FUNC Address,
然後就是:xxxDriver->MajorFunction[IRP_MJ_READ] = MyHookDispatch;
這樣應該很清楚惹吧 XD

以上所講僅限用於 PS/2 的鍵盤~
不支援 USB 鍵盤,可直接在 VMWare 中使用,因為 VMWare 預設就是跑 i8042prt。

有空再來寫一篇支援 USB 鍵盤的文章,因為 USB 鍵盤和 PS/2 差很大,
它沒有固定的 Device Name,只有 Driver Name,而且是走 kbdhid.sys,
很難搞定,難度大幅提升,要自己從 USB Driver Stack 中列舉,
所以假設有哪天忽然很閒,我應該就會再發一篇 XD,訂閱起來,文章不漏接。

好了,這篇就講到這結束了,
大家若有發現哪裡寫得不好或錯誤的地方,都留個言討論一下吧 XD
那我們下期見 o( ̄▽ ̄)ブ

References

下期預告

  • 【Day 15】- 今天來實作一個 Kernel mode Thread
  • 如果你/妳喜歡我的文章,請記得訂閱按讚分享並且打開小鈴鐺哦
  • 這樣就能在第一時間收到通知,也不會錯過任何文章啦~~

上一篇
【Day 13】- 有了這個,就沒有打得開的程式了 XD(SetCreateProcessNotifyRoutine)
下一篇
【Day 15】- 今天來實作一個 Kernel mode Thread
系列文
打通任督二脈奇幻之旅 - 用 30 天探索 Windows 底層運作原理15

尚未有邦友留言

立即登入留言