
Una guida su come scrivere regole YARA veloci ed efficienti in termini di memoria.
Scrivere regole YARA efficienti è essenziale per mantenere prestazioni di scansione rapide e accurate. Questa guida fornisce principi chiave e buone pratiche per aiutarti a ottimizzare le tue regole, ridurre i calcoli non necessari ed evitare le insidie più comuni. Include approfondimenti di esperti del settore, tra cui Victor M. Alvarez, WXS, e contributi della community YARA.
Questa sezione fornisce un riepilogo conciso delle buone pratiche sulle prestazioni di YARA. Per spiegazioni dettagliate ed esempi, fai riferimento alla guida completa qui sotto.
"Pensa a YARA come a un processo in due fasi: prima, la ricerca di tutti i pattern elencati nelle stringhe, e poi, la valutazione delle condizioni. Non puoi usare condizioni ben formate per compensare stringhe scelte male."
— Wesley Shields
YARA segue quattro passaggi principali quando scansiona un file:
YARA cerca prima le stringhe, rendendo la selezione delle stringhe il fattore singolarmente più importante per l'efficienza delle regole.
✅ Buone Pratiche per le Stringhe:
\x00\x00\x00\x00 appare troppo di frequente.nocase con cautela – Genera esponenzialmente più varianti di ricerca.YARA valuta le condizioni in sequenza e si ferma al primo insuccesso.
✅ Buone Pratiche per le Condizioni:
filesize < X) prima delle condizioni costose.for all i in (1..filesize) è inefficiente).@) invece delle regex per i controlli di sequenza.⚠ Nota: Le condizioni regex non applicano lo short-circuit e vengono sempre valutate per ultime.
Moduli come pe, elf o magic devono analizzare l'intero file prima della valutazione, aumentando il tempo di scansione.
✅ Alternative:
pe.is_pe, usa uint16(0) == 0x5A4D per identificare i file PE.I match eccessivi rallentano la scansione e possono attivare errori di "too many matches".
✅ Correggere i Match Inefficienti:
.*, .+ o {x,} senza un limite superiore.@herrcore ha creato un utile video tutorial che copre gli argomenti discussi in questa guida sulle prestazioni.
Introduzione a YARA - Scrivere Regole YARA Efficienti
Per capire meglio cosa e dove può essere ottimizzato nelle prestazioni di YARA, è utile comprendere il processo di scansione. È fondamentalmente suddiviso in 4 fasi che verranno spiegate in modo molto semplificato usando questa regola di esempio:
import "math"
rule example_php_webshell_rule
{
meta:
description = "Just an example php webshell rule"
date = "2021/02/16"
strings:
$php_tag = "<?php"
$input1 = "GET"
$input2 = "POST"
$payload = /assert[\t ]{0,100}\(/
condition:
filesize < 20KB and
$php_tag and
$payload and
any of ( $input* ) and
math.entropy(500, filesize-500) >= 5
}
Questo passaggio avviene prima della scansione vera e propria. YARA cercherà i cosiddetti atoms nelle stringhe di ricerca per alimentare l'automa di Aho-Corasick. I dettagli sono spiegati nel capitolo atomo, ma per ora è sufficiente sapere che sono lunghi al massimo 4 byte e che YARA li sceglie in modo piuttosto intelligente per evitare troppi match. Nel nostro esempio, YARA potrebbe selezionare i seguenti 4 atomi:
<?phGETPOSTsser (da assert)Qui la scansione è iniziata. I passaggi 2.-4. verranno eseguiti su tutti i file. YARA cercherà in ogni file i 4 atomi definiti sopra utilizzando un albero dei prefissi chiamato automa di Aho-Corasick. Qualsiasi corrispondenza viene passata al motore bytecode.
Se, ad esempio, c'è una corrispondenza su sser, YARA verificherà se è preceduta da una a e continua con una t. Se è così, proseguirà con la regex [\t ]{0,100}\(. Con questo approccio intelligente, YARA evita di far passare un lento motore regex sull'intero file e seleziona solo alcune parti da esaminare più da vicino.
Dopo che tutto il pattern matching è stato completato, vengono verificate le condizioni.
YARA ha un altro meccanismo di ottimizzazione per eseguire il controllo ad alta intensità di CPU math.entropy della nostra regola di esempio solo se le 4 condizioni precedenti sono soddisfatte. Spiegato in maggior dettaglio nel capitolo Condizioni e Valutazione Short-Circuit
Se le condizioni sono soddisfatte, viene segnalato un match. La scansione continua con il file successivo al passaggio 2.
YARA estrae dalle stringhe brevi sottostringhe lunghe fino a 4 byte chiamate "atoms". Questi atomi possono essere estratti da qualsiasi punto della stringa e YARA li cerca durante la scansione del file; se trova uno degli atomi, verifica che la stringa corrisponda effettivamente.
Ad esempio, considera queste stringhe:
/abc.*cde/
=> i possibili atomi sono abc e cde; si può usare l'uno o l'altro. L'atomo abc è attualmente preferito perché hanno la stessa qualità ed è il primo dei due.
/(one|two)three/
=> i possibili atomi sono one, two, thre e hree; possiamo cercare solo thre (o hree), oppure sia one che two. L'atomo thre è preferito perché porterà a meno corrispondenze potenziali rispetto a one e two (che sono più corti) e non contiene la doppia e (più la lettera è unica, meglio è).
YARA fa del suo meglio per selezionare i migliori atomi da ogni stringa, per esempio:
{ 00 00 00 00 [1-4] 01 02 03 04 }
=> qui YARA usa l'atomo 01 02 03 04, perché 00 00 00 00 è troppo comune
{ 01 02 [1-4] 01 02 03 04 }
=> 01 02 03 04 è preferito rispetto a 01 02 perché è più lungo
Quindi, il punto importante è che le stringhe dovrebbero contenere buoni atomi. Queste sono stringhe pessime perché contengono atomi troppo corti o troppo comuni:
{00 00 00 00 [1-2] FF FF [1-2] 00 00 00 00}
{AB [1-2] 03 21 [1-2] 01 02}
/a.*b/
/a(c|d)/
Le stringhe peggiori sono quelle che non contengono alcun atomo, come:
/\w.*\d/
/[0-9]+\n/