Skip to content

YARA conditions: counts, offsets, loops and sets

Write YARA conditions: boolean logic, string counts and offsets, filesize and magic-byte checks, any/all/N of sets, for loops, YARA-X with and rule references.

Published 3 min read

The condition is where a rule decides. Patterns only say what was found; the condition says which combination of findings — together with properties of the file — counts as a detection. This guide goes from simple boolean logic to loops.

Boolean logic and pattern references

A pattern identifier used alone is true when the pattern matched at least once. Combine them with and, or, not and parentheses:

rule Boolean_Logic
{
    strings:
        $a = "LoaderInit" ascii
        $b = "GetProcAddress" ascii
        $c = "debug build" ascii
    condition:
        ($a and $b) and not $c
}

Precedence follows the usual rules (not binds tighter than and, which binds tighter than or), but parentheses make intent obvious to the next reader.

Counting, offsets and lengths

ExpressionMeaning
#aNumber of matches of $a
@a[i]Offset of the i-th match (1-based)
!a[i]Length of the i-th match (useful for regexes and jumps)
$a at 0x40$a matches exactly at offset 0x40
$a in (0..1024)$a matches somewhere in the first 1 KB
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
}

File size and magic bytes

filesize is the size of the scanned data in bytes; KB and MB suffixes are allowed. The uint8/16/32 and int8/16/32 functions read an integer at an offset (little-endian), and their be variants read big-endian. Together they give cheap, reliable file-type checks:

File typeCheck
Windows PEuint16(0) == 0x5A4D
ELFuint32(0) == 0x464C457F
PDFuint32(0) == 0x46445025
ZIP / OOXML / JAR / APKuint32(0) == 0x04034B50
OLE (legacy Office, MSI)uint32(0) == 0xE011CFD0
Mach-O 64-bituint32(0) == 0xFEEDFACF
rule PE_With_PE_Header_Check
{
    condition:
        uint16(0) == 0x5A4D and
        uint32(uint32(0x3C)) == 0x00004550 and
        filesize < 10MB
}

The second line follows the e_lfanew pointer at offset 0x3C and checks the PE\0\0 signature — the same test the pe module performs, without loading a module.

Sets and quantifiers

Instead of listing every pattern, quantify over a set:

ExpressionTrue when
any of themat least one pattern matched
all of themevery pattern matched
none of ($fp*)no pattern of the set matched
2 of ($x*)at least two patterns whose name starts with $x
50% of themat least half of the patterns
any of ($a, $b, $c)one of an explicit list
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*))
}

This "highly specific OR several weak indicators" structure is the backbone of many public rules; the style guide explains the $x/$s/$fp naming.

Loops

for iterates over matches, sets or ranges:

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) )
}

Inside for … of, $ stands for the current pattern, # for its count and @ for its offsets. Keep loop ranges bounded by match counts (1..#a) rather than by filesize: iterating over every offset of a large file is one of the slowest things a rule can do.

Local variables with with (YARA-X)

YARA-X adds a with statement that names intermediate values, which keeps complex conditions readable:

rule With_Example
{
    condition:
        with pe_offset = uint32(0x3C) : (
            uint16(0) == 0x5A4D and pe_offset < filesize and uint32(pe_offset) == 0x00004550
        )
}

Rules using with do not compile with YARA 4.x — keep that in mind if your rules must run on both engines.

Referencing other rules

A rule name used in a condition evaluates to that rule's result. Combined with private rules this lets you factor shared checks, as shown in the syntax guide.

Order matters for speed

YARA evaluates conditions left to right and stops as soon as the result is known. Put the cheapest, most selective checks first — magic bytes, filesize — and expensive work last: module calls, math.entropy over large ranges, loops over many matches. The performance guide and the linter detect conditions where a module call runs before the cheap guards.

Try the examples above in the editor; each one compiles with YARA-X. Next, extend conditions with parsed file formats in YARA modules.

FAQ

How do I check the file type in a YARA condition?
Read the magic bytes at offset 0, for example uint16(0) == 0x5A4D for a Windows executable (MZ) or uint32(0) == 0x464C457F for ELF. Integers are read little-endian; the be-suffixed functions read big-endian.
What does 'N of them' mean?
At least N of the patterns declared in the rule must match. You can replace them with a set such as ($a*) or ($x1, $x2, $x3).
Does YARA stop evaluating a condition early?
Yes, boolean operators short-circuit, so putting cheap checks such as filesize and magic bytes first avoids evaluating more expensive expressions on files that cannot match.