usbhid

package
v0.592.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: AGPL-3.0 Imports: 3 Imported by: 0

Documentation

Overview

Package usbhid decodes USB HID Keyboard Boot Protocol reports — the 8-byte input reports that every BadUSB-class device (Hak5 Rubber Ducky, Bash Bunny, OMG Cable, Adafruit Trinket BadUSB, the Bruce ESP32 BadUSB add-on) generates to inject keystrokes into a victim host.

This package is the **defensive sibling** of the badusb_* family — those Specs *generate* BadUSB scripts and target profiles; this decoder *reconstructs* the keystrokes from a usbmon capture of a suspected rogue device, so an incident responder can answer "what did the attacker actually type?" from a pcap alone.

Operationally, this Spec is the post-incident forensic primitive used in:

  • **Insider-threat investigations** — a rogue HID device plugged into a workstation; the corporate USB-monitoring stack (usbmon, Sysmon, EDR) recorded the URBs but not the rendered text.
  • **DEF CON Recon Village CTFs** — challenges that hand out a usbmon pcap and ask "what was typed".
  • **Vendor abuse triage** — a benign HID device suspected of typing without operator intent; comparing recorded reports against authorised payloads.

Wrap-vs-native judgement

Native. The USB HID Usage Tables (HID 1.11 + HUT 1.5) are
publicly available; the 8-byte Boot Protocol Keyboard
Report layout is fully fixed (1-byte modifier bitmap +
1-byte reserved + 6 bytes of active HID Usage codes). No
crypto at the parse layer. The hard part is the
reconstruction policy: how to turn a stream of "currently
held" reports into a sequence of distinct keystroke events
+ a DuckyScript-style transcript.

What this package covers

  • **Per-report decode** (USB HID 1.11 §B.1 Boot Protocol, 8 bytes):

  • byte 0: **Modifier bitmap** — bit 0 LCtrl + bit 1 LShift + bit 2 LAlt + bit 3 LGui + bit 4 RCtrl + bit 5 RShift + bit 6 RAlt + bit 7 RGui.

  • byte 1: Reserved (= 0).

  • bytes 2-7: up to 6 simultaneous keys held as HID Usage codes (Usage Page Keyboard/Keypad). 0x00 padding for unused slots. 0x01-0x03 are error codes (ErrorRollOver / POSTFail / ErrorUndefined).

  • **80+ entry HID Usage code name + Shift-variant table** (HID Usage Tables 1.5 §10 — selected high-runners):

  • 0x04-0x1D `a..z` (Shift → `A..Z`).

  • 0x1E-0x27 `1..9 0` (Shift → `!@#$%^&*()`).

  • 0x28 `Enter` / 0x29 `Escape` / 0x2A `Backspace` / 0x2B `Tab` / 0x2C `Space`.

  • 0x2D-0x38 punctuation row (`-/_`, `=/+`, `[/{`, `]/}`, `\/|`, `;/:`, `'/"`, “ `/~ “, `,/<`, `./>`, `//?`).

  • 0x39 Caps Lock.

  • 0x3A-0x45 `F1..F12`.

  • 0x4A-0x4E `Home`, `PageUp`, `Delete`, `End`, `PageDown`.

  • 0x4F-0x52 arrow keys (`Right`, `Left`, `Down`, `Up`).

  • 0x53 NumLock + 0x54-0x63 keypad.

  • **Key-down event detection** by report-to-report diffing — successive reports declare which keys are currently held; transitions from "not in previous report" to "in current report" mark a fresh keystroke. Suppresses repeat reports of the same key-held state.

  • **Reconstructed text** — best-effort string concatenation of every printable key-down event (Shift state honoured). Caps Lock toggling is tracked across the report stream.

  • **DuckyScript-style transcript** — produces a sequence of directives that, fed back into a Rubber-Ducky-class encoder, would approximate the same keystroke sequence:

  • Consecutive printable characters → `STRING "<text>"`.

  • Standalone non-printable keys → their DuckyScript keyword (`ENTER`, `TAB`, `ESC`, `BACKSPACE`, `DELETE`, `UP`, `DOWN`, `LEFT`, `RIGHT`, `F1..F12`, `HOME`, `END`, `PAGEUP`, `PAGEDOWN`, `CAPSLOCK`).

  • Modifier + key combinations → DuckyScript modifier keywords (`CTRL`, `SHIFT`, `ALT`, `GUI`, `CTRL-SHIFT`, `CTRL-ALT`, `ALT-SHIFT`, `GUI-SHIFT`) followed by the bare key.

What this package does NOT cover (deliberately out of scope)

  • **usbmon framing** — operators must extract the 8-byte HID reports from a usbmon pcap (Linux) or USBPcap (Windows) by stripping the per-URB headers; this decoder takes the concatenated report stream as hex input.
  • **USB enumeration descriptors** — Device / Configuration / Interface / HID Report descriptors that *declare* the report layout (vendor ID, product ID, report-ID field, non-Boot-Protocol report shapes) are out of scope.
  • **Composite HID devices** — devices that mix Keyboard + Mouse + Consumer Control reports in the same pipe; callers must split per-report-ID streams before feeding this decoder.
  • **Non-Boot-Protocol reports** — devices that opt out of Boot Protocol and define a custom HID Report Descriptor (with Report ID + per-key bitmaps + variable-length reports) are out of scope. Most BadUSB hardware uses Boot Protocol, but enterprise keyboards often don't.
  • **Locale-specific keymaps** — the Shift-variant table reflects US QWERTY; reports from a UK / DE / FR / ES / IT / Dvorak / Colemak host would map to different printable characters. Operators with non-US keymaps must re-interpret the surfaced HID Usage codes against their local layout.
  • **Replay timing analysis** — the per-report inter-arrival timing in a usbmon pcap can fingerprint BadUSB vs human typing (BadUSB devices type at uniform sub-10ms cadences); this decoder works on pure hex without timing metadata.
  • **DuckyScript v2 / v3 control flow** — DuckyScript v2+ adds `IF`, `WHILE`, `VAR`, `RANDOM_INT()`, etc.; this decoder only outputs the v1 STRING / modifier / key primitives that map back from observed keystrokes.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExtractUsbmonReports added in v0.366.0

func ExtractUsbmonReports(capture string) (hexReports string, count int, err error)

ExtractUsbmonReports pulls the 8-byte USB HID Keyboard Boot Protocol reports out of a Linux usbmon text capture (the format emitted by `cat /sys/kernel/debug/usb/usbmon/<N>u`, and what tshark/Wireshark show for usbmon-sourced captures). It returns the concatenated report bytes as a hex string ready for Decode, plus the number of reports found.

usbmon text line format

Each line is, approximately:

<urb-tag> <timestamp> <type> <xfer><dir>:<bus>:<dev>:<ep> <status> <len> <marker> [<data-words>]

e.g.

	ffff8801ab33e3c0 1369381512 C Ii:1:003:1 0 8 = 00000400 00000000

  - type:   S (submit), C (callback/complete), E (submission error)
  - xfer:   C control, Z isoc, I interrupt, B bulk
  - dir:    i in, o out
  - marker: '=' data follows, '<' data not captured / none
  - data:   bytes in capture order, visually grouped into 4-byte words

What is extracted, and the heuristic

A keyboard Boot Protocol report is exactly 8 bytes and arrives on an Interrupt-IN endpoint, surfaced to the host on the callback (C) line. ExtractUsbmonReports therefore keeps every line that is a callback (C), on an Interrupt-IN transfer (xfer/dir "Ii"), carries data (marker '='), and is exactly 8 bytes long. The 8-byte filter is what separates the keyboard from a co-resident mouse (3-4 byte boot reports) on the same bus; the per-report decode in Decode then validates the Boot Protocol structure. Submit (S) lines, Interrupt-OUT (Io, e.g. LED reports), control/bulk transfers, and non-8-byte interrupt data are skipped.

Data bytes are printed in capture order (no endian swap), grouped into 4-byte words for readability; the words are simply concatenated.

Types

type KeyDownEvent

type KeyDownEvent struct {
	ReportIndex int      `json:"report_index"`
	Code        int      `json:"code"`
	Name        string   `json:"name"`
	Modifiers   []string `json:"modifiers,omitempty"`
	// Printable rendering of this key under the current modifier
	// state (empty for non-printable keys).
	Char string `json:"char,omitempty"`
}

KeyDownEvent is a key-press transition (key present in this report, absent from previous).

type KeyRef

type KeyRef struct {
	Code int    `json:"code"`
	Name string `json:"name"`
}

KeyRef is one active key in a report.

type Report

type Report struct {
	ModifierHex     string   `json:"modifier_hex"`
	ModifiersActive []string `json:"modifiers_active,omitempty"`
	Keys            []KeyRef `json:"keys,omitempty"`
}

Report is one 8-byte HID Keyboard Boot Protocol report.

type Result

type Result struct {
	TotalBytes  int      `json:"total_bytes"`
	ReportCount int      `json:"report_count"`
	Reports     []Report `json:"reports"`

	// Aggregated outputs.
	KeyDownEvents     []KeyDownEvent `json:"key_down_events"`
	ReconstructedText string         `json:"reconstructed_text"`
	DuckyScript       string         `json:"duckyscript"`
}

Result is the structured forensic decode of a stream of USB HID Keyboard Boot Protocol reports.

func Decode

func Decode(hexStr string) (*Result, error)

Decode parses a hex stream of 8-byte HID Keyboard reports. Separators (':' '-' '_' whitespace) tolerated; '0x' prefix tolerated.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL