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.
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
| Expression | Meaning |
|---|---|
#a | Number 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 type | Check |
|---|---|
| Windows PE | uint16(0) == 0x5A4D |
| ELF | uint32(0) == 0x464C457F |
uint32(0) == 0x46445025 | |
| ZIP / OOXML / JAR / APK | uint32(0) == 0x04034B50 |
| OLE (legacy Office, MSI) | uint32(0) == 0xE011CFD0 |
| Mach-O 64-bit | uint32(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:
| Expression | True when |
|---|---|
any of them | at least one pattern matched |
all of them | every 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 them | at 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 = 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.