def read_messages(self): """Read messages.""" from construct import Struct, PascalString, Int16ul, Tell, this, Bytes frmt = Struct( "instructions_length" / Int16ul, "instructions" / Bytes(this.instructions_length), "hints" / PascalString(Int16ul, "utf8"), "victory" / PascalString(Int16ul, "utf8"), "defeat" / PascalString(Int16ul, "utf8"), "history" / PascalString(Int16ul, "utf8"), "scouts" / PascalString(Int16ul, "utf8"), "offset" / Tell, ) parsing = frmt.parse(self.header[self.position:]) self.position += parsing.offset parsing.instructions = parsing.instructions.rstrip(b'\x00') parsing.hints = parsing.hints.rstrip('\x00') parsing.victory = parsing.victory.rstrip('\x00') parsing.defeat = parsing.defeat.rstrip('\x00') parsing.history = parsing.history.rstrip('\x00') parsing.scouts = parsing.scouts.rstrip('\x00') return dict(parsing)
def _iter_gnrl_files(self) -> Generator[ArchiveFile, None, None]: """Iterates over the parsed data for GNRL fiels and yields instances of `ArchiveFile`. Raises: ValueError: If a filename cannot be determined for a specific file record Yields: :class:`.ArchiveFile`: A file contained within the archive """ filename_offset = 0 for file_container in self.container.files: filepath_content = self.content[( self.container.header.names_offset + filename_offset):] filepath = PascalString(VarInt, "utf8").parse(filepath_content) # filename offset increased by length of parsed string accounting for # prefix and suffix bytes filename_offset += len(filepath) + 2 file_data = self.content[file_container.offset:( file_container.offset + file_container.unpacked_size)] if file_container.packed_size > 0: file_data = Compressed(GreedyBytes, "zlib").parse(file_data) yield ArchiveFile(filepath=PureWindowsPath(filepath[1:]), data=file_data)
def String(name): """ UTF-8 length-prefixed string. """ return PascalString(name, length_field=UBInt16("length"), encoding="utf-8")
def f(client: LedgerClient) -> DeviceResponse: response = client.apdu_exchange(INS, b"", P1, P2) response_template = Struct( p2pkh_prefix=Bytes(2), p2sh_prefix=Bytes(2), coin_family=Int8ub, coin_name=PascalString(Int8ub, "ascii"), coin_ticker=PascalString(Int8ub, "ascii"), ) parsed_response = response_template.parse(response) return DeviceResponse( p2pkh_prefix=parsed_response.p2pkh_prefix, p2sh_prefix=parsed_response.p2sh_prefix, coin_family=parsed_response.coin_family, coin_name=parsed_response.coin_name, coin_ticker=parsed_response.coin_ticker, )
def f(client: LedgerClient) -> DeviceResponse: response = client.apdu_exchange(INS, data, P1, P2) response_template = Struct( public_key=Prefixed(Int8ub, GreedyBytes), address=PascalString(Int8ub, "ascii"), chain_code=Bytes(32), ) parsed_response = response_template.parse(response) return DeviceResponse( public_key=parsed_response.public_key, address=parsed_response.address, chain_code=parsed_response.chain_code, )
def Label(name): ''' return a construct to parse/build a DNS label. Example: >> Label('qname').parse('\x05hello\x00') >> ['hello', ''] :param name: the name of the field :type name: string :returns: a construct to parse/build a DNS label :rtype: construct.core.RepeatUntil ''' return RepeatUntil( lambda obj, ctx: obj == '', PascalString(name), )
class InstantSoupData(object): # common server = Struct("server", CString("server_id"), PrefixedArray(CString('channels'), UBInt8("num_channels"))) # structures from rfc opt_client_nick = CString('nickname') opt_client_membership = PrefixedArray(server, UBInt8("num_servers")) opt_server = Struct("opt_server", UBInt16("port")) opt_server_channels = Struct( "opt_server_channels", PrefixedArray(CString("channels"), UBInt8("num_channels"))) opt_server_invite = Struct( "opt_server_invite", CString("channel_id"), PrefixedArray(CString("client_id"), UBInt8("num_clients"))) # option fields option = Struct( "option", Enum(UBInt8("option_id"), CLIENT_NICK_OPTION=0x01, CLIENT_MEMBERSHIP_OPTION=0x02, SERVER_OPTION=0x10, SERVER_CHANNELS_OPTION=0x11, SERVER_INVITE_OPTION=0x12), Switch( "option_data", lambda ctx: ctx["option_id"], { "CLIENT_NICK_OPTION": opt_client_nick, "CLIENT_MEMBERSHIP_OPTION": opt_client_membership, "SERVER_OPTION": opt_server, "SERVER_CHANNELS_OPTION": opt_server_channels, "SERVER_INVITE_OPTION": opt_server_invite })) # the peer pdu itself peer_pdu = Struct("peer_pdu", CString('id'), OptionalGreedyRange(option)) command = PascalString("command", length_field=UBInt32("length"), encoding='utf8')
def f(client: LedgerClient) -> DeviceResponse: response = client.apdu_exchange(INS, data, P1, P2) struct_kwargs = dict( public_key=Prefixed(Int8ub, GreedyBytes), address=PascalString(Int8ub, "ascii"), ) if opts.return_chain_code: struct_kwargs["chain_code"] = Bytes(32) response_template = Struct(**struct_kwargs) parsed_response = response_template.parse(response) return DeviceResponse( public_key=parsed_response.public_key, address=parsed_response.address, chain_code=parsed_response.chain_code if opts.return_chain_code else None, )
def _iter_dx10_files(self) -> Generator[ArchiveFile, None, None]: """Iterates over the parsed data for DX10 archives and yields instances of `ArchiveFile`. Raises: ValueError: If a filename cannot be determined for a specific file record Yields: :class:`.ArchiveFile`: A file contained within the archive """ filename_offset = 0 for file_container in self.container.files: filepath_content = self.content[( self.container.header.names_offset + filename_offset):] filepath = PascalString(Int16ul, "utf8").parse(filepath_content) filename_offset += len(filepath) + 2 (dds_header, dx10_header) = self._build_dds_headers(file_container) if dds_header: dds_content = b"DDS " dds_content += dds_header if dx10_header: dds_content += dx10_header for tex_chunk in file_container.chunks: if tex_chunk.packed_size > 0: dds_content += Compressed(GreedyBytes, "zlib").parse( self.content[tex_chunk.offset:( tex_chunk.offset + tex_chunk.packed_size)]) else: dds_content += self.content[tex_chunk.offset:( tex_chunk.offset + tex_chunk.unpacked_size)] yield ArchiveFile(filepath=PureWindowsPath(filepath), data=dds_content)
object_types[0x01] = Struct( "uno_" / Int8ul, "value" / Int8ul, ) # float double object_types[0x02] = Struct( "what_" / Int8ul, "value" / Float64l, ) # string object_types[0x03] = Struct( "red_" / Int8ul, "blue_" / Int8ul, "value" / PascalString(Int8ul, "latin-1"), ) KeyValuePair = Struct( "bloom_" / Int8ul, "key" / PascalString(Int8ul, "latin-1"), "value" / SerializedObject, ) # mapping object_types[0x05] = Struct( "monkey_" / Int8ul, # Usually 0 "items" / PrefixedArray(Int32ul, KeyValuePair), ) Version = Struct(
def test_parse(self): s = PascalString("foo") self.assertEqual(s.parse(b"\x05hello"), b"hello")
class ByteArray(FixedObjectByteArray): classID = 11 _construct = PascalString("value", length_field=UBInt32("length")) def __repr__(self): return '<%s(%i bytes)>' % (self.__class__.__name__, len(self.value))
class String(FixedObjectByteArray): classID = 9 _construct = PascalString("value", length_field=UBInt32("length"))
'event_code' / Enum( Int32ub, # bytes 12-15 VERSION=1, DATA_START=2, DATA_STOP=3, SENSOR_MAP=9, DATA_RATE=10), 'sending_node' / Int32ub, # bytes 16-19 # Message data is optional # Previous implementation used If(this._.payload_length > 8, ...), but this # method does not seem to work in v2.8. # message_length: bytes 20-23, message: bytes 24+ 'message' / Optional( PascalString(lengthfield='message_length' / Int32ub, encoding='ascii'))) EEG_data = Struct( 'timestamp' / Float32b, # bytes 12-15 'data_counter' / Int8ub, # byte 16; Unused, just 0 currently 'ADC_status' / Bytes(6), # bytes 17-22 # bytes 23-26, 27-30, etc. 'sensor_data' / Array((this._.payload_length - 11) // 4, Float32b)) null = Struct('none' / Array(111, Int8ub)) packet = Struct( Embedded(header), 'payload' / Embedded( Switch(this.type, { 'NULL': null, 'EVENT': event,
Struct( "prechunk", SBInt32("x"), SBInt32("z"), UBInt8("enabled"), ), 51: Struct( "chunk", SBInt32("x"), UBInt16("y"), SBInt32("z"), UBInt8("x_size"), UBInt8("y_size"), UBInt8("z_size"), PascalString("data", length_field=UBInt32("length"), encoding="zlib"), ), 52: Struct( "batch", SBInt32("x"), SBInt32("z"), UBInt16("length"), MetaArray(lambda context: context["length"], UBInt16("coords")), MetaArray(lambda context: context["length"], UBInt8("types")), MetaArray(lambda context: context["length"], UBInt8("metadata")), ), 53: Struct( "block", SBInt32("x"),
def test_parse_custom_length_field(self): s = PascalString("foo", length_field=UBInt16("length")) self.assertEqual(s.parse("\x00\x05hello"), "hello")
def test_build(self): s = PascalString("foo") self.assertEqual(s.build(b"hello world"), b"\x0bhello world")
def test_build(self): s = PascalString("foo", encoding="utf8") self.assertEqual(s.build(six.u("hello world")), six.b("\x0bhello world"))
def test_parse(self): s = PascalString("foo", encoding="utf8") self.assertEqual(s.parse(six.b("\x05hello")), six.u("hello"))
from __future__ import absolute_import, division, print_function, unicode_literals from construct import this, If, Switch, OneOf from construct import Struct from construct import Byte, Int8ub from construct import PascalString AuthRequest = Struct("version" / OneOf(Int8ub, [1]), "username" / PascalString(Byte), "password" / PascalString(Byte)) AuthResponse = Struct("version" / OneOf(Int8ub, [1]), "status" / Byte)
def test_build_custom_length_field(self): s = PascalString("foo", length_field=UBInt16("length")) self.assertEqual(s.build(b"hello"), b"\x00\x05hello")
def test_build_custom_length_field(self): s = PascalString("foo", length_field=UBInt16("length"), encoding="utf8") self.assertEqual(s.build(six.u("hello")), six.b("\x00\x05hello"))
"type" / DBFieldType, "value" / Switch( this.type, { "int8": Int8ub, "int16": Int16ub, "int32": Int32ub, "string": FocusedSeq( 0, PascalString(ExprAdapter(Int32ub, encoder=lambda obj, ctx: obj // 2 + 1, decoder=lambda obj, ctx: (obj - 1) * 2), encoding="utf-16-be"), Padding(2)), "binary": Prefixed(Int32ub, GreedyBytes) # parses to byte string })) class DBFieldFixedAdapter(Adapter): def __init__(self, subcon, ftype): self.ftype = ftype super().__init__(subcon) def _encode(self, obj, context): return {"type": self.ftype, "value": obj}
UnknownState=0x06, Disconnected=0x07) # not documented? ) # Name doesn't fit to one element, so there's an index I assume.. # >>> x = bytes.fromhex("43 51 01 00 0d 5965656c696768742042656473") # >>> x # b'CQ\x01\x00\rYeelight Beds' # >>> x = bytes.fromhex("43 51 01 01 08 696465204c616d700000000000") # >>> x # b'CQ\x01\x01\x08ide Lamp\x00\x00\x00\x00\x00' Name = "name" / Struct( "id" / Byte, "index" / Byte, # convert greeedystring to use this "text" / PascalString(Byte, "ascii"), ) Version = "version" / Struct( "currentrunning" / Enum(Byte, App1=0x01, App2=0x02, Candela=0x31), "hw_version" / Int16ub, "sw_version_app1" / Int16ub, "sw_version_app2" / Int16ub, "beacon_version" / Int16ub, ) SerialNumber = "serialno" / BytesInteger(12) OnOff = "OnOff" / Struct("state" / Mapping(Byte, {True: 0x01, False: 0x02})) # brightness max
Struct( "update sign", SBInt32("x"), UBInt16("y"), SBInt32("z"), AlphaString("line1"), AlphaString("line2"), AlphaString("line3"), AlphaString("line4"), ), 131: Struct( "item data", SBInt16("primary"), SBInt16("secondary"), PascalString("data", length_field=UBInt16("length")), ), 132: Struct( "update tile entity", SBInt32("x"), UBInt16("y"), SBInt32("z"), SBInt8("action"), SBInt16("size"), If(lambda context: context["size"] > 0, NBTdata("nbt", "size")), ), 200: Struct( "increment statistics", UBInt32("sid"),
) minidump_directory_entry = Struct( "StreamType" / Int32ul, "Location" / minidump_location_descriptor, # "Data" / Switch(this.StreamType, { # SystemInfoStream: Computed(lambda this: get_string(Pointer(this.Location.RVA, minidump_system_info))), # Memory64ListStream: Computed(lambda this: get_string(Pointer(this.Location.RVA, minidump_memory64_list))), # MemoryInfoListStream: Computed(lambda this: get_string(Pointer(this.Location.RVA, minidump_memory_info_list))), # ModuleListStream: Computed(lambda this: get_string(Pointer(this.Location.RVA, minidump_module_list))), # }) ) # TODO: More meaningful defaults MINIDUMP_HEADER = Struct( "Signature" / Const(MINIDUMP_SIGNATURE), "Version" / Const(MINIDUMP_VERSION), "VersionInternal" / Default(Int16ul, 0), "NumberOfStreams" / Int32ul, "StreamDirectoryRva" / Hex(Int32ul), "Checksum" / Default(Hex(Int32ul), 0), "TimeDateStamp" / Default(Int32ul, 0), "Flags" / Default(Hex(Int64ul), 0x421826), # "Directory" / Computed(lambda this: get_string(Pointer(this.StreamDirectoryRva, minidump_directory[this.NumberOfStreams]))) ) MINIDUMP_STRING = Struct( "String" / PascalString(Int32ul, "utf_16_le"), "ZeroTermination" / Const(b'\x00\x00') )
"vn" / Const(5, Byte), "method" / Byte ) def __init__(self, method): self.method = method def build(self): return self.SOCK_5_AUTH_RESPONSE.build(dict( method=self.method )) IP_4_Address = Int32ub IP_6_Address = Array(16, Byte) DOMAIN_NAME = PascalString(Byte, "ascii") class Atyp(object): """The address types supported by Socks5 """ IP_V4 = 1 DOMAIN_NAME = 3 IP_V6 = 4 class Command(object): """The available commands of Socks5 """ CONNECT = 1 BIND = 2
basemessage = Struct('base', snaptype, ULInt16('id'), ULInt16('refer'), Struct('sent', timestamp), Struct('recv', timestamp), ULInt32('payload_length'), ) mapmessage = Struct('map', ULInt16('num'), Array(lambda ctx: ctx.num, Struct('map', PascalString('field', length_field=ULInt16('length')), PascalString('value', length_field=ULInt16('length')) )) ) stringmessage = Struct('string', PascalString('string', length_field=ULInt16('string_length')) ) request = Struct('request', Struct('request_type', Embed(snaptype)) )
CipherSuites = Struct( "cipher_suites", UBInt16("length"), # TODO: Reject packets of length 0 Array(lambda ctx: ctx.length // 2, UBInt16("cipher_suites")), ) CompressionMethods = Struct( "compression_methods", UBInt8("length"), # TODO: Reject packets of length 0 Array(lambda ctx: ctx.length, UBInt8("compression_methods")), ) ServerName = Struct( "", UBInt8("type"), PascalString("name", length_field=UBInt16("length")), ) SNIExtension = Struct( "", TunnelAdapter( PascalString("server_names", length_field=UBInt16("length")), TunnelAdapter(PascalString("", length_field=UBInt16("length")), GreedyRange(ServerName)), ), ) ALPNExtension = Struct( "", TunnelAdapter( PascalString("alpn_protocols", length_field=UBInt16("length")),
def _encode(self, obj, context, path): out = list() elements = obj.split("/") if elements[0] == "m": elements = elements[1:] for element in elements: if element.endswith("'"): out.append(0x80000000 | int(element[:-1])) else: out.append(int(element)) return out Bip32Path = Bip32PathAdapter(PrefixedArray(Byte, Int32ub)) PrefixedString = PascalString(Asn1Length, "utf8") AppName = PrefixedString Version = PrefixedString Icon = Prefixed(Asn1Length, GreedyBytes) CURVE_SEPCK256K1 = 1 CURVE_PRIME256R1 = 2 CURVE_ED25519 = 4 Curve = FlagsEnum( Byte, secp256k1=CURVE_SEPCK256K1, prime256r1=CURVE_PRIME256R1, ed25519=CURVE_ED25519 ) DerivationPath = Prefixed( Asn1Length, Struct(curve=Curve, paths=Optional(GreedyRange(Bip32Path)))
LEAVE = 1 ERROR = 2 CHMOD = 3 CREATE = 4 GETATTR = 5 OPEN = 6 READ = 7 READDIR = 8 UNLINK = 9 WRITE = 10 TAKE = 11 MESSAGE = Struct( 'opcode' / BytesInteger(OPCODE_LEN), 'body' / PascalString(BytesInteger(LEN_FIELD_LEN), 'ascii') ) def read_message(socket): header = socket.recv(3) body = socket.recv(BytesInteger(4).parse(header[1:])) message = MESSAGE.parse(header + body) message['body'] = json.loads(message['body']) return message def write_message(socket, opcode, **kwargs): socket.send(MESSAGE.build(dict(opcode=opcode, body=json.dumps(kwargs)))) def message_addr(addr, opcode, **kwargs): sock = socket(AF_INET, SOCK_STREAM) sock.connect(addr)
"next_uid" / Int32ul, "constant" / Bytes(4), Array(16, "names" / String(256)), Array(16, "player_ids" / Int32ul), Array( 16, "player_data" / Struct( "active" / Int32ul, "human" / Int32ul, "civilization" / Int32ul, "constant" / Int32ul, # 0x04 0x00 0x00 0x00 )), Padding(5), "elapsed_time" / Float32l, "scenario_filename" / PascalString(lengthfield="scenario_filename_length" / Int16ul), If(lambda ctx: ctx._._.version == Version.DE, Padding(64))) # Scenarios have intro text, a bitmap, and cinematics. messages = "messages" / Struct( "instruction_id" / Int32ul, "hints_id" / Int32ul, "victory_id" / Int32ul, "defeat_id" / Int32ul, "history_id" / Int32ul, "scouts_id" / If(lambda ctx: ctx._._.version != Version.AOK, Int32ul), "instructions_length" / Int16ul, "instructions" / Bytes(lambda ctx: ctx.instructions_length), "hints" / PascalString(lengthfield="hints_length" / Int16ul), "victory" / PascalString(lengthfield="victory_length" / Int16ul), "defeat" / PascalString(lengthfield="defeat_length" / Int16ul),
"session_id" / Bytes(lambda ctx: ctx.length), ) CipherSuites = "cipher_suites" / Struct( "length" / Int16ub, # TODO: Reject packets of length 0 Array(lambda ctx: ctx.length // 2, "cipher_suites" / Int16ub), ) CompressionMethods = "compression_methods" / Struct( "length" / Int8ub, # TODO: Reject packets of length 0 Array(lambda ctx: ctx.length, "compression_methods" / Int8ub), ) ServerName = Struct( "type" / Int8ub, "name" / PascalString("length" / Int16ub), ) SNIExtension = Prefixed( Int16ub, Struct( Int16ub, "server_names" / GreedyRange("server_name" / Struct( "name_type" / Int8ub, "host_name" / PascalString("length" / Int16ub), )))) ALPNExtension = Prefixed( Int16ub, Struct( Int16ub, "alpn_protocols" / GreedyRange("name" / PascalString(Int8ub), ),
class Symbol(FixedObjectByteArray): classID = 10 _construct = PascalString("value", length_field=UBInt32("length")) def __repr__(self): return "Symbol(%r)" % self.value
garbage_args=4) RpcRejectStat = Enum(Int32ub, rpc_mismatch=0, auth_error=1) RpcAuthStat = Enum(Int32ub, badcred=0, rejectedcred=1, badverf=2, rejectedverf=3, tooweak=4) RpcAuthFlavor = Enum(Int32ub, null=0, unix=1, short=2, des=3) RpcAuthUnix = Struct( "stamp" / Int32ub, "machine_name" / Default(PascalString(Int32ub, encoding="ascii"), ""), "uid" / Default(Int32ub, 0), "gid" / Default(Int32ub, 0), "gids" / Default(Int32ub, 0) # should be length-prefixed array? ) RpcAuthShort = Pass RpcAuthDes = Pass RpcOpaqueAuth = Struct( "flavor" / Default(RpcAuthFlavor, "null"), Embedded( Prefixed( Int32ub, Switch( this.flavor, {
class UTF8(FixedObjectByteArray): classID = 14 _construct = PascalString("value", length_field=UBInt32("length"), encoding="utf8")
UBInt32('number'), # bytes 8-11 ) _event = Struct('embedded', Enum(UBInt32('event_code'), # bytes 12-15 VERSION=1, DATA_START=2, DATA_STOP=3, SENSOR_MAP=9, DATA_RATE=10 ), UBInt32('sending_node'), # byes 16-19 If(lambda ctx: ctx.payload_length > 8, # Message data is optional # message_length: bytes 20-23, message: bytes 24+ PascalString('message', length_field=UBInt32('message_length'), encoding='ascii') ) ) _EEG_data = Struct('embedded', BFloat32('timestamp'), # bytes 12-15 UBInt8('data_counter'), # byte 16; Unused, just 0 currently Field('ADC_status', 6), # bytes 17-22 Array(lambda ctx: (ctx.payload_length - 11)/4, BFloat32('sensor_data')) # bytes 23-26, 27-30, etc. ) _null = Struct('embedded', Array(111, UBInt8('none')) )