
1. 项目概述当软件模拟遇到硬件壁垒在自动化测试、游戏辅助或者一些需要模拟用户输入的特定场景里我们最先想到的工具往往是像pyautogui、pynput这样的软件库。它们通过调用操作系统的API来模拟鼠标点击和键盘按键对于大多数日常任务来说简单高效。然而当你把目标转向一些对输入设备有严格检测机制的应用特别是某些在线游戏或安全级别较高的软件时这套“软模拟”的方案就立刻失效了。这些应用会深入检查输入事件的来源判断其是来自真实的物理硬件中断还是来自系统内部的消息队列。一旦被判定为“非硬件输入”轻则操作无效重则直接触发反作弊机制导致账号受限。这就是硬件模拟模块的用武之地。CH9329 就是这样一款芯片它本质上是一个 USB 转串口UART的 HID人机接口设备键盘鼠标芯片。简单来说你的电脑通过串口比如USB转TTL模块向 CH9329 发送特定的指令数据包CH9329 接收到后会“扮演”成一个全新的、独立的USB键盘或鼠标设备向电脑发送真实的硬件级输入信号。从操作系统的视角看这就是一个实实在在插在USB口上的键盘或鼠标在操作软件层面的检测手段对此几乎无能为力。所以这个项目的核心就是利用 Python 作为控制大脑通过串口协议与 CH9329 硬件模块“对话”指挥它去执行我们预设的、精准的键盘鼠标操作序列从而绕过那些基于软件层的检测。这不仅仅是写一个脚本更是软硬件结合的实战涉及到串口通信、HID协议解析、时序控制等多个层面的知识。接下来我会带你从硬件连接到脚本编写完整走一遍这个流程并分享我在实际调试中踩过的坑和总结的技巧。2. 硬件准备与连接指南2.1 CH9329模块选购与引脚识别市面上常见的CH9329模块通常是一个集成了CH9329芯片、晶振、USB接口和排针的小板子。选购时建议选择带有TX、RX、VCC3.3V或5V、GND引脚引出并且最好自带USB-A母口或Type-C接口的版本这样连接和供电都更方便。拿到模块后首先要识别关键引脚VCC 电源正极。至关重要绝大多数CH9329模块的工作电压是3.3V。在连接前务必查阅你手中模块的数据手册或商品描述确认电压。如果误接5V很可能烧毁芯片。GND 电源地线。与控制器共地。TX (Transmit) 模块的发送引脚。注意这是从模块的角度定义的。所以模块的TX应该连接到你的控制器如电脑USB转TTL模块、单片机的RX (Receive)引脚。RX (Receive) 模块的接收引脚。同理模块的RX应连接到控制器的TX引脚。注意串口通信是交叉连接的即“发对收收对发”。这是新手最容易接错的地方。2.2 与电脑建立通信链路由于我们的控制端是运行Python的电脑而CH9329需要串口指令所以我们需要一个“中介”——USB转TTL串口模块如FT232RL、CH340、CP2102等。连接步骤如下连接硬件USB转TTL模块的3.3V- CH9329模块的VCC。再次确认电压USB转TTL模块的GND- CH9329模块的GND。USB转TTL模块的TX- CH9329模块的RX。USB转TTL模块的RX- CH9329模块的TX。连接电脑将USB转TTL模块插入电脑的USB端口。将CH9329模块自身的USB口如果它有插入电脑的另一个USB端口。这个USB口仅用于供电不用于数据传输。有些模块设计成只需串口供电则无需连接此USB口。系统识别打开电脑的设备管理器Windows或使用lsusb命令Linux。你应该能看到两个新设备一个是USB转TTL的串行端口COMx 或 /dev/ttyUSBx另一个是CH9329模拟出来的HID设备可能显示为“USB输入设备”或“HID-键盘”。2.3 Python环境与依赖库安装确保你的Python环境建议3.7以上已就绪。核心库是pyserial用于串口通信。pip install pyserial安装后可以在Python中导入serial模块。为了后续调试方便我强烈建议再安装一个keyboard库仅用于监听和测试非必须但注意它和我们的硬件模拟是两回事。pip install keyboard3. CH9329协议核心解析与Python封装CH9329通过串口接收特定格式的数据帧来控制键盘鼠标。理解这个协议是编写脚本的基础。官方协议文档是必备资料这里我提炼出最常用的键盘部分的核心帧格式。3.1 键盘报告帧结构详解一个完整的控制键盘的指令帧通常如下十六进制表示57 AB [Len] [Cmd] [Data0] [Data1] ... [DataN] [Sum]我们来拆解每一个部分帧头 (Header)0x57, 0xAB。这是固定的标识一个CH9329指令的开始。数据长度 (Len)1字节。表示从[Cmd]到[DataN]所有数据的字节数。例如如果后面有4个字节的数据长度就是0x04。命令字 (Cmd)1字节。对于键盘按键操作常用的是0x02带按下和释放的按键报告或0x03只发送按下状态的报告需要另发释放报告。数据区 (Data)长度可变。对于键盘报告Cmd0x02通常有8个字节Data0: 修饰键状态Bitmask。例如0x01左Ctrl0x02左Shift0x04左Alt0x08左Win/GUI0x10右Ctrl等。Data1: 保留位固定为0x00。Data2~Data7: 这6个字节代表同时按下的普通键的键值HID Usage ID。如果按键少于6个用0x00填充。每个键都有对应的HID码例如0x04是‘a/A’0x05是‘b/B’0x1E是‘1/!’0x28是回车Enter。校验和 (Sum)1字节。从[Len]开始到[DataN]结束所有字节相加后取低8位即和值 0xFF。用于验证数据在传输中是否出错。举例模拟按下并释放“A”键假设是大写即ShiftA。按下帧Shift修饰键(0x02) ‘a’键(0x04)。数据区[0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00](8字节)长度0x0A(Cmd 1字节 Data 8字节 9字节等等这里容易算错。标准键盘报告帧的数据区就是8字节加上Cmd是9字节所以Len0x09。我故意留个坑下面纠正。)正确计算Cmd(0x02) Data(8字节) 9字节 -Len 0x09。校验和0x09 0x02 0x02 0x00 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x11(十进制17)。0x11 0xFF 0x11。完整帧57 AB 09 02 02 00 04 00 00 00 00 00 00 11释放帧所有键值清零。数据区[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]Len:0x09校验和0x09 0x02 0x00 ... 0x00 0x0B完整帧57 AB 09 02 00 00 00 00 00 00 00 00 0B实操心得校验和计算错误是导致指令无响应的最常见原因。务必写一个独立的函数来计算校验和并在发送前打印出完整的帧进行核对。我习惯先用Python交互环境手动计算并发送一帧确认硬件有反应比如NumLock灯闪烁一下后再进行后续开发。3.2 构建Python控制类为了方便调用我们将协议封装成一个Python类。这里给出一个精简但功能核心的版本。import serial import time import threading class CH9329Keyboard: CH9329 键盘模拟控制器 # 常用HID键值映射部分 KEY_MAP { a: 0x04, b: 0x05, c: 0x06, d: 0x07, e: 0x08, 1: 0x1E, 2: 0x1F, 3: 0x20, enter: 0x28, esc: 0x29, backspace: 0x2A, tab: 0x2B, space: 0x2C, capslock: 0x39, f1: 0x3A, f2: 0x3B, f3: 0x3C, ctrl: 0xE0, shift: 0xE1, alt: 0xE2, win: 0xE3, right_shift: 0xE5, } # 修饰键位掩码 MODIFIER_MASK { left_ctrl: 0x01, left_shift: 0x02, left_alt: 0x04, left_gui: 0x08, right_ctrl: 0x10, right_shift: 0x20, right_alt: 0x40, right_gui: 0x80, } def __init__(self, portCOM3, baudrate9600, timeout1): 初始化串口连接 :param port: 串口号如 COM3, /dev/ttyUSB0 :param baudrate: 波特率CH9329 常用 9600 :param timeout: 读写超时时间 self.ser serial.Serial(port, baudrate, timeouttimeout) time.sleep(2) # 等待串口稳定 print(fCH9329 键盘模拟器已连接在 {port}) def _calculate_checksum(self, data): 计算从长度字段开始的校验和低8位 return sum(data) 0xFF def _send_frame(self, cmd, data_bytes): 构建并发送完整数据帧 length len(data_bytes) 1 # 数据长度 数据区字节数 1字节Cmd frame [0x57, 0xAB, length, cmd] data_bytes checksum self._calculate_checksum(frame[2:]) # 从Length开始计算 frame.append(checksum) self.ser.write(bytearray(frame)) # 调试时可打印 # print(f发送帧: {[hex(x) for x in frame]}) def press_key(self, key, modifier0): 按下单个键或组合键 :param key: 普通键的字符串名如 a, enter :param modifier: 修饰键的掩码如 MODIFIER_MASK[left_shift] if key not in self.KEY_MAP: raise ValueError(f不支持的键: {key}) key_code self.KEY_MAP[key] # 标准8字节键盘报告数据区 # 字节0: 修饰键字节1: 保留字节2-7: 6个普通键 data [modifier, 0x00, key_code] [0x00] * 5 self._send_frame(0x02, data) # Cmd 0x02 表示按键报告 def release_key(self): 释放所有按键 data [0x00] * 8 # 全零数据区表示释放 self._send_frame(0x02, data) def tap_key(self, key, modifier0, duration0.05): 点按一次按键按下后稍作延迟再释放 self.press_key(key, modifier) time.sleep(duration) # 按下持续时间模拟人手 self.release_key() time.sleep(0.02) # 释放后短暂间隔 def close(self): 关闭串口连接 self.ser.close() print(串口连接已关闭) # 使用示例 if __name__ __main__: kb CH9329Keyboard(portCOM3) # 请根据实际情况修改端口 time.sleep(1) # 额外等待设备初始化 kb.tap_key(a) # 输入 a kb.tap_key(b) # 模拟 Shift 1 ! kb.tap_key(1, modifierCH9329Keyboard.MODIFIER_MASK[left_shift]) kb.close()这个类提供了最基本的功能。在实际复杂脚本中你可能需要扩展它比如支持多个普通键同时按下填充data[2]到data[7]、处理鼠标命令Cmd0x05或0x06、或者加入错误重试机制。4. 自动化脚本实战设计与避坑要点有了控制硬件的底层能力我们就可以设计上层逻辑了。自动化脚本的核心是“在正确的时间发送正确的指令”。这涉及到监听、判断、决策、执行等多个环节。4.1 状态检测与触发机制游戏或应用中的自动化首先需要让脚本“知道”当前发生了什么。由于我们无法直接从游戏内存中安全读取数据这本身就可能触发反作弊通常采用基于图像识别或像素检测的方法。PyAutoGUI 图像定位适用于界面元素固定的情况。你可以截取一个“开始战斗”按钮的小图然后让脚本循环查找这个图片在屏幕上的位置。import pyautogui # 注意pyautogui的截图和查找功能会占用CPU且在全屏游戏下可能受限。 button_location pyautogui.locateOnScreen(start_button.png, confidence0.8) if button_location: # 找到按钮可以触发CH9329按键 kb.tap_key(f5) # 假设F5是开始快捷键避坑技巧confidence参数在OpenCV后端下可用能提高容错率。对于动态变化的UI截取最具特征性的小部分区域作为模板比截取整个按钮更可靠。像素点颜色检测更轻量、更快的方法。例如检测屏幕特定坐标如100200的像素颜色是否为某个代表“可点击”状态的颜色比如绿色的RGB值。import pyautogui pixel_color pyautogui.pixel(100, 200) if pixel_color (0, 255, 0): # 如果是绿色 kb.tap_key(e) # 执行交互注意事项像素检测对屏幕分辨率、游戏窗口位置非常敏感。务必在脚本开始时校准坐标或者使用相对坐标如窗口左上角偏移量。游戏全屏和窗口化模式下的坐标完全不同。4.2 操作序列与延时策略模拟人类操作的精髓在于“不规律”和“合理的延迟”。直接发送毫秒级精确的指令流是明显的机器行为。随机化延迟不要在每次操作间使用固定的time.sleep(0.5)。使用随机间隔。import random def human_delay(base0.3, variation0.2): 生成一个近似人类的延迟时间 delay base random.uniform(-variation, variation) delay max(0.05, delay) # 确保不为负或过小 time.sleep(delay) # 使用 kb.tap_key(1) human_delay(0.4, 0.15) # 延迟 ~0.25s ~ 0.55s kb.tap_key(2)操作序列多样化如果脚本是循环执行某个任务不要每次都完全一样的按键顺序。可以设计几套稍有不同的操作模式随机选择一套执行。加入无效操作偶尔在安全区域如非战斗状态随机移动一下鼠标通过CH9329的鼠标命令、按一下无关的按键可以增加行为的真实性。但要注意频率不能太高以免影响主要任务效率。4.3 一个简单的循环任务脚本框架结合状态检测和CH9329控制一个基本的自动化循环框架如下import time import random from ch9329_controller import CH9329Keyboard # 导入我们之前封装的类 import pyautogui import sys def main(): kb CH9329Keyboard(portCOM3) print(自动化脚本启动。按CtrlC终止。) try: while True: # 1. 状态检测阶段 # 示例检测屏幕中心偏下是否出现红色代表需要吃血瓶 check_x, check_y 960, 800 # 假设1080p屏幕中心 try: # 获取像素颜色可能失败如屏幕锁定时需要异常捕获 if pyautogui.pixel(check_x, check_y)[0] 200: # 红色通道值高 print(检测到低血量使用血瓶) # 2. 执行操作阶段 kb.tap_key(5) # 假设5键是血瓶 # 使用随机延迟模拟反应时间 time.sleep(random.uniform(0.2, 0.5)) except pyautogui.ImageNotFoundException: # 像素获取失败可能屏幕状态异常短暂等待后继续 time.sleep(1) continue except Exception as e: print(f检测时发生未知错误: {e}) time.sleep(1) # 3. 常规攻击循环示例 # 这里可以加入其他检测比如检测技能冷却通过检测技能图标颜色 kb.tap_key(1) # 使用技能1 time.sleep(random.uniform(0.8, 1.2)) # 技能后摇 kb.tap_key(left_click) # 假设你扩展了鼠标点击功能 time.sleep(random.uniform(0.3, 0.6)) # 4. 每轮大循环后稍作休息降低CPU占用和检测频率 time.sleep(0.1) except KeyboardInterrupt: print(\n用户中断脚本。) finally: kb.close() print(脚本结束。) if __name__ __main__: main()5. 调试技巧与高级应用思路5.1 串口调试与指令验证在编写复杂脚本前务必确保基础通信是正常的。使用串口调试助手先不写Python用工具如Arduino IDE的串口监视器、Putty、或者Windows的“串行终端”手动发送十六进制指令看CH9329是否有反应键盘灯闪烁、鼠标移动。这能最快隔离是硬件连接问题、指令格式问题还是Python代码问题。在Python中打印原始帧如前所述在_send_frame方法中加入打印语句将准备发送的字节列表以十六进制形式输出到控制台。与你在串口调试助手中测试成功的指令进行逐字节对比。监听系统输入打开一个记事本运行你的脚本观察字符是否正确输入。对于鼠标可以观察光标是否移动。5.2 应对复杂交互组合键与长按有些游戏操作需要长按如蓄力或复杂的组合键如CtrlShiftE。长按实现分别发送“按下”和“释放”指令中间间隔一段时间。注意CH9329的0x02命令是“瞬时状态报告”你发送一个带按键的帧它就会持续按下直到你发送一个全零的释放帧。# 长按W键2秒前进 kb.press_key(w) time.sleep(2.0) kb.release_key()组合键实现计算好修饰键掩码并填充多个普通键到数据区。# 模拟 Ctrl Shift S 保存 modifier (kb.MODIFIER_MASK[left_ctrl] | kb.MODIFIER_MASK[left_shift]) # 数据区修饰键保留位然后是s键和其他键最多6个 data [modifier, 0x00, kb.KEY_MAP[s]] [0x00]*5 kb._send_frame(0x02, data) # 按下 time.sleep(0.1) kb.release_key() # 释放5.3 性能与稳定性优化降低检测频率不要在一个紧密循环里疯狂调用pyautogui.pixel或locateOnScreen。这会导致CPU占用率飙升也可能让游戏客户端检测到异常。合理的做法是每次循环后有一个sleep或者使用单独的线程以固定频率进行检测。异常处理与重连在网络波动或USB口休眠时串口可能会断开。在脚本中增加异常捕获当串口读写失败时尝试重新初始化连接。资源清理使用try...finally确保脚本无论以何种方式退出都能正确关闭串口。5.4 道德、风险与替代方案最后必须强调将此类技术用于在线游戏自动化特别是多人游戏很可能违反游戏的服务条款存在账号被封禁的风险。此项目的技术分享旨在探讨硬件接口自动化在合法合规场景下的应用例如自动化测试软件UI。控制智能家居或办公设备将CH9329作为遥控器。为残障人士设计辅助输入设备。在单机游戏或自己拥有完全控制权的环境中进行自动化。如果你需要的只是合法合规的桌面自动化且不涉及对抗检测那么纯软件的pyautogui、pynput或更专业的RPA工具如UiPath, Automation Anywhere是更简单、更安全的选择。CH9329方案是当你明确遇到软件模拟被封锁时的“最后手段”。硬件模拟打开了自动化的一扇新门但它带来的控制和风险是并存的。理解原理谨慎使用把它当作学习嵌入式通信和协议交互的绝佳平台其价值远不止于游戏脚本本身。在调试过程中那一帧帧十六进制代码最终化为屏幕上精准的动作时那种软硬件协同工作的成就感才是这个项目最迷人的地方。