1
0
mirror of https://github.com/boku7/Loki synced 2026-06-06 15:24:27 +00:00
Files
boku7-Loki/dev/COFFLoader/COFFLoader.cpp
T
2025-05-05 13:16:59 -07:00

785 lines
41 KiB
C++

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <napi.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. */
#if defined(__x86_64__) || defined(_WIN64)
#define PREPENDSYMBOLVALUE "__imp_"
#else
#define PREPENDSYMBOLVALUE "__imp__"
#endif
#define COFFLOADER_RETURN_VAL_IF(expr, val, fmt, ...) if ((expr)) { DEBUG_PRINT(fmt, ##__VA_ARGS__); return val; }
unsigned char* unhexlify(unsigned char* value, int *outlen) {
unsigned char* retval = NULL;
char byteval[3] = { 0 };
unsigned 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 (strlen((char*)value) % 2 != 0) {
DEBUG_PRINT("Either value is NULL, or the hexlified string isn't valid\n");
goto errcase;
}
retval = (unsigned char*) 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 = (char)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;
size_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 = (unsigned char*) calloc(fsize, 1);
if (tempbuffer == NULL) {
fclose(fin);
return NULL;
}
memset(tempbuffer, 0, fsize);
readsize = fread(tempbuffer, 1, fsize, fin);
fclose(fin);
buffer = (unsigned char*) calloc(readsize, 1);
if (buffer == NULL) {
free(tempbuffer);
return NULL;
}
memset(buffer, 0, readsize);
memcpy(buffer, tempbuffer, readsize - 1);
free(tempbuffer);
*outsize = fsize;
return buffer;
}
static BOOL starts_with(const char* string, const char* substring) {
return strncmp(string, substring, strlen(substring)) == 0;
}
/* 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
strncpy(localcopy, symbolstring, sizeof(localcopy) - 1);
if (starts_with(symbolstring, PREPENDSYMBOLVALUE"Beacon") || starts_with(symbolstring, PREPENDSYMBOLVALUE"toWideChar") ||
starts_with(symbolstring, PREPENDSYMBOLVALUE"GetProcAddress") || starts_with(symbolstring, PREPENDSYMBOLVALUE"LoadLibraryA") ||
starts_with(symbolstring, PREPENDSYMBOLVALUE"GetModuleHandleA") || starts_with(symbolstring, PREPENDSYMBOLVALUE"FreeLibrary") ||
starts_with(symbolstring, "__C_specific_handler")) {
if(strcmp(symbolstring, "__C_specific_handler") == 0)
{
localfunc = symbolstring;
return InternalFunctions[29][1];
}
else
{
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 < 30; tempcounter++) {
if (InternalFunctions[tempcounter][0] != NULL) {
if (starts_with(localfunc, (char*)(InternalFunctions[tempcounter][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 = LoadLibraryA(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) {
//printf("argumentdata: %p\n", argumentdata);
//printf("argumentSize: %d\n", argumentSize);
coff_sect_t *coff_sect_ptr = NULL;
coff_reloc_t *coff_reloc_ptr = NULL;
int retcode = 0;
int counter = 0;
int reloccount = 0;
unsigned int tempcounter = 0;
uint32_t symptr = 0;
COFFLOADER_RETURN_VAL_IF(functionname == NULL, 1, "Function name is NULL\n");
COFFLOADER_RETURN_VAL_IF(coff_data == NULL, 1, "Can't execute NULL\n");
COFFLOADER_RETURN_VAL_IF(filesize == 0, 1, "COFF file size is 0\n");
COFFLOADER_RETURN_VAL_IF(filesize < sizeof(struct coff_file_header), 1,
"COFF file size too small for a COFF file header\n");
struct coff_file_header *coff_header_ptr = (struct coff_file_header*)coff_data;
COFFLOADER_RETURN_VAL_IF(coff_header_ptr->PointerToSymbolTable < sizeof(struct coff_file_header),
1, "COFF symbol table offset is inside the file header\n");
COFFLOADER_RETURN_VAL_IF(filesize < coff_header_ptr->PointerToSymbolTable, 1,
"COFF symbol table offset exceeds file size\n");
// Byte index of the strtab/end of symtab
size_t coff_strtab_index =
coff_header_ptr->PointerToSymbolTable + coff_header_ptr->NumberOfSymbols * sizeof(struct coff_sym);
COFFLOADER_RETURN_VAL_IF(filesize < coff_strtab_index, 1, "COFF symbol table exceeds COFF file size\n");
COFFLOADER_RETURN_VAL_IF(filesize < coff_strtab_index + sizeof(uint32_t), 1,
"COFF string table offset exceeds COFF file size\n");
uint32_t coff_strtab_size = *(uint32_t*)(coff_data + coff_strtab_index);
COFFLOADER_RETURN_VAL_IF(filesize < coff_strtab_index + coff_strtab_size, 1,
"COFF string table exceeds COFF file size\n");
COFFLOADER_RETURN_VAL_IF(filesize != coff_strtab_index + coff_strtab_size, 1,
"COFF file contains extraneous data\n");
struct coff_sym *coff_sym_ptr = (struct coff_sym*)(coff_data + coff_header_ptr->PointerToSymbolTable);
#ifdef _WIN32
void* funcptrlocation = NULL;
size_t offsetvalue = 0;
#endif
char* entryfuncname = functionname;
#if defined(__x86_64__) || defined(_WIN64)
#ifdef _WIN32
uint64_t longoffsetvalue = 0;
#endif
#else
/* Set the input function name to match the 32 bit version */
entryfuncname = (char*) calloc(strlen(functionname) + 2, 1);
if (entryfuncname == NULL) {
return 1;
}
(void)sprintf((char*)entryfuncname, "_%s", functionname);
#endif
HMODULE kern = GetModuleHandleA("kernel32.dll");
InternalFunctions[29][1] = (unsigned char *) GetProcAddress(kern, "__C_specific_handler");
DEBUG_PRINT("found address of %x\n", InternalFunctions[29][1]);
#ifdef _WIN32
/* NOTE: I just picked a size, look to see what is max/normal. */
char** sectionMapping = NULL;
#ifdef DEBUG
int *sectionSize = NULL;
#endif
void(*foo)(char* in, unsigned long datalen);
char* functionMapping = NULL;
int functionMappingCount = 0;
int relocationCount = 0;
#endif
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: %u\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");
/* Actually allocate an array to keep track of the sections */
sectionMapping = (char**)calloc(sizeof(char*)*(coff_header_ptr->NumberOfSections+1), 1);
#ifdef DEBUG
sectionSize = (int*)calloc(sizeof(int)*(coff_header_ptr->NumberOfSections+1), 1);
#endif
if (sectionMapping == NULL){
DEBUG_PRINT("Failed to allocate sectionMapping\n");
goto cleanup;
}
/* 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);
relocationCount += 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] = (char*) 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]);
if (coff_sect_ptr->PointerToRawData != 0){
memcpy(sectionMapping[counter], coff_data + coff_sect_ptr->PointerToRawData, coff_sect_ptr->SizeOfRawData);
}
else{
memset(sectionMapping[counter], 0, coff_sect_ptr->SizeOfRawData);
}
#endif
}
DEBUG_PRINT("Total Relocations: %d\n", relocationCount);
/* Allocate and setup the GOT for functions, same here as above. */
/* Actually allocate enough for worst case every relocation, may not be needed, but hey better safe than sorry */
#ifdef _WIN32
#ifdef _WIN64
functionMapping = (char*) VirtualAlloc(NULL, relocationCount*8, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);
#else
functionMapping = (char*) VirtualAlloc(NULL, relocationCount*8, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);
#endif
if (functionMapping == NULL){
DEBUG_PRINT("Failed to allocate functionMapping\n");
goto cleanup;
}
#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);
longoffsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
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));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
DEBUG_PRINT("\tSetting 0x%p to OffsetValue: 0x%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));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
DEBUG_PRINT("\t\tSetting 0x%p to relative address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, offsetvalue);
memcpy(sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t));
}
else if (coff_reloc_ptr->Type == IMAGE_REL_AMD64_REL32_1) {
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));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
offsetvalue += 1;
DEBUG_PRINT("\t\tSetting 0x%p to relative address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, offsetvalue);
memcpy(sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t));
}
else if (coff_reloc_ptr->Type == IMAGE_REL_AMD64_REL32_2) {
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));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
offsetvalue += 2;
DEBUG_PRINT("\t\tSetting 0x%p to relative address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, offsetvalue);
memcpy(sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t));
}
else if (coff_reloc_ptr->Type == IMAGE_REL_AMD64_REL32_3) {
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));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
offsetvalue += 3;
DEBUG_PRINT("\t\tSetting 0x%p to relative address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, offsetvalue);
memcpy(sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t));
}
else if (coff_reloc_ptr->Type == IMAGE_REL_AMD64_REL32_4) {
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));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
offsetvalue += 4;
DEBUG_PRINT("\t\tSetting 0x%p to relative address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, offsetvalue);
memcpy(sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t));
}
else if (coff_reloc_ptr->Type == IMAGE_REL_AMD64_REL32_5) {
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));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
offsetvalue += 5;
DEBUG_PRINT("\t\tSetting 0x%p to relative address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, 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 */
if (coff_reloc_ptr->Type == IMAGE_REL_I386_DIR32){
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;
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
DEBUG_PRINT("\tSetting 0x%p to: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, offsetvalue);
memcpy(sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t));
}
else if (coff_reloc_ptr->Type == IMAGE_REL_I386_REL32){
memcpy(&offsetvalue, sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, sizeof(int32_t));
DEBUG_PRINT("\tReadin OffsetValue : 0x%0X\n", offsetvalue);
offsetvalue += (sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber - 1] - (sectionMapping[counter] + coff_reloc_ptr->VirtualAddress + 4));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
DEBUG_PRINT("\tSetting 0x%p to relative address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, 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 && coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber == 0) {
DEBUG_PRINT("Failed to resolve symbol\n");
retcode = 1;
goto cleanup;
}
#ifdef _WIN64
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);
longoffsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
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));
}
else 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));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
DEBUG_PRINT("\t\tSetting 0x%p to relative address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, 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\tReferenced Section: 0x%X\n", sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber - 1] + offsetvalue);
DEBUG_PRINT("\t\tReadin offset value: 0x%X\n", offsetvalue);
DEBUG_PRINT("\t\tVirtualAddressOffset: 0x%X\n", (sectionMapping[counter] + coff_reloc_ptr->VirtualAddress + 4));
offsetvalue += (sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber - 1] - (sectionMapping[counter] + coff_reloc_ptr->VirtualAddress + 4));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
DEBUG_PRINT("\t\tSetting 0x%p to relative address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, offsetvalue);
memcpy(sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t));
}
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));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
DEBUG_PRINT("\tSetting 0x%p to OffsetValue: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, 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
if (coff_reloc_ptr->Type == IMAGE_REL_I386_DIR32 && funcptrlocation != NULL){
/* This is Type == IMAGE_REL_I386_DIR32 relocation code */
memcpy(functionMapping + (functionMappingCount * 4), &funcptrlocation, sizeof(uint32_t));
offsetvalue = (int32_t)(functionMapping + (functionMappingCount * 4));
DEBUG_PRINT("\tSetting 0x%p to virtual address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, offsetvalue);
memcpy(sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t));
functionMappingCount++;
}
else if (coff_reloc_ptr->Type == IMAGE_REL_I386_DIR32) {
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;
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
DEBUG_PRINT("\tSetting 0x%p to virtual address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, offsetvalue);
memcpy(sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, &offsetvalue, sizeof(uint32_t));
}
else if (coff_reloc_ptr->Type == IMAGE_REL_I386_REL32){
memcpy(&offsetvalue, sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, sizeof(int32_t));
DEBUG_PRINT("\tReadin OffsetValue : 0x%0X\n", offsetvalue);
offsetvalue += (sectionMapping[coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].SectionNumber - 1] - (sectionMapping[counter] + coff_reloc_ptr->VirtualAddress + 4));
offsetvalue += coff_sym_ptr[coff_reloc_ptr->SymbolTableIndex].Value;
DEBUG_PRINT("\tSetting 0x%p to relative address: 0x%X\n", sectionMapping[counter] + coff_reloc_ptr->VirtualAddress, 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);
}
#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 < coff_header_ptr->NumberOfSections; tempcounter++) {
DEBUG_PRINT("Section: %u\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
/* So for some reason VS 2017 doesn't like this, but char* casting works, so just going to do that */
#ifdef _MSC_VER
foo = (void(__cdecl*)(char*, unsigned long))(sectionMapping[coff_sym_ptr[tempcounter].SectionNumber - 1] + coff_sym_ptr[tempcounter].Value);
#else
foo = (void(*)(char *, unsigned long))(sectionMapping[coff_sym_ptr[tempcounter].SectionNumber - 1] + coff_sym_ptr[tempcounter].Value);
#endif
//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 :
if (sectionMapping){
for (tempcounter = 0; tempcounter < coff_header_ptr->NumberOfSections; tempcounter++) {
if (sectionMapping[tempcounter]) {
VirtualFree(sectionMapping[tempcounter], 0, MEM_RELEASE);
}
}
free(sectionMapping);
sectionMapping = NULL;
}
#ifdef DEBUG
if (sectionSize){
free(sectionSize);
sectionSize = NULL;
}
#endif
if (functionMapping){
VirtualFree(functionMapping, 0, MEM_RELEASE);
}
#endif
if (entryfuncname && entryfuncname != functionname){
free(entryfuncname);
}
DEBUG_PRINT("Returning\n");
return retcode;
}
// Node-API wrapper for RunCOFF
Napi::Value RunCOFFWrapper(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// Check arguments
if (info.Length() < 4) {
Napi::TypeError::New(env, "Wrong number of arguments. Expected: functionName, coffData, argumentData, argumentSize")
.ThrowAsJavaScriptException();
return env.Null();
}
// Validate argument types
if (!info[0].IsString()) {
Napi::TypeError::New(env, "First argument must be a string (functionName)")
.ThrowAsJavaScriptException();
return env.Null();
}
if (!info[1].IsBuffer()) {
Napi::TypeError::New(env, "Second argument must be a Buffer (coffData)")
.ThrowAsJavaScriptException();
return env.Null();
}
if (!info[2].IsBuffer()) {
Napi::TypeError::New(env, "Third argument must be a Buffer (argumentData)")
.ThrowAsJavaScriptException();
return env.Null();
}
if (!info[3].IsNumber()) {
Napi::TypeError::New(env, "Fourth argument must be a number (argumentSize)")
.ThrowAsJavaScriptException();
return env.Null();
}
// Get arguments
std::string functionName = info[0].As<Napi::String>().Utf8Value();
Napi::Buffer<unsigned char> coffBuffer = info[1].As<Napi::Buffer<unsigned char>>();
Napi::Buffer<unsigned char> argBuffer = info[2].As<Napi::Buffer<unsigned char>>();
int argSize = info[3].As<Napi::Number>().Int32Value();
printf("Calling RunCOFF with functionName: %s\n", functionName.c_str());
printf("coffBuffer length: %zu\n", coffBuffer.Length());
printf("argBuffer length: %zu\n", argBuffer.Length());
// Call the native function
int result = RunCOFF(
const_cast<char*>(functionName.c_str()),
coffBuffer.Data(),
static_cast<uint32_t>(coffBuffer.Length()),
argBuffer.Data(),
argSize
);
//printf("RunCOFF returned: %d\n", result);
// Create a result object to return both the status and output
Napi::Object resultObj = Napi::Object::New(env);
resultObj.Set("status", Napi::Number::New(env, result));
// Get the output data if available
int outsize = 0;
char* outdata = BeaconGetOutputData(&outsize);
//printf("BeaconGetOutputData returned outsize: %d\n", outsize);
if (outdata != NULL && outsize > 0) {
//printf("Output data: %.*s\n", outsize, outdata);
// Create a Buffer from the output data
Napi::Buffer<char> outputBuffer = Napi::Buffer<char>::Copy(env, outdata, outsize);
resultObj.Set("output", outputBuffer);
} else {
//printf("No output data available\n");
resultObj.Set("output", env.Null());
}
return resultObj;
}
// Initialize module
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("runCOFF", Napi::Function::New(env, RunCOFFWrapper));
return exports;
}
NODE_API_MODULE(COFFLoader, Init)
#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