commit 626956ecf5ae26bd2b0301eccf37d37054f19d6d Author: Kevin Haubris Date: Fri Feb 19 13:29:38 2021 -0600 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9cc18da --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.exe +*.out diff --git a/COFFLoader.c b/COFFLoader.c new file mode 100644 index 0000000..21c036f --- /dev/null +++ b/COFFLoader.c @@ -0,0 +1,475 @@ +/* + * COFF Loader Project + * ------------------- + * This is a re-implementation of a COFF loader, with a BOF compatibility layer + * it's meant to provide functional example of loading a COFF file in memory + * and maybe be useful. + */ +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#include "beacon_compatibility.h" +#endif + +#include "COFFLoader.h" + +/* Enable or disable debug output if testing or adding new relocation types */ +#ifdef DEBUG +#define DEBUG_PRINT(x, ...) printf(x, ##__VA_ARGS__) +#else +#define DEBUG_PRINT(x, ...) +#endif + +/* Defining symbols for the OS version, will try to define anything that is + * different between the arch versions by specifying them here. */ +#ifdef __x86_64__ +#define PREPENDSYMBOLVALUE "__imp_" +#else +#define PREPENDSYMBOLVALUE "__imp__" +#endif + +unsigned char* unhexlify(unsigned char* value, int *outlen){ + unsigned char* retval = NULL; + char byteval[3] = {0}; + int counter = 0; + int counter2 = 0; + char character = 0; + if (value == NULL){ + return NULL; + } + DEBUG_PRINT("Unhexlify Strlen: %lu\n", (long unsigned int)strlen((char*)value)); + if (value == NULL || strlen((char*)value)%2 != 0){ + DEBUG_PRINT("Either value is NULL, or the hexlified string isn't valid\n"); + goto errcase; + } + + retval = calloc(strlen((char*)value)+1, 1); + if (retval == NULL){ + goto errcase; + } + + counter2 = 0; + for (counter = 0; counter < strlen((char*)value); counter += 2){ + memcpy(byteval, value+counter, 2); + character = strtol(byteval, NULL, 16); + memcpy(retval+counter2, &character, 1); + counter2++; + } + *outlen = counter2; + +errcase: + return retval; +} + + + +/* Helper to just get the contents of a file, used for testing. Real + * implementations of this in an agent would use the tasking from the + * C2 server for this */ +unsigned char* getContents(char* filepath, uint32_t* outsize){ + FILE *fin = NULL; + uint32_t fsize = 0; + uint32_t readsize = 0; + unsigned char* buffer = NULL; + unsigned char* tempbuffer = NULL; + + fin = fopen(filepath, "rb"); + if (fin == NULL){ + return NULL; + } + fseek(fin, 0, SEEK_END); + fsize = ftell(fin); + fseek(fin, 0, SEEK_SET); + tempbuffer = calloc(fsize, 1); + if (tempbuffer == NULL){ + return NULL; + } + memset(tempbuffer, 0, fsize); + readsize = fread(tempbuffer, 1, fsize, fin); + + fclose(fin); + buffer = calloc(readsize, 1); + if (buffer == NULL){ + return NULL; + } + memset(buffer, 0, readsize); + memcpy(buffer, tempbuffer, readsize-1); + free(tempbuffer); + *outsize = fsize; + return buffer; +} + +/* Helper function to process a symbol string, determine what function and + * library its from, and return the right function pointer. Will need to + * implement in the loading of the beacon internal functions, or any other + * internal functions you want to have available. */ +void* process_symbol(char* symbolstring){ + void* functionaddress = NULL; + char localcopy[1024] = {0}; + char* locallib = NULL; + char* localfunc = NULL; + #if defined(_WIN32) + int tempcounter = 0; + HMODULE llHandle = NULL; + #endif + + memcpy(localcopy, symbolstring, strlen(symbolstring)); + if (strncmp(symbolstring, PREPENDSYMBOLVALUE"Beacon", strlen(PREPENDSYMBOLVALUE"Beacon")) == 0 || strncmp(symbolstring, PREPENDSYMBOLVALUE"toWideChar", strlen(PREPENDSYMBOLVALUE"toWideChar")) == 0 ){ + localfunc = symbolstring+strlen(PREPENDSYMBOLVALUE); + DEBUG_PRINT("\t\tInternalFunction: %s\n", localfunc); + /* TODO: Get internal symbol here and set to functionaddress, then + * return the pointer to the internal function*/ +#if defined(_WIN32) + for (tempcounter = 0; tempcounter < 25; tempcounter++){ + if (InternalFunctions[tempcounter][0] != NULL){ + if (strcmp(localfunc, (char*)(InternalFunctions[tempcounter][0])) == 0){ + functionaddress = (void*)InternalFunctions[tempcounter][1]; + return functionaddress; + } + } + } +#endif + } + else if (strncmp(symbolstring, PREPENDSYMBOLVALUE, strlen(PREPENDSYMBOLVALUE)) == 0){ + DEBUG_PRINT("\t\tYep its an external symbol\n"); + locallib = localcopy+strlen(PREPENDSYMBOLVALUE); + + locallib = strtok(locallib, "$"); + localfunc = strtok(NULL, "$"); + DEBUG_PRINT("\t\tLibrary: %s\n", locallib); + localfunc = strtok(localfunc, "@"); + DEBUG_PRINT("\t\tFunction: %s\n", localfunc); + /* Resolve the symbols here, and set the functionpointervalue */ +#if defined(_WIN32) + llHandle = LoadLibrary(locallib); + DEBUG_PRINT("\t\tHandle: 0x%lx\n", llHandle); + functionaddress = GetProcAddress(llHandle, localfunc); + DEBUG_PRINT("\t\tProcAddress: 0x%p\n", functionaddress); +#endif + } + return functionaddress; +} + +/* Just a generic runner for testing, this is pretty much just a reference + * implementation, return values will need to be checked, more relocation + * types need to be handled, and needs to have different arguments for use + * in any agent. */ +int RunCOFF(char* functionname, unsigned char* coff_data, uint32_t filesize, unsigned char* argumentdata, int argumentSize){ + coff_file_header_t *coff_header_ptr = NULL; + coff_sect_t *coff_sect_ptr = NULL; + coff_reloc_t *coff_reloc_ptr = NULL; + coff_sym_t * coff_sym_ptr = NULL; + int retcode = 0; + int counter = 0; + int reloccount = 0; + int tempcounter = 0; + uint32_t symptr = 0; +#ifdef WIN32 + void* funcptrlocation = NULL; + int32_t offsetvalue = 0; +#endif + char* entryfuncname = functionname; +#ifdef __x86_64__ +#ifdef WIN32 + uint64_t longoffsetvalue = 0; +#endif +#else + /* Set the input function name to match the 32 bit version */ + entryfuncname = calloc(strlen(functionname)+2, 1); + if (entryfuncname == NULL){ + return 1; + } + (void)sprintf(entryfuncname, "_%s", functionname); +#endif + +#ifdef WIN32 + /* NOTE: I just picked a size, look to see what is max/normal. */ + char* sectionMapping[25] = {0}; +#ifdef DEBUG + int sectionSize[25] = {0}; +#endif + void (*foo)(char* in, unsigned long datalen); + char* functionMapping = NULL; + int functionMappingCount = 0; +#endif + + if (coff_data == NULL){ + DEBUG_PRINT("Can't execute NULL\n"); + return 1; + } + coff_header_ptr = (coff_file_header_t*)coff_data; + DEBUG_PRINT("Machine 0x%X\n", coff_header_ptr->Machine); + DEBUG_PRINT("Number of sections: %d\n", coff_header_ptr->NumberOfSections); + DEBUG_PRINT("TimeDateStamp : %X\n", coff_header_ptr->TimeDateStamp); + DEBUG_PRINT("PointerToSymbolTable : 0x%X\n", coff_header_ptr->PointerToSymbolTable); + DEBUG_PRINT("NumberOfSymbols: %d\n", coff_header_ptr->NumberOfSymbols); + DEBUG_PRINT("OptionalHeaderSize: %d\n", coff_header_ptr->SizeOfOptionalHeader); + DEBUG_PRINT("Characteristics: %d\n", coff_header_ptr->Characteristics); + DEBUG_PRINT("\n"); + coff_sym_ptr = (coff_sym_t*)(coff_data + coff_header_ptr->PointerToSymbolTable); + + /* Handle the allocation and copying of the sections we're going to use + * for right now I'm just VirtualAlloc'ing memory, this can be changed to + * other methods, but leaving that up to the person implementing it. */ + for (counter = 0; counter < coff_header_ptr->NumberOfSections; counter++){ + coff_sect_ptr = (coff_sect_t*) (coff_data+sizeof(coff_file_header_t) + (sizeof(coff_sect_t) * counter)); + DEBUG_PRINT("Name: %s\n", coff_sect_ptr->Name); + DEBUG_PRINT("VirtualSize: 0x%X\n", coff_sect_ptr->VirtualSize); + DEBUG_PRINT("VirtualAddress: 0x%X\n", coff_sect_ptr->VirtualAddress); + DEBUG_PRINT("SizeOfRawData: 0x%X\n", coff_sect_ptr->SizeOfRawData); + DEBUG_PRINT("PointerToRelocations: 0x%X\n", coff_sect_ptr->PointerToRelocations); + DEBUG_PRINT("PointerToRawData: 0x%X\n", coff_sect_ptr->PointerToRawData); + DEBUG_PRINT("NumberOfRelocations: %d\n", coff_sect_ptr->NumberOfRelocations); + /* NOTE: When changing the memory loading information of the loader, + * you'll want to use this field and the defines from the Section + * Flags table of Microsofts page, some defined in COFFLoader.h */ + DEBUG_PRINT("Characteristics: %x\n", coff_sect_ptr->Characteristics); +#ifdef WIN32 + DEBUG_PRINT("Allocating 0x%x bytes\n", coff_sect_ptr->VirtualSize); + /* NOTE: Might want to allocate as PAGE_READWRITE and VirtualProtect + * before execution to either PAGE_READWRITE or PAGE_EXECUTE_READ + * depending on the Section Characteristics. Parse them all again + * before running and set the memory permissions. */ + sectionMapping[counter] = VirtualAlloc(NULL, coff_sect_ptr->SizeOfRawData, MEM_COMMIT|MEM_RESERVE|MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); +#ifdef DEBUG + sectionSize[counter] = coff_sect_ptr->SizeOfRawData; +#endif + if (sectionMapping[counter] == NULL){ + DEBUG_PRINT("Failed to allocate memory\n"); + } + DEBUG_PRINT("Allocated section %d at %p\n", counter, sectionMapping[counter]); + memcpy(sectionMapping[counter], coff_data + coff_sect_ptr->PointerToRawData, coff_sect_ptr->SizeOfRawData); +#endif + } + + /* Allocate and setup the GOT for functions, same here as above. */ +#ifdef WIN32 + #ifdef WIN64 + functionMapping = VirtualAlloc(NULL, 2048, MEM_COMMIT|MEM_RESERVE|MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); + #else + functionMapping = VirtualAlloc(NULL, 2048, MEM_COMMIT|MEM_RESERVE|MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); + #endif +#endif + + /* Start parsing the relocations, and *hopefully* handle them correctly. */ + for (counter = 0; counter < coff_header_ptr->NumberOfSections; counter++){ + DEBUG_PRINT("Doing Relocations of section: %d\n", counter); + coff_sect_ptr = (coff_sect_t*) (coff_data+sizeof(coff_file_header_t) + (sizeof(coff_sect_t) * counter)); + coff_reloc_ptr = (coff_reloc_t*)(coff_data + coff_sect_ptr->PointerToRelocations); + for (reloccount = 0; reloccount < coff_sect_ptr->NumberOfRelocations; reloccount++){ + DEBUG_PRINT("\tVirtualAddress: 0x%X\n", coff_reloc_ptr->VirtualAddress); + DEBUG_PRINT("\tSymbolTableIndex: 0x%X\n", coff_reloc_ptr->SymbolTableIndex); + DEBUG_PRINT("\tType: 0x%X\n", coff_reloc_ptr->Type); + if (coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].first.Name[0] != 0){ + symptr = coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].first.value[1]; + DEBUG_PRINT("\tSymPtr: 0x%X\n", symptr); + DEBUG_PRINT("\tSymName: %s\n", coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].first.Name); + DEBUG_PRINT("\tSectionNumber: 0x%X\n", coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber); + + /* This is the code for relative offsets in other sections of the COFF file. */ +#ifdef WIN32 + #ifdef WIN64 + /* Type == 1 relocation is the 64-bit VA of the relocation target */ + if (coff_reloc_ptr->Type == IMAGE_REL_AMD64_ADDR64){ + memcpy(&longoffsetvalue, sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, sizeof(uint64_t)); + DEBUG_PRINT("\tReadin longOffsetValue : 0x%llX\n", longoffsetvalue); + longoffsetvalue = (uint64_t)(sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber-1]+(uint64_t)longoffsetvalue); + DEBUG_PRINT("\tModified longOffsetValue : 0x%llX Base Address: %p\n", longoffsetvalue, sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber-1]); + memcpy(sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, &longoffsetvalue, sizeof(uint64_t)); + } + /* This is Type == 3 relocation code */ + else if (coff_reloc_ptr->Type == IMAGE_REL_AMD64_ADDR32NB){ + memcpy(&offsetvalue, sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, sizeof(int32_t)); + DEBUG_PRINT("\tReadin OffsetValue : 0x%0X\n", offsetvalue); + DEBUG_PRINT("\t\tReferenced Section: 0x%X\n", sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber-1]+offsetvalue); + DEBUG_PRINT("\t\tEnd of Relocation Bytes: 0x%X\n", sectionMapping[counter]+coff_reloc_ptr->VirtualAddress+4); + if (((char*)(sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber-1]+offsetvalue) - (char*)(sectionMapping[counter]+coff_reloc_ptr->VirtualAddress+4)) > 0xffffffff){ + DEBUG_PRINT("Relocations > 4 gigs away, exiting\n"); + retcode = 1; + goto cleanup; + } + offsetvalue = ((char*)(sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber-1]+offsetvalue) - (char*)(sectionMapping[counter]+coff_reloc_ptr->VirtualAddress+4)); + DEBUG_PRINT("\tOffsetValue : 0x%0X\n", offsetvalue); + DEBUG_PRINT("\t\tSetting 0x%X to %X\n", sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, &offsetvalue); + memcpy(sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t)); + } + /* This is Type == 4 relocation code, needed to make global variables to work correctly */ + else if (coff_reloc_ptr->Type == IMAGE_REL_AMD64_REL32){ + memcpy(&offsetvalue, sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, sizeof(int32_t)); + DEBUG_PRINT("\t\tReadin offset value: 0x%X\n", offsetvalue); + if ((sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber-1] - (sectionMapping[counter]+coff_reloc_ptr->VirtualAddress+4)) > 0xffffffff){ + DEBUG_PRINT("Relocations > 4 gigs away, exiting\n"); + retcode = 1; + goto cleanup; + } + offsetvalue += (sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber-1] - (sectionMapping[counter]+coff_reloc_ptr->VirtualAddress+4)); + DEBUG_PRINT("\t\tRelative address: 0x%X\n", offsetvalue); + memcpy(sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t)); + } + else { + DEBUG_PRINT("No code for relocation type: %d\n", coff_reloc_ptr->Type); + } + #else + /* This is Type == IMAGE_REL_I386_DIR32 relocation code */ + memcpy(&offsetvalue, sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, sizeof(int32_t)); + DEBUG_PRINT("\tReadin OffsetValue : 0x%0X\n", offsetvalue); + offsetvalue =(uint32_t)(sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber-1])+offsetvalue; + memcpy(sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t)); + #endif //WIN64 statement close +#endif //WIN32 statement close + } + else{ + symptr = coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].first.value[1]; + DEBUG_PRINT("\tSymPtr: 0x%X\n", symptr); + DEBUG_PRINT("\tSymVal: %s\n", ((char*)(coff_sym_ptr+coff_header_ptr->NumberOfSymbols))+symptr); + DEBUG_PRINT("\tSectionNumber: 0x%X\n", coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber); + + /* This is the code to handle functions themselves, so using a makeshift Global Offset Table for it */ +#ifdef WIN32 + funcptrlocation = process_symbol(((char*)(coff_sym_ptr+coff_header_ptr->NumberOfSymbols))+symptr); + if (funcptrlocation == NULL){ + DEBUG_PRINT("Failed to resolve symbol\n"); + retcode = 1; + goto cleanup; + } + #ifdef WIN64 + if (coff_reloc_ptr->Type == IMAGE_REL_AMD64_REL32 && funcptrlocation != NULL){ + /* This is Type == 4 relocation code */ + DEBUG_PRINT("Doing function relocation\n"); + if (((functionMapping+(functionMappingCount*8)) - (sectionMapping[counter]+coff_reloc_ptr->VirtualAddress+4)) > 0xffffffff){ + DEBUG_PRINT("Relocations > 4 gigs away, exiting\n"); + retcode = 1; + goto cleanup; + } + memcpy(functionMapping+(functionMappingCount*8), &funcptrlocation, sizeof(uint64_t)); + offsetvalue = (int32_t)((functionMapping+(functionMappingCount*8)) - (sectionMapping[counter]+coff_reloc_ptr->VirtualAddress+4)); + DEBUG_PRINT("\t\tRelative address : 0x%x\n", offsetvalue); + memcpy(sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t)); + functionMappingCount++; + } + else if (coff_reloc_ptr->Type == IMAGE_REL_AMD64_REL32){ + /* This shouldn't be needed here, but incase there's a defined symbol + * that somehow doesn't have a function, try to resolve it here.*/ + memcpy(&offsetvalue, sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, sizeof(int32_t)); + if ((sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber-1] - (sectionMapping[counter]+coff_reloc_ptr->VirtualAddress+4)) > 0xffffffff){ + DEBUG_PRINT("Relocations > 4 gigs away, exiting\n"); + retcode = 1; + goto cleanup; + } + DEBUG_PRINT("\t\tReadin offset value: 0x%X\n", offsetvalue); + offsetvalue += (sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber-1] - (sectionMapping[counter]+coff_reloc_ptr->VirtualAddress+4)); + DEBUG_PRINT("\t\tRelative address: 0x%X\n", offsetvalue); + memcpy(sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t)); + } + else{ + DEBUG_PRINT("No code for relocation type: %d\n", coff_reloc_ptr->Type); + } + #else + /* This is Type == IMAGE_REL_I386_DIR32 relocation code */ + memcpy(functionMapping+(functionMappingCount*4), &funcptrlocation, sizeof(uint32_t)); + offsetvalue = (int32_t)(functionMapping + (functionMappingCount*4)); + memcpy(sectionMapping[counter]+coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t)); + functionMappingCount++; + #endif +#endif + } + DEBUG_PRINT("\tValueNumber: 0x%X\n", coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value); + DEBUG_PRINT("\tSectionNumber: 0x%X\n", coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber); + coff_reloc_ptr = (coff_reloc_t*)(((char*)coff_reloc_ptr)+sizeof(coff_reloc_t)); + DEBUG_PRINT("\n"); + } + DEBUG_PRINT("\n"); + } + +/* Some debugging code to see what the sections look like in memory */ +#if DEBUG +#ifdef WIN32 + for (tempcounter = 0; tempcounter < 10; tempcounter++){ + DEBUG_PRINT("Section: %d\n", tempcounter); + if (sectionMapping[tempcounter] != NULL){ + DEBUG_PRINT("\t"); + for (counter=0; counter < sectionSize[tempcounter]; counter++){ + DEBUG_PRINT("%02X ", (uint8_t)(sectionMapping[tempcounter][counter])); + } + DEBUG_PRINT("\n"); + } + } +#endif +#endif + + DEBUG_PRINT("Symbols:\n"); + for (tempcounter = 0; tempcounter < coff_header_ptr->NumberOfSymbols; tempcounter++){ + DEBUG_PRINT("\t%s: Section: %d, Value: 0x%X\n", coff_sym_ptr[tempcounter].first.Name, coff_sym_ptr[tempcounter].SectionNumber, coff_sym_ptr[tempcounter].Value); + if (strcmp(coff_sym_ptr[tempcounter].first.Name, entryfuncname) == 0){ + DEBUG_PRINT("\t\tFound entry!\n"); +#ifdef WIN32 + foo = (void (*)(char *, unsigned long))sectionMapping[coff_sym_ptr[tempcounter].SectionNumber-1]+coff_sym_ptr[tempcounter].Value; + //sectionMapping[coff_sym_ptr[tempcounter].SectionNumber-1][coff_sym_ptr[tempcounter].Value+7] = '\xcc'; + DEBUG_PRINT("Trying to run: %p\n", foo); + foo((char*)argumentdata, argumentSize); +#endif + } + } + DEBUG_PRINT("Back\n"); + + /* Cleanup the allocated memory */ +#ifdef WIN32 +cleanup: + for (tempcounter=0; tempcounter < 25; tempcounter++){ + if (sectionMapping[tempcounter]){ + VirtualFree(sectionMapping[tempcounter], 0, MEM_RELEASE); + } + } + VirtualFree(functionMapping, 0, MEM_RELEASE); +#endif + DEBUG_PRINT("Returning\n"); + return retcode; +} + +#ifdef COFF_STANDALONE +int main(int argc, char* argv[]){ + char* coff_data = NULL; + unsigned char* arguments = NULL; + int argumentSize = 0; +#ifdef WIN32 + char* outdata = NULL; + int outdataSize = 0; +#endif + uint32_t filesize = 0; + int checkcode = 0; + if (argc < 3){ + printf("ERROR: %s go /path/to/object/file.o (arguments)\n", argv[0]); + return 1; + } + + coff_data = (char*)getContents(argv[2], &filesize); + if (coff_data == NULL){ + return 1; + } + printf("Got contents of COFF file\n"); + arguments = unhexlify((unsigned char*)argv[3], &argumentSize); + printf("Running/Parsing the COFF file\n"); + checkcode = RunCOFF(argv[1], (unsigned char*)coff_data, filesize, arguments, argumentSize); + if (checkcode == 0){ +#ifdef WIN32 + printf("Ran/parsed the coff\n"); + outdata = BeaconGetOutputData(&outdataSize); + if (outdata != NULL){ + printf("Outdata Below:\n\n%s\n", outdata); + } +#endif + } + else{ + printf("Failed to run/parse the COFF file\n"); + } + if (coff_data){ + free(coff_data); + } + return 0; +} + +#endif diff --git a/COFFLoader.h b/COFFLoader.h new file mode 100644 index 0000000..c1b912e --- /dev/null +++ b/COFFLoader.h @@ -0,0 +1,107 @@ +#ifndef COFFLOADER_H_ +#define COFFLOADER_H_ +#include +#include + +/* These seem to be the same sizes across architectures, relocations are different though. Defined both sets of types. */ + +/* sizeof 20 */ +typedef struct coff_file_header{ + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; +} coff_file_header_t; + +/* AMD64 should always be here */ +#define MACHINETYPE_AMD64 0x8664 + + +/* Size of 40 */ +typedef struct coff_sect{ + char Name[8]; + uint32_t VirtualSize; + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLineNumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; +}__attribute__((packed)) coff_sect_t; + + +typedef struct coff_reloc{ + uint32_t VirtualAddress; + uint32_t SymbolTableIndex; + uint16_t Type; +} __attribute__((packed)) coff_reloc_t; + +typedef struct coff_sym{ + union{ + char Name[8]; + uint32_t value[2]; + } first; + uint32_t Value; + uint16_t SectionNumber; + uint16_t Type; + uint8_t StorageClass; + uint8_t NumberOfAuxSymbols; + +} __attribute__((packed)) coff_sym_t; + +/* AMD64 Specific types */ +#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 +#define IMAGE_REL_AMD64_ADDR64 0x0001 +#define IMAGE_REL_AMD64_ADDR32 0x0002 +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 +/* Most common from the looks of it, just 32-bit relative address from the byte following the relocation */ +#define IMAGE_REL_AMD64_REL32 0x0004 +/* Second most common, 32-bit address without an image base. Not sure what that means... */ +#define IMAGE_REL_AMD64_REL32_1 0x0005 +#define IMAGE_REL_AMD64_REL32_2 0x0006 +#define IMAGE_REL_AMD64_REL32_3 0x0007 +#define IMAGE_REL_AMD64_REL32_4 0x0008 +#define IMAGE_REL_AMD64_REL32_5 0x0009 +#define IMAGE_REL_AMD64_SECTION 0x000A +#define IMAGE_REL_AMD64_SECREL 0x000B +#define IMAGE_REL_AMD64_SECREL7 0x000C +#define IMAGE_REL_AMD64_TOKEN 0x000D +#define IMAGE_REL_AMD64_SREL32 0x000E +#define IMAGE_REL_AMD64_PAIR 0x000F +#define IMAGE_REL_AMD64_SSPAN32 0x0010 + +/*i386 Relocation types */ + +#define IMAGE_REL_I386_ABSOLUTE 0x0000 +#define IMAGE_REL_I386_DIR16 0x0001 +#define IMAGE_REL_I386_REL16 0x0002 +#define IMAGE_REL_I386_DIR32 0x0006 +#define IMAGE_REL_I386_DIR32NB 0x0007 +#define IMAGE_REL_I386_SEG12 0x0009 +#define IMAGE_REL_I386_SECTION 0x000A +#define IMAGE_REL_I386_SECREL 0x000B +#define IMAGE_REL_I386_TOKEN 0x000C +#define IMAGE_REL_I386_SECREL7 0x000D +#define IMAGE_REL_I386_REL32 0x0014 + +/* Section Characteristic Flags */ + +#define IMAGE_SCN_MEM_WRITE 0x80000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 +#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 +#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 +#define IMAGE_SCN_MEM_SHARED 0x10000000 +#define IMAGE_SCN_CNT_CODE 0x00000020 +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 + +int RunCOFF(char* functionname, unsigned char* coff_data, uint32_t filesize, unsigned char* argumentdata, int argumentSize); +unsigned char* unhexlify(unsigned char* value, int *outlen); +#endif diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..be683b3 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,25 @@ +Copyright 2020, COFFLoader by TrustedSec, LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the distribution. + * Neither the name of TrustedSec, LLC nor the names of its contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The above licensing was taken from the BSD licensing and is applied to COFFLoader as well. + +Note that the COFFLoader is provided as is, and is a royalty free open-source application. + +Feel free to modify, use, change, market, do whatever you want with it as long as you give the appropriate credit where credit +is due (which means giving the authors the credit they deserve for writing it). + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5991f04 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ + +all: bof bof32 +debug: debug32 debug64 + +bof: + x86_64-w64-mingw32-gcc -Wall -DCOFF_STANDALONE beacon_compatibility.c COFFLoader.c -o COFFLoader64.exe + x86_64-w64-mingw32-gcc -c test.c -o test64.out + +bof32: + i686-w64-mingw32-gcc -Wall -DCOFF_STANDALONE beacon_compatibility.c COFFLoader.c -o COFFLoader32.exe + i686-w64-mingw32-gcc -c test.c -o test32.out + +debug64: + x86_64-w64-mingw32-gcc -DCOFF_STANDALONE -DDEBUG beacon_compatibility.c COFFLoader.c -o COFFLoader64.exe + x86_64-w64-mingw32-gcc -c test.c -o test64.out + +debug32: + i686-w64-mingw32-gcc -DCOFF_STANDALONE -DDEBUG beacon_compatibility.c COFFLoader.c -o COFFLoader32.exe + i686-w64-mingw32-gcc -c test.c -o test32.out + +nix: + gcc -DCOFF_STANDALONE -Wall -DDEBUG beacon_compatibility.c COFFLoader.c -o COFFLoader.out + +clean: + rm -f COFFLoader64.exe COFFLoader32.exe COFFLoader.out + rm -f test32.out test64.out diff --git a/README.md b/README.md new file mode 100644 index 0000000..d00a04d --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# COFF Loader + +This is a quick and dirty COFF loader (AKA Beacon Object Files), will eventually get it able to run a default BOF without changes, so that it can be used for testing without a CS agent running it. + +The main goal is to provide a working example and maybe be useful to someone. + + +## Parts +There are a few parts to it they are listed below. + +- beacon_compatibility: This will be the beacon internal functions so that you can load BOF files and run them inside your own agent, currently not used. +- COFFLoader: This is the actual coff loader, and when built for nix just loads the 64 bit object file and parses it. +- test: This is the example "COFF" file, will build to the COFF file for you when make is called. + diff --git a/beacon.h b/beacon.h new file mode 100644 index 0000000..3613159 --- /dev/null +++ b/beacon.h @@ -0,0 +1,61 @@ +/* + * Beacon Object Files (BOF) + * ------------------------- + * A Beacon Object File is a light-weight post exploitation tool that runs + * with Beacon's inline-execute command. + * + * Cobalt Strike 4.1. + */ + +/* data API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} datap; + +DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); +DECLSPEC_IMPORT int BeaconDataInt(datap * parser); +DECLSPEC_IMPORT short BeaconDataShort(datap * parser); +DECLSPEC_IMPORT int BeaconDataLength(datap * parser); +DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); + +/* format API */ +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} formatp; + +DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); +DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); +DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); +DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, char * text, int len); +DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, char * fmt, ...); +DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); +DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); + +/* Output Functions */ +#define CALLBACK_OUTPUT 0x0 +#define CALLBACK_OUTPUT_OEM 0x1e +#define CALLBACK_ERROR 0x0d +#define CALLBACK_OUTPUT_UTF8 0x20 + +DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...); +DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len); + +/* Token Functions */ +DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); +DECLSPEC_IMPORT void BeaconRevertToken(); +DECLSPEC_IMPORT BOOL BeaconIsAdmin(); + +/* Spawn+Inject Functions */ +DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); +DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); +DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); + +/* Utility Functions */ +DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); diff --git a/beacon_compatibility.c b/beacon_compatibility.c new file mode 100644 index 0000000..5f53669 --- /dev/null +++ b/beacon_compatibility.c @@ -0,0 +1,347 @@ +/* + * Cobalt Strike 4.X BOF compatibility layer + * ----------------------------------------- + * The whole point of these files are to allow beacon object files built for CS + * to run fine inside of other tools without recompiling. + * + * Built off of the beacon.h file provided to build for CS. + */ +#include +#include +#include +#include +#ifdef WIN32 +#include + +#include "beacon_compatibility.h" + +#define DEFAULTPROCESSNAME "rundll32.exe" +#ifdef WIN64 +#define X86PATH "SysWOW64" +#define X64PATH "System32" +#else +#define X86PATH "System32" +#define X64PATH "sysnative" +#endif + + +/* Data Parsing */ +unsigned char* InternalFunctions[25][2] = { + {(unsigned char*)"BeaconDataParse", (unsigned char*)BeaconDataParse}, + {(unsigned char*)"BeaconDataInt", (unsigned char*)BeaconDataInt}, + {(unsigned char*)"BeaconDataShort", (unsigned char*)BeaconDataShort}, + {(unsigned char*)"BeaconDataLength", (unsigned char*)BeaconDataLength}, + {(unsigned char*)"BeaconDataExtract", (unsigned char*)BeaconDataExtract}, + {(unsigned char*)"BeaconFormatAlloc", (unsigned char*)BeaconFormatAlloc}, + {(unsigned char*)"BeaconFormatReset", (unsigned char*)BeaconFormatReset}, + {(unsigned char*)"BeaconFormatFree", (unsigned char*)BeaconFormatFree}, + {(unsigned char*)"BeaconFormatAppend", (unsigned char*)BeaconFormatAppend}, + {(unsigned char*)"BeaconFormatPrintf", (unsigned char*)BeaconFormatPrintf}, + {(unsigned char*)"BeaconFormatToString", (unsigned char*)BeaconFormatToString}, + {(unsigned char*)"BeaconFormatInt", (unsigned char*)BeaconFormatInt}, + {(unsigned char*)"BeaconPrintf", (unsigned char*)BeaconPrintf}, + {(unsigned char*)"BeaconOutput", (unsigned char*)BeaconOutput}, + {(unsigned char*)"BeaconUseToken", (unsigned char*)BeaconUseToken}, + {(unsigned char*)"BeaconRevertToken", (unsigned char*)BeaconRevertToken}, + {(unsigned char*)"BeaconIsAdmin", (unsigned char*)BeaconIsAdmin}, + {(unsigned char*)"BeaconGetSpawnTo", (unsigned char*)BeaconGetSpawnTo}, + {(unsigned char*)"BeaconSpawnTemporaryProcess", (unsigned char*)BeaconSpawnTemporaryProcess}, + {(unsigned char*)"BeaconInjectProcess", (unsigned char*)BeaconInjectProcess}, + {(unsigned char*)"BeaconInjectTemporaryProcess", (unsigned char*)BeaconInjectTemporaryProcess}, + {(unsigned char*)"BeaconCleanupProcess", (unsigned char*)BeaconCleanupProcess}, + {(unsigned char*)"toWideChar", (unsigned char*)toWideChar} +}; + +uint32_t swap_endianess(uint32_t indata){ + uint32_t testint = 0xaabbccdd; + uint32_t outint = indata; + if (((unsigned char*)&testint)[0] == 0xdd){ + ((unsigned char*)&outint)[0] = ((unsigned char*)&indata)[3]; + ((unsigned char*)&outint)[1] = ((unsigned char*)&indata)[2]; + ((unsigned char*)&outint)[2] = ((unsigned char*)&indata)[1]; + ((unsigned char*)&outint)[3] = ((unsigned char*)&indata)[0]; + } + return outint; +} + +char* beacon_compatibility_output = NULL; +int beacon_compatibility_size = 0; +int beacon_compatibility_offset = 0; + +void BeaconDataParse(datap* parser, char* buffer, int size){ + if (parser == NULL){ + return; + } + parser->original = buffer; + parser->buffer = buffer; + parser->length = size-4; + parser->size = size-4; + parser->buffer += 4; + return; +} + +int BeaconDataInt(datap* parser){ + int32_t fourbyteint = 0; + if (parser->length < 4){ + return 0; + } + memcpy(&fourbyteint, parser->buffer, 4); + parser->buffer += 4; + parser->length -= 4; + return (int)fourbyteint; +} + +short BeaconDataShort(datap* parser){ + int16_t retvalue = 0; + if (parser->length < 2){ + return 0; + } + memcpy(&retvalue, parser->buffer, 2); + parser->buffer += 2; + parser->length -= 2; + return (short)retvalue; +} + +int BeaconDataLength(datap* parser){ + return parser->length; +} + +char* BeaconDataExtract(datap* parser, int* size){ + uint32_t length = 0; + char* outdata = NULL; + /*Length prefixed binary blob, going to assume uint32_t for this.*/ + if (parser->length < 4){ + return NULL; + } + memcpy(&length, parser->buffer, 4); + parser->buffer += 4; + + outdata = parser->buffer; + if (outdata == NULL){ + return NULL; + } + parser->length -=4; + parser->length -= length; + parser->buffer += length; + if (size != NULL && outdata != NULL){ + *size = length; + } + return outdata; +} + +/* format API */ + +void BeaconFormatAlloc(formatp* format, int maxsz){ + if (format == NULL){ + return; + } + format->original = calloc(maxsz, 1); + format->buffer = format->original; + format->length = 0; + format->size = maxsz; + return; +} + +void BeaconFormatReset(formatp* format){ + memset(format->original, 0, format->size); + format->buffer = format->original; + format->length = format->size; + return; +} + +void BeaconFormatFree(formatp* format){ + if (format == NULL){ + return; + } + if (format->original){ + free(format->original); + format->original = NULL; + } + format->buffer = NULL; + format->length = 0; + format->size = 0; + return; +} + +void BeaconFormatAppend(formatp* format, char* text, int len){ + memcpy(format->buffer, text, len); + format->buffer+= len; + format->length+= len; + return; +} + +void BeaconFormatPrintf(formatp* format, char* fmt, ...){ + /*Take format string, and sprintf it into here*/ + va_list args; + int length = 0; + + va_start (args, fmt); + length = vsnprintf(NULL, 0, fmt, args); + va_end(args); + if (format->length + length > format->size){ + return; + } + + va_start (args, fmt); + (void)vsnprintf(format->buffer, length, fmt, args); + va_end(args); + format->length += length; + format->buffer+= length; + return; +} + + +char* BeaconFormatToString(formatp* format, int* size){ + *size = format->length; + return format->original; +} + +void BeaconFormatInt(formatp* format, int value){ + uint32_t indata = value; + uint32_t outdata = 0; + if (format->length + 4 > format->size){ + return; + } + outdata = swap_endianess(indata); + memcpy(format->buffer, &outdata, 4); + format->length += 4; + format->buffer += 4; + return; +} + +/* Main output functions */ + +void BeaconPrintf(int type, char* fmt, ...){ + /* Change to maintain internal buffer, and return after done running. */ + int length = 0; + char* tempptr = NULL; + va_list args; + va_start (args, fmt); + vprintf(fmt, args); + va_end(args); + + va_start (args, fmt); + length = vsnprintf(NULL, 0, fmt, args); + va_end(args); + tempptr = realloc(beacon_compatibility_output, beacon_compatibility_size+length+1); + if (tempptr == NULL){ + return; + } + beacon_compatibility_output = tempptr; + memset(beacon_compatibility_output+beacon_compatibility_offset, 0, length+1); + va_start (args, fmt); + length = vsnprintf(beacon_compatibility_output+beacon_compatibility_offset, length, fmt, args); + beacon_compatibility_size+=length; + beacon_compatibility_offset+=length; + va_end(args); + return; +} + +void BeaconOutput(int type, char* data, int len){ + char* tempptr = NULL; + tempptr = realloc(beacon_compatibility_output, beacon_compatibility_size+len+1); + beacon_compatibility_output = tempptr; + if (tempptr == NULL){ + return; + } + memset(beacon_compatibility_output+beacon_compatibility_offset, 0, len+1); + memcpy(beacon_compatibility_output+beacon_compatibility_offset, data, len); + beacon_compatibility_size+=len; + beacon_compatibility_offset+=len; + return; +} + +/* Token Functions */ + +BOOL BeaconUseToken(HANDLE token){ + /* Probably needs to handle DuplicateTokenEx too */ + SetThreadToken(NULL, token); + return TRUE; +} + +void BeaconRevertToken(void){ + if (!RevertToSelf()){ + #ifdef DEBUG + printf("RevertToSelf Failed!\n"); + #endif + } + return; +} + +BOOL BeaconIsAdmin(void){ + /* Leaving this to be implemented by people needing it */ + #ifdef DEBUG + printf("BeaconIsAdmin Called\n"); + #endif + return FALSE; +} + +/* Injection/spawning related stuffs + * + * These functions are basic place holders, and if implemented into something + * real should be just calling internal functions for your tools. */ +void BeaconGetSpawnTo(BOOL x86, char* buffer, int length){ + char* tempBufferPath = NULL; + if (buffer == NULL){ + return; + } + if (x86){ + tempBufferPath = "C:\\Windows\\"X86PATH"\\"DEFAULTPROCESSNAME; + if (strlen(tempBufferPath) > length){ + return; + } + memcpy(buffer, tempBufferPath, strlen(tempBufferPath)); + } + else{ + tempBufferPath = "C:\\Windows\\"X64PATH"\\"DEFAULTPROCESSNAME; + if (strlen(tempBufferPath) > length){ + return; + } + memcpy(buffer, tempBufferPath, strlen(tempBufferPath)); + + } + return; +} + +BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * sInfo, PROCESS_INFORMATION * pInfo){ + BOOL bSuccess = FALSE; + if (x86){ + bSuccess = CreateProcessA(NULL, (char*)"C:\\Windows\\"X86PATH"\\"DEFAULTPROCESSNAME, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, sInfo, pInfo); + } + else{ + bSuccess = CreateProcessA(NULL, (char*)"C:\\Windows\\"X64PATH"\\"DEFAULTPROCESSNAME, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, sInfo, pInfo); + } + return bSuccess; +} + +void BeaconInjectProcess(HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char * arg, int a_len){ + /* Leaving this to be implemented by people needing/wanting it */ + return; +} + +void BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len){ + /* Leaving this to be implemented by people needing/wanting it */ + return; +} + +void BeaconCleanupProcess(PROCESS_INFORMATION* pInfo){ + (void)CloseHandle(pInfo->hThread); + (void)CloseHandle(pInfo->hProcess); + return; +} + +BOOL toWideChar(char* src, wchar_t* dst, int max){ + /* Leaving this to be implemented by people needing/wanting it */ + return FALSE; +} + +char* BeaconGetOutputData(int *outsize){ + char* outdata = beacon_compatibility_output; + *outsize = beacon_compatibility_size; + beacon_compatibility_output = NULL; + beacon_compatibility_size = 0; + beacon_compatibility_offset = 0; + return outdata; +} + +#endif diff --git a/beacon_compatibility.h b/beacon_compatibility.h new file mode 100644 index 0000000..b7fb6dd --- /dev/null +++ b/beacon_compatibility.h @@ -0,0 +1,66 @@ +/* + * Cobalt Strike 4.X BOF compatibility layer + * ----------------------------------------- + * The whole point of these files are to allow beacon object files built for CS + * to run fine inside of other tools without recompiling. + * + * Built off of the beacon.h file provided to build for CS. + */ +#ifndef BEACON_COMPATIBILITY_H_ +/* Structures as is in beacon.h */ +extern unsigned char* InternalFunctions[25][2]; +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} datap; + +typedef struct { + char * original; /* the original buffer [so we can free it] */ + char * buffer; /* current pointer into our buffer */ + int length; /* remaining length of data */ + int size; /* total size of this buffer */ +} formatp; + +void BeaconDataParse(datap * parser, char * buffer, int size); +int BeaconDataInt(datap * parser); +short BeaconDataShort(datap * parser); +int BeaconDataLength(datap * parser); +char * BeaconDataExtract(datap * parser, int * size); + +void BeaconFormatAlloc(formatp * format, int maxsz); +void BeaconFormatReset(formatp * format); +void BeaconFormatFree(formatp * format); +void BeaconFormatAppend(formatp * format, char * text, int len); +void BeaconFormatPrintf(formatp * format, char * fmt, ...); +char * BeaconFormatToString(formatp * format, int * size); +void BeaconFormatInt(formatp * format, int value); + +#define CALLBACK_OUTPUT 0x0 +#define CALLBACK_OUTPUT_OEM 0x1e +#define CALLBACK_ERROR 0x0d +#define CALLBACK_OUTPUT_UTF8 0x20 + + +void BeaconPrintf(int type, char * fmt, ...); +void BeaconOutput(int type, char * data, int len); + +/* Token Functions */ +BOOL BeaconUseToken(HANDLE token); +void BeaconRevertToken(); +BOOL BeaconIsAdmin(); + +/* Spawn+Inject Functions */ +void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); +BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * sInfo, PROCESS_INFORMATION * pInfo); +void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); +void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); +void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); + +/* Utility Functions */ +BOOL toWideChar(char * src, wchar_t * dst, int max); +uint32_t swap_endianess(uint32_t indata); + +char* BeaconGetOutputData(int *outsize); +#endif diff --git a/beacon_generate.py b/beacon_generate.py new file mode 100644 index 0000000..cb65893 --- /dev/null +++ b/beacon_generate.py @@ -0,0 +1,96 @@ +from struct import pack, calcsize +import binascii +import cmd + +class BeaconPack: + def __init__(self): + self.buffer = b'' + self.size = 0 + + def getbuffer(self): + return pack(" +#include +#include +#include +#include "beacon.h" + +DECLSPEC_IMPORT DWORD WINAPI NETAPI32$DsGetDcNameA(LPVOID, LPVOID, LPVOID, LPVOID, ULONG, LPVOID); +DECLSPEC_IMPORT DWORD WINAPI NETAPI32$NetApiBufferFree(LPVOID); +WINBASEAPI int __cdecl MSVCRT$printf(const char * __restrict__ _Format,...); + +char* TestGlobalString = "This is a global string"; +/* Can't do stuff like "int testvalue;" in a coff file, because it assumes that + * the symbol is like any function, so you would need to allocate a section of bss + * (without knowing the size of it), and then resolve the symbol to that. So safer + * to just not support that */ +int testvalue = 0; + +int test(void){ + MSVCRT$printf("Test String from test\n"); + testvalue = 1; + return 0; +} + +int test2(void){ + MSVCRT$printf("Test String from test2\n"); + return 0; +} + + +void go(char * args, unsigned long alen) { + DWORD dwRet; + PDOMAIN_CONTROLLER_INFO pdcInfo; + BeaconPrintf(1, "This GlobalString \"%s\"\n", TestGlobalString); + MSVCRT$printf("Test Value: %d\n", testvalue); + (void)test(); + MSVCRT$printf("Test ValueBack: %d\n", testvalue); + (void)test2(); + dwRet = NETAPI32$DsGetDcNameA(NULL, NULL, NULL, NULL, 0, &pdcInfo); + if (ERROR_SUCCESS == dwRet) { + MSVCRT$printf("%s", pdcInfo->DomainName); + } + + NETAPI32$NetApiBufferFree(pdcInfo); +}