miyakejima/hs75t-custom-firmware

GitHub: miyakejima/hs75t-custom-firmware

为 HelloGanss HS75T 键盘逆向并注入随机延迟 opcode 的自定义固件补丁,附 WPF 桌面宏编程工具。

Stars: 0 | Forks: 0

# hs75t 自定义 firmware **HelloGanss HS75T** 无线机械键盘的自定义固件补丁及配套桌面工具——源于一个简单的问题:*硬件宏可以有随机延迟吗?* ## 背景故事 HS75T 将宏直接存储在键盘上,并通过硬件回放——无需保持任何软件处于打开状态。问题是厂商固件仅支持**固定延迟**。固定延迟使得宏回放每次的节奏都完全相同,这很容易被检测到。 目标是为板载宏解释器添加一个**随机延迟 opcode**,以便硬件回放可以在编程后自然地改变其时序,而无需 PC 的任何介入。 ### 逆向工程固件 该键盘运行在 **HFD2201KBA** MCU 上——这是一颗频率为 48 MHz 的 ARM Cortex-M0,拥有 64 KB 的 flash 和 8 KB 的 SRAM(与 SN32F248B 芯片相同)。厂商以自解压可执行文件的形式提供固件,其中封装了一个原始的 `.bin` 文件以及一个 `SnxHidLib.DLL` 烧录工具。 使用 Ghidra,定位并完整映射了宏解释器循环: - 解释器从板载 flash 偏移量 `0x0414` 遍历到 `0x1FFF` 的字节缓冲区 - 每个宏槽位均以 null 终止符 (`0x00`) 结尾——payload 内部不允许出现 null 字节 - opcode:`01 02 key` = Key Down(按下),`01 03 key` = Key Up(抬起),`01 04 lo hi` = 固定延迟(以 base-255 非零方式编码) - 位于 `0x1C8E` 的固定延迟处理分支即为注入点 ### Code Cave 注入 该固件在最后一个实际函数之后有一段 136 字节的零填充空间——这是一个位于 `0xD864` 的干净 code cave。补丁操作: 1. 将 `0x1C8E` 处的 opcode 分支重定向到该 code cave 2. code cave 会检查新的 `0x08` opcode 3. 若匹配:读取 `min_lo min_hi max_lo max_hi`,解码 base-255 数值,使用以时间戳和存储偏移量为种子的 XOR-shift 算法生成伪随机数,并在 `[min, max]` 毫秒内等待随机时长 4. **跳过阶段保护**机制可防止恢复重放机制将解释器困在无限延迟循环中(这是在 v1 版本中发现的关键 bug) 5. 每次修改后都会重新计算二进制文件末尾的校验和尾迹 6. 若不匹配:回落至原始的 opcode 处理程序 最终结果是一个新的 opcode:`01 08 min_lo min_hi max_lo max_hi`——**随机延迟**——完全可与现有的宏缓冲区格式互操作。 ## Hs75tTool — 桌面配套工具 这是一款通过 USB HID 将宏编程录入键盘的 WPF / .NET 9 桌面应用程序。 ![工具概览](https://static.pigsec.cn/wp-content/uploads/repos/cas/58/582c225f6deb108ac753f5ae010691140a09009878115d61387478431ce20b2c.png) ### 功能 - **连接 / 读取 / 恢复** —— 通过 HID 从键盘拉取实时宏缓冲区 - **保存** —— 将完整的缓冲区写回;保存后,键盘将独立播放宏 - **16 个宏槽位**,带有单槽位时间轴视图 - **录制** —— 全局键盘和鼠标钩子可捕获系统范围内的按键操作 - 真实延迟 —— 可选地根据按键之间的实际时间间隔插入固定延迟 - 追加模式 —— 在不清除的情况下添加到现有行 - 鼠标点击 / 滚轮录制 - **操作编辑器** —— 添加、重新排序、复制、删除事件;重新绑定按键、切换按下/抬起、在之后插入点按 - **将所有固定延迟转换为随机延迟** —— 将槽位中的每个固定延迟替换为由可配置的 ±offset 限定边界的随机延迟(需要已打补丁的固件) - **播放模式**:单次、重复 N 次、停止、按住 - **导入 / 导出** —— 将槽位保存/加载为 `.json` 文件 - **紧急按键释放** —— 为所有 HID 键码发送 KeyUp,以解开卡住的按键 ## 仓库结构 ``` hs75t-custom-firmware/ ├── src/ # C# source — Hs75tTool WPF app │ ├── Hs75tTool.sln │ ├── Hs75tTool.Core/ # HID client, codec, macro model │ ├── Hs75tTool.Desktop/ # WPF frontend (MainWindow, Services) │ └── Hs75tTool.Tests/ # Unit tests (MSTest) ├── firmware/ │ ├── HFD2201KBA_SN32F248B_VENDOR_STOCK.bin # Original unmodified firmware │ └── HFD2201KBA_SN32F248B_patched_v5.bin # Current patched firmware (v5) ├── tools/ # Flashing and bootloader utilities │ ├── reboot_to_bootloader.py # Software bootloader entry via HID magic bytes │ ├── CHECK_BOOTLOADER.bat # Verify keyboard is in bootloader mode │ ├── ENTER_BOOTLOADER.bat # Force keyboard into bootloader │ ├── SonixFlasherC/ # Open-source Sonix flash tool (third-party) │ └── ... ├── patching/ # Python analysis and patch verification scripts │ ├── pack_v2.py # Repack patched binary into vendor SFX format │ ├── verify_sfx_contents.py # Verify SFX contents match expected binaries │ └── ... ├── docs/ │ ├── 00_Hardware_Identification.md │ ├── 02_Official_Flasher_Protocol_Analysis.md │ ├── 06_Original_Walkthrough2.md # Full RE walkthrough and HID protocol reference │ ├── 07_Patched_Disassembly.md # Capstone disassembly of the injected cave │ └── firmware_full_decompiled.c # Ghidra decompiled C output (reference) └── CHANGELOG.md ``` ## 硬件信息 | 字段 | 值 | |---|---| | 键盘 | HelloGanss HS75T | | MCU | HFD2201KBA (ARM Cortex-M0, 48 MHz) | | Flash | 64 KB | | SRAM | 8 KB | | USB VID | `0x05AC` | | USB PID | `0x0256` | | 固件版本 | V1.15(干电池变体) | | Hook 地址 | `0x1C8E` | | Code cave 地址 | `0xD864`(136 字节) | ## 刷入已打补丁的固件 ### 要求 - Python 3.x - [SonixFlasherC](tools/SonixFlasherC/)(已包含) - libusb(SonixFlasherC 必需) ### 步骤 1. **进入 bootloader 模式** python tools/reboot_to_bootloader.py 或者运行 `tools/ENTER_BOOTLOADER.bat`。键盘将断开连接,并作为 Sonix bootloader 设备重新枚举。 2. **刷入已打补丁的固件** SonixFlasherC -d SN248B -r 0 -t 0 -w firmware/HFD2201KBA_SN32F248B_patched_v5.bin 3. **验证**键盘是否正常重新枚举并且工具能够连接。 要恢复官方固件,请使用相同的步骤刷入 `firmware/HFD2201KBA_SN32F248B_VENDOR_STOCK.bin`。 ## 构建工具 需要 .NET 9 SDK 和 Windows 系统。 ``` cd src dotnet build Hs75tTool.sln dotnet run --project Hs75tTool.Desktop ``` ## 宏协议参考 宏存储在偏移量 `0x0414` 到 `0x1FFF` 的板载 flash 中。每个槽位均以 null 终止。**宏 payload 中绝不能出现 `0x00`**——它会被视为槽位终止符。 ### Opcode 表 | 字节 | 含义 | |---|---| | `01 02 key` | Key Down | | `01 03 key` | Key Up | | `01 04 lo hi` | 固定延迟(以 base-255 非零方式编码) | | `01 08 min_lo min_hi max_lo max_hi` | **随机延迟** —— 自定义扩展,仅限已打补丁的固件 | ### Base-255 非零编码 延迟值经过编码以避免出现 null 字节: ``` lo = (ms % 255) + 1 hi = (ms // 255) + 1 decoded = (lo - 1) + (hi - 1) * 255 ``` ## 致谢 - **[SonixFlasherC](https://github.com/SonixQMK/SonixFlasherC)** —— 开源 Sonix HID 烧录工具(在原始许可证下包含)
标签:云资产清单, 固件修改, 外设驱动, 嵌入式开发, 桌面工具, 硬件宏, 逆向工具, 逆向工程