1 Introduction
YARA is a pattern-matching tool that lets you describe a family of files with a small, readable rule: "a Windows executable, smaller than 2 MB, that contains these two strings and this byte sequence". It was created by Victor M. Alvarez at VirusTotal and has become the lingua franca of malware researchers, incident responders and threat intelligence teams. People often call it "grep for malware", but it is much more expressive than grep.
Why not simply block a file hash? Because a hash changes completely as soon as the attacker recompiles or modifies one byte (see the Pyramid of Pain). A YARA rule targets what stays stable across variants: a mutex name, a configuration marker, a piece of code, a typo in a ransom note. One good rule can catch hundreds of samples that have never been seen before.
3 blocks
meta, strings and condition: that is the whole rule
1 hash
is what an IOC matches. A YARA rule matches a whole family
Everywhere
VirusTotal, EDRs, sandboxes, forensic tools and memory scanners speak YARA
Where YARA is used
Threat hunting
Sweep file shares, endpoints or a malware repository for files resembling a sample from a CTI report.
Incident response
Once one infected machine is found, write a rule from the sample and scan the whole fleet to find the others.
Malware triage
Classify incoming samples automatically: sandboxes run YARA on files and on memory dumps to name the family.
Sharing intelligence
Rules are plain text: they travel inside threat intel feeds, reports and STIX indicators.
2 Rule anatomy
Here is a complete rule for a fictional "MLAB demo loader". Every part is annotated;
the sections below explain each keyword. Comments use //
(single line) or /* */ (multi-line), exactly like in C.
demo_loader.yar
import "pe" // optional: load a module (pe, elf, math, hash...)
rule MLAB_Demo_Loader : demo loader // rule name + optional tags
{
meta: // free-form key/value context, never used for matching
author = "mlab Academy"
description = "Detects the fictional MLAB demo loader (training sample)"
date = "2026-10-21"
reference = "https://academy.mlab.sh/page/yara-rules"
hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
tlp = "CLEAR"
strings: // what to look for
$s1 = "mlab-demo-loader" ascii wide nocase // text string + modifiers
$s2 = "/gate.php?id=" ascii
$s3 = "MLAB_MUTEX_7f3a" ascii wide fullword
$h1 = { 4D 4C 41 42 ?? ?? 00 [2-4] FF } // hex string: ?? = any byte, [2-4] = jump
$r1 = /beacon_[0-9]{4}\.dat/ // regular expression
$x1 = "S3cr3tC0nf1g" xor(0x01-0xff) // single-byte XOR, every key 1..255
condition: // boolean logic deciding the match
uint16(0) == 0x5A4D // file starts with "MZ" (Windows PE)
and filesize < 2MB
and pe.number_of_sections > 2
and 2 of ($s*) // at least two of the $s strings
and ($h1 or $r1 or $x1)
}
The header: name, tags, modifiers
A rule name is an identifier (letters, digits, underscore, max 128 characters, cannot start with a digit) and must be
unique within its namespace. A common convention is TYPE_Family_Detail
(e.g. MAL_ for malware, SUSP_
for suspicious, HKTL_ for hack tools). Optional tags after the colon let you filter
output (yara -t loader). A rule can be prefixed with
private (it never reports a match itself but can be used by other rules)
or global (its condition is applied to every rule in the file).
meta: context for humans and tools
Key/value pairs (string, integer or boolean) that are never used for matching. Always fill in at least author, description, date and reference, plus the hash of a sample the rule was tested on. Six months later, the analyst triaging an alert will thank you.
strings: three kinds of patterns
| Type | Syntax | Use it for |
|---|---|---|
| Text | "mlab-demo-loader" | Readable strings: URLs, mutexes, error messages, PDB paths. Escapes like \n \t \" \\ \x41 work. |
| Hex | { 4D 4C ?? [2-4] (41 | 42) } | Raw bytes and code. ?? = any byte, 4? = nibble wildcard, [2-4] = jump of 2 to 4 bytes, ( A | B ) = alternatives. |
| Regex | /beacon_[0-9]{4}\.dat/ | Variable patterns. Powerful but the slowest option: always anchor them on a few fixed characters. |
String modifiers are written after the pattern and change how it is searched:
| Modifier | Effect |
|---|---|
| ascii | Plain one-byte-per-character search. Implicit, unless wide is present. |
| wide | Characters interleaved with zero bytes (UTF-16LE), as stored by most Windows programs. Use ascii wide to search both. |
| nocase | Case-insensitive match. |
| fullword | Only matches if delimited by non-alphanumeric characters: "mal" will not hit inside "normal". |
| xor / xor(1-255) | Also searches every single-byte XOR encoding of the string, a trick malware uses to hide strings. Cannot be combined with nocase. |
| base64 / base64wide | Searches the base64-encoded forms of the string (useful in scripts and documents). |
| private | The string is matched but never printed in the output (handy for noisy helper strings). |
condition: the decision
The condition is a boolean expression that may use and, or, not, comparisons and arithmetic. A string identifier alone ($a) is true when it is found. Every string you declare must be used in the condition, otherwise the rule does not compile.
| Expression | Meaning |
|---|---|
| 2 of ($s*) | At least two strings whose name starts with $s. Also: any of them, all of them, none of ($x*). |
| #url >= 3 | # = number of occurrences of the string. |
| $mz at 0 | The string is at exactly offset 0. |
| $cfg in (0..4096) | The string appears within the first 4 KB. |
| @url[1] | @ = offset of the n-th occurrence (1-based). |
| filesize < 2MB | File size, with KB / MB suffixes. |
| uint16(0) == 0x5A4D | Reads 2 bytes at offset 0 (little-endian): the MZ magic of a Windows executable. Also uint8, uint32, uint32be… |
| for all i in (1..#a) : ( ... ) | Loop over occurrences, integers or module arrays. |
| pe.number_of_sections | Module data. pe, elf, macho, dotnet, math, hash… parse the file for you. |
3 Worked examples
We build up from a one-line rule to a realistic one. All markers are invented for training: these rules will not make your antivirus flag this page, and you can safely reproduce them by creating test files with the same strings.
Step 1: the smallest useful rule
One string, one condition. The meta block is optional; condition is the only mandatory section.
demo_hello.yar
rule Demo_Hello_Marker
{
strings:
$marker = "MLAB-ACADEMY-TEST-MARKER"
condition:
$marker
}
Test it: printf 'xx MLAB-ACADEMY-TEST-MARKER yy' > sample.txt then yara -s demo_hello.yar sample.txt. You should see the rule name, the file name and the offset of the match.
Step 2: combining strings (suspicious PowerShell)
A single keyword like DownloadString appears in plenty of legitimate admin scripts. The trick is to require a combination: something that downloads and something that executes in memory, in a small text file that is not an executable.
ps_cradle.yar
rule SUSP_PS_Download_Cradle
{
meta:
author = "mlab Academy"
description = "PowerShell script that downloads content and executes it in memory"
date = "2026-10-21"
score = 60
strings:
$dl1 = "Net.WebClient" ascii wide nocase
$dl2 = "DownloadString" ascii wide nocase
$dl3 = "Invoke-WebRequest" ascii wide nocase
$ex1 = "Invoke-Expression" ascii wide nocase
$ex2 = "IEX(" ascii wide nocase
$ex3 = "IEX (" ascii wide nocase
$b64 = "FromBase64String" ascii wide nocase
condition:
filesize < 200KB
and uint16(0) != 0x5A4D
and 1 of ($ex*)
and (1 of ($dl*) or $b64)
}
$ yara -s ps_cradle.yar invoice_update.ps1
SUSP_PS_Download_Cradle invoice_update.ps1
0x11:$dl1: Net.WebClient
0x28:$dl2: DownloadString
0x1f:$ex3: IEX (
Note the $dl* / $ex* naming: grouping strings by role with a prefix makes conditions such as 1 of ($ex*) short and readable. The score meta is a convention used by scanners such as THOR and LOKI to weight results.
Step 3: a family rule with file-type checks and a module
This is the annotated rule from section 2. It shows the classic structure of a malware-family rule: cheap structural checks first (MZ header, size, number of PE sections), then a threshold of "weak" strings (2 of ($s*)), then at least one "strong" indicator (a byte pattern, a regex or an XOR-hidden string). Requiring several independent clues is what keeps false positives low.
Step 4: private rules, counts, offsets and loops
A private rule acts as a reusable building block. Here Is_PE checks both the MZ header and the PE\0\0 signature found at the offset stored at 0x3C. The main rule then requires a configuration marker in the first 4 KB and at least three URLs that all appear after that marker.
demo_config.yar
private rule Is_PE
{
condition:
uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550
}
rule MLAB_Demo_Config_Block
{
meta:
description = "Demo: string counts, offsets and loops"
strings:
$cfg = "[mlab-cfg]"
$url = /https?:\/\/[a-z0-9.-]{4,64}\/[a-z]{3,10}\.php/ nocase
condition:
Is_PE
and $cfg in (0..4096)
and #url >= 3
and for all i in (1..#url) : ( @url[i] > @cfg[1] )
}
Bonus: hashes and entropy without strings
A rule does not need a strings section. The hash module can match an exact SHA-256 (here the hash of an empty file, as a placeholder) and the math module can flag packed or encrypted content, whose entropy is close to 8. Hash checks in YARA are convenient but slow (the whole file is hashed): for plain hash IOCs, a lookup in your EDR or on mlab.sh is faster.
import "hash"
import "math"
rule MLAB_Demo_Exact_Hash_And_Entropy
{
condition:
filesize < 5MB
and (
hash.sha256(0, filesize) == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
or math.entropy(0, filesize) >= 7.5
)
}
4 Testing & tools
The yara command line
Install it with your package manager (apt install yara, brew install yara) or from the VirusTotal/yara GitHub releases. The essential commands:
# 1. Check that a rule file compiles (no output = OK)
$ yara -w demo_hello.yar /dev/null
# 2. Scan one file and print the matching strings (-s)
$ yara -s demo_hello.yar sample.txt
Demo_Hello_Marker sample.txt
0x3:$marker: MLAB-ACADEMY-TEST-MARKER
# 3. Scan a folder recursively (-r) and print the meta block (-m)
$ yara -r -m rules/ ./samples/
# 4. Compile once with yarac, then scan many times with -C
$ yarac rules.yar rules.yarc
$ yara -C rules.yarc ./samples/suspicious.bin
The compiler is your first reviewer. These are real messages it prints, and each one points at a classic mistake covered in section 5:
error: rule "bad" in bad.yar(1): unreferenced string "$b"
warning: rule "slow" in slow.yar(1): string "$a" may slow down scanning
warning: rule "slow" in slow.yar(1): $r contains .*, .+ or .{x,} consider using .{,N}, .{1,N} or {x,N} with a reasonable value for N
YARA-X, the next generation
YARA-X is a full rewrite of YARA in Rust by the same team, stable since its 1.0 release in 2025 and positioned as the future of the project (classic YARA 4.x now mostly receives fixes). It keeps the same rule language with very few incompatibilities, gives much clearer error messages and is faster on complex rules. Every rule on this page compiles with both YARA 4.5 and YARA-X.
# YARA-X: the Rust rewrite of YARA by VirusTotal (command: yr)
$ yr check rules/ # compile-check every rule file
$ yr fmt demo_loader.yar # normalise indentation and layout
$ yr scan demo_loader.yar ./samples/ # scan, same rule language
A testing routine that actually works
1. True positives
Keep the samples the rule was written for (in a password-protected archive, outside synced folders) and check that every one of them matches.
2. Goodware
Scan a large set of clean files: C:\Windows\System32, /usr/bin, popular installers, Office documents. Any hit is a false positive to fix before deployment.
3. Retro-hunt
Platforms such as VirusTotal Retrohunt, abuse.ch YARAify or MalwareBazaar run your rule against millions of past samples and show what else it would catch.
4. Version it
Store rules in Git, run yara or yr check in CI, and bump the date/modified meta each time you change one.
YARA-L is not YARA
Google Security Operations (formerly Chronicle) uses a language called YARA-L. Despite the name and a similar meta / events / condition layout, it matches log events in a SIEM, not bytes in files. If you want a portable way to write log detections, look at Sigma rules instead.
Start from a real sample on mlab.sh
Before writing a rule, analyze the suspicious file: hashes, strings and verdicts give you the stable markers your YARA rule should target.
Analyze a file on mlab.sh5 Common pitfalls
Mistakes that cause false positives (or misses)
Matching on library or compiler strings
Strings like "This program cannot be run in DOS mode", OpenSSL error messages or Go runtime symbols are present in thousands of legitimate binaries. Pick strings that are specific to the author, not to the toolchain.
any of them on generic strings
One weak string is never enough. Combine several clues, add a file-type check (uint16(0) == 0x5A4D) and a filesize bound.
Forgetting wide
Windows programs often store strings in UTF-16. If you copied a string from a strings/sandbox report and the rule does not match, try ascii wide.
Scanning packed or compressed files
YARA sees raw bytes. Strings inside a ZIP, an Office document (which is a ZIP) or a UPX-packed executable are not visible until you unpack them, or scan the process memory instead.
A rule that triggers on its own threat report
Text-only rules (no file-type check) also match PDFs, web pages and rule files that quote the strings. Your antivirus quarantining your own rules repository is a classic.
Performance tips
YARA first extracts short atoms (up to 4 bytes) from each string and searches them all at once with the Aho-Corasick algorithm; only then does it verify full strings and evaluate conditions. Rules that give it poor atoms slow down every scan, not just their own.
Use strings of at least 4 distinctive bytes
Avoid { 00 00 00 00 }, { ?? ?? 00 } or "abc": the compiler warns "may slow down scanning" for a reason.
Bound your regular expressions
Never .* or .+; use .{1,64}. Start the regex with literal characters so YARA can extract an atom.
Put cheap checks first in the condition
Conditions short-circuit: filesize and uint16(0) before loops, math.entropy or hash calls avoids expensive work on non-matching files.
Prefer hex strings with fixed bytes at the start
Wildcards and jumps at the beginning of a hex string leave nothing to anchor on. Start and end with known bytes and keep jumps small.
Compile large rule sets
With thousands of rules, compiling once with yarac (or loading them once in your scanner) saves the parsing time on every run.
6 Going further
Community rule sets
Do not start from scratch: read good rules to learn the style, then reuse them. Always check the licence before using rules in a commercial product.
Neo23x0/signature-base
Florian Roth's large, well-maintained collection used by the LOKI and THOR scanners. Released under the Detection Rule License (DRL).
YARA-Forge
Aggregates, tests and de-duplicates dozens of public repositories into ready-to-use packages (core, extended, full) with consistent metadata.
elastic/protections-artifacts
The YARA rules Elastic Defend uses in production, a great reference for clean, low-false-positive style.
abuse.ch YARAify
Community platform to share rules and scan files against them, connected to MalwareBazaar.
How YARA fits with the rest of your detection
| Tool | Looks at | Typical question |
|---|---|---|
| YARA | Files and memory | "Is this file part of family X?" |
| Sigma | Logs (SIEM) | "Did a process behave like technique Y?" |
| Suricata / Snort | Network traffic | "Is a host talking to infrastructure Z?" |
The three are complementary: a file caught by YARA gives you IOCs (C2 domains, hashes) for network and log rules, and tagging every rule with its MITRE ATT&CK technique in the meta block lets you measure your coverage. To understand what the families you detect actually do, see malware and threat families.
Related Modules
Sigma rules for log detection
Write vendor-neutral Sigma detection rules in YAML and convert them to Splunk, Elastic or Microsoft Sentinel KQL with sigma-cli.
What is a file hash?
Understanding digital fingerprints: how a hash uniquely identifies a file.
Dynamic analysis in a sandbox
What a sandbox records, how to read its report, why evasion makes a clean verdict unreliable, and how to turn results into detections.
Source: mlab Academy — Cybersecurity Awareness Platform
URL: https://academy.mlab.sh//page/yara-rules
Module: Writing YARA rules — Detection Engineering
Disclaimer: This content is for awareness purposes only.