Awesome
Raw Gadget
Raw Gadget is a Linux kernel module that implements a low-level interface for the Linux USB Gadget subsystem.
Raw Gadget can be used to emulate USB devices, both physical and virtual ones. Emulating physical devices requires a Linux-based board with a USB Device Controller (UDC), such as a Raspberry Pi. Emulating virtual devices requires no hardware and instead relies on the Dummy HCD/UDC module (such devices get connected to the kernel Raw Gadget is running on).
This repository contains instructions, examples, and tests for Raw Gadget. In addition, this repository hosts a copy of the Dummy HCD/UDC kernel module for out-of-tree building.
See the Fuzzing USB with Raw Gadget talk [video] for details about the Linux Host and Gadget USB subsystems and Raw Gadget.
Note: Do not use Raw Gadget in production for emulating USB devices with specific classes. Instead, use the Composite Framework or the legacy gadget driver modules.
Comparison to other interfaces
The Linux kernel provides a number of interfaces for the USB Gadget subsystem that allow emulating USB devices. Most notably there is the Composite Framework (including the FunctionFS-based composite function) and the legacy gadget drivers modules (including GadgetFS).
Most of the Gadset subsystem interfaces (with the exception of GadgetFS and the FunctionFS-based composite function) only allow emulating USB devices of specific classes. Compared to them, Raw Gadget allows emulating USB devices of arbitary classes.
GadgetFS and the FunctionFS-based composite function do allow emulating USB devices of arbitrary classes. However, these interfaces allow only a limited control over the responses to some USB requests, as they perform sanity checks on the responses provided from userspace. This limits their ability of emulating improper USB devices, which might be useful for fuzzing or exploitation. Compared to them, Raw Gadget has minimal checks on the provided responses.
Raw Gadget is thus the perfect choice for fuzzing and exploiting USB hosts or for software proxying of USB devices.
You can find more details about the difference between Raw Gadget and GadgetFS in the kernel documentation.
Limitations
While Raw Gadget does support emulating a wide range of USB device types, it has a set of known limitations:
- Most notably, there is no support for USB 3 SuperSpeed device emulation (see https://github.com/xairy/raw-gadget/issues/61);
- Also see TODOs for a list of other more minor missing features.
These are not foundational limitations of the technology but rather just features missing from the implementation. They might addressed in the future.
Usage
Raw Gadget can be used on any Linux-based board that has a USB Device Controller (UDC) — a hardware component that allows the board to act as a USB peripheral device.
Consult the documentation for your board on whether it has a UDC (often marketed as USB OTG
) and how to enable it.
To set up Raw Gadget, you need to:
- Enable the UDC on your board;
- Find out the UDC device and driver names;
- Build and load the Raw Gadget module.
Once the setup is done, you can try running the provided examples.
See Raw Gadget on Raspberry Pi for end-to-end instructions on how to set up Raw Gadget on a Raspberry Pi board.
Building
There are two options of building Raw Gadget:
-
Rebuild the whole kernel with
CONFIG_USB_RAW_GADGET
enabled. This requires using a kernel version5.7+
, as Raw Gadget was merged into the mainline in5.7
(or backporting Raw Gadget patches to an older kernel); -
Build Raw Gadget as an out-of-tree kernel module without rebuilding the whole kernel; see raw_gadget (and dummy_hcd) for the instructions. Both modules should be compatible with kernel versions down to
4.14
; see the table below.
USB Device Controllers
Raw Gadget requires the user to provide the UDC device and driver names. This allows using Raw Gadget with a particular UDC if a few of them are present on the system.
UDC device names can be found in /sys/class/udc/
:
$ ls /sys/class/udc/
dummy_udc.0
The UDC driver name is usually present in /sys/class/udc/$UDC_DEVICE_NAME/uevent
:
$ cat /sys/class/udc/dummy_udc.0/uevent
USB_UDC_NAME=dummy_udc
Below is a table of hardware with various UDCs that was tested with Raw Gadget.
Hardware | Kernel | Driver | Device | Works? |
---|---|---|---|---|
Any | 5.3.0-45-generic | dummy_udc | dummy_udc.0 | Yes |
Raspberry Pi Zero | 4.14.97+ | 20980000.usb | 20980000.usb (dwc2 ) | Yes |
Raspberry Pi 4 | 5.10.63-v7l+ | fe980000.usb | fe980000.usb (dwc2 ) | Yes |
Raspberry Pi 5 | 6.8.0-1007-raspi | 1000480000.usb | 1000480000.usb (dwc2 ) | Likely |
USB Armory Mk II | 5.4.87-0 | 2184000.usb | ci_hdrc.0 | Yes |
Orange Pi PC | 5.10.60 | musb-hdrc | musb-hdrc.4.auto | Yes |
Orange Pi PC 2 | 5.10.60 | musb-hdrc | musb-hdrc.4.auto | Yes |
Khadas VIM1 | 5.10.60-meson64 | c9100000.usb | c9100000.usb | Yes |
ThinkPad X1 Carbon Gen 6 | 5.15.0-107-generic | dwc3-gadget | dwc3.1.auto | Yes |
NXP i.MX8MP | 6.6.23 | dwc3-gadget | 38100000.usb | Yes |
BeagleBone Black | 4.19.94-ti-r42 | musb-hdrc | musb-hdrc.0 | Probably |
BeagleBone AI | 4.14.108-ti-r131 | dwc3-gadget | 48890000.usb | Probably |
EC3380-AB | 5.3.0-45-generic | net2280 | 0000:04:00.0 (e.g.) | Partially,<br />net2280 buggy |
Odroid C2 | 3.14.79-116 | dwc_otg_pcd | dwc2_a | No, kernel too old |
"Works" in the table above means that the setup passed the provided tests. However, note that those only cover a subset of functionality.
syzkaller integration
Raw Gadget powers the syzkaller's ability to fuzz the Linux kernel USB stack.
See Running syzkaller USB reproducers for instructions on running syzkaller USB reproducers on a Linux-based board plugged into a physical USB host.
You also set up syzkaller in the isolated mode to fuzz physical USB hosts with the help of Raw Gadget. Instructions for this are not provided.
Facedancer backend
There's a prototype of a Facedancer backend based on Raw Gadget.
This backend relies on a few out-of-tree Raw Gadget patches present in the dev branch. Once the backend is thoroughly tested, these patches will be submitted to the mainline.
Raw Gadget–based backend accepts a few parameters through environment variables:
Parameter | Description | Default value |
---|---|---|
RG_UDC_DRIVER | UDC driver name | dummy_udc |
RG_UDC_DEVICE | UDC device name | dummy_udc.0 |
RG_USB_SPEED | USB device speed | 3 (High Speed) |
Example of using Facedancer with Raw Gadget to emulate a USB keyboard on a Raspberry Pi 4:
export BACKEND=rawgadget
export RG_UDC_DRIVER=fe980000.usb
export RG_UDC_DEVICE=fe980000.usb
./legacy-applets/facedancer-keyboard.py
Note: Some Facedancer examples might fail if a wrong USB speed is specified.
Failures happen either with EINVAL
in USB_RAW_IOCTL_EP_ENABLE
, with ESHUTDOWN
in USB_RAW_IOCTL_EP_READ/WRITE
, or can be completely random.
For example, with Dummy UDC, examples/ftdi-echo.py
requires RG_USB_SPEED=2
and legacy-applets/facedancer-ftdi.py
requires RG_USB_SPEED=3
.
In turn, legacy-applets/facedancer-umass.py
requires RG_USB_SPEED=2
.
Note: This backend is still a prototype. Outstanding tasks:
- Rebase the backend onto Facedancer 3.0 release;
- Make sure that all required backend callbacks are implemented. For example,
read_from_endpoint
should probably be implemented; - Provide a common Python wrapper for Raw Gadget ioctls, and use it in the backend;
- Finalize and submit out-of-tree Raw Gadget patches to the mainline.
Note: Facedancer assumes that every backend supports non-blocking I/O, which is not the case for Raw Gadget. To work around this limitation, the backend prototype relies on timeouts. The proper solution to this issue would be to add non-blocking I/O support to Raw Gadget.
Troubleshooting
As a generic guidance to troubleshooting Raw Gadget errors:
-
Consider switching to the dev branch.
This branch might contain fixes for some known issues;
-
Enable debug output for Raw Gadget (and Dummy HCD/UDC if you're using it).
To do this, add the following line to the very beginning of
raw_gadget/raw_gadget.c
:#define DEBUG
Then, rebuild and reinsert the Raw Gadget module;
-
Check the kernel log via
dmesg
to find out what is failing.
No such device
USB_RAW_IOCTL_RUN
returns ENODEV
, error code 19
:
ioctl(USB_RAW_IOCTL_RUN): No such device
This error means that bad UDC driver/device names were provided. Make sure that the UDC driver module is loaded. Also see USB Device Controllers about UDC names.
Cannot send after transport endpoint shutdown
Endpoint operations return ESHUTDOWN
, error code 108
:
ioctl(USB_RAW_IOCTL_EP0_WRITE): Cannot send after transport endpoint shutdown
This error means that the emulated USB device tried to perform a read or write operation on a disabled endpoint. This error can happen due to a variety of reasons, but it is commonly observed when the host decides to reset the device during its operation, following which the UDC driver disables all enabled endpoints.
Often, a reset happens when the device emulation code does something wrong. For example, provides a bad USB descriptor that is either malformed or inconsistent with the emulated device speed or other parameters. As a result, either the UDC driver or the host resets or disconnects the device.
However, a reset can happen during a normal device operation.
For example, the host might decide to reconfigure the device and thus will reset it.
The UDC driver will then deactivate all endpoints and any attempt to perform an endpoint operation will fail with ESHUTDOWN
.
When this happens, Raw Gadget will issue a USB_RAW_EVENT_RESET
event (or USB_RAW_EVENT_DISCONNECT
for dwc2
).
The device emulation code needs to gracefully handle ESHUTDOWN
, disable all Raw Gadget endpoints when hanlding the USB_RAW_EVENT_RESET
event, restart enumeration, and reenable the endpoints when handling a new SET_CONFIGURATION
request.
See the keyboard example for a reference implementation.
Projects based on Raw Gadget
Fuzzing-related:
- google/syzkaller — kernel fuzzer, uses Raw Gadget for fuzzing Linux kernel USB drivers;
- ReUSB: Replay-Guided USB Driver Fuzzing — research work on extending syzkaller's USB fuzzing capability by collecting corpus seeds from real devices.
Proxies:
- AristoChen/usb-proxy — USB proxy with injection support based on Raw Gadget and libusb;
- blegas78/usb-sniffify — another USB proxy with injection support based on Raw Gadget and libusb;
- patryk4815/usb-proxy — USB proxy based on Raw Gadget and written in Go;
- michaelforney/proxy.c — USB proxy used for reverse engineering some USB MIDI device;
- Mjollnirs/UsbMITMAttack — research project about using Raw Gadget on Raspberry Pi 4B to MitM USB devices.
Emulators:
- msawahara/me56ps2-emulator — emulator for ME56PS2 (PlayStation 2–compatible modem) [article];
- Berghopper/360-raw-gadget and Berghopper/360-w-raw-gadget — emulators for Xbox 360 contoller.
Other:
- blegas78/chaos — MitM tool running on Raspberry Pi that allows Twitch chat to mess around with the inputs from a Dualshock 4 Generation 2 controller.
TODO
License
The parts of code in this repository that are derived from the Linux kernel are covered by GPL-2.0. Everything else is covered by Apache-2.0. SPDX-License-Identifier
marks the used license in each file.