Aller au contenu

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.

Publié le 3 min de lecture

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

ExpressionSignification
#aNombre 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 :

TypeContrôle
PE Windowsuint16(0) == 0x5A4D
ELFuint32(0) == 0x464C457F
PDFuint32(0) == 0x46445025
ZIP / OOXML / JAR / APKuint32(0) == 0x04034B50
OLE (Office ancien, MSI)uint32(0) == 0xE011CFD0
Mach-O 64 bitsuint32(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 :

ExpressionVraie quand
any of themau moins un motif correspond
all of themtous 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 themau 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 pe_offset = 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.