Evasión Estática a Bajo Nivel: Destruyendo Firmas de Mirai con Aritmética en ASM

Cómo romper las reglas YARA y los hashes SHA256 sin packers ni crypters. Una guía avanzada de polimorfismo manual, ofuscación aritmética dinámica y parcheo hexadecimal quirúrgico que redujo la tasa de detección de una muestra real de Mirai de 16/64 a 0/65 en VirusTotal. Incluye la propuesta de una arquitectura de entrega mutante orquestada por DGA.

01. El Mito del Packer: ¿Por Qué Seguimos Ofuscando Mal?

Existe una creencia arraigada en el ecosistema Red Team y entre los desarrolladores de malware: para evadir un motor antivirus (AV) o un EDR en su fase estática, necesitas un packer, un crypter o un loader complejo. Esta premisa es técnicamente falsa. De hecho, el uso de herramientas como UPX o crypters comerciales a menudo se ha convertido en una sentencia de muerte para cualquier payload. La mayoría de los motores modernos levantan alertas heurísticas inmediatas basadas únicamente en la entropía anómala del binario o las firmas propias del propio packer T1027.002.

La fase inicial de la detección depende críticamente del análisis estático: hashes de integridad (SHA256), reglas YARA que buscan secuencias de opcodes específicas, y la extracción de "strings" reveladores. La solución más elegante y quirúrgica no es añadir capas de ofuscación ruidosas, sino manipular la microarquitectura del binario. Se trata de cambiar los opcodes subyacentes manteniendo intacta la lógica de ejecución: polimorfismo manual. Esta técnica se alinea directamente con la táctica de Defense Evasion del framework MITRE ATT&CK TA0005.

La analogía de la placa de un carro

Mientras estaba en la cola de la gasolina, vi la placa de un carro y pensé: "parece una secuencia de bytes". Ahí nació esta investigación. Si cambias los bytes pero el carro sigue siendo el mismo, ¿cómo te identifican? La respuesta está en la aritmética dinámica y en una infraestructura de entrega que nunca repite la misma matrícula.

Diagrama de flujo del proceso de evasión estática: de la muestra original de Mirai al binario indetectable
evasion-mirai-flowchart.png — Flujo completo del proceso de ofuscación. Desde la muestra original detectada por 16/64 motores hasta el binario modificado con 0/65 detecciones, pasando por la extracción de opcodes, generación de claves dinámicas, compilación del stub en NASM y parcheo hexadecimal.

02. Entorno de Laboratorio y Preparación

Para esta prueba de concepto se utilizó un entorno aislado sobre Proxmox VE. La metodología se basa en el rigor del análisis estático y la validación dinámica:

16/64 detecciones en VirusTotal para la muestra original de Mirai
preview-1.jpg — Estado inicial en VirusTotal. La muestra es detectada como trojan.mirai/gafgyt por 16 proveedores, incluyendo Avast, Kaspersky, Microsoft, Google y Fortinet.

03. Teoría de la Evasión: Sustitución Aritmética en ASM

Una regla YARA detecta una secuencia de bytes específica. Por ejemplo, la instrucción MOV EAX, 1 se traduce a los bytes B8 01 00 00 00. Si un motor de seguridad busca esa cadena exacta, basta con alterarla. Este principio es análogo a lo que el DRM Denuvo realiza con sus "constantes robadas" (stolen constants), donde ciertos bytes del binario original simplemente no existen en el ejecutable distribuido y deben ser recuperados dinámicamente mediante una licencia T1027.005.

El metamorfismo aritmético se basa en un principio fundamental: en ensamblador, hay múltiples caminos para llegar al mismo resultado en un registro. Alterar las instrucciones rompe el hash de la sección .text y evade la coincidencia exacta de bytes sin necesidad de modificar el flujo lógico del programa.

Árbol de decisión para la estrategia de mutación de opcodes según el tipo de instrucción
opcode-mutation-decision-tree.png — Árbol de decisión completo para la mutación de opcodes. La estrategia varía según el tipo de instrucción: asignaciones a cero (MOV→XOR), cargas de constantes (descomposición aritmética), operaciones aritméticas (descomposición en múltiples instrucciones) o instrucciones no críticas (NOPs aleatorios). Cada rama incluye la verificación de alineación de bytes.

Ejemplo 1: Asignación a Cero

; Firma original (5 bytes) MOV EAX, 0 ; B8 00 00 00 00   ; Mutaciones (2 bytes) XOR EAX, EAX ; 31 C0 SUB EAX, EAX ; 29 C0

Ejemplo 2: Ofuscación de Constantes Lógicas

En lugar de cargar un valor directamente, se puede cargar el doble y desplazar bits, o usar operaciones compuestas que rompen los patrones de búsqueda lineales. Esta técnica se conecta directamente con el Mixed Boolean-Arithmetic (MBA) usado por Denuvo, donde expresiones simples se convierten en polinomios booleanos extremadamente complejos.

; Firma original: MOV ECX, 0x10 MOV ECX, 0x20 SHR ECX, 1 ; ECX ahora contiene 0x10
Referencia: Denuvo y la Ofuscación de Constantes

En el análisis de Connor-Jay Dunn sobre Denuvo, se detalla cómo el DRM elimina constantes de las instrucciones (ej. un -4 en mov DWORD PTR [rbp-4], edi). El programa no funciona a menos que esa constante sea recuperada del servidor de licencias. Nuestra técnica de "stub descifrador" opera bajo el mismo principio: el opcode original no existe en el binario hasta que se ejecuta la aritmética de descifrado en tiempo de ejecución.

04. La Fábrica de Opcodes: Ofuscación Dinámica en C++

Inspirado por la idea de que las constantes nunca deben aparecer planas en el binario, desarrollé un generador de bloques de ofuscación. La función generar_bloque utiliza números pseudoaleatorios para crear valores auxiliares (val_B, val_C, val_D) y, mediante una operación combinada de suma, resta y XOR, calcula un valor final (val_A) que reconstruye el opcode original.

Esta técnica es extremadamente efectiva porque elimina por completo las firmas de "Magic Numbers". El valor final nunca aparece estático en el binario compilado; solo existe en tiempo de ejecución dentro de los registros de la CPU. Esto se correlaciona con la sub-técnica de MITRE T1027.005 (Code Signing and Obfuscated Constants).

// dynamic_key_obfuscator.cpp (Fragmento) KeyBlock generar_bloque(uint32_t target) { KeyBlock k; // 1. Ruido aleatorio k.val_B = (rand() << 16) | (rand() & 0xFFFF); k.val_C = (rand() << 16) | (rand() & 0xFFFF); k.val_D = (rand() << 16) | (rand() & 0xFFFF);   // 2. Despejar A: (A ^ B) = Target + (D + 1) - (C - 1) uint32_t term_C_dec = k.val_C - 1; uint32_t term_D_inc = k.val_D + 1; uint32_t needed_xor = target + term_D_inc - term_C_dec; k.val_A = needed_xor ^ k.val_B;   return k; }
El Reto del Stub Compacto

El éxito de esta técnica depende de que el stub en Assembly x86 realice la operación inversa de forma compacta. Si el descifrador es demasiado complejo, corremos el riesgo de crear una nueva firma estática: "detectar el mecanismo de evasión en lugar del malware". Este equilibrio es el mismo que enfrenta Denuvo con sus spin-locks para proteger CPUID encriptados on-the-fly.

05. Ejecución Práctica: Parcheando el Binario de Mirai

Paso 1: Del Binario al Arreglo de Opcodes

Desarrollé un script en Bash que, utilizando objdump y otras utilidades, extrae todos los bytes de la sección .text y los empaqueta en un arreglo de uint32_t en formato little-endian. Este arreglo se convierte en el "payload" a proteger, similar a cómo Denuvo selecciona funciones críticas para proteger dentro de su VM.

// Opcodes originales: // xor ebx, ebx (31 DB) // mov eax, 1 (B8 01 00 00 00) // int 0x80 (CD 80) // NOPs de relleno (90...) // // Empaquetado en Little Endian (uint32_t): // Bloque 0: 31 DB B8 01 -> 0x01B8DB31 // Bloque 1: 00 00 00 CD -> 0xCD000000 // Bloque 2: 80 90 90 90 -> 0x90909080 (Relleno con NOPs)   uint32_t payload[] = { 0x01B8DB31, 0xCD000000, 0x90909080 };

Paso 2: El Parche Hexadecimal y el Code Cave

Al mutar las instrucciones, el tamaño en bytes cambia. Si la mutación ocupa menos espacio, se utiliza un relleno inteligente con NOPs (0x90) o instrucciones basura que no alteren los flags de la CPU T1001.003. Este detalle es crítico: cualquier desplazamiento incorrecto rompería los offsets de memoria relativos y causaría un crash inmediato del binario.

Paso 3: El Stub Desensamblador en NASM

El stub final en Assembly es el encargado de leer los bloques cifrados, aplicar la fórmula inversa ((A⊕B)+(C−1))−(D+1), y reconstruir los opcodes originales en un buffer de memoria. Una vez descifrado, transfiere el control al código generado mediante un JMP directo, una técnica análoga a la transferencia de control que Denuvo realiza al OEP (Original Entry Point) después de validar la licencia.

; ASM resultante del stub descifrador _start: mov esi, encrypted_data mov edi, decoded_buffer mov ecx, 3 ; Bloques detectados automáticamente   decode_loop: mov eax, [esi] ; A mov ebx, [esi+4] ; B mov edx, [esi+8] ; C mov ebp, [esi+12] ; D xor eax, ebx ; A ^ B dec edx ; C - 1 add eax, edx ; + (C-1) inc ebp ; D + 1 sub eax, ebp ; - (D+1) mov [edi], eax ; Escribir opcode restaurado add esi, 16 ; Siguiente bloque de llaves add edi, 4 ; Siguiente bloque destino loop decode_loop   ; Transferencia de control al payload original jmp decoded_buffer
Diagrama de componentes comparando el stub descifrador con el DRM Denuvo
stub-denuvo-comparison.png — Comparativa arquitectónica entre el stub descifrador desarrollado en esta investigación y el DRM Denuvo. Ambos comparten el principio de eliminar bytes críticos del binario (opcodes vs stolen constants), almacenarlos de forma protegida (claves A,B,C,D vs license file) y recuperarlos en tiempo de ejecución mediante descifrado aritmético (loop XOR+ADD+SUB vs VM handlers con MBA).

06. Resultados: Rompiendo los Motores Estáticos

El binario modificado se ejecutó en un entorno aislado, capturando el tráfico de red para verificar que el beacon de Mirai se ejecutaba sin crashes. Una vez validada la integridad lógica, se procedió al análisis estático.

El resultado fue contundente. Al no haber un packer, el cálculo de entropía seguía siendo bajo (parecía un ejecutable normal). Al mutar los opcodes, el hash SHA256 cambió y las reglas YARA estáticas quedaron completamente ciegas, demostrando la efectividad de T1562.001 (Impair Defenses: Disable or Modify Tools).

0/65 detecciones en VirusTotal para el binario de Mirai modificado
preview-2.jpg — El clímax del artículo. Tasa de detección reducida a 0/65. Todos los campos de actividad maliciosa figuran como "NOT FOUND": Detections, Mitre Signatures, IDS Rules, Sigma Rules, Dropped Files y Network Communications.
Diagrama comparativo del antes y después en VirusTotal y cambios estructurales del binario
vt-comparison-diagram.png — Comparativa estructural del binario antes y después de la ofuscación. El hash SHA256 cambia completamente, la sección .text contiene opcodes mutados que ciegan las firmas YARA, los strings del C2 quedan ofuscados con XOR, y la entropía se mantiene normal (sin packer). El resultado final: de 16/64 a 0/65 detecciones.

07. Propuesta: Arquitectura de Entrega Mutante con DGA

La evasión estática es el primer eslabón. Pero, ¿qué ocurre cuando el binario debe ser distribuido a gran escala? Si cada víctima descarga exactamente el mismo archivo, basta con que un solo sensor lo capture para que el hash sea quemado y distribuido a todos los endpoints del mundo en minutos. La solución es una arquitectura de entrega mutante orquestada por DGA (Domain Generation Algorithm).

Diagrama de secuencia de la arquitectura de entrega mutante con DGA
dga-mutant-delivery-sequence.png — Arquitectura completa de entrega mutante en tres fases. Fase 1 (Preparación): la fábrica genera binarios con claves únicas y el algoritmo DGA calcula y registra dominios. Fase 2 (Entrega): la víctima descarga el binario vía dominio DGA, ejecuta el stub descifrador, solicita token de sesión al C2 y ejecuta el payload. Fase 3 (Rotación): desregistro de dominios antiguos, apagado de listeners y despliegue de nueva infraestructura para el siguiente ciclo.

El Concepto: Fábrica de Binarios Efímeros

Inspirado por la microsegmentación de campañas de malware moderno y la rotación de dominios observada en mi análisis de cmdxcapital (ver caso), propongo un sistema donde cada muestra entregada sea genéticamente única:

Diagrama de estados del ciclo de vida de un binario mutante
mutant-binary-lifecycle.png — Ciclo de vida completo de un binario mutante. Desde la generación con claves únicas (T1027), pasando por la distribución vía dominio DGA activo, la descarga por la víctima, la ejecución con validación Denuvo-like (el payload no se ejecuta sin token del C2), hasta la expiración y autodestrucción cuando el TTL se cumple o el dominio es desregistrado.
Diagrama de infraestructura DGA mostrando la rotación de dominios
dga-domain-flow.png — Infraestructura de rotación de dominios DGA. Una semilla (fecha + nonce + clave simétrica) alimenta el algoritmo generador que produce 30+ dominios diarios. Solo 3-5 se registran efectivamente y apuntan a IPs C2 activas. Los 25+ dominios restantes actúan como señuelo para generar ruido DNS y dificultar la ingeniería inversa del algoritmo.
Advertencia de Investigación

Esta arquitectura no ha sido probada en un entorno de producción. Representa una propuesta teórica de investigación ofensiva que sintetiza técnicas observadas en APT28, la rotación de infraestructura de Pig Butchering, y los mecanismos de validación de DRM como Denuvo. La implementación real requeriría resolver problemas de sincronización de claves y race conditions en la generación de binarios.

Paralelismos con Denuvo y MBA

La conexión con Denuvo no es casual. El análisis de Connor-Jay Dunn revela que Denuvo utiliza Mixed Boolean-Arithmetic (MBA) para transformar instrucciones simples en expresiones polinómicas de complejidad extrema. Según los teoremas de zhou2007, toda operación en álgebra booleana puede representarse como un polinomio MBA de alto grado. Esto significa que, en teoría, podríamos reescribir nuestro stub descifrador utilizando MBA, haciendo que cada muestra no solo tenga claves diferentes, sino que el propio algoritmo de descifrado sea estructuralmente distinto en cada binario.

Denuvo también emplea "bit vectors" para almacenar valores de registro con sus bits dispersos en memoria, y "on-the-fly decrypted+re-encrypted CPUID" para proteger sus handlers de integridad. Estas técnicas ofrecen un roadmap claro para la evolución de este proyecto: un stub que no solo descifra opcodes, sino que también muta su propia lógica de descifrado en cada instancia, haciendo imposible una firma estática del propio mecanismo de evasión.

Referencia Cruzada: Operación General JP y SilentWarn

En mi investigación de campañas de phishing (Operation General JP), documenté cómo los atacantes rotan dominios y utilizan infraestructura efímera en Cloudflare Pages. La combinación de DGA con ofuscación aritmética es la evolución natural de esas tácticas hacia un modelo de entrega completamente dinámico. T1583.004

Diagrama de contexto del ecosistema de detección versus evasión por capas
defense-vs-evasion-context.png — Ecosistema completo de detección vs evasión organizado por capas. En la capa de red: DNS sinkholing, IDS/IPS y firewalls. En la capa de endpoint: AV/EDR estático (EVADIDO: VT 0/65), sandbox dinámico (PARCIAL: requiere token del C2) y escaneo de memoria (DETECTABLE: post-descifrado). Las técnicas de evasión se dividen en ofuscación aritmética, DGA + infraestructura efímera y validación Denuvo-like.

08. Perspectiva Defensiva (Blue Team)

Esta demostración valida la base de la Pirámide del Dolor de David Bianco: los IoCs basados en Hashes son triviales de evadir. Si los SOCs dependen únicamente de firmas en disco, están condenados al fracaso. La defensa contra arquitecturas DGA mutantes requiere un enfoque en capas:

/¿Querés ver el análisis completo del binario y su comportamiento en red?

Esta técnica de evasión se conecta con metodologías de triage automatizado, análisis de infraestructura criminal y desarrollo de malware ofensivo. Explorá cómo aplico estos principios en casos reales de Threat Hunting, pentesting y reverse engineering.