Un NOP no rompió mi regla YARA. Un solo cambio de codificación sí.
Seguridad Ingeniería 9 de agosto de 2026

Un NOP no rompió mi regla YARA. Un solo cambio de codificación sí.

Una investigación a nivel de byte para ingenieros de detección: por qué insertar un NOP deja intacta una regla hexadecimal YARA, mientras que un único cambio de codificación x86-64 equivalente la anula — reproducido con Aether 1.0.0 como implementación de referencia.

C
Christian Schnapka
Macstab Engineering
6 min de lectura
También en Medium Leer en Medium

Código fuente en GitHub

Una firma fija un único punto en el espacio de codificaciones

Una regla hexadecimal de YARA es un predicado sintáctico sobre un búfer de bytes. Lo que intenta detectar es un objeto semántico, por ejemplo una función que devuelve 7. Entre ambos planos se encuentra la propiedad que define a x86-64: una codificación de longitud variable en la que la correspondencia entre secuencias de bytes y efectos arquitectónicos es masivamente de muchos a uno. Poner RAX a 7 y retornar admite, siendo conservadores, más de una docena de codificaciones legales.

Una firma fija un punto en ese espacio. Todo el trabajo de un mutador consiste en desplazarse a otro. Por eso la suposición intuitiva —insertar un NOP, cambiar el hash y el tamaño, y la regla de identidad queda anulada— mide en el plano equivocado. La pregunta nunca es si algunos bytes se han movido. Es si el punto exacto que fija la regla sigue apareciendo en algún lugar del búfer, y si la regla fija un punto o una familia.

Se trata de un análisis de laboratorio en x86-64 para la evaluación autorizada de detecciones, con Aether 1.0.0 y Zydis v4.1.1 como implementación de referencia reproducible. No pretende cubrir la semántica arbitraria de .text, ni la reescritura binaria de propósito general, ni la evasión de ningún producto de seguridad.

Diagrama que contrasta el plano de identidad (SHA-256, tamaño, volcado hexadecimal, coincidencia de cadenas YARA) con el plano semántico (oráculo multientrada, simulador, ejecución nativa)
Plano de identidad frente a plano semántico: una firma de bytes y un oráculo de comportamiento observan cosas distintas.

Seis bytes, un NOP y la regla sigue acertando

La función de prueba tiene seis bytes: B8 07 00 00 00 C3, es decir, mov eax, 7; ret. La regla didáctica coincide exactamente con esos bytes, de forma contigua. Es una regla solo para laboratorio, no una firma de producción; su valor está en que hace visible el plano de identidad.

La primera captura SAFE con la semilla 42 produjo 90 B8 07 00 00 00 C3: un NOP de un byte delante del original sin cambios. El hash cambió, el tamaño cambió, el valor de retorno se mantiene y la regla sigue coincidiendo, porque la aguja de seis bytes está intacta un byte más adelante.

La razón es mecánica. YARA reduce el patrón a átomos cortos, normalmente subcadenas de hasta cuatro bytes como 07 00 00 00, los coloca en un autómata Aho-Corasick y solo ejecuta el verificador completo cuando hay un acierto de átomo. El autómata es independiente de la posición, así que un desplazamiento de un byte no cambia nada. El corolario: la fuerza real de una regla de identidad es la calidad de su átomo más raro, no su longitud. Usar comodines en el inmediato, como B8 ?? ?? ?? ?? C3, obliga a YARA a anclarse en los átomos de un byte B8 y C3, que saturan cualquier .text real y convierten la regla en una verificación lenta y poco precisa.

Por tanto, la conclusión de que insertar relleno anuló la firma no es aceptable sin probar la propia regla. Este experimento midió relleno, no la destrucción de la regla.

yara
{ B8 07 00 00 00 C3 }   // contiguous identity

Un cambio de codificación y la regla falla

La semilla 5 toma otro camino a través de la misma política SAFE y emite 48 C7 C0 07 00 00 00 C3. La cadena original ya no aparece porque la instrucción cambió de clase de codificación: un prefijo REX cuyo bit W selecciona un tamaño de operando de 64 bits, el grupo de opcodes MOV C7 /0, un ModR/M de registro directo que selecciona RAX, el mismo inmediato 7 y ret. El resultado es, en la práctica, mov rax, 7; ret.

La afirmación tiene un límite preciso. B8 extiende con ceros su inmediato de 32 bits en RAX, mientras que REX.W + C7 /0 lo extiende con signo. Para el inmediato positivo 7, ambas formas producen el mismo valor de 64 bits. Sin embargo, el campo pure_rax de Aether es un oráculo de valor de retorno de 32 bits. Con un inmediato como 0x80000000, las dos formas diferirían en los 32 bits superiores y el oráculo no podría verlo. La afirmación semántica se limita deliberadamente a esta forma con inmediato positivo y al dominio de salida de 32 bits declarado.

Tampoco la semilla por sí sola determina la salida; una captura publicada debe indicar un proceso nuevo, el primer morph con semilla y la compilación exacta. Con esa disciplina, la comparación es limpia:

Diagrama de la codificación original B8 que se ramifica en la variante NOP de la semilla 42 (la regla acierta) y la variante REX de la semilla 5 (la regla falla), ambas con RAX puro igual a 7
Mismo resultado puro, distinta clase de codificación: la variante con NOP mantiene intacta la aguja, la forma REX la elimina.
  • Original B8 … C3: regla de identidad HIT, pure_rax 7
  • Semilla 42 (90 + original): regla de identidad HIT, pure_rax 7 → 7 OK
  • Semilla 5 (48 C7 C0 … C3): regla de identidad MISS, pure_rax 7 → 7 OK

Qué hace creíble el resultado

Esto no es un argumento de que la detección sea imposible. Una regla que reconozca ambas familias de codificación puede seguir detectando las dos. Es un argumento de que un único fragmento de identidad es incompleto.

Aether decodifica cada instrucción con Zydis y la clasifica como Control, Memory, Identity, PureRax u otra clase; solo las instrucciones clasificadas entran en un catálogo de sustituciones acotado semánticamente, y ret nunca se diversifica como pura. Los bytes que Zydis no puede decodificar se conservan como opacos, de modo que una mala decodificación reduce la superficie de transformación en lugar de inventar una reescritura. Cada salida se vuelve a decodificar y se comprueba mediante una comparación pura del retorno de 32 bits, un simulador independiente del host con pares fijos de múltiples entradas y, en x86-64 o en el laboratorio Docker amd64, ejecución nativa. En los modos Lab o Industry, una discrepancia pura provoca un fallo cerrado.

El benchmark del laboratorio del autor en Apple Silicon utilizó 5.096 entradas de corpus (1.000 sintéticas y 4.096 procedentes de ELF). Registra 10.192 morphs correctos, 5.020 comprobaciones puras y 4.862 comprobaciones nativas sin ninguna ruptura, y un 94,89 % de salidas únicas según SHA-256. En la matriz focalizada de 128 semillas, la regla de identidad de laboratorio acertó 30 veces (23,44 %) y falló 98 veces (76,56 %), sin ninguna ruptura pura. Son datos de captura para esa compilación y ese host, no cifras permanentes del producto.

Diagrama de flujo del pipeline MorphEngine de Aether: lift, analyze, rama de modo de producto, diversify, expand, shuffle, split, permute, assemble y verify
El pipeline MorphEngine: cada variante emitida pasa por una nueva decodificación y verificación antes de contar.

Las capas que tienen que coincidir

La lección no es usar más firmas, ni que el morphing gane. Es probar las reglas contra el plano que realmente observan. El ejemplo de seis bytes atraviesa cinco capas, y tratar cualquiera de ellas como el sistema completo es la forma en que un volcado de bytes convincente se convierte en una afirmación falsa. Una nueva decodificación estructural no puede probar la semántica completa de la máquina, un oráculo puro superado no puede probar que un EDR no generó una alerta, y un MISS de YARA no puede probar que una codificación esté ausente de todas las familias de detección.

  • Decodificación: ¿qué bytes y qué grafo de flujo de control decodificamos realmente?
  • Análisis: ¿permite el modelo actual esta transformación?
  • Lowering: ¿siguen apuntando los saltos a los bloques lógicos previstos?
  • Oráculo: ¿se cumplió el contrato de salida declarado en este dominio?
  • Detección: ¿qué observable cambió y qué sensor lo sigue viendo?

Convertir el experimento en CI de detección

El patrón útil: generar gemelos semánticos legales dentro de un dominio declarado de forma estrecha, ejecutar la regla y el oráculo, conservar la evidencia y después reparar el sensor que falló. Ejecute el job en al menos tres planos: el oráculo semántico rechaza las mutaciones que violan el contrato declarado, la identidad se registra como bytes, tamaño y hash exactos, y el detector nombrado se ejecuta con su regla exacta. Una observación de ClamAV o de un EDR es una cuarta medición independiente, no un sustituto del resultado de YARA.

Conserve el código fuente exacto de la regla YARA de laboratorio, el informe JSON del benchmark, los listados decodificados de antes y después, y la versión y la fecha del detector. Nombre el oráculo en lugar de escribir validado: Apple Silicon puede ejecutar la simulación, pero no ejecutar x86-64 de forma nativa, así que utilice el laboratorio Docker amd64 cuando se requiera ejecución en hardware.

Cuando una regla falla, la reparación debe seguir al plano. Una regla de bytes contiguos puede necesitar alternativas por familia de codificación, contexto o una segunda señal; una regla que cubra tanto B8 ?? … C3 como 48 C7 C0 ?? … C3 puede reconocer ambas formas. Un detector de tamaño o entropía puede que ya vea la expansión media de aproximadamente 2,19× y solo necesite calibración. Un sensor de comportamiento debe probarse frente a superficies de ejecución, asignación, temporización y memoria, en lugar de deducirse a partir de un hash estático.

Topología del laboratorio: host del autor que compila Aether con Zydis v4.1.1, un contenedor Linux amd64 para la prueba nativa y un contenedor de escaneo opcional con ClamAV y YARA
Topología del laboratorio: prueba nativa en un contenedor amd64 y mediciones de detectores en un contenedor de escaneo separado.

Key Takeaways

Un NOP no rompió la regla; una codificación equivalente sí. El experimento demuestra una debilidad de una regla de identidad contigua, no un fallo universal de la detección, y solo es válido dentro de su dominio declarado: una forma con inmediato positivo, un oráculo de retorno de 32 bits, una compilación concreta y una regla concreta.

La respuesta correcta no es prometer invulnerabilidad, sino hacer que los supuestos de una regla sean ejecutables y estén cubiertos por pruebas de regresión. Aether está pensado para su uso autorizado en laboratorio bajo la Aether Research License. El artículo completo en Medium contiene cada medición y cada salvedad, y los documentos SCOPE, SCORECARD, LIBRARY y SECURITY del repositorio deben leerse antes de tratar cualquier resultado como una afirmación de despliegue.

#detection-engineering #yara #x86-64 #reverse-engineering #malware-analysis
C

Christian Schnapka

Macstab Engineering

Christian Schnapka desarrolla las herramientas de ingeniería abiertas de Macstab. Publica las versiones extensas de estas investigaciones en Medium, con cada medición, cada salvedad y cada resultado negativo incluidos.