class DescriptorField(construct.Subconstruct): """ Construct field definition that automatically adds fields of the proper size to Descriptor definitions. """ # # The C++-wonk operator overloading is Construct, not me, I swear. # # FIXME: these are really primitive views of these types; # we should extend these to get implicit parsing wherever possible USB_TYPES = { 'b' : construct.Optional(construct.Int8ul), 'bcd' : construct.Optional(construct.Int16ul), # Create a BCD parser for this 'i' : construct.Optional(construct.Int8ul), 'id' : construct.Optional(construct.Int16ul), 'bm' : construct.Optional(construct.Int8ul), 'w' : construct.Optional(construct.Int16ul), } @staticmethod def _get_prefix(name): """ Returns the lower-case prefix on a USB descriptor name. """ prefix = [] # Silly loop that continues until we find an uppercase letter. # You'd be aghast at how the 'pythonic' answers look. for c in name: # Ignore leading underscores. if c == '_': continue if c.isupper(): break prefix.append(c) return ''.join(prefix) @classmethod def _get_type_for_name(cls, name): """ Returns the type that's appropriate for a given descriptor field name. """ try: return cls.USB_TYPES[cls._get_prefix(name)] except KeyError: raise ValueError("field names must be formatted per the USB standard!") def __init__(self, description=""): self.description = description def __rtruediv__(self, field_name): field_type = self._get_type_for_name(field_name) # wew does construct make this look weird return (field_name / field_type) * self.description
class GetDeviceQualifierDescriptorRequest(GetDescriptorRequest): DESCRIPTOR_NAME = "device qualifier" BINARY_FORMAT = DescriptorFormat( "bLength" / DescriptorField("Length"), "bDescriptorType" / DescriptorNumber(6), "bcdUSB" / DescriptorField("USB Version"), "bDeviceClass" / DescriptorField("Class"), "bDeviceSubclass" / DescriptorField("Subclass"), "bDeviceProtocol" / DescriptorField("Protocol"), "bMaxPacketSize0" / DescriptorField("EP0 Max Pkt Size"), "bNumConfigurations" / DescriptorField("Configuration Count"), "_bReserevd" / construct.Optional(construct.Const(b"\0")))
def _apply_optional(subcon): subcon_type = cls._get_subcon_field_type(subcon) # # If it's already Optional then we don't need to apply it again. # if isinstance(subcon_type, construct.Select): # construct uses a weird singleton to define Pass. `construct.core.Pass` would normally be # the type's name, but then they create a singleton of that same name, replacing that name and # making the type technically unnamable and only accessable via `type()`. if isinstance(subcon_type.subcons[1], type(construct.Pass)): return subcon return (subcon.name / construct.Optional(subcon_type)) * subcon.docs
construct.Embed( construct.Struct('encrypted', construct.Bytes('data', lambda ctx: ctx.size - 1))))) VAULT_ATTRIBUTE = construct.Struct( 'VAULT_ATTRIBUTE', construct.ULInt32('id'), construct.ULInt32('attr_unknown_1'), construct.ULInt32('attr_unknown_2'), construct.ULInt32('attr_unknown_3'), # Ok, this is bad, but till now I have not understood how to distinguish # the different structs used. Actually the last ATTRIBUTE is different. # Usually we have 6 more bytes zeroed, not always aligned: otherwise, # if id >= 100, we have 4 more bytes: weird. construct.Optional( construct.Const(construct.Bytes('padding', 6), '\x00' * 6)), construct.If(lambda ctx: not ctx.padding and ctx.id >= 0x64, construct.ULInt32('attr_unknown_4')), construct.ULInt32('size'), construct.If(lambda ctx: ctx.size > 0, construct.Embed(VAULT_ATTRIBUTE_ENCRYPTED)), construct.Anchor('stream_end')) VAULT_ATTRIBUTE_EXTRA = construct.Struct('VAULT_ATTRIBUTE_EXTRA', construct.ULInt32('id'), construct.ULInt32('attr_unknown_1'), construct.ULInt32('attr_unknown_2'), construct.Embed(SIZED_DATA)) VAULT_ATTRIBUTE_MAP_ENTRY = construct.Struct( 'VAULT_ATTRIBUTE_MAP_ENTRY', construct.ULInt32('id'),
c.Terminated, ) FirmwareOne = c.Struct( "magic" / c.Const(b"TRZR"), "code_length" / c.Rebuild(c.Int32ul, c.len_(c.this.code)), "key_indexes" / c.Int8ul[V1_SIGNATURE_SLOTS], # pylint: disable=E1136 "flags" / c.BitStruct( c.Padding(7), "restore_storage" / c.Flag, ), "reserved" / c.Padding(52), "signatures" / c.Bytes(64)[V1_SIGNATURE_SLOTS], "code" / c.Bytes(c.this.code_length), c.Terminated, "embedded_onev2" / c.RestreamData(c.this.code, c.Optional(FirmwareOneV2)), ) # fmt: on class FirmwareFormat(Enum): TREZOR_ONE = 1 TREZOR_T = 2 TREZOR_ONE_V2 = 3 FirmwareType = NewType("FirmwareType", c.Container) ParsedFirmware = Tuple[FirmwareFormat, FirmwareType]
"DATA" / c.Array(c.this.NUM_SAMPLES, c.Int16ub)) SpeechCMODE = c.Struct("FIELD_ID" / c.Const(b'\x02'), "CMODE_IN" / ECMODE_IN) SpeechDCMODE = c.Struct("FIELD_ID" / c.Const(b'\x02'), "DCMODE_OUT" / DCMODE_OUT) SpeechTONE = c.Struct( "FIELD_ID" / c.Const(b'\x08'), "TONE_IDX" / c.Byte, "TONE_AMPLITUDE" / c.Byte, ) SpeechPCMPacket = c.Struct( "FIELD_ID" / c.Const(b'\x40'), "SPEECHD" / c.Optional(SpeechPCM), "CMODE" / c.Optional(SpeechCMODE), "TONE" / c.Optional(SpeechTONE), ) SpeechPCMResp = c.Struct( "FIELD_ID" / c.Const(b'\x00'), "NUM_SAMPLES" / c.Byte, # 156 - 164 "DATA" / c.Array(c.this.NUM_SAMPLES, c.Int16ub), "BYTES" / c.Computed(lambda this: struct.pack('H' * this.NUM_SAMPLES, *this.DATA)), "CMODE" / c.Optional(SpeechDCMODE)) ############################################################################### # Channel Messages
class Status(Enum): VALID = click.style("VALID", fg="green", bold=True) INVALID = click.style("INVALID", fg="red", bold=True) MISSING = click.style("MISSING", fg="blue", bold=True) DEVEL = click.style("DEVEL", fg="red", bold=True) def is_ok(self): return self is Status.VALID or self is Status.DEVEL VHASH_DEVEL = bytes.fromhex( "c5b4d40cb76911392122c8d1c277937e49c69b2aaf818001ec5c7663fcce258f") AnyFirmware = c.Struct( "vendor_header" / c.Optional(firmware.VendorHeader), "image" / c.Optional(firmware.FirmwareImage), ) class ImageType(Enum): VENDOR_HEADER = 0 BOOTLOADER = 1 FIRMWARE = 2 def _make_dev_keys(*key_bytes: bytes) -> List[bytes]: return [k * 32 for k in key_bytes] def compute_vhash(vendor_header):
def __init__(self, const): self.const = const super().__init__(c.Optional(c.Const(const)))
CONTROL_TYPE_TUNNEL = 0x04 CONTROL_TYPE_KEEPALIVE = 0x05 CONTROL_TYPE_PMTUD = 0x06 CONTROL_TYPE_PMTUD_ACK = 0x07 CONTROL_TYPE_REL_ACK = 0x08 # Reliable messages (0x80 - 0xFF) MASK_CONTROL_TYPE_RELIABLE = 0x80 CONTROL_TYPE_LIMIT = 0x80 # Prepare message PrepareMessage = cs.Struct( "prepare", cs.String("cookie", 8), cs.PascalString("uuid"), cs.Optional(cs.UBInt16("tunnel_id")), ) # Limit message LimitMessage = cs.Struct( "limit", # Limit type cs.UBInt8("type"), # Limit configuration cs.PascalString("data")) LIMIT_TYPE_BANDWIDTH_DOWN = 0x01 # Overhead of IP and UDP headers for measuring PMTU IPV4_HDR_OVERHEAD = 28
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; """ ImageSectionHeader = construct.Struct( "name" / construct.PaddedString(8, 'ascii'), "virtual_size" / construct.Int32ul, "virtual_address" / construct.Int32ul, "raw_data_size" / construct.Int32ul, "raw_data_ptr" / construct.Int32ul, "relocations_ptr" / construct.Int32ul, "linenumbers_ptr" / construct.Int32ul, "relocation_count" / construct.Int16ul, "linenumbers_count" / construct.Int16ul, "characteristics" / construct.Int32ul) ImageNtHeader = construct.Struct( "pe_magic" / construct.Const("PE\x00\x00"), "file_header" / ImageFileHeader, "optional_header" / construct.Optional(ImageOptionalHeader), ) def dos_stub(lfanew): return construct.Bytes(lfanew - ImageDosHeader.sizeof()) PeFile = construct.Struct( "dos_header" / ImageDosHeader, "dos_stub_program" / dos_stub(construct.this.dos_header.e_lfanew), "nt_header" / ImageNtHeader, "section_table" / construct.Array(construct.this.nt_header.file_header.section_count, ImageSectionHeader))
# maintain compatibility with LTPv3 in the kernel (first bit must be 1); also # the packet must be at least 12 bytes in length, otherwise some firewalls # may filter it when used over port 53 ControlMessage = cs.Struct("control", # Ensure that the first bit is 1 (L2TP control packet) cs.Const(cs.UBInt8("magic1"), 0x80), # Reduce conflict matching to other protocols as we run on port 53 cs.Const(cs.UBInt16("magic2"), 0x73A7), # Protocol version to allow future upgrades cs.UBInt8("version"), # Message type cs.UBInt8("type"), # Message data (with length prefix) cs.PascalString("data"), # Pad the message so it is at least 12 bytes long cs.Optional(cs.Padding(lambda ctx: max(0, 6 - len(ctx["data"])))), ) # Unreliable messages (0x00 - 0x7F) CONTROL_TYPE_COOKIE = 0x01 CONTROL_TYPE_PREPARE = 0x02 CONTROL_TYPE_ERROR = 0x03 CONTROL_TYPE_TUNNEL = 0x04 CONTROL_TYPE_KEEPALIVE = 0x05 CONTROL_TYPE_PMTUD = 0x06 CONTROL_TYPE_PMTUD_ACK = 0x07 CONTROL_TYPE_REL_ACK = 0x08 CONTROL_TYPE_PMTU_NTFY = 0x09 CONTROL_TYPE_USAGE = 0x0A # Error Reason Byte
C.IfThenElse(C.this.frameCount != 0, C.Pointer(C.this.dataOffset, C.Int32sl[4]), C.Int32sl[0]), ) def defaultMapIndices(l): return l + [0] * (4 - len(l)) StringHead = C.Struct("blank" / C.Int64sl, "stringOffset" / C.Int64sl, "type" / C.Int32sl) StringBody = C.Struct("string" / C.CString("utf-8"), ) StringData = C.Struct( "blank" / C.Int64sl, "stringOffset" / C.Int64sl, "type" / C.Int32sl, "string" / C.Pointer(C.this.stringOffset, C.CString("utf-8")), "padding" / C.Optional(C.Const(0, C.Int32sl))) UVSFile = C.Struct( "Header" / Header, "Groups" / C.Pointer(C.this.Header.groupOffset, Group[C.this.Header.groupCount]), "Strings" / C.Pointer(C.this.Header.stringOffset, StringData[C.this.Header.stringCount])) def pad(size, offset): cursor = offset return (-cursor) % size + offset def bpad(size, data): return data + b"\x00" * ((-len(data)) % size)
def __init__(self, const: bytes) -> None: self.const = const super().__init__(c.Optional(c.Const(const)))
"bEndpointAddress" / DescriptorField("Endpoint Address"), "bmAttributes" / DescriptorField("Attributes", default=2), "wMaxPacketSize" / DescriptorField("Maximum Packet Size", default=64), "bInterval" / DescriptorField("Polling interval", default=255), ) DeviceQualifierDescriptor = DescriptorFormat( "bLength" / construct.Const(9, construct.Int8ul), "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.DEVICE_QUALIFIER), "bcdUSB" / DescriptorField("USB Version"), "bDeviceClass" / DescriptorField("Class"), "bDeviceSubclass" / DescriptorField("Subclass"), "bDeviceProtocol" / DescriptorField("Protocol"), "bMaxPacketSize0" / DescriptorField("EP0 Max Pkt Size"), "bNumConfigurations" / DescriptorField("Configuration Count"), "_bReserved" / construct.Optional(construct.Const(b"\0"))) class DescriptorParserCases(unittest.TestCase): STRING_DESCRIPTOR = bytes([ 40, # Length 3, # Type ord('G'), 0x00, ord('r'), 0x00, ord('e'), 0x00, ord('a'), 0x00,
EndpointDescriptor = DescriptorFormat( # [USB2.0: 9.6; USB Audio Device Class Definition 1.0: 4.6.1.1, 4.6.2.1] # Interfaces of the Audio 1.0 class extend their subordinate endpoint descriptors with # 2 additional bytes (extending it from 7 to 9 bytes). Thankfully, this is the only extension that # changes the length of a standard descriptor type, but we do have to handle this case in Construct. "bLength" / construct.Default(construct.OneOf(construct.Int8ul, [7, 9]), 7), "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.ENDPOINT), "bEndpointAddress" / DescriptorField("Endpoint Address"), "bmAttributes" / DescriptorField("Attributes", default=2), "wMaxPacketSize" / DescriptorField("Maximum Packet Size", default=64), "bInterval" / DescriptorField("Polling interval", default=255), # 2 bytes that are only present on endpoint descriptors for Audio 1.0 class interfaces. ("bRefresh" / construct.Optional(construct.Int8ul)) * "Refresh Rate", ("bSynchAddress" / construct.Optional(construct.Int8ul)) * "Synch Endpoint Address", ) DeviceQualifierDescriptor = DescriptorFormat( "bLength" / construct.Const(9, construct.Int8ul), "bDescriptorType" / DescriptorNumber(StandardDescriptorNumbers.DEVICE_QUALIFIER), "bcdUSB" / DescriptorField("USB Version"), "bDeviceClass" / DescriptorField("Class"), "bDeviceSubclass" / DescriptorField("Subclass"), "bDeviceProtocol" / DescriptorField("Protocol"), "bMaxPacketSize0" / DescriptorField("EP0 Max Pkt Size"), "bNumConfigurations" / DescriptorField("Configuration Count"), "_bReserved" / construct.Optional(construct.Const(b"\0")))
construct.Embed( construct.Struct( 'encrypted', construct.ULInt32('iv_size'), construct.Bytes('iv', lambda ctx: ctx.iv_size), construct.Bytes('data', lambda ctx: ctx.size - 1 - 4 - ctx.iv_size))), construct.Embed( construct.Struct('encrypted', construct.Bytes('data', lambda ctx: ctx.size - 1))))) VAULT_ATTRIBUTE = construct.Struct( 'VAULT_ATTRIBUTE', construct.ULInt32('id'), construct.ULInt32('attr_unknown_1'), construct.ULInt32('attr_unknown_2'), construct.ULInt32('attr_unknown_3'), construct.Optional( construct.Const(construct.Bytes('padding', 6), '\x00' * 6)), construct.ULInt32('size'), construct.If(lambda ctx: ctx.size > 0, construct.Embed(VAULT_ATTRIBUTE_ENCRYPTED))) ''' VAULT_ATTRIBUTE_EX = construct.Struct( 'VAULT_ATTRIBUTE_EX', construct.ULInt32('id'), construct.ULInt32('attr_unknown_1'), construct.ULInt32('attr_unknown_2'), construct.ULInt32('attr_unknown_3'), construct.ULInt32('attr_unknown_4'), construct.Anchor('attr_stream_pos'), construct.If( lambda ctx: ctx.attr_stream_pos % 8, construct.Bytes('padding',