From 161cc751320d8973ed03a0f77893a9508c5ba918 Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 23 Oct 2024 19:48:34 +0100 Subject: [PATCH 1/3] Go via add_interface() in USBConfiguration.from_binary_descriptor(). Fixes interface.parent not being set to the configuration. --- facedancer/configuration.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/facedancer/configuration.py b/facedancer/configuration.py index 31580bf..ef82d31 100644 --- a/facedancer/configuration.py +++ b/facedancer/configuration.py @@ -64,21 +64,22 @@ def from_binary_descriptor(cls, data): descriptor_type, total_length, num_interfaces, index, string_index, \ attributes, max_power = struct.unpack_from('> 6) & 1, supports_remote_wakeup=(attributes >> 5) & 1, - interfaces=interfaces, ) + # Extract the subordinate descriptors, and parse them. + interfaces = cls._parse_subordinate_descriptors(data[length:total_length]) + + for interface in interfaces: + configuration.add_interface(interface) + + return configuration + @classmethod def _parse_subordinate_descriptors(cls, data): From aa9014bb7231dac3ed00e725ee210f52d8f3e32b Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Wed, 23 Oct 2024 19:50:08 +0100 Subject: [PATCH 2/3] Index interfaces in a configuration by (number, alternate). Fixes interfaces being overwritten by alternate settings. --- facedancer/configuration.py | 4 ++-- facedancer/interface.py | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/facedancer/configuration.py b/facedancer/configuration.py index ef82d31..f1e77bf 100644 --- a/facedancer/configuration.py +++ b/facedancer/configuration.py @@ -145,7 +145,7 @@ def get_device(self): def add_interface(self, interface: USBInterface): """ Adds an interface to the configuration. """ - self.interfaces[interface.number] = interface + self.interfaces[interface.get_identifier()] = interface interface.parent = self @@ -247,7 +247,7 @@ def get_descriptor(self) -> bytes: # FIXME: use construct # All all subordinate descriptors together to create a big subordinate descriptor. - interfaces = sorted(self.interfaces.values(), key=lambda item: item.number) + interfaces = sorted(self.interfaces.values(), key=lambda item: item.get_identifier()) for interface in interfaces: interface_descriptors += interface.get_descriptor() diff --git a/facedancer/interface.py b/facedancer/interface.py index 1e05280..742da3f 100644 --- a/facedancer/interface.py +++ b/facedancer/interface.py @@ -262,8 +262,17 @@ def handle_set_interface_request(self, request: USBControlRequest): # Automatic instantiation support. # - def get_identifier(self) -> int: - return self.number + def get_identifier(self) -> (int, int): + return (self.number, self.alternate) + + + # Although we identify interfaces by (number, alternate), this helper + # is called from the request handling code, where we only want to + # match by interface number. The correct alternate interface should have + # been selected earlier in the request handling process. + def matches_identifier(self, other: int) -> bool: + return (other == self.number) + # # Request handler functions. From f319d0677fd3d882e838527c9b788c6f7d2f9c8b Mon Sep 17 00:00:00 2001 From: Martin Ling Date: Thu, 24 Oct 2024 12:47:18 +0100 Subject: [PATCH 3/3] Support switching between alternate interface settings. --- facedancer/configuration.py | 8 ++++---- facedancer/device.py | 36 +++++++++++++++++++++++++++--------- facedancer/interface.py | 18 ++++++++++++------ 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/facedancer/configuration.py b/facedancer/configuration.py index f1e77bf..165eaf9 100644 --- a/facedancer/configuration.py +++ b/facedancer/configuration.py @@ -158,7 +158,7 @@ def get_endpoint(self, number: int, direction: USBDirection) -> USBEndpoint: """ # Search each of our interfaces for the relevant endpoint. - for interface in self.interfaces.values(): + for interface in self.active_interfaces.values(): endpoint = interface.get_endpoint(number, direction) if endpoint is not None: return endpoint @@ -185,7 +185,7 @@ def handle_data_received(self, endpoint: USBEndpoint, data: bytes): data : The raw bytes received on the relevant endpoint. """ - for interface in self.interfaces.values(): + for interface in self.active_interfaces.values(): if interface.has_endpoint(endpoint.number, direction=USBDirection.OUT): interface.handle_data_received(endpoint, data) return @@ -205,7 +205,7 @@ def handle_data_requested(self, endpoint: USBEndpoint): endpoint : The endpoint on which the host requested data. """ - for interface in self.interfaces.values(): + for interface in self.active_interfaces.values(): if interface.has_endpoint(endpoint.number, direction=USBDirection.IN): interface.handle_data_requested(endpoint) return @@ -224,7 +224,7 @@ def handle_buffer_empty(self, endpoint: USBEndpoint): This function is called only once per buffer. """ - for interface in self.interfaces.values(): + for interface in self.active_interfaces.values(): if interface.has_endpoint(endpoint.number, direction=USBDirection.IN): interface.handle_buffer_empty(endpoint) return diff --git a/facedancer/device.py b/facedancer/device.py index 5cc6698..37f0170 100644 --- a/facedancer/device.py +++ b/facedancer/device.py @@ -887,6 +887,15 @@ def handle_set_configuration_request(self, request): else: try: self.configuration = self.configurations[request.value] + + # On a configuration change, all interfaces revert + # to alternate setting 0. + self.configuration.active_interfaces = { + interface.number: interface + for interface in self.configuration.get_interfaces() + if interface.alternate == 0 + } + request.acknowledge() except KeyError: request.stall() @@ -902,11 +911,13 @@ def handle_get_interface_request(self, request): """ Handle GET_INTERFACE requests; per USB2 [9.4.4] """ log.debug("received GET_INTERFACE request") - # TODO: support alternate interfaces. - # Since we don't support alternate interfaces [yet], we'll always - # indicate use of interface zero. - if self.configuration and (request.index_low in self.configuration.interfaces): - request.reply(b'\x00') + if self.configuration: + try: + number = request.index_low + interface = self.configuration.active_interfaces[number] + request.reply(bytes([interface.alternate])) + except KeyError: + request.stall() else: request.stall() @@ -917,10 +928,17 @@ def handle_set_interface_request(self, request): """ Handle SET_INTERFACE requests; per USB2 [9.4.10] """ log.debug(f"f{self.name} received SET_INTERFACE request") - # We don't support alternate interface settings; so ACK - # alternate setting zero, and stall all others. - if request.value == 0: - request.acknowledge() + if self.configuration: + try: + # Find this alternate setting and switch to it. + number = request.index_low + alternate = request.value + identifier = (number, alternate) + interface = self.configuration.interfaces[identifier] + self.configuration.active_interfaces[number] = interface + request.acknowledge() + except KeyError: + request.stall() else: request.stall() diff --git a/facedancer/interface.py b/facedancer/interface.py index 742da3f..c4166a0 100644 --- a/facedancer/interface.py +++ b/facedancer/interface.py @@ -243,17 +243,23 @@ def get_descriptor(self) -> bytes: # Alternate interface support. # - # TODO: support these! - @standard_request_handler(number=USBStandardRequests.SET_INTERFACE) @to_this_interface def handle_set_interface_request(self, request: USBControlRequest): """ Handle SET_INTERFACE requests; per USB2 [9.4.10] """ log.debug(f"f{self.name} received SET_INTERFACE request") - # We don't support alternate interface settings; so ACK - # alternate setting zero, and stall all others. - if request.value == 0: - request.acknowledge() + + if self.parent: + try: + # Find this alternate setting and switch to it. + number = request.index_low + alternate = request.value + identifier = (number, alternate) + interface = self.parent.interfaces[identifier] + self.parent.active_interfaces[number] = interface + request.acknowledge() + except KeyError: + request.stall() else: request.stall()