USB Reverse Engineering: What Wireshark Captures at the Kernel Layer
Source: lobsters
This article on crescentro.se walks through the practical mechanics of reverse-engineering a USB device using Wireshark. The technique works well. A conceptual point worth building on: Wireshark is not capturing the USB wire. It captures kernel URBs, and that shapes everything about what you can and cannot see.
The Capture Layer
On Linux, Wireshark uses usbmon, a kernel facility introduced in 2.6.11. On Windows it uses USBPcap, an open-source filter driver. Both intercept traffic at the USB core layer, between the host controller driver (xhci_hcd, ehci_hcd) and the device drivers above it. The tap point is usb_submit_urb() and usb_hcd_giveback_urb().
Application (Wireshark)
↕ read() / ioctl()
/dev/usbmonN ← capture happens here
↕ usbmon hooks into USB core
USB Core (usb_submit_urb / usb_hcd_giveback_urb)
↕
Host Controller Driver (xhci_hcd, ehci_hcd)
↕
Physical USB bus
↕
Device
What you get from this architecture is the logical request/response pairs: transfer type, endpoint, direction, status, and payload. What you do not get is the raw USB packet structure, token packets, handshake packets (ACK, NAK, STALL), NRZI encoding, or start-of-frame markers; the link layer is entirely invisible at this tap point. For protocol analysis that is sufficient. For diagnosing a device that is intermittently disconnecting or producing bus errors, Wireshark cannot help.
For raw bus access you need hardware. OpenVizsla is an open-source FPGA-based USB 2.0 sniffer capable of capturing at 480 Mbps. LUNA from Great Scott Gadgets supports USB man-in-the-middle at the physical layer. USB 3.x high-speed analysis requires commercial analyzers in the thousands-of-dollars range. Most protocol reverse engineering stays at the URB layer because the software tools are free and the URB view answers the questions being asked.
To get started on Linux:
# Load the usbmon module
sudo modprobe usbmon
# Find which bus your device is on
lsusb
# Bus 003 Device 007: ID 046d:c52b Logitech, Inc. Unifying Receiver
# Wireshark will list usbmon1, usbmon2, usbmon3, etc.
# Select usbmon3 to capture bus 3 specifically
If you miss device enumeration because Wireshark was not running when the device was connected, you can force re-enumeration without unplugging:
echo '3-1' | sudo tee /sys/bus/usb/drivers/usb/unbind
echo '3-1' | sudo tee /sys/bus/usb/drivers/usb/bind
Capturing enumeration matters. The descriptor exchange during plug-in is where most of the useful structural information lives.
Reading the Descriptor Hierarchy
When a USB device is first seen by the host, it responds to a series of GET_DESCRIPTOR control transfers on endpoint zero. The hierarchy is: device descriptor (VID, PID, USB version, device class), configuration descriptor (power budget, number of interfaces), interface descriptors (one per functional unit, each with class codes), and endpoint descriptors (transfer type, direction, max packet size, polling interval).
In Wireshark, this display filter shows all GET_DESCRIPTOR requests and their responses:
usb.bRequest == 0x06
The interface class field (bInterfaceClass) is the most informative single byte for understanding what you are dealing with. Class 0x03 is HID, 0x08 is Mass Storage, 0x0E is video, and 0xFF is vendor-specific. Class 0xFF means the device defines its own protocol and provides no standard way to discover its format.
For HID devices (class 0x03), the standard requires an additional report descriptor: a compact TLV-encoded byte array specifying the exact binary format of every data report the device sends and receives. Wireshark captures it automatically as part of enumeration, and the usbhid dissector decodes subsequent interrupt transfers against it, labeling each field.
You can extract the report descriptor independently with usbhid-dump:
sudo usbhid-dump -a 3:7 -e descriptor
Paste the hex output into the online HID descriptor parser to get a readable decode. A minimal mouse report descriptor looks like this decoded:
Usage Page (Generic Desktop), Usage (Mouse)
Collection (Application)
Usage Page (Buttons)
Report Count (3), Report Size (1)
Input (Data, Variable, Abs) ← 3 button bits
Report Count (1), Report Size (5)
Input (Constant) ← 5 padding bits
Usage (X), Usage (Y)
Logical Minimum (-127), Logical Maximum (127)
Report Size (8), Report Count (2)
Input (Data, Variable, Rel) ← X/Y relative axes
End Collection
From this point every byte in every report is labeled. No further reverse engineering is needed for a standard HID device.
Vendor Protocols
Devices with bInterfaceClass == 0xFF require a different approach. There is no report descriptor, no standard field definitions. The methodology is systematic correlation: capture all interrupt or bulk IN traffic, perform a known physical action (press a button, move a slider, change a setting), and find which bytes change in response.
Wireshark is useful for initial exploration. A short Python script using PyUSB is more practical for continuous correlation sessions:
import usb.core, time
dev = usb.core.find(idVendor=0x1234, idProduct=0x5678)
dev.set_configuration()
ep = dev[0][(0, 0)][0] # first endpoint of first interface
prev = None
while True:
data = bytes(ep.read(64, timeout=100))
if data != prev:
print(f"{time.time():.3f}: {data.hex()}")
prev = data
Run this and interact with the device methodically: each button in sequence, each axis, each state toggle. Record which byte at which position changes and by how much. For most consumer peripherals the full protocol maps out in a few hours.
tshark handles batch export from saved captures:
tshark -r capture.pcapng \
-Y "usb.transfer_type==1 && usb.endpoint_address.direction==1" \
-T fields -e frame.number -e usb.capdata \
-E separator=, > reports.csv
From Protocol Map to Driver
Once the protocol is documented, libusb provides the fastest path to a working userspace driver. It is cross-platform, requires no kernel changes, and covers all four USB transfer types. For interrupt-based devices:
libusb_interrupt_transfer(
handle,
0x81, // EP 0x81: IN endpoint 1
data,
sizeof(data),
&transferred,
5000 // timeout ms
);
For HID devices specifically, hidapi is a thinner cross-platform wrapper that handles platform-specific HID access: hidraw on Linux, IOHIDDevice on macOS, SetupAPI on Windows. Either library sidesteps the need to write a kernel module, which is the right call for most reverse engineering projects where the goal is a working tool rather than upstreaming a driver.
What This Methodology Has Produced
The technique has a substantial track record. The xpad driver for Xbox 360 controllers was built from usbmon captures correlating button presses with bulk transfer bytes. The controller uses a custom non-HID protocol that had to be discovered entirely through observation. Logitech’s HID++ extension protocol over Unifying Receivers was reverse-engineered the same way, producing the Solaar device manager. OpenRGB documented USB lighting control protocols for hundreds of devices from a dozen manufacturers using USBPcap captures of vendor Windows software; the result runs entirely without proprietary dependencies and has become the de-facto open source RGB controller.
The methodology works because most proprietary USB protocols rely on obscurity rather than cryptography. A device sending 8 bytes every 10ms over an interrupt endpoint cannot conceal what those bytes mean from systematic observation.
Limits of the Approach
The URB layer has real blind spots. Isochronous transfers for audio and video carry timing information that the kernel reduces to simple timestamps; the payload is visible but the synchronization feedback mechanism is not. Some DRM dongles and gaming peripherals encrypt their control channel, though this remains uncommon. Any analysis requiring visibility into packet-level retransmissions or physical bus errors requires hardware access to the wire rather than the kernel’s view of it.
For most protocol reverse engineering work these limits do not apply. The Wireshark USB capture wiki covers practical setup well, and USB in a NutShell at Beyond Logic remains the clearest introduction to the protocol fundamentals despite being written against USB 2.0 specifications. The USB-IF also publishes the full USB 2.0 specification and HID class specification freely, which are worth reading once the initial captures have given you enough context to make the specs legible.