diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
commit | e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch) | |
tree | 68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/tvbuff_rdp.c | |
parent | Initial commit. (diff) | |
download | wireshark-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.c | 529 |
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; + } +} |