python 创建钩子 创建鼠标钩子和键盘钩子的测试代码

python 创建鼠标钩子和键盘钩子的测试代码

基于python 3.10.8 x64
使用ctypes 调用win32库实现

import win32api
import win32con
import win32process
import time
import ctypes
import io
import os
import threading
import queue
import tkinter as tk
import tkinter.ttk as ttk

from ctypes import wintypes, CFUNCTYPE, WINFUNCTYPE
from win32con import WH_KEYBOARD, WH_KEYBOARD_LL, WH_MOUSE, WH_MOUSE_LL, HC_ACTION
from win32con import WM_KEYDOWN, WM_KEYUP
from win32con import KF_EXTENDED, KF_ALTDOWN, KF_REPEAT, KF_UP

user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), (0x4 | 0x80 | 0x20 | 0x2 | 0x10 | 0x1 | 0x00 | 0x100))

CallNextHookEx = user32.CallNextHookEx
UnhookWindowsHookEx = user32.UnhookWindowsHookEx
SetWindowsHookEx = user32.SetWindowsHookExW

LRESULT = wintypes.LPARAM
# HINSTANCE = wintypes.WPARAM
# HHOOK = wintypes.WPARAM

Hookproc_ptr = WINFUNCTYPE(LRESULT, wintypes.INT, wintypes.WPARAM, wintypes.LPARAM)
SetWindowsHookEx.argtypes = (wintypes.INT, Hookproc_ptr, wintypes.HINSTANCE, wintypes.DWORD)
SetWindowsHookEx.restype = wintypes.HHOOK

CallNextHookEx.argtypes = (wintypes.HHOOK, wintypes.INT, wintypes.WPARAM, wintypes.LPARAM)
CallNextHookEx.restype = LRESULT

class Structure_walk:
    '''遍历结构体'''

    def __init__(self, struct_obj):
        self.struct_obj = struct_obj
        self.stack = []
        self.dump = {}

    def walk_struct(self, struct_obj):
        for field_name, field_type in struct_obj._fields_:
            field_value = getattr(struct_obj, field_name)
            self.stack.append(field_name)
            if isinstance(field_value, (ctypes.Structure, ctypes.Union)):
                self.walk_struct(field_value)
            else:
                self.dump['.'.join(self.stack)] = field_value
            self.stack.pop()

    def __str__(self):
        self.stack.clear()
        self.dump.clear()
        self.walk_struct(self.struct_obj)
        return str(self.dump)


class Structure_tool:
    def __str__(self) -> str:
        return str(Structure_walk(self))
    __repr__ = __str__


class MOUSEHOOKSTRUCT(ctypes.Structure, Structure_tool):
    _fields_ = [("pt", wintypes.POINT),
                ("hwnd", wintypes.HWND),
                ("wHitTestCode", wintypes.UINT),
                ("dwExtraInfo", wintypes.DWORD),
                ]


class MSLLHOOKSTRUCT(ctypes.Structure, Structure_tool):
    _fields_ = [("pt", wintypes.POINT),
                ("mouseData", wintypes.DWORD),
                ("flags", wintypes.DWORD),
                ("time", wintypes.DWORD),
                ("dwExtraInfo", wintypes.DWORD),
                ]


class KBDLLHOOKSTRUCT(ctypes.Structure, Structure_tool):
    _fields_ = [("vkCode", wintypes.DWORD),
                ("scanCode", wintypes.DWORD),
                ("flags", wintypes.DWORD),
                ("time", wintypes.DWORD),
                ("dwExtraInfo", wintypes.DWORD),
                ]

pidmsg = io.StringIO()
print("当前进程ID: ", os.getpid(), f'{os.getpid():08X}',file=pidmsg)
print("当前线程ID: ", threading.get_ident(), f'{threading.get_ident():08X}',file=pidmsg)
print(pidmsg.getvalue())

g_log = queue.Queue(1024)


def show_log():
    while True:
        msg = g_log.get()
        print(msg)


td = threading.Thread(target=show_log, args=(), kwargs=None, daemon=True)
td.start()


class 钩子测试App:
    def __init__(self, master=None):
        # build ui
        self.toplevel1 = tk.Tk() if master is None else tk.Toplevel(master)
        self.toplevel1.configure(height=200, width=200)
        self.toplevel1.geometry("320x200")
        self.button1 = ttk.Button(self.toplevel1)
        self.button1.configure(text='button1')
        self.button1.pack(side="top")
        self.button1.bind("<Button>", self.callback1, add="")
        self.label1 = ttk.Label(self.toplevel1)
        self.message1 = tk.StringVar(value='label1')
        self.label1.configure(text='label1', textvariable=self.message1)
        self.label1.pack(side="top")

        # Main widget
        self.mainwindow = self.toplevel1

    def run(self):
        self._hookID = 0
        self.message1.set(pidmsg.getvalue())
        self.mainwindow.mainloop()

    def callback1(self, event=None):
        if self._hookID == 0:
            chwnd = self.mainwindow.winfo_id()
            threadId, processId = win32process.GetWindowThreadProcessId(chwnd)

            self.KeyboardProc = Hookproc_ptr(self.PyKeyboardProc)
            self.MouseProc = Hookproc_ptr(self.PyMouseProc)

            mode = 2
            if mode == 1:
                self.idHook = WH_KEYBOARD
                self._hookID = SetWindowsHookEx(self.idHook, self.KeyboardProc, 0, threadId)
            elif mode == 2:
                self.idHook = WH_KEYBOARD_LL
                self._hookID = SetWindowsHookEx(self.idHook, self.KeyboardProc, 0, 0)
            elif mode == 3:
                self.idHook = WH_MOUSE
                self._hookID = SetWindowsHookEx(self.idHook, self.MouseProc, 0, threadId)
            elif mode == 4:
                self.idHook = WH_MOUSE_LL
                self._hookID = SetWindowsHookEx(self.idHook, self.MouseProc, 0, 0)

            print(self._hookID)

            if isinstance(self._hookID, int) and self._hookID != 0:
                print('挂钩成功')
                pidmsg.write(f'\n挂钩ID: {self._hookID} {self._hookID:08X}')
                self.message1.set(pidmsg.getvalue())
            else:
                print('挂钩失败')
                self._hookID = 0

    def PyMouseProc(self, nCode: int, wParam: int, lParam: int):

        if nCode == HC_ACTION:
            if self.idHook == WH_MOUSE:
                MS_Data = MOUSEHOOKSTRUCT.from_address(lParam)
                g_log.put((nCode, wParam, MS_Data))
            elif self.idHook == WH_MOUSE_LL:
                MS_Data = MSLLHOOKSTRUCT.from_address(lParam)
                g_log.put((nCode, wParam, MS_Data))
        return CallNextHookEx(self._hookID, nCode, wParam, lParam)

    def PyKeyboardProc(self, nCode: int, wParam: int, lParam: int):

        if nCode == HC_ACTION:
            if self.idHook == WH_KEYBOARD:
                keyFlags = (lParam & 0xFFFF_0000) >> 16
                KB_Data = {
                    'vkCode': wParam,
                    'lParam': f"{lParam:08X}",
                    'repeatCount': lParam & 0x0000FFFF,
                    # 0-15	重复计数。 该值是用户按住键后重复击键的次数。
                    'ScanCode': (lParam & 0x00FF0000) >> 16,
                    # 16-23 扫描代码。 此值取决于 OEM。
                    # 'keyFlags': (lParam & 0xFFFF_0000) >> 16,
                    'isExtendedKey': (keyFlags & KF_EXTENDED) == KF_EXTENDED,
                    # 24	指示键是扩展键,例如功能键还是数字键盘上的键。 如果键是扩展键,则值为 1;否则为 0。
                    'isAltDown': (keyFlags & KF_ALTDOWN) == KF_ALTDOWN,
                    # 29	上下文代码。 如果 Alt 键关闭,则值为 1;否则为 0。
                    'isRepeat': (keyFlags & KF_REPEAT) == KF_REPEAT,
                    # 30	上一个键状态。 如果在发送消息之前键关闭,则值为 1;如果键已打开,则为 0。
                    'isKeyUp': (keyFlags & KF_UP) == KF_UP,
                    # 31	转换状态。 如果按下键,则值为 0;如果释放键,则值为 1。
                }
                g_log.put((nCode, wParam, KB_Data))
            elif self.idHook == WH_KEYBOARD_LL:
                KB_Data = KBDLLHOOKSTRUCT.from_address(lParam)
                g_log.put((nCode, wParam, KB_Data))
        return CallNextHookEx(self._hookID, nCode, wParam, lParam)

    def __del__(self):
        if self._hookID > 0:
            UnhookWindowsHookEx(self._hookID)


if __name__ == "__main__":
    app = 钩子测试App()
    app.run()


  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值