summaryrefslogtreecommitdiffstats
path: root/epan/tvbuff_rdp.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/tvbuff_rdp.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/tvbuff_rdp.c')
-rw-r--r--epan/tvbuff_rdp.c529
1 files changed, 529 insertions, 0 deletions
diff --git a/epan/tvbuff_rdp.c b/epan/tvbuff_rdp.c
new file mode 100644
index 00000000..4beb24c5
--- /dev/null
+++ b/epan/tvbuff_rdp.c
@@ -0,0 +1,529 @@
+/* tvbuff_rdp.c
+ * Decompression routines used in RDP
+ * Copyright 2021, David Fort
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "config.h"
+
+#include <glib.h>
+#include <stdbool.h>
+
+#include <epan/tvbuff.h>
+#include <epan/proto.h>
+#include <epan/tvbuff_rdp.h>
+
+enum {
+ ZGFX_SEGMENTED_SINGLE = 0xe0,
+ ZGFX_SEGMENTED_MULTIPART = 0xe1,
+
+ ZGX_PACKET_COMPRESSED = 0x20,
+};
+
+
+typedef struct {
+ tvbuff_t *input;
+ guint offset;
+ guint remainingBits;
+ guint32 currentValue;
+ guint currentBits;
+} bitstream_t;
+
+static void
+bitstream_init(bitstream_t *b, tvbuff_t *input, guint blen) {
+ b->input = input;
+ b->offset = 0;
+ b->remainingBits = blen;
+ b->currentValue = 0;
+ b->currentBits = 0;
+}
+
+static guint32
+bitstream_getbits(bitstream_t *b, guint8 nbits, gboolean *ok) {
+ guint32 ret = 0;
+
+ if (nbits > b->remainingBits) {
+ *ok = FALSE;
+ return 0;
+ }
+
+ while (b->currentBits < nbits) {
+ if (!tvb_reported_length_remaining(b->input, b->offset)) {
+ *ok = FALSE;
+ return 0;
+ }
+
+ b->currentValue <<= 8;
+ b->currentValue += tvb_get_guint8(b->input, b->offset++);
+
+ b->currentBits += 8;
+ }
+
+ *ok = TRUE;
+ ret = b->currentValue >> (b->currentBits-nbits);
+ b->currentBits -= nbits;
+ b->remainingBits -= nbits;
+ b->currentValue &= (1 << b->currentBits) - 1;
+
+ return ret;
+}
+
+static gboolean
+bitstream_copyraw(bitstream_t *b, guint8 *dest, gint nbytes)
+{
+ if (tvb_captured_length_remaining(b->input, b->offset) < nbytes)
+ return FALSE;
+
+ tvb_memcpy(b->input, dest, b->offset, nbytes);
+
+ return TRUE;
+}
+
+static gboolean
+bitstream_copyraw_advance(bitstream_t *b, guint8 *dest, guint nbytes)
+{
+ if (!bitstream_copyraw(b, dest, nbytes))
+ return FALSE;
+
+ b->offset += nbytes;
+ b->remainingBits -= (nbytes * 8);
+ return TRUE;
+}
+
+
+static void
+bitstream_realign(bitstream_t *b) {
+ b->remainingBits -= b->currentBits;
+ b->currentBits = 0;
+ b->currentValue = 0;
+}
+
+typedef struct {
+ guint32 prefixLength;
+ guint32 prefixCode;
+ guint32 valueBits;
+ guint32 valueBase;
+} zgfx_token_t;
+
+static const zgfx_token_t ZGFX_LITTERAL_TABLE[] = {
+ // prefixLength prefixCode valueBits valueBase
+ { 5, 24, 0, 0x00 }, // 11000
+ { 5, 25, 0, 0x01 }, // 11001
+ { 6, 52, 0, 0x02 }, // 110100
+ { 6, 53, 0, 0x03 }, // 110101
+ { 6, 54, 0, 0xFF }, // 110110
+ { 7, 110, 0, 0x04 }, // 1101110
+ { 7, 111, 0, 0x05 }, // 1101111
+ { 7, 112, 0, 0x06 }, // 1110000
+ { 7, 113, 0, 0x07 }, // 1110001
+ { 7, 114, 0, 0x08 }, // 1110010
+ { 7, 115, 0, 0x09 }, // 1110011
+ { 7, 116, 0, 0x0A }, // 1110100
+ { 7, 117, 0, 0x0B }, // 1110101
+ { 7, 118, 0, 0x3A }, // 1110110
+ { 7, 119, 0, 0x3B }, // 1110111
+ { 7, 120, 0, 0x3C }, // 1111000
+ { 7, 121, 0, 0x3D }, // 1111001
+ { 7, 122, 0, 0x3E }, // 1111010
+ { 7, 123, 0, 0x3F }, // 1111011
+ { 7, 124, 0, 0x40 }, // 1111100
+ { 7, 125, 0, 0x80 }, // 1111101
+ { 8, 252, 0, 0x0C }, // 11111100
+ { 8, 253, 0, 0x38 }, // 11111101
+ { 8, 254, 0, 0x39 }, // 11111110
+ { 8, 255, 0, 0x66 }, // 11111111
+};
+
+static const zgfx_token_t ZGFX_MATCH_TABLE[] = {
+ // prefixLength prefixCode valueBits tokenType valueBase
+ { 5, 17, 5, 0 }, // 10001
+ { 5, 18, 7, 32 }, // 10010
+ { 5, 19, 9, 160 }, // 10011
+ { 5, 20, 10, 672 }, // 10100
+ { 5, 21, 12, 1696 }, // 10101
+ { 6, 44, 14, 5792 }, // 101100
+ { 6, 45, 15, 22176 }, // 101101
+ { 7, 92, 18, 54944 }, // 1011100
+ { 7, 93, 20, 317088 }, // 1011101
+ { 8, 188, 20, 1365664 }, // 10111100
+ { 8, 189, 21, 2414240 }, // 10111101
+ { 9, 380, 22, 4511392 }, // 101111100
+ { 9, 381, 23, 8705696 }, // 101111101
+ { 9, 382, 24, 17094304 }, // 101111110
+};
+
+
+struct _zgfx_context_t{
+ guint8 historyBuffer[2500000];
+ guint32 historyIndex;
+ guint32 historyBufferSize;
+
+ guint32 outputCount;
+ guint8 outputSegment[65536];
+};
+
+zgfx_context_t *zgfx_context_new(wmem_allocator_t *allocator) {
+ zgfx_context_t *ret = wmem_alloc0(allocator, sizeof(*ret));
+ ret->historyBufferSize = sizeof(ret->historyBuffer);
+ return ret;
+}
+
+static void
+zgfx_write_history_litteral(zgfx_context_t *zgfx, guint8 c)
+{
+ zgfx->historyBuffer[zgfx->historyIndex] = c;
+ zgfx->historyIndex = (zgfx->historyIndex + 1) % zgfx->historyBufferSize;
+}
+
+static void
+zgfx_write_history_buffer_tvb(zgfx_context_t *zgfx, tvbuff_t *src, guint32 count)
+{
+ gint src_offset = 0;
+ guint32 front;
+
+ if (count > zgfx->historyBufferSize) {
+ const guint32 residue = count - zgfx->historyBufferSize;
+ count = zgfx->historyBufferSize;
+ src_offset += residue;
+ zgfx->historyIndex = (zgfx->historyIndex + residue) % zgfx->historyBufferSize;
+ }
+
+ if (zgfx->historyIndex + count <= zgfx->historyBufferSize)
+ {
+ tvb_memcpy(src, &(zgfx->historyBuffer[zgfx->historyIndex]), src_offset, count);
+ }
+ else
+ {
+ front = zgfx->historyBufferSize - zgfx->historyIndex;
+ tvb_memcpy(src, &(zgfx->historyBuffer[zgfx->historyIndex]), src_offset, front);
+ tvb_memcpy(src, &(zgfx->historyBuffer), src_offset + count, count - front);
+ }
+
+ zgfx->historyIndex = (zgfx->historyIndex + count) % zgfx->historyBufferSize;
+}
+
+
+static void
+zgfx_write_history_buffer(zgfx_context_t *zgfx, const guint8 *src, guint32 count)
+{
+ guint32 front;
+
+ if (count > zgfx->historyBufferSize) {
+ const guint32 residue = count - zgfx->historyBufferSize;
+ count = zgfx->historyBufferSize;
+ zgfx->historyIndex = (zgfx->historyIndex + residue) % zgfx->historyBufferSize;
+ }
+
+ if (zgfx->historyIndex + count <= zgfx->historyBufferSize)
+ {
+ memcpy(&(zgfx->historyBuffer[zgfx->historyIndex]), src, count);
+ }
+ else
+ {
+ front = zgfx->historyBufferSize - zgfx->historyIndex;
+ memcpy(&(zgfx->historyBuffer[zgfx->historyIndex]), src, front);
+ memcpy(&(zgfx->historyBuffer), src + front, count - front);
+ }
+
+ zgfx->historyIndex = (zgfx->historyIndex + count) % zgfx->historyBufferSize;
+}
+
+
+static gboolean
+zgfx_write_litteral(zgfx_context_t *zgfx, guint8 c)
+{
+ if (zgfx->outputCount == 65535)
+ return FALSE;
+
+ zgfx->outputSegment[zgfx->outputCount++] = c;
+
+ zgfx_write_history_litteral(zgfx, c);
+ return TRUE;
+}
+
+static gboolean
+zgfx_write_raw(zgfx_context_t *zgfx, bitstream_t *b, guint32 count)
+{
+ guint32 rest, tocopy;
+
+ /* first copy in the output buffer */
+ if (zgfx->outputCount > 65535 - count)
+ return FALSE;
+
+ if (!bitstream_copyraw(b, &(zgfx->outputSegment[zgfx->outputCount]), count))
+ return FALSE;
+
+ zgfx->outputCount += count;
+
+ /* then update the history buffer */
+ rest = (zgfx->historyBufferSize - zgfx->historyIndex);
+ tocopy = count;
+ if (rest < count)
+ tocopy = rest;
+
+ if (!bitstream_copyraw_advance(b, &(zgfx->historyBuffer[zgfx->historyIndex]), tocopy))
+ return FALSE;
+
+ zgfx->historyIndex = (zgfx->historyIndex + tocopy) % zgfx->historyBufferSize;
+ count -= tocopy;
+ if (count) {
+ if (!bitstream_copyraw_advance(b, &(zgfx->historyBuffer[zgfx->historyIndex]), tocopy))
+ return FALSE;
+
+ zgfx->historyIndex = (zgfx->historyIndex + tocopy) % zgfx->historyBufferSize;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+zgfx_write_from_history(zgfx_context_t *zgfx, guint32 distance, guint32 count)
+{
+ guint idx;
+ guint32 remainingCount, copyTemplateSize, toCopy;
+ guint8 *outputPtr;
+
+ if (zgfx->outputCount > 65535 - count)
+ return FALSE;
+
+ remainingCount = count;
+ idx = (zgfx->historyIndex + zgfx->historyBufferSize - distance) % zgfx->historyBufferSize;
+ copyTemplateSize = (distance > count) ? count : distance;
+
+ /* first do copy a single copy in output */
+ outputPtr = &(zgfx->outputSegment[zgfx->outputCount]);
+ toCopy = copyTemplateSize;
+ if (idx + toCopy < zgfx->historyBufferSize) {
+ memcpy(outputPtr, &(zgfx->historyBuffer[idx]), toCopy);
+ } else {
+ guint32 partial = zgfx->historyBufferSize - idx;
+ memcpy(outputPtr, &(zgfx->historyBuffer[idx]), partial);
+ memcpy(outputPtr + partial, zgfx->historyBuffer, toCopy - partial);
+ }
+ outputPtr += toCopy;
+ remainingCount -= toCopy;
+
+ /* then duplicate output as much as needed by count, at each loop turn we double
+ * the size of the template we can copy */
+ while (remainingCount) {
+ toCopy = (remainingCount < copyTemplateSize) ? remainingCount : copyTemplateSize;
+ memcpy(outputPtr, &(zgfx->outputSegment[zgfx->outputCount]), toCopy);
+
+ outputPtr += toCopy;
+ remainingCount -= toCopy;
+ copyTemplateSize *= 2;
+ }
+
+ /* let's update the history from output and update counters */
+ zgfx_write_history_buffer(zgfx, &(zgfx->outputSegment[zgfx->outputCount]), count);
+ zgfx->outputCount += count;
+ return TRUE;
+}
+
+
+static gboolean
+rdp8_decompress_segment(zgfx_context_t *zgfx, tvbuff_t *tvb)
+{
+ bitstream_t bitstream;
+ gint offset = 0;
+ gint len = tvb_reported_length(tvb);
+ guint8 flags = tvb_get_guint8(tvb, offset);
+ guint8 v;
+ offset++;
+ len--;
+
+ if (!(flags & ZGX_PACKET_COMPRESSED)) {
+ tvbuff_t *raw = tvb_new_subset_remaining(tvb, 1);
+ zgfx_write_history_buffer_tvb(zgfx, raw, len);
+
+ tvb_memcpy(tvb, zgfx->outputSegment, 1, len);
+ zgfx->outputCount += len;
+ return TRUE;
+ }
+
+ v = tvb_get_guint8(tvb, offset + len - 1);
+ if (v > 7)
+ return FALSE;
+ len--;
+
+ bitstream_init(&bitstream, tvb_new_subset_length(tvb, offset, len), (len * 8) - v);
+ while (bitstream.remainingBits) {
+ gboolean ok, ismatch, found;
+ guint32 bits_val = bitstream_getbits(&bitstream, 1, &ok);
+ guint32 inPrefix;
+ const zgfx_token_t *tokens;
+ gint ntokens, i;
+ guint32 prefixBits;
+
+ if (!ok)
+ return FALSE;
+
+ // 0 - litteral
+ if (bits_val == 0) {
+
+ bits_val = bitstream_getbits(&bitstream, 8, &ok);
+ if (!zgfx_write_litteral(zgfx, bits_val))
+ return FALSE;
+ continue;
+ }
+
+ // 1x - match or litteral branch
+ bits_val = bitstream_getbits(&bitstream, 1, &ok);
+ if (bits_val == 0) {
+ // 10 - match
+ ismatch = true;
+ tokens = ZGFX_MATCH_TABLE;
+ ntokens = sizeof(ZGFX_MATCH_TABLE) / sizeof(ZGFX_MATCH_TABLE[0]);
+ inPrefix = 2;
+ } else {
+ // 11 - litteral
+ ismatch = false;
+ tokens = ZGFX_LITTERAL_TABLE;
+ ntokens = sizeof(ZGFX_LITTERAL_TABLE) / sizeof(ZGFX_LITTERAL_TABLE[0]);
+ inPrefix = 3;
+ }
+
+ prefixBits = 2;
+ found = FALSE;
+ for (i = 0; i < ntokens; i++) {
+ if (prefixBits != tokens[i].prefixLength) {
+ guint32 missingBits = (tokens[i].prefixLength - prefixBits);
+ inPrefix <<= missingBits;
+ inPrefix |= bitstream_getbits(&bitstream, missingBits, &ok);
+ if (!ok)
+ return FALSE;
+ prefixBits = tokens[i].prefixLength;
+ }
+
+ if (inPrefix == tokens[i].prefixCode) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) // TODO: is it an error ?
+ continue;
+
+ if (ismatch) {
+ /* It's a match */
+ guint32 count, distance, extra = 0;
+
+ distance = tokens[i].valueBase + bitstream_getbits(&bitstream, tokens[i].valueBits, &ok);
+ if (!ok)
+ return FALSE;
+
+ if (distance != 0) {
+ bits_val = bitstream_getbits(&bitstream, 1, &ok);
+ if (!ok)
+ return FALSE;
+
+ if (bits_val == 0) {
+ count = 3;
+ } else {
+ count = 4;
+ extra = 2;
+
+ bits_val = bitstream_getbits(&bitstream, 1, &ok);
+ if (!ok)
+ return FALSE;
+
+ while (bits_val == 1) {
+ count *= 2;
+ extra ++;
+ bits_val = bitstream_getbits(&bitstream, 1, &ok);
+ if (!ok)
+ return FALSE;
+ }
+
+ count += bitstream_getbits(&bitstream, extra, &ok);
+ if (!ok)
+ return FALSE;
+ }
+
+ if (count > sizeof(zgfx->outputSegment) - zgfx->outputCount)
+ return FALSE;
+
+ if (!zgfx_write_from_history(zgfx, distance, count))
+ return FALSE;
+ } else {
+ /* Unencoded */
+ count = bitstream_getbits(&bitstream, 15, &ok);
+ if (!ok)
+ return FALSE;
+
+ bitstream_realign(&bitstream);
+ if (!zgfx_write_raw(zgfx, &bitstream, count))
+ return FALSE;
+ }
+ } else {
+ /* Litteral */
+ bits_val = tokens[i].valueBase;
+ if (!zgfx_write_litteral(zgfx, bits_val))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+tvbuff_t *
+rdp8_decompress(zgfx_context_t *zgfx, wmem_allocator_t *allocator, tvbuff_t *tvb, guint offset)
+{
+ void *output;
+ guint8 descriptor;
+
+ descriptor = tvb_get_guint8(tvb, offset);
+ offset++;
+
+ switch (descriptor) {
+ case ZGFX_SEGMENTED_SINGLE:
+ zgfx->outputCount = 0;
+ if (!rdp8_decompress_segment(zgfx, tvb_new_subset_remaining(tvb, offset)))
+ return NULL;
+
+ output = wmem_alloc(allocator, zgfx->outputCount);
+ memcpy(output, zgfx->outputSegment, zgfx->outputCount);
+ return tvb_new_real_data(output, zgfx->outputCount, zgfx->outputCount);
+
+ case ZGFX_SEGMENTED_MULTIPART: {
+ guint16 segment_count, i;
+ guint32 output_consumed, uncompressed_size;
+ guint8 *output_ptr;
+
+ segment_count = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
+ offset += 2;
+ uncompressed_size = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ output = output_ptr = wmem_alloc(allocator, uncompressed_size);
+ output_consumed = 0;
+ for (i = 0; i < segment_count; i++) {
+ guint32 segment_size = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN);
+ offset += 4;
+
+ zgfx->outputCount = 0;
+ if (!rdp8_decompress_segment(zgfx, tvb_new_subset_length(tvb, offset, segment_size)))
+ return NULL;
+
+ output_consumed += zgfx->outputCount;
+ if (output_consumed > uncompressed_size) {
+ // TODO: error message ?
+ return NULL;
+ }
+ memcpy(output_ptr, zgfx->outputSegment, zgfx->outputCount);
+
+ offset += segment_size;
+ output_ptr += zgfx->outputCount;
+ }
+ return tvb_new_real_data(output, uncompressed_size, uncompressed_size);
+ }
+ default:
+ return tvb;
+ }
+}