Show HN: Live, system-wide USB transfer sniffer in eBPF

r3tr01 pts0 comments

GitHub - yeet-src/usbsnoop: Live, system-wide USB transfer sniffer in eBPF — decodes USB traffic inline (control SETUP, SCSI, HID) from two universal URB hooks. No usbmon, no hardware sniffer. CO-RE portable. · GitHub

/" data-turbo-transient="true" />

Skip to content

Search or jump to...

Search code, repositories, users, issues, pull requests...

-->

Search

Clear

Search syntax tips

Provide feedback

--><br>We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Cancel

Submit feedback

Saved searches

Use saved searches to filter your results more quickly

-->

Name

Query

To see all available qualifiers, see our documentation.

Cancel

Create saved search

Sign in

/;ref_cta:Sign up;ref_loc:header logged out"}"<br>Sign up

Appearance settings

Resetting focus

You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

{{ message }}

yeet-src

usbsnoop

Public

Notifications<br>You must be signed in to change notification settings

Fork

Star

master

BranchesTags

Go to file

CodeOpen more actions menu

Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit

History<br>4 Commits<br>4 Commits

assets

assets

.gitignore

.gitignore

Makefile

Makefile

README.md

README.md

main.js

main.js

usbsnoop.bpf.c

usbsnoop.bpf.c

View all files

Repository files navigation

usbsnoop — live USB transfer sniffer from two fentry hooks

A real-time, colorized feed of USB traffic system-wide — built on the two<br>universal URB chokepoints every host-controller driver funnels through, so it<br>works on xHCI/EHCI/OHCI/dwc alike with no per-controller tracepoints and no<br>usbmon. Fully CO-RE portable.

fentry hook<br>what it tells us

usb_submit_urb<br>a transfer was queued (device, endpoint, type, payload)

usb_hcd_giveback_urb<br>it completed (status, bytes moved, latency, payload)

An lru_hash keyed by the URB pointer stitches the two together: submit stamps<br>a start time, completion reads it back for the submit→complete latency, then<br>deletes it. This mirrors httpbody's request/response pairing — SUBMIT is<br>the "request" (what the host sends), COMPLETE the "response" (what the<br>device returns).

Control transfers get their 8-byte SETUP packet decoded into the standard<br>request name (GET_DESCRIPTOR, SET_CONFIGURATION, …); data stages render as<br>text when they look textual and as a hexdump otherwise.

Output is one line per event (compact). The first time a device appears it<br>gets a ▸ legend line (bus-dev, vid:pid, product, link speed); after that<br>each row carries only the short DEV tag, so the left-hand columns stay aligned<br>and scannable under heavy traffic. Each row shows time, kind (SUBMIT/CMPLT),<br>transfer type, epNdir, the direction arrow (← device→host IN, →<br>host→device OUT), byte counts, status, latency, and the owning kernel driver,<br>then a · and the most useful detail (decoded SETUP, SCSI command, or a short<br>payload preview). Pass --hex for the full multi-line hexdump instead. Hex<br>bytes are colored by value class (null blue, printable ASCII cyan, whitespace<br>green, other control magenta, high/non-ASCII yellow) on a TTY; piped output is<br>plain.

Use cases

Reverse-engineering peripherals — watch a device enumerate and exchange<br>vendor control requests and HID reports live, no hardware sniffer or usbmon<br>setup. SETUP packets and payloads are decoded as you poke at the device.

Driver / firmware debugging — see exactly which commands your driver or<br>app sends a device and what comes back, with submit→complete latency on every<br>transfer.

Mass-storage / SCSI inspection — Bulk-Only Transport wrappers decode to<br>the SCSI command (READ(10) lba=… blocks=…, WRITE(10), CSW PASS/FAIL).

Catching errors — --errors-only surfaces stalls (EPIPE), timeouts,<br>babble, and CRC errors across every device at once.

Spotting rogue devices — a freshly plugged device shows what it does the<br>instant it attaches; BadUSB-style HID injection surfaces as INT reports or<br>SET_REPORT control writes you didn't trigger.

Capture for offline analysis — --json emits NDJSON; pipe to jq or a<br>file to diff payloads across runs.

Performance triage — on a timed exit you get a per-device rollup and a<br>log2 latency histogram to find the slow or chatty devices.

Install

curl -fsSL https://yeet.cx | sh

Then run it straight from GitHub — yeet fetches the example and builds it for<br>you, no clone needed:

yeet run github:yeet-src/usbsnoop

Build

To build from a local checkout instead:

make

Dumps the kernel's BTF to vmlinux.h (for struct urb, usb_device, and the<br>device descriptor), then compiles. Requires clang, bpftool, and a kernel<br>with BTF.

Run

yeet run . # all devices, runs until Ctrl-C<br>yeet run . -- --secs 30 # stop after 30s (prints a summary)<br>yeet run . -- --vid 0x320f # one vendor<br>yeet run . -- --vendor-id 0x046d...

device yeet transfer usbsnoop submit sniffer

Related Articles