Facedancer

repository·main·Indexed 21 days ago

https://github.com/greatscottgadgets/facedancer

A Python-based framework for emulating USB devices and fuzzing USB host controllers. It provides fine-grained control over USB communications, including MITM (Meddler-In-The-Middle) capabilities for protocol analysis. Supported hardware backends include Cynthion, GreatFET, GoodFET, Raspdancer, and HydraDancer/HydraUSB3. The library allows users to implement custom USB devices using a hierarchical model of USBDevice, USBConfiguration, USBInterface, and USBEndpoint.

Tokens
19.2K
Snippets
68
Records
93
Agent score
76%

What's inside facedancer

  1. Overview of Facedancer Modules

    main

    The Facedancer library is organized into several functional categories:

    Device Emulation

    Contains example USB device classes and emulations:

    • facedancer.classes
    • facedancer.devices

    Hardware Backends

    Contains implementations for the various supported Facedancer hardware boards:

    • facedancer.backends

    Core and Support

    • facedancer.core: The Facedancer scheduler and execution core.
    • facedancer.errors: Error types.
    • facedancer.types: Type definitions and constants.
    • facedancer.logging: Logging boilerplate.
  2. How USB Proxy MITM works

    main

    Facedancer's USB Proxy allows for Meddler-In-The-Middle (MITM) attacks on USB connections. In this setup, the Facedancer device acts as a USB-controlled controller positioned between a Control Host (running Facedancer software) and a Target Host. This allows for protocol analysis and live manipulation of USB packets, even when the Target Host is an external system like a game console or embedded device.

    Architecture Model:

    1. Proxied USB Device <-> Control Host (Facedancer software)
    2. Control Host <-> Facedancer Device (USB Controller)
    3. Facedancer Device <-> Target Host
  3. Configure device interfaces and endpoints

    main

    Facedancer uses a hierarchical structure to define the USB topology. A USBDevice contains USBConfiguration objects, which contain USBInterface objects, which in turn contain USBEndpoint objects.

    When defining endpoints within an interface class, specify:

    • number: int (the endpoint address/number)
    • direction: USBDirection (e.g., USBDirection.IN or USBDirection.OUT)
    class MyConfiguration(USBConfiguration):
        class MyInterface(USBInterface):
            class MyInEndpoint(USBEndpoint):
                number    : int          = 1
                direction : USBDirection = USBDirection.IN
            class MyOutEndpoint(USBEndpoint):
                number    : int          = 1
                direction : USBDirection = USBDirection.OUT
  4. Understand the Core USB Device Model

    main

    The Facedancer library uses a hierarchical model to define USB devices, mirroring the standard USB specification. When building a custom device, you will interact with these components to define the device's structure and how it responds to host requests.

    Hierarchy and Components

    • USBDevice (facedancer.device): The root of the device. It manages the device's descriptors and marshals requests from the host.
    • USBConfiguration (facedancer.configuration): Manages the device's configuration descriptor(s).
    • USBInterface (facedancer.interface): Manages the device's interface descriptor(s).
    • USBEndpoint (facedancer.endpoint): Manages the device's endpoints.
    • USBControlRequest (facedancer.request): Manages USB control transfers.

    Supporting Modules for Device Definition

    • facedancer.descriptor: Provides utilities for working with USB descriptors.
    • facedancer.magic: Provides a declarative syntax for defining devices more easily.
  5. Hardware limitations and USBProxy-nv compatibility

    main

    MITM (USBProxy-nv) Compatibility

    To use the Meddler-In-The-Middle (MITM) feature for protocol analysis and live packet manipulation, you must use modern boards.

    • Supported: BACKEND=greatfet, BACKEND=hydradancer
    • Not Supported: BACKEND=goodfet or BACKEND=raspdancer (due to MAX3420/MAX3421 hardware restrictions).

    Endpoint and Host Mode Limitations

    • MAX3420/MAX3421 boards: Limited in the number and type of endpoints they can set up.
    • HydraDancer/HydraUSB3: Do not currently support host-mode.
  6. Set up a basic USB Proxy

    main

    To create a transparent USB proxy that forwards transactions and logs them to the console, follow these steps:

    1. Create an instance of facedancer.proxy.USBProxyDevice using the vendor and product IDs of the device you wish to proxy.
    2. Add facedancer.filters.standard.USBProxySetupFilters. This is required to ensure control transfers are forwarded; without it, enumeration will fail.
    3. Add facedancer.filters.logging.USBProxyPrettyPrintFilter to log transactions to the console.

    Note for macOS users: USBProxy must run as root to claim the device being proxied from the operating system.

    # Example of a basic proxy setup
    from facedancer.proxy import USBProxyDevice
    from facedancer.filters.standard import USBProxySetupFilters
    from facedancer.filters.logging import USBProxyPrettyPrintFilter
    
    proxy = USBProxyDevice(vendor_id=0xXXXX, product_id=0xXXXX)
    proxy.add_filter(USBProxySetupFilters())
    proxy.add_filter(USBProxyPrettyPrintFilter())
    # ... start proxy logic ...
  7. Use the Suggestion Engine to map undocumented devices

    main

    The Facedancer suggestion engine monitors control requests from the host that are not supported by your current emulation. When you exit the emulation, it provides code snippets for the missing handlers.

    To enable this feature, run your emulation script with the --suggest flag.

    python ./emulation.py --suggest
  8. How to write a new Facedancer Backend

    main

    To create a custom Facedancer board backend, you must implement a class that inherits from both FacedancerApp and FacedancerBackend. The process involves three main stages: deriving the class, implementing required callback methods, and implementing the backend event loop via service_irqs().

    Existing backend implementations can be found in the facedancer/backends/ directory for reference.

    from facedancer.core           import FacedancerApp
    from facedancer.backends.base  import FacedancerBackend
    
    class MydancerBackend(FacedancerApp, FacedancerBackend):
        app_name = "Mydancer"
  9. Implement the Facedancer backend event loop

    main

    Facedancer uses a polling approach to service events from the board. You must implement the service_irqs() method, which acts as the core execution loop.

    While service_irqs() is scheduled automatically by Facedancer, you are responsible for dispatching events generated by your board to the corresponding methods of the USBDevice object (which is obtained during the FacedancerBackend.connect() callback).

    Common events to handle include:

    • USB_RECEIVE_SETUP: Receiving a setup packet.
    • USB_RECEIVE_PACKET: Receiving data on an endpoint.
    • USB_EP_IN_NAK: Receiving NAK events (e.g., host requested data from an IN endpoint).

    Use the self.usb_device object to process these events.

    class MydancerBackend(FacedancerApp, FacedancerBackend):
        ...
    
        def service_irqs(self):
            """
            Core routine of the Facedancer execution/event loop. Continuously monitors the
            Moondancer's execution status, and reacts as events occur.
            """
    
            # obtain latest events and handle them
            for event in self.mydancer.get_events():
                match event:
                    case USB_RECEIVE_SETUP:
                        self.usb_device.create_request(event.data)
                    case USB_RECEIVE_PACKET:
                        self.usb_device.handle_data_available(event.endpoint_number, event.data)
                    case USB_EP_IN_NAK:
                        self.usb_device.handle_nak(event.endpoint_number)