/*
 * COFFLoader.c -- COFF Loader with Section Loading and Symbol Resolution
 * Step 05: Extends step04 by adding symbol resolution.
 *
 * What this step does:
 *   1. Reads the COFF object file into memory
 *   2. Parses headers, sections, symbols, and string table
 *   3. Allocates executable memory for each section (VirtualAlloc)
 *   4. Copies section data into allocated memory
 *   5. Resolves symbols:
 *      - Internal Beacon API functions (via InternalFunctions table)
 *      - External DLL imports (via LoadLibraryA + GetProcAddress)
 *   6. Prints resolution results for every symbol
 *
 * What this step does NOT do:
 *   - Relocation processing (step06)
 *   - Calling the BOF entry point (step06+)
 *
 * Compile (links against kernel32 for VirtualAlloc, LoadLibraryA, etc.):
 *   x86_64-w64-mingw32-gcc COFFLoader.c beacon_compatibility.c -o COFFLoader.exe
 *
 * Usage:
 *   COFFLoader.exe hello_bof.o
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "COFFLoader.h"
#include "beacon_compatibility.h"

/* ================================================================
 * PREPENDSYMBOLVALUE -- the import prefix the compiler generates
 *
 * On x64: DECLSPEC_IMPORT produces symbols like "__imp_KERNEL32$Func"
 * On x86: DECLSPEC_IMPORT produces symbols like "__imp__KERNEL32$Func"
 * We need to strip this prefix to get the raw "LIBRARY$Function" name.
 * ================================================================ */
#ifdef _WIN64
#define PREPENDSYMBOLVALUE "__imp_"
#else
#define PREPENDSYMBOLVALUE "__imp__"
#endif

/* ================================================================
 * get_symbol_name -- retrieve symbol name handling short/long names
 *
 * COFF symbols store names in two ways:
 *   - Short names (<= 8 chars): directly in the Name[8] field
 *   - Long names (> 8 chars): first 4 bytes are 0, next 4 are an
 *     offset into the string table
 * ================================================================ */
static const char* get_symbol_name(coff_sym_t* sym, char* string_table) {
    static char buf[9];
    if (sym->first.value[0] != 0) {
        /* Short name: may not be null-terminated if exactly 8 chars */
        memcpy(buf, sym->first.Name, 8);
        buf[8] = '\0';
        return buf;
    } else {
        /* Long name: offset into string table */
        return string_table + sym->first.value[1];
    }
}

/* ================================================================
 * process_symbol -- resolve a single external symbol
 *
 * Resolution order:
 *   1. Strip the __imp_ prefix (compiler-generated for DECLSPEC_IMPORT)
 *   2. Check the InternalFunctions table (Beacon API)
 *   3. If not found, split on '$' for LIBRARY$Function convention
 *      and resolve via LoadLibraryA + GetProcAddress
 *
 * Returns: resolved function pointer, or NULL if unresolved
 * ================================================================ */
static void* process_symbol(const char* symbol_name) {
    void*  address     = NULL;
    char   buf[256]    = {0};
    const char* name   = symbol_name;
    size_t prefix_len  = strlen(PREPENDSYMBOLVALUE);

    /* Step 1: Strip the __imp_ / __imp__ prefix if present */
    if (strncmp(name, PREPENDSYMBOLVALUE, prefix_len) == 0) {
        name = name + prefix_len;
    }

    /* Step 2: Check InternalFunctions table for Beacon API match */
    for (int i = 0; i < 30; i++) {
        if (InternalFunctions[i][0] == NULL) break;
        if (strcmp((char*)InternalFunctions[i][0], name) == 0) {
            address = (void*)InternalFunctions[i][1];
            return address;
        }
    }

    /* Step 3: Split on '$' for LIBRARY$Function convention */
    strncpy(buf, name, sizeof(buf) - 1);
    char* dollar = strchr(buf, '$');
    if (dollar != NULL) {
        *dollar = '\0';  /* null-terminate the library name */
        char* lib_name  = buf;
        char* func_name = dollar + 1;

        /* Load the DLL */
        HMODULE hLib = LoadLibraryA(lib_name);
        if (hLib) {
            address = (void*)GetProcAddress(hLib, func_name);
            if (!address) {
                printf("    [!] GetProcAddress failed: %s!%s\n",
                       lib_name, func_name);
            }
        } else {
            printf("    [!] LoadLibraryA failed: %s\n", lib_name);
        }
    }

    return address;
}

/* ================================================================
 * populate_internal_functions -- fill the InternalFunctions table
 *
 * Maps each Beacon API function name to its implementation pointer.
 * process_symbol() searches this table before trying LoadLibrary.
 * 24 entries, 6 spare slots in the 30-entry table.
 * ================================================================ */
static void populate_internal_functions(void) {
    int i = 0;

    /* Data Parsing API */
    InternalFunctions[i][0] = (unsigned char*)"BeaconDataParse";
    InternalFunctions[i][1] = (unsigned char*)BeaconDataParse;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconDataInt";
    InternalFunctions[i][1] = (unsigned char*)BeaconDataInt;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconDataShort";
    InternalFunctions[i][1] = (unsigned char*)BeaconDataShort;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconDataLength";
    InternalFunctions[i][1] = (unsigned char*)BeaconDataLength;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconDataExtract";
    InternalFunctions[i][1] = (unsigned char*)BeaconDataExtract;
    i++;

    /* Output API */
    InternalFunctions[i][0] = (unsigned char*)"BeaconPrintf";
    InternalFunctions[i][1] = (unsigned char*)BeaconPrintf;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconOutput";
    InternalFunctions[i][1] = (unsigned char*)BeaconOutput;
    i++;

    /* Format Buffer API */
    InternalFunctions[i][0] = (unsigned char*)"BeaconFormatAlloc";
    InternalFunctions[i][1] = (unsigned char*)BeaconFormatAlloc;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconFormatReset";
    InternalFunctions[i][1] = (unsigned char*)BeaconFormatReset;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconFormatFree";
    InternalFunctions[i][1] = (unsigned char*)BeaconFormatFree;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconFormatAppend";
    InternalFunctions[i][1] = (unsigned char*)BeaconFormatAppend;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconFormatPrintf";
    InternalFunctions[i][1] = (unsigned char*)BeaconFormatPrintf;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconFormatToString";
    InternalFunctions[i][1] = (unsigned char*)BeaconFormatToString;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconFormatInt";
    InternalFunctions[i][1] = (unsigned char*)BeaconFormatInt;
    i++;

    /* Token / Process API */
    InternalFunctions[i][0] = (unsigned char*)"BeaconUseToken";
    InternalFunctions[i][1] = (unsigned char*)BeaconUseToken;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconRevertToken";
    InternalFunctions[i][1] = (unsigned char*)BeaconRevertToken;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconIsAdmin";
    InternalFunctions[i][1] = (unsigned char*)BeaconIsAdmin;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconGetSpawnTo";
    InternalFunctions[i][1] = (unsigned char*)BeaconGetSpawnTo;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconSpawnTemporaryProcess";
    InternalFunctions[i][1] = (unsigned char*)BeaconSpawnTemporaryProcess;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconInjectProcess";
    InternalFunctions[i][1] = (unsigned char*)BeaconInjectProcess;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconInjectTemporaryProcess";
    InternalFunctions[i][1] = (unsigned char*)BeaconInjectTemporaryProcess;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconCleanupProcess";
    InternalFunctions[i][1] = (unsigned char*)BeaconCleanupProcess;
    i++;

    /* Utility */
    InternalFunctions[i][0] = (unsigned char*)"toWideChar";
    InternalFunctions[i][1] = (unsigned char*)toWideChar;
    i++;
    InternalFunctions[i][0] = (unsigned char*)"BeaconGetOutputData";
    InternalFunctions[i][1] = (unsigned char*)BeaconGetOutputData;
    i++;

    /* Remaining slots stay NULL (sentinel for table-end detection) */
}

/* ================================================================
 * Helper: determine VirtualAlloc protection flags from section
 * characteristics.
 * ================================================================ */
static DWORD get_section_protection(uint32_t characteristics) {
    BOOL exec  = (characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
    BOOL read  = (characteristics & IMAGE_SCN_MEM_READ)    != 0;
    BOOL write = (characteristics & IMAGE_SCN_MEM_WRITE)   != 0;

    if (exec && write) return PAGE_EXECUTE_READWRITE;
    if (exec && read)  return PAGE_EXECUTE_READ;
    if (exec)          return PAGE_EXECUTE;
    if (write)         return PAGE_READWRITE;
    if (read)          return PAGE_READONLY;
    return PAGE_READWRITE;  /* default for .bss / unknown */
}

/* ================================================================
 * main -- orchestrate file reading, section loading, and symbol
 *         resolution
 * ================================================================ */
int main(int argc, char* argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <coff_file.o>\n", argv[0]);
        return 1;
    }

    /* ---- Populate the Beacon API lookup table ---- */
    populate_internal_functions();

    /* ---- Read the entire COFF file into memory ---- */
    FILE* f = fopen(argv[1], "rb");
    if (!f) {
        fprintf(stderr, "Error: cannot open '%s'\n", argv[1]);
        return 1;
    }

    fseek(f, 0, SEEK_END);
    long filesize = ftell(f);
    fseek(f, 0, SEEK_SET);

    unsigned char* data = (unsigned char*)malloc(filesize);
    if (!data) {
        fprintf(stderr, "Error: malloc failed\n");
        fclose(f);
        return 1;
    }
    fread(data, 1, filesize, f);
    fclose(f);

    printf("=== COFFLoader Step 05: Symbol Resolution ===\n");
    printf("File: %s (%ld bytes)\n\n", argv[1], filesize);

    /* ---- Parse COFF structures ---- */
    coff_file_header_t* hdr      = (coff_file_header_t*)data;
    coff_sect_t*        sections = (coff_sect_t*)(data + sizeof(coff_file_header_t));
    coff_sym_t*         symbols  = (coff_sym_t*)(data + hdr->PointerToSymbolTable);
    char*               strtab   = ((char*)symbols) + (hdr->NumberOfSymbols * sizeof(coff_sym_t));

    printf("  Machine:     0x%04X (%s)\n", hdr->Machine,
           hdr->Machine == MACHINE_AMD64 ? "x64" : "x86");
    printf("  Sections:    %u\n", hdr->NumberOfSections);
    printf("  Symbols:     %u\n\n", hdr->NumberOfSymbols);

    /* ---- Phase 1: Load sections into executable memory ---- */
    printf("--- Phase 1: Section Loading ---\n");

    /* Array to track allocated memory for each section */
    void** section_memory = (void**)calloc(hdr->NumberOfSections, sizeof(void*));
    if (!section_memory) {
        fprintf(stderr, "Error: calloc failed for section_memory\n");
        free(data);
        return 1;
    }

    for (int i = 0; i < hdr->NumberOfSections; i++) {
        char name[9] = {0};
        memcpy(name, sections[i].Name, 8);

        uint32_t raw_size = sections[i].SizeOfRawData;
        uint32_t chars    = sections[i].Characteristics;

        /* Determine allocation size -- at least 1 byte for .bss */
        uint32_t alloc_size = raw_size > 0 ? raw_size : 1;

        /* Allocate with appropriate memory protection */
        DWORD protect = get_section_protection(chars);
        void* mem = VirtualAlloc(NULL, alloc_size,
                                 MEM_COMMIT | MEM_RESERVE, protect);

        if (!mem) {
            printf("  [%d] %-8s  FAILED to allocate %u bytes\n",
                   i, name, alloc_size);
            section_memory[i] = NULL;
            continue;
        }

        /* Copy raw data if this section has any */
        if (raw_size > 0 && sections[i].PointerToRawData > 0) {
            memcpy(mem, data + sections[i].PointerToRawData, raw_size);
        } else {
            memset(mem, 0, alloc_size);
        }

        section_memory[i] = mem;
        printf("  [%d] %-8s  %5u bytes -> %p  (prot=0x%02lX)\n",
               i, name, alloc_size, mem, protect);
    }
    printf("\n");

    /* ---- Phase 2: Symbol Resolution ---- */
    printf("--- Phase 2: Symbol Resolution ---\n");
    printf("  Resolving %u symbols...\n\n", hdr->NumberOfSymbols);

    for (uint32_t i = 0; i < hdr->NumberOfSymbols; i++) {
        const char* name = get_symbol_name(&symbols[i], strtab);
        uint16_t    sec  = symbols[i].SectionNumber;
        uint8_t     cls  = symbols[i].StorageClass;

        if (cls == IMAGE_SYM_CLASS_EXTERNAL && sec == 0) {
            /* ---- Undefined external: needs resolution ---- */
            void* addr = process_symbol(name);
            if (addr) {
                printf("  [%2u] %-45s -> RESOLVED   %p\n",
                       i, name, addr);
            } else {
                printf("  [%2u] %-45s -> UNRESOLVED\n",
                       i, name);
            }
        } else if (cls == IMAGE_SYM_CLASS_EXTERNAL && sec > 0) {
            /* ---- Defined external: lives in a loaded section ---- */
            void* addr = NULL;
            if (sec <= hdr->NumberOfSections && section_memory[sec - 1]) {
                addr = (char*)section_memory[sec - 1] + symbols[i].Value;
            }
            printf("  [%2u] %-45s -> DEFINED     %p  (section %d + 0x%X)\n",
                   i, name, addr, sec, symbols[i].Value);
        } else if (cls == IMAGE_SYM_CLASS_STATIC) {
            /* ---- Static symbol: section-local ---- */
            void* addr = NULL;
            if (sec > 0 && sec <= hdr->NumberOfSections && section_memory[sec - 1]) {
                addr = (char*)section_memory[sec - 1] + symbols[i].Value;
            }
            printf("  [%2u] %-45s -> STATIC      %p  (section %d)\n",
                   i, name, addr ? addr : NULL, sec);
        } else {
            /* ---- Other storage class ---- */
            printf("  [%2u] %-45s -> class=%d sec=%d\n",
                   i, name, cls, sec);
        }

        /* Skip auxiliary symbol entries */
        i += symbols[i].NumberOfAuxSymbols;
    }
    printf("\n");

    /* ---- Cleanup ---- */
    printf("--- Cleanup ---\n");
    for (int i = 0; i < hdr->NumberOfSections; i++) {
        if (section_memory[i]) {
            VirtualFree(section_memory[i], 0, MEM_RELEASE);
            char name[9] = {0};
            memcpy(name, sections[i].Name, 8);
            printf("  Freed section [%d] %s\n", i, name);
        }
    }
    free(section_memory);
    free(data);

    printf("\n=== Done ===\n");
    return 0;
}
