/*
 * COFFLoader.c -- Partial COFF loader: section loading only
 * Step 04: Reads a COFF object file, parses headers, allocates
 *          executable memory for each section, and copies raw data.
 *
 * This is an INCOMPLETE loader. It performs the first half of the
 * loading process:
 *   1. Read the .o file into a flat buffer
 *   2. Parse the COFF file header
 *   3. Locate the section table, symbol table, and string table
 *   4. Allocate RWX memory for each section (VirtualAlloc)
 *   5. Copy section raw data into the allocated pages
 *   6. Count relocations and allocate a functionMapping table
 *   7. Print a summary of what was loaded
 *
 * What it does NOT do (yet):
 *   - Resolve symbols (no LoadLibrary/GetProcAddress)
 *   - Apply relocations (no patching of addresses)
 *   - Find or call the entry point (go/main)
 *
 * Compile as a normal Windows program (WITH linking):
 *   gcc COFFLoader.c -o COFFLoader.exe -lkernel32
 *   cl.exe COFFLoader.c /Fe:COFFLoader.exe
 *
 * Usage:
 *   COFFLoader.exe hello_bof.o
 */

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

/* ================================================================
 * functionMapping -- maps symbol indices to resolved addresses
 *
 * In a complete loader, each relocation references a symbol by
 * index. After resolving that symbol (via DLL import or internal
 * lookup), we store the result here so the relocation pass can
 * patch the code with the correct address.
 *
 * In this step we only allocate the table -- filling it in
 * happens in Step 05 (symbol resolution).
 * ================================================================ */
typedef struct {
    char*    name;     /* symbol name (for debugging)                */
    uint64_t address;  /* resolved virtual address (0 = unresolved) */
} functionMapping_t;


/* ================================================================
 * Helper: get symbol name (handles short vs. string-table names)
 * ================================================================ */
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: stored inline, may not be null-terminated */
        memcpy(buf, sym->first.Name, 8);
        buf[8] = '\0';
        return buf;
    } else {
        /* Long name: first 4 bytes are zero, next 4 are offset
         * into the string table */
        return string_table + sym->first.value[1];
    }
}


/* ================================================================
 * Helper: describe section characteristics as a human-readable string
 * ================================================================ */
static void print_section_flags(uint32_t ch) {
    if (ch & IMAGE_SCN_CNT_CODE)               printf("CODE ");
    if (ch & IMAGE_SCN_CNT_INITIALIZED_DATA)   printf("INIT_DATA ");
    if (ch & IMAGE_SCN_CNT_UNINITIALIZED_DATA) printf("UNINIT_DATA ");
    if (ch & IMAGE_SCN_MEM_EXECUTE)            printf("EXEC ");
    if (ch & IMAGE_SCN_MEM_READ)               printf("READ ");
    if (ch & IMAGE_SCN_MEM_WRITE)              printf("WRITE ");
    if (ch & IMAGE_SCN_MEM_DISCARDABLE)        printf("DISCARD ");
}


/* ================================================================
 * main -- Entry point for the partial COFF loader
 * ================================================================ */
int main(int argc, char* argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <coff_file.o>\n", argv[0]);
        return 1;
    }

    /* ----------------------------------------------------------
     * PHASE 1: Read the entire COFF file into memory
     *
     * We load the whole file into a contiguous buffer so we can
     * use pointer arithmetic and struct casts for zero-copy parsing.
     * ---------------------------------------------------------- */
    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* coff_data = (unsigned char*)malloc(filesize);
    if (!coff_data) {
        fprintf(stderr, "[!] Error: malloc failed (%ld bytes)\n", filesize);
        fclose(f);
        return 1;
    }
    fread(coff_data, 1, filesize, f);
    fclose(f);

    printf("=== COFF Section Loader (Step 04) ===\n");
    printf("File: %s (%ld bytes)\n\n", argv[1], filesize);

    /* ----------------------------------------------------------
     * PHASE 2: Parse the COFF file header
     *
     * The file header is always the first 20 bytes. We cast the
     * raw buffer directly to our packed struct -- no copying needed.
     * ---------------------------------------------------------- */
    coff_file_header_t* hdr = (coff_file_header_t*)coff_data;

    printf("[*] COFF File Header\n");
    printf("    Machine:          0x%04X (%s)\n", hdr->Machine,
           hdr->Machine == MACHINE_AMD64 ? "x64" :
           hdr->Machine == MACHINE_I386  ? "x86" : "unknown");
    printf("    Sections:         %u\n", hdr->NumberOfSections);
    printf("    Symbols:          %u\n", hdr->NumberOfSymbols);
    printf("    SymbolTable at:   0x%08X\n\n", hdr->PointerToSymbolTable);

    /* ----------------------------------------------------------
     * PHASE 3: Locate major structures via pointer arithmetic
     *
     * Section headers immediately follow the file header.
     * The symbol table is at the offset given in the file header.
     * The string table immediately follows the symbol table.
     * ---------------------------------------------------------- */

    /* Section headers start right after the 20-byte file header */
    coff_sect_t* sections = (coff_sect_t*)(coff_data + sizeof(coff_file_header_t));

    /* Symbol table is at the absolute file offset stored in the header */
    coff_sym_t* symbols = (coff_sym_t*)(coff_data + hdr->PointerToSymbolTable);

    /* String table starts right after the last symbol entry.
     * Each symbol is 18 bytes, so:
     *   string_table = symbols_base + (num_symbols * 18)
     * The first 4 bytes of the string table hold its total size. */
    char* string_table = (char*)symbols + (hdr->NumberOfSymbols * sizeof(coff_sym_t));

    printf("[*] Pointers resolved\n");
    printf("    sections[]    = coff_data + 0x%02X\n",
           (unsigned)(sizeof(coff_file_header_t)));
    printf("    symbols[]     = coff_data + 0x%08X\n",
           hdr->PointerToSymbolTable);
    printf("    string_table  = symbols + %u * 18\n\n",
           hdr->NumberOfSymbols);

    /* ----------------------------------------------------------
     * PHASE 4: Allocate memory and load each section
     *
     * For each section we:
     *   a) Allocate a fresh page of RWX memory with VirtualAlloc
     *   b) Copy the raw data bytes from the file buffer
     *   c) Print what we loaded
     *
     * We use PAGE_EXECUTE_READWRITE for simplicity. A production
     * loader would set proper R/W/X permissions per section after
     * relocations are applied (VirtualProtect).
     *
     * We store each section's allocated base address back into an
     * array so the relocation pass (Step 05) can find it.
     * ---------------------------------------------------------- */

    int num_sections = hdr->NumberOfSections;

    /* Array to track each section's allocated virtual address.
     * section_bases[i] = VirtualAlloc'd pointer for section i. */
    void** section_bases = (void**)calloc(num_sections, sizeof(void*));
    if (!section_bases) {
        fprintf(stderr, "[!] Error: calloc for section_bases failed\n");
        free(coff_data);
        return 1;
    }

    printf("[*] Loading %d sections into memory...\n\n", num_sections);

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

        uint32_t raw_size = sections[i].SizeOfRawData;

        /* Determine allocation size:
         * - If the section has raw data, allocate at least that much.
         * - For BSS (uninitialized data), SizeOfRawData is 0 but
         *   VirtualSize tells us how much to reserve. We use the
         *   larger of the two, with a minimum of 1 byte to avoid
         *   allocating zero-size pages. */
        uint32_t alloc_size = raw_size;
        if (sections[i].VirtualSize > alloc_size)
            alloc_size = sections[i].VirtualSize;
        if (alloc_size == 0)
            alloc_size = 1;

        /* Allocate RWX memory for this section.
         * MEM_COMMIT | MEM_RESERVE in one call: reserves virtual
         * address space AND commits physical pages immediately. */
        void* section_mem = VirtualAlloc(
            NULL,                               /* let OS choose address  */
            alloc_size,                         /* bytes to allocate      */
            MEM_COMMIT | MEM_RESERVE,           /* reserve + commit       */
            PAGE_EXECUTE_READWRITE              /* RWX for simplicity     */
        );

        if (!section_mem) {
            fprintf(stderr, "[!] VirtualAlloc failed for section %d (%s), "
                    "size=%u, error=%lu\n",
                    i, name, alloc_size, GetLastError());
            /* Clean up previously allocated sections */
            for (int j = 0; j < i; j++) {
                if (section_bases[j])
                    VirtualFree(section_bases[j], 0, MEM_RELEASE);
            }
            free(section_bases);
            free(coff_data);
            return 1;
        }

        /* Zero the allocation (VirtualAlloc guarantees zeroed pages
         * for MEM_COMMIT, but we do it explicitly for clarity) */
        memset(section_mem, 0, alloc_size);

        /* Copy raw data from the COFF file into our allocated page.
         * If the section has no raw data (e.g., BSS), skip the copy. */
        if (raw_size > 0 && sections[i].PointerToRawData > 0) {
            memcpy(section_mem,
                   coff_data + sections[i].PointerToRawData,
                   raw_size);
        }

        section_bases[i] = section_mem;

        /* Print what we loaded */
        printf("    [%d] %-8s  FileOff=0x%04X  RawSize=0x%04X  "
               "AllocSize=0x%04X  VA=%p  Relocs=%u  [",
               i, name,
               sections[i].PointerToRawData,
               raw_size,
               alloc_size,
               section_mem,
               sections[i].NumberOfRelocations);
        print_section_flags(sections[i].Characteristics);
        printf("]\n");
    }
    printf("\n");

    /* ----------------------------------------------------------
     * PHASE 5: Count total relocations across all sections
     *
     * We count how many relocations exist in total. In a complete
     * loader, we would also count the number of unique symbols
     * referenced by those relocations to size the functionMapping
     * table. For simplicity, we allocate one slot per symbol.
     * ---------------------------------------------------------- */

    int total_relocs = 0;
    for (int i = 0; i < num_sections; i++) {
        total_relocs += sections[i].NumberOfRelocations;
    }

    printf("[*] Total relocations across all sections: %d\n", total_relocs);

    /* Allocate the functionMapping table -- one entry per symbol.
     * In Step 05, process_symbol() will fill in the address field
     * for each symbol as it resolves imports and internal references. */
    int num_symbols = hdr->NumberOfSymbols;
    functionMapping_t* func_map = NULL;

    if (num_symbols > 0) {
        func_map = (functionMapping_t*)calloc(num_symbols,
                                               sizeof(functionMapping_t));
        if (!func_map) {
            fprintf(stderr, "[!] Error: calloc for functionMapping failed\n");
            /* Continue anyway -- we just can't do symbol resolution */
        } else {
            printf("[*] Allocated functionMapping table: %d entries "
                   "(%zu bytes)\n",
                   num_symbols,
                   (size_t)(num_symbols * sizeof(functionMapping_t)));
        }
    }

    /* ----------------------------------------------------------
     * PHASE 6: Summary
     *
     * At this point we have:
     *   - The COFF file parsed and understood
     *   - Each section loaded into its own RWX page
     *   - A function mapping table ready for symbol resolution
     *
     * What is MISSING to actually execute the BOF:
     *   - Symbol resolution (Step 05): look up __imp_KERNEL32$...
     *     symbols via LoadLibraryA/GetProcAddress, and resolve
     *     internal symbols like BeaconPrintf.
     *   - Relocation processing (Step 06): patch each relocation
     *     site with the resolved address.
     *   - Entry point invocation: find the "go" or "_go" symbol
     *     in the symbol table, cast its address to a function
     *     pointer, and call it.
     * ---------------------------------------------------------- */

    printf("\n[*] === Section loading complete ===\n");
    printf("    Sections loaded:     %d\n", num_sections);
    printf("    Relocations pending: %d\n", total_relocs);
    printf("    Symbols to resolve:  %u\n", hdr->NumberOfSymbols);
    printf("\n[!] This is a partial loader -- cannot execute the BOF yet.\n");
    printf("    Symbol resolution and relocations are needed (Step 05+).\n");

    /* ----------------------------------------------------------
     * PHASE 7: Cleanup
     *
     * Free everything in reverse order of allocation.
     * VirtualFree with MEM_RELEASE decommits and releases the pages.
     * ---------------------------------------------------------- */

    printf("\n[*] Cleaning up...\n");

    if (func_map)
        free(func_map);

    for (int i = 0; i < num_sections; i++) {
        if (section_bases[i])
            VirtualFree(section_bases[i], 0, MEM_RELEASE);
    }
    free(section_bases);
    free(coff_data);

    printf("[*] Done.\n");
    return 0;
}
