Zum Inhalt springen

YARA-Performance: Atome, Strings und Bedingungen

Warum manche YARA-Regeln langsam sind und wie man sie beschleunigt: Atome und Vorfilter, gute und schlechte Strings, Hex mit festen Bytes, verankerte Regex, günstige Prüfungen.

Veröffentlicht am 2 Min. Lesezeit

Eine einzige langsame Regel kann den Scan tausender Dateien bremsen, weil alle Regeln eines kompilierten Satzes denselben Suchdurchlauf teilen.

Wie ein Scan abläuft

  1. Mustersuche: Beim Kompilieren wählt YARA aus jedem Muster eine feste Folge von bis zu vier Bytes, das Atom. Alle Atome landen in einem Aho-Corasick-Automaten; die Daten werden einmal durchlaufen, und nur bei Atom-Treffern wird das ganze Muster geprüft.
  2. Bedingungsauswertung mit Treffern, Dateieigenschaften und Moduldaten.

Die Kosten hängen davon ab, wie häufig Atome vorkommen: E8 45 FF 0A ist selten, 00 00 millionenfach.

Gute und schlechte Atome

MusterQualitätGrund
"EvilCorpUpdater"Sehr gutLanger, seltener Text
{ 8B 45 FC 33 D2 F7 75 F8 }GutFeste Code-Bytes
"abc"SchlechtDrei Bytes, zu häufig
{ 00 00 00 00 ?? ?? 00 00 }Sehr schlechtNur Nullen fest
/[a-z]{8}\.dll/SchlechtNur .dll ist literal

YARA-X warnt beim Kompilieren vor manchen langsamen Mustern; der Editor zeigt diese Warnungen, der Linter ergänzt Heuristiken.

Schnelle Strings

  • Mindestens vier aufeinanderfolgende feste Bytes, idealerweise am Anfang.
  • Häufige Bytes meiden: 00, FF, 90, CC, Leerzeichen.
  • Wildcards nur für Veränderliches (Adressen, Immediates), nicht für Opcodes.
  • nocase, xor, base64 nur für lange Strings.
  • Regex mit Literalen verankern.
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
}

Günstiges zuerst

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
}

Eine filesize-Grenze schützt auch vor riesigen Dateien, die zufällig ein Atom enthalten.

Schleifen und Modulfunktionen

Über Treffer iterieren (for any i in (1..#a)), nie über jeden Offset (0..filesize). math.entropy und hash.* über (0, filesize) lesen die ganze Datei — Bereich begrenzen oder absichern.

Messen

yr scan rules.yar korpus/ vor und nach einer Änderung stoppen, den Satz mit und ohne verdächtige Regel vergleichen und yaraQA für bekannte Probleme nutzen.

Checkliste

  1. Jedes Muster hat mindestens vier feste Bytes am Stück.
  2. Kein Muster besteht überwiegend aus 00, FF, 90, CC oder Leerzeichen.
  3. nocase, xor, base64 nur bei Strings ab acht Zeichen.
  4. Regex enthalten ein Literal ab drei Zeichen und kein führendes .*.
  5. Die Bedingung beginnt mit Magic Bytes und einer filesize-Grenze.
  6. Schleifen laufen über Treffer, Modulfunktionen über begrenzte Bereiche.

Regel im Linter prüfen und mit Tests und CI weitermachen.

FAQ

Was ist ein Atom in YARA?
Eine kurze feste Byte-Folge von höchstens vier Bytes, die YARA aus jedem Muster extrahiert. Alle Atome werden gleichzeitig gesucht; das ganze Muster wird nur dort geprüft, wo sein Atom vorkommt.
Warum ist meine YARA-Regel langsam?
Meist fehlt einem Muster ein gutes Atom: String unter vier Bytes, Hex fast nur aus Wildcards, sehr häufige Bytes wie 00 00 00 00 oder Regex ohne Literal.
Spielt die Reihenfolge in der Bedingung eine Rolle?
Ja. Ausgewertet wird von links nach rechts mit Kurzschluss, daher gehören filesize und Magic Bytes vor Module, Schleifen, Entropie und Hashes.