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.
Compression using RDP API
/**
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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment