WindowsAPI编程核心技术-压缩技术

数据压缩API

RtlGetCompressionWorkSpaceSize

确定压缩和解压缩工作空间缓冲区的正确大小,单位字节。

1
2
3
4
5
6
7
8
NTSTATUS RtlGetCompressionWorkSpaceSize(
_In_ USHORT CompressionFormatAndEngine,
//压缩格式和引擎类型
_Out_ PULONG CompressBufferWorkSpaceSize,
//接收接收缓冲区所需大小
_Out_ PULONG CompressFragmentWorkSpaceSize
//接收解压缓冲区为片段所需大小
)//成功返回STATUS_SUCCESS 否则失败

RtlCompressBuffer

压缩缓冲区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
NTSTATUS RtlCompressBuffer(
_In_ USHORT CompressionFormatAndEngine,
//压缩格式和引擎类型 有枚举值自己去查
_In_ PUCHAR UncompressedBuffer,
//要被压缩的缓冲区
_In_ ULONG UncompressedBufferSize,
//UncompressedBuffer大小
_Out_ PUCHAR CompressedBuffer,
//压缩后缓冲区
_In_ ULONG CompressedBufferSize,
//CompressedBuffer大小
_In_ ULONG UnCompressedChunkSize,
//压缩UncompressedBuffer使用块大小 不能随意设定 推荐4096
_Out_ PULONG FinalCompressedSize,
//接收CompressedBuffer压缩数据大小
_In_ PVOID WorkSpace
//工作空间缓冲区大小
)//成功返回STATUS_SUCCESS 否则失败

RtlDecompressBuffer

解压缩整个压缩缓冲区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NTSTATUS RtlDecompressBuffer(
_In_ USHORT CompressionFormat,
//压缩格式
_Out_ PUCHAR UncompressedBuffer,
//存储解压缩数据的缓冲区
_In_ ULONG UncompressedBufferSize,
//UncompressedBuffer大小
_In_ PUCHAR CompressedBuffer,
//要被解压缩的数据缓冲区
_In_ ULONG CompressedBufferSize,
//CompressedBuffer大小
_Out_ PULONG FinalUncompressedSize
//解压后大小指针
)//成功返回STATUS_SUCCESS 否则失败

例子

压缩和解压缩分别用ntdll.dll中的RtlCompressBufferRtlDecompressBuffer,这俩都需要动态调用。压缩和解压缩后所需的缓冲区大小都不确定,先初始为$4096$字节,如果不够再重新申请重新压缩或解压缩。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <Windows.h>
#include <cstdio>
typedef NTSTATUS(NTAPI* pfnRtlGetCompressionWorkSpaceSize)(
_In_ USHORT CompressionFormatAndEngine,
_Out_ PULONG CompressBufferWorkSpaceSize,
_Out_ PULONG CompressFragmentWorkSpaceSize
);
typedef NTSTATUS(NTAPI* pfnRtlCompressBuffer)(
_In_ USHORT CompressionFormatAndEngine,
_In_ PUCHAR UncompressedBuffer,
_In_ ULONG UncompressedBufferSize,
_Out_ PUCHAR CompressedBuffer,
_In_ ULONG CompressedBufferSize,
_In_ ULONG UnCompressedChunkSize,
_Out_ PULONG FinalCompressedSize,
_In_ PVOID WorkSpace
);
typedef NTSTATUS(NTAPI* pfnRtlDecompressBuffer)(
_In_ USHORT CompressionFormat,
_Out_ PUCHAR UncompressedBuffer,
_In_ ULONG UncompressedBufferSize,
_In_ PUCHAR CompressedBuffer,
_In_ ULONG CompressedBufferSize,
_Out_ PULONG FinalUncompressedSize
);
BOOL CompressData(BYTE* pUncompressData, DWORD dwUncompressDataLength, BYTE** ppCompressData, DWORD* pdwCompressDataLength) {
BOOL bRet = FALSE;
HMODULE hModule=NULL;
pfnRtlGetCompressionWorkSpaceSize RtlGetCompressionWorkSpaceSize = NULL;
pfnRtlCompressBuffer RtlCompressBuffer=NULL;
NTSTATUS status=0;
DWORD dwWorkSpaceSize=0, dwFragmentWorkSpaceSize=0;
PUCHAR pWorkSpace,pCompressData;
DWORD dwCompressDataLength=4096;
DWORD dwFinalCompressSize=0;
do {
hModule = ::LoadLibrary(L"ntdll.dll");
if (NULL == hModule)
break;
RtlGetCompressionWorkSpaceSize = (pfnRtlGetCompressionWorkSpaceSize)::GetProcAddress(hModule, "RtlGetCompressionWorkSpaceSize");
if (RtlGetCompressionWorkSpaceSize == NULL)
break;
RtlCompressBuffer = (pfnRtlCompressBuffer)::GetProcAddress(hModule, "RtlCompressBuffer");
if (NULL == RtlCompressBuffer)
break;
status = RtlGetCompressionWorkSpaceSize(COMPRESSION_FORMAT_LZNT1|COMPRESSION_ENGINE_STANDARD,&dwWorkSpaceSize,&dwFragmentWorkSpaceSize);
if (0 != status)
break;
pWorkSpace = new BYTE[dwWorkSpaceSize];
if (NULL == pWorkSpace)
break;
::RtlZeroMemory(pWorkSpace, dwWorkSpaceSize);
while (TRUE) {
pCompressData = new BYTE[dwCompressDataLength];
if (NULL == pCompressData)
break;
::RtlZeroMemory(pCompressData, dwCompressDataLength);
RtlCompressBuffer(COMPRESSION_FORMAT_LZNT1, pUncompressData, dwUncompressDataLength, pCompressData, dwCompressDataLength, 4096, &dwFinalCompressSize, (PVOID)pWorkSpace);
if (dwCompressDataLength < dwFinalCompressSize) {
if (pCompressData) {
delete[]pCompressData;
pCompressData = NULL;
};
dwCompressDataLength = dwFinalCompressSize;
}
else
break;
};
*ppCompressData = pCompressData;
*pdwCompressDataLength = dwFinalCompressSize;
bRet = TRUE;
}while(FALSE);
if (pWorkSpace) {
delete[]pWorkSpace;
pWorkSpace = NULL;
};
if (hModule)
::FreeLibrary(hModule);
return bRet;
};
BOOL UncompressData(BYTE* pCompressData, DWORD dwCompressDataLength, BYTE** ppUncompressData, DWORD* pdwUncompressDataLength) {
BOOL bRet = FALSE;
HMODULE hModule = NULL;
pfnRtlDecompressBuffer RtlDecompressBuffer = NULL;
BYTE* pUncompressData = NULL;
DWORD dwUncompressDataLength = 4096;
DWORD dwFinalUncompressSize = 0;
do {
hModule = ::LoadLibrary(L"ntdll.dll");
if (NULL == hModule)
break;
RtlDecompressBuffer = (pfnRtlDecompressBuffer)::GetProcAddress(hModule, "RtlDecompressBuffer");
if (NULL == RtlDecompressBuffer)
break;
while (TRUE) {
pUncompressData = new BYTE[dwUncompressDataLength];
if (NULL == pUncompressData)
break;
::RtlZeroMemory(pUncompressData, dwUncompressDataLength);
RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, pUncompressData, dwUncompressDataLength, pCompressData, dwCompressDataLength, &dwFinalUncompressSize);
if (dwUncompressDataLength < dwFinalUncompressSize) {
if (pUncompressData) {
delete[]pUncompressData;
pUncompressData = NULL;
};
dwUncompressDataLength = dwFinalUncompressSize;
}
else
break;
};
*ppUncompressData = pUncompressData;
*pdwUncompressDataLength = dwFinalUncompressSize;
bRet = TRUE;
}while(FALSE);
if (hModule)
::FreeLibrary(hModule);
return bRet;
}
int main(int argc,char* argv[]) {
BOOL bRet = FALSE;
char szBuffer[] = "xxx"; //要被压缩的数据
DWORD dwBufferLength = ::lstrlen((LPCWSTR)szBuffer);
BYTE* pCompressData = NULL;
DWORD dwCompressDataLength = 0;
BYTE* pUncompressData = NULL;
DWORD dwUncompressDataLength = 0;
CompressData((BYTE*)szBuffer, dwBufferLength, &pCompressData, &dwCompressDataLength);
UncompressData(pCompressData, dwCompressDataLength, &pUncompressData, &dwUncompressDataLength);
for (DWORD i = 0; i < dwBufferLength; i++)
printf("%X", szBuffer[i]);
for (DWORD i = 0; i < dwCompressDataLength; i++)
printf("%X", pCompressData[i]);
for (DWORD i = 0; i < dwUncompressDataLength; i++)
printf("%X", pUncompressData[i]);
if (pUncompressData) {
delete[]pUncompressData;
pUncompressData = NULL;
};
if (pCompressData) {
delete[]pCompressData;
pCompressData = NULL;
};
system("pause");
return 0;
};

ZLIB

安装

Github上下载ZLIB的源码,找到“zlib-1.3.1\contrib\vstudio\vc17”,用Visual Studio 2022 打开“zlibvc.sln”,选中“zlibstat”解决方案进行生成,生成结果在“zlib-1.3.1\contrib\vstudio\vc17\x64\ZlibStatDebug\zlibstat.lib”。在需要导入的工程.sln下新建文件夹叫“zlib”,里面塞上zconf.h、zlib.h和zlibstat.lib仨文件。

对该项目进行一些配置:“C/C++”->“预处理器”->“预处理器定义”添加“ZLIB_WINAPI”,“代码生成”->“运行库”在Debug下设置为“/MTd”及Release下设置为“/MT”,Debug下“链接器”->“命令行”->“其他选项”中添加“/FORCE:MULTIPLE”使链接器创建一个有效的exe或dll即使一个函数或变量多次引用或多处定义,Release下则添加“/SAFESEH:NO”来解决“SAFESEH映像不安全”。

compress

将源缓冲区内容压缩到目标缓冲区。

1
2
3
4
5
6
7
8
9
10
int compress(
Bytef* dest,
//目标数据缓冲区
uLongf* destLen,
//dest大小 单位字节
const Bytef* source,
//源数据缓冲区
uLong sourceLen
//source大小
)//0成功 -3数据类型有误 -4没有足够内存 -5输出缓冲区不够大

uncompress

源缓冲区内容解压缩到目标缓冲区。

1
2
3
4
5
6
int uncompress(
Bytef* dest,
uLongf* destLen,
const Bytef* source,
uLong sourceLen
)//都同上不写了

例子

缓冲区大小不明确,需要重复申请,$100\mathrm{KB}$为递增周期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include "zlib\\zconf.h"
#include "zlib\\zlib.h"
#pragma comment(lib,"zlib\\zlibstat.lib") //x64就编译成x64 x86就编译成x86
BOOL CompressData(BYTE* pDestData, DWORD dwDestDataSize, BYTE* pSrcData, DWORD dwFileSize) {
int iRet = 0;
do {
iRet = compress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);
if (iRet == 0)
break; //成功
else if (-5 == iRet) {
delete[]pDestData;
pDestData = NULL;
dwDestDataSize += 100 * 1024;
pDestData = new BYTE[dwDestDataSize];
if (NULL == pDestData) {
delete[]pSrcData;
pSrcData = NULL;
return FALSE;
};
}
else {
delete[]pDestData;
pDestData = NULL;
delete[]pSrcData;
pSrcData = NULL;
return FALSE;
};
} while (TRUE);
};
BOOL UnCompressData(BYTE* pDestData, DWORD dwDestDataSize, BYTE* pSrcData, DWORD dwFileSize) {
int iRet = 0;
do {
iRet = uncompress(pDestData, &dwDestDataSize, pSrcData, dwFileSize);
if (iRet == 0)
break; //成功
else if (-5 == iRet) {
delete[]pDestData;
pDestData = NULL;
dwDestDataSize += 100 * 1024;
pDestData = new BYTE[dwDestDataSize];
if (NULL == pDestData) {
delete[]pSrcData;
pSrcData = NULL;
return FALSE;
};
}
else {
delete[]pDestData;
pDestData = NULL;
delete[]pSrcData;
pSrcData = NULL;
return FALSE;
};
} while (TRUE);
};