Conditions YARA : comptages, positions, boucles
Écrire des conditions YARA : logique booléenne, comptage et position des chaînes, filesize et magic bytes, ensembles any/all/N of, boucles for, with et références.
La condition est l’endroit où la règle décide. Les motifs disent seulement ce qui a été trouvé ; la condition dit quelle combinaison de résultats — avec les propriétés du fichier — constitue une détection. Ce guide va de la logique booléenne simple jusqu’aux boucles.
Logique booléenne et références aux motifs
Un identifiant de motif seul vaut vrai si le motif a été trouvé au moins une fois. On les combine avec and, or, not et des parenthèses :
rule Boolean_Logic
{
strings:
$a = "LoaderInit" ascii
$b = "GetProcAddress" ascii
$c = "debug build" ascii
condition:
($a and $b) and not $c
}
La priorité est classique (not avant and, and avant or), mais les parenthèses rendent l’intention évidente pour le lecteur suivant.
Comptages, positions et longueurs
| Expression | Signification |
|---|---|
#a | Nombre d’occurrences de $a |
@a[i] | Position de la i-ème occurrence (à partir de 1) |
!a[i] | Longueur de la i-ème occurrence (utile pour regex et sauts) |
$a at 0x40 | $a exactement à l’offset 0x40 |
$a in (0..1024) | $a quelque part dans le premier Ko |
rule Counts_And_Offsets
{
strings:
$mz = "MZ"
$section = ".text" ascii
$marker = { DE AD BE EF }
condition:
$mz at 0 and #section >= 1 and $marker in (0..0x10000) and @marker[1] > 0x200
}
Taille de fichier et magic bytes
filesize donne la taille des données scannées en octets ; les suffixes KB et MB sont acceptés. Les fonctions uint8/16/32 et int8/16/32 lisent un entier à un offset (little-endian), leurs variantes be en big-endian. Ensemble, elles offrent des contrôles de type de fichier fiables et peu coûteux :
| Type | Contrôle |
|---|---|
| PE Windows | uint16(0) == 0x5A4D |
| ELF | uint32(0) == 0x464C457F |
uint32(0) == 0x46445025 | |
| ZIP / OOXML / JAR / APK | uint32(0) == 0x04034B50 |
| OLE (Office ancien, MSI) | uint32(0) == 0xE011CFD0 |
| Mach-O 64 bits | uint32(0) == 0xFEEDFACF |
rule PE_With_PE_Header_Check
{
condition:
uint16(0) == 0x5A4D and
uint32(uint32(0x3C)) == 0x00004550 and
filesize < 10MB
}
La deuxième ligne suit le pointeur e_lfanew à l’offset 0x3C et vérifie la signature PE\0\0 — le même test que le module pe, sans charger de module. La division entière s’écrit \ en YARA (filesize \ 2).
Ensembles et quantificateurs
Plutôt que d’énumérer chaque motif, on quantifie sur un ensemble :
| Expression | Vraie quand |
|---|---|
any of them | au moins un motif correspond |
all of them | tous les motifs correspondent |
none of ($fp*) | aucun motif de l’ensemble ne correspond |
2 of ($x*) | au moins deux motifs dont le nom commence par $x |
50% of them | au moins la moitié des motifs |
any of ($a, $b, $c) | un élément d’une liste explicite |
rule Quantifier_Example
{
strings:
$x1 = "stage2_payload" ascii
$x2 = "BeaconSleep" ascii
$s1 = "cmd.exe" ascii
$s2 = "rundll32" ascii
$s3 = "\\AppData\\Roaming\\" ascii
$fp = "Microsoft Corporation" wide
condition:
uint16(0) == 0x5A4D and not $fp and (1 of ($x*) or 2 of ($s*))
}
Cette structure « un indicateur très spécifique OU plusieurs indicateurs faibles » est l’ossature de nombreuses règles publiques ; les conventions expliquent le nommage $x/$s/$fp.
Boucles
for itère sur des occurrences, des ensembles ou des intervalles :
rule Loop_Examples
{
strings:
$hdr = { 50 4B 03 04 }
$a = "config" ascii
$b = "payload" ascii
condition:
for any i in (1..#hdr) : ( uint16(@hdr[i] + 26) > 64 ) and
for all of ($a, $b) : ( # >= 2 ) and
for any of ($a, $b) : ( $ in (0..4096) )
}
Dans un for … of, $ désigne le motif courant, # son nombre d’occurrences et @ ses positions. Bornez les boucles par des comptages (1..#a) plutôt que par filesize : parcourir chaque offset d’un gros fichier est l’une des opérations les plus lentes qui soient.
Variables locales avec with (YARA-X)
YARA-X ajoute l’instruction with, qui nomme des valeurs intermédiaires et garde les conditions complexes lisibles :
rule With_Example
{
condition:
with = uint32(0x3C) : (
uint16(0) == 0x5A4D and pe_offset < filesize and uint32(pe_offset) == 0x00004550
)
}
Les règles utilisant with ne compilent pas avec YARA 4.x — à garder en tête si elles doivent tourner sur les deux moteurs.
Référencer d’autres règles
Un nom de règle utilisé dans une condition vaut le résultat de cette règle. Combiné aux règles private, cela permet de factoriser des contrôles communs, comme montré dans le guide de syntaxe.
L’ordre compte pour la vitesse
YARA évalue les conditions de gauche à droite et s’arrête dès que le résultat est connu. Placez d’abord les contrôles les moins chers et les plus sélectifs — magic bytes, filesize — et le travail coûteux à la fin : appels de modules, math.entropy sur de grandes plages, boucles sur de nombreuses occurrences. Le guide performance et le linter détectent les conditions où un appel de module précède les garde-fous.
Tous les exemples ci-dessus compilent avec YARA-X : testez-les dans l’éditeur. Ensuite, découvrez les modules YARA.
FAQ
- Comment vérifier le type de fichier dans une condition ?
- Lisez les magic bytes à l’offset 0, par exemple uint16(0) == 0x5A4D pour un exécutable Windows (MZ) ou uint32(0) == 0x464C457F pour un ELF. Les entiers sont lus en little-endian ; les fonctions suffixées be lisent en big-endian.
- Que signifie « N of them » ?
- Au moins N des motifs déclarés dans la règle doivent correspondre. them peut être remplacé par un ensemble comme ($a*) ou ($x1, $x2, $x3).
- YARA arrête-t-il l’évaluation d’une condition en cours de route ?
- Oui, les opérateurs booléens court-circuitent : placer d’abord les vérifications peu coûteuses (filesize, magic bytes) évite d’évaluer des expressions chères sur des fichiers qui ne peuvent pas correspondre.