十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Flipper Zero JS BadUSB 模块实战:用 JavaScript 脚本驱动 USB HID 键盘,从 setup 配置到 Alt+Numpad 输入

Flipper Zero JS BadUSB 模块实战:用 JavaScript 脚本驱动 USB HID 键盘,从 setup 配置到 Alt+Numpad 输入 Flipper Zero JS BadUSB 模块实战用 JavaScript 脚本驱动 USB HID 键盘从 setup 配置到 AltNumpad 输入【免费下载链接】flipperzero-firmwareFlipper Zero firmware source code项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmware本文基于 Flipper Zero 固件仓库的 BadUSB 模块官方文档 与对应插件实现 js_badusb.c完整讲解 JS 端badusb模块的每个方法setup()设备身份配置、press()/hold()/release()按键控制、print()/altPrint()字符串输出以及完整按键名表并结合固件源码HID 报告描述符、ASCII 映射表、USB 配置切换说明每个 API 在设备端的真实行为帮助你在 JS 应用中以代码方式把 Flipper Zero 变成一个可编程的 USB HID 键盘。1. 模块定位require(badusb) 背后是什么在 JS 应用中引入模块只需一行let badusb require(badusb);从源码结构看badusb是 JS 引擎js_app注册的一个插件模块其实现位于 js_badusb.c通过JsModuleDescriptor以名称badusb导出setup、quit、isConnected、press、hold、release、print、println、altPrint、altPrintln十个方法与 文档 的 API 列表一一对应。模块内部维护一个实例结构JsBadusbInstjs_badusb.chid_cfgFuriHalUsbHidConfig*setup()时解析出的设备身份信息layout[128]ASCII 字符到 HID 键码的映射表默认复制自 HAL 层的hid_asciimapusb_if_prevsetup()之前正在使用的 USB 接口指针quit()时用于恢复key_hold_cnt当前被hold()按住的非修饰键计数用于 5 键上限检查。所有按键事件最终都调用furi_hal_hid_kb_press/release系列函数由 HID 驱动furi_hal_usb_hid.c填充键盘 Boot 报告并通过中断端点EP0x81发送到主机。2. setup()启动 USB HID 并配置设备身份setup()必须在其他方法之前调用。它接受一个可选的配置对象参数类型必填说明vidnumber与pid成对必填USB Vendor IDpidnumber与vid成对必填USB Product IDmfrNamestring否厂商名最多 32 个 ASCII 字符prodNamestring否产品名最多 32 个 ASCII 字符layoutPathstring否键盘布局文件路径128 字节二进制映射表// Start USB HID with default parameters badusb.setup(); // Start USB HID with custom vid:pid AAAA:BBBB, manufacturer and product strings not defined badusb.setup({ vid: 0xAAAA, pid: 0xBBBB }); // Start USB HID with custom vid:pid AAAA:BBBB, manufacturer string Flipper Devices, product string Flipper Zero badusb.setup({ vid: 0xAAAA, pid: 0xBBBB, mfrName: Flipper Devices, prodName: Flipper Zero });源码中的参数校验规则js_badusb_setup 与 setup_parse_params值得注意零参数直接使用默认设备身份启动传参时vid与pid必须同时为数字缺一即抛出参数错误BadArgsError。字符串字段mfrName/prodName通过strlcpy拷入char[32]的manuf/product缓冲结构体定义见 furi_hal_usb_hid.h超长会被截断到 31 个字符传入空字符串会直接判定为参数错误。layoutPath模块用 storage 服务以只读方式打开文件并必须恰好读到 128 字节覆盖 ASCII 0x00–0x7F 全部键码否则视为加载失败。若不指定则复制默认的hid_asciimap表furi_hal_usb_hid.h。重复启动usb_if_prev非空时再次调用setup()会抛出HID is already started。USB 被占用furi_hal_usb_set_config(usb_hid, ...)失败时抛出USB is locked, close companion app first——即宿主机的 Companion 应用占用着 USB 连接时无法切换为 HID。设备端的效果在 hid_init 中可见未传vid/pid时使用默认身份0x046D:0xC529HID_VID_DEFAULT/HID_PID_DEFAULT见 furi_hal_usb_hid.c传入则写入设备描述符厂商/产品字符串非空时动态构造 USB 字符串描述符。也就是说主机在设备管理器中看到的 vid:pid 和名称完全由setup()参数决定。setup()同时会记录当前 USB 接口furi_hal_usb_get_config()保存到usb_if_prev这就是后文quit()能还原现场的基础。3. isConnected()查询主机连接状态if (badusb.isConnected()) { // Do something } else { // Show an error }该方法是 furi_hal_hid_is_connected() 的直通封装返回设备与主机之间 USB 连接的实际状态USB 在hid_on_wakeup主机枚举/唤醒时置为已连接hid_on_suspend时断开。因此正确用法是setup()之后、执行按键前用它判断对端是否已接入。未调用setup()时调用会抛出HID is not started。4. press()单键与组合键press()按下并立即释放按键。参数可以是键名字符串、修饰键名与主键的组合或直接传入 HID 键码数值badusb.press(a); // Press a key badusb.press(A); // SHIFT a badusb.press(CTRL, a); // CTRL a badusb.press(CTRL, SHIFT, ESC); // CTRL SHIFT ESC combo badusb.press(98); // Press key with HID code (dec) 98 (Numpad 0 / Insert) badusb.press(0x47); // Press key with HID code (hex) 0x47 (Scroll lock)参数解析由 parse_keycode 完成规则如下单字符字符串直接查 ASCII 映射表layout[128]多字符字符串在内置key_codes表中按精确长度 名称匹配js_badusb.c例如ENTER映射HID_KEYBOARD_RETURN、MENU映射上下文菜单键。数字参数直接作为 HID 键码且必须 ≤ 0xFF与文档示例中 98、0x47 一致。修饰键可多个、主键只能有一个解析时按位或累加若低 8 位主键域已被占用再次给出主键会报参数错误。所以press(CTRL, SHIFT, ESC)合法而press(a, b)非法。未知键名返回HID_KEYBOARD_NONE同样抛出参数错误。执行层面press()依次调用furi_hal_hid_kb_press(keycode)与furi_hal_hid_kb_release(keycode)js_badusb.c。在驱动里furi_hal_usb_hid.c键码低 8 位写入键盘报告的按键数组空闲槽位、高 8 位写入修饰键字节每次按键/释放都会触发一次 Report 发送——这就是按下即松开能可靠送达主机的原因。5. hold() / release()长按与键位上限badusb.hold(a); // Press and hold a key badusb.hold(CTRL, v); // Press and hold CTRL v combobadusb.release(); // Release all keys badusb.release(a); // Release a key参数规则与press()相同release()不带参数时释放全部按键。5 键上限的来源文档说最多同时按住 5 个键不含修饰键对应源码中的 HID_KB_MAX_KEYS 6——键盘 Boot 报告有 6 个按键槽位hold()每次按住一个非修饰键就累加key_hold_cnt超过HID_KB_MAX_KEYS - 1即 5时抛出Too many keys are hold并调用furi_hal_hid_kb_release_all()自动清空所有按住状态js_badusb.c避免按键卡死在设备上。注意该计数按主键次数统计同一物理键连续hold会累计计数因此脚本里成对使用hold/release或在不必要时用release()归零。6. print() / println()字符串输出与延时badusb.print(Hello, world!); // print Hello, world! badusb.print(Hello, world!, 100); // Add 100ms delay between key pressesbadusb.println(Hello, world!); // print Hello, world! and press ENTERprintln与print唯一区别是结尾追加一次ENTER按压。两个可选参数字符串 键间延时毫秒的解析约束badusb_print延时必须是数字且不超过 60000 ms否则按参数错误处理每个字符经layout[128]映射为键码后执行按下-释放ASCII ≥ 128 的字符映射为HID_KEYBOARD_NONE见 ASCII_TO_KEY即非 US 键盘布局的字符无法用默认布局直接输入——这是自定义layoutPath的主要用武之地延时通过js_delay_with_flags实现是可被事件循环中断的等待若等待期间收到退出标志函数会提前返回便于用户通过 Flipper 侧按键中断长脚本。典型用法是复制-粘贴print出内容后配合修饰键组合操作剪贴板官方示例脚本中正是如此见第 10 节。7. altPrint() / altPrintln()AltNumpad 输入仅限 Windowsbadusb.altPrint(Hello, world!); // print Hello, world! badusb.altPrint(Hello, world!, 100); // Add 100ms delay between key pressesbadusb.altPrintln(Hello, world!); // print Hello, world! and press ENTER该方法模拟 Windows 的Alt 小键盘 ASCII 码输入方式文档明确标注仅在 Windows 主机上有效。实现细节js_badusb.c自动确保 NUMLOCK 开启ducky_numlock_on()先查询主机侧 LED 状态furi_hal_hid_get_led_state()的HID_KB_LED_NUM位若小键盘灯未亮就先补按一次NUMLOCK——没有这一步小键盘会输出方向键而非数字对每个字符取其uint8_t十进制 ASCII 值如H→72按住ALT后依次按NUM0–NUM9对应的数字键最后松开ALT。这也解释了为什么按键名表必须包含NUM0–NUM9AltNumpad 路径完全依赖小键盘键位。由于该方法逐字符、逐数字位发送按键序列配合altPrint(text, 100)的键间延时可以提高在慢主机上的稳定性。8. quit()释放 USB 接口badusb.quit(); // usbdisk.start(...)quit()释放 USB 接口文档标注其为可选项但能解锁 USB 以便切换到其他 USB 身份如 U 盘usbdisk。源码 js_badusb_quit 做了三件事furi_hal_hid_kb_release_all()释放所有按键、furi_hal_usb_set_config(usb_if_prev, NULL)把 USB 接口恢复到setup()之前的状态、释放hid_cfg内存。未setup()时调用会抛出HID is not started。模块销毁时js_badusb_destroy也会走同一清理路径因此即使脚本异常退出设备端也不会残留按键被按住的状态。9. 按键名完整列表以下表格继承自 官方文档并与 js_badusb.c 中的 key_codes 表 一致。修饰键名称说明CTRL左 CtrlSHIFT左 ShiftALT左 AltGUI左 Win/Cmd源码映射为KEY_MOD_LEFT_GUI功能键与特殊键名称说明DOWN/LEFT/RIGHT/UP方向键ENTER回车DELETE向前删除BACKSPACE退格END行尾/文档末尾HOME行首/文档开头ESC退出INSERT插入PAGEUP/PAGEDOWN翻页CAPSLOCK/NUMLOCK/SCROLLLOCK锁定键PRINTSCREEN截屏键PAUSEPause/Break 键SPACE空格TABTabMENU上下文菜单键ApplicationFxF1–F24 功能键NUMxNUM0–NUM9 小键盘数字键另有两类隐式输入方式单字符字符串a–z、A–Z、标点等经 ASCII 映射表转换与数字 HID 键码。10. 完整示例仓库内置的 badusb_demo.js仓库自带一个可直接运行的演示脚本 badusb_demo.js完整覆盖了上述 APIlet badusb require(badusb); let flipper require(flipper); // ... gui / dialog / eventLoop 省略 badusb.setup({ vid: 0xAAAA, pid: 0xBBBB, mfrName: Flipper, prodName: Zero, layoutPath: /ext/badusb/assets/layouts/en-US.kl }); // 用户点击对话框后 if (badusb.isConnected()) { badusb.println(Hello, world!); badusb.press(CTRL, a); badusb.press(CTRL, c); badusb.press(DOWN); delay(1000); badusb.press(CTRL, v); delay(1000); badusb.press(CTRL, v); badusb.println(1234, 200); badusb.println(Flipper Model: flipper.getModel()); badusb.altPrintln(This was printed with AltNumpad method!); // 仅 Windows } badusb.quit(); // Optional, but allows to unlock usb interface to switch profile示例要点自定义布局文件layoutPath: /ext/badusb/assets/layouts/en-US.kl展示了如何用 SD 卡上的 128 字节映射表替换默认 ASCII 表isConnected()判断后再执行按键结尾quit()解锁 USB 接口以便切换其他 USB 身份。该脚本依赖的flipper模块获取型号、电量等文档见 js_flipper.mdJS 应用入门见 js_your_first_js_app.md。11. 源码可确认的约束与排错提示结合 js_badusb.c 的错误抛出点实际开发中最可能遇到的报错及其含义报错MJS 错误类型触发条件处理建议HID is already startedsetup()被重复调用先quit()再重新setup()或复用已有 HID 会话USB is locked, close companion app first主机 Companion 应用占用 USB接口切换失败关闭宿主机侧占用 USB 的应用后重试HID is not started未setup()就调用isConnected/press/hold/release/print...按文档要求先setup()Too many keys are holdhold()同时按住的非修饰键超过 5 个减少并发长按键模块会自动release_all兜底Bad args空消息vid/pid不成对、layoutPath文件不足 128 字节、延时 60000 ms、未知键名、一次press含两个主键等对照第 2、4、6 节的参数规则逐项检查总结badusb模块把 Flipper Zero 固件的 USB HID 能力身份伪装、键码发送、主机 LED 状态读取完整暴露给了 JS 层——setup()决定主机看到什么设备press/hold/release决定发什么按键print/altPrint提供两种批量输入策略quit()负责资源归还。所有行为的底层依据都可回溯到 furi_hal_usb_hid.c设备描述符与报告构建和 furi_hal_usb_hid.hHID_KB_MAX_KEYS、修饰键枚举、hid_asciimap默认布局表便于在脚本表现与预期不符时逐层定位。【免费下载链接】flipperzero-firmwareFlipper Zero firmware source code项目地址: https://gitcode.com/GitHub_Trending/fl/flipperzero-firmware创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表