def __init__(self, name, default, length=None, length_from=None, max_length=None, null_terminated=None): if null_terminated: self.null_terminated = null_terminated else: self.null_terminated = lambda pkt: True self.max_length = max_length or 200 StrFixedLenField.__init__(self, name, default, length=length, length_from=length_from)
def randval(self): if self.null_terminated: try: l = self.length_from(None) - 1 except: l = RandTermString(RandNum(0, self.max_length), "\x00") return RandBin(l) return StrFixedLenField.randval(self)
class SAPMSStat3(PacketNoPadded): """SAP Message Server Statistics version 3 Packet that contains statistics information of the Message Server service. This packet is for version 3. """ # TODO: The fields on this packet are not correctly defined name = "SAP Message Server Statistics version 3" fields_desc = [ ByteField("active", 0), ByteField("version", 0), ShortField("unused1", 0), IntField("no_requests", 0), StrFixedLenField("no_requests_padd", "", 12), IntField("no_error", 0), StrFixedLenField("no_error_padd", "", 4), IntField("no_login", 0), StrFixedLenField("no_login_padd", "", 4), IntField("no_logout", 0), StrFixedLenField("no_logout_padd", "", 4), IntField("no_send_by_name", 0), StrFixedLenField("no_send_by_name_padd", "", 4), IntField("no_send_by_type", 0), StrFixedLenField("no_send_by_type_padd", "", 4), IntField("no_adm_messages", 0), StrFixedLenField("no_adm_messages_padd", "", 4), IntField("no_adms", 0), StrFixedLenField("no_amds_padd", "", 4), StrFixedLenField("no_adm_type", "", 648), IntField("no_mod_types", 0), StrFixedLenField("no_mod_types_padd", "", 4), IntField("no_opcodes_rcvd", 0), StrFixedLenField("no_opcodes_rcvd_padd", "", 4), IntField("no_opcodes_send", 0), StrFixedLenField("no_opcodes_send_padd", "", 4), IntField("no_opcodes", 0), StrFixedLenField("no_opcode_type", "", 408), IntField("no_keepalive_send", 0), StrFixedLenField("no_keepalive_send_padd", "", 4), IntField("no_keepalive_rcvd", 0), StrFixedLenField("no_keepalive_rcvd_padd", "", 4), IntField("no_keepalive_disc", 0), StrFixedLenField("no_keepalive_disc_padd", "", 4), IntField("no_bytes_read", 0), StrFixedLenField("no_bytes_read_padd", "", 12), IntField("no_bytes_written", 0), StrFixedLenField("no_bytes_written_padd", "", 12), IntField("no_clients", 0), StrFixedLenField("sta_time", "", 30), StrFixedLenField("act_time", "", 30), ]
class SAPMS(Packet): """SAP Message Server packet This packet is used for the Message Server protocol. """ name = "SAP Message Server" fields_desc = [ StrFixedLenField("eyecatcher", "**MESSAGE**\x00", 12), ByteField("version", 0x04), ByteEnumKeysField("errorno", 0x00, ms_errorno_values), StrFixedLenField("toname", "-" + " " * 39, 40), FlagsField("msgtype", 0, 8, ["DIA", "UPD", "ENQ", "BTC", "SPO", "UP2", "ATP", "ICM"]), ByteField("reserved", 0x00), ByteEnumKeysField("domain", 0x00, ms_domain_values), ByteField("reserved", 0x00), StrFixedLenField("key", "\x00" * 8, 8), ByteEnumKeysField("flag", 0x01, ms_flag_values), ByteEnumKeysField("iflag", 0x01, ms_iflag_values), StrFixedLenField("fromname", "-" + " " * 39, 40), ConditionalField(ShortField("diag_port", 3200), lambda pkt: pkt.iflag == 0x08 and pkt.flag == 0x02 ), # for MS_REQUEST+MS_LOGIN_2 it's the diag port ConditionalField(ShortField("padd", 0x0000), lambda pkt: pkt.iflag != 0x08 or pkt.flag != 0x02), # OpCode fields ConditionalField( ByteEnumKeysField("opcode", 0x01, ms_opcode_values), lambda pkt: pkt.iflag in [0x00, 0x01, 0x02, 0x07] ), # extending all those fields with MS_SEND_TYPE and MS_SEND_TYPE_ONCE packets ConditionalField( ByteEnumKeysField("opcode_error", 0x00, ms_opcode_error_values), lambda pkt: pkt.iflag in [0x00, 0x01, 0x02, 0x7]), ConditionalField(ByteField("opcode_version", 0x01), lambda pkt: pkt.iflag in [0x00, 0x01, 0x02, 0x07]), ConditionalField(ByteField("opcode_charset", 0x03), lambda pkt: pkt.iflag in [0x00, 0x01, 0x02, 0x07]), ConditionalField( StrField("opcode_value", ""), lambda pkt: pkt.iflag in [0x00, 0x01] and pkt.opcode not in [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x11, 0x1c, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2f, 0x43, 0x44, 0x45, 0x46, 0x47, 0x4a ]), ConditionalField( StrField("opcode_trailer", ""), lambda pkt: pkt.iflag in [0x00, 0x01] and pkt.opcode not in [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x11, 0x1c, 0x1e, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2f, 0x43, 0x44, 0x45, 0x46, 0x47, 0x4a ]), # Dispatcher info ConditionalField( ByteField("dp_version", 0x0), lambda pkt: pkt.opcode == 0x0 or (pkt.opcode_version == 0x00 and pkt.opcode_charset == 0x00)), ConditionalField( PacketLenField("dp_info1", SAPDPInfo1(), SAPDPInfo1, length_from=lambda x: 507), lambda pkt: (pkt.opcode == 0x0 or (pkt.opcode_version == 0x00 and pkt.opcode_charset == 0x00 )) and pkt.dp_version == 0x0d), # 745 kernel ConditionalField( PacketLenField("dp_info2", SAPDPInfo2(), SAPDPInfo2, length_from=lambda x: 203), lambda pkt: (pkt.opcode == 0x0 or (pkt.opcode_version == 0x00 and pkt.opcode_charset == 0x00 )) and pkt.dp_version == 0x0b), # 720 kernel ConditionalField( PacketLenField("dp_info3", SAPDPInfo3(), SAPDPInfo3, length_from=lambda x: 179), lambda pkt: (pkt.opcode == 0x0 or (pkt.opcode_version == 0x00 and pkt.opcode_charset == 0x00 )) and pkt.dp_version == 0x0e), # 749 kernel # MS ADM layer ConditionalField( StrFixedLenField("adm_eyecatcher", "AD-EYECATCH\x00", 12), lambda pkt: pkt.iflag in [0x00, 0x02, 0x05, 0x07] or pkt.opcode == 0x0), ConditionalField( ByteField("adm_version", 0x01), lambda pkt: pkt.iflag in [0x00, 0x02, 0x05, 0x07] or pkt.opcode == 0x0), ConditionalField( ByteEnumKeysField("adm_type", 0x01, ms_adm_type_values), lambda pkt: pkt.iflag in [0x00, 0x02, 0x05, 0x07] or pkt.opcode == 0x0), ConditionalField( IntToStrField("adm_recsize", 104, 11), lambda pkt: pkt.iflag in [0x00, 0x02, 0x05, 0x07] or pkt.opcode == 0x0), ConditionalField( IntToStrField("adm_recno", 1, 11), lambda pkt: pkt.iflag in [0x00, 0x02, 0x05, 0x07] or pkt.opcode == 0x0), ConditionalField( PacketListField("adm_records", None, SAPMSAdmRecord), lambda pkt: pkt.iflag in [0x00, 0x02, 0x05, 0x07] or pkt.opcode == 0x0), # Server List fields ConditionalField( PacketListField("clients", None, SAPMSClient1), lambda pkt: pkt. opcode in [0x02, 0x03, 0x04, 0x05] and pkt.opcode_version == 0x01), ConditionalField( PacketListField("clients", None, SAPMSClient2), lambda pkt: pkt. opcode in [0x02, 0x03, 0x04, 0x05] and pkt.opcode_version == 0x02), ConditionalField( PacketListField("clients", None, SAPMSClient3), lambda pkt: pkt. opcode in [0x02, 0x03, 0x04, 0x05] and pkt.opcode_version == 0x03), ConditionalField( PacketListField("clients", None, SAPMSClient4), lambda pkt: pkt. opcode in [0x02, 0x03, 0x04, 0x05] and pkt.opcode_version == 0x04), # Change IP fields ConditionalField(IPField("change_ip_addressv4", "0.0.0.0"), lambda pkt: pkt.opcode == 0x06), ConditionalField( IP6Field("change_ip_addressv6", "::"), lambda pkt: pkt.opcode == 0x06 and pkt.opcode_version == 0x02), # Get/Set Text fields ConditionalField(StrFixedLenField("text_name", "", 40), lambda pkt: pkt.opcode in [0x22, 0x23]), ConditionalField( FieldLenField("text_length", None, length_of="text_value", fmt="!I"), lambda pkt: pkt.opcode in [0x22, 0x23]), ConditionalField( StrFixedLenField("text_value", "", length_from=lambda pkt: pkt.text_length or 80), lambda pkt: pkt.opcode in [0x22, 0x23]), # Counter fields ConditionalField( PacketField("counter", None, SAPMSCounter), lambda pkt: pkt.opcode in [0x24, 0x25, 0x26, 0x27, 0x28, 0x29]), ConditionalField(PacketListField("counters", None, SAPMSCounter), lambda pkt: pkt.opcode in [0x2a]), # Security Key 1 fields ConditionalField(StrFixedLenField("security_name", None, 40), lambda pkt: pkt.opcode in [0x07, 0x08]), ConditionalField(StrFixedLenField("security_key", None, 256), lambda pkt: pkt.opcode in [0x07, 0x08]), # Security Key 2 fields ConditionalField(IPField("security2_addressv4", "0.0.0.0"), lambda pkt: pkt.opcode == 0x09), ConditionalField(ShortField("security2_port", 0), lambda pkt: pkt.opcode == 0x09), ConditionalField(StrFixedLenField("security2_key", None, 256), lambda pkt: pkt.opcode == 0x09), ConditionalField(IP6Field("security2_addressv6", "::"), lambda pkt: pkt.opcode == 0x09), # Hardware ID field ConditionalField(StrNullFixedLenField("hwid", "", length=99), lambda pkt: pkt.opcode == 0x0a), # Statistics ConditionalField(PacketField("stats", None, SAPMSStat3), lambda pkt: pkt.opcode == 0x11 and pkt.flag == 0x03), # Codepage ConditionalField(IntField("codepage", 0), lambda pkt: pkt.opcode == 0x1c and pkt.flag == 0x03), # Dump Info Request fields ConditionalField(ByteField("dump_dest", 0x02), lambda pkt: pkt.opcode == 0x1E and pkt.flag == 0x02), ConditionalField(StrFixedLenField("dump_filler", "\x00\x00\x00", 3), lambda pkt: pkt.opcode == 0x1E and pkt.flag == 0x02), ConditionalField(ShortField("dump_index", 0x00), lambda pkt: pkt.opcode == 0x1E and pkt.flag == 0x02), ConditionalField( ShortEnumKeysField("dump_command", 0x01, ms_dump_command_values), lambda pkt: pkt.opcode == 0x1E and pkt.flag == 0x02), ConditionalField(StrFixedLenField("dump_name", "\x00" * 40, 40), lambda pkt: pkt.opcode == 0x1E and pkt.flag == 0x02), # File Reload fields ConditionalField( ByteEnumKeysField("file_reload", 0, ms_file_reload_values), lambda pkt: pkt.opcode == 0x1f), ConditionalField(StrFixedLenField("file_padding", "\x00\x00", 2), lambda pkt: pkt.opcode == 0x1f), # Get/Set/Del Logon fields ConditionalField(PacketField("logon", None, SAPMSLogon), lambda pkt: pkt.opcode in [0x2b, 0x2c, 0x2d]), # Server Disconnect/Shutdown fields ConditionalField(PacketField("shutdown_client", None, SAPMSClient3), lambda pkt: pkt.opcode in [0x2e, 0x2f, 0x30, 0x4a]), ConditionalField( FieldLenField("shutdown_reason_length", None, length_of="shutdown_reason", fmt="!H"), lambda pkt: pkt.opcode in [0x2e, 0x2f, 0x30, 0x4a]), ConditionalField( StrLenField("shutdown_reason", "", length_from=lambda pkt: pkt.shutdown_reason_length), lambda pkt: pkt.opcode in [0x2e, 0x2f, 0x30, 0x4a]), # Get/Set Property fields ConditionalField(PacketField("property", None, SAPMSProperty), lambda pkt: pkt.opcode in [0x43, 0x44, 0x45]), # IP/Port to name fields ConditionalField( IPField("ip_to_name_address4", "0.0.0.0"), lambda pkt: pkt.opcode == 0x46 and pkt.opcode_version == 0x01), ConditionalField( IP6Field("ip_to_name_address6", "::"), lambda pkt: pkt.opcode == 0x46 and pkt.opcode_version == 0x02), ConditionalField(ShortField("ip_to_name_port", 0), lambda pkt: pkt.opcode == 0x46), ConditionalField( FieldLenField("ip_to_name_length", None, length_of="ip_to_name", fmt="!I"), lambda pkt: pkt.opcode == 0x46), ConditionalField( StrLenField("ip_to_name", "", length_from=lambda pkt: pkt.logonname_length), lambda pkt: pkt.opcode == 0x46), # Check ACL fields ConditionalField(ShortField("error_code", 0), lambda pkt: pkt.opcode == 0x47), ConditionalField(StrFixedLenField("acl", "", 46), lambda pkt: pkt.opcode == 0x47), ]
class EncryptedPreMasterSecret(_GenericTLSSessionInheritance): """ Pay attention to implementation notes in section 7.4.7.1 of RFC 5246. """ name = "RSA Encrypted PreMaster Secret" fields_desc = [ _TLSClientVersionField("client_version", None, _tls_version), StrFixedLenField("random", None, 46) ] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if 'tls_session' in kargs: s = kargs['tls_session'] if s.server_tmp_rsa_key is None and s.server_rsa_key is None: return _UnEncryptedPreMasterSecret return EncryptedPreMasterSecret def pre_dissect(self, m): s = self.tls_session tbd = m tls_version = s.tls_version if tls_version is None: tls_version = s.advertised_tls_version if tls_version >= 0x0301: if len(m) < 2: # Should not happen return m tmp_len = struct.unpack("!H", m[:2])[0] if len(m) != tmp_len + 2: err = "TLS 1.0+, but RSA Encrypted PMS with no explicit length" warning(err) else: tbd = m[2:] if s.server_tmp_rsa_key is not None: # priority is given to the tmp_key, if there is one decrypted = s.server_tmp_rsa_key.decrypt(tbd) pms = decrypted[-48:] elif s.server_rsa_key is not None: decrypted = s.server_rsa_key.decrypt(tbd) pms = decrypted[-48:] else: # the dispatch_hook is supposed to prevent this case pms = b"\x00" * 48 err = "No server RSA key to decrypt Pre Master Secret. Skipping." warning(err) s.pre_master_secret = pms s.compute_ms_and_derive_keys() return pms def post_build(self, pkt, pay): """ We encrypt the premaster secret (the 48 bytes) with either the server certificate or the temporary RSA key provided in a server key exchange message. After that step, we add the 2 bytes to provide the length, as described in implementation notes at the end of section 7.4.7.1. """ enc = pkt s = self.tls_session s.pre_master_secret = enc s.compute_ms_and_derive_keys() if s.server_tmp_rsa_key is not None: enc = s.server_tmp_rsa_key.encrypt(pkt, t="pkcs") elif s.server_certs is not None and len(s.server_certs) > 0: enc = s.server_certs[0].encrypt(pkt, t="pkcs") else: warning("No material to encrypt Pre Master Secret") tmp_len = b"" tls_version = s.tls_version if tls_version is None: tls_version = s.advertised_tls_version if tls_version >= 0x0301: tmp_len = struct.pack("!H", len(enc)) return tmp_len + enc + pay def guess_payload_class(self, p): return Padding
class SAPRFC(Packet): """SAP Remote Function Call packet This packet is used for the Remote Function Call (RFC) protocol. """ name = "SAP Remote Function Call" fields_desc = [ ByteField( "version", 3 ), # If the version is 3, the packet has a size > 88h, versions 1 and 2 are 40h ByteEnumKeysField("req_type", 0, rfc_req_type_values), # Normal client fields (GW_NORMAL_CLIENT) ConditionalField(IPField("address", "0.0.0.0"), lambda pkt: pkt.req_type == 0x03), ConditionalField(IntField("padd1", 0), lambda pkt: pkt.req_type == 0x03), ConditionalField(StrFixedLenPaddedField("service", "", length=10), lambda pkt: pkt.req_type == 0x03), ConditionalField(StrFixedLenField("codepage", "1100", length=4), lambda pkt: pkt.req_type == 0x03), ConditionalField(StrFixedLenField("padd2", "\x00" * 6, length=6), lambda pkt: pkt.req_type == 0x03), ConditionalField(StrFixedLenPaddedField("lu", "", length=8), lambda pkt: pkt.req_type == 0x03), ConditionalField(StrFixedLenPaddedField("tp", "", length=8), lambda pkt: pkt.req_type == 0x03), ConditionalField( StrFixedLenPaddedField("conversation_id", "", length=8), lambda pkt: pkt.req_type == 0x03), ConditionalField(ByteField("appc_header_version", 6), lambda pkt: pkt.req_type == 0x03), ConditionalField(ByteField("accept_info", 0xcb), lambda pkt: pkt.req_type == 0x03), ConditionalField(SignedShortField("idx", -1), lambda pkt: pkt.req_type == 0x03), ConditionalField( IP6Field("address6", "::"), lambda pkt: pkt.req_type == 0x03 and pkt.version == 3), ConditionalField(IntField("rc", 0), lambda pkt: pkt.req_type == 0x03), ConditionalField(ByteField("echo_data", 0), lambda pkt: pkt.req_type == 0x03), ConditionalField(ByteField("filler", 0), lambda pkt: pkt.req_type == 0x03), # Monitor Command fields (GW_SEND_CMD) ConditionalField(ByteEnumKeysField("cmd", 0, rfc_monitor_cmd_values), lambda pkt: pkt.req_type == 0x09), # General padding for non implemented request types ConditionalField(StrFixedLenField("padd_v12", "\x00" * 61, length=61), lambda pkt: pkt.version < 3 and pkt.req_type == 0x09), ConditionalField( StrFixedLenField("padd_v12", "\x00" * 62, length=62), lambda pkt: pkt.version < 3 and pkt.req_type not in [0x03, 0x09]), ConditionalField( StrFixedLenField("padd_v3", "\x00" * 133, length=133), lambda pkt: pkt.version == 3 and pkt.req_type == 0x09), ConditionalField( StrFixedLenField("padd_v3", "\x00" * 134, length=134), lambda pkt: pkt.version == 3 and pkt.req_type not in [0x03, 0x09]), ]
class SAPCARArchiveFilev200Format(PacketNoPadded): """SAP CAR file information format This is ued to parse files inside a SAP CAR archive. """ name = "SAP CAR Archive File 2.00" version = SAPCAR_VERSION_200 is_filename_null_terminated = False fields_desc = [ StrFixedLenField("type", SAPCAR_TYPE_FILE, 2), LEIntField("perm_mode", 0), LELongField("file_length_low", 0), LEIntField("file_length_high", 0), LELongField("timestamp", 0), LEIntField("code_page", 0), FieldLenField("user_info_length", 0, length_of="user_info", fmt="<H"), FieldLenField("filename_length", 0, length_of="filename", fmt="<H"), StrNullFixedLenField( "filename", None, length_from=lambda x: x.filename_length, null_terminated=lambda x: x.is_filename_null_terminated), StrFixedLenField("user_info", None, length_from=lambda x: x.user_info_length), ConditionalField( PacketListStopField("blocks", None, SAPCARCompressedBlockFormat, stop=sapcar_is_last_block), lambda x: x.type == SAPCAR_TYPE_FILE and x.file_length > 0), ] @property def file_length(self): """Getter for the file length fields. It converts the two length fields (low and high) as provided in the archive file into a long long integer. """ return (self.file_length_high * SIZE_FOUR_GB) + self.file_length_low @file_length.setter def file_length(self, file_length): """Setter for the file length fields. It splits the long long integer int on the two length fields (low and high) as required by the archive file. """ self.file_length_low = file_length & 0xffffffff self.file_length_high = file_length >> 32 def extract(self, fd): """Extracts the archive file and writes the extracted file to the provided file object. Returns the checksum obtained from the archive. If blocks are uncompressed, the file is directly extracted. If the blocks are compressed, each block is added to a buffer, skipping the length field, and decompression is performed after the block marked as end of data. Expected length and compression header is obtained from the first block and checksum from the end of data block. :param fd: file-like object to write the extracted file to :type fd: file :return: checksum :rtype: int :raise DecompressError: If there's a decompression error :raise SAPCARInvalidFileException: If the file is invalid """ if self.file_length == 0: return 0 compressed = "" checksum = 0 exp_length = None remaining_length = self.file_length for block in self.blocks: # Process uncompressed block types if block.type in [ SAPCAR_BLOCK_TYPE_UNCOMPRESSED, SAPCAR_BLOCK_TYPE_UNCOMPRESSED_LAST ]: fd.write(block.compressed) remaining_length -= len(block.compressed) # Store compressed block types for later decompression elif block.type in [ SAPCAR_BLOCK_TYPE_COMPRESSED, SAPCAR_BLOCK_TYPE_COMPRESSED_LAST ]: # Add compressed block to a buffer, skipping the first 4 bytes of each block (uncompressed length) compressed += str(block.compressed)[4:] # If the expected length wasn't already set, do it if not exp_length: exp_length = block.compressed.uncompress_length else: raise SAPCARInvalidFileException("Invalid block type found") # Check end of data block, performing decompression if needed if sapcar_is_last_block(block): checksum = block.checksum # If there was at least one compressed block that set the expected length, decompress it if exp_length: (_, block_length, block_buffer) = decompress(str(compressed), exp_length) if block_length != exp_length or not block_buffer: raise DecompressError("Error decompressing block") fd.write(block_buffer) break return checksum
def i2repr(self, pkt, v): if self.null_terminated(pkt): if type(v) is str: v = v.rstrip("\0") return repr(v) return StrFixedLenField.i2repr(self, pkt, v)
def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, 6)
def __init__(self, name, default): StrFixedLenField.__init__(self, name, default, 32)
def addfield(self, pkt, s, val): l = self.length_from(pkt) val += self.padd * l return StrFixedLenField.addfield(self, pkt, s, val)
def __init__(self, name, default, length=None, length_from=None, padd=" "): StrFixedLenField.__init__(self, name, default, length, length_from) self.padd = padd
def __init__(self, name, default): return StrFixedLenField.__init__(self, name, default, length=60)
def addfield(self, pkt, s, val): if self.null_terminated(pkt): l = self.length_from(pkt) - 1 return s + struct.pack("%is" % l, self.i2m(pkt, val)) + "\x00" return StrFixedLenField.addfield(self, pkt, s, val)
def getfield(self, pkt, s): if self.null_terminated(pkt): l = self.length_from(pkt) - 1 return s[l + 1:], self.m2i(pkt, s[:l]) return StrFixedLenField.getfield(self, pkt, s)
class SAPDPInfo1(Packet): """SAP Dispatcher Info packet This packet is encapsulated inside SAPMS packet and before the MS ADM payload. Kernel 745 """ name = "SAP Dispatcher Info V1" fields_desc = [ ByteEnumKeysField("dp_req_prio", 0x1, dp_prio_values), IntField("dp_user_trace", 0x0), IntField("dp_req_len", 0x0), ShortField("dp_padd3", 0x0), ByteField("dp_padd4", 0x0), ByteEnumKeysField("dp_type_from", 0x2, dp_type_values), StrFixedLenField("dp_fromname", " " * 40, 40), ShortField("dp_padd41", 0x0), ByteField("dp_padd42", 0x0), ByteEnumKeysField("dp_agent_type_from", 0x6, dp_agent_type_values), ShortField("dp_padd43", 0x0), ByteField("dp_padd44", 0x0), ByteEnumKeysField("dp_worker_type_from", 0x1, dp_worker_type_values), IntField("dp_worker_from_num", 0x0), ShortField("dp_padd6", 0x0), ShortField("dp_padd7", 0x0), ByteField("dp_addr_from_t", 0xff), ShortField("dp_padd8", 0x0), ShortField("dp_addr_from_u", 0xffff), ByteField("dp_addr_from_m", 0xff), ByteField("dp_padd9", 0x1), IntField("dp_respid_from", 0x0), ShortField("dp_padd10", 0x0), ByteField("dp_padd11", 0x0), ByteEnumKeysField("dp_type_to", 0x2, dp_type_values), StrFixedLenField("dp_toname", " " * 40, 40), ShortField("dp_padd51", 0x0), ByteField("dp_padd52", 0x0), ByteEnumKeysField("dp_agent_type_to", 0x6, dp_agent_type_values), ShortField("dp_padd54", 0x0), ByteField("dp_padd55", 0x0), ByteEnumKeysField("dp_worker_type_to", 0x1, dp_worker_type_values), IntField("dp_worker_to_num", 0x0), ShortField("dp_padd01", 0), ShortField("dp_padd02", 0), ByteField("dp_addr_to_t", 0xff), ShortField("dp_padd03", 0x1), ShortField("dp_addr_to_u", 0xffff), ByteField("dp_addr_to_m", 0x0), ByteField("dp_padd21", 0x0), IntField("dp_respid_to", 0), ByteField("dp_padd22", 0), ShortField("dp_padd23", 0), ByteEnumKeysField("dp_req_handler", 40, dp_req_handler_values), IntField("dp_req_rc", 0x0), StrFixedLenField("dp_blob_padding", None, 224), Field("dp_blob_56", 6, '<L'), Field("dp_blob_57", 1, '<L'), Field("dp_blob_worker_from_num", 0, '<L'), ByteField("dp_blob_worker_type_from", 0), StrFixedLenField("dp_blob_62", "", 3), Field("dp_blob_addr_from_t", 0, '<L'), Field("dp_blob_addr_from_u", 0, '<L'), ByteField("dp_blob_worker_type_to", 0), StrFixedLenField("dp_blob_63", "", 3), Field("dp_blob_respid_from", 0, '<L'), StrFixedLenField("dp_blob_64", "", 3), StrFixedLenField("dp_blob_dst", "", 80), ByteField("dp_blob_xx", 0), StrFixedLenField("dp_blob_yy", "", 8), ]
class DoIP(Packet): """ Implementation of the DoIP (ISO 13400) protocol. DoIP packets can be sent via UDP and TCP. Depending on the payload type, the correct connection need to be chosen: +--------------+--------------------------------------------------------------+-----------------+ | Payload Type | Payload Type Name | Connection Kind | +--------------+--------------------------------------------------------------+-----------------+ | 0x0000 | Generic DoIP header negative acknowledge | UDP / TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0001 | Vehicle Identification request message | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0002 | Vehicle identification request message with EID | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0003 | Vehicle identification request message with VIN | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0004 | Vehicle announcement message/vehicle identification response | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0005 | Routing activation request | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0006 | Routing activation response | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0007 | Alive Check request | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x0008 | Alive Check response | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x4001 | IP entity status request | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x4002 | DoIP entity status response | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x4003 | Diagnostic power mode information request | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x4004 | Diagnostic power mode information response | UDP | +--------------+--------------------------------------------------------------+-----------------+ | 0x8001 | Diagnostic message | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x8002 | Diagnostic message positive acknowledgement | TCP | +--------------+--------------------------------------------------------------+-----------------+ | 0x8003 | Diagnostic message negative acknowledgement | TCP | +--------------+--------------------------------------------------------------+-----------------+ Example with UDP: >>> socket = L3RawSocket(iface="eth0") >>> resp = socket.sr1(IP(dst="169.254.117.238")/UDP(dport=13400)/DoIP(payload_type=1)) Example with TCP: >>> socket = DoIPSocket("169.254.117.238") >>> pkt = DoIP(payload_type=0x8001, source_address=0xe80, target_address=0x1000) / UDS() / UDS_RDBI(identifiers=[0x1000]) >>> resp = socket.sr1(pkt, timeout=1) Example with UDS: >>> socket = UDS_DoIPSocket("169.254.117.238") >>> pkt = UDS() / UDS_RDBI(identifiers=[0x1000]) >>> resp = socket.sr1(pkt, timeout=1) """ # noqa: E501 payload_types = { 0x0000: "Generic DoIP header NACK", 0x0001: "Vehicle identification request", 0x0002: "Vehicle identification request with EID", 0x0003: "Vehicle identification request with VIN", 0x0004: "Vehicle announcement message/vehicle identification response message", # noqa: E501 0x0005: "Routing activation request", 0x0006: "Routing activation response", 0x0007: "Alive check request", 0x0008: "Alive check response", 0x4001: "DoIP entity status request", 0x4002: "DoIP entity status response", 0x4003: "Diagnostic power mode information request", 0x4004: "Diagnostic power mode information response", 0x8001: "Diagnostic message", 0x8002: "Diagnostic message ACK", 0x8003: "Diagnostic message NACK" } name = 'DoIP' fields_desc = [ XByteField("protocol_version", 0x02), XByteField("inverse_version", 0xFD), XShortEnumField("payload_type", 0, payload_types), IntField("payload_length", None), ConditionalField( ByteEnumField( "nack", 0, { 0: "Incorrect pattern format", 1: "Unknown payload type", 2: "Message too large", 3: "Out of memory", 4: "Invalid payload length" }), lambda p: p.payload_type in [0x0]), ConditionalField(StrFixedLenField("vin", b"", 17), lambda p: p.payload_type in [3, 4]), ConditionalField(XShortField("logical_address", 0), lambda p: p.payload_type in [4]), ConditionalField(StrFixedLenField("eid", b"", 6), lambda p: p.payload_type in [2, 4]), ConditionalField(StrFixedLenField("gid", b"", 6), lambda p: p.payload_type in [4]), ConditionalField( XByteEnumField( "further_action", 0, { 0x00: "No further action required", 0x01: "Reserved by ISO 13400", 0x02: "Reserved by ISO 13400", 0x03: "Reserved by ISO 13400", 0x04: "Reserved by ISO 13400", 0x05: "Reserved by ISO 13400", 0x06: "Reserved by ISO 13400", 0x07: "Reserved by ISO 13400", 0x08: "Reserved by ISO 13400", 0x09: "Reserved by ISO 13400", 0x0a: "Reserved by ISO 13400", 0x0b: "Reserved by ISO 13400", 0x0c: "Reserved by ISO 13400", 0x0d: "Reserved by ISO 13400", 0x0e: "Reserved by ISO 13400", 0x0f: "Reserved by ISO 13400", 0x10: "Routing activation required to initiate central security", }), lambda p: p.payload_type in [4]), ConditionalField( XByteEnumField( "vin_gid_status", 0, { 0x00: "VIN and/or GID are synchronized", 0x01: "Reserved by ISO 13400", 0x02: "Reserved by ISO 13400", 0x03: "Reserved by ISO 13400", 0x04: "Reserved by ISO 13400", 0x05: "Reserved by ISO 13400", 0x06: "Reserved by ISO 13400", 0x07: "Reserved by ISO 13400", 0x08: "Reserved by ISO 13400", 0x09: "Reserved by ISO 13400", 0x0a: "Reserved by ISO 13400", 0x0b: "Reserved by ISO 13400", 0x0c: "Reserved by ISO 13400", 0x0d: "Reserved by ISO 13400", 0x0e: "Reserved by ISO 13400", 0x0f: "Reserved by ISO 13400", 0x10: "Incomplete: VIN and GID are NOT synchronized" }), lambda p: p.payload_type in [4]), ConditionalField( XShortField("source_address", 0), lambda p: p.payload_type in [5, 8, 0x8001, 0x8002, 0x8003]), # noqa: E501 ConditionalField( XByteEnumField("activation_type", 0, { 0: "Default", 1: "WWH-OBD", 0xe0: "Central security" }), lambda p: p.payload_type in [5]), ConditionalField(XShortField("logical_address_tester", 0), lambda p: p.payload_type in [6]), ConditionalField(XShortField("logical_address_doip_entity", 0), lambda p: p.payload_type in [6]), ConditionalField( XByteEnumField( "routing_activation_response", 0, { 0x00: "Routing activation denied due to unknown source address.", 0x01: "Routing activation denied because all concurrently supported TCP_DATA sockets are registered and active.", # noqa: E501 0x02: "Routing activation denied because an SA different from the table connection entry was received on the already activated TCP_DATA socket.", # noqa: E501 0x03: "Routing activation denied because the SA is already registered and active on a different TCP_DATA socket.", # noqa: E501 0x04: "Routing activation denied due to missing authentication.", 0x05: "Routing activation denied due to rejected confirmation.", 0x06: "Routing activation denied due to unsupported routing activation type.", # noqa: E501 0x07: "Reserved by ISO 13400.", 0x08: "Reserved by ISO 13400.", 0x09: "Reserved by ISO 13400.", 0x0a: "Reserved by ISO 13400.", 0x0b: "Reserved by ISO 13400.", 0x0c: "Reserved by ISO 13400.", 0x0d: "Reserved by ISO 13400.", 0x0e: "Reserved by ISO 13400.", 0x0f: "Reserved by ISO 13400.", 0x10: "Routing successfully activated.", 0x11: "Routing will be activated; confirmation required." }), lambda p: p.payload_type in [6]), ConditionalField(XIntField("reserved_iso", 0), lambda p: p.payload_type in [5, 6]), ConditionalField(XIntField("reserved_oem", 0), lambda p: p.payload_type in [5, 6]), ConditionalField( XByteEnumField("diagnostic_power_mode", 0, { 0: "not ready", 1: "ready", 2: "not supported" }), lambda p: p.payload_type in [0x4004]), ConditionalField( ByteEnumField("node_type", 0, { 0: "DoIP gateway", 1: "DoIP node" }), lambda p: p.payload_type in [0x4002]), ConditionalField(XByteField("max_open_sockets", 0), lambda p: p.payload_type in [0x4002]), ConditionalField(XByteField("cur_open_sockets", 0), lambda p: p.payload_type in [0x4002]), ConditionalField(IntField("max_data_size", 0), lambda p: p.payload_type in [0x4002]), ConditionalField(XShortField("target_address", 0), lambda p: p.payload_type in [0x8001, 0x8002, 0x8003 ]), # noqa: E501 ConditionalField(XByteEnumField("ack_code", 0, {0: "ACK"}), lambda p: p.payload_type in [0x8002]), ConditionalField( ByteEnumField( "nack_code", 0, { 0x00: "Reserved by ISO 13400", 0x01: "Reserved by ISO 13400", 0x02: "Invalid source address", 0x03: "Unknown target address", 0x04: "Diagnostic message too large", 0x05: "Out of memory", 0x06: "Target unreachable", 0x07: "Unknown network", 0x08: "Transport protocol error" }), lambda p: p.payload_type in [0x8003]), ] def answers(self, other): # type: (Packet) -> int """DEV: true if self is an answer from other""" if isinstance(other, type(self)): if self.payload_type == 0: return 1 matches = [(4, 1), (4, 2), (4, 3), (6, 5), (8, 7), (0x4002, 0x4001), (0x4004, 0x4003), (0x8001, 0x8001), (0x8003, 0x8001)] if (self.payload_type, other.payload_type) in matches: if self.payload_type == 0x8001: return self.payload.answers(other.payload) return 1 return 0 def hashret(self): # type: () -> bytes if self.payload_type in [0x8001, 0x8002, 0x8003]: return bytes(self)[:2] + struct.pack( "H", self.target_address ^ self.source_address) return bytes(self)[:2] def post_build(self, pkt, pay): # type: (bytes, bytes) -> bytes """ This will set the Field 'payload_length' to the correct value. """ if self.payload_length is None: pkt = pkt[:4] + struct.pack("!I", len(pay) + len(pkt) - 8) + \ pkt[8:] return pkt + pay def extract_padding(self, s): # type: (bytes) -> Tuple[bytes, Optional[bytes]] if self.payload_type == 0x8001: return s[:self.payload_length - 4], None else: return b"", None
class SkinnyMessageCallInfo(Packet): name = 'call information' fields_desc = [ StrFixedLenField("callername", "Jean Valjean", 40), StrFixedLenField("callernum", "1337", 24), StrFixedLenField("calledname", "Causette", 40), StrFixedLenField("callednum", "1034", 24), LEIntField("lineinstance", 1), LEIntField("callid", 0), StrFixedLenField("originalcalledname", "Causette", 40), StrFixedLenField("originalcallednum", "1034", 24), StrFixedLenField("lastredirectingname", "Causette", 40), StrFixedLenField("lastredirectingnum", "1034", 24), LEIntField("originalredirectreason", 0), LEIntField("lastredirectreason", 0), StrFixedLenField('voicemailboxG', b'\0' * 24, 24), StrFixedLenField('voicemailboxD', b'\0' * 24, 24), StrFixedLenField('originalvoicemailboxD', b'\0' * 24, 24), StrFixedLenField('lastvoicemailboxD', b'\0' * 24, 24), LEIntField('security', 0), FlagsField('restriction', 0, 16, _skinny_message_callinfo_restrictions), # noqa: E501 LEIntField('unknown', 0) ]
class SAPRouter(Packet): """SAP Router packet This packet is used for general SAP Router packets. There are (at least) five types of SAP Router packets: 1. Route packets. For requesting the routing of a connection to a remote hosts. The packet contains some general information and a connection string with a list of routing hops (:class:`SAPRouterRouteHop`). 2. Administration packets. This packet is used for the SAP Router to send administrative commands. It's suppose to be used only from the hosts running the SAP Router or when an specific route is included in the routing table. Generally administration packets are not accepted from the external binding. 3. Error Information packets. Packets sent when an error occurred. 4. Control Message packets. Used to perform some control activities, like retrieving the current SAPRouter version or to perform the SNC handshake. They have the same structure that error information packets. 5. Route accepted packet. Used to acknowledge a route request ("NI_PONG"). Routed packets and some responses doesn't fill in these five packet types. For identifying those cases, you should check the type using the function :class:`router_is_known_type`. NI Versions found (unconfirmed): - 30: Release 40C - 36: Release <6.20 - 38: Release 7.00/7.10 - 39: Release 7.11 - 40: Release 7.20/7.21 """ # Default router version to use SAPROUTER_DEFAULT_VERSION = 40 # Constants for router types SAPROUTER_ROUTE = "NI_ROUTE" """ :cvar: Constant for route packets :type: C{string} """ SAPROUTER_ADMIN = "ROUTER_ADM" """ :cvar: Constant for administration packets :type: C{string} """ SAPROUTER_ERROR = "NI_RTERR" """ :cvar: Constant for error information packets :type: C{string} """ SAPROUTER_CONTROL = "NI_RTERR" """ :cvar: Constant for control messages packets :type: C{string} """ SAPROUTER_PONG = "NI_PONG" """ :cvar: Constant for route accepted packets :type: C{string} """ router_type_values = [ SAPROUTER_ADMIN, SAPROUTER_ERROR, SAPROUTER_CONTROL, SAPROUTER_ROUTE, SAPROUTER_PONG, ] """ :cvar: List of known packet types :type: ``list`` of C{string} """ name = "SAP Router" fields_desc = [ # General fields present in all SAP Router packets StrNullField("type", SAPROUTER_ROUTE), ConditionalField( ByteField("version", 2), lambda pkt: router_is_known_type(pkt) and not router_is_pong(pkt)), # Route packets ConditionalField( ByteField("route_ni_version", SAPROUTER_DEFAULT_VERSION), router_is_route), ConditionalField(ByteField("route_entries", 0), router_is_route), ConditionalField( ByteEnumKeysField("route_talk_mode", 0, router_ni_talk_mode_values), router_is_route), ConditionalField(ShortField("route_padd", 0), router_is_route), ConditionalField(ByteField("route_rest_nodes", 0), router_is_route), ConditionalField( FieldLenField("route_length", 0, length_of="route_string", fmt="I"), router_is_route), ConditionalField(IntField("route_offset", 0), router_is_route), ConditionalField( PacketListField("route_string", None, SAPRouterRouteHop, length_from=lambda pkt: pkt.route_length), router_is_route), # Admin packets ConditionalField( ByteEnumKeysField("adm_command", 0x02, router_adm_commands), router_is_admin), ConditionalField( ShortField("adm_unused", 0x00), lambda pkt: router_is_admin(pkt) and pkt.adm_command not in [10, 11, 12, 13]), # Info Request fields ConditionalField( StrNullFixedLenField("adm_password", "", 19), lambda pkt: router_is_admin(pkt) and pkt.adm_command in [2]), # Cancel Route fields ConditionalField( FieldLenField("adm_client_count", None, count_of="adm_client_ids", fmt="H"), lambda pkt: router_is_admin(pkt) and pkt.adm_command in [6]), # Trace Connection fields ConditionalField( FieldLenField("adm_client_count", None, count_of="adm_client_ids", fmt="I"), lambda pkt: router_is_admin(pkt) and pkt.adm_command in [12, 13]), # Cancel Route or Trace Connection fields ConditionalField( FieldListField("adm_client_ids", [0x00], IntField("", 0), count_from=lambda pkt: pkt.adm_client_count), lambda pkt: router_is_admin(pkt) and pkt.adm_command in [6, 12, 13]), # Set/Clear Peer Trace fields # TODO: Check whether this field should be a IPv6 address or another proper field ConditionalField( StrFixedLenField("adm_address_mask", "", 32), lambda pkt: router_is_admin(pkt) and pkt.adm_command in [10, 11]), # Error Information/Control Messages fields ConditionalField( ByteEnumKeysField("opcode", 0, router_control_opcodes), lambda pkt: router_is_error(pkt) or router_is_control(pkt)), ConditionalField( ByteField("opcode_padd", 0), lambda pkt: router_is_error(pkt) or router_is_control(pkt)), ConditionalField( SignedIntEnumField("return_code", 0, router_return_codes), lambda pkt: router_is_error(pkt) or router_is_control(pkt)), # Error Information fields ConditionalField( FieldLenField("err_text_length", None, length_of="err_text_value", fmt="!I"), lambda pkt: router_is_error(pkt) and pkt.opcode == 0), ConditionalField( PacketField("err_text_value", SAPRouterError(), SAPRouterError), lambda pkt: router_is_error(pkt) and pkt.opcode == 0 and pkt.err_text_length > 0), ConditionalField(IntField("err_text_unknown", 0), lambda pkt: router_is_error(pkt) and pkt.opcode == 0), # Control Message fields ConditionalField( IntField("control_text_length", 0), lambda pkt: router_is_control(pkt) and pkt.opcode != 0), ConditionalField( StrField("control_text_value", "*ERR"), lambda pkt: router_is_control(pkt) and pkt.opcode != 0), # SNC Frame fields ConditionalField( PacketField("snc_frame", None, SAPSNCFrame), lambda pkt: router_is_control(pkt) and pkt.opcode in [70, 71]) ]
class NTLMSSPLE(Packet): name = 'NTLM Secure Service Provider' fields_desc = [ StrFixedLenField('identifier', 'NTLMSSP', length=8), LEIntEnumField('messageType', 3, {3: 'NTLMSSP_AUTH'}), LEShortField('lanManagerLen', 0), LEShortField('lanManagerMax', 0), LEIntField('lanManagerOffset', 0), LEShortField('NTLMRepLen', 0), LEShortField('NTLMRepMax', 0), LEIntField('NTLMRepOffset', 0), LEShortField('domainNameLen', 0), LEShortField('domainNameMax', 0), LEIntField('domainNameOffset', 0), LEShortField('userNameLen', 0), LEShortField('userNameMax', 0), LEIntField('userNameOffset', 0), LEShortField('hostNameLen', 0), LEShortField('hostNameMax', 0), LEIntField('hostNameOffset', 0), LEShortField('sessionKeyLen', 0), LEShortField('sessionKeyMax', 0), LEIntField('sessionKeyOffset', 0), FlagsField('negociateFlags', 0, 32, _negociate_flags), ByteField('versionMajor', 0), ByteField('versionMinor', 0), LEShortField('buildNumber', 0), ByteField('reserved', 0), ShortField('reserved2', 0), ByteField('NTLMCurrentRevision', 0), StrFixedLenField('MIC', '', 16), StrLenField('domainName', '', length_from=lambda pkt: pkt.domainNameLen), StrLenField('userName', '', length_from=lambda pkt: pkt.userNameLen), StrLenField('hostName', '', length_from=lambda pkt: pkt.hostNameLen), StrLenField('lanManager', '', length_from=lambda pkt: pkt.lanManagerLen), StrFixedLenField('NTLMRep', '', length=16), ByteField('responseVersion', 0), ByteField('hiResponseVersion', 0), StrFixedLenField('Z', '', 6), LELongField('timestamp', 0), # Time in nanosecond StrFixedLenField('clientChallenge', '', 8), LEIntField('Z', 0), PacketField('attribute1', None, AttributeNameLE), PacketField('attribute2', None, AttributeNameLE), PacketField('attribute3', None, AttributeNameLE), PacketField('attribute4', None, AttributeNameLE), PacketField('attribute5', None, AttributeNameLE), PacketField('attribute6', None, AttributeNameLE), PacketField('attribute7', None, AttributeNameLE), PacketField('attribute8', None, AttributeNameLE), PacketField('attribute9', None, AttributeNameLE), PacketField('attribute10', None, AttributeNameLE), LEIntField('Z', 0), LEIntField('padding', 0), StrLenField('sessionKey', '', length_from=lambda pkt: pkt.sessionKeyLen), ] def extract_padding(self, p): return b"", p
class ARP(Packet): name = "ARP" fields_desc = [ XShortField("hwtype", 0x0001), XShortEnumField("ptype", 0x0800, ETHER_TYPES), FieldLenField("hwlen", None, fmt="B", length_of="hwsrc"), FieldLenField("plen", None, fmt="B", length_of="psrc"), ShortEnumField( "op", 1, { "who-has": 1, "is-at": 2, "RARP-req": 3, "RARP-rep": 4, "Dyn-RARP-req": 5, "Dyn-RAR-rep": 6, "Dyn-RARP-err": 7, "InARP-req": 8, "InARP-rep": 9 }), MultipleTypeField( [ (SourceMACField("hwsrc"), (lambda pkt: pkt.hwtype == 1 and pkt.hwlen == 6, lambda pkt, val: pkt.hwtype == 1 and (pkt.hwlen == 6 or (pkt.hwlen is None and (val is None or len(val) == 6 or valid_mac(val)))))), ], StrFixedLenField("hwsrc", None, length_from=lambda pkt: pkt.hwlen), ), MultipleTypeField( [ (SourceIPField("psrc", "pdst"), (lambda pkt: pkt.ptype == 0x0800 and pkt.plen == 4, lambda pkt, val: pkt.ptype == 0x0800 and (pkt.plen == 4 or (pkt.plen is None and (val is None or valid_net(val)))))), (SourceIP6Field("psrc", "pdst"), (lambda pkt: pkt.ptype == 0x86dd and pkt.plen == 16, lambda pkt, val: pkt.ptype == 0x86dd and (pkt.plen == 16 or (pkt.plen is None and (val is None or valid_net6(val)))))), ], StrFixedLenField("psrc", None, length_from=lambda pkt: pkt.plen), ), MultipleTypeField( [ (MACField("hwdst", ETHER_ANY), (lambda pkt: pkt.hwtype == 1 and pkt.hwlen == 6, lambda pkt, val: pkt.hwtype == 1 and (pkt.hwlen == 6 or (pkt.hwlen is None and (val is None or len(val) == 6 or valid_mac(val)))))), ], StrFixedLenField("hwdst", None, length_from=lambda pkt: pkt.hwlen), ), MultipleTypeField( [ (IPField("pdst", "0.0.0.0"), (lambda pkt: pkt.ptype == 0x0800 and pkt.plen == 4, lambda pkt, val: pkt.ptype == 0x0800 and (pkt.plen == 4 or (pkt.plen is None and (val is None or valid_net(val)))))), (IP6Field("pdst", "::"), (lambda pkt: pkt.ptype == 0x86dd and pkt.plen == 16, lambda pkt, val: pkt.ptype == 0x86dd and (pkt.plen == 16 or (pkt.plen is None and (val is None or valid_net6(val)))))), ], StrFixedLenField("pdst", None, length_from=lambda pkt: pkt.plen), ), ] def hashret(self): return struct.pack(">HHH", self.hwtype, self.ptype, ((self.op + 1) // 2)) + self.payload.hashret() def answers(self, other): if not isinstance(other, ARP): return False if self.op != other.op + 1: return False # We use a loose comparison on psrc vs pdst to catch answers # with ARP leaks self_psrc = self.get_field('psrc').i2m(self, self.psrc) other_pdst = other.get_field('pdst').i2m(other, other.pdst) return self_psrc[:len(other_pdst)] == other_pdst[:len(self_psrc)] def route(self): fld, dst = self.getfield_and_val("pdst") fld, dst = fld._find_fld_pkt_val(self, dst) if isinstance(dst, Gen): dst = next(iter(dst)) if isinstance(fld, IP6Field): return conf.route6.route(dst) elif isinstance(fld, IPField): return conf.route.route(dst) else: return None, None, None def extract_padding(self, s): return "", s def mysummary(self): if self.op == 1: return self.sprintf("ARP who has %pdst% says %psrc%") if self.op == 2: return self.sprintf("ARP is at %hwsrc% says %psrc%") return self.sprintf("ARP %op% %psrc% > %pdst%")
class RadioTap(Packet): name = "RadioTap dummy" fields_desc = [ByteField('version', 0), ByteField('pad', 0), LEShortField('len', None), FlagsField('present', None, -32, ['TSFT', 'Flags', 'Rate', 'Channel', 'FHSS', 'dBm_AntSignal', # noqa: E501 'dBm_AntNoise', 'Lock_Quality', 'TX_Attenuation', 'dB_TX_Attenuation', # noqa: E501 'dBm_TX_Power', 'Antenna', 'dB_AntSignal', 'dB_AntNoise', # noqa: E501 'RXFlags', 'TXFlags', 'b17', 'b18', 'ChannelPlus', 'MCS', 'A_MPDU', # noqa: E501 'VHT', 'timestamp', 'b24', 'b25', 'b26', 'b27', 'b28', 'b29', # noqa: E501 'RadiotapNS', 'VendorNS', 'Ext']), # noqa: E501 # Extended presence mask ConditionalField(PacketListField("Ext", [], next_cls_cb=_next_radiotap_extpm), lambda pkt: pkt.present and pkt.present.Ext), # noqa: E501 # Default fields ConditionalField(_RadiotapReversePadField(BitField("mac_timestamp", 0, -64)), lambda pkt: pkt.present and pkt.present.TSFT), # noqa: E501 ConditionalField( _RadiotapReversePadField( FlagsField("Flags", None, -8, ['CFP', 'ShortPreamble', 'wep', 'fragment', # noqa: E501 'FCS', 'pad', 'badFCS', 'ShortGI']) # noqa: E501 ), lambda pkt: pkt.present and pkt.present.Flags), ConditionalField(_RadiotapReversePadField(ByteField("Rate", 0)), lambda pkt: pkt.present and pkt.present.Rate), # noqa: E501 ConditionalField(_RadiotapReversePadField(LEShortField("Channel", 0)), lambda pkt: pkt.present and pkt.present.Channel), # noqa: E501 ConditionalField( _RadiotapReversePadField( FlagsField("ChannelFlags", None, -16, ['res1', 'res2', 'res3', 'res4', 'Turbo', 'CCK', # noqa: E501 'OFDM', '2GHz', '5GHz', 'Passive', 'Dynamic_CCK_OFDM', # noqa: E501 'GFSK', 'GSM', 'StaticTurbo', '10MHz', '5MHz']) # noqa: E501 ), lambda pkt: pkt.present and pkt.present.Channel), ConditionalField(_RadiotapReversePadField(_dbmField("dBm_AntSignal", -256)), lambda pkt: pkt.present and pkt.present.dBm_AntSignal), # noqa: E501 ConditionalField(_RadiotapReversePadField(_dbmField("dBm_AntNoise", -256)), lambda pkt: pkt.present and pkt.present.dBm_AntNoise), # noqa: E501 ConditionalField(_RadiotapReversePadField(ByteField("Antenna", 0)), lambda pkt: pkt.present and pkt.present.Antenna), # noqa: E501 # RX Flags ConditionalField(_RadiotapReversePadField(FlagsField("RXFlags", None, -16, ["res1", "BAD_PLCP", "res2"])), # noqa: E501 lambda pkt: pkt.present and pkt.present.RXFlags), # noqa: E501# # TX Flags ConditionalField(_RadiotapReversePadField(FlagsField("TXFlags", None, -16, ["TX_FAIL", "CTS", "RTS", "NOACK", "NOSEQ"])), # noqa: E501 lambda pkt: pkt.present and pkt.present.TXFlags), # noqa: E501 # ChannelPlus ConditionalField( _RadiotapReversePadField( FlagsField("ChannelFlags2", None, -32, ['res1', 'res2', 'res3', 'res4', 'Turbo', 'CCK', # noqa: E501 'OFDM', '2GHz', '5GHz', 'Passive', 'Dynamic_CCK_OFDM', # noqa: E501 'GFSK', 'GSM', 'StaticTurbo', '10MHz', '5MHz', # noqa: E501 '20MHz', '40MHz_ext_channel_above', '40MHz_ext_channel_below', # noqa: E501 'res5', 'res6', 'res7', 'res8', 'res9']) # noqa: E501 ), lambda pkt: pkt.present and pkt.present.ChannelPlus), ConditionalField(_RadiotapReversePadField(LEShortField("ChannelFrequency", 0)), lambda pkt: pkt.present and pkt.present.ChannelPlus), # noqa: E501 ConditionalField(_RadiotapReversePadField(ByteField("ChannelNumber", 0)), lambda pkt: pkt.present and pkt.present.ChannelPlus), # noqa: E501 # MCS ConditionalField( _RadiotapReversePadField(FlagsField("knownMCS", None, -8, ['bandwidth', 'MCS_index', 'guard_interval', 'HT_format', # noqa: E501 'FEC_type', 'STBC_streams', 'Ness', 'Ness_MSB'])), # noqa: E501 lambda pkt: pkt.present and pkt.present.MCS), ConditionalField(BitEnumField("bandwidth", 0, 2, {0: "20MHz", 1: "40MHz", 2: "ht40Mhz-", 3: "ht40MHz+"}), # noqa: E501 lambda pkt: pkt.present and pkt.present.MCS), # noqa: E501 ConditionalField(BitEnumField("guard_interval", 0, 1, {0: "Long_GI", 1: "Short_GI"}), lambda pkt: pkt.present and pkt.present.MCS), # noqa: E501 ConditionalField(BitEnumField("HT_format", 0, 1, {0: "mixed", 1: "greenfield"}), lambda pkt: pkt.present and pkt.present.MCS), # noqa: E501 ConditionalField(BitEnumField("FEC_type", 0, 1, {0: "BCC", 1: "LDPC"}), lambda pkt: pkt.present and pkt.present.MCS), # noqa: E501 ConditionalField(BitField("STBC_streams", 0, 2), lambda pkt: pkt.present and pkt.present.MCS), # noqa: E501 ConditionalField(BitField("Ness_LSB", 0, 1), lambda pkt: pkt.present and pkt.present.MCS), # noqa: E501 ConditionalField(ByteField("MCS_index", 0), lambda pkt: pkt.present and pkt.present.MCS), # noqa: E501 # A_MPDU ConditionalField(_RadiotapReversePadField(LEIntField("A_MPDU_ref", 0)), lambda pkt: pkt.present and pkt.present.A_MPDU), # noqa: E501 ConditionalField( _RadiotapReversePadField( FlagsField("A_MPDU_flags", None, -32, ['Report0Subframe', 'Is0Subframe', 'KnownLastSubframe', # noqa: E501 'LastSubframe', 'CRCerror', 'EOFsubframe', 'KnownEOF', # noqa: E501 'res1', 'res2', 'res3', 'res4', 'res5', 'res6', 'res7', 'res8']) # noqa: E501 ), lambda pkt: pkt.present and pkt.present.A_MPDU), # VHT ConditionalField( _RadiotapReversePadField( FlagsField("KnownVHT", None, -16, ['STBC', 'TXOP_PS_NOT_ALLOWED', 'GuardInterval', 'SGINsysmDis', # noqa: E501 'LDPCextraOFDM', 'Beamformed', 'Bandwidth', 'GroupID', 'PartialAID', # noqa: E501 'res1', 'res2', 'res3', 'res4', 'res5', 'res6', 'res7']) # noqa: E501 ), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField( _RadiotapReversePadField( FlagsField("PresentVHT", None, -8, ['STBC', 'TXOP_PS_NOT_ALLOWED', 'GuardInterval', 'SGINsysmDis', # noqa: E501 'LDPCextraOFDM', 'Beamformed', 'res1', 'res2']) # noqa: E501 ), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField(_RadiotapReversePadField(ByteEnumField("bandwidth", 0, _vht_bandwidth)), lambda pkt: pkt.present and pkt.present.VHT), # noqa: E501 ConditionalField(_RadiotapReversePadField(StrFixedLenField("mcs_nss", 0, length=5)), lambda pkt: pkt.present and pkt.present.VHT), # noqa: E501 ConditionalField(_RadiotapReversePadField(ByteField("GroupID", 0)), lambda pkt: pkt.present and pkt.present.VHT), # noqa: E501 ConditionalField(_RadiotapReversePadField(ShortField("PartialAID", 0)), lambda pkt: pkt.present and pkt.present.VHT), # noqa: E501 StrLenField('notdecoded', "", length_from=lambda pkt: pkt.len - pkt._tmp_dissect_pos)] # noqa: E501 def guess_payload_class(self, payload): if self.present and self.present.Flags and self.Flags.FCS: return Dot11FCS return Dot11 def post_build(self, p, pay): if self.len is None: p = p[:2] + struct.pack("!H", len(p))[::-1] + p[4:] return p + pay
class PNIORealTime(Packet): """PROFINET cyclic real-time""" name = "PROFINET Real-Time" fields_desc = [ NotionalLenField("len", None, length_from=lambda p, s: len(s)), NotionalLenField("dataLen", None, length_from=lambda p, s: len(s[:-4].rstrip(b"\0"))), LowerLayerBoundPacketListField("data", [], _pnio_rtc_guess_payload_class, length_from=lambda p: p.dataLen), StrFixedLenField("padding", "", length_from=lambda p: p[PNIORealTime].padding_length()), ShortField("cycleCounter", 0), FlagsField("dataStatus", 0x35, 8, _PNIO_DS_FLAGS), ByteField("transferStatus", 0) ] overload_fields = { ProfinetIO: {"frameID": 0x8000}, # RT_CLASS_1 } def padding_length(self): """Compute the length of the padding need for the ethernet frame""" fld, val = self.getfield_and_val("data") # use the len field if available to define the padding length, eg for # dissected packets pkt_len = self.getfieldval("len") if pkt_len is not None: return max(0, pkt_len - len(fld.addfield(self, b"", val)) - 4) if isinstance(self.underlayer, ProfinetIO) and \ isinstance(self.underlayer.underlayer, UDP): return max(0, 12 - len(fld.addfield(self, b"", val))) else: return max(0, 40 - len(fld.addfield(self, b"", val))) @staticmethod def analyse_data(packets): """Analyse the data to find heuristical properties and determine location and type of data""" loc = PNIORealTime.find_data(packets) loc = PNIORealTime.analyse_profisafe(packets, loc) pnio_update_config(loc) return loc @staticmethod def find_data(packets): """Analyse a packet list to extract data offsets from packets data.""" # a dictionary to count data offsets (ie != 0x80) # It's formatted: {(src, dst): (total, [count for offset in len])} heuristic = {} # Counts possible data locations # 0x80 are mainly IOxS and trailling 0x00s are just padding for pkt in packets: if PNIORealTime in pkt: pdu = bytes(pkt[PNIORealTime])[:-4].rstrip(b"\0") if (pkt.src, pkt.dst) not in heuristic: heuristic[(pkt.src, pkt.dst)] = (0, []) total, counts = heuristic[(pkt.src, pkt.dst)] if len(counts) < len(pdu): counts.extend([0 for _ in range(len(pdu) - len(counts))]) for i in range(len(pdu)): if orb(pdu[i]) != 0x80: counts[i] += 1 comm = (pkt.src, pkt.dst) heuristic[comm] = (total + 1, counts) # Determine data locations locations = {} for comm in heuristic: total, counts = heuristic[comm] length = len(counts) loc = locations[comm] = [] start = None for i in range(length): if counts[i] > total // 2: # Data if more than half is != 0x80 if start is None: start = i else: if start is not None: loc.append(( start - length, PNIORealTimeRawData, {"length": i - start} )) start = None return locations @staticmethod def analyse_profisafe(packets, locations=None): """Analyse a packet list to find possible PROFISafe profils. It's based on an heuristical analysis of each payload to try to find CRC and control/status byte. locations: possible data locations. If not provided, analyse_pn_rt will be called beforehand. If not given, it calls in the same time analyse_data which update the configuration of the data field""" # get data locations and entropy of bytes if not locations: locations = PNIORealTime.find_data(packets) entropies = PNIORealTime.data_entropy(packets, locations) # Try to find at least 3 high entropy successive bytes (the CRC) for comm in entropies: entropy = dict(entropies[comm]) # Convert tuples to key => value for i in range(len(locations[comm])): # update each location with its value after profisafe analysis locations[comm][i] = \ PNIORealTime.analyse_one_profisafe_location( locations[comm][i], entropy ) return locations @staticmethod def analyse_one_profisafe_location(location, entropy): """Analyse one PNIO RTC data location to find if its a PROFISafe :param location: location to analyse, a tuple (start, type, config) :param entropy: the entropy of each byte of the packet data :returns: the configuration associated with the data """ start, klass, conf = location if conf["length"] >= 4: # Minimal PROFISafe length succ_count = 0 for j in range(start, start + conf["length"]): # Limit for a CRC is set to 6 bit of entropy min if j in entropy and entropy[j] >= 6: succ_count += 1 else: succ_count = 0 # PROFISafe profiles must end with at least 3 bytes of high entropy if succ_count >= 3: # Possible profisafe CRC return ( start, Profisafe, {"CRC": succ_count, "length": conf["length"]} ) # Not a PROFISafe profile return (start, klass, conf) @staticmethod def data_entropy(packets, locations=None): """Analyse a packet list to find the entropy of each data byte locations: possible data locations. If not provided, analyse_pn_rt will be called beforehand. If not given, it calls in the same time analyse_data which update the configuration of the data field""" if not locations: locations = PNIORealTime.find_data(packets) # Retrieve the entropy of each data byte, for each communication entropies = {} for comm in locations: if len(locations[comm]) > 0: # Doesn't append empty data entropies[comm] = [] comm_packets = [] # fetch all packets from the communication for pkt in packets: if PNIORealTime in pkt and (pkt.src, pkt.dst) == comm: comm_packets.append( bytes(pkt[PNIORealTime])[:-4].rstrip(b"\0") ) # Get the entropy for start, dummy, config in locations[comm]: for i in range(start, start + config["length"]): entropies[comm].append( (i, entropy_of_byte(comm_packets, i)) ) return entropies @staticmethod def draw_entropy(packets, locations=None): """Plot the entropy of each data byte of PN RT communication""" import matplotlib.pyplot as plt import matplotlib.cm as cm entropies = PNIORealTime.data_entropy(packets, locations) rows = len(entropies) cur_row = 1 for comm in entropies: index = [] vals = [] for i, ent in entropies[comm]: index.append(i) vals.append(ent) # Offsets the indexes to get the index from the beginning offset = -min(index) index = [i + offset for i in index] plt.subplot(rows, 1, cur_row) plt.bar(index, vals, 0.8, color="r") plt.xticks([i + 0.4 for i in index], index) plt.title("Entropy from %s to %s" % comm) cur_row += 1 plt.ylabel("Shannon Entropy") plt.xlabel("Byte offset") # x label only on the last row plt.legend() plt.tight_layout() plt.show()
class SAPDiag(PacketNoPadded): """SAP Diag packet This packet holds the Diag Header and serve as a container for :class:`SAPDiagItem` items. It handles compression/decompression, adding the appropriate Compression Header when necessary. """ name = "SAP Diag" fields_desc = [ ByteField("mode", 0), # Communication flags BitField("com_flag_TERM_GRA", 0, 1), BitField("com_flag_TERM_NNM", 0, 1), BitField("com_flag_TERM_CAS", 0, 1), BitField("com_flag_TERM_INI", 0, 1), BitField("com_flag_TERM_EOP", 0, 1), BitField("com_flag_TERM_NOP", 0, 1), BitField("com_flag_TERM_EOC", 0, 1), BitField("com_flag_TERM_EOS", 0, 1), ByteField("mode_stat", 0), ByteField("err_no", 0), ByteField("msg_type", 0), ByteField("msg_info", 0), ByteField("msg_rc", 0), ByteEnumKeysField("compress", 0, diag_compress_values), # Compression Header ConditionalField(LEIntField("uncompress_length", None), lambda pkt: pkt.compress == 1), ConditionalField( ByteEnumField("algorithm", 0x12, { 0x12: "LZH", 0x10: "LZC" }), lambda pkt: pkt.compress == 1), ConditionalField(StrFixedLenField("magic_bytes", "\x1f\x9d", 2), lambda pkt: pkt.compress == 1), ConditionalField(ByteField("special", 2), lambda pkt: pkt.compress == 1), # SNC Frame ConditionalField(PacketField("snc_frame", None, SAPSNCFrame), lambda pkt: pkt.compress in [2, 3]), # Message info ConditionalField(StrEncodedPaddedField("info", None), lambda pkt: pkt.err_no != 0), # Payload PacketListField("message", None, SAPDiagItem) ] def do_compress(self, s): """Compress a string using SAP compression C++ extension. :param s: string to compress :type s: C{string} :return: string compression header plus the compressed string :rtype: C{string} :raise pysapcompress.Error: when a compression error is raised """ if len(s) > 0: # Compress the payload and return the output (_, _, outbuffer) = pysapcompress.compress(s, pysapcompress.ALG_LZH) return outbuffer def do_decompress(self, s, length): """Decompress a string using SAP compression C++ extension. :param s: compression header plus compressed string :type s: C{string} :param length: reported compressed length :type length: ``int`` :return: decompressed string :rtype: C{string} :raise pysapcompress.Error: when a decompression error is raised """ if len(s) > 0: # Decompress the payload and return the output (_, _, outbuffer) = pysapcompress.decompress(s, length) return outbuffer def pre_dissect(self, s): """Prepares the packet for dissection. If the compression flag is set, decompress the payload. """ # If the compression flag is set, decompress everything after the headers if s[7] == "\x01": # First need to get the reported decompressed length (reported_length, ) = unpack("<I", s[8:12]) # Then return the headers (Diag and Compression) and the payload (message field) try: return s[:16] + self.do_decompress(s[8:], reported_length) except DecompressError: return s # Uncompressed packet, just return them return s def post_build(self, p, pay): """Compress the payload. If the compression flag is set, compress both the message field and the payload. """ if pay is None: pay = '' if self.compress == 1: payload = "".join([str(item) for item in self.message]) + pay if len(payload) > 0: try: return p[:8] + self.do_compress(payload) except CompressError: return p + pay return p + pay def get_item(self, item_type=None, item_id=None, item_sid=None): """Get an item from the packet's message. Returns None if the message is not found, or a list if the item is found multiple times. :param item_type: item type byte or string value :type item_type: ``int`` or C{string} or ``list`` :param item_id: item ID byte or string value :type item_id: ``int`` or C{string} or ``list`` :param item_sid: item SID byte or string value :type item_sid: ``int`` or C{string} or ``list`` :return: list of items found on the packet or None :rtype: ``list`` of :class:`SAPDiagItem` """ # Expand list lookups items = [] if item_type is not None and type(item_type) == list: for itype in item_type: items.extend(self.get_item(itype, item_id, item_sid)) return items if item_id is not None and type(item_id) == list: for iid in item_id: items.extend(self.get_item(item_type, iid, item_sid)) return items if item_sid is not None and type(item_sid) == list: for isid in item_sid: items.extend(self.get_item(item_type, item_id, isid)) return items # Perform name lookups if item_type is not None and isinstance(item_type, str): item_type = list(diag_item_types.keys())[list( diag_item_types.values()).index(item_type)] if item_id is not None and isinstance(item_id, str): item_id = list(diag_appl_ids.keys())[list( diag_appl_ids.values()).index(item_id)] if item_sid is not None and isinstance(item_sid, str): item_sid = list(diag_appl_sids[item_id].keys())[list( diag_appl_sids[item_id].values()).index(item_sid)] # Filter and return items if item_sid is None and item_id is None: items = [ item for item in self.message if hasattr(item, "item_type") and item.item_type == item_type ] elif item_sid is None: items = [ item for item in self.message if hasattr(item, "item_type") and item.item_type == item_type and item.item_id == item_id ] else: items = [ item for item in self.message if hasattr(item, "item_type") and item.item_type == item_type and item.item_id == item_id and item.item_sid == item_sid ] return items
class SAPIGS(Packet): """SAP Internet Graphic Server call packet This packet is used for the Remote IGS Function Call protocol. """ name = "SAP IGS Call" fields_desc = [ StrFixedLenPaddedField("function", "", padd="\x00", length=32), StrFixedLenPaddedField("listener", "ListenerX", padd="\x00", length=32), StrFixedLenPaddedField("hostname", "pysap", padd="\x00", length=81), StrFixedLenPaddedField("id", "1234", padd="\x00", length=4), StrFixedLenPaddedField("padd1", "\x00" * 15, length=15), ByteField("todo1", 0x01), # TODO: need to work on it StrFixedLenPaddedField("padd2", "\x00" * 20, length=20), ByteField("todo2", 0x08), # TODO: need to work on it StrFixedLenPaddedField("padd3", "\x00" * 6, length=6), StrFixedLenField("eye_catch", "TransMagic", length=10), StrFixedLenPaddedField("padd4", "\x00" * 2, length=2), StrFixedLenField("codepage", "4103", length=4), StrFixedLenPaddedField("offset_content", "", padd="\x00", length=16), StrFixedLenPaddedField("packet_size", "", padd="\x00", length=16), ] @staticmethod def http(host, port, interpreter, files=None, tls=False, method='POST'): """HTTP request for IGS This method build a http(s) request for IGS HTTP Multiplexer service instead of Remote IGS Function Call :param host: remote host to connect to :type host: ``string`` :param port: remote port to connect to :type port: ``int`` :param interpreter: interpreter of function to call :type interpreter: ``string`` :param files: list of files to send in case of multipart/form-data :type files: ``dict`` :param tls: using https or not :type tls: ``bool`` :param method: HTTP request method to use :type method: ``string`` :return: the HTTP request to send as raw :rtype: ``string`` :raise ImportError: if the requests library can't be imported """ if Request is None: raise ImportError("requests library not available") protocol = 'https' if tls else 'http' url = "%s://%s:%d/%s" % (protocol, host, port, interpreter ) # forge url # using PreparedRequest to retrieve the raw http request req = Request(method, url, files=files).prepare() # update User-Agent header req.headers['User-Agent'] = 'pysap' # format the request than could be send with SAP NI req_format = ('{}\r\n{}\r\n\r\n{}'.format( req.method + ' ' + req.url + ' HTTP/1.1', '\r\n'.join('{}: {}'.format(k, v) for k, v in req.headers.items()), req.body)) return req_format
class SAPDPInfo3(Packet): """SAP Dispatcher Info packet This packet is encapsulated inside SAPMS packet and before the MS ADM payload. Kernel 749. """ name = "SAP Dispatcher Info v3" fields_desc = [ IntField("dp_padd1", 185), ByteEnumKeysField("dp_req_prio", 0x1, dp_prio_values), ShortField("dp_padd2", 0x0), ByteField("dp_padd3", 0x0), ByteField("dp_padd4", 0x0), IntField("dp_req_len", 0x0), ShortField("dp_padd5", 0x0), ByteField("dp_padd6", 0x0), ByteEnumKeysField("dp_type_from", 0x2, dp_type_values), StrFixedLenField("dp_fromname", " " * 40, 40), ShortField("dp_padd7", 0x0), ByteField("dp_padd8", 0x0), ByteEnumKeysField("dp_agent_type_from", 0x6, dp_agent_type_values), ShortField("dp_padd9", 0x0), ByteField("dp_padd10", 0x0), ByteField("dp_padd12", 0xff), ShortField("dp_padd13", 0x0), ByteEnumKeysField("dp_worker_type_from", 0x1, dp_worker_type_values), ByteField("dp_worker_from_num", 0x0), ByteField("dp_padd133", 0x0), ShortField("dp_padd111", 0x0000), ByteField("dp_padd11", 0x00), ByteField("dp_addr_from_t", 0x0), ShortField("dp_padd14", 0x0), ShortField("dp_addr_from_u", 0x0), ByteField("dp_addr_from_m", 0x0), ByteField("dp_padd15", 0x1), IntField("dp_respid_from", 0x0), ShortField("dp_padd16", 0x0), ByteField("dp_padd17", 0x0), ByteEnumKeysField("dp_type_to", 0x2, dp_type_values), StrFixedLenField("dp_toname", " " * 40, 40), ShortField("dp_padd18", 0x0), ByteField("dp_padd19", 0x0), ByteEnumKeysField("dp_agent_type_to", 0x6, dp_agent_type_values), ShortField("dp_padd20", 0x0), ByteField("dp_padd21", 0x0), ByteEnumKeysField("dp_worker_type_to", 0x1, dp_worker_type_values), ShortField("dp_padd22", 0x0), ByteField("dp_worker_to_num", 0x0), ByteField("dp_padd222", 0x0), IntField("dp_padd23", 0x0), ByteField("dp_addr_to_t", 0x0), ShortField("dp_padd24", 0x0), ShortField("dp_addr_to_u", 0x0), ByteField("dp_addr_from_m", 0x0), ByteField("dp_padd25", 0x1), IntField("dp_respid_to", 0x0), ShortField("dp_padd26", 0x0), ByteField("dp_padd27", 0x0), ByteEnumKeysField("dp_req_handler", 40, dp_req_handler_values), StrFixedLenField("dp_padd28", "\xff\xff\xff\xff", 4), IntField("dp_padd29", 0x0), IntField("dp_padd30", 0x0), IntField("dp_padd31", 0x0), StrFixedLenField("dp_padd32", "\x00" * 5, 5), ]
class SM_Confirm(Packet): name = "Pairing Confirm" fields_desc = [StrFixedLenField("confirm", b'\x00' * 16, 16)]
class SAPMSAdmRecord(PacketNoPadded): """SAP Message Server Administration Record packet Packet for Message Server administration records. Each administration package has a variable number of records, each one specifies an operation to execute. """ name = "SAP Message Server Adm Record" fields_desc = [ ByteEnumKeysField("opcode", 0x00, ms_adm_opcode_values), ByteField("serial_number", 0x00), ByteField("executed", 0x00), ByteField("errorno", 0x00), # TODO: Look for error names ConditionalField( StrFixedLenField("record", None, 75), lambda pkt: pkt.opcode not in [0x01, 0x15, 0x2e] and pkt.executed == 0x01), ConditionalField( StrFixedLenField("record_pad", None, 25), lambda pkt: pkt.opcode not in [0x01, 0x15, 0x2e] and pkt.executed == 0x01), ConditionalField( StrFixedLenField("record", None, 100), lambda pkt: pkt.opcode not in [0x01, 0x15, 0x2e] and pkt.executed == 0x00), # TODO: Add more opcodes fields # AD_PROFILE and AD_SHARED_PARAMETER fields ConditionalField(StrNullFixedLenPaddedField("parameter", "", 100), lambda pkt: pkt.opcode in [0x01, 0x2e]), # AD_RZL_STRG opcode ConditionalField( ByteEnumKeysField("rzl_strg_type", 1, ms_adm_rzl_strg_type_values), lambda pkt: pkt.opcode in [0x15]), ConditionalField(ByteField("rzl_strg_name_length", 0), lambda pkt: pkt.opcode in [0x15]), ConditionalField(ByteField("rzl_strg_value_offset", 0), lambda pkt: pkt.opcode in [0x15]), ConditionalField(ByteField("rzl_strg_value_length", 0), lambda pkt: pkt.opcode in [0x15]), ConditionalField(StrFixedLenField("rzl_strg_name", None, 20), lambda pkt: pkt.opcode in [0x15]), ConditionalField( IntField("rzl_strg_uptime", 0), lambda pkt: pkt.opcode in [0x15] and pkt.rzl_strg_type in [11, 15, 21, 31, 41, 51]), ConditionalField( IntField("rzl_strg_integer1", 0), lambda pkt: pkt.opcode in [0x15] and pkt.rzl_strg_type in [11, 15, 21, 31, 41, 51]), ConditionalField( IntField("rzl_strg_delay", 0), lambda pkt: pkt.opcode in [0x15] and pkt.rzl_strg_type in [11, 15, 21, 31, 41, 51]), ConditionalField( IntField("rzl_strg_integer3", 0), lambda pkt: pkt.opcode in [0x15] and pkt.rzl_strg_type in [11, 15, 21, 31, 41, 51]), ConditionalField( IntField("rzl_strg_users", 0), lambda pkt: pkt.opcode in [0x15] and pkt.rzl_strg_type in [11, 15, 21, 31, 41, 51]), ConditionalField( IntField("rzl_strg_quality", 0), lambda pkt: pkt.opcode in [0x15] and pkt.rzl_strg_type in [11, 15, 21, 31, 41, 51]), ConditionalField( IntField("rzl_strg_integer6", 0), lambda pkt: pkt.opcode in [0x15] and pkt.rzl_strg_type in [11, 15, 21, 31, 41, 51]), ConditionalField( IntField("rzl_strg_integer7", 0), lambda pkt: pkt.opcode in [0x15] and pkt.rzl_strg_type in [11, 15, 21, 31, 41, 51]), ConditionalField( IntField("rzl_strg_integer8", 0), lambda pkt: pkt.opcode in [0x15] and pkt.rzl_strg_type in [11, 15, 21, 31, 41, 51]), ConditionalField( IntField("rzl_strg_integer9", 0), lambda pkt: pkt.opcode in [0x15] and pkt.rzl_strg_type in [11, 15, 21, 31, 41, 51]), ConditionalField( StrFixedLenField("rzl_strg_value", None, 40), lambda pkt: pkt.opcode in [0x15] and pkt. rzl_strg_type not in [11, 15, 21, 31, 41, 51]), ConditionalField(StrFixedLenField("rzl_strg_padd2", None, 36), lambda pkt: pkt.opcode in [0x15]), ]
class SM_Random(Packet): name = "Pairing Random" fields_desc = [StrFixedLenField("random", b'\x00' * 16, 16)]
class SAPMSProperty(Packet): """SAP Message Server Property packet. Packet containing information about properties. """ name = "SAP Message Server Property" fields_desc = [ StrNullFixedLenField("client", None, 40), IntEnumField("id", 0x00, ms_property_id_values), # MS_PROPERTY_VHOST ConditionalField(ShortEnumKeysField("logon", 0, ms_logon_type_values), lambda pkt: pkt.id in [0x02]), ConditionalField(StrFixedLenField("pad", None, 12), lambda pkt: pkt.id in [0x02]), ConditionalField(ShortField("len", 0), lambda pkt: pkt.id in [0x02]), ConditionalField( StrLenField("value", "", length_from=lambda pkt: pkt.len), lambda pkt: pkt.id in [0x02]), ConditionalField(ShortField("pad2", 0xffff), lambda pkt: pkt.id in [0x02]), # MS_PROPERTY_IPADR ConditionalField(IPField("address", "0.0.0.0"), lambda pkt: pkt.id in [0x03]), ConditionalField(IP6Field("address6", "::"), lambda pkt: pkt.id in [0x03]), # MS_PROPERTY_PARAM ConditionalField( FieldLenField("param_len", 0, length_of="param", fmt="I"), lambda pkt: pkt.id in [0x04]), ConditionalField( StrLenField("param", "", length_from=lambda pkt: pkt.param_len), lambda pkt: pkt.id in [0x04]), ConditionalField( StrLenField("param_padding", "", length_from=lambda pkt: 100 - pkt.param_len), lambda pkt: pkt.id in [0x04]), ConditionalField(ShortField("pad3", 0), lambda pkt: pkt.id in [0x04]), ConditionalField( FieldLenField("value_len", 0x0, length_of="value", fmt="H"), lambda pkt: pkt.id in [0x04]), ConditionalField( StrLenField("value", "", length_from=lambda pkt: pkt.value_len), lambda pkt: pkt.id in [0x04]), # MS_PROPERTY_SERVICE ConditionalField(ShortField("service", 0), lambda pkt: pkt.id in [0x05]), ConditionalField(ByteField("value", 0), lambda pkt: pkt.id in [0x05]), # Release Information fields ConditionalField(StrNullFixedLenField("release", "720", length=10), lambda pkt: pkt.id in [0x07]), ConditionalField(IntField("patchno", 0), lambda pkt: pkt.id in [0x07]), ConditionalField(IntField("supplvl", 0), lambda pkt: pkt.id in [0x07]), ConditionalField(IntField("platform", 0), lambda pkt: pkt.id in [0x07]), ]
class SM_Encryption_Information(Packet): name = "Encryption Information" fields_desc = [ StrFixedLenField("ltk", b"\x00" * 16, 16), ]
class SAPDPInfo2(Packet): """SAP Dispatcher Info packet This packet is encapsulated inside SAPMS packet and before the MS ADM payload. Kernel 720. """ name = "SAP Dispatcher Info v2" fields_desc = [ ByteEnumKeysField("dp_req_prio", 0x1, dp_prio_values), XByteField("dp_blob_00", 0x2), XByteField("dp_blob_01", 0x80), XByteField("dp_blob_02", 0x21), ShortField("dp_blob_03", 0x0), ShortField("dp_blob_04", 0xffff), StrFixedLenField("dp_blob_05", "\xff\xff\xff\xff\xff", 5), ByteField("dp_addr_from_t", 0x0), StrFixedLenField("dp_blob_06", "\xff\xff", 2), StrFixedLenField("dp_blob_07", "\xff\xff\xff\xff", 4), StrFixedLenField("dp_blob_08", "\xff\xff\xff\xff", 4), StrFixedLenField("dp_blob_09", "\xff\xcc", 2), StrFixedLenField("dp_blob_10", "\x01\x00", 2), ByteField("dp_addr_from_m", 0x0), ByteField("dp_addr_from_u", 0x0), StrFixedLenField("dp_blob_11", "\xff\xff", 2), StrFixedLenField("dp_blob_12", "\xff\xff\xff\xff", 4), StrFixedLenField("dp_blob_13", "", 86), StrFixedLenField("dp_blob_14", "", 5), StrFixedLenField("dp_name_to", "", 40), XByteField("dp_blob_15", 0x0), ByteField("dp_addr_to_t", 0x0), ShortField("dp_addr_to_u", 0x0), ByteField("dp_addr_to_m", 0x0), ByteField("dp_blob_16", 0x0), ShortField("dp_respid_to", 0x0), StrFixedLenField("dp_blob_17", "\xff\xff\xff\xff", 4), StrFixedLenField("dp_blob_18", "\x00\x00\x00\x00", 4), Field("dp_blob_19", 0x1, '<L'), StrFixedLenField("dp_blob_20", "", 12), Field("dp_blob_21", 0x0, '<L'), ]
class SM_Master_Identification(Packet): name = "Master Identification" fields_desc = [ XLEShortField("ediv", 0), StrFixedLenField("rand", b'\x00' * 8, 8), ]
class HCI_Cmd_LE_Long_Term_Key_Request_Reply(Packet): name = "LE Long Term Key Request Reply" fields_desc = [ LEShortField("handle", 0), StrFixedLenField("ltk", b'\x00' * 16, 16), ]
class SM_Identity_Information(Packet): name = "Identity Information" fields_desc = [ StrFixedLenField("irk", b'\x00' * 16, 16), ]
def __init__(self, name, default, length=None, length_from=None, max_length=None): self.max_length = max_length or 200 StrFixedLenField.__init__(self, name, default, length=length, length_from=length_from)
class SM_Signing_Information(Packet): name = "Signing Information" fields_desc = [ StrFixedLenField("csrk", b'\x00' * 16, 16), ]
class HCI_Cmd_Set_Event_Mask(Packet): name = "Set Event Mask" fields_desc = [ StrFixedLenField("mask", b"\xff\xff\xfb\xff\x07\xf8\xbf\x3d", 8) ] # noqa: E501