[OneDev #28] peripherals: crack feat 0x0c finalize CRC for G-HUB-independent binding writes #6
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Imported from OneDev issue #28 (id 35528)
Original project: internal-joseph
Original state: Closed
Original submitterId: 5
Original submitDate: 2026-05-05T15:31:59.990+00:00
Original lastActivity: {'date': '2026-05-05T21:25:22.561+00:00', 'description': "changed state to 'Closed'", 'userId': 5}
Original fields:
Context
We've cracked the
0x8101 OnboardProfiles-kbdsimple-bind wire format end-to-end except the CRC at bytes 7-10 of the finalize LONG packet.Why this matters
Right now we can REPLAY captured G HUB binding writes verbatim (because the CRCs are precomputed in the captured bytes). We CANNOT compose new bindings from scratch — every keymap variant requires re-capturing G HUB applying it.
If we crack the CRC algorithm, we can emit arbitrary bindings (e.g. "G7 → F13, G8 → custom HID usage 0x80, G9 → mouse left click") without ever launching G HUB.
What we know
From the simple-bind 3-call sequence:
XX XX XX XXis uniform-distribution per binding — almost certainly CRC32. The0dbyte just before the CRC is the begin offset (matches the SHORT call's offset param).Approach
crcalcor similar0x8Ferror)Done when
peripherals/scripts/g915x_crc.pySee also
peripherals/g915x-nkro-findings.md— wire format detailsperipherals/scripts/g915x_open_writes.txt— captured CRCs across multiple bindingsImported from OneDev comment id 542 on issue #28
Original userId: -1
Original date: 2026-05-05T17:57:22.702+00:00
State changed as code fixing the issue is committed (b6a6a30abbf90f7aec0341ab094ebc6e361d88e9)
Imported from OneDev comment id 547 on issue #28
Original userId: 5
Original date: 2026-05-05T20:31:26.216+00:00
Reopening — the auto-close from b6a6a30 was premature. The "32-bit CRC at bytes 7-10" framing in the original description is also wrong; it's actually two 16-bit fields. See follow-up comment for the partial crack + remaining work.
Imported from OneDev comment id 548 on issue #28
Original userId: 5
Original date: 2026-05-05T20:31:58.053+00:00
2026-05-05 RE session — half cracked
The "CRC at bytes 7-10" is actually two 16-bit fields, not a single 32-bit CRC. Both must validate for the keyboard to accept the profile commit.
Field 1: data CRC at fn=9 params[7:9] — CRACKED
Verified by:
0x1991produces0x802dat offset 7 BE for the captured page datacrc(page[2:] + crc_LE_appended) == 0x0000✓Reference implementation:
peripherals/scripts/patch_gkey_color_v2.py(crc16_logitech_8101).Field 2: secondary check at fn=9 params[9:11] — STILL BLOCKED
80 2dfe 0141 d912 ffEliminated:
page[2:]with poly=0x1991 (any init in 65k-space) → no matchpage[2:] + crc_BEorpage[2:] + crc_LEwith poly=0x1991 → no matchHardware test: patched the page (
80 00 80purple →ff ff ffwhite) with a recomputed first CRC and unchanged second field → keyboard rejected the profile commit and fell into preset wave animation (HID++ event-emit mode preserved). Confirms the second field IS validated.Likely remaining shape
Concrete next steps
lghub_agent.exe— find theFeature8101OnboardProfilesvftable and locate the second checksum routine. Same approach as the v1-v4 RTTI walks that cracked FKC activation.peripherals/scripts/RECAPTURE.md.Artifacts in this session
scripts/g915x_dual_collection_io.py— proper Col01-SHORT / Col02-LONG routing helpersscripts/g915x_sector_read.py— paginated 0x8101 sector reader (works after fixing offset encoding bug — params[0:2] BE, params[2]=0)scripts/g915x_sector_scan.py— scan readable sector range (confirms 0x0065 is write-only RAM)scripts/g915x_crc_bruteforce*.py— three iterations of CRC search; v3 found0x1991scripts/patch_gkey_color_v2.py— patcher with CRC1 recompute (works for CRC1, blocked on CRC2)scripts/RECAPTURE.md— workaround procedure for default G-key color via G HUB recaptureUpdated docs
peripherals/g915x-rgb-control.md— § "G-key LED control — partial crack (2026-05-05)"peripherals/README.md— phases 3.7 (TOML painter shipped) and 3.8 (CRC partial)Imported from OneDev comment id 550 on issue #28
Original userId: 5
Original date: 2026-05-05T21:25:19.019+00:00
2026-05-05 — moot for color control
G-key LED color is fully programmable via 0x8081 little-frame at indices 0xB4-0xBC (G1=0xB4 ... G9=0xBC), cribbed from OpenRGB's G915 controller (
DeviceUpdateLEDsadds 0xB3 to zone-local G-key indices for the GKEYS zone). No profile-page CRC needed for colors.Verified end-to-end with rainbow test (
peripherals/scripts/g915x_gkey_color_test.py) — G1=red, G2=orange, ... G9=grey, all paint correctly.Wired into
peripherals/scripts/g915x_paint.py(GKEY_NAME_TO_INDEX) and exposed in TOML config asG1-G9.Status of the actual CRC question
The "we cracked half (poly 0x1991)" framing was wrong — a follow-up capture proved the polynomial doesn't generalize. The original match was a 1-in-65k coincidence. The 0x8101 fn=9 CRC is NOT a clean polynomial CRC over the visible page bytes; the input includes session/device state we don't have.
But this doesn't matter for color control. The 0x8101 profile page only matters for changing G-key bindings (which key emits what), which is issue #27 territory and a separate concern.
Closing this issue. If/when bindings-without-G-HUB becomes a real need, reopen and pursue Ghidra into
lghub_agent.exe'sFeature8101checksum routine.Imported from OneDev comment id 551 on issue #28
Original userId: 5
Original date: 2026-05-05T21:25:21.561+00:00
Moot for color control — G-keys go through 0x8081 little-frame (0xB4-0xBC), not 0x8101. See follow-up comment.