/writing-yara-rules
Author durable YARA detection rules — meta/strings/condition anatomy, string types and modifiers, structural conditions with file magic and offsets, the PE/ELF/math/hash modules, specificity-vs-durability tuning, atom-aware performance, memory and process scanning, and YARA-X.
$ npx -y skills add trilwu/secskills --skill writing-yara-rules --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/writing-yara-rules
Context preview
The summary Claude sees to decide when to auto-load this skill.
Author durable YARA detection rules — meta/strings/condition anatomy, string types and modifiers, structural conditions with file magic and offsets, the PE/ELF/math/hash modules, specificity-vs-durability tuning, atom-aware performance, memory and process scanning, and YARA-X.
SKILL.md
writing-yara-rules.SKILL.mdname: writing-yara-rules
description: Author durable YARA detection rules — meta/strings/condition anatomy, string types and modifiers, structural conditions with file magic and offsets, the PE/ELF/math/hash modules, specificity-vs-durability tuning, atom-aware performance, memory and process scanning, and YARA-X. Use when writing a YARA rule, creating a signature for a malware family or a file/memory artifact, turning IOCs or a captured sample into a detection, or hunting for a family across a corpus with Retrohunt or an on-host scanner.
verified: 2026-07-26
Writing YARA Rules
A good YARA rule matches the malware's *nature* — its code, its structure, its unavoidable constants — not its *costume*, which is a filename or a mutable string that changes on the next build. Match the nature and the rule survives recompilation and catches the whole family; match the costume and you catch one sample once. The entire craft is choosing strings and a condition that are both specific enough to avoid false positives and durable enough to generalize.
When to Use
- Writing a YARA rule to detect a malware family, tool, or capability
- Creating a signature for a file or in-memory artifact from a known sample
- Turning IOCs or a captured specimen into a portable, testable detection
- Hunting for a family across a corpus (VirusTotal Retrohunt, LOKI/THOR, on-host scan)
- Clustering related samples by shared code, constants, or structure
- Reviewing or tuning an existing rule for false positives and scan performance
When NOT to Use
- **The detection belongs in log or SIEM telemetry, not on files or memory** —
use `writing-sigma-rules`
- **Choosing what to detect and where it should live, the strategy above rule
syntax** — use `engineering-detections`
- **You do not yet understand the sample well enough to pick durable anchors** —
use `analyzing-malware` first
- **Running the hunt rather than authoring the signature** — use `hunting-threats`
- **Packaging IOCs, attribution, and a finished report** — use
`producing-threat-intelligence`
Rule Anatomy
Three sections: `meta` (documentation, never matched on), `strings` (the patterns), `condition` (the boolean that decides a hit).
import "pe"
rule Family_Loader_v1
{
meta:
author = "analyst"
date = "2026-07-26"
description = "ExampleLoader stage-1, config-decode stub + family constants"
hash = "a1b2c3...<sha256 of the analyzed sample>"
reference = "https://internal/case/1234"
version = "1"
tlp = "amber"
strings:
$decode = { 8A 04 0? 32 0? 88 0? 4? 3B ?? 72 }
$marker = "cfg::begin" ascii
$mutex = "Global\\ExL-%08x" ascii
condition:
uint16(0) == 0x5A4D and filesize < 2MB and 2 of them
}**Meta fields that matter.** `author` and `date` for provenance; `hash` records the exact sample the rule was derived from so a future analyst can re-verify; `reference` links the case or report that justifies it; `description` states what and why. Meta is free-form text — YARA never evaluates it — so treat it as the rule's changelog. Add `version`/`tlp`/`score` fields your tooling consumes.
Strings and Modifiers
**Text strings** are the default. **Hex strings** match raw bytes and support wildcards and jumps. **Regex** is written between slashes.
strings:
$text = "Mozilla/5.0 (Custom-UA)" ascii wide
// Hex: ?? = any byte, 0? = any low nibble, [4-6] = 4 to 6 arbitrary bytes
$hex = { 55 8B EC 83 EC ?? 53 56 57 [4-6] E8 ?? ?? ?? ?? }
$re = /\/gate\/[a-f0-9]{16}\.php/ nocaseModifiers change how a string matches. Chain those that combine.
| Modifier | Effect | | --- | --- | | `nocase` | Case-insensitive text/regex match | | `wide` | Match UTF-16LE (two bytes per char) — most Windows strings | | `ascii` | Match single-byte form; pair with `wide` to match both | | `fullword` | Match only when bounded by non-alphanumeric bytes | | `xor` | Match all 256 single-byte XOR-encoded variants (keys 0x00-0xFF) | | `xor(0x01-0xff)` | XOR over a bounded key range | | `base64` / `base64wide` | Match the three base64-encoded alignments | | `private` | Match is never shown in output (`-s`/callback); still counts in `of them` |
`xor` finds strings hidden behind a one-byte key without knowing the key; `base64` catches config values embedded in encoded blobs. Both cost scan time — use them when you have evidence the encoding is present, not by default.
Building Durable Conditions
The condition is where specificity lives. Beyond `all of them` / `N of them`:
condition:
// Anchor on file type before touching strings — cheap and decisive
uint16(0) == 0x5A4D and // MZ (PE)
uint32(0) == 0x464C457F and // 0x7F 'E' 'L' 'F' (ELF)
// Offset constraints
$magic at 0 and // string must sit at offset 0
$tag in (0..1024) and // within the first 1 KB
#beacon >= 3 and // the $beacon string appears 3+ times
// Bound the search — a 40-byte dropper is not a 50 MB installer
filesize < 500KB and
// Quorum across a candidate set, resilient to any one string changing
3 of ($cfg*) and
// for..of: k of a set, with a per-match predicate
for any i in (1..#url) : ( @url[i] < 0x2000 )`#string` is the match count, `@string[i]` the offset of the i-th match, `!string[i]` its length. `for..of` expresses "any/all/N of these strings, each satisfying a condition" — the tool for rules like "an RWX marker within 100 bytes of a known stub." Anchoring on `uint*` magic first lets the engine skip non-matching files before scanning strings, which is both correctness and speed.
Specificity vs. Durability
Every rule sits on a line between too-broad (false positives) and too-narrow (catches one build). Aim for a **cluster of family-specific constants plus one structural ancho
Read more
name: writing-yara-rules description: Author durable YARA detection rules — meta/strings/condition anatomy, string types and modifiers, structural conditions with file magic and offsets, the PE/ELF/math/hash modules, specificity-vs-durability tuning, atom-aware performance, memory and process scanning, and YARA-X. Use when writing a YARA rule, creating a signature for a malware family or a file/memory artifact, turning IOCs or a captured sample into a detection, or hunting for a family across a corpus with Retrohunt or an on-host scanner. verified: 2026-07-26
Writing YARA Rules
A good YARA rule matches the malware's *nature* — its code, its structure, its unavoidable constants — not its *costume*, which is a filename or a mutable string that changes on the next build. Match the nature and the rule survives recompilation and catches the whole family; match the costume and you catch one sample once. The entire craft is choosing strings and a condition that are both specific enough to avoid false positives and durable enough to generalize.
When to Use
- Writing a YARA rule to detect a malware family, tool, or capability
- Creating a signature for a file or in-memory artifact from a known sample
- Turning IOCs or a captured specimen into a portable, testable detection
- Hunting for a family across a corpus (VirusTotal Retrohunt, LOKI/THOR, on-host scan)
- Clustering related samples by shared code, constants, or structure
- Reviewing or tuning an existing rule for false positives and scan performance
When NOT to Use
- **The detection belongs in log or SIEM telemetry, not on files or memory** —
use `writing-sigma-rules`
- **Choosing what to detect and where it should live, the strategy above rule
syntax** — use `engineering-detections`
- **You do not yet understand the sample well enough to pick durable anchors** —
use `analyzing-malware` first
- **Running the hunt rather than authoring the signature** — use `hunting-threats`
- **Packaging IOCs, attribution, and a finished report** — use
`producing-threat-intelligence`
Rule Anatomy
Three sections: `meta` (documentation, never matched on), `strings` (the patterns), `condition` (the boolean that decides a hit).
import "pe"
rule Family_Loader_v1
{
meta:
author = "analyst"
date = "2026-07-26"
description = "ExampleLoader stage-1, config-decode stub + family constants"
hash = "a1b2c3...<sha256 of the analyzed sample>"
reference = "https://internal/case/1234"
version = "1"
tlp = "amber"
strings:
$decode = { 8A 04 0? 32 0? 88 0? 4? 3B ?? 72 }
$marker = "cfg::begin" ascii
$mutex = "Global\\ExL-%08x" ascii
condition:
uint16(0) == 0x5A4D and filesize < 2MB and 2 of them
}**Meta fields that matter.** `author` and `date` for provenance; `hash` records the exact sample the rule was derived from so a future analyst can re-verify; `reference` links the case or report that justifies it; `description` states what and why. Meta is free-form text — YARA never evaluates it — so treat it as the rule's changelog. Add `version`/`tlp`/`score` fields your tooling consumes.
Strings and Modifiers
**Text strings** are the default. **Hex strings** match raw bytes and support wildcards and jumps. **Regex** is written between slashes.
strings:
$text = "Mozilla/5.0 (Custom-UA)" ascii wide
// Hex: ?? = any byte, 0? = any low nibble, [4-6] = 4 to 6 arbitrary bytes
$hex = { 55 8B EC 83 EC ?? 53 56 57 [4-6] E8 ?? ?? ?? ?? }
$re = /\/gate\/[a-f0-9]{16}\.php/ nocaseModifiers change how a string matches. Chain those that combine.
| Modifier | Effect | | --- | --- | | `nocase` | Case-insensitive text/regex match | | `wide` | Match UTF-16LE (two bytes per char) — most Windows strings | | `ascii` | Match single-byte form; pair with `wide` to match both | | `fullword` | Match only when bounded by non-alphanumeric bytes | | `xor` | Match all 256 single-byte XOR-encoded variants (keys 0x00-0xFF) | | `xor(0x01-0xff)` | XOR over a bounded key range | | `base64` / `base64wide` | Match the three base64-encoded alignments | | `private` | Match is never shown in output (`-s`/callback); still counts in `of them` |
`xor` finds strings hidden behind a one-byte key without knowing the key; `base64` catches config values embedded in encoded blobs. Both cost scan time — use them when you have evidence the encoding is present, not by default.
Building Durable Conditions
The condition is where specificity lives. Beyond `all of them` / `N of them`:
condition:
// Anchor on file type before touching strings — cheap and decisive
uint16(0) == 0x5A4D and // MZ (PE)
uint32(0) == 0x464C457F and // 0x7F 'E' 'L' 'F' (ELF)
// Offset constraints
$magic at 0 and // string must sit at offset 0
$tag in (0..1024) and // within the first 1 KB
#beacon >= 3 and // the $beacon string appears 3+ times
// Bound the search — a 40-byte dropper is not a 50 MB installer
filesize < 500KB and
// Quorum across a candidate set, resilient to any one string changing
3 of ($cfg*) and
// for..of: k of a set, with a per-match predicate
for any i in (1..#url) : ( @url[i] < 0x2000 )`#string` is the match count, `@string[i]` the offset of the i-th match, `!string[i]` its length. `for..of` expresses "any/all/N of these strings, each satisfying a condition" — the tool for rules like "an RWX marker within 100 bytes of a known stub." Anchoring on `uint*` magic first lets the engine skip non-matching files before scanning strings, which is both correctness and speed.
Specificity vs. Durability
Every rule sits on a line between too-broad (false positives) and too-narrow (catches one build). Aim for a **cluster of family-specific constants plus one structural ancho
**Security skills for Claude Code that encode the judgment professionals bring, not another wrapper around a scanner.** Offense, defense, and the reverse-engineering and code-audit core both sides share. Every skill is fact-checked against primary sources.

