Windows: Harden Windows driver input validation

Validate SecRegion password cache offsets before use.
Wipe decrypted SecRegion password-cache data even when cache validation fails.
Clamp encrypted I/O work item counts and check allocation sizing.
Reject invalid boot drive sector writes and initialize decoy wipe data unit.
Validate hidden-system boot offsets and remap arithmetic before use.
This commit is contained in:
Mounir IDRASSI
2026-04-26 18:42:26 +09:00
parent d841ac63e4
commit a9b1d5ce57
3 changed files with 146 additions and 27 deletions

View File

@@ -74,6 +74,82 @@ static NTSTATUS DecoySystemWipeResult;
static uint64 BootArgsRegionsDefault[] = { EFI_BOOTARGS_REGIONS_DEFAULT };
static uint64 BootArgsRegionsEFI[] = { EFI_BOOTARGS_REGIONS_EFI };
static BOOL GetHiddenSystemPartitionOffset (uint64 *hiddenPartitionOffset)
{
uint64 hiddenOffset = BootArgs.HiddenSystemPartitionStart;
if (hiddenOffset == 0
|| hiddenOffset > (uint64) _I64_MAX
|| (hiddenOffset % TC_SECTOR_SIZE_BIOS) != 0)
return FALSE;
*hiddenPartitionOffset = hiddenOffset;
return TRUE;
}
static BOOL GetHiddenSystemPartitionOffsets (uint64 *hiddenPartitionOffset, uint64 *decoyPartitionOffset)
{
uint64 hiddenOffset;
uint64 decoyOffset = BootArgs.DecoySystemPartitionStart;
if (!GetHiddenSystemPartitionOffset (&hiddenOffset)
|| decoyOffset > (uint64) _I64_MAX
|| (decoyOffset % TC_SECTOR_SIZE_BIOS) != 0
|| decoyOffset >= hiddenOffset)
return FALSE;
*hiddenPartitionOffset = hiddenOffset;
*decoyPartitionOffset = decoyOffset;
return TRUE;
}
static BOOL GetHiddenVolumeHeaderOffset (LARGE_INTEGER *offset)
{
uint64 hiddenPartitionOffset;
uint64 hiddenHeaderOffset;
if (!GetHiddenSystemPartitionOffset (&hiddenPartitionOffset)
|| hiddenPartitionOffset > ((uint64) _I64_MAX - TC_HIDDEN_VOLUME_HEADER_OFFSET))
return FALSE;
hiddenHeaderOffset = hiddenPartitionOffset + TC_HIDDEN_VOLUME_HEADER_OFFSET;
if (hiddenHeaderOffset > ((uint64) _I64_MAX - TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE))
return FALSE;
offset->QuadPart = (LONGLONG) hiddenHeaderOffset;
return TRUE;
}
static BOOL GetHiddenSystemRemapOffsets (PCRYPTO_INFO cryptoInfo, int64 *hiddenVolumeStartOffset, int64 *remappedAreaOffset, int64 *remappedAreaDataUnitOffset)
{
uint64 hiddenOffset;
uint64 decoyOffset;
uint64 encryptedAreaStart = cryptoInfo->EncryptedAreaStart.Value;
uint64 encryptedAreaLength = cryptoInfo->EncryptedAreaLength.Value;
uint64 hiddenToDecoyDistance;
uint64 hiddenVolumeStart;
if (!GetHiddenSystemPartitionOffsets (&hiddenOffset, &decoyOffset)
|| encryptedAreaStart > (uint64) _I64_MAX
|| encryptedAreaLength > (uint64) _I64_MAX
|| cryptoInfo->VolumeSize.Value > (uint64) _I64_MAX
|| (encryptedAreaStart % ENCRYPTION_DATA_UNIT_SIZE) != 0)
return FALSE;
hiddenToDecoyDistance = hiddenOffset - decoyOffset;
if (cryptoInfo->VolumeSize.Value > hiddenToDecoyDistance
|| encryptedAreaLength > hiddenToDecoyDistance
|| encryptedAreaStart > ((uint64) _I64_MAX - hiddenOffset))
return FALSE;
hiddenVolumeStart = hiddenOffset + encryptedAreaStart;
*hiddenVolumeStartOffset = (int64) hiddenVolumeStart;
*remappedAreaOffset = (int64) (hiddenVolumeStart - decoyOffset);
*remappedAreaDataUnitOffset = (int64) (encryptedAreaStart / ENCRYPTION_DATA_UNIT_SIZE) - (int64) (decoyOffset / ENCRYPTION_DATA_UNIT_SIZE);
return TRUE;
}
NTSTATUS LoadBootArguments (BOOL bIsEfi)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
@@ -410,7 +486,6 @@ static void ComputeBootLoaderFingerprint(PDEVICE_OBJECT LowerDeviceObject, uint8
static NTSTATUS MountDrive (DriveFilterExtension *Extension, Password *password, __unaligned uint32 *headerSaltCrc32)
{
BOOL hiddenVolume = (BootArgs.HiddenSystemPartitionStart != 0);
int64 hiddenHeaderOffset = BootArgs.HiddenSystemPartitionStart + TC_HIDDEN_VOLUME_HEADER_OFFSET;
NTSTATUS status;
LARGE_INTEGER offset;
unsigned char *header;
@@ -462,7 +537,17 @@ static NTSTATUS MountDrive (DriveFilterExtension *Extension, Password *password,
if (!header)
return STATUS_INSUFFICIENT_RESOURCES;
offset.QuadPart = hiddenVolume ? hiddenHeaderOffset : TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET;
if (hiddenVolume)
{
if (!GetHiddenVolumeHeaderOffset (&offset))
{
status = STATUS_INVALID_PARAMETER;
goto ret;
}
}
else
offset.QuadPart = TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET;
Dump ("Reading volume header at %I64u\n", offset.QuadPart);
status = TCReadDevice (Extension->LowerDeviceObject, header, offset, TC_BOOT_ENCRYPTION_VOLUME_HEADER_SIZE);
@@ -527,22 +612,25 @@ static NTSTATUS MountDrive (DriveFilterExtension *Extension, Password *password,
if (Extension->Queue.CryptoInfo->hiddenVolume)
{
int64 hiddenPartitionOffset = BootArgs.HiddenSystemPartitionStart;
Dump ("Hidden volume start offset = %I64d\n", Extension->Queue.CryptoInfo->EncryptedAreaStart.Value + hiddenPartitionOffset);
int64 hiddenVolumeStartOffset;
int64 remappedAreaOffset;
int64 remappedAreaDataUnitOffset;
if (!GetHiddenSystemRemapOffsets (Extension->Queue.CryptoInfo, &hiddenVolumeStartOffset, &remappedAreaOffset, &remappedAreaDataUnitOffset))
{
// We have already erased boot loader scheduled keys.
TC_THROW_FATAL_EXCEPTION;
}
Dump ("Hidden volume start offset = %I64d\n", hiddenVolumeStartOffset);
Extension->HiddenSystem = TRUE;
Extension->Queue.RemapEncryptedArea = TRUE;
Extension->Queue.RemappedAreaOffset = hiddenPartitionOffset + Extension->Queue.CryptoInfo->EncryptedAreaStart.Value - BootArgs.DecoySystemPartitionStart;
Extension->Queue.RemappedAreaDataUnitOffset = Extension->Queue.CryptoInfo->EncryptedAreaStart.Value / ENCRYPTION_DATA_UNIT_SIZE - BootArgs.DecoySystemPartitionStart / ENCRYPTION_DATA_UNIT_SIZE;
Extension->Queue.RemappedAreaOffset = remappedAreaOffset;
Extension->Queue.RemappedAreaDataUnitOffset = remappedAreaDataUnitOffset;
Extension->Queue.CryptoInfo->EncryptedAreaStart.Value = BootArgs.DecoySystemPartitionStart;
if (Extension->Queue.CryptoInfo->VolumeSize.Value > hiddenPartitionOffset - BootArgs.DecoySystemPartitionStart)
{
// we have already erased boot loader scheduled keys
TC_THROW_FATAL_EXCEPTION;
}
Dump ("RemappedAreaOffset = %I64d\n", Extension->Queue.RemappedAreaOffset);
Dump ("RemappedAreaDataUnitOffset = %I64d\n", Extension->Queue.RemappedAreaDataUnitOffset);
@@ -585,17 +673,24 @@ static NTSTATUS MountDrive (DriveFilterExtension *Extension, Password *password,
if(crc == crcSaved){
if(DeList->DE[DE_IDX_PWDCACHE].Type == DE_PwdCache) {
uint64 sector = 0;
DCS_DEP_PWD_CACHE* pwdCache = (DCS_DEP_PWD_CACHE*)(BootSecRegionData + DeList->DE[DE_IDX_PWDCACHE].Sectors.Offset);
DecryptDataUnits((unsigned char*)pwdCache, (UINT64_STRUCT*)&sector, 1, Extension->Queue.CryptoInfo);
crcSaved = pwdCache->CRC;
pwdCache->CRC = 0;
crc = GetCrc32((unsigned char*)pwdCache, 512);
if(crcSaved == crc && pwdCache->Count < CACHE_SIZE){
uint32 i;
for(i = 0; i<pwdCache->Count; ++i){
if (CacheBootPassword && pwdCache->Pwd[i].Length > 0) {
int cachedPim = CacheBootPim? (int) (pwdCache->Pim[i]) : 0;
AddLegacyPasswordToCache (&pwdCache->Pwd[i], cachedPim);
uint32 pwdCacheOffset = DeList->DE[DE_IDX_PWDCACHE].Offset;
if ((DeList->DE[DE_IDX_PWDCACHE].Length >= sizeof(DCS_DEP_PWD_CACHE))
&& ((pwdCacheOffset % TC_SECTOR_SIZE_BIOS) == 0)
&& (pwdCacheOffset <= BootSecRegionSize)
&& ((BootSecRegionSize - pwdCacheOffset) >= sizeof(DCS_DEP_PWD_CACHE)))
{
DCS_DEP_PWD_CACHE* pwdCache = (DCS_DEP_PWD_CACHE*)(BootSecRegionData + pwdCacheOffset);
DecryptDataUnits((unsigned char*)pwdCache, (UINT64_STRUCT*)&sector, 1, Extension->Queue.CryptoInfo);
crcSaved = pwdCache->CRC;
pwdCache->CRC = 0;
crc = GetCrc32((unsigned char*)pwdCache, 512);
if(crcSaved == crc && pwdCache->Count < CACHE_SIZE){
uint32 i;
for(i = 0; i<pwdCache->Count; ++i){
if (CacheBootPassword && pwdCache->Pwd[i].Length > 0) {
int cachedPim = CacheBootPim? (int) (pwdCache->Pim[i]) : 0;
AddLegacyPasswordToCache (&pwdCache->Pwd[i], cachedPim);
}
}
}
burn(pwdCache, sizeof(*pwdCache));
@@ -1155,7 +1250,13 @@ void ReopenBootVolumeHeader (PIRP irp)
}
if (BootDriveFilterExtension->HiddenSystem)
offset.QuadPart = BootArgs.HiddenSystemPartitionStart + TC_HIDDEN_VOLUME_HEADER_OFFSET;
{
if (!GetHiddenVolumeHeaderOffset (&offset))
{
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
goto ret;
}
}
else
offset.QuadPart = TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET;
@@ -2378,5 +2479,11 @@ NTSTATUS WriteBootDriveSector (PIRP irp, PIO_STACK_LOCATION irpSp)
return STATUS_INVALID_PARAMETER;
request = (WriteBootDriveSectorRequest *) irp->AssociatedIrp.SystemBuffer;
if (request->Offset.QuadPart < 0
|| (request->Offset.QuadPart % TC_SECTOR_SIZE_BIOS) != 0
|| BootDriveLength.QuadPart < (LONGLONG) sizeof (request->Data)
|| request->Offset.QuadPart > BootDriveLength.QuadPart - (LONGLONG) sizeof (request->Data))
return STATUS_INVALID_PARAMETER;
return TCWriteDevice (BootDriveFilterExtension->LowerDeviceObject, request->Data, request->Offset, sizeof (request->Data));
}

View File

@@ -1250,10 +1250,15 @@ NTSTATUS EncryptedIoQueueStart (EncryptedIoQueue *queue)
NTSTATUS status;
EncryptedIoQueueBuffer *buffer;
int i, j, preallocatedIoRequestCount, preallocatedItemCount, fragmentSize;
int maxWorkItems;
SIZE_T workItemPoolSize;
preallocatedIoRequestCount = EncryptionIoRequestCount;
preallocatedItemCount = EncryptionItemCount;
fragmentSize = EncryptionFragmentSize;
maxWorkItems = EncryptionMaxWorkItems;
if (maxWorkItems <= 0 || maxWorkItems > VC_MAX_WORK_ITEMS)
maxWorkItems = VC_MAX_WORK_ITEMS;
queue->StartPending = TRUE;
queue->ThreadExitRequested = FALSE;
@@ -1355,11 +1360,16 @@ retry_preallocated:
// Initialize the free work item list
InitializeListHead(&queue->FreeWorkItemsList);
KeInitializeSemaphore(&queue->WorkItemSemaphore, EncryptionMaxWorkItems, EncryptionMaxWorkItems);
KeInitializeSemaphore(&queue->WorkItemSemaphore, maxWorkItems, maxWorkItems);
KeInitializeSpinLock(&queue->WorkItemLock);
queue->MaxWorkItems = EncryptionMaxWorkItems;
queue->WorkItemPool = (PCOMPLETE_IRP_WORK_ITEM)TCalloc(sizeof(COMPLETE_IRP_WORK_ITEM) * queue->MaxWorkItems);
queue->MaxWorkItems = maxWorkItems;
if (FAILED(SizeTMult(sizeof(COMPLETE_IRP_WORK_ITEM), queue->MaxWorkItems, &workItemPoolSize)))
{
goto noMemory;
}
queue->WorkItemPool = (PCOMPLETE_IRP_WORK_ITEM)TCalloc(workItemPoolSize);
if (!queue->WorkItemPool)
{
goto noMemory;

View File

@@ -4704,6 +4704,8 @@ NTSTATUS ReadRegistryConfigFlags (BOOL driverEntry)
if (EncryptionMaxWorkItems == 0)
EncryptionMaxWorkItems = VC_MAX_WORK_ITEMS;
else if (EncryptionMaxWorkItems < 0 || EncryptionMaxWorkItems > VC_MAX_WORK_ITEMS)
EncryptionMaxWorkItems = VC_MAX_WORK_ITEMS;
}