Malware and forensics
File Hashes in DFIR and the Ethics of Uploading to VirusTotal
Learn how hashes identify files, why SHA-256 replaced MD5, how NSRL filters known software, and why uploading a file to VirusTotal shares it.
Write a first YARA rule, match one harmless test file and not another, and learn why every rule needs positive and negative tests.
YARA is a pattern matching tool. You write a rule that describes what a file looks like, and YARA lists the files that fit the description. Malware analysts use it most. They write rules for strings, file layouts and byte sequences, then scan folders, disk images and memory dumps. The same mechanism works on any file you own, for example a template that must never carry an internal marker, or a config file that must never contain a token.
YARA has no idea what is dangerous. It matches what the rule says. A rule is a claim about bytes, and the rule is only as good as that claim.
The project is hosted on GitHub under VirusTotal. The official writing guide on Read the Docs is the reference for syntax. VirusTotal also maintains YARA-X, a rewrite in Rust that keeps most of the rule language. The two overlap but are not identical, so read the compatibility notes before you move a rule from one to the other.
A file hash answers one narrow question: is this exact file already known? Change one byte and the hash changes. The file hashes article covers that in detail. A YARA rule asks a wider question: does this file contain the pieces we expect? One rule can match many files that share a string, a structure or a byte pattern, even when no two of them share a hash.
The wider question is also where the trouble starts. A hash almost never matches a file it should not. A careless rule can match half of a file server. So the skill to learn is less about syntax and more about writing a rule that matches what you want and nothing else, then proving it with files you control.
Every rule has a name, then up to three sections inside braces:
rule Example_Rule {
meta:
description = "What this rule is for"
strings:
$text = "some text"
condition:
$text
}Rule names use letters, digits and underscores, and cannot start with a digit. The name appears in every match report, so pick one that tells a reader what the rule is about.
Meta holds key-value pairs for people: a description, an author, a date, a link to the research behind the rule. YARA never uses meta to decide a match. Keep it accurate anyway, because the next person to open the file will trust it.
Each string gets an identifier that starts with $. The condition refers to strings by that identifier. There are three kinds.
Text strings go in double quotes:
$marker = "LAB-TRAINING-MARKER"Modifiers change how the text is matched. nocase ignores case. wide matches UTF-16LE text, where each ASCII character is followed by a zero byte, which is common in Windows binaries. fullword requires the match to stand alone, not inside a longer word.
Hex strings go in braces and match raw bytes:
$mz = { 4D 5A }Those two bytes are the MZ signature at the start of many Windows executables. ?? matches any single byte, [2-4] allows a gap of two to four unknown bytes, and ( 90 | 91 ) matches either byte.
Regular expressions go in slashes:
$version = /LAB-[A-Z]+ v[0-9]+/Regex is the most flexible kind and the easiest to make too broad. Use a plain text or hex string whenever one can express the pattern.
The condition is a boolean expression. It combines strings and file properties into one answer: match or no match.
$marker is true when the string appears at least once.any of them and all of them cover every string in the rule. 2 of ($a, $b, $c) requires two of three.$marker at 0 requires the string at the first byte. $marker in (0..512) requires it somewhere in the first 512 bytes.#marker > 3 counts occurrences.filesize < 2KB checks the file size.uint16(0) == 0x5A4D reads the first two bytes as a little-endian integer and checks for the MZ signature.Every rule needs a condition. A rule with strings and no sensible condition is either broken or useless.
-s, YARA also prints each string that matched, with the byte offset where it was found.The strings come first and the condition comes second. A condition such as $a and $b is strict, because both strings must be found before the rule can match.
The expected output below is described rather than pasted, because exact formatting can differ between YARA versions. Compare what you see with those descriptions.
Work in an empty folder. On Windows, run the commands in Git Bash. The heredoc in step 3 does not work the same way in PowerShell.
On Debian and Ubuntu:
sudo apt install yaraOn macOS with Homebrew:
brew install yaraOn Windows, download the release zip from https://github.com/VirusTotal/yara/releases, extract it and put the executable on your PATH. The file may be called yara64.exe. Rename it to yara.exe, or copy it under that name, so the commands below work as written.
Check the install:
yara --versionYou should see a version number. This article does not pin one, so use the latest release your install method gives you.
mkdir yara-lab && cd yara-lab
printf 'LAB-TRAINING-MARKER v1\n' > match.txt
printf 'ordinary text file\n' > nomatch.txtBoth files are plain text you wrote yourself. The first should match the rule. The second should not.
The quotes around EOF matter. Without them, the shell replaces $m with an empty variable before YARA sees the file, and the rule no longer compiles.
cat > lab.yar <<'EOF'
rule Lab_Training_Marker {
meta: description = "Matches the lab training marker"
strings: $m = "LAB-TRAINING-MARKER"
condition: $m
}
EOFRead it as three parts. The meta says what the rule is for. The string says the file must contain the marker text. The condition says a file matches when that string is found at least once.
yara -s lab.yar match.txtYARA should print the rule name and the file path. Because of -s, it also prints a line for the matched string, with the byte offset and the text it found. The marker starts at the first byte of match.txt, so the offset should be 0x0.
yara lab.yar nomatch.txtExpected output: nothing. YARA prints matches only, so silence means the file did not match.
Silence only means something if the positive test worked. Run step 4 first. If step 4 printed nothing, the rule or the install is wrong, and the silence in step 5 tells you nothing.
A near miss is the most useful negative file, because it fails for one narrow reason.
printf 'LAB-TRAINING\n' > near.txt
yara lab.yar near.txtExpected output: nothing. The file has the start of the marker but not the full string, so the string is not found and the condition is false.
yara -r lab.yar .The -r flag scans the folder recursively. Expect match.txt in the output. You will probably also see lab.yar, the rule file itself. The rule file contains the marker text, so the rule matches its own source. This is a common mistake, and the next section covers it. Keep rules in a different folder from the data you scan.
The lab rule is fine for a training marker. Real rules usually need a constraint that the string alone does not give you. Suppose you only want small files that carry the marker:
rule Lab_Training_Marker_Small {
strings: $m = "LAB-TRAINING-MARKER"
condition: $m and filesize < 1KB
}Test it the same way. match.txt should match. nomatch.txt and near.txt should not. Every change to the strings or the condition is a new rule as far as testing goes, so run both test files again.
This rule looks reasonable:
rule Too_Broad {
strings: $a = "text"
condition: $a
}It will match nomatch.txt, because the sentence ordinary text file contains the word text. You can check that with the negative file you already made. A rule that matches it has failed its own test.
Patterns that match too much:
text, error, copyright or this program.{ 00 } or { 90 }.nocase on a common word.any of them over a list of strings that each appear in ordinary files.filesize < 10KB, which matches most small files./.+/.Ways to tighten a rule:
and, or with 2 of (...), so no single common string decides the result.at or in when you know where the string should appear.uint16(0) == 0x5A4D.$m before YARA reads the file. See step 3.This is the rule that matters most in this article. Every YARA rule needs both kinds of test file before anyone deploys it.
match.txt. In real work, they come from samples you are authorized to hold, under your organization's handling rules.near.txt.A rule passes when it matches every positive file and no negative file. Keep both sets next to the rule, in version control if your team uses it, and rerun them after every edit. A rule that passed the positive test but was never run against negatives has not been tested.
pe, which lets a rule test the headers and sections of a Windows executable instead of raw bytes.Malware and forensics
Learn how hashes identify files, why SHA-256 replaced MD5, how NSRL filters known software, and why uploading a file to VirusTotal shares it.