type TPKT(is_orig: bool) = record { version: uint8; reserved: uint8; tpkt_len: uint16; # These data structures are merged together into TPKT # because there are packets that report incorrect # lengths in the tpkt length field. No clue why. cotp: COTP; } &byteorder=bigendian &length=tpkt_len; type COTP = record { cotp_len: uint8; pdu: uint8; switch: case pdu of { 0xd0 -> connect_confirm: Connect_Confirm; 0xe0 -> client_request: Connect_Request; 0xf0 -> data: DT_Data; # In case we don't support the PDU we just # consume the rest of it and throw it away. default -> not_done: bytestring &restofdata &transient; }; } &byteorder=littleendian; type DT_Data = record { tpdu_number: uint8; # multiple octet variant of the ASN.1 type field, should handle this better. application_defined_type: uint8; application_type: uint8; data: case application_type of { 0x65 -> client: Client_Header; # 0x65 is a client 0x66 -> server: Server_Header; # 0x66 is a server default -> none: empty; }; } &byteorder=littleendian; ###################################################################### # Data Blocks ###################################################################### type Data_Header = record { type: uint16; length: uint16; } &byteorder=littleendian; type Data_Block = record { header: Data_Header; block: case header.type of { 0xc001 -> client_core: Client_Core_Data; #0xc002 -> client_security: Client_Security_Data; #0xc003 -> client_network: Client_Network_Data; #0xc004 -> client_cluster: Client_Cluster_Data; #0xc005 -> client_monitor: Client_Monitor_Data; #0xc006 -> client_msgchannel: Client_MsgChannel_Data; #0xc008 -> client_monitor_ex: Client_MonitorExtended_Data; #0xc00A -> client_multitrans: Client_MultiTransport_Data; 0x0c01 -> server_core: Server_Core_Data(header); 0x0c02 -> server_security: Server_Security_Data; 0x0c03 -> server_network: Server_Network_Data; #0x0c04 -> server_msgchannel: Server_MsgChannel_Data; #0x0c08 -> server_multitrans: Server_MultiTransport_Data; default -> unhandled: bytestring &restofdata &transient; } &length=header.length-4; } &byteorder=littleendian; ###################################################################### # Client X.224 ###################################################################### type Connect_Request = record { destination_reference: uint16; source_reference: uint16; flow_control: uint8; cookie_mstshash: RE/Cookie: mstshash\=/; cookie_value: RE/[^\x0d]+/; cookie_terminator: RE/\x0d\x0a/; rdp_neg_req: RDP_Negotiation_Request; } &byteorder=littleendian; type RDP_Negotiation_Request = record { type: uint8; flags: uint8; length: uint16; # must be set to 8 requested_protocols: uint32; } &let { PROTOCOL_RDP: bool = requested_protocols & 0x00; PROTOCOL_SSL: bool = requested_protocols & 0x01; PROTOCOL_HYBRID: bool = requested_protocols & 0x02; PROTOCOL_HYBRID_EX: bool = requested_protocols & 0x08; } &byteorder=littleendian; ###################################################################### # Server X.224 ###################################################################### type Connect_Confirm = record { destination_reference: uint16; source_reference: uint16; flags: uint8; response_type: uint8; response_switch: case response_type of { 0x02 -> neg_resp: RDP_Negotiation_Response; 0x03 -> neg_fail: RDP_Negotiation_Failure; }; }; type RDP_Negotiation_Response = record { flags: uint8; length: uint16; # must be set to 8 selected_protocol: uint32; } &let { # Seems to be encrypted after this message if # selected_protocol > 0 enc: bool = $context.connection.go_encrypted(selected_protocol>0); } &byteorder=littleendian; type RDP_Negotiation_Failure = record { flags: uint8; length: uint16; failure_code: uint32; } &byteorder=littleendian; ###################################################################### # Client MCS ###################################################################### type Client_Header = record { type_length: ASN1Integer; calling_domain_selector: ASN1OctetString; called_domain_selector: ASN1OctetString; upward_flag: ASN1Boolean; target_parameters: ASN1SequenceMeta; targ_parameters_pad: bytestring &length=target_parameters.encoding.length &transient; minimum_parameters: ASN1SequenceMeta; min_parameters_pad: bytestring &length=minimum_parameters.encoding.length &transient; maximum_parameters: ASN1SequenceMeta; max_parameters_pad: bytestring &length=maximum_parameters.encoding.length &transient; # BER encoded OctetString and long variant, can be safely skipped for now user_data_length: uint32; gcc_connection_data: GCC_Client_Connection_Data; gcc_client_create_request: GCC_Client_Create_Request; data_blocks: Data_Block[] &until($input.length() == 0); }; type GCC_Client_Connection_Data = record { key_object_length: uint16; key_object: uint8[key_object_length]; connect_data_connect_pdu: uint16; } &byteorder=bigendian; type GCC_Client_Create_Request = record { extension_bit: uint8; privileges: uint8; numeric_length: uint8; numeric: uint8; termination_method: uint8; number_user_data_sets: uint8; user_data_value_present: uint8; h221_nonstandard_length: uint8; h221_nonstandard_key: RE/Duca/; user_data_value_length: uint16; } &byteorder=bigendian; type Client_Core_Data = record { version_major: uint16; version_minor: uint16; desktop_width: uint16; desktop_height: uint16; color_depth: uint16; sas_sequence: uint16; keyboard_layout: uint32; client_build: uint32; client_name: bytestring &length=32; keyboard_type: uint32; keyboard_sub: uint32; keyboard_function_key: uint32; ime_file_name: bytestring &length=64; # Everything below here is optional and should be handled better. # If some of these fields aren't included it could lead to parse failure. post_beta2_color_depth: uint16; client_product_id: uint16; serial_number: uint32; high_color_depth: uint16; supported_color_depths: uint16; early_capability_flags: uint16; dig_product_id: bytestring &length=64; # There are more optional fields here but they are # annoying to optionally parse in binpac. # Documented here: https://msdn.microsoft.com/en-us/library/cc240510.aspx } &let { SUPPORT_ERRINFO_PDU: bool = early_capability_flags & 0x01; WANT_32BPP_SESSION: bool = early_capability_flags & 0x02; SUPPORT_STATUSINFO_PDU: bool = early_capability_flags & 0x04; STRONG_ASYMMETRIC_KEYS: bool = early_capability_flags & 0x08; SUPPORT_MONITOR_LAYOUT_PDU: bool = early_capability_flags & 0x40; SUPPORT_NETCHAR_AUTODETECT: bool = early_capability_flags & 0x80; SUPPORT_DYNVC_GFX_PROTOCOL: bool = early_capability_flags & 0x0100; SUPPORT_DYNAMIC_TIME_ZONE: bool = early_capability_flags & 0x0200; SUPPORT_HEARTBEAT_PDU: bool = early_capability_flags & 0x0400; } &byteorder=littleendian; ###################################################################### # Server MCS ###################################################################### type Server_Header = record { # We don't need this value, but it's ASN.1 integer in definite length # so I think we can skip over it. type_length: uint8[3]; connect_response_result: ASN1Enumerated; connect_response_called_id: ASN1Integer; connect_response_domain_parameters: ASN1SequenceMeta; # Skipping over domain parameters for now. domain_parameters: bytestring &length=connect_response_domain_parameters.encoding.length &transient; # I think this is another definite length encoded value. user_data_length: uint32; gcc_connection_data: GCC_Server_Connection_Data; gcc_create_response: GCC_Server_Create_Response; data_blocks: Data_Block[] &until($input.length() == 0); } &byteorder=littleendian; type GCC_Server_Connection_Data = record { key_object_length: uint16; key_object: uint8[key_object_length]; connect_data_connect_pdu: uint8; } &byteorder=bigendian; type GCC_Server_Create_Response = record { extension_bit: uint8; node_id: uint16; tag_length: uint8; tag: uint8; result: uint8; number_user_data_sets: uint8; user_data_value_present: uint8; h221_nonstandard_length: uint8; h221_nonstandard_key: RE/McDn/; user_data_value_length: uint16; } &byteorder=bigendian; type Server_Core_Data(h: Data_Header) = record { version_major: uint16; version_minor: uint16; switch1: case h.length of { 8 -> none: empty; default -> client_requested_protocols: uint32; }; } &byteorder=littleendian; type Server_Network_Data = record { mcs_channel_id: uint16; channel_count: uint16; } &byteorder=littleendian; type Server_Security_Data = record { encryption_method: uint32; encryption_level: uint32; server_random_length: uint32; server_cert_length: uint32; server_random: bytestring &length=server_random_length; server_certificate: Server_Certificate &length=server_cert_length; } &let { # Seems to be encrypted after this message if # encryption level is >0 enc: bool = $context.connection.go_encrypted(encryption_level>0); } &byteorder=littleendian; type Server_Certificate = record { version: uint32; switch: case cert_type of { 0x01 -> proprietary: Server_Proprietary_Cert(this); 0x02 -> x509: X509; }; } &let { cert_type: uint32 = version & 0x7FFFFFFF; permanently_issued: bool = (version & 0x80000000) == 0; } &byteorder=littleendian; type Server_Proprietary_Cert(cert: Server_Certificate) = record { signature_algorithm: uint32; key_algorithm: uint32; public_key_blob_type: uint16; public_key_blob_length: uint16; public_key_blob: Public_Key_Blob &length=public_key_blob_length; signature_blob_type: uint16; signature_blob_length: uint16; signature_blob: bytestring &length=signature_blob_length; } &byteorder=littleendian; type Public_Key_Blob = record { magic: bytestring &length=4; key_length: uint32; bit_length: uint32; public_exponent: uint32; modulus: bytestring &length=key_length; } &byteorder=littleendian; type X509 = record { num_of_certs: uint32; certs: X509_Cert_Data[num_of_certs]; } &byteorder=littleendian; type X509_Cert_Data = record { cert_len: uint32; cert: bytestring &length=cert_len; } &byteorder=littleendian; ###################################################################### # ASN.1 Encodings ###################################################################### type ASN1Encoding = record { meta: ASN1EncodingMeta; content: bytestring &length = meta.length; }; type ASN1EncodingMeta = record { tag: uint8; len: uint8; more_len: bytestring &length = long_len ? len & 0x7f : 0; } &let { long_len: bool = (len & 0x80) > 0; length: uint64 = long_len ? binary_to_int64(more_len) : len & 0x7f; }; type ASN1SequenceMeta = record { encoding: ASN1EncodingMeta; }; type ASN1Integer = record { encoding: ASN1Encoding; }; type ASN1OctetString = record { encoding: ASN1Encoding; }; type ASN1ObjectIdentifier = record { encoding: ASN1Encoding; }; type ASN1Boolean = record { encoding: ASN1Encoding; }; type ASN1Enumerated = record { encoding: ASN1Encoding; }; ###################################################################### # ASN.1 Conversion Functions ###################################################################### function binary_to_int64(bs: bytestring): int64 %{ int64 rval = 0; for ( int i = 0; i < bs.length(); ++i ) { uint64 byte = bs[i]; rval |= byte << (8 * (bs.length() - (i + 1))); } return rval; %} refine connection RDP_Conn += { %member{ bool is_encrypted_; %} %init{ is_encrypted_ = false; %} function go_encrypted(should_we: bool): bool %{ if ( should_we ) { is_encrypted_ = true; } return is_encrypted_; %} function is_encrypted(): bool %{ return is_encrypted_; %} };