Saltar al contenido

Rendimiento en YARA: átomos, cadenas y condiciones

Por qué algunas reglas YARA son lentas y cómo arreglarlas: átomos y prefiltro, cadenas buenas y malas, hex con bytes fijos, regex ancladas y comprobaciones baratas primero.

Publicado el 2 min de lectura

Una sola regla lenta puede frenar el escaneo de miles de archivos, porque todas las reglas de un conjunto compilado comparten la misma pasada de búsqueda.

Cómo funciona un escaneo

  1. Búsqueda de patrones: al compilar, YARA elige de cada patrón una secuencia fija de hasta cuatro bytes, el átomo. Todos los átomos entran en un autómata Aho-Corasick y los datos se recorren una vez; donde aparece un átomo, se verifica el patrón completo.
  2. Evaluación de condiciones con las coincidencias encontradas, las propiedades del archivo y los datos de módulos.

El coste depende de lo frecuentes que sean los átomos: E8 45 FF 0A es raro; 00 00 aparece millones de veces.

Átomos buenos y malos

PatrónCalidadPor qué
"EvilCorpUpdater"ExcelenteTexto largo y raro
{ 8B 45 FC 33 D2 F7 75 F8 }BuenaBytes de código fijos
"abc"MalaTres bytes, demasiado frecuente
{ 00 00 00 00 ?? ?? 00 00 }Muy malaSolo ceros fijos
/[a-z]{8}\.dll/MalaSolo .dll es literal

YARA-X avisa al compilar de algunos patrones lentos; el editor muestra esos avisos y el linter añade heurísticas.

Cadenas rápidas

  • Al menos cuatro bytes fijos consecutivos, idealmente al principio.
  • Evita bytes comunes: 00, FF, 90, CC, espacios.
  • Comodines solo en lo que cambia (direcciones, valores inmediatos), no en los opcodes.
  • nocase, xor y base64 solo en cadenas largas.
  • Ancla las regex con literales.
rule Fast_Code_Pattern
{
    strings:
        $decrypt = { 8A 04 0E 34 ?? 88 04 0F 41 81 F9 ?? ?? ?? ?? 72 }
        $cfg = "cfg_version=" ascii
    condition:
        uint16(0) == 0x5A4D and filesize < 3MB and all of them
}

Primero lo barato

import "pe"
import "math"

rule Ordered_Condition
{
    strings:
        $a = "ReflectiveLoader" ascii
    condition:
        uint16(0) == 0x5A4D and
        filesize < 4MB and
        $a and
        pe.number_of_sections > 3 and
        math.entropy(0, 0x1000) < 7.0
}

Un límite de filesize también protege frente a archivos enormes que contengan un átomo por casualidad.

Bucles y funciones de módulos

Recorre coincidencias (for any i in (1..#a)), nunca cada offset (0..filesize). math.entropy y hash.* sobre (0, filesize) leen todo el archivo: acota el rango o protégelo.

Medir

Cronometra yr scan rules.yar corpus/ antes y después de un cambio, compara el conjunto con y sin la regla sospechosa y usa yaraQA para detectar problemas conocidos.

Lista de comprobación

  1. Cada patrón tiene cuatro o más bytes fijos consecutivos.
  2. Ningún patrón está hecho sobre todo de 00, FF, 90, CC o espacios.
  3. nocase, xor y base64 solo en cadenas de ocho caracteres o más.
  4. Las regex tienen un literal de tres o más caracteres y ningún .* inicial.
  5. La condición empieza con magic bytes y un límite de filesize.
  6. Los bucles recorren coincidencias y las funciones usan rangos acotados.

Comprueba tu regla con el linter y sigue con pruebas y CI.

FAQ

¿Qué es un átomo en YARA?
Una secuencia fija corta, de hasta cuatro bytes, que YARA extrae de cada patrón. Todos los átomos se buscan a la vez y el patrón completo solo se verifica donde aparece su átomo.
¿Por qué mi regla YARA es lenta?
Casi siempre porque un patrón no tiene un buen átomo: cadena de menos de cuatro bytes, hex casi todo comodines, bytes muy comunes como 00 00 00 00 o regex sin parte literal.
¿Importa el orden de la condición?
Sí. Se evalúa de izquierda a derecha con cortocircuito, así que filesize y los magic bytes deben ir antes que módulos, bucles, entropía o hashes.