aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAurelien Aptel <aaptel@suse.com>2019-07-05 16:08:18 +0200
committerPeter Wu <peter@lekensteyn.nl>2019-07-15 21:00:14 +0000
commit0db39ae59aaefc13a38ec4e7728da44a647b1a10 (patch)
treee82f234b0928c832d23bd6fe128958611131c060
parent1a91aac9747ec3d937dd20ec962c86a149248876 (diff)
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>
-rw-r--r--debian/libwireshark0.symbols7
-rw-r--r--epan/CMakeLists.txt3
-rw-r--r--epan/dissectors/packet-smb2.c250
-rw-r--r--epan/dissectors/packet-smb2.h8
-rw-r--r--epan/tvbuff.h60
-rw-r--r--epan/tvbuff_lz77.c155
-rw-r--r--epan/tvbuff_lz77huff.c415
-rw-r--r--epan/tvbuff_lznt1.c165
-rw-r--r--epan/wmem/wmem_array.c9
-rw-r--r--epan/wmem/wmem_array.h4
-rw-r--r--epan/wmem/wmem_test.c14
-rw-r--r--test/captures/smb311-lz77-lz77huff-lznt1.pcap.gzbin0 -> 446 bytes
-rw-r--r--test/suite_decryption.py4
-rw-r--r--test/suite_dissection.py20
14 files changed, 1095 insertions, 19 deletions
diff --git a/debian/libwireshark0.symbols b/debian/libwireshark0.symbols
index 99a0d2f431..b4b9c5b2ca 100644
--- a/debian/libwireshark0.symbols
+++ b/debian/libwireshark0.symbols
@@ -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
diff --git a/epan/CMakeLists.txt b/epan/CMakeLists.txt
index 3724e55e82..a1c0215b0a 100644
--- a/epan/CMakeLists.txt
+++ b/epan/CMakeLists.txt
@@ -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
diff --git a/epan/dissectors/packet-smb2.c b/epan/dissectors/packet-smb2.c
index e3f88d8cd4..03a42912d8 100644
--- a/epan/dissectors/packet-smb2.c
+++ b/epan/dissectors/packet-smb2.c
@@ -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);
@@ -9398,6 +9438,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,
smb2_transform_info_t *sti,
@@ -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[] = {
diff --git a/epan/dissectors/packet-smb2.h b/epan/dissectors/packet-smb2.h
index 46b61408d5..2c63dcb9d6 100644
--- a/epan/dissectors/packet-smb2.h
+++ b/epan/dissectors/packet-smb2.h
@@ -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);
diff --git a/epan/tvbuff.h b/epan/tvbuff.h
index 7b93f98772..6431436499 100644
--- a/epan/tvbuff.h
+++ b/epan/tvbuff.h
@@ -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
diff --git a/epan/tvbuff_lz77.c b/epan/tvbuff_lz77.c
new file mode 100644
index 0000000000..082e58a133
--- /dev/null
+++ b/epan/tvbuff_lz77.c
@@ -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:
+ */
diff --git a/epan/tvbuff_lz77huff.c b/epan/tvbuff_lz77huff.c
new file mode 100644
index 0000000000..4e22194546
--- /dev/null
+++ b/epan/tvbuff_lz77huff.c
@@ -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 node’s 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 symbol’s 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:
+ */
diff --git a/epan/tvbuff_lznt1.c b/epan/tvbuff_lznt1.c
new file mode 100644
index 0000000000..a909c06af7
--- /dev/null
+++ b/epan/tvbuff_lznt1.c
@@ -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:
+ */
diff --git a/epan/wmem/wmem_array.c b/epan/wmem/wmem_array.c
index 2445afb552..974384cab9 100644
--- a/epan/wmem/wmem_array.c
+++ b/epan/wmem/wmem_array.c
@@ -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*))
{
diff --git a/epan/wmem/wmem_array.h b/epan/wmem/wmem_array.h
index f0a6cf0184..e8d8dc76e1 100644
--- a/epan/wmem/wmem_array.h
+++ b/epan/wmem/wmem_array.h
@@ -65,6 +65,10 @@ 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*));
diff --git a/epan/wmem/wmem_test.c b/epan/wmem/wmem_test.c
index a3a4e240f3..7df9a1feda 100644
--- a/epan/wmem/wmem_test.c
+++ b/epan/wmem/wmem_test.c
@@ -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));
diff --git a/test/captures/smb311-lz77-lz77huff-lznt1.pcap.gz b/test/captures/smb311-lz77-lz77huff-lznt1.pcap.gz
new file mode 100644
index 0000000000..f14bfbaf42
--- /dev/null
+++ b/test/captures/smb311-lz77-lz77huff-lznt1.pcap.gz
Binary files differ
diff --git a/test/suite_decryption.py b/test/suite_decryption.py
index 568e095a43..e87358ee86 100644
--- a/test/suite_decryption.py
+++ b/test/suite_decryption.py
@@ -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.'''
diff --git a/test/suite_dissection.py b/test/suite_dissection.py
index 06725e1181..772b40f5ca 100644
--- a/test/suite_dissection.py
+++ b/test/suite_dissection.py
@@ -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)