mirror of
https://github.com/ladislav-zezula/FileTest
synced 2026-06-08 15:24:12 +00:00
ad3fd132d2
* Workspaces updated to VS 2022 * Minor changes
303 lines
11 KiB
C++
303 lines
11 KiB
C++
/*****************************************************************************/
|
|
/* 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;
|
|
}
|