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.