1. 项目缘起:从“软件爪子”到“硬件爪子”的进化
最近在折腾一个挺有意思的玩意儿,我把它叫做DuckyClaw。这个名字听起来有点怪,但如果你玩过或者听说过“OpenClaw”这个项目,大概就能猜到几分。简单来说,DuckyClaw 是一个硬件导向的 OpenClaw 实现。OpenClaw 本身是一个开源的、基于软件的自动化点击和模拟工具,常用于一些重复性的桌面操作,比如游戏挂机、软件测试、数据录入等等。它的核心思想是“录制-回放”,你录下鼠标和键盘的操作,它就能帮你一遍遍执行。
但纯软件的方案,玩久了总会遇到一些天花板。比如,它很难绕过一些基于硬件或底层驱动的反作弊检测;再比如,它对系统资源的占用和操作的精确度,在要求极高的场景下会显得力不从心。更重要的是,作为一个喜欢动手的硬件爱好者,我总觉得纯软件的方案少了点“物理交互”的乐趣和可靠性。于是,一个想法就冒出来了:能不能把 OpenClaw 的核心逻辑,从电脑软件里“抽”出来,塞进一个独立的硬件设备里?让这个设备自己就能模拟 USB 键盘和鼠标,直接与目标电脑交互,成为一个物理层面的“自动化爪子”?这就是 DuckyClaw 项目的起点。
它的目标很明确:打造一个低成本、易开发、高性能的硬件自动化工具。它不再依赖于在目标电脑上安装和运行任何软件客户端,自身就是一个标准的 USB HID(人机接口设备)。插上电脑,电脑就认为它是一个普通的键盘和鼠标,从而实现了最大程度的兼容性和隐蔽性。这个思路其实并不新鲜,市面上早有类似“USB Rubber Ducky”(橡皮鸭)或一些基于微控制器的宏键盘产品。但 DuckyClaw 的独特之处在于,它试图在硬件平台上,复现并扩展 OpenClaw 那种灵活、可编程的脚本能力,同时保持极致的轻量化和可定制性。
2. 核心架构选型:为什么是 RP2040?
确定了硬件化的方向,下一个关键问题就是:用什么来做这颗“大脑”?市面上主流的微控制器(MCU)选择很多,从经典的 Arduino(AVR)、到性能强大的 ESP32,再到资源丰富的 STM32 系列。经过一番权衡,我最终选择了Raspberry Pi Pico 的核心——RP2040 双核 ARM Cortex-M0+ 微控制器。这个选择背后有几个非常实际的考量。
2.1 性能与成本的完美平衡
RP2040 拥有 133MHz 的主频和双核架构,处理复杂的脚本逻辑和时序控制绰绰有余。相比传统 AVR 芯片(如 Arduino Uno 用的 ATmega328P,16MHz),性能是碾压级的。而它的价格却非常亲民,搭载 RP2040 的 Raspberry Pi Pico 开发板官方售价仅 4 美元左右,性价比极高。对于 DuckyClaw 这种可能需要大量部署(比如多机同步测试)或者爱好者自制项目来说,成本是必须严肃考虑的因素。
2.2 出色的 USB 支持
这是最关键的一点。RP2040 内置了 USB 1.1 控制器,并且可以通过软件灵活配置为各种 USB 设备类型。开源社区围绕它已经建立了非常成熟和稳定的 USB 协议栈,特别是TinyUSB这个开源项目,被广泛用于 RP2040 的 USB 设备开发。这意味着,我可以相对轻松地让 DuckyClaw 被识别为一个复合 USB 设备(同时是键盘和鼠标),而无需像某些 MCU 那样需要外接 USB 芯片,简化了硬件设计,也降低了成本和功耗。
2.3 丰富的内存与灵活的存储
RP2040 片上集成了 264KB 的 SRAM,这为运行复杂的脚本解释器或缓存大量指令序列提供了充足的空间。同时,它支持通过 QSPI 接口连接外部 Flash。Raspberry Pi Pico 板载了 2MB 的 Flash,这足以存储大量的自动化脚本、配置文件甚至简单的文件系统。我们可以把脚本以文本或二进制格式存放在这里,实现“脱机运行”——即编写好脚本存入 DuckyClaw,之后它可以在任何电脑上即插即用,无需额外携带脚本文件。
2.4 活跃的生态系统与开发体验
Raspberry Pi 基金会带来的不仅仅是芯片,还有一整套友好的开发环境。无论是使用官方的 C/C++ SDK,还是通过 CircuitPython、MicroPython 进行快速原型开发,都有完善的文档和社区支持。特别是MicroPython,对于实现 DuckyClaw 的脚本功能来说简直是天作之合。我们可以用 Python 语法来编写自动化脚本,极大降低了用户的学习和使用门槛,这与 OpenClaw 的易用性理念一脉相承。开发工具链(如picotool)和调试手段也比较完善。
注意:虽然 ESP32 也有很强的网络能力和不错的性能,但其 USB OTG 功能通常需要额外的硬件支持(如 ESP32-S2/S3),且作为 USB 设备的生态和稳定性在早期不如 RP2040 成熟。STM32 系列固然强大,但型号繁杂,入门门槛和开发环境配置相对复杂。综合来看,RP2040 在“USB 设备”、“性价比”、“开发生态”这三个 DuckyClaw 最核心的需求点上,取得了最好的平衡。
3. 硬件设计详解:从原理图到PCB
确定了核心芯片,接下来就是围绕 RP2040 设计具体的硬件电路。DuckyClaw 的硬件部分追求极简和实用,核心功能就是“模拟输入”和“脚本存储/执行”。
3.1 最小系统与电源设计
RP2040 的最小系统非常简单。需要一颗外部低速晶振(通常为 12MHz)作为 USB 时钟的参考源,这是稳定实现 USB 通信所必须的。几个去耦电容(0.1uF 和 10uF)靠近电源引脚放置,以保证芯片工作的稳定性。电源方面,由于我们主要通过 USB 总线取电(5V),所以需要一个 LDO(低压差线性稳压器)将 5V 转换为 RP2040 核心所需的 3.3V。这里我选择了常见的 AMS1117-3.3,成本低且足够可靠。如果考虑低功耗休眠等进阶功能,也可以选用效率更高的 DC-DC 降压芯片。
一个重要的细节是USB 数据线(D+ 和 D-)的走线。它们应该是一对差分线,在 PCB 布局上需要尽量等长、平行走线,并避免穿过噪声大的区域(如电源线),以减少信号完整性问题,确保 USB 连接稳定。对于简单的双面板设计,这需要稍加注意。
3.2 用户交互与状态指示
尽管是自动化工具,但基本的用户交互还是必要的。我设计了两个实体按钮:
- 脚本启动/停止按钮:用于手动触发或中止自动化脚本的执行。这个按钮可以连接到 RP2040 的任一 GPIO,并配置为上拉输入,通过检测下降沿来触发动作。
- 模式切换按钮(可选):用于在“脚本模式”、“编程模式”(通过 USB 传输新脚本)、“配置模式”之间切换。长按或组合按键可以实现更多功能。
状态指示则通过一个 RGB LED(WS2812B 这类可寻址 LED 即可)来实现。它只需要一根数据线连接到 RP2040。我们可以用不同的颜色和闪烁模式来表示不同的状态:
- 蓝色常亮:设备已连接,处于就绪状态。
- 绿色呼吸:脚本正在运行中。
- 红色闪烁:脚本执行出错或设备故障。
- 黄色闪烁:处于脚本接收/编程模式。 这种视觉反馈对于调试和了解设备状态非常直观。
3.3 存储扩展与“隐身”考虑
板载的 2MB Flash 对于大多数脚本来说已经足够。但为了极致灵活性,我额外预留了一个MicroSD 卡槽(通过 SPI 接口连接)。这样,用户可以将庞大的脚本库、配置文件甚至需要注入的文本文件存放在 SD 卡中,DuckyClaw 在启动时读取并执行。这大大扩展了应用场景,比如进行大规模的数据注入测试。
“隐身”或说“兼容性”是硬件自动化工具的一个有趣话题。为了让 DuckyClaw 看起来更“人畜无害”,PCB 形状可以设计成类似一个 U 盘、一个无线接收器或者一个普通的 USB 小工具。避免使用显眼的 LED(RGB LED 在非工作时可以完全熄灭)和按钮。甚至可以设计一个外壳,让它看起来像一个普通的 USB 闪存盘。这些细节能减少在特定使用环境下的“注目度”。
4. 固件开发核心:USB HID模拟与脚本引擎
硬件是躯体,固件才是灵魂。DuckyClaw 的固件主要完成两件大事:一是完美地扮演一个 USB 键盘和鼠标;二是解析并执行用户编写的自动化脚本。
4.1 基于 TinyUSB 的复合设备实现
我选择使用Raspberry Pi Pico 的 C/C++ SDK结合TinyUSB库来开发核心固件。TinyUSB 是一个优秀的开源嵌入式 USB 协议栈,对 RP2040 支持非常好。
首先,我们需要在tusb_config.h配置文件中启用 HID 设备(键盘和鼠标)以及 MSC(大容量存储设备,用于模拟 U 盘模式更新脚本)功能。
// tusb_config.h 中的关键配置 #define CFG_TUD_HID 2 // 启用 HID,并设置端点数量 #define CFG_TUD_MSC 1 // 启用 MSC然后,在代码中定义并实现键盘和鼠标的报告描述符。报告描述符是一种告诉电脑“我这个设备有哪些按键、几个轴”的数据结构。这里以键盘为例,其报告描述符定义了按键数组、修饰键(Ctrl, Shift等)等。
// 示例:一个简单的键盘HID报告描述符(部分) const uint8_t hid_report_descriptor[] = { HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ), HID_USAGE ( HID_USAGE_DESKTOP_KEYBOARD ), HID_COLLECTION ( HID_COLLECTION_APPLICATION ), // 修饰键 (Ctrl, Shift, Alt, GUI) HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ), HID_USAGE_MIN ( 224 ), // 左Ctrl HID_USAGE_MAX ( 231 ), // 右GUI HID_LOGICAL_MIN ( 0 ), HID_LOGICAL_MAX ( 1 ), HID_REPORT_SIZE ( 1 ), HID_REPORT_COUNT( 8 ), HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ), // 保留位 HID_REPORT_COUNT( 1 ), HID_REPORT_SIZE ( 8 ), HID_INPUT ( HID_CONSTANT ), // 按键数组(6键无冲) HID_USAGE_PAGE ( HID_USAGE_PAGE_KEYBOARD ), HID_USAGE_MIN ( 0 ), HID_USAGE_MAX ( 255 ), HID_LOGICAL_MIN ( 0 ), HID_LOGICAL_MAX ( 255 ), HID_REPORT_SIZE ( 8 ), HID_REPORT_COUNT( 6 ), HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ), HID_COLLECTION_END };实现tud_hid_report_complete_cb等回调函数,用于处理主机对报告的请求。发送按键或鼠标移动时,我们调用tud_hid_keyboard_report或tud_hid_mouse_report函数,将构建好的报告发送给电脑。
4.2 嵌入式脚本引擎的设计
为了让用户能灵活编写自动化流程,我需要一个在 RP2040 上运行的脚本引擎。直接让 RP2040 运行 Python 解释器(MicroPython)虽然可行,但为了极致性能和精简,我决定自己实现一个轻量级的、领域特定的脚本语言解释器。
这个脚本语言(我们暂且叫它 DuckyScript,向 Rubber Ducky 致敬)语法非常简单,一条指令占一行,类似于汇编或简单的配置文件。
// 示例 DuckyScript DELAY 1000 // 延迟1000毫秒 STRING Hello World // 输入字符串“Hello World” ENTER // 按下回车键 DELAY 500 GUI r // 按下Win+R DELAY 300 STRING cmd ENTER固件中需要实现一个解析器(Parser)来逐行读取脚本(从内部Flash或SD卡)。解析器识别指令(如STRING,DELAY,GUI,CONTROL等)和参数。然后,一个执行器(Executor)根据解析出的指令,调用对应的底层函数。例如,遇到STRING指令,就逐个字符调用tud_hid_keyboard_report模拟按键;遇到DELAY,则使用sleep_ms()函数进行忙等待或利用定时器实现非阻塞延迟。
为了支持更复杂的逻辑(如条件判断、循环),脚本引擎需要支持变量和流程控制指令。这会使解析器和执行器复杂很多,但能极大提升脚本能力。一种折中方案是支持LABEL和GOTO指令来实现简单跳转。
// 简化版的脚本执行核心逻辑 void execute_script_line(const char* line) { char cmd[20]; char arg[100]; sscanf(line, "%s %[^\n]", cmd, arg); // 简单分割指令和参数 if (strcmp(cmd, "DELAY") == 0) { uint32_t ms = atoi(arg); sleep_ms(ms); } else if (strcmp(cmd, "STRING") == 0) { for (int i = 0; arg[i] != '\0'; i++) { send_keypress(arg[i]); // 模拟输入单个字符 } } else if (strcmp(cmd, "ENTER") == 0) { send_keycode(HID_KEY_ENTER); } // ... 处理更多指令 }4.3 双核分工与实时性保障
RP2040 是双核的,我们可以利用这个优势。一个常见的架构设计是:
- Core 0:运行TinyUSB 协议栈和主循环。负责处理 USB 通信、解析来自主机的请求、执行脚本引擎的主逻辑。USB 事件需要及时响应,放在主核。
- Core 1:处理时间关键型任务。例如,负责精确的延时控制、监控硬件按钮的状态(防抖、检测长按等)、驱动 RGB LED 显示复杂动画。通过核间 FIFO 与 Core 0 通信。
这种分工确保了即使脚本引擎在执行一个耗时操作(比如解析大脚本),USB 的响应和精确的定时也不会被阻塞。例如,我们可以让 Core 1 上的一个硬件定时器(PIO 或 PWM)来产生精确的毫秒级中断,用于实现非阻塞的DELAY指令,这样在等待期间 Core 0 仍然可以处理其他事件。
5. 软件生态与使用流程:从编写到执行
一个孤立的硬件工具价值有限,围绕 DuckyClaw 构建一个简单的软件生态能极大提升用户体验。核心是脚本编写工具和脚本上传工具。
5.1 脚本编写与调试
用户可以使用任何文本编辑器编写 DuckyScript。但一个专用的、带有语法高亮、代码片段和实时预览功能的编辑器会友好得多。我计划开发一个非常轻量级的跨平台桌面应用(使用 Electron 或 Tauri 框架),或者甚至是一个网页应用。
这个编辑器可以提供:
- 语法高亮:区分指令、参数、注释。
- 命令补全:输入
STR自动补全为STRING。 - 延时计算器:帮助计算操作序列的总时间。
- 模拟运行(高级功能):在编辑器内虚拟一个环境,预览脚本执行后的效果(如模拟文本输出),这能帮助调试复杂的脚本逻辑,而无需每次都刷写到硬件上测试。
5.2 脚本部署的多种方式
如何把写好的脚本放到 DuckyClaw 里?我设计了三种模式:
MSC 模式(即 U 盘模式):这是最用户友好的方式。DuckyClaw 启动时如果检测到特定按钮被按下(或通过脚本配置),会将自己枚举为一个USB 大容量存储设备(U盘)。电脑上会出现一个盘符,里面有一个
scripts文件夹和一个配置文件config.ini。用户只需将.txt或.ducky脚本文件拖入scripts文件夹,然后安全弹出硬件。DuckyClaw 重启后,会自动从 Flash 或 SD 卡加载最新的脚本。这种方式无需安装任何驱动或软件。串口命令模式:DuckyClaw 同时也是一个 USB CDC(串行通信设备),在电脑上显示为一个串口。我们可以通过 PuTTY、
screen或自制的上位机软件,连接到这个串口,使用命令行来管理脚本。例如:> list 1: hello_world.txt 2: auto_login.txt > run 2 执行脚本 auto_login.txt... > write new_script.txt (进入文本接收模式,粘贴或输入脚本内容) > save 脚本已保存。这种方式对开发者或高级用户更灵活。
Web 配置界面(未来构想):如果 DuckyClaw 集成了 Wi-Fi 功能(比如使用 RP2040-W 型号或外接 ESP01s),可以启动一个 AP 热点,用户手机或电脑连接后,通过浏览器访问一个内网页面上传和管理脚本。这更适合需要频繁更新脚本或远程管理的场景。
5.3 实战应用场景与脚本示例
DuckyClaw 的用途非常广泛,这里举几个我实际测试过的例子:
场景一:自动化软件安装与配置新电脑到手,需要安装一堆开发环境(VS Code, Git, Python, Node.js...)。可以编写一个脚本,自动打开浏览器下载安装包、运行安装程序、点击“下一步”、同意协议、选择安装路径。虽然不同电脑分辨率可能不同,但可以通过寻找固定颜色的像素点(如果未来集成屏幕识别,需要摄像头)或依赖键盘导航(Tab, Space, Enter)来提高通用性。目前更可靠的方式是针对特定标准化安装程序编写脚本。
场景二:硬件测试与产线模拟在工厂测试工位上,需要模拟人工对一台设备进行上千次的按键操作测试。将 DuckyClaw 接入设备,运行一个包含循环的脚本,可以 7x24 小时不间断地执行测试序列,并记录通过串口输出的日志。这比人工测试更精确、更可靠,成本也远低于工业机器人。
场景三:安全领域的合法授权测试(非常重要!)必须强调,任何自动化工具都可能被滥用。DuckyClaw 仅用于在获得明确书面授权的环境下进行安全测试、渗透测试和教育研究。例如,在授权进行的内部网络安全演练中,测试人员可以使用 DuckyClaw 模拟“丢失的U盘”攻击(Dropbox attack),测试员工是否会捡起并插入公司电脑,以及公司的终端安全软件能否检测和阻止其自动运行脚本。相关的脚本可能是模拟打开终端、下载并运行一个无害的测试程序。
一个简单的自动登录脚本示例:
// auto_login.txt // 假设光标已在用户名输入框 DELAY 1000 STRING my_username TAB // 切换到密码框 STRING my_password ENTER DELAY 2000 // 如果登录后需要点击确认按钮 ALT y // 假设按 Alt+Y 确认6. 进阶优化与踩坑实录
在开发 DuckyClaw 的过程中,遇到了不少坑,也积累了一些优化经验。
6.1 USB 枚举稳定性与兼容性
最初版本在部分 Windows 电脑和某些品牌的 USB 集线器上枚举失败,表现为设备无法识别或识别为未知设备。排查后发现几个问题:
- 上电时序:RP2040 的 USB 控制器需要在系统时钟稳定后才能初始化。在
main()函数一开始就调用tusb_init()可能太早。解决方案是确保在main()中完成所有硬件初始化(特别是时钟和 GPIO)后,再初始化 TinyUSB。 - 描述符配置:USB 描述符(设备描述符、配置描述符、字符串描述符)必须完全符合规范。一个常见的错误是端点包大小(
wMaxPacketSize)设置不当。对于全速 USB(12 Mbps),中断传输端点的最大包大小通常是 64 字节。在tusb_config.h中正确设置CFG_TUD_HID_EP_BUFSIZE至关重要。 - 电源滤波:USB 端口提供的 5V 电源可能带有噪声。在 PCB 上,USB 的 VBUS 引脚到 LDO 输入之间,增加一个 π 型滤波电路(如 10uF 钽电容 + 磁珠/小电阻 + 0.1uF 陶瓷电容),可以显著提高在劣质 USB 电源下的稳定性。
6.2 脚本执行的精确时序控制
最初的DELAY指令使用sleep_ms()实现,这是忙等待,会阻塞整个核心。如果脚本中有长延迟,期间设备无法响应任何其他事件(如按钮中断)。优化方案是使用状态机和非阻塞延时。
typedef struct { const char** script_lines; uint32_t total_lines; uint32_t current_line; uint32_t delay_until; // 非阻塞延时到期的时间戳 bool is_waiting; } script_state_t; script_state_t g_script; void process_script_state_machine() { if (g_script.is_waiting) { if (time_us_32() / 1000 >= g_script.delay_until) { g_script.is_waiting = false; } else { return; // 还在等待,直接返回,不阻塞 } } if (g_script.current_line >= g_script.total_lines) { // 脚本执行完毕 set_led_color(SCRIPT_FINISHED_COLOR); return; } const char* line = g_script.script_lines[g_script.current_line]; // 解析指令 if (is_delay_command(line)) { uint32_t ms = extract_delay_ms(line); g_script.delay_until = time_us_32() / 1000 + ms; g_script.is_waiting = true; g_script.current_line++; } else if (is_keyboard_command(line)) { // 执行键盘命令(非阻塞,立即完成) execute_keyboard_command(line); g_script.current_line++; } // ... 其他指令 } // 在主循环中调用 while (true) { tud_task(); // TinyUSB 后台任务,必须频繁调用 process_script_state_machine(); // ... 处理按钮、更新LED等 }这样,主循环始终在运行,USB 事件能得到及时处理,脚本的延时也不会阻塞系统。
6.3 抗干扰与错误恢复
硬件设备可能会遇到意外拔插、静电干扰等。需要增加鲁棒性设计:
- 看门狗(Watchdog):启用 RP2040 的硬件看门狗。如果主程序因为某种原因卡死,看门狗超时后会强制重启设备。
- 脚本语法校验:在加载或接收脚本时,进行基础的语法检查,避免因错误脚本导致执行逻辑混乱。
- 安全模式:如果设备连续启动失败(例如由于损坏的脚本),可以自动进入一个安全模式。在这个模式下,只提供最基本的串口功能,允许用户擦除错误脚本或更新固件。可以通过检测启动时某个按钮是否被按下来触发。
6.4 功耗优化
虽然 USB 总线供电通常不是问题,但对于电池供电或希望更“绿色”的场景,可以做一些优化:
- 动态频率调整:当没有脚本执行且一段时间无操作时,可以将 RP2040 的系统时钟从 133MHz 降低到 12MHz 甚至更低,并让核心进入休眠模式(
__wfi()指令)。 - 外设断电:在不使用 SD 卡时,可以将其 SPI 总线时钟拉低,并置片选为高,减少静态功耗。
- LED 管理:在脚本执行完毕后,将 RGB LED 完全关闭。
开发 DuckyClaw 的过程,是一个不断在硬件限制、软件复杂度和用户体验之间寻找平衡点的过程。从最初的概念验证,到解决 USB 兼容性问题,再到实现一个稳定可靠的脚本引擎,每一步都充满了挑战和学习的乐趣。这个项目最吸引我的地方在于,它将一个纯粹的软件概念,通过具体的硬件呈现出来,并且因为硬件的特性(独立性、隐蔽性、实时性)而开辟了新的应用可能性。它不仅仅是一个复制品,更是在新的维度上对自动化工具的一次探索。