mirror of
https://github.com/trustedsec/COFFLoader
synced 2026-06-08 17:55:49 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
*.exe
|
||||
*.out
|
||||
+475
@@ -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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#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
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
#ifndef COFFLOADER_H_
|
||||
#define COFFLOADER_H_
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* 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
|
||||
+25
@@ -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).
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
@@ -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("<L", self.size) + self.buffer
|
||||
|
||||
def addshort(self, short):
|
||||
self.buffer += pack("<h", short)
|
||||
self.size += 2
|
||||
|
||||
def addint(self, dint):
|
||||
self.buffer += pack("<i", dint)
|
||||
self.size += 4
|
||||
|
||||
def addstr(self, s):
|
||||
if isinstance(s, str):
|
||||
s = s.encode("utf-8")
|
||||
fmt = "<L{}s".format(len(s) + 1)
|
||||
self.buffer += pack(fmt, len(s)+1, s)
|
||||
self.size += calcsize(fmt)
|
||||
|
||||
def addWstr(self, s):
|
||||
if isinstance(s, str):
|
||||
s = s.encode("utf-16_le")
|
||||
fmt = "<L{}s".format(len(s) + 2)
|
||||
self.buffer += pack(fmt, len(s)+2, s)
|
||||
self.size += calcsize(fmt)
|
||||
|
||||
class MainLoop(cmd.Cmd):
|
||||
def __init__(self):
|
||||
cmd.Cmd.__init__(self)
|
||||
self.BeaconPack = BeaconPack()
|
||||
self.intro = "Beacon Argument Generator"
|
||||
self.prompt = "Beacon>"
|
||||
|
||||
def do_addWString(self, text):
|
||||
'''addWString String here
|
||||
Append the wide string to the text.
|
||||
'''
|
||||
self.BeaconPack.addWstr(text)
|
||||
|
||||
def do_addString(self, text):
|
||||
'''addString string here
|
||||
Append the utf-8 string here.
|
||||
'''
|
||||
self.BeaconPack.addstr(text)
|
||||
|
||||
def do_generate(self, text):
|
||||
'''generate
|
||||
Generate the buffer for the BOF arguments
|
||||
'''
|
||||
outbuffer = self.BeaconPack.getbuffer()
|
||||
print(binascii.hexlify(outbuffer))
|
||||
|
||||
def do_addint(self, text):
|
||||
'''addint integer
|
||||
Add an int32_t to the buffer
|
||||
'''
|
||||
try:
|
||||
converted = int(text)
|
||||
self.BeaconPack.addint(converted)
|
||||
except:
|
||||
print("Failed to convert to int\n");
|
||||
|
||||
def do_addshort(self, text):
|
||||
'''addshort integer
|
||||
Add an uint16_t to the buffer
|
||||
'''
|
||||
try:
|
||||
converted = int(text)
|
||||
self.BeaconPack.addshort(converted)
|
||||
except:
|
||||
print("Failed to convert to short\n");
|
||||
|
||||
def do_reset(self, text):
|
||||
'''reset
|
||||
Reset the buffer here.
|
||||
'''
|
||||
self.BeaconPack.buffer = b''
|
||||
self.BeaconPack.size = 0
|
||||
|
||||
def do_exit(self, text):
|
||||
'''exit
|
||||
Exit the console
|
||||
'''
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
cmdloop = MainLoop()
|
||||
cmdloop.cmdloop()
|
||||
@@ -0,0 +1,44 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <lm.h>
|
||||
#include <dsgetdc.h>
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user