實驗內容:
通過python編程調用windows的api,編寫鍵盤和鼠標監控的hook,將相關信息保存記錄到txt文檔中。
實驗步驟:
1.Hook技術,pyHook3和pywin32簡介
1.1 Hook簡介
windows應用程序是基於消息驅動的。各種應用程序對各種消息作出響應從而實現各種功能。
hook(鈎子)是一種特殊的消息處理機制,它可以監視系統或者進程中的各種事件消息,截獲發往目標窗口的消息並進行處理。所以說,我們可以在系統中自定義鈎子,用來監視系統中特定事件的發生,完成特定功能,如屏幕取詞,監視日志,截獲鍵盤、鼠標輸入等等。
鈎子實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒有到達目的窗口前,鈎子程序就先捕獲該消息,亦即鈎子函數先得到控制權。這時鈎子函數即可以加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。Windows消息帶了一些程序有用的信息,比如Mouse類信息,就帶有鼠標所在窗體句柄、鼠標位置等信息,攔截了這些消息,就可以利用這些信息做出一些功能
每一個Hook都有一個與之相關聯的指針列表,稱之為鈎子鏈表,由系統來維護。這個列表的指針指向指定的,應用程序定義的,被Hook子程調用的回調函數,也就是該鈎子的各個處理子程序。當與指定的Hook類型關聯的消息發生時,系統就把這個消息傳遞到Hook子程。一些Hook子程可以只監視消息,或者修改消息,或者停止消息的前進,避免這些消息傳遞到下一個Hook子程或者目的窗口。最近安裝的鈎子放在鏈的開始,而最早安裝的鈎子放在最后,也就是后加入的先獲得控制權。
系統鈎子與線程鈎子:
SetWindowsHookEx()函數的最后一個參數決定了此鈎子是系統鈎子還是線程鈎子。
線程鈎子用於監視指定線程的事件消息。線程鈎子一般在當前線程或者當前線程派生的線程內。
系統鈎子監視系統中的所有線程的事件消息。因為系統鈎子會影響系統中所有的應用程序,所以鈎子函數必須放在獨立的動態鏈接庫(DLL) 中。系統自動將包含“鈎子回調函數”的DLL映射到受鈎子函數影響的所有進程的地址空間中,即將這個DLL注入了那些進程。
對於 Hook 技術,可以分為兩塊,第一塊是在 Ring3 層的 Hook,俗稱應用層 Hook 技術,另外一塊自然是在 Ring0 層得 Hook,俗稱為內核層 Hook 技術。
1.2 Hook的windows API
操作系統支持多種類型的鈎子,每種類型都提供了它特有的消息處理機制。
對於每種類型的鈎子,系統都維護一個各自獨立的鈎子鏈,鈎子鏈是一個指向用戶提供的回調函數鈎子過程的鏈表指針。
Hook的安裝:
HHOOK SetWindowsHookEx{
int idHook,//要安裝的鈎子的類型
HOOKPPROC lpfn,//鈎子過程的指針,攔截到制定系統消息后的預處理過程
HINSTANCE hMod,//應用程序實例的句柄,如果是全局鈎子,hInstance是DLL句柄(DllMain中給的模塊地址。就是包含HookProc的動態庫加載地址。否則給0就可以了,即勾自己。 )
DWORD dwThreadId //要安裝鈎子的線程id,指定被監視的線程,如果明確指定了某個線程的id就只監視該線程,此時的鈎子為線程鈎子;如果該參數被設置為0,則表示此鈎子為監視系統所有線程的全局鈎子
}
返回值:若此函數執行成功,則返回值就是該掛鈎處理過程的句柄;若此函數執行失敗,則返回值為NULL(0).
Hook過程:
LRESULT CALLBACK HookProc{ //
int nCode, //該參數是一個鈎子標識碼,鈎子過程會利用它決定下一步的進行的操作。這個標識嘛的值與安裝的鈎子類型相關
WPARAM wParam,//后面兩個參數的定義都依賴於nCode參數,一般用於存放於窗口消息相關的內容
LPARAM lParam}
LRESULT 就是long型,是Windows API的一種返回類型;CALLBACK表示這個函數是給系統調用的,實際上CALLBACK就是__stdcall(回調函數)。HookProc指代自定義的函數。
Hook卸載:
BOOL WINAPI UnhookWindowsHookEx( __in HHOOK hhk);
HHOOK要刪除的鈎子的句柄。這個參數是函數SetWindowsHookEx的返回值。返回值類型: BOOL,如果函數成功,返回值為非零值。如果函數失敗,返回值為零。 要獲得更多的錯誤信息,調用GetLastError函數.
Hook信息傳遞:
CallNextHookEx是一種函數,可以將鈎子信息傳遞到當前鈎子鏈中的下一個子程,一個鈎子程序可以調用這個函數之前或之后處理鈎子信息。
LRESULT WINAPI CallNextHookEx( _In_opt_ HHOOK hhk, _In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam);
Hook類型:
有很多,舉兩個例子:
WH_KEYBOARD //當敲擊鍵盤時將觸發此鈎子
WH_MOUSE //當有鼠標操作時將觸發此鈎子
1.3 pyHook3與pywin32簡介
Pywin32:一個開源的python項目,該模塊包含了幾乎所有的windows API,供調用。https://github.com/mhammond/pywin32。封裝方式是從windows的DLL中按類提取了API函數放在不同位置,比如process進程類API放在win32process模塊。
PyHook3:依賴於Pywin32,用於捕捉特定的Windows事件,封裝了所有底層調用(比如對SetWindowsHookEx(),UnhookWindowsHookEx(),等函數封裝),我們只需要關注程序邏輯。提供鍵盤和鼠標的Hook。
2.安裝pyHook3和pywin32(pycom)
需要注意pyHook和pywin32與python版本的對應。
2.1 查看python版本
Python 3.7,AMD64
2.2 pyHook3安裝
pyHook官方只支持python2,所以安裝pyhook3
Conda install swig
Pip install pyhook3
2.3 Pycom安裝:
Pycom即pywin32
https://github.com/mhammond/pywin32/releases
2.4 查看是否安裝成功:
Conda list
安裝成功
3.利用Hook實現鍵盤監控與鼠標監控
3.1 思路:
沒有Hook時:
鍵盤輸入--> 系統消息隊列 --> 對應應用程序的消息隊列 --> 將消息發送到對應的窗口中
添加Hook后:
鍵盤輸入 --> 系統消息隊列 --> 對應應用程序消息隊列 --> 將消息發送到鈎子鏈中 --> 消息一一調用完畢所有的鈎子函數(需要調用CallNextHookEx函數才能將消息傳遞下去) --> 將消息發送到對應的窗口中
添加兩個Hook函數分別對應鍵盤和鼠標。在Hook函數接收到信息后保存到txt中,完成監控,然后把信息傳給目標程序。
3.2 代碼實現
思路:
添加一個鍵盤hook,會獲取到鍵盤信息。Hook函數功能為將信息轉化為string后寫入txt文檔,然后將信息繼續傳遞給目標窗口。
有用的信息為Time 事件時間,MessageName事件名稱,WindowName事件所在窗口名,Ascii事件ASCII碼,KEY 事件KEY,Scancode掃描碼,Alt控制信息。
添加一個鼠標Hook,獲取鼠標信息,功能類似於鍵盤Hook。
有用的信息為Time 事件時間,MessageName事件名稱,WindowName事件所在窗口名,Position鼠標所在坐標位置,Wheel鼠標滾輪的信息。
在鍵盤Hook的函數中判斷鍵盤輸入是否為Q,為Q時quit。
存入的文檔設置為F://records.txt。
實驗結果記錄:
1.code:
# -*- coding: utf-8 -*- """ Created on Wed Dec 25 11:33:41 2019 @author: erio """ #USB 接口的鍵盤鼠標 import pythoncom import PyHook3 as pyHook import win32api import time path = 'F://records.txt' def onMouseEvent(event): with open(path, 'a+') as f: f.write("-----Mouse Event Start-----\n") # 監聽鼠標事件 print ("MessageName:", event.MessageName) print ("WindowName:", event.WindowName) print ("Position:", event.Position) print ("Wheel:", event.Wheel) print ("---") # 返回 True 以便將事件傳給其它處理程序 # 注意,這兒如果返回 False ,則鼠標事件將被全部攔截 t = time.localtime() t = time.asctime(t) result = "Time : " + t + "\n" + "MessageName: "+ str(event.MessageName)+'\n'+"WindowName: " + str(event.WindowName) + \ "\n" +"Position: "+str(event.Position)+'\n'+"Wheel: " +str(event.Wheel)+'\n' f.write(result) f.write("-----Mouse Event End-----\n\n\n") f.close() return True def onKeyboardEvent(event): with open(path, 'a+') as f: # 監聽鍵盤事件 f.write("-----KeyBoard Event Start-----\n") print ("MessageName:", event.MessageName) print ("WindowName:", event.WindowName) print ("Ascii:", event.Ascii, chr(event.Ascii)) print ("Key:", event.Key) print ("ScanCode:", event.ScanCode) print ("Alt", event.Alt) print ("---") t=time.localtime() t=time.asctime(t) result ="Time : " + t + "\n" +"MessageName: "+ str(event.MessageName)+'\n'+"WindowName: " + str(event.WindowName) + " \n" +\ "Ascii: "+str(event.Ascii)+ ' '+chr(event.Ascii)+'\n'+"Key: " + str( event.Key) + "\n" +"ScanCode: "+str(event.ScanCode)+"\n"+\ "Alt: "+str(event.Alt)+'\n' f.write(result) f.write("-----KeyBoard Event End-----\n\n\n") f.close() if event.Key== 'Q': # 按下F12后終止adsw win32api.PostQuitMessage() return True def main(): # 創建一個“鈎子”管理對象aaavv q hm = pyHook.HookManager() # 監聽所有鍵盤事件 hm.KeyDown = onKeyboardEvent # 設置鍵盤“鈎子” hm.HookKeyboard() # 監聽所有鼠標事件 hm.MouseAll = onMouseEvent # 設置鼠標“鈎子” hm.HookMouse() # 進入循環,如不手動關閉,程序將一直處於監聽狀態 pythoncom.PumpMessages() if __name__ == "__main__": main()
2.records.txt文檔展示
實際隨機操作后記錄在records.txt中。由於記錄較長(13kb),截取部分展示。
鍵盤信息記錄:包含普通按鍵和Alt
信息為Time 事件時間,MessageName事件名稱,WindowName事件所在窗口名,Ascii事件ASCII碼,KEY 事件KEY,Scancode掃描碼,Alt控制信息。
-----KeyBoard Event Start-----
Time : Wed Dec 25 17:19:28 2019
MessageName: key down
WindowName: Pyhook [D:\Pyhook] - ...\records.py [Pyhook] - PyCharm
Ascii: 13
Key: Return
ScanCode: 28
Alt: 0
-----KeyBoard Event End-----
-----KeyBoard Event Start-----
Time : Wed Dec 25 17:19:32 2019
MessageName: key sys down
WindowName: Pyhook [D:\Pyhook] - ...\records.py [Pyhook] - PyCharm
Ascii: 0
Key: Lmenu
ScanCode: 56
Alt: 32
-----KeyBoard Event End-----
-----KeyBoard Event Start-----
Time : Wed Dec 25 17:19:34 2019
MessageName: key sys down
WindowName: Pyhook [D:\Pyhook] - ...\records.py [Pyhook] - PyCharm
Ascii: 0
Key: Lmenu
ScanCode: 56
Alt: 32
-----KeyBoard Event End-----
-----KeyBoard Event Start-----
Time : Wed Dec 25 17:19:34 2019
MessageName: key sys down
WindowName: Pyhook [D:\Pyhook] - ...\records.py [Pyhook] - PyCharm
Ascii: 0
Key: Numpad2
ScanCode: 80
Alt: 32
-----KeyBoard Event End-----
鼠標信息記錄:包含移動和按下左鍵
信息為Time 事件時間,MessageName事件名稱,WindowName事件所在窗口名,Position鼠標所在坐標位置,Wheel鼠標滾輪的信息。
-----Mouse Event Start-----
Time : Wed Dec 25 17:19:42 2019
MessageName: mouse move
WindowName: None
Position: (997, 458)
Wheel: 0
-----Mouse Event End-----
-----Mouse Event Start-----
Time : Wed Dec 25 17:19:42 2019
MessageName: mouse move
WindowName: None
Position: (995, 458)
Wheel: 0
-----Mouse Event End-----
-----Mouse Event Start-----
Time : Wed Dec 25 17:19:42 2019
MessageName: mouse left down
WindowName: None
Position: (995, 458)
Wheel: 0
-----Mouse Event End-----
-----Mouse Event Start-----
Time : Wed Dec 25 17:19:42 2019
MessageName: mouse left up
WindowName: Pyhook [D:\Pyhook] - ...\records.py [Pyhook] - PyCharm
Position: (995, 458)
Wheel: 0
-----Mouse Event End-----