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...