smb2: add support for decompression

The latest iteration of Microsoft updates to SMB3 added compression to
the protocol. This commit implements decompressing and dissecting
compressed payloads.

The compression algorithms that can be used are "Plain LZ77",
"LZ77+Huffman" and "LZNT1" which you can read more about in the
[MS-XCA] documentation. This set of algorithm is sometimes referred to
as XPRESS.

This commit reuses the existing uncompression API scheme already in
place with zlib and brotli and adds 3 tvb_uncompress_*() function
implemented in:
* epan/tvbuff_lz77.c
* epan/tvbuff_lz77huff.c
* epan/tvbuff_lznt1.c

A new function wmem_array_try_index() was added to the wmem_array API
to make bound checked reads that fail gracefully. New tests for it
have been added as well.

Since both reads (tvb) and writes (wmem_array) are bound checked the
risk for buffer overruns is drastically reduced. LZ77+Huffman has
decoding tables and special care was taken to bound check these.

Simplified versions of the implementations were succesfully tested
against AFL (American Fuzzy Lop) for ~150 millions executions each.

The SMB2/3 dissector was changed to deal with the new transform header
for compressed packets (new protocol_id value) and READ request
flags (COMPRESSED). Badly compressed or encrypted packets are now
reported as such, and the decryption test suite was changed to reflect
that.

This commit also adds a test capture with 1 packet compressed with
each algorithm as returned by Windows Server 2019, along with 3
matching tests in test/suite_dissection.py

Change-Id: I2b84f56541f2f4ee7d886152794b993987dd10e7
Reviewed-on: https://code.wireshark.org/review/33855
Petri-Dish: Anders Broman <a.broman58@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Peter Wu <peter@lekensteyn.nl>
This commit is contained in:
Aurelien Aptel 2019-07-05 16:08:18 +02:00 committed by Peter Wu
parent 1a91aac974
commit 0db39ae59a
14 changed files with 1095 additions and 19 deletions

View File

@ -1688,6 +1688,9 @@ libwireshark.so.0 libwireshark0 #MINVER#
tvb_captured_length_remaining@Base 1.12.0~rc1
tvb_child_uncompress@Base 1.12.0~rc1
tvb_child_uncompress_brotli@Base 3.1.0
tvb_child_uncompress_lz77@Base 3.1.0
tvb_child_uncompress_lz77huff@Base 3.1.0
tvb_child_uncompress_lznt1@Base 3.1.0
tvb_clone@Base 1.12.0~rc1
tvb_clone_offset_len@Base 1.12.0~rc1
tvb_composite_append@Base 1.9.1
@ -1810,6 +1813,9 @@ libwireshark.so.0 libwireshark0 #MINVER#
tvb_strsize@Base 1.9.1
tvb_uncompress@Base 1.9.1
tvb_uncompress_brotli@Base 3.1.0
tvb_uncompress_lz77@Base 3.1.0
tvb_uncompress_lz77huff@Base 3.1.0
tvb_uncompress_lznt1@Base 3.1.0
tvb_unicode_strsize@Base 1.9.1
tvb_ws_mempbrk_pattern_guint8@Base 1.99.3
tvbparse_casestring@Base 1.9.1
@ -1959,6 +1965,7 @@ libwireshark.so.0 libwireshark0 #MINVER#
wmem_array_set_null_terminator@Base 2.1.0
wmem_array_sized_new@Base 1.12.0~rc1
wmem_array_sort@Base 1.12.0~rc1
wmem_array_try_index@Base 3.1.0
wmem_ascii_strdown@Base 1.12.0~rc1
wmem_cleanup@Base 1.12.0~rc1
wmem_destroy_allocator@Base 1.9.1

View File

@ -270,6 +270,9 @@ set(LIBWIRESHARK_NONGENERATED_FILES
tvbuff_real.c
tvbuff_subset.c
tvbuff_zlib.c
tvbuff_lz77.c
tvbuff_lz77huff.c
tvbuff_lznt1.c
uat.c
value_string.c
unit_strings.c

View File

@ -51,8 +51,13 @@
void proto_register_smb2(void);
void proto_reg_handoff_smb2(void);
#define SMB2_NORM_HEADER 0xFE
#define SMB2_ENCR_HEADER 0xFD
#define SMB2_COMP_HEADER 0xFC
static const char smb_header_label[] = "SMB2 Header";
static const char smb_transform_header_label[] = "SMB2 Transform Header";
static const char smb_comp_transform_header_label[] = "SMB2 Compression Transform Header";
static const char smb_bad_header_label[] = "Bad SMB2 Header";
static int proto_smb2 = -1;
static int hf_smb2_cmd = -1;
@ -174,6 +179,10 @@ static int hf_smb2_write_count = -1;
static int hf_smb2_write_remaining = -1;
static int hf_smb2_read_length = -1;
static int hf_smb2_read_remaining = -1;
static int hf_smb2_read_padding = -1;
static int hf_smb2_read_flags = -1;
static int hf_smb2_read_flags_unbuffered = -1;
static int hf_smb2_read_flags_compressed = -1;
static int hf_smb2_file_offset = -1;
static int hf_smb2_qfr_length = -1;
static int hf_smb2_qfr_usage = -1;
@ -483,8 +492,12 @@ static int hf_smb2_transform_msg_size = -1;
static int hf_smb2_transform_reserved = -1;
static int hf_smb2_transform_enc_alg = -1;
static int hf_smb2_transform_encrypted_data = -1;
static int hf_smb2_server_component_smb2 = -1;
static int hf_smb2_server_component_smb2_transform = -1;
static int hf_smb2_protocol_id = -1;
static int hf_smb2_comp_transform_orig_size = -1;
static int hf_smb2_comp_transform_comp_alg = -1;
static int hf_smb2_comp_transform_reserved = -1;
static int hf_smb2_comp_transform_offset = -1;
static int hf_smb2_comp_transform_data = -1;
static int hf_smb2_truncated = -1;
static int hf_smb2_pipe_fragments = -1;
static int hf_smb2_pipe_fragment = -1;
@ -528,6 +541,7 @@ static gint ett_smb2_olb = -1;
static gint ett_smb2_ea = -1;
static gint ett_smb2_header = -1;
static gint ett_smb2_encrypted = -1;
static gint ett_smb2_compressed = -1;
static gint ett_smb2_command = -1;
static gint ett_smb2_secblob = -1;
static gint ett_smb2_negotiate_context_element = -1;
@ -624,6 +638,7 @@ static gint ett_smb2_error_data = -1;
static gint ett_smb2_error_context = -1;
static gint ett_smb2_error_redir_context = -1;
static gint ett_smb2_error_redir_ip_list = -1;
static gint ett_smb2_read_flags = -1;
static expert_field ei_smb2_invalid_length = EI_INIT;
static expert_field ei_smb2_bad_response = EI_INIT;
@ -912,6 +927,7 @@ static const val64_string nfs_type_vals[] = {
};
#define SMB2_NUM_PROCEDURES 256
#define MAX_UNCOMPRESSED_SIZE (1<<24) /* 16MB */
static int dissect_windows_sockaddr_storage(tvbuff_t *, packet_info *, proto_tree *, int, int);
static void dissect_smb2_error_data(tvbuff_t *, packet_info *, proto_tree *, int, int, smb2_info_t *);
@ -7281,6 +7297,19 @@ dissect_smb2_ioctl_response(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
}
#define SMB2_READFLAG_READ_UNBUFFERED 0x01
#define SMB2_READFLAG_READ_COMPRESSED 0x02
static const true_false_string tfs_read_unbuffered = {
"Client is asking for UNBUFFERED read",
"Client is NOT asking for UNBUFFERED read"
};
static const true_false_string tfs_read_compressed = {
"Client is asking for COMPRESSED data",
"Client is NOT asking for COMPRESSED data"
};
static int
dissect_smb2_read_request(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, smb2_info_t *si)
{
@ -7289,12 +7318,23 @@ dissect_smb2_read_request(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, i
guint32 len;
guint64 off;
static const int *flags[] = {
&hf_smb2_read_flags_unbuffered,
&hf_smb2_read_flags_compressed,
NULL
};
/* buffer code */
offset = dissect_smb2_buffercode(tree, tvb, offset, NULL);
/* padding and reserved */
proto_tree_add_item(tree, hf_smb2_reserved, tvb, offset, 2, ENC_NA);
offset += 2;
/* padding */
proto_tree_add_item(tree, hf_smb2_read_padding, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
/* flags */
proto_tree_add_bitmask(tree, tvb, offset, hf_smb2_read_flags,
ett_smb2_read_flags, flags, ENC_LITTLE_ENDIAN);
offset += 1;
/* length */
len = tvb_get_letohl(tvb, offset);
@ -9397,6 +9437,90 @@ decrypt_smb_payload(packet_info *pinfo,
}
#endif
static int
dissect_smb2_comp_transform_header(packet_info *pinfo, proto_tree *tree,
tvbuff_t *tvb, int offset,
smb2_comp_transform_info_t *scti,
tvbuff_t **comp_tvb,
tvbuff_t **plain_tvb)
{
guint final_size;
guint8 *orig_data;
gint in_size;
tvbuff_t *uncomp_tvb = NULL;
*comp_tvb = NULL;
*plain_tvb = NULL;
/* SMB2_COMPRESSION_TRANSFORM marker */
proto_tree_add_item(tree, hf_smb2_protocol_id, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
proto_tree_add_item_ret_uint(tree, hf_smb2_comp_transform_orig_size, tvb, offset, 4, ENC_LITTLE_ENDIAN, &scti->orig_size);
offset += 4;
proto_tree_add_item_ret_uint(tree, hf_smb2_comp_transform_comp_alg, tvb, offset, 2, ENC_LITTLE_ENDIAN, &scti->alg);
offset += 2;
proto_tree_add_item(tree, hf_smb2_comp_transform_reserved, tvb, offset, 2, ENC_NA);
offset += 2;
proto_tree_add_item_ret_uint(tree, hf_smb2_comp_transform_offset, tvb, offset, 4, ENC_LITTLE_ENDIAN, &scti->comp_offset);
offset += 4;
*comp_tvb = tvb_new_subset_length(tvb, offset, tvb_reported_length_remaining(tvb, offset));
if (scti->orig_size > MAX_UNCOMPRESSED_SIZE || scti->comp_offset > MAX_UNCOMPRESSED_SIZE) {
col_append_str(pinfo->cinfo, COL_INFO, "Comp. SMB3 (too big)");
goto out;
}
/* alloc enough space for partial normal packet + uncompressed segment */
final_size = scti->orig_size + scti->comp_offset;
orig_data = (guint8*)wmem_alloc0(pinfo->pool, final_size);
/* copy start of partial packet */
tvb_memcpy(tvb, orig_data, offset, scti->comp_offset);
in_size = tvb_reported_length_remaining(tvb, offset + scti->comp_offset);
/* decompress compressed segment */
switch (scti->alg) {
case SMB2_COMP_ALG_LZ77:
uncomp_tvb = tvb_uncompress_lz77(tvb, offset + scti->comp_offset, in_size);
break;
case SMB2_COMP_ALG_LZ77HUFF:
uncomp_tvb = tvb_uncompress_lz77huff(tvb, offset + scti->comp_offset, in_size);
break;
case SMB2_COMP_ALG_LZNT1:
uncomp_tvb = tvb_uncompress_lznt1(tvb, offset + scti->comp_offset, in_size);
break;
case SMB2_COMP_ALG_NONE:
default:
col_append_str(pinfo->cinfo, COL_INFO, "Comp. SMB3 (unknown)");
uncomp_tvb = NULL;
goto out;
}
if (!uncomp_tvb || tvb_reported_length(uncomp_tvb) != scti->orig_size) {
/* decompression error */
col_append_str(pinfo->cinfo, COL_INFO, "Comp. SMB3 (invalid)");
goto out;
}
/* write decompressed segment at the end of partial packet */
tvb_memcpy(uncomp_tvb, orig_data + scti->comp_offset, 0, scti->orig_size);
col_append_str(pinfo->cinfo, COL_INFO, "Decomp. SMB3");
*plain_tvb = tvb_new_child_real_data(tvb, orig_data, final_size, final_size);
add_new_data_source(pinfo, *plain_tvb, "Decomp. SMB3");
out:
if (uncomp_tvb)
tvb_free(uncomp_tvb);
return offset;
}
static int
dissect_smb2_transform_header(packet_info *pinfo, proto_tree *tree,
tvbuff_t *tvb, int offset,
@ -9570,7 +9694,7 @@ dissect_smb2_tid_sesid(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb,
static int
dissect_smb2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, gboolean first_in_chain)
{
gboolean smb2_transform_header = FALSE;
int msg_type;
proto_item *item = NULL;
proto_tree *tree = NULL;
proto_item *header_item = NULL;
@ -9582,19 +9706,34 @@ dissect_smb2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, gboolea
smb2_saved_info_t *ssi = NULL, ssi_key;
smb2_info_t *si;
smb2_transform_info_t *sti;
smb2_comp_transform_info_t *scti;
char *fid_name;
guint32 open_frame,close_frame;
smb2_eo_file_info_t *eo_file_info;
e_ctx_hnd *policy_hnd_hashtablekey;
sti = wmem_new(wmem_packet_scope(), smb2_transform_info_t);
scti = wmem_new(wmem_packet_scope(), smb2_comp_transform_info_t);
si = wmem_new0(wmem_packet_scope(), smb2_info_t);
si->top_tree = parent_tree;
if (tvb_get_guint8(tvb, 0) == 0xfd) {
smb2_transform_header = TRUE;
msg_type = tvb_get_guint8(tvb, 0);
switch (msg_type) {
case SMB2_COMP_HEADER:
label = smb_comp_transform_header_label;
break;
case SMB2_ENCR_HEADER:
label = smb_transform_header_label;
break;
case SMB2_NORM_HEADER:
label = smb_header_label;
break;
default:
label = smb_bad_header_label;
break;
}
/* find which conversation we are part of and get the data for that
* conversation
*/
@ -9628,6 +9767,7 @@ dissect_smb2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, gboolea
}
sti->conv = si->conv;
scti->conv = si->conv;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SMB2");
if (first_in_chain) {
@ -9644,9 +9784,9 @@ dissect_smb2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, gboolea
/* Decode the header */
if (!smb2_transform_header) {
if (msg_type == SMB2_NORM_HEADER) {
/* SMB2 marker */
proto_tree_add_item(header_tree, hf_smb2_server_component_smb2, tvb, offset, 4, ENC_NA);
proto_tree_add_item(header_tree, hf_smb2_protocol_id, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
/* we need the flags before we know how to parse the credits field */
@ -9838,13 +9978,13 @@ dissect_smb2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, gboolea
/* Decode the payload */
offset = dissect_smb2_command(pinfo, tree, tvb, offset, si);
} else {
} else if (msg_type == SMB2_ENCR_HEADER) {
proto_tree *enc_tree;
tvbuff_t *enc_tvb = NULL;
tvbuff_t *plain_tvb = NULL;
/* SMB2_TRANSFORM marker */
proto_tree_add_item(header_tree, hf_smb2_server_component_smb2_transform, tvb, offset, 4, ENC_NA);
proto_tree_add_item(header_tree, hf_smb2_protocol_id, tvb, offset, 4, ENC_BIG_ENDIAN);
offset += 4;
offset = dissect_smb2_transform_header(pinfo, header_tree, tvb, offset, sti,
@ -9863,6 +10003,38 @@ dissect_smb2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, gboolea
if (tvb_reported_length_remaining(tvb, offset) > 0) {
chain_offset = offset;
}
} else if (msg_type == SMB2_COMP_HEADER) {
proto_tree *comp_tree;
tvbuff_t *plain_tvb = NULL;
tvbuff_t *comp_tvb = NULL;
offset = dissect_smb2_comp_transform_header(pinfo, tree, tvb, offset,
scti, &comp_tvb, &plain_tvb);
if (plain_tvb) {
comp_tree = proto_tree_add_subtree(tree, plain_tvb, 0,
tvb_reported_length_remaining(plain_tvb, 0),
ett_smb2_compressed, NULL,
"Compressed SMB3 data");
dissect_smb2(plain_tvb, pinfo, comp_tree, FALSE);
} else {
comp_tree = proto_tree_add_subtree(tree, tvb, offset,
tvb_reported_length_remaining(tvb, offset),
ett_smb2_compressed, NULL,
"Compressed SMB3 data");
/* show the compressed payload only if we cant uncompress it */
proto_tree_add_item(comp_tree, hf_smb2_comp_transform_data,
tvb, offset,
tvb_reported_length_remaining(tvb, offset),
ENC_NA);
}
offset += tvb_reported_length_remaining(tvb, offset);
} else {
col_append_str(pinfo->cinfo, COL_INFO, "Invalid header");
/* bad packet after decompressing/decrypting */
offset += tvb_reported_length_remaining(tvb, offset);
}
if (chain_offset > 0) {
@ -9880,12 +10052,14 @@ dissect_smb2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, gboolea
static gboolean
dissect_smb2_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
{
guint8 b;
/* must check that this really is a smb2 packet */
if (tvb_captured_length(tvb) < 4)
return FALSE;
if (((tvb_get_guint8(tvb, 0) != 0xfe) && (tvb_get_guint8(tvb, 0) != 0xfd))
b = tvb_get_guint8(tvb, 0);
if (((b != SMB2_COMP_HEADER) && (b != SMB2_ENCR_HEADER) && (b != SMB2_NORM_HEADER))
|| (tvb_get_guint8(tvb, 1) != 'S')
|| (tvb_get_guint8(tvb, 2) != 'M')
|| (tvb_get_guint8(tvb, 3) != 'B') ) {
@ -10222,6 +10396,26 @@ proto_register_smb2(void)
NULL, 0, NULL, HFILL }
},
{ &hf_smb2_read_padding,
{ "Padding", "smb2.read_padding", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }
},
{ &hf_smb2_read_flags,
{ "Flags", "smb2.read_flags", FT_UINT8, BASE_HEX,
NULL, 0, NULL, HFILL }
},
{ &hf_smb2_read_flags_unbuffered,
{ "Unbuffered", "smb2.read_flags.unbuffered", FT_BOOLEAN, 8,
TFS(&tfs_read_unbuffered), SMB2_READFLAG_READ_UNBUFFERED, "If client requests unbuffered read", HFILL }
},
{ &hf_smb2_read_flags_compressed,
{ "Compressed", "smb2.read_flags.compressed", FT_BOOLEAN, 8,
TFS(&tfs_read_compressed), SMB2_READFLAG_READ_COMPRESSED, "If client requests compressed response", HFILL }
},
{ &hf_smb2_create_flags,
{ "Create Flags", "smb2.create_flags", FT_UINT64, BASE_HEX,
NULL, 0, NULL, HFILL }
@ -12026,13 +12220,33 @@ proto_register_smb2(void)
NULL, 0, NULL, HFILL }
},
{ &hf_smb2_server_component_smb2,
{ "Server Component: SMB2", "smb2.server_component_smb2", FT_NONE, BASE_NONE,
{ &hf_smb2_comp_transform_orig_size,
{ "OriginalSize", "smb2.header.comp_transform.original_size", FT_UINT32, BASE_DEC,
NULL, 0, NULL, HFILL }
},
{ &hf_smb2_server_component_smb2_transform,
{ "Server Component: SMB2_TRANSFORM", "smb2.server_component_smb2_transform", FT_NONE, BASE_NONE,
{ &hf_smb2_comp_transform_comp_alg,
{ "CompressionAlgorithm", "smb2.header.comp_transform.comp_alg", FT_UINT16, BASE_HEX,
VALS(smb2_comp_alg_types), 0, NULL, HFILL }
},
{ &hf_smb2_comp_transform_reserved,
{ "Reserved", "smb2.header.comp_transform.reserved", FT_BYTES, BASE_NONE,
NULL, 0, NULL, HFILL }
},
{ &hf_smb2_comp_transform_offset,
{ "Offset", "smb2.header.comp_transform.offset", FT_UINT32, BASE_HEX,
NULL, 0, NULL, HFILL }
},
{ &hf_smb2_comp_transform_data,
{ "CompressedData", "smb2.header.comp_transform.data", FT_BYTES, BASE_NONE,
NULL, 0, NULL, HFILL }
},
{ &hf_smb2_protocol_id,
{ "ProtocolId", "smb2.protocol_id", FT_UINT32, BASE_HEX,
NULL, 0, NULL, HFILL }
},
@ -12211,6 +12425,7 @@ proto_register_smb2(void)
&ett_smb2_olb,
&ett_smb2_header,
&ett_smb2_encrypted,
&ett_smb2_compressed,
&ett_smb2_command,
&ett_smb2_secblob,
&ett_smb2_negotiate_context_element,
@ -12307,6 +12522,7 @@ proto_register_smb2(void)
&ett_smb2_error_context,
&ett_smb2_error_redir_context,
&ett_smb2_error_redir_ip_list,
&ett_smb2_read_flags,
};
static ei_register_info ei[] = {

View File

@ -205,6 +205,14 @@ typedef struct _smb2_transform_info_t {
smb2_sesid_info_t *session;
} smb2_transform_info_t;
typedef struct _smb2_comp_transform_info_t {
guint orig_size;
guint alg;
guint comp_offset;
smb2_conv_info_t *conv;
smb2_sesid_info_t *session;
} smb2_comp_transform_info_t;
int dissect_smb2_FILE_OBJECTID_BUFFER(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, int offset);
int dissect_smb2_ioctl_function(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, int offset, guint32 *ioctl_function);

View File

@ -925,6 +925,66 @@ WS_DLL_PUBLIC tvbuff_t *tvb_uncompress_brotli(tvbuff_t *tvb, const int offset,
WS_DLL_PUBLIC tvbuff_t *tvb_child_uncompress_brotli(tvbuff_t *parent, tvbuff_t *tvb,
const int offset, int comprlen);
/* From tvbuff_lz77.c */
/**
* Uncompresses a Microsoft Plain LZ77 compressed payload inside a
* tvbuff at offset with length comprlen. Returns an uncompressed
* tvbuffer if uncompression succeeded or NULL if uncompression
* failed.
*/
WS_DLL_PUBLIC tvbuff_t *tvb_uncompress_lz77(tvbuff_t *tvb,
const int offset, int comprlen);
/**
* Uncompresses a Microsoft Plain LZ77 compressed payload inside a
* tvbuff at offset with length comprlen. Returns an uncompressed
* tvbuffer attached to tvb if uncompression succeeded or NULL if
* uncompression failed.
*/
WS_DLL_PUBLIC tvbuff_t *tvb_child_uncompress_lz77(tvbuff_t *parent,
tvbuff_t *tvb, const int offset, int comprlen);
/* From tvbuff_lz77huff.c */
/**
* Uncompresses a Microsoft LZ77+Huffman compressed payload inside a
* tvbuff at offset with length comprlen. Returns an uncompressed
* tvbuffer if uncompression succeeded or NULL if uncompression
* failed.
*/
WS_DLL_PUBLIC tvbuff_t *tvb_uncompress_lz77huff(tvbuff_t *tvb,
const int offset, int comprlen);
/**
* Uncompresses a Microsoft LZ77+Huffman compressed payload inside a
* tvbuff at offset with length comprlen. Returns an uncompressed
* tvbuffer attached to tvb if uncompression succeeded or NULL if
* uncompression failed.
*/
WS_DLL_PUBLIC tvbuff_t *tvb_child_uncompress_lz77huff(tvbuff_t *parent,
tvbuff_t *tvb, const int offset, int comprlen);
/* From tvbuff_lznt1.c */
/**
* Uncompresses a Microsoft LZNT1 compressed payload inside
* a tvbuff at offset with length comprlen. Returns an uncompressed
* tvbuffer if uncompression succeeded or NULL if uncompression
* failed.
*/
WS_DLL_PUBLIC tvbuff_t *tvb_uncompress_lznt1(tvbuff_t *tvb,
const int offset, int comprlen);
/**
* Uncompresses a Microsoft LZNT1 compressed payload inside
* a tvbuff at offset with length comprlen. Returns an uncompressed
* tvbuffer if uncompression succeeded or NULL if uncompression
* failed.
*/
WS_DLL_PUBLIC tvbuff_t *tvb_child_uncompress_lznt1(tvbuff_t *parent,
tvbuff_t *tvb, const int offset, int comprlen);
/* From tvbuff_base64.c */
/** Return a tvb that contains the binary representation of a base64

155
epan/tvbuff_lz77.c Normal file
View File

@ -0,0 +1,155 @@
/*
* Decompression code for Plain LZ77. This encoding is used by
* Microsoft in various file formats and protocols including SMB3.
*
* Copyright (C) 2019 Aurélien Aptel
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <glib.h>
#include <epan/exceptions.h>
#include <epan/tvbuff.h>
#include <epan/wmem/wmem.h>
#define MAX_INPUT_SIZE (16*1024*1024) /* 16MB */
static gboolean do_uncompress(tvbuff_t *tvb, int offset, int in_size,
wmem_array_t *obuf)
{
guint buf_flags = 0, buf_flag_count = 0;
int in_off = 0;
int last_length_half_byte = 0;
guint match_bytes, match_len, match_off;
guint i;
if (!tvb)
return FALSE;
if (in_size > MAX_INPUT_SIZE)
return FALSE;
while (1) {
if (buf_flag_count == 0) {
buf_flags = tvb_get_letohl(tvb, offset+in_off);
in_off += 4;
buf_flag_count = 32;
}
buf_flag_count--;
if ((buf_flags & (1 << buf_flag_count)) == 0) {
guint8 v = tvb_get_guint8(tvb, offset+in_off);
wmem_array_append_one(obuf, v);
in_off++;
} else {
if (in_off == in_size)
return TRUE;
match_bytes = tvb_get_letohs(tvb, offset+in_off);
in_off += 2;
match_len = match_bytes % 8;
match_off = (match_bytes/8) + 1;
if (match_len == 7) {
if (last_length_half_byte == 0) {
match_len = tvb_get_guint8(tvb, offset+in_off);
match_len = match_len % 16;
last_length_half_byte = in_off;
in_off++;
} else {
match_len = tvb_get_guint8(tvb, offset+last_length_half_byte);
match_len = match_len / 16;
last_length_half_byte = 0;
}
if (match_len == 15) {
match_len = tvb_get_guint8(tvb, offset+in_off);
in_off++;
if (match_len == 255) {
match_len = tvb_get_letohs(tvb, offset+in_off);
in_off += 2;
if (match_len == 0) {
/* This case isn't documented */
match_len = tvb_get_letohs(tvb, offset+in_off);
in_off += 4;
}
if (match_len < 15+7)
return FALSE;
match_len -= (15 + 7);
}
match_len += 15;
}
match_len += 7;
}
match_len += 3;
for (i = 0; i < match_len; i++) {
guint8 byte;
if (match_off > wmem_array_get_count(obuf))
return FALSE;
if (wmem_array_try_index(obuf, wmem_array_get_count(obuf)-match_off, &byte))
return FALSE;
wmem_array_append_one(obuf, byte);
}
}
}
return TRUE;
}
tvbuff_t *
tvb_uncompress_lz77(tvbuff_t *tvb, const int offset, int in_size)
{
volatile gboolean ok = FALSE;
wmem_allocator_t *pool;
wmem_array_t *obuf;
tvbuff_t *out;
pool = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
obuf = wmem_array_sized_new(pool, 1, in_size*2);
TRY {
ok = do_uncompress(tvb, offset, in_size, obuf);
} CATCH_ALL {
ok = FALSE;
}
ENDTRY;
if (ok) {
/*
* Cannot pass a tvb free callback that frees the wmem
* pool, so we make an make an extra copy that uses
* bare pointers. This could be optimized if tvb API
* had a free pool callback of some sort.
*/
guint size = wmem_array_get_count(obuf);
guint8 *p = (guint8 *)g_malloc(size);
memcpy(p, wmem_array_get_raw(obuf), size);
out = tvb_new_real_data(p, size, size);
tvb_set_free_cb(out, g_free);
} else {
out = NULL;
}
wmem_destroy_allocator(pool);
return out;
}
tvbuff_t *
tvb_child_uncompress_lz77(tvbuff_t *parent, tvbuff_t *tvb, const int offset, int in_size)
{
tvbuff_t *new_tvb = tvb_uncompress_lz77(tvb, offset, in_size);
if (new_tvb)
tvb_set_child_real_data_tvbuff(parent, new_tvb);
return new_tvb;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: t
* End:
*
* vi: set shiftwidth=8 tabstop=8 noexpandtab:
* :indentSize=8:tabSize=8:noTabs=false:
*/

415
epan/tvbuff_lz77huff.c Normal file
View File

@ -0,0 +1,415 @@
/*
* Decompression code for LZ77+Huffman. This encoding is used by
* Microsoft in various file formats and protocols including SMB3.
*
* Initial code from Samba re-licensed with Samuel's permission.
* Copyright (C) Samuel Cabrero 2017
*
* Glib-ification, extra error-checking and WS integration
* Copyright (C) Aurélien Aptel 2019
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <glib.h>
#include <stdlib.h> /* qsort */
#include <epan/exceptions.h>
#include <epan/tvbuff.h>
#include <epan/wmem/wmem.h>
#define MAX_INPUT_SIZE (16*1024*1024) /* 16MB */
#define TREE_SIZE 1024
#define ENCODED_TREE_SIZE 256
struct input {
tvbuff_t *tvb;
int offset;
gsize size;
};
/**
* Represents a node in a Huffman prefix code tree
*/
struct prefix_code_node {
/* Stores the symbol encoded by this node in the prefix code tree */
guint16 symbol;
/* Indicates whether this node is a leaf in the tree */
gboolean leaf;
/* Points to the nodes two children. The value NIL is used to
* indicate that a particular child does not exist */
struct prefix_code_node *child[2];
};
/**
* Represent information about a Huffman-encoded symbol
*/
struct prefix_code_symbol {
/* Stores the symbol */
guint16 symbol;
/* Stores the symbols Huffman prefix code length */
guint16 length;
};
/**
* Represent a byte array as a bit string from which individual bits can
* be read
*/
struct bitstring {
/* The byte array */
const struct input *input;
/* The index in source from which the next set of bits will be pulled
* when the bits in mask have been consumed */
guint32 index;
/* Stores the next bits to be consumed in the bit string */
guint32 mask;
/* Stores the number of bits in mask that remain to be consumed */
gint32 bits;
};
struct hf_tree {
struct prefix_code_node nodes[TREE_SIZE];
struct prefix_code_node *root;
};
static gboolean is_node_valid(struct hf_tree *tree, struct prefix_code_node *node)
{
return (node && node >= tree->nodes && node < tree->nodes + TREE_SIZE);
}
/**
* Links a symbol's prefix_code_node into its correct position in a Huffman
* prefix code tree
*/
static int prefix_code_tree_add_leaf(struct hf_tree *tree,
guint32 leaf_index,
guint32 mask,
guint32 bits,
guint32 *out_index)
{
struct prefix_code_node *node = &tree->nodes[0];
guint32 i = leaf_index + 1;
guint32 child_index;
if (leaf_index >= TREE_SIZE)
return -1;
while (bits > 1) {
bits = bits - 1;
child_index = (mask >> bits) & 1;
if (node->child[child_index] == NULL) {
if (i >= TREE_SIZE)
return -1;
node->child[child_index] = &tree->nodes[i];
tree->nodes[i].leaf = FALSE;
i = i + 1;
}
node = node->child[child_index];
if (!is_node_valid(tree, node))
return -1;
}
node->child[mask & 1] = &tree->nodes[leaf_index];
*out_index = i;
return 0;
}
/**
* Determines the sort order of one prefix_code_symbol relative to another
*/
static int compare_symbols(const void *ve1, const void *ve2)
{
const struct prefix_code_symbol *e1 = (const struct prefix_code_symbol *)ve1;
const struct prefix_code_symbol *e2 = (const struct prefix_code_symbol *)ve2;
if (e1->length < e2->length)
return -1;
else if (e1->length > e2->length)
return 1;
else if (e1->symbol < e2->symbol)
return -1;
else if (e1->symbol > e2->symbol)
return 1;
else
return 0;
}
/**
* Rebuilds the Huffman prefix code tree that will be used to decode symbols
* during decompression
*/
static int PrefixCodeTreeRebuild( struct hf_tree *tree,
const struct input *input)
{
struct prefix_code_symbol symbolInfo[512];
guint32 i, j, mask, bits;
int rc;
for (i = 0; i < TREE_SIZE; i++) {
tree->nodes[i].symbol = 0;
tree->nodes[i].leaf = FALSE;
tree->nodes[i].child[0] = NULL;
tree->nodes[i].child[1] = NULL;
}
if (input->size < ENCODED_TREE_SIZE)
return FALSE;
for (i = 0; i < ENCODED_TREE_SIZE; i++) {
symbolInfo[2*i].symbol = 2*i;
symbolInfo[2*i].length = tvb_get_guint8(input->tvb, input->offset+i) & 15;
symbolInfo[2*i+1].symbol = 2*i+1;
symbolInfo[2*i+1].length = tvb_get_guint8(input->tvb, input->offset+i) >> 4;
}
qsort(symbolInfo, 512, sizeof(symbolInfo[0]), compare_symbols);
i = 0;
while (i < 512 && symbolInfo[i].length == 0) {
i = i + 1;
}
mask = 0;
bits = 1;
tree->root = &tree->nodes[0];
tree->root->leaf = FALSE;
j = 1;
for (; i < 512; i++) {
tree->nodes[j].symbol = symbolInfo[i].symbol;
tree->nodes[j].leaf = TRUE;
mask <<= symbolInfo[i].length - bits;
bits = symbolInfo[i].length;
rc = prefix_code_tree_add_leaf(tree, j, mask, bits, &j);
if (rc)
return rc;
mask += 1;
}
return 0;
}
/**
* Initializes a bitstream data structure
*/
static void bitstring_init(struct bitstring *bstr,
const struct input *input,
guint32 index)
{
bstr->mask = tvb_get_letohs(input->tvb, input->offset+index);
bstr->mask <<= sizeof(bstr->mask) * 8 - 16;
index += 2;
bstr->mask += tvb_get_letohs(input->tvb, input->offset+index);
index += 2;
bstr->bits = 32;
bstr->input = input;
bstr->index = index;
}
/**
* Returns the next n bits from the front of a bit string.
*/
static guint32 bitstring_lookup(struct bitstring *bstr, guint32 n)
{
if (n == 0 || bstr->bits < 0 || n > (guint32)bstr->bits) {
return 0;
}
return bstr->mask >> (sizeof(bstr->mask) * 8 - n);
}
/**
* Advances the bit string's cursor by n bits.
*/
static void bitstring_skip(struct bitstring *bstr, guint32 n)
{
bstr->mask = bstr->mask << n;
bstr->bits = bstr->bits - n;
if (bstr->bits < 16) {
bstr->mask += tvb_get_letohs(bstr->input->tvb,
bstr->input->offset + bstr->index)
<< (16 - bstr->bits);
bstr->index = bstr->index + 2;
bstr->bits = bstr->bits + 16;
}
}
/**
* Returns the symbol encoded by the next prefix code in a bit string.
*/
static int prefix_code_tree_decode_symbol(struct hf_tree *tree,
struct bitstring *bstr,
guint32 *out_symbol)
{
guint32 bit;
struct prefix_code_node *node = tree->root;
do {
bit = bitstring_lookup(bstr, 1);
bitstring_skip(bstr, 1);
node = node->child[bit];
if (!is_node_valid(tree, node))
return -1;
} while (node->leaf == FALSE);
*out_symbol = node->symbol;
return 0;
}
static gboolean do_uncompress(struct input *input,
wmem_array_t *obuf)
{
guint32 symbol;
guint32 length;
gint32 match_offset;
int rc;
struct hf_tree tree = {0};
struct bitstring bstr = {0};
if (!input->tvb)
return FALSE;
if (input->size > MAX_INPUT_SIZE)
return FALSE;
rc = PrefixCodeTreeRebuild(&tree, input);
if (rc)
return FALSE;
bitstring_init(&bstr, input, ENCODED_TREE_SIZE);
while (1) {
rc = prefix_code_tree_decode_symbol(&tree, &bstr, &symbol);
if (rc < 0)
return FALSE;
if (symbol < 256) {
guint8 v = symbol & 0xFF;
wmem_array_append_one(obuf, v);
} else {
if (symbol == 256) {
/* EOF symbol */
return bstr.index == bstr.input->size;
}
symbol = symbol - 256;
length = symbol & 0xF;
symbol = symbol >> 4;
match_offset = (1U << symbol) + bitstring_lookup(&bstr, symbol);
match_offset *= -1;
if (length == 15) {
if (bstr.index >= bstr.input->size)
return FALSE;
length = tvb_get_guint8(bstr.input->tvb,
bstr.input->offset+bstr.index) + 15;
bstr.index += 1;
if (length == 270) {
if (bstr.index+1 >= bstr.input->size)
return FALSE;
length = tvb_get_letohs(bstr.input->tvb, bstr.input->offset+bstr.index);
bstr.index += 2;
}
}
bitstring_skip(&bstr, symbol);
length += 3;
do {
guint8 byte;
int index = wmem_array_get_count(obuf)+match_offset;
if (index < 0)
return FALSE;
if (wmem_array_try_index(obuf, index, &byte))
return FALSE;
wmem_array_append_one(obuf, byte);
length--;
} while (length != 0);
}
}
return TRUE;
}
tvbuff_t *
tvb_uncompress_lz77huff(tvbuff_t *tvb,
const int offset,
int input_size)
{
volatile gboolean ok;
wmem_allocator_t *pool;
wmem_array_t *obuf;
tvbuff_t *out;
struct input input = {
.tvb = tvb,
.offset = offset,
.size = input_size
};
pool = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
obuf = wmem_array_sized_new(pool, 1, input_size*2);
TRY {
ok = do_uncompress(&input, obuf);
} CATCH_ALL {
ok = FALSE;
}
ENDTRY;
if (ok) {
/*
* Cannot pass a tvb free callback that frees the wmem
* pool, so we make an make an extra copy that uses
* bare pointers. This could be optimized if tvb API
* had a free pool callback of some sort.
*/
guint size = wmem_array_get_count(obuf);
guint8 *p = (guint8 *)g_malloc(size);
memcpy(p, wmem_array_get_raw(obuf), size);
out = tvb_new_real_data(p, size, size);
tvb_set_free_cb(out, g_free);
} else {
out = NULL;
}
wmem_destroy_allocator(pool);
return out;
}
tvbuff_t *
tvb_child_uncompress_lz77huff(tvbuff_t *parent, tvbuff_t *tvb, const int offset, int in_size)
{
tvbuff_t *new_tvb = tvb_uncompress_lz77huff(tvb, offset, in_size);
if (new_tvb)
tvb_set_child_real_data_tvbuff(parent, new_tvb);
return new_tvb;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: t
* End:
*
* vi: set shiftwidth=8 tabstop=8 noexpandtab:
* :indentSize=8:tabSize=8:noTabs=false:
*/

165
epan/tvbuff_lznt1.c Normal file
View File

@ -0,0 +1,165 @@
/*
* Decompression code for LZ77+Huffman. This encoding is used by
* Microsoft in various file formats and protocols including SMB3.
*
* Copyright (C) 2019 Aurélien Aptel
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <glib.h>
#include <epan/exceptions.h>
#include <epan/tvbuff.h>
#include <epan/wmem/wmem.h>
#define MAX_INPUT_SIZE (16*1024*1024) /* 16MB */
static gboolean
uncompress_chunk(tvbuff_t *tvb, int offset, int in_size, wmem_array_t *obuf)
{
int in_off = 0, out_off = 0, out_start = 0;
guint8 flags;
guint i, j, val, pos;
out_start = wmem_array_get_count(obuf);
while (in_off < in_size) {
flags = tvb_get_guint8(tvb, offset+in_off);
in_off++;
for (i = 0; i < 8; i++) {
if (0 == ((flags>>i)&1)) {
val = tvb_get_guint8(tvb, offset+in_off);
in_off++;
wmem_array_append_one(obuf, val);
out_off++;
} else {
guint f, l_mask = 0x0FFF, o_shift = 12;
guint match_len, match_off;
f = tvb_get_letohs(tvb, offset+in_off);
in_off += 2;
pos = out_off-1;
while (pos >= 0x10) {
l_mask >>= 1;
o_shift -= 1;
pos >>= 1;
}
match_len = (f & l_mask) + 3;
match_off = (f >> o_shift) + 1;
for (j = 0; j < match_len; j++) {
guint8 byte;
if (match_off > (guint)out_off)
return FALSE;
if (wmem_array_try_index(obuf, out_start+out_off-match_off, &byte))
return FALSE;
wmem_array_append_one(obuf, byte);
out_off++;
}
}
if (in_off == in_size) {
goto out;
}
}
}
out:
return TRUE;
}
static gboolean
do_uncompress(tvbuff_t *tvb, int offset, int in_size, wmem_array_t *obuf)
{
int in_off = 0;
guint32 header, length, i;
gboolean ok;
if (!tvb)
return FALSE;
if (in_size > MAX_INPUT_SIZE)
return FALSE;
while (in_off < in_size) {
header = tvb_get_letohs(tvb, offset+in_off);
in_off += 2;
length = (header & 0x0FFF) + 1;
if (!(header & 0x8000)) {
for (i = 0; i < length; i++) {
guint8 v = tvb_get_guint8(tvb, offset+in_off);
wmem_array_append_one(obuf, v);
in_off++;
}
} else {
ok = uncompress_chunk(tvb, offset + in_off, length, obuf);
if (!ok)
return FALSE;
in_off += length;
}
}
return TRUE;
}
tvbuff_t *
tvb_uncompress_lznt1(tvbuff_t *tvb, const int offset, int in_size)
{
volatile gboolean ok = FALSE;
wmem_allocator_t *pool;
wmem_array_t *obuf;
tvbuff_t *out;
pool = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
obuf = wmem_array_sized_new(pool, 1, in_size*2);
TRY {
ok = do_uncompress(tvb, offset, in_size, obuf);
} CATCH_ALL {
ok = FALSE;
}
ENDTRY;
if (ok) {
/*
* Cannot pass a tvb free callback that frees the wmem
* pool, so we make an make an extra copy that uses
* bare pointers. This could be optimized if tvb API
* had a free pool callback of some sort.
*/
guint size = wmem_array_get_count(obuf);
guint8 *p = (guint8 *)g_malloc(size);
memcpy(p, wmem_array_get_raw(obuf), size);
out = tvb_new_real_data(p, size, size);
tvb_set_free_cb(out, g_free);
} else {
out = NULL;
}
wmem_destroy_allocator(pool);
return out;
}
tvbuff_t *
tvb_child_uncompress_lznt1(tvbuff_t *parent, tvbuff_t *tvb, const int offset, int in_size)
{
tvbuff_t *new_tvb = tvb_uncompress_lznt1(tvb, offset, in_size);
if (new_tvb)
tvb_set_child_real_data_tvbuff(parent, new_tvb);
return new_tvb;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: t
* End:
*
* vi: set shiftwidth=8 tabstop=8 noexpandtab:
* :indentSize=8:tabSize=8:noTabs=false:
*/

View File

@ -131,6 +131,15 @@ wmem_array_index(wmem_array_t *array, guint array_index)
return &array->buf[array_index * array->elem_size];
}
int
wmem_array_try_index(wmem_array_t *array, guint array_index, void *val)
{
if (array_index >= array->elem_count)
return -1;
memcpy(val, &array->buf[array_index * array->elem_size], array->elem_size);
return 0;
}
void
wmem_array_sort(wmem_array_t *array, int (*compar)(const void*,const void*))
{

View File

@ -64,6 +64,10 @@ WS_DLL_PUBLIC
void *
wmem_array_index(wmem_array_t *array, guint array_index);
WS_DLL_PUBLIC
int
wmem_array_try_index(wmem_array_t *array, guint array_index, void *val);
WS_DLL_PUBLIC
void
wmem_array_sort(wmem_array_t *array, int (*compar)(const void*,const void*));

View File

@ -657,16 +657,24 @@ wmem_test_array(void)
val = *(guint32*)wmem_array_index(array, i);
g_assert(val == i);
g_assert(wmem_array_try_index(array, i, &val) == 0);
g_assert(val == i);
g_assert(wmem_array_try_index(array, i+1, &val) < 0);
}
wmem_strict_check_canaries(allocator);
for (i=0; i<CONTAINER_ITERS; i++) {
val = *(guint32*)wmem_array_index(array, i);
g_assert(val == i);
g_assert(wmem_array_try_index(array, i, &val) == 0);
g_assert(val == i);
}
array = wmem_array_sized_new(allocator, sizeof(guint32), 73);
wmem_array_set_null_terminator(array);
for (i=0; i<75; i++)
g_assert(wmem_array_try_index(array, i, &val) < 0);
for (i=0; i<CONTAINER_ITERS; i++) {
for (j=0; j<8; j++) {
@ -690,16 +698,22 @@ wmem_test_array(void)
for (j=0; j<=i; j++, k++) {
val = *(guint32*)wmem_array_index(array, k);
g_assert(val == i);
g_assert(wmem_array_try_index(array, k, &val) == 0);
g_assert(val == i);
}
}
for (j=k; k<8*(CONTAINER_ITERS+1)-j; k++) {
val = *(guint32*)wmem_array_index(array, k);
g_assert(val == ((k-j)/8)+8);
g_assert(wmem_array_try_index(array, k, &val) == 0);
g_assert(val == ((k-j)/8)+8);
}
for (i=0; i<7; i++) {
for (j=0; j<7-i; j++, k++) {
val = *(guint32*)wmem_array_index(array, k);
g_assert(val == CONTAINER_ITERS+i);
g_assert(wmem_array_try_index(array, k, &val) == 0);
g_assert(val == CONTAINER_ITERS+i);
}
}
g_assert(k == wmem_array_get_count(array));

Binary file not shown.

View File

@ -1068,7 +1068,7 @@ class case_decrypt_smb2(subprocesstest.SubprocessTestCase):
'-o', 'uat:smb2_seskey_list:{},{}'.format(sesid, seskey),
'-Y', 'frame.number == 7',
))
self.assertIn('unknown', proc.stdout_str)
self.assertIn('Invalid header', proc.stdout_str)
def test_smb311_bad_key(self, cmd_tshark, capture_file):
seskey = 'ffffffffffffffffffffffffffffffff'
@ -1078,7 +1078,7 @@ class case_decrypt_smb2(subprocesstest.SubprocessTestCase):
'-o', 'uat:smb2_seskey_list:{},{}'.format(sesid, seskey),
'-Y', 'frame.number == 7'
))
self.assertIn('unknown', proc.stdout_str)
self.assertIn('Invalid header', proc.stdout_str)
def test_smb300_aes128ccm(self, cmd_tshark, capture_file):
'''Check SMB 3.0 AES128CCM decryption.'''

View File

@ -199,3 +199,23 @@ class case_dissect_tls(subprocesstest.SubprocessTestCase):
'''Verify that TCP and TLS handshake reassembly works (second pass).'''
self.check_tls_handshake_reassembly(
cmd_tshark, capture_file, extraArgs=['-2'])
@fixtures.mark_usefixtures('test_env')
@fixtures.uses_fixtures
class case_decompress_smb2(subprocesstest.SubprocessTestCase):
def extract_compressed_payload(self, cmd_tshark, capture_file, frame_num):
proc = self.assertRun((cmd_tshark,
'-r', capture_file('smb311-lz77-lz77huff-lznt1.pcap.gz'),
'-Tfields', '-edata.data',
'-Y', 'frame.number == %d'%frame_num,
))
self.assertEqual(b'a'*4096, bytes.fromhex(proc.stdout_str.strip()))
def test_smb311_read_lz77(self, cmd_tshark, capture_file):
self.extract_compressed_payload(cmd_tshark, capture_file, 1)
def test_smb311_read_lz77huff(self, cmd_tshark, capture_file):
self.extract_compressed_payload(cmd_tshark, capture_file, 2)
def test_smb311_read_lznt1(self, cmd_tshark, capture_file):
self.extract_compressed_payload(cmd_tshark, capture_file, 3)