""" Waits for the host to connect, and then says hello. """ logging.info("Waiting for the host to connect.") await device.wait_for_host() logging.info("Host connected!") logging.info("Telling the user hello...") device.transmit("Hello! Welcome to the FTDI demo.\n") device.transmit( "Enter any text you'd like, and we'll send it back in UPPERCASE.\n") def uppercasize(data): """ Convert any received data to uppercase. """ # Convert the data to uppercase... uppercase = data.decode('utf-8').upper() # ... convert serial line endings to Python line endings... uppercase = uppercase.replace('\r', '\n') # ... and transmit our response. device.transmit(uppercase) # Override the serial data handler by adding a singleton method on our object. # This is an easy way to create one-off objects. :) device.handle_serial_data_received = uppercasize main(device, send_hello())
import logging from facedancer import main from facedancer.devices.keyboard import USBKeyboardDevice from facedancer.classes.hid.keyboard import KeyboardModifiers device = USBKeyboardDevice() async def type_letters(): logging.info("Beginning message typing demo...") # Type ls. await asyncio.sleep(5) await device.type_letters('l', 's', '\n') # Echo hi. await asyncio.sleep(2) await device.type_string("echo hi, user\n") # Finally, try to pop calc, just for fun. logging.info("Bonus: trying to pop calc.") await device.type_string('r', modifiers=KeyboardModifiers.MOD_LEFT_META) await asyncio.sleep(0.5) await device.type_string('calc\n') logging.info("Typing complete. Idly handling USB requests.") main(device, type_letters())
# theoretically do our reverse engineering, and then rename the request. # # Because the decorator indicates to the backend that this is a vendor # request handler, these names can be whatever we'd like -- and we don't # have to update anything when we change them! # @vendor_request_handler(number=15, direction=USBDirection.IN) @to_device def handle_get_version_request(self, request): # Most recent request was for 255B of data. # When hackrf_info gets to this point, we can see that it's # failing with "hackrf_version_string_read() failed: Pipe error (-1000)." # # That's a pretty good hint of what it expects. request.reply(b"Sekret FaceDancer Version") @vendor_request_handler(number=18, direction=USBDirection.IN) @to_device def handle_get_serial_request(self, request): # Most recent request was for 24B of data. request.reply(b'A' * 24) # # There's one last thing to do -- we'll need to implement one more # simple request. We'll leave this last one as an exercise to the reader. :) # main(HackRF)
# on the request. request.ack() # Of course, if we want to let the host know we can't handle a request, we # may also choose to stall it. This is as simple as calling request.stall(). # # Note that request handlers can be used on configurations, interfaces, and # endpoints as well. For the latter two cases, the decorators `to_this_interface` # and `to_this_endpoint` are convenient -- they tell a request to run only if # it's directed at that endpoint in particular, as selected by its ``index`` parameter. # # FaceDancer ships with a default main() function that you can use to set up and run # your device. It ships with some nice features -- including a ``--suggest`` function # that can suggest pieces of boilerplate code that might be useful in device emulation. # # main() will accept either the type of device to emulate, or an device instance. # It'll also accept asyncio coroutines, in case you want to run things alongside the # relevant device code. See e.g. `examples/rubber-ducky.py` for an example. # main(TemplateDevice) # # Of course, this template looks verbose as heck. # For an example that's much less verbose, check out `examples/hackrf-info.py`. #
# The constructor arguments to each type accept the same fields as the declarative # API -- and like the declarative API, parameters have sane defaults... configuration = USBConfiguration() self.add_configuration(configuration) # ... which means we don't really need to do much to create the various components. interface = USBInterface() configuration.add_interface(interface) # Like the declarative APIs, endpoints require a number and direction. out_endpoint = USBEndpoint(number=3, direction=USBDirection.OUT) interface.add_endpoint(out_endpoint) # # We'll still use our request decorators to declare request handlers # on the relevant objects... # @vendor_request_handler(number=13) def handle_my_request(self, request): request.acknowledge() # # ... and callbacks continue to work the same way. # def handle_data_received(self, endpoint, data): logging.info(f"New data: {data} on {endpoint}.") main(ImperativeDevice())