mirror of
https://github.com/ladislav-zezula/FileTest
synced 2026-06-08 15:24:12 +00:00
612 lines
17 KiB
C++
612 lines
17 KiB
C++
/*****************************************************************************/
|
|
/* AceResource.cpp Copyright (c) Ladislav Zezula 2023 */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Description: */
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Date Ver Who Comment */
|
|
/* -------- ---- --- ------- */
|
|
/* 23.11.23 1.00 Lad Created */
|
|
/*****************************************************************************/
|
|
|
|
#include "FileTest.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local structures
|
|
|
|
typedef struct _LOCAL_OCTET_STRING
|
|
{
|
|
ULONG Length;
|
|
BYTE Data[256];
|
|
} LOCAL_OCTET_STRING, *PLOCAL_OCTET_STRING;
|
|
|
|
static void MakeOctetString(LOCAL_OCTET_STRING & OctetString, LPCVOID lpData, ULONG cbData)
|
|
{
|
|
assert(cbData <= 256);
|
|
|
|
memset(&OctetString, 0, sizeof(LOCAL_OCTET_STRING));
|
|
memcpy(OctetString.Data, lpData, cbData);
|
|
OctetString.Length = cbData;
|
|
}
|
|
|
|
static void MakeOctetString(LOCAL_OCTET_STRING & OctetString, ULONGLONG IntValue)
|
|
{
|
|
// Prepare an 8-byte octet string (little endian)
|
|
MakeOctetString(OctetString, &IntValue, sizeof(IntValue));
|
|
}
|
|
|
|
static void MakeOctetStringSid(LOCAL_OCTET_STRING & OctetString, PSID pSid)
|
|
{
|
|
MakeOctetString(OctetString, pSid, RtlLengthSid(pSid));
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Copies data to an output buffer
|
|
|
|
NTSTATUS CopyDataAway(LPBYTE pbPtr, LPBYTE pbEnd, LPCVOID lpData, ULONG cbData, PULONG pcbMoveBy)
|
|
{
|
|
if(cbData > (ULONG)(pbEnd - pbPtr))
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
|
|
// Copy the data to the target buffer
|
|
memcpy(pbPtr, lpData, cbData);
|
|
|
|
// Give the length of the data
|
|
if(pcbMoveBy != NULL)
|
|
pcbMoveBy[0] = cbData;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static LPBYTE ExportDataAway(LPBYTE pbPtr, LPBYTE pbEnd, LPCVOID lpData, ULONG cbData)
|
|
{
|
|
if(CopyDataAway(pbPtr, pbEnd, lpData, cbData, NULL) != STATUS_SUCCESS)
|
|
return NULL;
|
|
return pbPtr;
|
|
}
|
|
|
|
static LPBYTE ExportDataAway(LPBYTE pbPtr, LPBYTE pbEnd, LOCAL_OCTET_STRING & OctetString)
|
|
{
|
|
return ExportDataAway(pbPtr, pbEnd, &OctetString, sizeof(ULONG) + OctetString.Length);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ACE_CSA_OBJECT implementation
|
|
|
|
ACE_CSA_OBJECT::ACE_CSA_OBJECT()
|
|
{
|
|
lpData = NULL;
|
|
cbData = 0;
|
|
}
|
|
|
|
ACE_CSA_OBJECT::~ACE_CSA_OBJECT()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
LPBYTE ACE_CSA_OBJECT::ImportData(LPBYTE pbStructure, LPBYTE pbEnd, size_t Offset, size_t Length)
|
|
{
|
|
LPVOID lpNewData;
|
|
|
|
// Check for length overflow
|
|
if((Length & 0xFFFFFFFF) != Length)
|
|
{
|
|
SetLastError((DWORD)STATUS_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
// Is there enough data in the input?
|
|
if((pbStructure + Offset + Length) > pbEnd)
|
|
{
|
|
SetLastError((DWORD)STATUS_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
// Allocate the buffer for the data.
|
|
// Make sure that it's always aligned to 8
|
|
if((lpNewData = LocalAlloc(LPTR, ALIGN_TO_SIZE(Length, 8))) == NULL)
|
|
{
|
|
SetLastError((DWORD)STATUS_NO_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
// Free any existing data
|
|
Clear();
|
|
|
|
// Copy the data
|
|
memcpy(lpNewData, pbStructure + Offset, Length);
|
|
lpData = lpNewData;
|
|
cbData = (ULONG)(Length);
|
|
|
|
// Return the position of the data after the input
|
|
return pbStructure + Offset + ALIGN_TO_SIZE(Length, sizeof(DWORD));
|
|
}
|
|
|
|
LPBYTE ACE_CSA_OBJECT::ImportOctet(PACE_OCTET_STRING pOctetString)
|
|
{
|
|
LPBYTE pbOctet = (LPBYTE)(pOctetString);
|
|
size_t cbOctet = OctetStringSize(pOctetString);
|
|
|
|
return ImportData(pbOctet, pbOctet + cbOctet, 0, cbOctet);
|
|
}
|
|
|
|
void ACE_CSA_OBJECT::Clear()
|
|
{
|
|
if(lpData != NULL)
|
|
LocalFree(lpData);
|
|
lpData = NULL;
|
|
cbData = 0;
|
|
}
|
|
|
|
size_t ACE_CSA_OBJECT::ExportSize(size_t cbAlignSize) const
|
|
{
|
|
return ALIGN_TO_SIZE(cbData, cbAlignSize);
|
|
}
|
|
|
|
LPBYTE ACE_CSA_OBJECT::Export(LPBYTE pbPtr, LPBYTE pbEnd) const
|
|
{
|
|
size_t cbLength = ExportSize();
|
|
|
|
// Check whether the object fits into the buffer
|
|
if((pbPtr + cbLength) > pbEnd)
|
|
{
|
|
SetLastError(ERROR_BUFFER_OVERFLOW);
|
|
return NULL;
|
|
}
|
|
|
|
// Copy the data to the buffer
|
|
memcpy(pbPtr, lpData, cbLength);
|
|
return pbPtr + ALIGN_TO_SIZE(cbLength, sizeof(DWORD));
|
|
}
|
|
|
|
LPBYTE ACE_CSA_OBJECT::Default(LPBYTE /* pbPtr */, LPBYTE /* pbEnd */, DWORD /* dwIndexHint */) const
|
|
{
|
|
// Should never be called
|
|
assert(false);
|
|
return NULL;
|
|
}
|
|
|
|
LPBYTE ACE_CSA_OBJECT::Import(LPCVOID /* lpObject */)
|
|
{
|
|
// Import of an unknown object is not implemented
|
|
SetLastError((DWORD)STATUS_NOT_SUPPORTED);
|
|
return NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ACE_CSA_DWORD64 implementation
|
|
|
|
LPBYTE ACE_CSA_DWORD64::Default(LPBYTE pbPtr, LPBYTE pbEnd, DWORD i) const
|
|
{
|
|
DWORD64 DefaultValues[] = {0xDEADBABEULL, 0x12342ULL};
|
|
DWORD64 DefaultValue = (i < _countof(DefaultValues)) ? DefaultValues[i] : 0x1234567812345678ULL + i;
|
|
|
|
return ExportDataAway(pbPtr, pbEnd, &DefaultValue, sizeof(DefaultValue));
|
|
}
|
|
|
|
LPBYTE ACE_CSA_DWORD64::Import(LPCVOID lpObject)
|
|
{
|
|
LPBYTE pbObject = (LPBYTE)(lpObject);
|
|
|
|
return ImportData(pbObject, pbObject + sizeof(DWORD64), 0, sizeof(DWORD64));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ACE_CSA_LPWSTR implementation
|
|
|
|
LPBYTE ACE_CSA_LPWSTR::Default(LPBYTE pbPtr, LPBYTE pbEnd, DWORD i) const
|
|
{
|
|
LPCWSTR DefaultValues[] = {L"Daenerys", L"Targaryen", L"TheStormBorn"};
|
|
LPCWSTR DefaultValue = DefaultValues[i];
|
|
WCHAR szBuffer[128];
|
|
|
|
if(i >= _countof(DefaultValues))
|
|
{
|
|
StringCchPrintfW(szBuffer, _countof(szBuffer), L"DefaultString%u", i);
|
|
DefaultValue = szBuffer;
|
|
}
|
|
return ExportDataAway(pbPtr, pbEnd, DefaultValue, (ULONG)StringLength(DefaultValue));
|
|
}
|
|
|
|
LPBYTE ACE_CSA_LPWSTR::Import(LPCVOID lpObject)
|
|
{
|
|
LPBYTE pbString = (LPBYTE)(lpObject);
|
|
size_t cbString;
|
|
|
|
// Ignore the length, calculate it on our own
|
|
if(lpObject == NULL)
|
|
return pbString;
|
|
cbString = StringLength(lpObject);
|
|
|
|
// Proceed with the import
|
|
return ImportData(pbString, pbString + cbString, 0, cbString);
|
|
}
|
|
|
|
size_t ACE_CSA_LPWSTR::StringLength(LPCVOID lpObject) const
|
|
{
|
|
size_t cbString = 0;
|
|
|
|
if(lpObject != NULL)
|
|
cbString = (wcslen((LPWSTR)(lpObject)) + 1) * sizeof(WCHAR);
|
|
return cbString;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ACE_CSA_SID implementation
|
|
// Windows kernel requires the SID to be prepended with 32-bit length: [Length] [SID]
|
|
|
|
LPBYTE ACE_CSA_SID::Default(LPBYTE pbPtr, LPBYTE pbEnd, DWORD i) const
|
|
{
|
|
LOCAL_OCTET_STRING OctetString;
|
|
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
MakeOctetStringSid(OctetString, (PSID)(SidLocAdmins));
|
|
break;
|
|
|
|
case 1:
|
|
MakeOctetStringSid(OctetString, (PSID)(SidLocUsers));
|
|
break;
|
|
|
|
default:
|
|
MakeOctetStringSid(OctetString, (PSID)(SidEveryone));
|
|
break;
|
|
}
|
|
return ExportDataAway(pbPtr, pbEnd, OctetString);
|
|
}
|
|
|
|
LPBYTE ACE_CSA_SID::Import(LPCVOID lpObject)
|
|
{
|
|
return ImportOctet((PACE_OCTET_STRING)(lpObject));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ACE_CSA_BOOLEAN implementation
|
|
// Windows kernel requires each BOOLEAN value to have 8 bytes
|
|
|
|
size_t ACE_CSA_BOOLEAN::ExportSize(size_t cbAlignSize) const
|
|
{
|
|
return ALIGN_TO_SIZE(sizeof(ULONG64), cbAlignSize);
|
|
}
|
|
|
|
LPBYTE ACE_CSA_BOOLEAN::Default(LPBYTE pbPtr, LPBYTE pbEnd, DWORD i) const
|
|
{
|
|
ULONG64 DefaultValue = i & 1;
|
|
|
|
return ExportDataAway(pbPtr, pbEnd, &DefaultValue, sizeof(DefaultValue));
|
|
}
|
|
|
|
LPBYTE ACE_CSA_BOOLEAN::Import(LPCVOID lpObject)
|
|
{
|
|
LPBYTE pbObject = (LPBYTE)(lpObject);
|
|
|
|
return ImportData(pbObject, pbObject + sizeof(BOOLEAN), 0, sizeof(BOOLEAN));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ACE_CSA_OCTET_STRING implementation
|
|
|
|
LPBYTE ACE_CSA_OCTET_STRING::Default(LPBYTE pbPtr, LPBYTE pbEnd, DWORD i) const
|
|
{
|
|
LOCAL_OCTET_STRING OctetString;
|
|
BYTE OctetString0[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
|
|
BYTE OctetString1[] = {0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6, 0x07, 0x18, 0x29, 0x3A, 0x4B, 0x3C, 0x4D, 0x01, 0x02, 0x03, 0x04};
|
|
|
|
switch(i)
|
|
{
|
|
case 0:
|
|
MakeOctetString(OctetString, OctetString0, sizeof(OctetString0));
|
|
break;
|
|
|
|
case 1:
|
|
MakeOctetString(OctetString, OctetString1, sizeof(OctetString1));
|
|
break;
|
|
|
|
default:
|
|
MakeOctetString(OctetString, i);
|
|
break;
|
|
}
|
|
return ExportDataAway(pbPtr, pbEnd, OctetString);
|
|
}
|
|
|
|
LPBYTE ACE_CSA_OCTET_STRING::Import(LPCVOID lpObject)
|
|
{
|
|
return ImportOctet((PACE_OCTET_STRING)(lpObject));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// ACE_CSA_HELPER implementation
|
|
|
|
ACE_CSA_HELPER::ACE_CSA_HELPER(LPCWSTR szName, WORD wValueType, DWORD dwValueCount)
|
|
{
|
|
// Setup the object so that it does not contain anything
|
|
InitialReset();
|
|
|
|
// Create the object name
|
|
if(Name.Import(szName) == NULL)
|
|
return;
|
|
|
|
// Set the value type and count
|
|
SetValueType(wValueType, dwValueCount);
|
|
}
|
|
|
|
ACE_CSA_HELPER::ACE_CSA_HELPER()
|
|
{
|
|
InitialReset();
|
|
}
|
|
|
|
ACE_CSA_HELPER::~ACE_CSA_HELPER()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void ACE_CSA_HELPER::InitialReset()
|
|
{
|
|
ValueType = Reserved = 0;
|
|
ValueCount = Flags = 0;
|
|
ppObjects = NULL;
|
|
}
|
|
|
|
void ACE_CSA_HELPER::Clear()
|
|
{
|
|
// Reset the attribute name
|
|
Name.Clear();
|
|
|
|
// Free the values
|
|
if(ppObjects != NULL)
|
|
delete[] ppObjects;
|
|
InitialReset();
|
|
}
|
|
|
|
NTSTATUS ACE_CSA_HELPER::SetValueName(LPCWSTR szName)
|
|
{
|
|
return (Name.Import(szName) != NULL) ? ERROR_SUCCESS : GetLastError();
|
|
}
|
|
|
|
NTSTATUS ACE_CSA_HELPER::SetValueType(WORD wValueType, DWORD dwValueCount)
|
|
{
|
|
ACE_CSA_OBJECT * ppSaveObjects = NULL;
|
|
NTSTATUS Status;
|
|
DWORD dwSaveValueCount = 0;
|
|
BYTE CopyBuffer[MAX_ACL_LENGTH];
|
|
|
|
// If we are changing the value type, we need to free the current values
|
|
if(wValueType != ValueType)
|
|
{
|
|
// Free the current objects
|
|
if(ppObjects != NULL)
|
|
delete[] ppObjects;
|
|
ppSaveObjects = ppObjects = NULL;
|
|
}
|
|
else if(dwValueCount != ValueCount)
|
|
{
|
|
dwSaveValueCount = ValueCount;
|
|
ppSaveObjects = ppObjects;
|
|
ppObjects = NULL;
|
|
}
|
|
else
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Setup the value type and value count
|
|
ValueCount = dwValueCount;
|
|
ValueType = wValueType;
|
|
|
|
// Allocate elements and supply default values
|
|
if((Status = AllocateElements()) == STATUS_SUCCESS)
|
|
{
|
|
LPBYTE pbPtr = CopyBuffer;
|
|
LPBYTE pbEnd = pbPtr + sizeof(CopyBuffer);
|
|
LPBYTE pbResult;
|
|
|
|
for(DWORD i = 0; i < ValueCount; i++)
|
|
{
|
|
// Make sure that the copy buffer is zeroed
|
|
memset(CopyBuffer, 0, sizeof(CopyBuffer));
|
|
|
|
// Export the existing object or create default value
|
|
if(ppSaveObjects && i < dwSaveValueCount)
|
|
{
|
|
pbResult = ppSaveObjects[i].Export(pbPtr, pbEnd);
|
|
}
|
|
else
|
|
{
|
|
pbResult = ppObjects[i].Default(pbPtr, pbEnd, i);
|
|
}
|
|
|
|
// Do we have some value?
|
|
if(pbResult != NULL)
|
|
{
|
|
ppObjects[i].Import(pbPtr);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ValueCount = dwSaveValueCount;
|
|
ppObjects = ppSaveObjects;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS ACE_CSA_HELPER::SetValueData(LPCVOID lpObject, ULONG Index)
|
|
{
|
|
// Objects must be already allocated
|
|
assert(ppObjects != NULL);
|
|
assert(ValueCount != 0);
|
|
|
|
// Check for overflow
|
|
if(Index >= ValueCount)
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
|
|
// Import the object
|
|
return (ppObjects[Index].Import(lpObject) != NULL) ? STATUS_SUCCESS : GetLastError();
|
|
}
|
|
|
|
NTSTATUS ACE_CSA_HELPER::AllocateElements()
|
|
{
|
|
// Sanity checks
|
|
assert(ppObjects == NULL);
|
|
assert(ValueCount != 0);
|
|
assert(ValueType != 0);
|
|
|
|
// Allocate the elements based on element type
|
|
switch(ValueType)
|
|
{
|
|
case CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
|
|
case CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
|
|
ppObjects = new ACE_CSA_DWORD64[ValueCount];
|
|
break;
|
|
|
|
case CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
|
|
ppObjects = new ACE_CSA_LPWSTR[ValueCount];
|
|
break;
|
|
|
|
case CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
|
|
ppObjects = new ACE_CSA_SID[ValueCount];
|
|
break;
|
|
|
|
case CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
|
|
ppObjects = new ACE_CSA_BOOLEAN[ValueCount];
|
|
break;
|
|
|
|
case CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
|
|
ppObjects = new ACE_CSA_OCTET_STRING[ValueCount];
|
|
break;
|
|
|
|
default:
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// If the allocation failed, return error
|
|
return (ppObjects != NULL) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
|
|
}
|
|
|
|
NTSTATUS ACE_CSA_HELPER::Import(LPBYTE pbAttrRel, LPBYTE pbAttrEnd, PULONG pcbMoveBy)
|
|
{
|
|
NTSTATUS Status = STATUS_BAD_DATA;
|
|
ULONG cbBase = FIELD_OFFSET(CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, Values);
|
|
ULONG cbMoveBy = 0;
|
|
|
|
// Free the current values
|
|
Clear();
|
|
|
|
// Enough data to cover CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1?
|
|
if((pbAttrRel + cbBase) <= pbAttrEnd)
|
|
{
|
|
PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 pAttrRel = (PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1)(pbAttrRel);
|
|
LPBYTE pbEndObject;
|
|
|
|
// Import the base values
|
|
ValueType = pAttrRel->ValueType;
|
|
Reserved = pAttrRel->Reserved;
|
|
Flags = pAttrRel->Flags;
|
|
ValueCount = pAttrRel->ValueCount;
|
|
cbMoveBy = cbBase;
|
|
|
|
// Import the name
|
|
if((pbEndObject = Name.Import(pbAttrRel + pAttrRel->Name)) == NULL)
|
|
return GetLastError();
|
|
cbMoveBy = max(cbMoveBy, (ULONG)(pbEndObject - pbAttrRel));
|
|
|
|
// Enough to cover the value offsets too?
|
|
if((pbAttrRel + (ValueCount * sizeof(DWORD))) <= pbAttrEnd)
|
|
{
|
|
// Make sure that we have the elements
|
|
if((Status = AllocateElements()) == STATUS_SUCCESS)
|
|
{
|
|
for(ULONG i = 0; i < ValueCount; i++)
|
|
{
|
|
// Import the n-th value. TODO: Verify size of the data!!!
|
|
pbEndObject = ppObjects[i].Import(pbAttrRel + pAttrRel->Values.pUint64[i]);
|
|
if(pbEndObject == NULL)
|
|
return GetLastError();
|
|
|
|
// Update the biggest offset
|
|
cbMoveBy = max(cbMoveBy, (ULONG)(pbEndObject - pbAttrRel));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Give the result to the caller. Always try to eat up to 8-byte boundary
|
|
if(pcbMoveBy != NULL)
|
|
pcbMoveBy[0] = GetSizeAlignedToMax(pbAttrRel, pbAttrEnd, cbMoveBy);
|
|
return Status;
|
|
}
|
|
|
|
// In case NtSetSecurityObject returns STATUS_INVALID_ACL, look here:
|
|
// nt!RtlpValidRelativeAttribute(PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 pAttrRel, size_t cbAttrRel)
|
|
PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 ACE_CSA_HELPER::Export(PULONG pcbLength) const
|
|
{
|
|
PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 pAttrRel = NULL;
|
|
ULONG cbLength = 0;
|
|
|
|
// Allocate buffer. Don't bother with calculating the length.
|
|
// The maximum size of an ACE is 0xFFF8 bytes, which we can afford to allocate
|
|
pAttrRel = (PCLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1)LocalAlloc(LPTR, MAX_ACL_LENGTH);
|
|
if(pAttrRel != NULL)
|
|
{
|
|
LPBYTE pbStructure = (LPBYTE)(pAttrRel);
|
|
LPBYTE pbPtr = pbStructure + FIELD_OFFSET(CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1, Values) + ValueCount * sizeof(ULONG);
|
|
LPBYTE pbEnd = pbStructure + MAX_ACL_LENGTH;
|
|
|
|
// Copy the base structure
|
|
pAttrRel->ValueType = ValueType;
|
|
pAttrRel->Reserved = Reserved;
|
|
pAttrRel->Flags = Flags;
|
|
pAttrRel->ValueCount = ValueCount;
|
|
|
|
// Export the attribute name
|
|
if(Name.Export(pbPtr, pbEnd) > pbPtr)
|
|
{
|
|
pAttrRel->Name = (ULONG)(pbPtr - pbStructure);
|
|
pbPtr = pbPtr + Name.ExportSize(sizeof(DWORD));
|
|
}
|
|
|
|
// Copy values
|
|
for(ULONG i = 0; i < ValueCount; i++)
|
|
{
|
|
// Write the value offset
|
|
pAttrRel->Values.pUint64[i] = (ULONG)(pbPtr - pbStructure);
|
|
|
|
// Write the value itself
|
|
pbPtr = ppObjects[i].Export(pbPtr, pbEnd);
|
|
assert(pbPtr != NULL);
|
|
}
|
|
|
|
// Update the length
|
|
cbLength = (ULONG)(pbPtr - pbStructure);
|
|
}
|
|
|
|
// Give the result to the caller
|
|
if(pcbLength != NULL)
|
|
pcbLength[0] = cbLength;
|
|
return pAttrRel;
|
|
}
|
|
|
|
ULONG ACE_CSA_HELPER::GetSizeAlignedToMax(LPBYTE pbPtr, LPBYTE pbEnd, ULONG cbLength)
|
|
{
|
|
ULONG cbLengthAligned;
|
|
|
|
// Can we eat up size aligned to 8 bytes?
|
|
cbLengthAligned = ALIGN_TO_SIZE(cbLength, 8);
|
|
if((pbPtr + cbLengthAligned) <= pbEnd)
|
|
return cbLengthAligned;
|
|
|
|
// Can we eat up size aligned to 4 bytes?
|
|
cbLengthAligned = ALIGN_TO_SIZE(cbLength, 4);
|
|
if((pbPtr + cbLengthAligned) <= pbEnd)
|
|
return cbLengthAligned;
|
|
|
|
// Can we eat up size aligned to 2 bytes?
|
|
cbLengthAligned = ALIGN_TO_SIZE(cbLength, 2);
|
|
if((pbPtr + cbLengthAligned) <= pbEnd)
|
|
return cbLengthAligned;
|
|
|
|
// Return the length as-is
|
|
assert(false);
|
|
return cbLength;
|
|
}
|