Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hs75t-custom-firmware

Custom firmware patch and companion desktop tool for the HelloGanss HS75T wireless mechanical keyboard — born from a single question: can hardware macros have random delays?


The Story

The HS75T stores macros directly on the keyboard and plays them back in hardware — no software needs to stay open. The problem is the vendor firmware only supports fixed delays. Fixed delays make macro playback rhythmically identical every time, which is trivially detectable.

The goal was to add a random delay opcode to the onboard macro interpreter so that hardware playback could vary its timing naturally, without any PC involvement after programming.

Reversing the Firmware

The keyboard runs a HFD2201KBA MCU — an ARM Cortex-M0 at 48 MHz with 64 KB of flash and 8 KB of SRAM (same die as the SN32F248B). The vendor ships firmware as a self-extracting executable that wraps a raw .bin paired with a SnxHidLib.DLL flasher.

Using Ghidra, the macro interpreter loop was located and fully mapped:

  • The interpreter walks a byte buffer from onboard flash offset 0x0414 to 0x1FFF
  • Each macro slot is null-terminated (0x00) — no null bytes allowed inside a payload
  • Opcodes: 01 02 key = Key Down, 01 03 key = Key Up, 01 04 lo hi = Fixed Delay (base-255 non-zero encoded)
  • The fixed delay handler branch at 0x1C8E was the injection point

Code Cave Injection

The firmware had 136 bytes of zero-filled space after the last real function — a clean code cave at 0xD864. The patch:

  1. Redirects the opcode dispatch at 0x1C8E with a branch to the cave
  2. The cave checks for the new 0x08 opcode
  3. On match: reads min_lo min_hi max_lo max_hi, decodes base-255 values, generates a pseudo-random number using an XOR-shift seeded from the timestamp and storage offset, and waits a random duration within [min, max] ms
  4. A skip-phase guard prevents the resume-replay mechanism from trapping the interpreter in an infinite delay loop (this was the critical bug discovered in v1)
  5. Checksum trailer at the end of the binary is recalculated after every modification
  6. On no match: falls through to the original opcode handler

The result is a new opcode: 01 08 min_lo min_hi max_lo max_hiRandom Delay — fully interoperable with the existing macro buffer format.


Hs75tTool — Desktop Companion

A WPF / .NET 9 desktop application that programs macros into the keyboard over USB HID.

Tool overview

Features

  • Connect / Read / Restore — pull the live macro buffer from the keyboard via HID
  • Save — write the full buffer back; keyboard plays macros independently once saved
  • 16 macro slots with per-slot timeline view
  • Record — global keyboard and mouse hook captures keystrokes system-wide
    • Real delays — optionally inserts Fixed Delays based on actual timing between keystrokes
    • Append mode — add to existing rows without clearing
    • Mouse clicks / scroll wheel recording
  • Action editor — add, reorder, duplicate, delete events; Rebind Key, Toggle Down/Up, Insert Tap After
  • Convert All Fixed to Random — replaces every Fixed Delay in a slot with a Random Delay bounded by a configurable ±offset (requires patched firmware)
  • Playback modes: Once, Repeat N times, Stop, Held
  • Import / Export — save/load slots as .json files
  • Emergency Key Release — sends KeyUp for all HID keycodes to unstick a held key

⚠️ Random Delay and Repeat/Held playback require the custom patched firmware. These features are silently ignored by stock firmware.


Repository Structure

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

Hardware

Field Value
Keyboard HelloGanss HS75T
MCU HFD2201KBA (ARM Cortex-M0, 48 MHz)
Flash 64 KB
SRAM 8 KB
USB VID 0x05AC
USB PID 0x0256
Firmware version V1.15 (dry battery variant)
Hook address 0x1C8E
Code cave address 0xD864 (136 bytes)

Flashing the Patched Firmware

⚠️ Flash at your own risk. The patched binary has been tested on the dry-battery V1.15 variant only. Flashing the wrong binary can brick the keyboard. Keep the vendor stock binary as a backup.

Requirements

  • Python 3.x
  • SonixFlasherC (included)
  • libusb (required by SonixFlasherC)

Steps

  1. Enter bootloader mode

    python tools/reboot_to_bootloader.py
    

    Or run tools/ENTER_BOOTLOADER.bat. The keyboard disconnects and re-enumerates as a Sonix bootloader device.

  2. Flash the patched firmware

    SonixFlasherC -d SN248B -r 0 -t 0 -w firmware/HFD2201KBA_SN32F248B_patched_v5.bin
    
  3. Verify the keyboard re-enumerates normally and the tool connects.

To restore stock firmware, flash firmware/HFD2201KBA_SN32F248B_VENDOR_STOCK.bin with the same procedure.


Building the Tool

Requires .NET 9 SDK and Windows.

cd src
dotnet build Hs75tTool.sln
dotnet run --project Hs75tTool.Desktop

Macro Protocol Reference

Macros are stored in onboard flash from offset 0x0414 to 0x1FFF. Each slot is null-terminated. 0x00 must not appear inside a macro payload — it is treated as a slot terminator.

Opcode Table

Bytes Meaning
01 02 key Key Down
01 03 key Key Up
01 04 lo hi Fixed Delay (base-255 non-zero encoded)
01 08 min_lo min_hi max_lo max_hi Random Delay — custom extension, patched firmware only

Base-255 Non-Zero Encoding

Delay values are encoded to avoid null bytes:

lo = (ms % 255) + 1
hi = (ms // 255) + 1
decoded = (lo - 1) + (hi - 1) * 255

Credits

  • SonixFlasherC — open-source Sonix HID flasher (included under its original license)

About

ARM Cortex-M0 firmware patch and WPF macro tool for the HelloGanss HS75T — reverse-engineered random delay opcode injected into a code cave, with a full HID desktop companion app

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages