forked from ladislav-zezula/FileTest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ReparseDataHsm.cpp
302 lines (256 loc) · 11.1 KB
/
ReparseDataHsm.cpp
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*****************************************************************************/
/* ReparseDataHsm.cpp Copyright (c) Ladislav Zezula 2018 */
/*---------------------------------------------------------------------------*/
/* Description: Implementation of HSM reparse data functions */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 18.03.14 1.00 Lad The first version of ReparseDataHsm.cpp */
/*****************************************************************************/
#include "FileTest.h"
#include "ReparseDataHsm.h"
#include "resource.h"
//-----------------------------------------------------------------------------
// Local functions
static bool HsmpCheckElement(PHSM_DATA HsmData, ULONG ElementIndex)
{
PHSM_ELEMENT_INFO pElementInfo = &HsmData->ElementInfos[ElementIndex];
ULONG ElementCount = HsmData->NumberOfElements;
if (pElementInfo->Type >= HSM_ELEMENT_TYPE_MAX)
return false;
if (pElementInfo->Offset != 0 && pElementInfo->Offset < HSM_MIN_DATA_SIZE(ElementCount))
return false;
if (pElementInfo->Offset > HsmData->Length)
return false;
if (pElementInfo->Length > HsmData->Length)
return false;
if ((pElementInfo->Offset + pElementInfo->Length) > HsmData->Length)
return false;
return true;
}
static NTSTATUS HsmValidateCommonData(PHSM_DATA HsmData, ULONG Magic, ULONG ElementCount, ULONG RemainingLength)
{
ULONG FirstElementOffset;
ULONG NumberOfElements;
ULONG CheckPhase = 0;
// The remaining data must be at least HSM_FILE_DATA_MINSIZE bytes
if (RemainingLength < HSM_MIN_DATA_SIZE(1))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 1;
if (HsmData->Magic != Magic)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 2;
// Check CRC, if present
if ((HsmData->Flags & HSM_DATA_HAVE_CRC) && RtlComputeCrc32(0, &HsmData->Length, RemainingLength - 8) != HsmData->Crc32)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 3;
// Check the remaining size
if (HsmData->Length != RemainingLength)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 4;
// Check the zero field
if ((NumberOfElements = HsmData->NumberOfElements) == 0)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 5;
// Check the offset of the first element
if ((FirstElementOffset = HSM_MIN_DATA_SIZE(NumberOfElements)) > RemainingLength)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 0x10000;
// Check the elements
for (ULONG i = 0; i < min(NumberOfElements, ElementCount); i++)
{
if (!HsmpCheckElement(HsmData, i))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase++;
}
CheckPhase = 0x20000;
// Check element[0] (version?)
if (NumberOfElements == 0 || RemainingLength < HSM_MIN_DATA_SIZE(1))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if (!HsmpCheckElement(HsmData, 0))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if (HsmData->ElementInfos[0].Type != HSM_ELEMENT_TYPE_BYTE || HsmData->ElementInfos[0].Length != sizeof(BYTE))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if (*HsmGetElementData(HsmData, 0) != 1)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
CheckPhase = 0x20001;
return STATUS_SUCCESS;
}
//-----------------------------------------------------------------------------
// Uncompresses the HSM reparse data buffer. Note that the buffer may be already
// uncompressed (if the flag 0x8000 is not set).
LPBYTE HsmGetElementData(PHSM_DATA HsmData, ULONG ElementIndex)
{
return ((LPBYTE)HsmData) + HsmData->ElementInfos[ElementIndex].Offset;
}
NTSTATUS HsmUncompressData(PREPARSE_DATA_BUFFER RawReparseData, ULONG RawReparseDataLength, PREPARSE_DATA_BUFFER * OutReparseData)
{
PREPARSE_DATA_BUFFER HsmReparseData = NULL;
NTSTATUS Status = STATUS_SUCCESS;
ULONG HsmReparseDataLength;
ULONG UncompressedSize = 0;
// Is the cloud buffer compressed?
if (RawReparseData->HsmReparseBufferRaw.Flags & 0x8000)
{
HsmReparseDataLength = sizeof(ULONG) + sizeof(USHORT) + sizeof(USHORT) + RawReparseData->HsmReparseBufferRaw.Length;
HsmReparseData = (PREPARSE_DATA_BUFFER)HeapAlloc(g_hHeap, HEAP_ZERO_MEMORY, HsmReparseDataLength);
if (HsmReparseData != NULL)
{
// Copy the data that don't belong in the compressed area
memcpy(HsmReparseData, RawReparseData, FIELD_OFFSET(REPARSE_DATA_BUFFER, HsmReparseBufferRaw.RawData));
Status = RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1,
HsmReparseData->HsmReparseBufferRaw.RawData,
HsmReparseDataLength - FIELD_OFFSET(REPARSE_DATA_BUFFER, HsmReparseBufferRaw.RawData),
RawReparseData->HsmReparseBufferRaw.RawData,
RawReparseDataLength - FIELD_OFFSET(REPARSE_DATA_BUFFER, HsmReparseBufferRaw.RawData),
&UncompressedSize);
if (NT_SUCCESS(Status))
{
HsmReparseData->ReparseDataLength = RawReparseData->HsmReparseBufferRaw.Length;
OutReparseData[0] = HsmReparseData;
return STATUS_SUCCESS;
}
// Free the allocated buffer
HeapFree(g_hHeap, 0, HsmReparseData);
HsmReparseData = NULL;
}
return STATUS_INSUFFICIENT_RESOURCES;
}
else
{
OutReparseData[0] = RawReparseData;
return STATUS_SUCCESS;
}
}
//-----------------------------------------------------------------------------
// Validates the HSM reparse point. Basically the same implementation like
// cldflt.sys!HsmpRpValidateBuffer:
// NTSTATUS HsmpRpValidateBuffer(PHSM_REPARSE_DATA pHsmReparseData, ULONG ReparseDataSize)
NTSTATUS HsmpBitmapIsReparseBufferSupported(PHSM_DATA HsmData, ULONG RemainingLength)
{
NTSTATUS Status;
ULONG NumberOfElements;
BYTE Element1;
BYTE Element2;
// Perform common data validation
Status = HsmValidateCommonData(HsmData, HSM_BITMAP_MAGIC, HSM_BITMAP_ELEMENTS, RemainingLength);
if (!NT_SUCCESS(Status))
return Status;
NumberOfElements = HsmData->NumberOfElements;
// Check element[2]
if (NumberOfElements < 2 || RemainingLength < HSM_MIN_DATA_SIZE(2))
return STATUS_NOT_FOUND;
if (!HsmpCheckElement(HsmData, 2))
return STATUS_NOT_FOUND;
if (HsmData->ElementInfos[2].Type != HSM_ELEMENT_TYPE_BYTE || HsmData->ElementInfos[2].Length != sizeof(BYTE))
return STATUS_NOT_FOUND;
Element2 = *HsmGetElementData(HsmData, 2);
if (Element2 != 0)
{
if (NumberOfElements < 4 || HsmData->ElementInfos[4].Offset == 0)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if (HsmData->ElementInfos[4].Length > 0x1000)
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
}
if (Element2 > 1)
STATUS_CLOUD_FILE_METADATA_CORRUPT;
if (NumberOfElements < 1 || RemainingLength < HSM_MIN_DATA_SIZE(1))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if (!HsmpCheckElement(HsmData, 1))
return STATUS_CLOUD_FILE_METADATA_CORRUPT;
if (HsmData->ElementInfos[1].Type != HSM_ELEMENT_TYPE_BYTE || HsmData->ElementInfos[1].Length != sizeof(BYTE))
return STATUS_NOT_FOUND;
Element1 = *HsmGetElementData(HsmData, 1);
if (Element1 <= 0 || Element1 > 0x14)
STATUS_CLOUD_FILE_METADATA_CORRUPT;
return STATUS_SUCCESS;
}
NTSTATUS HsmpCheckBitmapElement(PHSM_DATA HsmData, ULONG ElementIndex)
{
PHSM_DATA pBitmap = NULL;
NTSTATUS Status = STATUS_SUCCESS;
ULONG RemainingLength = HsmData->Length;
ULONG BitmapLength = 0;
__try
{
if (HsmData->NumberOfElements < ElementIndex || RemainingLength < HSM_MIN_DATA_SIZE(ElementIndex))
{
Status = STATUS_NOT_FOUND;
__leave;
}
if (!HsmpCheckElement(HsmData, ElementIndex))
{
Status = STATUS_NOT_FOUND;
__leave;
}
if (HsmData->ElementInfos[ElementIndex].Type != HSM_ELEMENT_TYPE_BITMAP)
{
Status = STATUS_NOT_FOUND;
__leave;
}
if (HsmData->ElementInfos[ElementIndex].Offset && HsmData->ElementInfos[ElementIndex].Length)
{
pBitmap = (PHSM_DATA)HsmGetElementData(HsmData, ElementIndex);
BitmapLength = HsmData->ElementInfos[ElementIndex].Length;
}
Status = HsmpBitmapIsReparseBufferSupported(pBitmap, BitmapLength);
}
__finally
{
Status = (Status == STATUS_NOT_FOUND) ? STATUS_SUCCESS : Status;
}
return Status;
}
NTSTATUS HsmValidateReparseData(PREPARSE_DATA_BUFFER ReparseData)
{
PHSM_REPARSE_DATA HsmReparseData = (PHSM_REPARSE_DATA)(&ReparseData->HsmReparseBufferRaw);
PHSM_DATA HsmData;
NTSTATUS Status;
ULONG NumberOfElements;
ULONG RemainingLength;
ULONG ElementFlags;
// Check the length
if (HsmReparseData->Length != ReparseData->ReparseDataLength)
return STATUS_INVALID_BLOCK_LENGTH;
// Check the revision
if ((HsmReparseData->Flags & 0x0F) > 1)
return STATUS_UNKNOWN_REVISION;
if ((HsmReparseData->Flags & 0x0F) < 1)
return STATUS_REVISION_MISMATCH;
// Get the HSM data and the remaining length
HsmData = (PHSM_DATA)&HsmReparseData->FileData;
RemainingLength = HsmReparseData->Length - FIELD_OFFSET(HSM_REPARSE_DATA, FileData);
// Check the common part of the data
Status = HsmValidateCommonData(HsmData, HSM_FILE_MAGIC, HSM_FILE_ELEMENTS, RemainingLength);
if (!NT_SUCCESS(Status))
return Status;
NumberOfElements = HsmData->NumberOfElements;
// Check element[1] (flags?)
if (NumberOfElements < 1 || RemainingLength < HSM_MIN_DATA_SIZE(2))
return STATUS_NOT_FOUND;
if (!HsmpCheckElement(HsmData, 1))
return STATUS_NOT_FOUND;
if (HsmData->ElementInfos[1].Type != HSM_ELEMENT_TYPE_UINT32 || HsmData->ElementInfos[1].Length != sizeof(DWORD))
return STATUS_NOT_FOUND;
ElementFlags = *(PULONG)HsmGetElementData(HsmData, 1);
if (ElementFlags & 0x10)
return STATUS_SUCCESS;
// Check element[2] (stream size)
if (NumberOfElements < 2 || RemainingLength < HSM_MIN_DATA_SIZE(3))
return STATUS_NOT_FOUND;
if (!HsmpCheckElement(HsmData, 2))
return STATUS_NOT_FOUND;
if (HsmData->ElementInfos[2].Type != HSM_ELEMENT_TYPE_UINT64 || HsmData->ElementInfos[2].Length != sizeof(ULONGLONG))
return STATUS_NOT_FOUND;
// Check element[4] (HSM_BITMAP)
Status = HsmpCheckBitmapElement(HsmData, 4);
if (!NT_SUCCESS(Status))
return Status;
// Check element[5] (HSM_BITMAP)
Status = HsmpCheckBitmapElement(HsmData, 5);
if (!NT_SUCCESS(Status))
return Status;
// Check element[6] (HSM_BITMAP)
Status = HsmpCheckBitmapElement(HsmData, 6);
return Status;
}