DEV Community

Cover image for Turning Windows-only USB gadgets into open Python drivers with usb-rea
Emir Mrkaljević
Emir Mrkaljević

Posted on

Turning Windows-only USB gadgets into open Python drivers with usb-rea

Every desk has one: an RGB controller, a PC-case LCD, a fan hub, a "smart" lamp that only works with a Windows vendor app. On Linux it's a paperweight.

The usual fix is hours in Wireshark: click a slider, stare at hex, click again, diff by eye. usb-rea automates that loop.

demo

How it works

  1. Capture USB traffic: usbmon on Linux, USBPcap/Wireshark on Windows.
  2. Label every click: usb-rea mark "fan 1200", usb-rea mark "color 255 0 0".
  3. usb-rea diffs the packets per action and infers each byte's role.
  4. You get report.md, where every claim cites Wireshark frame numbers, and a standalone pyusb driver.
$ pip install usb-rea
$ usb-rea demo --device fan
fan    100%  0x5a | counter (+1 per message) | 0x10 | u16be round(value * 1 + 0) | 0x00 | u16le crc16_modbus(bytes[0:6])
color  100%  0x5a | counter (+1 per message) | 0x20 | round(value * 1 + 0) | round(value[1] * 1 + 0) | round(value[2] * 1 + 0) | u16le crc16_modbus(bytes[0:6])
mode   100%  0x5a | counter (+1 per message) | 0x30 | lookup 1→0x11, 2→0x22, 3→0x47 | 0x00 | 0x00 | u16le crc16_modbus(bytes[0:6])
Enter fullscreen mode Exit fullscreen mode

What it can infer (v0.4.0)

  • constant headers and command ids
  • linear values, 8-, 16- or 32-bit, little- or big-endian, several per action (RGB)
  • packed bitfields (a 4-bit level next to constant flag bits, 12-bit spans)
  • lookup tables for non-linear choices (DPI, polling rate)
  • rolling sequence counters
  • multi-packet commands (write + apply) and checksums that cover an earlier packet
  • checksums: sum8, xor8, neg/inv sum8, sum16, CRC8, CRC8/MAXIM, CRC16 CCITT-FALSE/XMODEM/MODBUS/KERMIT

The generated driver encodes values you never captured, including the counter and the CRC.

A Wireshark dissector for free

usb-rea dissector cap.pcapng -o lamp.lua turns the inferred protocol into a Lua dissector. Load it in Wireshark and the Info column says brightness 50 instead of hex; values, counters and lookup labels are decoded and bad checksums get an expert warning. It is tested against real tshark in CI.

No markers? Fine

Already have a plain USBPcap capture? --labels "off,on,brightness 10,brightness 90" splits the traffic at the pauses between your clicks. HID devices get their report descriptor decoded too (usb-rea hid).

Vendor apps that never stop talking

Most vendor apps poll the device several times a second. Those packets used to land between your clicks and wreck the diff. Since v0.3.0 usb-rea spots messages that repeat on a steady rhythm independent of your actions, even when they carry a sequence number and checksum, and lists them in a separate Background traffic (ignored) section with ?? at the changing bytes instead of mistaking them for commands.

Reading the device's answers

Sending commands is half a driver. Since v0.4.0 usb-rea also pairs every host query with the reply the device sends back and decodes it: echoed command ids and sequence numbers, checksums, values that follow your labels, and readings that change on their own (RPM, temperature) with the range it saw. The synthetic fan in the demo answers its status poll like this:

device replies:
fan          6/6  0xa5 | = query byte 2 (0x10) | = query byte 1 | u16le round(value * 1 + 0) | sum8(bytes[0:5])
poll 1       4/7  0xa5 | = query byte 2 (0x40) | u16le reading, seen 794..4012 | u8 reading, seen 35..38 | 0x00 | sum8(bytes[0:6])
Enter fullscreen mode Exit fullscreen mode

The generated driver gets query_fan(), query_poll_1() and friends that send the query, read the reply and return a dict with the decoded fields and whether the checksum matched.

Evidence over vibes

LLMs are great at guessing protocols and bad at admitting they guessed. usb-rea's inference is deterministic. A byte is only called a value if a linear fit survives leave-one-out cross-checks; otherwise it becomes a lookup table and the report says it only covers what you captured. Fewer than 3 samples lowers the confidence score.

How I tested it without hardware

  • synthetic lamp and fan-controller captures with known ground truth
  • packets built exactly like liquidctl's NZXT Kraken X driver and rivalcfg's SteelSeries Rival 100 profile; usb-rea recovers the same structure
  • real public Wireshark captures (usbmon mass storage, USBPcap USB3 Vision)
  • CRC implementations checked against the standard catalogue values

It has not been run on a physical gadget yet. That's where you come in.

Agents welcome, safely

usb-rea mcp is an MCP server, listed in the official MCP Registry as io.github.emirmrkaljevic/usb-rea (uvx --from usb-rea usb-rea mcp). Tools: capture, mark, diff, report, scaffold, HID descriptor, Wireshark dissector, confirmed replay. An agent can run the session while you click. Sending anything to hardware always needs explicit human confirmation.

Legal

Reverse engineering for interoperability is allowed under EU Directive 2009/24/EC Art. 6 and US DMCA §1201(f). usb-rea only analyses traffic from devices you own and never touches vendor code.

Next

Real-device case studies, and naming the reply fields from your labels. If you have a Windows-only gadget, open an issue with a capture and your action labels.

Repo (MIT): https://github.com/emirmrkaljevic/usb-rea

I built usb-rea with help from AI coding tools; the inference itself is deterministic and every claim in its reports points at captured frames.

Top comments (0)