Skip to content

Instantly share code, notes, and snippets.

@odzhan
Last active December 4, 2024 03:15
Show Gist options
  • Select an option

  • Save odzhan/56eb105a611dcdebd1d3a084c7312190 to your computer and use it in GitHub Desktop.

Select an option

Save odzhan/56eb105a611dcdebd1d3a084c7312190 to your computer and use it in GitHub Desktop.

Revisions

  1. odzhan revised this gist Jan 1, 2024. 1 changed file with 48 additions and 29 deletions.
    77 changes: 48 additions & 29 deletions rdp_pack.cpp
    Original file line number Diff line number Diff line change
    @@ -36,9 +36,33 @@
    #define PACKET_COMPR_TYPE_RDP8 4 // MSDN references this, but I doubt it's actually supported by OS

    #define PACKET_ENCRYPTED 0x10
    #define PACKET_COMPRESSED 0x20
    #define PACKET_AT_FRONT 0x40
    #define PACKET_FLUSHED 0x80

    // Indicates that RDP 5.0 bulk compression (see [MS-RDPBCGR] section 3.1.8.4.2) was used.
    //#define PACKET_COMPR_TYPE_64K 0x01

    // The data in the MatchCount, MatchDetails, and Literals fields has been compressed with the level-2 compressor.
    #define PACKET_COMPRESSED 0x20

    // The decompressed data MUST be placed at the beginning of the level-2 history buffer.
    #define PACKET_AT_FRONT 0x40

    // The level-2 history buffer MUST be reinitialized (by filling it with zeros).
    #define PACKET_FLUSHED 0x80

    // The level-1 history buffer MUST be reinitialized (by filling it with zeros).
    #define L1_PACKET_AT_FRONT 0x04

    // No compression was performed. In this case, the MatchCount and MatchDetails fields MUST NOT be present.
    // The Literals field MUST be present.
    #define L1_NO_COMPRESSION 0x02

    // Compression with the level-1 compressor was performed and the MatchCount
    // and MatchDetails fields MUST be present and contain at least one match. The Literals field MUST also be present.
    #define L1_COMPRESSED 0x01

    // Indicates that additional level-2 compression has been performed on the level-1 compressor output
    // and that the Level2ComprFlags /field contains valid data and MUST be processed.
    #define L1_INNER_COMPRESSION 0x10

    // ******************************************************
    // Needed to allocate memory for compression context.
    @@ -51,10 +75,10 @@ void (WINAPI *_RDPCompress_InitSendContext)(void *ctx, SIZE_T ctx_len, DWORD Com

    // Compress inbuf and store in outbuf
    typedef
    DWORD (WINAPI *_RDPCompress)(DWORD ComprType, void *inbuf, void *outbuf, PDWORD outlen, void *ctx);
    BYTE (WINAPI *_RDPCompress)(DWORD ComprType, void *inbuf, void *outbuf, PDWORD outlen, void *ctx);

    typedef
    DWORD (WINAPI *_RDPCompressEx)(DWORD opt, void *inbuf, DWORD inlen, void *outbuf, PDWORD outlen, void *ctx);
    BYTE (WINAPI *_RDPCompressEx)(DWORD opt, void *inbuf, DWORD inlen, void *outbuf, PDWORD outlen, void *ctx);

    // ******************************************************
    typedef
    @@ -91,17 +115,20 @@ typedef struct _RDP_pack_ctx {
    std::vector<BYTE> inbuf, outbuf;
    } RDP_pack_ctx;

    #define RDP_COMPR_BLK_LEN 8192 * 4
    #define RDP_COMPR_BLK_LEN (8192 * 4)

    #pragma pack(push,1)
    #pragma pack(push, 1)
    typedef struct _RDP_pack_blk {
    DWORD len;
    WORD flags;
    WORD len;
    BYTE data[RDP_COMPR_BLK_LEN + 12];
    } RDP_pack_blk;
    #pragma pack(pop)

    bool
    init_pack_ctx(RDP_pack_ctx *c) {
    printf("%zd\n", sizeof(RDP_pack_blk) - (RDP_COMPR_BLK_LEN + 12));

    HMODULE rdpbase = LoadLibrary("rdpbase.dll");

    if (!rdpbase) return false;
    @@ -145,14 +172,14 @@ WriteFileData(std::string path, std::vector<BYTE> data) {
    BOOL
    rdp_encode(RDP_pack_ctx *c) {
    void *SendCtx = NULL;
    BOOL result = FALSE;
    BOOL result = TRUE;

    do {
    DWORD ctx_len = c->RDPCompress_GetContextSize(c->type);
    if (!ctx_len) break;
    if (!ctx_len) { result = FALSE; break; }

    SendCtx = malloc(ctx_len);
    if (!SendCtx) break;
    if (!SendCtx) { result = FALSE; break; }

    c->RDPCompress_InitSendContext(SendCtx, ctx_len, c->type);

    @@ -166,17 +193,20 @@ rdp_encode(RDP_pack_ctx *c) {
    // 12 bytes are needed for header/flags
    in.len = inlen > c->blk - 12 ? c->blk - 12: inlen;

    out.len = c->blk;
    DWORD outlen = c->blk;
    memcpy(in.data, inbuf, in.len);

    result = c->RDPCompressEx(c->type, in.data, in.len, out.data, &out.len, SendCtx);
    out.flags = c->RDPCompressEx(c->type, in.data, in.len, out.data, &outlen, SendCtx);

    if (!result) {
    if (!out.flags) {
    printf("RDPCompressEx failed for block %ld, %ld\n", cnt, in.len);
    result = FALSE;
    break;
    }

    //printf("Block : %ld (%ld -> %ld) bytes...\n", cnt, in.len, out.len);
    out.len = outlen & 0xFFFF;

    //printf("flags=%02X Block : %ld (%ld -> %ld) bytes...\n", out.flags, cnt, in.len, out.len);
    c->outbuf.insert(c->outbuf.end(), (PBYTE)&out, (PBYTE)&out + out.len + sizeof(DWORD));

    inlen -= in.len;
    @@ -217,26 +247,15 @@ rdp_decode(RDP_pack_ctx *c) {
    result = c->RDPDecompress(
    in->data,
    in->len,
    0,
    in->flags & PACKET_AT_FRONT,
    (PVOID*)&outbuf,
    &outlen,
    RecvContext,
    c->type);

    if (!result) {
    result = c->RDPDecompress(
    in->data,
    in->len,
    1,
    (PVOID*)&outbuf,
    &outlen,
    RecvContext,
    c->type);

    if (!result) {
    printf("RDPDecompress() failed for block %ld, %ld -> %ld\n", cnt, in->len, outlen);
    break;
    }
    printf("RDPDecompress() failed for block %ld, %ld -> %ld\n", cnt, in->len, outlen);
    break;
    }

    //printf("Saving %ld bytes for block %ld...\n", outlen, cnt);
  2. odzhan revised this gist Jan 1, 2024. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion rdp_pack.cpp
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@
    /**
    Compression using undocumented API in rdpbase.dll
    RDPCompress supports four algorithms : MPPC-8K, MPPC-64K, NCRUSH and XCRUSH.
    RDPCompressEx supports four algorithms : MPPC-8K, MPPC-64K, NCRUSH and XCRUSH.
    This code supports all except NCRUSH.
    The MPPC compression ratio is very similar to LZSS, so this could be quite useful for shellcode trying to evade detection.
  3. odzhan revised this gist Jan 1, 2024. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions rdp_pack.cpp
    Original file line number Diff line number Diff line change
    @@ -145,7 +145,7 @@ WriteFileData(std::string path, std::vector<BYTE> data) {
    BOOL
    rdp_encode(RDP_pack_ctx *c) {
    void *SendCtx = NULL;
    BOOL result = false;
    BOOL result = FALSE;

    do {
    DWORD ctx_len = c->RDPCompress_GetContextSize(c->type);
    @@ -183,7 +183,7 @@ rdp_encode(RDP_pack_ctx *c) {
    inbuf += in.len;
    cnt++;
    }
    } while (false);
    } while (0);

    if (SendCtx) free(SendCtx);
    return result;
    @@ -195,7 +195,7 @@ rdp_encode(RDP_pack_ctx *c) {
    BOOL
    rdp_decode(RDP_pack_ctx *c) {
    void *RecvContext = NULL;
    BOOL result = TRUE;
    BOOL result = FALSE;

    do {
    DWORD ctx_len = c->RDPDeCompress_GetContextSize(c->type);
    @@ -246,7 +246,7 @@ rdp_decode(RDP_pack_ctx *c) {
    inbuf += in->len + sizeof(DWORD);
    cnt++;
    }
    } while (false);
    } while (0);

    if (RecvContext) free(RecvContext);
    return result;
  4. odzhan revised this gist Jan 1, 2024. 1 changed file with 108 additions and 77 deletions.
    185 changes: 108 additions & 77 deletions rdp_pack.cpp
    Original file line number Diff line number Diff line change
    @@ -4,21 +4,14 @@
    RDPCompress supports four algorithms : MPPC-8K, MPPC-64K, NCRUSH and XCRUSH.
    This code only supports compression and decompression using MPPC algorithms because these were the easiest to figure out.
    This code supports all except NCRUSH.
    The MPPC compression ratio is very similar to LZSS, so this could be quite useful for shellcode trying to evade detection.
    I was unable to get decompression working for NCRUSH and XCRUSH, which is why I'm publishing this.
    Maybe someone will figure out how to provide the correct input for these.
    Internally, NCRUSH expects the length of uncompressed or compressed data in the buffer.
    If you discover correct input for NCRUSH or XCRUSH, please let me know.
    The parameters to decompress data may be wrong. It appears to work fine for MPPC, but perhaps it won't work correctly
    for the others.
    NCRUSH compression appears to work but fails for decompression.
    cl /EHsc rdp_pack.cpp
    compress: rdp_pack e infile outfile.8k
    decompress: rdp_pack d infile.8k outfile
    compress: rdp_pack e0 infile outfile.8k
    decompress: rdp_pack d0 infile.8k outfile
    */

    #include <windows.h>
    @@ -35,30 +28,6 @@
    #include <sstream>
    #include <iomanip>

    #pragma pack(push, 1)
    typedef struct {
    uint8_t header; // An 8-bit, unsigned integer specifying the compression type and flags
    uint8_t data[1]; // A variable-length array of bytes containing data encoded using RDP 8.0 Bulk Compression techniques
    } RDP8_BULK_ENCODED_DATA;
    #pragma pack(pop)

    typedef struct {
    uint8_t descriptor; // SINGLE (0xE0) or MULTIPART (0xE1)

    // Optional fields
    uint16_t segmentCount; // Only present if descriptor is MULTIPART
    uint32_t uncompressedSize; // Only present if descriptor is MULTIPART
    RDP8_BULK_ENCODED_DATA bulkData; // Only present if descriptor is SINGLE
    //RDP_DATA_SEGMENT *segmentArray; // Only present if descriptor is MULTIPART
    } RDP_SEGMENTED_DATA_MP;

    #pragma pack(push, 1)
    typedef struct {
    uint32_t uncompressedSize; // Only present if descriptor is MULTIPART
    RDP8_BULK_ENCODED_DATA bulkData; // Only present if descriptor is SINGLE
    } RDP_SEGMENTED_DATA;
    #pragma pack(pop)

    // type of compression
    #define PACKET_COMPR_TYPE_8K 0 // MPPC
    #define PACKET_COMPR_TYPE_64K 1 // MPPC
    @@ -84,6 +53,9 @@ void (WINAPI *_RDPCompress_InitSendContext)(void *ctx, SIZE_T ctx_len, DWORD Com
    typedef
    DWORD (WINAPI *_RDPCompress)(DWORD ComprType, void *inbuf, void *outbuf, PDWORD outlen, void *ctx);

    typedef
    DWORD (WINAPI *_RDPCompressEx)(DWORD opt, void *inbuf, DWORD inlen, void *outbuf, PDWORD outlen, void *ctx);

    // ******************************************************
    typedef
    DWORD (WINAPI *_RDPDeCompress_GetContextSize)(DWORD ComprType);
    @@ -92,7 +64,7 @@ typedef
    void (WINAPI *_RDPCompress_InitRecvContext)(void *ctx, SIZE_T ctx_len, DWORD ComprType, void *workspace);

    typedef
    DWORD (WINAPI *_RDPDecompress)(
    BOOL (WINAPI *_RDPDecompress)(
    PVOID inbuf,
    DWORD inlen,
    DWORD start,
    @@ -104,10 +76,13 @@ DWORD (WINAPI *_RDPDecompress)(

    typedef struct _RDP_pack_ctx {
    DWORD type; // compression algorithm
    PCHAR name;
    DWORD blk;

    _RDPCompress_GetContextSize RDPCompress_GetContextSize;
    _RDPCompress_InitSendContext RDPCompress_InitSendContext;
    _RDPCompress RDPCompress;
    _RDPCompress RDPCompress; // doesn't work with XCRUSH
    _RDPCompressEx RDPCompressEx;

    _RDPDeCompress_GetContextSize RDPDeCompress_GetContextSize;
    _RDPCompress_InitRecvContext RDPCompress_InitRecvContext;
    @@ -116,12 +91,12 @@ typedef struct _RDP_pack_ctx {
    std::vector<BYTE> inbuf, outbuf;
    } RDP_pack_ctx;

    #define RDP_COMPR_BLK_LEN 4096
    #define RDP_COMPR_BLK_LEN 8192 * 4

    #pragma pack(push,1)
    typedef struct _RDP_pack_blk {
    DWORD len;
    BYTE data[RDP_COMPR_BLK_LEN];
    BYTE data[RDP_COMPR_BLK_LEN + 12];
    } RDP_pack_blk;
    #pragma pack(pop)

    @@ -134,6 +109,7 @@ init_pack_ctx(RDP_pack_ctx *c) {
    c->RDPCompress_GetContextSize = (_RDPCompress_GetContextSize)GetProcAddress(rdpbase, "RDPCompress_GetContextSize");
    c->RDPCompress_InitSendContext = (_RDPCompress_InitSendContext)GetProcAddress(rdpbase, "RDPCompress_InitSendContext");
    c->RDPCompress = (_RDPCompress)GetProcAddress(rdpbase, "RDPCompress");
    c->RDPCompressEx = (_RDPCompressEx)GetProcAddress(rdpbase, "RDPCompressEx");

    c->RDPDeCompress_GetContextSize = (_RDPDeCompress_GetContextSize)GetProcAddress(rdpbase, "RDPDeCompress_GetContextSize");
    c->RDPCompress_InitRecvContext = (_RDPCompress_InitRecvContext)GetProcAddress(rdpbase, "RDPCompress_InitRecvContext");
    @@ -142,6 +118,7 @@ init_pack_ctx(RDP_pack_ctx *c) {
    return c->RDPCompress_GetContextSize != NULL &&
    c->RDPCompress_InitSendContext != NULL &&
    c->RDPCompress != NULL &&
    c->RDPCompressEx != NULL &&
    c->RDPDeCompress_GetContextSize != NULL &&
    c->RDPCompress_InitRecvContext != NULL &&
    c->RDPDecompress != NULL;
    @@ -165,10 +142,10 @@ WriteFileData(std::string path, std::vector<BYTE> data) {
    //
    // Use RDP algorithms to compress file.
    //
    bool
    BOOL
    rdp_encode(RDP_pack_ctx *c) {
    void *SendCtx = NULL;
    bool result = false;
    BOOL result = false;

    do {
    DWORD ctx_len = c->RDPCompress_GetContextSize(c->type);
    @@ -181,35 +158,31 @@ rdp_encode(RDP_pack_ctx *c) {

    PBYTE inbuf = (PBYTE)c->inbuf.data();
    DWORD inlen = c->inbuf.size();
    DWORD cnt = 0;

    while (inlen) {
    RDP_pack_blk in={0}, out={0};

    in.len = inlen > RDP_COMPR_BLK_LEN ? RDP_COMPR_BLK_LEN : inlen;
    out.len = in.len;
    memcpy(in.data, inbuf, in.len);
    // 12 bytes are needed for header/flags
    in.len = inlen > c->blk - 12 ? c->blk - 12: inlen;

    out.len = c->blk;
    memcpy(in.data, inbuf, in.len);

    c->RDPCompress(c->type, in.data, out.data, &out.len, SendCtx);
    result = c->RDPCompressEx(c->type, in.data, in.len, out.data, &out.len, SendCtx);

    if (!out.len) {
    printf("RDPCompress failed.\n");
    if (!result) {
    printf("RDPCompressEx failed for block %ld, %ld\n", cnt, in.len);
    break;
    }

    // no compression?
    if (out.len == RDP_COMPR_BLK_LEN)
    {
    // copy uncompressed
    memcpy(&out, &in, sizeof(RDP_pack_blk));
    }

    //printf("Block : %ld (%ld -> %ld) bytes...\n", cnt, in.len, out.len);
    c->outbuf.insert(c->outbuf.end(), (PBYTE)&out, (PBYTE)&out + out.len + sizeof(DWORD));

    inlen -= in.len;
    inbuf += in.len;
    cnt++;
    }
    result = true;
    } while (false);

    if (SendCtx) free(SendCtx);
    @@ -219,10 +192,10 @@ rdp_encode(RDP_pack_ctx *c) {
    //
    //
    //
    bool
    BOOL
    rdp_decode(RDP_pack_ctx *c) {
    void *RecvContext = NULL;
    bool result = false;
    BOOL result = TRUE;

    do {
    DWORD ctx_len = c->RDPDeCompress_GetContextSize(c->type);
    @@ -233,31 +206,45 @@ rdp_decode(RDP_pack_ctx *c) {

    PBYTE inbuf = (PBYTE)c->inbuf.data();
    DWORD inlen = c->inbuf.size();
    DWORD start = 0;
    DWORD cnt = 0;

    while (inlen) {
    RDP_pack_blk *in = (RDP_pack_blk*)inbuf;

    PBYTE outbuf = NULL;
    DWORD outlen = 0;

    if (in->len == RDP_COMPR_BLK_LEN) {
    outbuf = in->data;
    outlen = RDP_COMPR_BLK_LEN;
    } else {
    c->RDPDecompress(in->data, in->len, start, (PVOID*)&outbuf, &outlen, RecvContext, c->type);
    result = c->RDPDecompress(
    in->data,
    in->len,
    0,
    (PVOID*)&outbuf,
    &outlen,
    RecvContext,
    c->type);

    if (!outbuf) {
    printf("RDPDecompress() failed.\n");
    if (!result) {
    result = c->RDPDecompress(
    in->data,
    in->len,
    1,
    (PVOID*)&outbuf,
    &outlen,
    RecvContext,
    c->type);

    if (!result) {
    printf("RDPDecompress() failed for block %ld, %ld -> %ld\n", cnt, in->len, outlen);
    break;
    }
    }

    //printf("Saving %ld bytes for block %ld...\n", outlen, cnt);
    c->outbuf.insert(c->outbuf.end(), outbuf, outbuf + outlen);
    start = 1;


    inlen -= in->len + sizeof(DWORD);
    inbuf += in->len + sizeof(DWORD);
    cnt++;
    }
    } while (false);

    @@ -268,11 +255,19 @@ rdp_decode(RDP_pack_ctx *c) {
    int
    main(int argc, char *argv[]) {
    if ((argc != 4) || ((argv[1][0] != 'e') && (argv[1][0] != 'd'))) {
    printf("usage: rdp_pack [e/d] <infile> <outfile>\n");
    printf("\nusage: rdp_pack [e/d]x <infile> <outfile>\n\n");
    printf(" x denotes algorithm:\n\n");
    printf(" 0 - MPPC-8K\n");
    printf(" 1 - MPPC-64K\n");
    printf(" 2 - NCRUSH\n");
    printf(" 3 - XCRUSH\n\n");
    printf("example compressing infile.txt with XCRUSH: rdp_pack e3 infile.txt outfile.bin\n");
    return 0;
    }

    bool encode = (argv[1][0] == 'e');
    bool result, encode = (argv[1][0] == 'e');
    int alg = argv[1][1] - '0';

    const char* infile = argv[2];
    const char* outfile = argv[3];

    @@ -283,18 +278,54 @@ main(int argc, char *argv[]) {
    return 0;
    }

    // set compression and input data
    c.type = PACKET_COMPR_TYPE_8K;
    switch(alg) {
    case 0:
    c.type = PACKET_COMPR_TYPE_8K;
    c.name = "MPPC-8K";
    c.blk = 4096;
    break;
    case 1:
    c.type = PACKET_COMPR_TYPE_64K;
    c.name = "MPPC-64K";
    c.blk = 8192;
    break;
    case 2:
    c.type = PACKET_COMPR_TYPE_RDP6;
    c.name = "NCRUSH";
    c.blk = 8192;
    break;
    case 3:
    c.type = PACKET_COMPR_TYPE_RDP61;
    c.name = "XCRUSH";
    c.blk = 8192;
    break;
    default:
    printf("Invalid compression algorithm specified.\n");
    return 0;
    }

    c.inbuf = ReadFileData(infile);

    if (encode) {
    rdp_encode(&c);
    printf("Compressing %zd bytes in %s with %s\n",
    c.inbuf.size(),
    infile,
    c.name
    );
    result = rdp_encode(&c);
    } else {
    rdp_decode(&c);
    printf("Decompressing %zd bytes in %s with %s\n",
    c.inbuf.size(),
    infile,
    c.name
    );
    result = rdp_decode(&c);
    }

    // save output to file
    WriteFileData(outfile, c.outbuf);
    printf("OK\n");
    if (result) {
    printf("Saving %zd bytes to %s.\n", c.outbuf.size(), outfile);
    WriteFileData(outfile, c.outbuf);
    }
    printf("Status : %s\n", result ? "OK" : "FAILED");
    return 0;
    }
    }
  5. odzhan revised this gist Dec 31, 2023. 1 changed file with 3 additions and 4 deletions.
    7 changes: 3 additions & 4 deletions rdp_pack.cpp
    Original file line number Diff line number Diff line change
    @@ -17,7 +17,8 @@
    for the others.
    cl /EHsc rdp_pack.cpp
    compress: rdp_pack e infile outfile.8k
    decompress: rdp_pack d infile.8k outfile
    */

    #include <windows.h>
    @@ -197,9 +198,7 @@ rdp_encode(RDP_pack_ctx *c) {
    }

    // no compression?
    if (c->type == PACKET_COMPR_TYPE_8K ||
    c->type == PACKET_COMPR_TYPE_64K ||
    out.len == RDP_COMPR_BLK_LEN)
    if (out.len == RDP_COMPR_BLK_LEN)
    {
    // copy uncompressed
    memcpy(&out, &in, sizeof(RDP_pack_blk));
  6. odzhan revised this gist Dec 31, 2023. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions rdp_pack.cpp
    Original file line number Diff line number Diff line change
    @@ -16,6 +16,8 @@
    The parameters to decompress data may be wrong. It appears to work fine for MPPC, but perhaps it won't work correctly
    for the others.
    cl /EHsc rdp_pack.cpp
    */

    #include <windows.h>
    @@ -195,8 +197,8 @@ rdp_encode(RDP_pack_ctx *c) {
    }

    // no compression?
    if (c.type == PACKET_COMPR_TYPE_8K ||
    c.type == PACKET_COMPR_TYPE_64K ||
    if (c->type == PACKET_COMPR_TYPE_8K ||
    c->type == PACKET_COMPR_TYPE_64K ||
    out.len == RDP_COMPR_BLK_LEN)
    {
    // copy uncompressed
  7. odzhan renamed this gist Dec 31, 2023. 1 changed file with 22 additions and 12 deletions.
    34 changes: 22 additions & 12 deletions rdp.cpp → rdp_pack.cpp
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,20 @@

    /**
    Compression using rdpbase.dll exports
    Compression using undocumented API in rdpbase.dll
    RDPCompress supports four algorithms : MPPC-8K, MPPC-64K, NCRUSH and XCRUSH.
    This code only supports compression and decompression using MPPC algorithms.
    This code only supports compression and decompression using MPPC algorithms because these were the easiest to figure out.
    The MPPC compression ratio is very similar to LZSS, so this could be quite useful for shellcode trying to evade detection.
    I was unable to get decompression working for NCRUSH and XCRUSH, which is why I'm publishing this.
    Maybe someone will figure out how to provide the correct input for these.
    Internally, NCRUSH expects the length of uncompressed or compressed data in the buffer.
    If you discover correct input for NCRUSH or XCRUSH, please let me know.
    The parameters to decompress data may be wrong. It appears to work fine for MPPC, but perhaps it won't work correctly
    for the others.
    */

    @@ -24,7 +34,7 @@

    #pragma pack(push, 1)
    typedef struct {
    uint8_t header; // An 8-bit, unsigned integer specifying the compression type and flags
    uint8_t header; // An 8-bit, unsigned integer specifying the compression type and flags
    uint8_t data[1]; // A variable-length array of bytes containing data encoded using RDP 8.0 Bulk Compression techniques
    } RDP8_BULK_ENCODED_DATA;
    #pragma pack(pop)
    @@ -51,7 +61,7 @@ typedef struct {
    #define PACKET_COMPR_TYPE_64K 1 // MPPC
    #define PACKET_COMPR_TYPE_RDP6 2 // NCRUSH
    #define PACKET_COMPR_TYPE_RDP61 3 // XCRUSH
    #define PACKET_COMPR_TYPE_RDP8 4
    #define PACKET_COMPR_TYPE_RDP8 4 // MSDN references this, but I doubt it's actually supported by OS

    #define PACKET_ENCRYPTED 0x10
    #define PACKET_COMPRESSED 0x20
    @@ -153,7 +163,7 @@ WriteFileData(std::string path, std::vector<BYTE> data) {
    // Use RDP algorithms to compress file.
    //
    bool
    rdp_pack(RDP_pack_ctx *c) {
    rdp_encode(RDP_pack_ctx *c) {
    void *SendCtx = NULL;
    bool result = false;

    @@ -185,12 +195,14 @@ rdp_pack(RDP_pack_ctx *c) {
    }

    // no compression?
    if (out.len == RDP_COMPR_BLK_LEN) {
    if (c.type == PACKET_COMPR_TYPE_8K ||
    c.type == PACKET_COMPR_TYPE_64K ||
    out.len == RDP_COMPR_BLK_LEN)
    {
    // copy uncompressed
    memcpy(&out, &in, sizeof(RDP_pack_blk));
    }

    //printf("Compressed %ld -> %ld...\n", in.len, out.len);
    c->outbuf.insert(c->outbuf.end(), (PBYTE)&out, (PBYTE)&out + out.len + sizeof(DWORD));

    inlen -= in.len;
    @@ -207,7 +219,7 @@ rdp_pack(RDP_pack_ctx *c) {
    //
    //
    bool
    rdp_depack(RDP_pack_ctx *c) {
    rdp_decode(RDP_pack_ctx *c) {
    void *RecvContext = NULL;
    bool result = false;

    @@ -240,8 +252,6 @@ rdp_depack(RDP_pack_ctx *c) {
    }
    }

    //if (!outlen) break;
    //printf("Decompressed %ld -> %ld...\n", in->len, outlen);
    c->outbuf.insert(c->outbuf.end(), outbuf, outbuf + outlen);
    start = 1;

    @@ -277,9 +287,9 @@ main(int argc, char *argv[]) {
    c.inbuf = ReadFileData(infile);

    if (encode) {
    rdp_pack(&c);
    rdp_encode(&c);
    } else {
    rdp_depack(&c);
    rdp_decode(&c);
    }

    // save output to file
  8. odzhan created this gist Dec 31, 2023.
    289 changes: 289 additions & 0 deletions rdp.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,289 @@

    /**
    Compression using rdpbase.dll exports
    RDPCompress supports four algorithms : MPPC-8K, MPPC-64K, NCRUSH and XCRUSH.
    This code only supports compression and decompression using MPPC algorithms.
    */

    #include <windows.h>

    #include <cstdio>
    #include <cstdint>
    #include <cstdlib>
    #include <cstring>

    #include <string>
    #include <vector>
    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <iomanip>

    #pragma pack(push, 1)
    typedef struct {
    uint8_t header; // An 8-bit, unsigned integer specifying the compression type and flags
    uint8_t data[1]; // A variable-length array of bytes containing data encoded using RDP 8.0 Bulk Compression techniques
    } RDP8_BULK_ENCODED_DATA;
    #pragma pack(pop)

    typedef struct {
    uint8_t descriptor; // SINGLE (0xE0) or MULTIPART (0xE1)

    // Optional fields
    uint16_t segmentCount; // Only present if descriptor is MULTIPART
    uint32_t uncompressedSize; // Only present if descriptor is MULTIPART
    RDP8_BULK_ENCODED_DATA bulkData; // Only present if descriptor is SINGLE
    //RDP_DATA_SEGMENT *segmentArray; // Only present if descriptor is MULTIPART
    } RDP_SEGMENTED_DATA_MP;

    #pragma pack(push, 1)
    typedef struct {
    uint32_t uncompressedSize; // Only present if descriptor is MULTIPART
    RDP8_BULK_ENCODED_DATA bulkData; // Only present if descriptor is SINGLE
    } RDP_SEGMENTED_DATA;
    #pragma pack(pop)

    // type of compression
    #define PACKET_COMPR_TYPE_8K 0 // MPPC
    #define PACKET_COMPR_TYPE_64K 1 // MPPC
    #define PACKET_COMPR_TYPE_RDP6 2 // NCRUSH
    #define PACKET_COMPR_TYPE_RDP61 3 // XCRUSH
    #define PACKET_COMPR_TYPE_RDP8 4

    #define PACKET_ENCRYPTED 0x10
    #define PACKET_COMPRESSED 0x20
    #define PACKET_AT_FRONT 0x40
    #define PACKET_FLUSHED 0x80

    // ******************************************************
    // Needed to allocate memory for compression context.
    typedef
    DWORD (WINAPI *_RDPCompress_GetContextSize)(DWORD ComprType);

    // Initialise compression context.
    typedef
    void (WINAPI *_RDPCompress_InitSendContext)(void *ctx, SIZE_T ctx_len, DWORD ComprType);

    // Compress inbuf and store in outbuf
    typedef
    DWORD (WINAPI *_RDPCompress)(DWORD ComprType, void *inbuf, void *outbuf, PDWORD outlen, void *ctx);

    // ******************************************************
    typedef
    DWORD (WINAPI *_RDPDeCompress_GetContextSize)(DWORD ComprType);

    typedef
    void (WINAPI *_RDPCompress_InitRecvContext)(void *ctx, SIZE_T ctx_len, DWORD ComprType, void *workspace);

    typedef
    DWORD (WINAPI *_RDPDecompress)(
    PVOID inbuf,
    DWORD inlen,
    DWORD start,
    PVOID* outbuf,
    PDWORD outlen,
    PVOID RecvContext,
    DWORD ComprType
    );

    typedef struct _RDP_pack_ctx {
    DWORD type; // compression algorithm

    _RDPCompress_GetContextSize RDPCompress_GetContextSize;
    _RDPCompress_InitSendContext RDPCompress_InitSendContext;
    _RDPCompress RDPCompress;

    _RDPDeCompress_GetContextSize RDPDeCompress_GetContextSize;
    _RDPCompress_InitRecvContext RDPCompress_InitRecvContext;
    _RDPDecompress RDPDecompress;

    std::vector<BYTE> inbuf, outbuf;
    } RDP_pack_ctx;

    #define RDP_COMPR_BLK_LEN 4096

    #pragma pack(push,1)
    typedef struct _RDP_pack_blk {
    DWORD len;
    BYTE data[RDP_COMPR_BLK_LEN];
    } RDP_pack_blk;
    #pragma pack(pop)

    bool
    init_pack_ctx(RDP_pack_ctx *c) {
    HMODULE rdpbase = LoadLibrary("rdpbase.dll");

    if (!rdpbase) return false;

    c->RDPCompress_GetContextSize = (_RDPCompress_GetContextSize)GetProcAddress(rdpbase, "RDPCompress_GetContextSize");
    c->RDPCompress_InitSendContext = (_RDPCompress_InitSendContext)GetProcAddress(rdpbase, "RDPCompress_InitSendContext");
    c->RDPCompress = (_RDPCompress)GetProcAddress(rdpbase, "RDPCompress");

    c->RDPDeCompress_GetContextSize = (_RDPDeCompress_GetContextSize)GetProcAddress(rdpbase, "RDPDeCompress_GetContextSize");
    c->RDPCompress_InitRecvContext = (_RDPCompress_InitRecvContext)GetProcAddress(rdpbase, "RDPCompress_InitRecvContext");
    c->RDPDecompress = (_RDPDecompress)GetProcAddress(rdpbase, "RDPDecompress");

    return c->RDPCompress_GetContextSize != NULL &&
    c->RDPCompress_InitSendContext != NULL &&
    c->RDPCompress != NULL &&
    c->RDPDeCompress_GetContextSize != NULL &&
    c->RDPCompress_InitRecvContext != NULL &&
    c->RDPDecompress != NULL;
    }

    std::vector<BYTE>
    ReadFileData(std::string path) {
    std::ifstream instream(path, std::ios::in | std::ios::binary);
    std::vector<BYTE> data((std::istreambuf_iterator<char>(instream)), std::istreambuf_iterator<char>());
    return data;
    }

    bool
    WriteFileData(std::string path, std::vector<BYTE> data) {
    std::ofstream outstream(path, std::ios::out | std::ios::binary);
    if (!outstream) return false;
    std::copy(data.begin(), data.end(), std::ostreambuf_iterator<char>(outstream));
    return outstream.good();
    }

    //
    // Use RDP algorithms to compress file.
    //
    bool
    rdp_pack(RDP_pack_ctx *c) {
    void *SendCtx = NULL;
    bool result = false;

    do {
    DWORD ctx_len = c->RDPCompress_GetContextSize(c->type);
    if (!ctx_len) break;

    SendCtx = malloc(ctx_len);
    if (!SendCtx) break;

    c->RDPCompress_InitSendContext(SendCtx, ctx_len, c->type);

    PBYTE inbuf = (PBYTE)c->inbuf.data();
    DWORD inlen = c->inbuf.size();

    while (inlen) {
    RDP_pack_blk in={0}, out={0};

    in.len = inlen > RDP_COMPR_BLK_LEN ? RDP_COMPR_BLK_LEN : inlen;
    out.len = in.len;
    memcpy(in.data, inbuf, in.len);


    c->RDPCompress(c->type, in.data, out.data, &out.len, SendCtx);

    if (!out.len) {
    printf("RDPCompress failed.\n");
    break;
    }

    // no compression?
    if (out.len == RDP_COMPR_BLK_LEN) {
    // copy uncompressed
    memcpy(&out, &in, sizeof(RDP_pack_blk));
    }

    //printf("Compressed %ld -> %ld...\n", in.len, out.len);
    c->outbuf.insert(c->outbuf.end(), (PBYTE)&out, (PBYTE)&out + out.len + sizeof(DWORD));

    inlen -= in.len;
    inbuf += in.len;
    }
    result = true;
    } while (false);

    if (SendCtx) free(SendCtx);
    return result;
    }

    //
    //
    //
    bool
    rdp_depack(RDP_pack_ctx *c) {
    void *RecvContext = NULL;
    bool result = false;

    do {
    DWORD ctx_len = c->RDPDeCompress_GetContextSize(c->type);
    RecvContext = malloc(ctx_len);
    if (!RecvContext) break;

    c->RDPCompress_InitRecvContext(RecvContext, ctx_len, c->type, NULL);

    PBYTE inbuf = (PBYTE)c->inbuf.data();
    DWORD inlen = c->inbuf.size();
    DWORD start = 0;

    while (inlen) {
    RDP_pack_blk *in = (RDP_pack_blk*)inbuf;

    PBYTE outbuf = NULL;
    DWORD outlen = 0;

    if (in->len == RDP_COMPR_BLK_LEN) {
    outbuf = in->data;
    outlen = RDP_COMPR_BLK_LEN;
    } else {
    c->RDPDecompress(in->data, in->len, start, (PVOID*)&outbuf, &outlen, RecvContext, c->type);

    if (!outbuf) {
    printf("RDPDecompress() failed.\n");
    break;
    }
    }

    //if (!outlen) break;
    //printf("Decompressed %ld -> %ld...\n", in->len, outlen);
    c->outbuf.insert(c->outbuf.end(), outbuf, outbuf + outlen);
    start = 1;

    inlen -= in->len + sizeof(DWORD);
    inbuf += in->len + sizeof(DWORD);
    }
    } while (false);

    if (RecvContext) free(RecvContext);
    return result;
    }

    int
    main(int argc, char *argv[]) {
    if ((argc != 4) || ((argv[1][0] != 'e') && (argv[1][0] != 'd'))) {
    printf("usage: rdp_pack [e/d] <infile> <outfile>\n");
    return 0;
    }

    bool encode = (argv[1][0] == 'e');
    const char* infile = argv[2];
    const char* outfile = argv[3];

    RDP_pack_ctx c;

    if (!init_pack_ctx(&c)) {
    printf("Unable to initialise RDP API.\n");
    return 0;
    }

    // set compression and input data
    c.type = PACKET_COMPR_TYPE_8K;
    c.inbuf = ReadFileData(infile);

    if (encode) {
    rdp_pack(&c);
    } else {
    rdp_depack(&c);
    }

    // save output to file
    WriteFileData(outfile, c.outbuf);
    printf("OK\n");
    return 0;
    }