Using USB Proxy
Introduction
A major new feature of the newer Facedancer codebase is the ability to MITM (Meddler-In-The-Middle) USB connections – replacing the authors’ original USBProxy project. This opens up a whole new realm of applications – including protocol analysis and live manipulation of USB packets – and is especially useful when you don’t control the software running on the target device (e.g. on embedded systems or games consoles).
+-----------------------------------------------------------------------+
+------------+ | +--------------------------------+ +---------------------------+ | +--------------+
| | | | | | | | | |
| PROXIED | | | HOST COMPUTER | | FACEDANCER DEVICE | | | TARGET USB |
| DEVICE <------> running Facedancer software <---> acts as USB-Controlled <------> HOST |
| | | | | | USB Controller | | | |
| | | | | | | | | |
+------------+ | +--------------------------------+ +---------------------------+ | +--------------+
| |
| MITM Setup (HOST + FACEDANCER) |
+-----------------------------------------------------------------------+
The Simplest USB Proxy
The simplest use for USB Proxy is to transparently forward USB transactions between the host to the device and log them to the console.
7from facedancer import *
8from facedancer import main
9
10from facedancer.proxy import USBProxyDevice
11from facedancer.filters import USBProxySetupFilters, USBProxyPrettyPrintFilter
12
13# replace with the proxied device's information
14ID_VENDOR=0x09e8
15ID_PRODUCT=0x0031
16
17
18if __name__ == "__main__":
19 # create a USB Proxy Device
20 proxy = USBProxyDevice(idVendor=ID_VENDOR, idProduct=ID_PRODUCT)
21
22 # add a filter to forward control transfers between the target host and
23 # proxied device
24 proxy.add_filter(USBProxySetupFilters(proxy, verbose=0))
25
26 # add a filter to log USB transactions to the console
27 proxy.add_filter(USBProxyPrettyPrintFilter(verbose=5))
28
29 main(proxy)
Setting up a USB Proxy begins by creating an instance of the USBProxyDevice
with the vendor and product id’s of the proxied device as arguments.
The actual behaviour of USB Proxy is governed by adding filters
to the proxy that can intercept, read, modify and forward USB transactions between the host and device.
The first filter is a USBProxySetupFilters
which is a simple forwarding filter that ensures all control transfers are forwarded between the target host and the proxied device. Without the presence of this script the target host will detect your proxied device but all attempts at enumeration would fail.
The second filter is a USBProxyPrettyPrintFilter
which will intercept all transactions and then log them to the console.
Writing USB Proxy Filters
To write your own proxy filter you’d derive a new filter from USBProxyFilter
and override the request handlers for the transactions you want to intercept.
For example, a simple filter to intercept and modify data from a MIDI controller could look like this:
from facedancer.filters import USBProxyFilter
class MyFilter(USBProxyFilter):
# intercept the midi controllers IN endpoint
def filter_in(self, ep_num, data):
# check if the data is from the correct endpoint and a midi message
if ep_num == (0x82 & 0x7f) and len(data) == 4:
# check if it is a midi note-on/off message
if data[1] in [0x80, 0x90]:
# transpose the note up by an octave - 7f
data[2] += 12
# return the endpoint number and modified data
return ep_num, data
Which you can then add to the proxy using USBProxyDevice
’s add_filter()
method:
# add my filter to the proxy
proxy.add_filter(MyFilter())
You can find more information about the supported handlers in the USBProxyFilter
documentation.