From hardware-re-usb-hid
Reverse engineer USB HID peripherals via Linux hidraw. Methodical approach: enumerate, capture, decode, replicate. Triggers on "reverse engineer USB", "USB HID protocol", "HID analysis", "hidraw", "HID report descriptor", "USB peripheral RE", "vendor-specific HID", or any USB HID device reverse engineering task.
How this skill is triggered — by the user, by Claude, or both
Slash command
/hardware-re-usb-hid:hardware-re-usb-hidThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Reverse engineer USB HID peripherals from unknown protocol to working Linux control code via hidraw. Follow the RE loop: Enumerate → Capture → Decode → Replicate → Document.
Reverse engineer USB HID peripherals from unknown protocol to working Linux control code via hidraw. Follow the RE loop: Enumerate → Capture → Decode → Replicate → Document.
A USB HID peripheral has vendor-specific features (lighting, macros, profiles, display control) that only work through proprietary Windows software with no published protocol documentation. Linux users have no way to control these features, and the only path forward is reverse engineering the HID protocol from raw packet captures and report descriptors.
Entry: User identifies a target USB device. Exit: Complete device profile documented (VID:PID, interfaces, hidraw map, packet sizes, endpoint types).
Enumerate everything about the target before touching any protocol.
# Full descriptor dump
lsusb -d VEND:PROD -v
# Topology tree (hub/port/speed)
lsusb -t
# HID interface enumeration (use Bash for the loop, Read for individual files)
for h in /dev/hidraw*; do
echo "=== $h ==="
done
# Then use the Read tool on each /sys/class/hidraw/hidrawN/device/uevent
# HID report descriptors
for h in /sys/class/hidraw/hidraw*/device/report_descriptor; do
echo "=== $h ==="
xxd -l 256 "$h"
done
Use the Read tool to examine each sysfs file (/sys/class/hidraw/hidrawN/device/uevent, report descriptors). Use Glob to find all hidraw device paths matching /sys/class/hidraw/hidraw*/device/uevent. Use Grep to search for device identifiers in kernel logs (/var/log/kern.log or dmesg output).
Kernel driver unbinding: If the device has an active kernel driver (e.g., usbhid), you may need to unbind it before raw access works reliably:
# Find the device's driver binding
ls -la /sys/class/hidraw/hidrawN/device/driver
# Unbind if needed (use the device ID from uevent HID_ID)
echo -n "DEVICE_ID" | sudo tee /sys/bus/hid/drivers/hid-generic/unbind
Some devices work through hidraw without unbinding, but if reads return incomplete data or writes fail silently, unbinding is the likely fix.
Required facts before proceeding:
| Fact | Source |
|---|---|
| VID:PID | lsusb |
| USB speed | lsusb -t (1.5/12/480/5000 Mbps) |
| Interface count & classes | lsusb -v |
| HID report sizes | wMaxPacketSize per interface |
| hidraw mapping | /sys/class/hidraw/*/device/uevent |
| Usage pages | Report descriptor (0xFF00+ = vendor-specific) |
| Endpoint types | Interrupt IN/OUT, Bulk, etc. |
Entry: Device profile complete from Phase 1. Exit: Raw packet captures of normal device operation, with and without vendor software.
Layer captures from passive to active:
Step 1 — Passive sniffing (safe, no device interaction):
# usbmon is a streaming interface — Bash is required here (not Read)
sudo modprobe usbmon
sudo timeout 10 cat /sys/kernel/debug/usb/usbmon/1u > /tmp/usb-capture.txt
Step 2 — Vendor software capture (QEMU VM):
Pass device to a Windows VM, run vendor software, capture traffic on host usbmon. See {baseDir}/resources/qemu-setup.md for VM configuration.
Step 3 — Active probing (careful, sends data to device):
WARNING: Adapt the packet format to match YOUR device. The header structure, status byte offset, and success value below are ONE example — discover yours from passive captures first.
import os, time, sys
fd = os.open("/dev/hidrawN", os.O_RDWR)
log = open("/tmp/probe-log.txt", "w")
for cmd_id in range(0x00, 0xFF):
packet = bytes([REPORT_ID, cmd_id]) + b'\x00' * (PACKET_SIZE - 2)
try:
os.write(fd, packet)
resp = os.read(fd, PACKET_SIZE)
line = f"CMD 0x{cmd_id:02X}: {resp[:16].hex()}"
print(line)
log.write(line + "\n")
log.flush()
except OSError as e:
# Log the error — a USB disconnect here means STOP probing
line = f"CMD 0x{cmd_id:02X}: ERROR — {e}"
print(line, file=sys.stderr)
log.write(line + "\n")
if e.errno == 19: # ENODEV — device disconnected
print("DEVICE DISCONNECTED — aborting probe", file=sys.stderr)
break
time.sleep(0.05) # 50ms delay — prevents overwhelming the device
log.close()
os.close(fd)
Entry: Raw captures from Phase 2. Exit: Documented packet format, command table, and error codes.
Build the protocol model incrementally. Do not assume a specific packet format — discover it from captures. Header length, field positions, and status codes vary widely between devices.
Use Grep to search captured data for patterns. Use the Write tool to maintain a protocol specification document as findings accumulate. See {baseDir}/resources/protocol-template.md for the documentation format.
Entry: Firmware binary obtained (vendor update, debug interface, or flash dump). Exit: Identified architecture, command dispatch table, and key data structures.
Extraction sources (try in order):
Analysis:
binwalk firmware.bin
file firmware.bin
strings firmware.bin | grep -i "version\|build\|copyright\|arm\|cortex"
For ARM Cortex-M: examine vector table at offset 0 — SP at [0:4], Reset handler at [4:8]. Use this to determine flash base address for Ghidra loading.
Key targets in firmware:
Entry: Decoded protocol from Phase 3. Exit: Working Python code that controls the device from Linux without vendor software.
Build minimal reproducers for each discovered command:
#!/usr/bin/env python3
import os, glob
def find_device(name_pattern, interface_pattern):
"""Auto-discover hidraw device by name and interface."""
for h in sorted(glob.glob("/dev/hidraw*")):
base = os.path.basename(h)
try:
uevent = open(f"/sys/class/hidraw/{base}/device/uevent").read()
if name_pattern in uevent and interface_pattern in uevent:
return h
except: pass
return None
def send_recv(dev_path, packet, size):
"""Send packet, read response."""
fd = os.open(dev_path, os.O_RDWR)
try:
padded = packet.ljust(size, b'\x00')
os.write(fd, padded)
return os.read(fd, size)
finally:
os.close(fd)
Use the Write tool to save working scripts. Test each command individually before combining into larger tools.
Entry: Working code from Phase 5. Exit: Complete RE report with device profile, protocol spec, working code, open questions, and risk assessment.
Produce these deliverables:
See {baseDir}/resources/ directory:
{baseDir}/resources/qemu-setup.md — QEMU VM configuration for USB passthrough and vendor software capture{baseDir}/resources/protocol-template.md — Protocol documentation template{baseDir}/resources/display-re.md — Display protocol RE techniques for LCD/OLED peripheralsProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub dmaynor/dmaynor-skills-marketplace --plugin hardware-re-usb-hid