[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-82803":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":10,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":36,"readmeContent":37,"aiSummary":38,"trendingCount":15,"starSnapshotCount":15,"syncStatus":39,"lastSyncTime":40,"discoverSource":41},82803,"usbsnoop","yeet-src\u002Fusbsnoop","yeet-src","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.","https:\u002F\u002Fyeet.cx",null,"JavaScript",76,5,53,0,1,16,23,3,2.33,false,"master",true,[25,26,27,28,29,30,31,32,33,34,35],"bpf","co-re","ebpf","libbpf","linux","observability","reverse-engineering","tracing","usb","usb-sniffer","yeet","2026-06-12 02:04:28","# usbsnoop — live USB transfer sniffer from two fentry hooks\n\n![usbsnoop demo](assets\u002Fdemo.gif)\n\nA real-time, colorized feed of USB traffic **system-wide** — built on the two\nuniversal URB chokepoints every host-controller driver funnels through, so it\nworks on xHCI\u002FEHCI\u002FOHCI\u002Fdwc alike with no per-controller tracepoints and no\n`usbmon`. Fully CO-RE portable.\n\n| fentry hook              | what it tells us                                        |\n| ------------------------ | ------------------------------------------------------- |\n| `usb_submit_urb`         | a transfer was queued (device, endpoint, type, payload) |\n| `usb_hcd_giveback_urb`   | it completed (status, bytes moved, latency, payload)    |\n\nAn `lru_hash` keyed by the URB pointer stitches the two together: submit stamps\na start time, completion reads it back for the submit→complete latency, then\ndeletes it. This mirrors `httpbody`'s request\u002Fresponse pairing — **SUBMIT** is\nthe \"request\" (what the host sends), **COMPLETE** the \"response\" (what the\ndevice returns).\n\nControl transfers get their 8-byte SETUP packet decoded into the standard\nrequest name (`GET_DESCRIPTOR`, `SET_CONFIGURATION`, …); data stages render as\ntext when they look textual and as a hexdump otherwise.\n\nOutput is **one line per event** (compact). The first time a device appears it\ngets a `▸` legend line (`bus-dev`, `vid:pid`, product, link speed); after that\neach row carries only the short `DEV` tag, so the left-hand columns stay aligned\nand scannable under heavy traffic. Each row shows time, kind (SUBMIT\u002FCMPLT),\ntransfer type, `epNdir`, the direction arrow (`←` device→host IN, `→`\nhost→device OUT), byte counts, status, latency, and the owning kernel driver,\nthen a `·` and the most useful detail (decoded SETUP, SCSI command, or a short\npayload preview). Pass `--hex` for the full multi-line hexdump instead. Hex\nbytes are colored by value class (null blue, printable ASCII cyan, whitespace\ngreen, other control magenta, high\u002Fnon-ASCII yellow) on a TTY; piped output is\nplain.\n\n## Use cases\n\n- **Reverse-engineering peripherals** — watch a device enumerate and exchange\n  vendor control requests and HID reports live, no hardware sniffer or `usbmon`\n  setup. SETUP packets and payloads are decoded as you poke at the device.\n- **Driver \u002F firmware debugging** — see exactly which commands your driver or\n  app sends a device and what comes back, with submit→complete latency on every\n  transfer.\n- **Mass-storage \u002F SCSI inspection** — Bulk-Only Transport wrappers decode to\n  the SCSI command (`READ(10) lba=… blocks=…`, `WRITE(10)`, `CSW PASS\u002FFAIL`).\n- **Catching errors** — `--errors-only` surfaces stalls (`EPIPE`), timeouts,\n  babble, and CRC errors across every device at once.\n- **Spotting rogue devices** — a freshly plugged device shows what it does the\n  instant it attaches; BadUSB-style HID injection surfaces as `INT` reports or\n  `SET_REPORT` control writes you didn't trigger.\n- **Capture for offline analysis** — `--json` emits NDJSON; pipe to `jq` or a\n  file to diff payloads across runs.\n- **Performance triage** — on a timed exit you get a per-device rollup and a\n  log2 latency histogram to find the slow or chatty devices.\n\n## Install\n\n```sh\ncurl -fsSL https:\u002F\u002Fyeet.cx | sh\n```\n\nThen run it straight from GitHub — yeet fetches the example and builds it for\nyou, no clone needed:\n\n```sh\nyeet run github:yeet-src\u002Fusbsnoop\n```\n\n## Build\n\nTo build from a local checkout instead:\n\n```sh\nmake\n```\n\nDumps the kernel's BTF to `vmlinux.h` (for `struct urb`, `usb_device`, and the\ndevice descriptor), then compiles. Requires `clang`, `bpftool`, and a kernel\nwith BTF.\n\n## Run\n\n```sh\nyeet run .                              # all devices, runs until Ctrl-C\nyeet run . -- --secs 30                 # stop after 30s (prints a summary)\nyeet run . -- --vid 0x320f              # one vendor\nyeet run . -- --vendor-id 0x046d --product-id 0xc52b # one device by id\nyeet run . -- --bus 3 --dev 4           # one device by bus address\nyeet run . -- --type control,int        # only these transfer types\nyeet run . -- --no-data                 # metadata only, skip payload capture\nyeet run . -- --max-data 64             # cap rendered payload at 64 bytes\nyeet run . -- --errors-only             # only failed completions (stalls, timeouts)\nyeet run . -- --hex                      # full multi-line hexdump per transfer\nyeet run . -- --json | jq .             # NDJSON, one object per event\n```\n\n## Flags\n\n| flag           | default | meaning                                              |\n| -------------- | ------- | ---------------------------------------------------- |\n| `--secs`       | forever | how long to run; omit to run until Ctrl-C (a number stops + prints a summary) |\n| `--vid`, `--vendor-id`  | any | filter by vendor id (hex `0x1d6b` or decimal)    |\n| `--pid`, `--product-id` | any | filter by product id                             |\n| `--bus`        | any     | filter by bus number                                 |\n| `--dev`        | any     | filter by device address                             |\n| `--type`       | all     | csv of `iso`, `int`, `control`, `bulk`               |\n| `--no-data`    | off     | don't read transfer buffers (metadata only)          |\n| `--max-data`   | `4096`  | max bytes of payload rendered per event              |\n| `--errors-only`| off     | show only non-OK completions (skips SUBMIT and OK)   |\n| `--hex`        | off     | full multi-line hexdump per transfer (compact inline preview otherwise) |\n| `--json`       | off     | emit NDJSON (one object per event) instead of the TTY view |\n| `--page-offset-base` | off | kernel `page_offset_base` address (hex) — enables SG payload capture (x86-64) |\n| `--vmemmap-base`     | off | kernel `vmemmap_base` address (hex) — paired with `--page-offset-base` |\n\nAll filtering happens kernel-side, so filtered-out traffic never reaches\nuserspace.\n\nEach event line ends with the owning kernel driver in brackets\n(`[hid_irq_in]`, `[usb_api_blocking_completion]`) — `urb->complete` symbolized\nin-kernel via `bpf_snprintf(\"%ps\")`, so no `\u002Fproc\u002Fkallsyms` lookup is needed.\nMass-storage bulk transfers decode their Bulk-Only Transport wrapper into the\nSCSI command (`CBW READ(10) lba=… blocks=…` \u002F `CSW PASS`). On a timed exit\n(reaching `--secs`) a per-device summary and a log2 latency histogram print;\na Ctrl-C exit skips it (there is no JS-visible signal hook).\n\n## Scatter-gather payloads\n\nBulk traffic (mass storage and friends) often hands the stack a `struct\nscatterlist` array (`urb->sg`) instead of a single linear `transfer_buffer`, so\nthe payload lives scattered across pages. usbsnoop walks that array and copies\neach segment's bytes, but reaching them means translating a page to its kernel\nvirtual address — the inverse of x86-64's `page_to_virt`, which needs the\nrunning kernel's `page_offset_base` and `vmemmap_base` (both KASLR-randomized).\n\nThe JS isolate can't read `\u002Fproc\u002Fkallsyms` and the loader has no ksym support,\nso you pass the two symbol *addresses* in and the BPF side dereferences them:\n\n```sh\nyeet run . -- \\\n  --page-offset-base 0x$(sudo awk '$3==\"page_offset_base\"{print $1}' \u002Fproc\u002Fkallsyms) \\\n  --vmemmap-base     0x$(sudo awk '$3==\"vmemmap_base\"{print $1}'     \u002Fproc\u002Fkallsyms)\n```\n\nWithout those flags, SG transfers still show full metadata, just no payload\nbytes — the prior behavior. This path is **x86-64 only**: on other arches leave\nthe flags off.\n\n## Limits\n\n- Only the first **16384 bytes** of each transfer are captured (a power of two —\n  the verifier read-clamp depends on it). Larger buffers are truncated; the\n  header still reports the true `actual\u002Frequested` length. Each ring record\n  carries a full `data[16384]`, so the 8 MiB ring holds ~512 events.\n- Scatter-gather payloads need the `--page-offset-base` \u002F `--vmemmap-base` flags\n  above and an x86-64 host; each segment is captured up to a page, and only the\n  first 64 segments of a transfer are walked.\n- A transfer submitted before usbsnoop attached has no start stamp, so its\n  completion shows no latency.\n- USB descriptors are little-endian and read directly — correct on the\n  little-endian hosts BPF runs on.\n","yeet-src\u002Fusbsnoop 是一个基于 eBPF 的实时系统级 USB 传输嗅探工具，能够从两个通用 URB 钩子中解码 USB 流量（包括控制 SETUP、SCSI 和 HID）。该项目使用了 CO-RE 技术以确保跨平台的可移植性。其核心功能包括对 USB 数据包的实时解码与显示，支持多种类型的 USB 控制器（如 xHCI\u002FEHCI\u002FOHCI\u002Fdwc），无需依赖特定硬件或 `usbmon`。通过解析 `usb_submit_urb` 和 `usb_hcd_giveback_urb` 两个关键点的数据，usbsnoop 能够提供详细的传输信息，包括设备标识、端点、类型、状态、延迟等，并且能够将控制传输的SETUP包解码为标准请求名称。该工具适用于外设逆向工程、驱动程序\u002F固件调试、存储\u002FSCSI检查、错误捕获以及可疑设备检测等多种场景。",2,"2026-06-11 04:09:19","CREATED_QUERY"]