AceInfinity
Emeritus, Contributor
I seen a reference to these functions during some debugging, and thought I'd see how they do for file compression...
Here's some code I finally put together after some time, and it works pretty well:
*edit: Cleaned up code a bit.
I was able to compress a file down to 714 bytes from an original 9.18 kb (9408 bytes). That's ~7% of the original filesize...
Right now, I haven't thought of a good way to guess the decompressed size from the compressed size, unless that value is stored. You would have to guess at that based on the compressed filesize.
I've provided an example of a compressed txt file and it's original in an attachment at this url: RtlCompressBuffer & RtlDecompressBuffer. I can successfully compress to the compressed version, and decompress that output file to get back to the original.
Note: Combined with zip, I can get this data down to a compact 310 bytes and probably even less with some trial and error for other compression methods out there before they start producing larger output than input.
Edit: 202 bytes with bzip2.
Here's some code I finally put together after some time, and it works pretty well:
Code:
#include <iostream>
#include <Windows.h>
#include <fstream>
typedef unsigned long NTSTATUS;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000UL)
#define STATUS_BUFFER_ALL_ZEROS ((NTSTATUS)0x00000117UL)
#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DUL)
#define STATUS_UNSUPPORTED_COMPRESSION ((NTSTATUS)0xC000025FUL)
#define STATUS_NOT_SUPPORTED_ON_SBS ((NTSTATUS)0xC0000300UL)
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023UL)
#define STATUS_BAD_COMPRESSION_BUFFER ((NTSTATUS)0xC0000242UL)
HMODULE ntdll = GetModuleHandle("ntdll.dll");
typedef NTSTATUS(__stdcall *_RtlCompressBuffer)(
USHORT CompressionFormatAndEngine,
PUCHAR UncompressedBuffer,
ULONG UncompressedBufferSize,
PUCHAR CompressedBuffer,
ULONG CompressedBufferSize,
ULONG UncompressedChunkSize,
PULONG FinalCompressedSize,
PVOID WorkSpace
);
typedef NTSTATUS(__stdcall *_RtlDecompressBuffer)(
USHORT CompressionFormat,
PUCHAR UncompressedBuffer,
ULONG UncompressedBufferSize,
PUCHAR CompressedBuffer,
ULONG CompressedBufferSize,
PULONG FinalUncompressedSize
);
typedef NTSTATUS(__stdcall *_RtlGetCompressionWorkSpaceSize)(
USHORT CompressionFormatAndEngine,
PULONG CompressBufferWorkSpaceSize,
PULONG CompressFragmentWorkSpaceSize
);
void get_ntstatus_err(NTSTATUS ntstatus)
{
switch (ntstatus) {
case STATUS_BUFFER_ALL_ZEROS:
std::cout << "Error: STATUS_BUFFER_ALL_ZEROS";
break;
case STATUS_INVALID_PARAMETER:
std::cout << "Error: STATUS_INVALID_PARAMETER";
break;
case STATUS_UNSUPPORTED_COMPRESSION:
std::cout << "Error: STATUS_UNSUPPORTED_COMPRESSION";
break;
case STATUS_NOT_SUPPORTED_ON_SBS:
std::cout << "Error: STATUS_NOT_SUPPORTED_ON_SBS";
break;
case STATUS_BUFFER_TOO_SMALL:
std::cout << "Error: STATUS_BUFFER_TOO_SMALL";
break;
case STATUS_BAD_COMPRESSION_BUFFER:
std::cout << "Error: STATUS_BAD_COMPRESSION_BUFFER";
break;
default:
std::cout << "(Unknown Error)";
}
std::endl(std::cout);
}
// ----------------------------------------------------------------------------------------------------
char *get_file_buffer(const char *filepath, int &buffer_len)
{
std::ifstream ifs(filepath, std::ofstream::binary);
if (!ifs.is_open()) return 0;
ifs.seekg(0, ifs.end);
buffer_len = ifs.tellg();
ifs.seekg(0, ifs.beg);
char *buffer = new char[buffer_len];
ifs.read(buffer, buffer_len);
return buffer;
}
// ----------------------------------------------------------------------------------------------------
int write_file_buffer(const char *filepath, char *buffer, int buffer_len)
{
std::ofstream ofs(filepath, std::ofstream::binary);
if (!ofs.is_open()) return 1;
ofs.write(buffer, buffer_len);
ofs.close();
return 0;
}
// ----------------------------------------------------------------------------------------------------
UCHAR *compress_buffer(const char *buffer, const ULONG bufferLen, ULONG compBufferLen, ULONG *compBufferSize)
{
_RtlCompressBuffer RtlCompressBuffer = (_RtlCompressBuffer) GetProcAddress(ntdll, "RtlCompressBuffer");
_RtlGetCompressionWorkSpaceSize RtlGetCompressionWorkSpaceSize = (_RtlGetCompressionWorkSpaceSize) GetProcAddress(ntdll, "RtlGetCompressionWorkSpaceSize");
ULONG bufWorkspaceSize; // Workspace Size
ULONG fragWorkspaceSize; // Fragmented Workspace Size (Unused)
NTSTATUS ret = RtlGetCompressionWorkSpaceSize(
COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, // CompressionFormatAndEngine
&bufWorkspaceSize, // CompressBufferWorkSpaceSize
&fragWorkspaceSize // CompressFragmentWorkSpaceSize
);
if (ret != STATUS_SUCCESS) {
std::cout << "An error ocurred while trying to calculate the compression workspace size." << std::endl;
get_ntstatus_err(ret);
return 0;
}
std::cout << "Compression Workspace Size: 0x" << std::hex << bufWorkspaceSize
<< std::dec << " (" << bufWorkspaceSize << ")" << std::endl;
VOID *workspace = (VOID *)LocalAlloc(LMEM_FIXED, bufWorkspaceSize);
if (workspace == NULL) {
std::cout << "Failed to allocate space for workspace" << std::endl;
return 0;
}
UCHAR *compBuffer = new UCHAR[compBufferLen];
NTSTATUS result = RtlCompressBuffer(
COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, // CompressionFormatAndEngine
(UCHAR *)buffer, // UncompressedBuffer
bufferLen, // UncompressedBufferSize
compBuffer, // CompressedBuffer
compBufferLen, // CompressedBufferSize
4096, // UncompressedChunkSize
compBufferSize, // FinalCompressedSize
workspace // WorkSpace
);
LocalFree(workspace);
if (result != STATUS_SUCCESS) {
get_ntstatus_err(result);
return 0;
}
// std::cout << "Compressed DATA: " << compBuffer << std::endl;
std::cout << "Compressed Length: " << compBufferSize << std::endl;
return compBuffer;
}
// ----------------------------------------------------------------------------------------------------
UCHAR *decompress_buffer(const char *buffer, const int bufferLen, const int uncompBufferLen, ULONG *uncompBufferSize)
{
_RtlDecompressBuffer RtlDecompressBuffer = (_RtlDecompressBuffer) GetProcAddress(ntdll, "RtlDecompressBuffer");
UCHAR *uncompBuffer = new UCHAR[uncompBufferLen];
NTSTATUS result = RtlDecompressBuffer(
COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, // CompressionFormat
uncompBuffer, // UncompressedBuffer
uncompBufferLen, // UncompressedBufferSize
(UCHAR *)buffer, // CompressedBuffer
bufferLen, // CompressedBufferSize
uncompBufferSize // FinalUncompressedSize
);
if (result != STATUS_SUCCESS) {
get_ntstatus_err(result);
return 0;
}
// std::cout << "Uncompressed DATA: " << uncompBuffer << std::endl;
std::cout << "Uncompressed Length: " << uncompBufferSize << std::endl;
return uncompBuffer;
}
// ----------------------------------------------------------------------------------------------------
// #define COMPRESS
int main()
{
#ifdef COMPRESS
const char originalFile[] = "Z:\\ai.gif";
#endif
const char compressedFile[] = "Z:\\ai-compressed.gif";
#ifndef COMPRESS
const char decompressedFile[] = "Z:\\ai-decompressed.gif";
#endif
#ifdef COMPRESS
int bufferLen;
char *buffer = get_file_buffer(originalFile, bufferLen);
if (buffer == 0) {
std::cout << "An error ocurred while trying to read the bytes of the specified file into the buffer." << std::endl;
return 1;
}
ULONG realCompSize;
UCHAR *compressed_buffer = compress_buffer(buffer, bufferLen, bufferLen + 512, &realCompSize);
if (compressed_buffer == 0) return 1;
if (write_file_buffer(compressedFile, (char *)compressed_buffer, (int)realCompSize) != 0) {
std::cout << "An error ocurred while trying to write the bytes of the compressed buffer to a file." << std::endl;
return 1;
}
std::cout << compressed_buffer << std::endl;
#else
int bufferLen;
char *buffer = get_file_buffer(compressedFile, bufferLen);
if (buffer == 0) {
std::cout << "An error ocurred while trying to read the bytes of the specified file into the buffer." << std::endl;
return 1;
}
ULONG realDecompSize;
UCHAR *decompressed_buffer = decompress_buffer(buffer, bufferLen, bufferLen * 100, &realDecompSize);
if (decompressed_buffer == 0) return 1;
if (write_file_buffer(decompressedFile, (char *)decompressed_buffer, (int)realDecompSize) != 0) {
std::cout << "An error ocurred while trying to write the bytes of the compressed buffer to a file." << std::endl;
return 1;
}
#endif
return 0;
}
I was able to compress a file down to 714 bytes from an original 9.18 kb (9408 bytes). That's ~7% of the original filesize...
Right now, I haven't thought of a good way to guess the decompressed size from the compressed size, unless that value is stored. You would have to guess at that based on the compressed filesize.
I've provided an example of a compressed txt file and it's original in an attachment at this url: RtlCompressBuffer & RtlDecompressBuffer. I can successfully compress to the compressed version, and decompress that output file to get back to the original.
Note: Combined with zip, I can get this data down to a compact 310 bytes and probably even less with some trial and error for other compression methods out there before they start producing larger output than input.
Edit: 202 bytes with bzip2.
Last edited: