如何用Python发送键鼠原始输入,适配仅接受原生输入的程序?
嘿,这个问题我太熟了——很多游戏、专业软件会刻意屏蔽上层Python库的模拟输入,只认硬件级的原始输入流。别慌,给你几个从易到难的解决方案,总有一个能搞定:
方案1:用Windows API直接发送原始输入(Windows平台首选)
像pynput、pyautogui这类库都是在系统输入框架的上层做模拟,容易被目标程序拦截。而Windows自带的SendInput API是直接把输入事件注入系统的原始输入队列,和真实键鼠硬件产生的输入几乎无差别,大部分程序都会买账。
你可以用Python的ctypes库直接调用这个API,不用额外安装依赖。下面是完整的示例代码,包含鼠标点击和键盘按键模拟:
import ctypes from ctypes import wintypes # 加载Windows用户32位动态链接库 user32 = ctypes.WinDLL('user32', use_last_error=True) # 定义输入类型常量 INPUT_MOUSE = 0 INPUT_KEYBOARD = 1 # 定义鼠标输入结构体 class MOUSEINPUT(ctypes.Structure): _fields_ = (("dx", wintypes.LONG), ("dy", wintypes.LONG), ("mouseData", wintypes.DWORD), ("dwFlags", wintypes.DWORD), ("time", wintypes.DWORD), ("dwExtraInfo", wintypes.ULONG_PTR)) # 定义键盘输入结构体 class KEYBDINPUT(ctypes.Structure): _fields_ = (("wVk", wintypes.WORD), ("wScan", wintypes.WORD), ("dwFlags", wintypes.DWORD), ("time", wintypes.DWORD), ("dwExtraInfo", wintypes.ULONG_PTR)) # 定义通用输入结构体 class INPUT(ctypes.Structure): class _INPUT(ctypes.Union): _fields_ = (("mi", MOUSEINPUT), ("ki", KEYBDINPUT)) _anonymous_ = ("_input",) _fields_ = (("type", wintypes.DWORD), ("_input", _INPUT)) # 模拟鼠标左键点击(传入屏幕坐标x,y) def simulate_mouse_click(x, y): # 把屏幕坐标转换为API要求的0-65535绝对坐标范围 screen_width = user32.GetSystemMetrics(0) screen_height = user32.GetSystemMetrics(1) dx = int(x * 65535 / screen_width) dy = int(y * 65535 / screen_height) # 构造移动到目标位置的输入事件 move_input = INPUT(type=INPUT_MOUSE) move_input.mi.dx = dx move_input.mi.dy = dy move_input.mi.dwFlags = 0x0001 | 0x8000 # MOUSEEVENTF_ABSOLUTE + MOUSEEVENTF_MOVE # 构造左键按下事件 down_input = INPUT(type=INPUT_MOUSE) down_input.mi.dwFlags = 0x0002 # MOUSEEVENTF_LEFTDOWN # 构造左键释放事件 up_input = INPUT(type=INPUT_MOUSE) up_input.mi.dwFlags = 0x0004 # MOUSEEVENTF_LEFTUP # 批量发送输入事件 inputs = (INPUT * 3)(move_input, down_input, up_input) user32.SendInput(len(inputs), ctypes.byref(inputs), ctypes.sizeof(INPUT)) # 模拟键盘按键(传入虚拟键码,比如0x41对应A键) def simulate_key_press(key_code): # 构造按键按下事件 down_input = INPUT(type=INPUT_KEYBOARD) down_input.ki.wVk = key_code down_input.ki.dwFlags = 0 # 构造按键释放事件 up_input = INPUT(type=INPUT_KEYBOARD) up_input.ki.wVk = key_code up_input.ki.dwFlags = 0x0002 # KEYEVENTF_KEYUP # 批量发送输入事件 inputs = (INPUT * 2)(down_input, up_input) user32.SendInput(len(inputs), ctypes.byref(inputs), ctypes.sizeof(INPUT)) # 测试代码:移动到(500,500)点击,然后按A键 simulate_mouse_click(500, 500) simulate_key_press(0x41)
小提示:
- 虚拟键码可以查Windows官方文档,常用的比如
0x0D是回车,0x1B是ESC。 - 运行脚本时最好以管理员身份启动Python,避免权限不足的问题。
方案2:创建虚拟输入设备(跨平台,最底层)
如果SendInput还是被拦截,那得直接模拟硬件级的输入——也就是创建一个虚拟的键鼠设备,让系统认为是真实硬件在发送输入。这个方法几乎能绕过所有检测,但需要系统级权限。
Linux平台示例(需要root权限):
Linux下可以通过/dev/uinput设备创建虚拟输入,直接写入输入事件:
import os import struct # 打开uinput设备(需要root) uinput_fd = os.open("/dev/uinput", os.O_WRONLY | os.O_NONBLOCK) # 设置支持的输入事件类型(鼠标移动、左右键) os.write(uinput_fd, struct.pack('HHI', 0x01, 0x00, 0x01)) # EV_KEY + BTN_LEFT os.write(uinput_fd, struct.pack('HHI', 0x01, 0x01, 0x01)) # EV_KEY + BTN_RIGHT os.write(uinput_fd, struct.pack('HHI', 0x02, 0x00, 0x01)) # EV_REL + REL_X(X轴移动) os.write(uinput_fd, struct.pack('HHI', 0x02, 0x01, 0x01)) # EV_REL + REL_Y(Y轴移动) # 创建虚拟设备 os.write(uinput_fd, struct.pack('I', 0x00)) # UI_DEV_CREATE # 模拟鼠标移动+左键点击 def simulate_mouse_action(): # 向右移动10像素,向下移动10像素 os.write(uinput_fd, struct.pack('LLHHI', 0, 0, 0x02, 0x00, 10)) os.write(uinput_fd, struct.pack('LLHHI', 0, 0, 0x02, 0x01, 10)) os.write(uinput_fd, struct.pack('LLHHI', 0, 0, 0x00, 0x00, 0)) # 同步事件 # 按下左键 os.write(uinput_fd, struct.pack('LLHHI', 0, 0, 0x01, 0x00, 1)) os.write(uinput_fd, struct.pack('LLHHI', 0, 0, 0x00, 0x00, 0)) # 释放左键 os.write(uinput_fd, struct.pack('LLHHI', 0, 0, 0x01, 0x00, 0)) os.write(uinput_fd, struct.pack('LLHHI', 0, 0, 0x00, 0x00, 0)) simulate_mouse_action() # 销毁虚拟设备并关闭文件句柄 os.write(uinput_fd, struct.pack('I', 0x01)) # UI_DEV_DESTROY os.close(uinput_fd)
Windows平台:
Windows下需要用HID(人机接口设备)相关的API或者第三方驱动库(比如ViGEm)来创建虚拟设备,复杂度较高,如果你是Windows用户,优先试试方案1和方案3。
方案3:借助第三方工具简化开发(最省心)
如果不想写底层代码,直接用专门的自动化工具配合Python调用就行。比如Windows下的AutoHotkey,它的模拟输入是硬件级的,很多屏蔽模拟输入的程序都能识别。
步骤如下:
- 安装AutoHotkey(官网下载即可)
- 写一个简单的ahk脚本,比如
auto_task.ahk:
; 移动鼠标到(500,500)并左键点击 Click 500,500 ; 按下A键 Send {a}
- 在Python中通过
subprocess调用这个脚本:
import subprocess subprocess.run(["AutoHotkey.exe", "auto_task.ahk"])
这个方法简单粗暴,不用关心底层实现,适合快速解决问题。
最后提醒
- 如果目标程序是带反作弊的游戏,可能会检测虚拟输入设备或者API调用,这种情况可能需要更复杂的绕过手段,但这不在常规技术讨论范围内。
- 所有底层模拟操作都建议关闭杀毒软件的实时防护,避免被误判为恶意程序。
内容的提问来源于stack exchange,提问作者Joseph Lawrence Loeffler




