
Yara Rule Authoring
- 14 installs
- 7 repo stars
- Updated May 20, 2026
- daemon-blockint-tech/agentic-enteprises-skill
Author, review, optimize, and false-positive-debug YARA-X detection rules for malware across PE, script, npm, Office, Chrome extensions, and Android DEX.
About
Guides authoring and optimizing YARA-X detection rules covering string and atom quality, condition short-circuiting, legacy YARA migration, yarGen/FLOSS workflows, and goodware validation. Used when writing malware detection signatures and debugging false positives.
- Convert IOCs and threat intel into maintainable YARA-X signatures
- Debug false positives and tune any-of/all-of condition logic
Yara Rule Authoring by the numbers
- 14 all-time installs (skills.sh)
- Ranked #1,622 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Jul 27, 2026 (Skillselion catalog sync)
npx skills add https://github.com/daemon-blockint-tech/agentic-enteprises-skill --skill yara-rule-authoringAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 14 |
|---|---|
| repo stars | ★ 7 |
| Last updated | May 20, 2026 |
| Repository | daemon-blockint-tech/agentic-enteprises-skill ↗ |
What it does
Author, review, optimize, and false-positive-debug YARA-X detection rules for malware across PE, script, npm, Office, Chrome extensions, and Android DEX.
Files
YARA Rule Authoring
Write YARA-X rules that catch the intended family without drowning analysts in false positives.
Target runtime: YARA-X (Rust successor to legacy YARA). Install:brew install yara-xorcargo install yara-x. Essential CLI:yr check,yr scan,yr fmt,yr dump.
When to Use
- Write, review, or optimize YARA-X rules for malware, hacktools, webshells, or supply-chain artifacts
- Convert IOCs or threat intel into maintainable signatures
- Debug false positives or tune
any of/all oflogic - Migrate legacy YARA rules to YARA-X stricter validation
- Author Chrome extension (
crx) or Android DEX (dex) module rules - Prepare rulesets for production, YARA-CI, or VirusTotal retrohunt
When NOT to Use
- Full malware reverse engineering, disassembly, or unpacker development →
reverse-engineer - Network intrusion detection (Suricata, Snort, Zeek) → network security / SOC tooling skills
- Memory forensics with Volatility or live RAM analysis →
digital-forensics-analyst - Hash-only blocklists with no pattern logic → use IOC lists or EDR hash feeds
- Enterprise security strategy, GRC, or audit evidence →
cybersecurity,compliance-engineer - Embedding YARA in CI/CD pipelines as the primary task →
devsecops - Adversarial LLM or application red team →
ai-redteam
Related skills
| Need | Skill |
|---|---|
| Security program, IR strategy, detection philosophy | cybersecurity |
| SIEM/EDR rules, logging, control implementation | information-security-engineer |
| Audit evidence, control mapping, CCM | compliance-engineer |
| Pipeline gates, artifact scanning, SBOM | devsecops |
| Binary RE, unpacking, patch diff | reverse-engineer |
| SOC alert triage and detection tuning (non-YARA) | defensive-security-analyst, soc-analyst |
| Proactive threat hunts and ATT&CK campaigns | threat-hunter |
| CTI briefs, IOC/TTP production | cti-analyst |
| Adversarial AI / prompt injection testing | ai-redteam |
| Disk imaging and forensic reports | digital-forensics-analyst |
Core principles
1. Atoms matter — YARA extracts 4-byte subsequences for Aho-Corasick prefilter. Strings with repeated bytes, common sequences, or under 4 bytes force expensive bytecode verification on too many files. 2. Family-specific, not category-generic — "Detects ransomware" matches everything and nothing. Target identifiable mutexes, PDB paths, C2 paths, or structural markers for one family or campaign. 3. Goodware before production — Validate against ecosystem-appropriate clean corpus (VT goodware for PE; top npm packages for JS; marketplace extensions for CRX). 4. Short-circuit cheap checks first — filesize → magic bytes → strings → modules. 5. Metadata is documentation — Name, description, author, reference, and date survive personnel changes.
Essential toolkit
| Tool | Purpose |
|---|---|
| yarGen | Candidate strings from samples (--excludegood); always yr check output |
| FLOSS | Obfuscated/stack strings when yarGen fails |
| yr | yr check, yr scan -s, yr fmt, `yr dump -m pe\ |
| YARA-CI / VT retrohunt | Goodware corpus testing before deploy |
Core workflows
1. Scope samples and file type
1. Collect 3+ variants when possible (single-sample rules are brittle) 2. Check packing: entropy > 7.0 or few strings → unpack or target packer/structure, not encrypted layer 3. Choose platform path: PE magic / JS / Office ZIP / import "crx" / import "dex"
See `references/yara_x_scope_and_tooling.md` for install, CLI workflow, and migration.
2. Extract and filter strings
1. Run yarGen or FLOSS on unpacked samples 2. Reject ~80% of yarGen output: API names, C:\Windows\, format strings, require/fetch alone 3. Prefer gold tier: mutex names, PDB paths, stack strings; silver: C2 paths, config markers
See `references/string_selection_and_atoms.md` for decision trees and modifiers.
3. Write rule with ordered conditions
rule MAL_Win_Example_Loader_Jan26
{
meta:
description = "Detects Example loader via unique mutex and config path"
author = "Team <team@example.com>"
reference = "https://example.com/analysis"
date = "2026-01-15"
strings:
$mutex = "Global\\ExampleMutex" ascii wide
$cfg = "/api/beacon/check" ascii
condition:
filesize < 10MB and
uint16(0) == 0x5A4D and
all of ($mutex, $cfg)
}Condition order: filesize → magic bytes → string matches → module calls (pe, crx, dex).
See `references/conditions_and_performance.md` for atom theory, regex bounds, and loops.
4. Validate and test
yr check rule.yar && yr fmt -w rule.yar
yr scan -s rule.yar malware_samples/ # must match all targets
yr scan -c rule.yar goodware_corpus/ # must be zeroFP flow: yr scan -s on false positive → identify matching string → tighten, exclude vendor, or pivot to structure.
See `references/testing_goodware_and_fp_debugging.md` for corpus selection and investigation.
5. Platform modules (when applicable)
- Chrome extensions:
import "crx"— permissions,permhash()(v1.11.0+). Alwayscrx.is_crxfirst. - Android:
import "dex"—dex.contains_class(),contains_method(),contains_string(). API differs from legacy YARA dex module.
See `references/platform_modules_pe_crx_dex.md`.
6. Deploy
1. Naming: {CATEGORY}_{PLATFORM}_{FAMILY}_{VARIANT}_{DATE} (e.g. MAL_Win_Emotet_Loader_Jan26) 2. Peer review + quality checklist 3. Monitor production FPs; version rules in Git with full metadata
See `references/style_metadata_and_deployment.md`.
Decision trees (quick reference)
Is this string good enough?
< 4 bytes? → reject
Repeated bytes (0000, 9090)? → reject
API name or common path? → reject
Unique to family? → use
Common across malware? → combine with family-specific markerany of vs all of
- Individually unique strings →
any of ($a*) - Common strings that are suspicious only together →
all of ($a*) - Mixed confidence →
all of ($core_*) and any of ($variant_*)
Production lesson: any of ($network_*) with fetch, axios, http matches most web apps — require credential path and exfil destination and network call.
When strings fail → pivot
Use yr dump -m pe for sections, imports, imphash, resources; math.entropy() on sections; packer signatures. If nothing unique remains, YARA alone may not be the right control.
Legacy YARA migration
yr check --relaxed-re-syntax rules/ # diagnostic only
yr check rules/ # fix until cleanCommon fixes: escape \{ in regex; base64 strings need 3+ chars; @a[-1] → @a[#a - 1]; remove duplicate modifiers.
Rationalizations to reject
| Thought | Reality |
|---|---|
| "yarGen gave me these strings" | yarGen suggests; you validate each string |
| "It works on 10 samples" | Test goodware corpus before deploy |
| "I'll tighten after FPs" | FPs burn trust — write tight rules upfront |
| "This API name is malicious" | Legitimate software uses the same APIs |
| "any of them is fine" | Common strings + any = FP flood |
Quality checklist
- [ ] Name follows
{CATEGORY}_{PLATFORM}_{FAMILY}_{VARIANT}_{DATE} - [ ] Description starts with "Detects" and states distinguishing feature
- [ ] Required meta: author, reference, date
- [ ] Strings ≥4 bytes with good atoms; no unbounded regex (
.*) - [ ] Condition:
filesizeand magic bytes before modules - [ ] Matches all target samples; zero goodware matches
- [ ]
yr checkandyr fmt --checkpass - [ ] Peer review completed
When to load references
| Topic | Reference |
|---|---|
| YARA-X install, CLI, migration, toolkit | references/yara_x_scope_and_tooling.md |
| String quality, types, modifiers | references/string_selection_and_atoms.md |
| Atoms, condition order, regex, loops | references/conditions_and_performance.md |
| PE, macOS, JS, crx, dex patterns | references/platform_modules_pe_crx_dex.md |
| Goodware testing, FP debugging | references/testing_goodware_and_fp_debugging.md |
| Naming, metadata, deployment | references/style_metadata_and_deployment.md |
Conditions and Performance
Table of contents
1. How scanning works 2. Condition ordering 3. Slow pattern killers 4. Regex and loops 5. Modules vs raw bytes 6. Platform short-circuits 7. Optimization checklist
How scanning works
Three phases:
1. Atom extraction — 4-byte subsequences from strings/hex 2. Aho-Corasick — fast multi-pattern atom search 3. Bytecode verification — full string/condition evaluation per hit
Goal: maximize Phase 2 selectivity so Phase 3 runs rarely.
Good atoms
- Rare in target file types
- No wildcards in the 4-byte window
- Not common PE/JS noise (
MZ,This program,http)
String: "MalwareConfig" → atom "Malw"
String: { 4D 5A ?? ?? 50 45 } → limited atom options due to wildcardsCondition ordering
Always order from cheapest to most expensive:
condition:
filesize < 10MB and // 1. Instant
uint16(0) == 0x5A4D and // 2. Magic bytes
all of ($core_*) and // 3. Strings (if good atoms)
pe.imports("kernel32.dll", "Sleep") and // 4. Module (moderate)
pe.imphash() == "abc123..." // 5. Expensive hashesFailed cheap checks skip expensive work.
Rule of thumb: If the condition block exceeds ~5 lines of logic, consider splitting into focused rules.
Slow pattern killers
| Anti-pattern | Why | Fix |
|---|---|---|
| Strings < 4 bytes | No useful atoms | Lengthen or use hex with context |
{ 90 90 90 90 } NOP sleds | Atom 9090 everywhere | Add surrounding opcode context |
/https?:\/\/.*/ | Unbounded regex | Bound length and charset |
Leading wildcards { ?? ?? 4D 5A } | Unstable atoms | Put fixed bytes first |
nocase on common strings | Doubles atoms | Remove or narrow string |
| Module call without file filter | Parses non-target files | Magic bytes + filesize first |
Regex and loops
Regex rules
- Bound repetitions:
.{0,30}not.* - Anchor to distinctive literals
- Validate with
yr check(YARA-X strict escapes)
Loops
// GOOD: bound by filesize and index range
filesize < 100KB and
for all i in (1..#a) : ( @a[i] < 1000 )
// BAD: unbounded #a on large files
for all i in (1..#a) : ( ... )Modules vs raw bytes
Need imphash, authenticode, rich header?
→ PE module (too complex to hand-parse)
Only magic bytes or simple offsets?
→ uint16/uint32 — faster, no module load
Chrome extension permissions?
→ crx module — fragile to parse manifest as strings
LNK target paths?
→ lnk modulePrinciple (Neo23x0): If uint32() suffices, do not load a module.
Platform short-circuits
| Platform | Pattern |
|---|---|
| Windows PE | filesize < 10MB and uint16(0) == 0x5A4D |
| Mach-O 64 | uint32(0) == 0xFEEDFACF |
| Universal binary | uint32(0) == 0xCAFEBABE or uint32(0) == 0xBEBAFECA |
| OOXML | uint32(0) == 0x504B0304 |
| JavaScript | filesize < 1MB (no magic) |
| Chrome CRX | crx.is_crx |
| Android DEX | dex.is_dex |
Optimization checklist
- [ ]
filesizelimit appropriate for file type - [ ] Magic bytes or
crx.is_crx/dex.is_dexbefore modules - [ ] All strings ≥ 4 bytes with non-trivial atoms
- [ ] No unbounded regex; braces escaped for YARA-X
- [ ]
nocase/wideonly with sample evidence - [ ] Loops bounded by
filesizeand index range - [ ]
time yr scanacceptable on representative corpus - [ ] Split mega-rules into family-specific rules if still slow
Platform Modules: PE, CRX, DEX
Table of contents
1. Windows PE 2. macOS Mach-O 3. JavaScript and Office 4. Chrome extensions (crx) 5. Android DEX (dex) 6. When strings fail
Windows PE
Short-circuit:
condition:
filesize < 10MB and
uint16(0) == 0x5A4D and
...Good PE indicators: unique mutex, PDB path, overlay watermark, rare section names combined with strings.
Avoid: pe.imports() alone on common APIs; imphash without family context unless clustered.
Explore:
yr dump -m pe sample.exe --output-format yamlmacOS Mach-O
No dedicated Mach-O module—use magic bytes plus strings:
// Mach-O 64-bit
uint32(0) == 0xFEEDFACF
// Universal (fat) binary
uint32(0) == 0xCAFEBABE or uint32(0) == 0xBEBAFECAUseful strings: LaunchAgents paths, CGEventTapCreate, security find-generic-password, SSH tunnel messages.
Example pattern (multi-category):
rule SUSP_Mac_ProtonRAT
{
strings:
$lib1 = "SRWebSocket" ascii
$lib2 = "SocketRocket" ascii
$beh1 = "SSH tunnel not launched" ascii
$beh2 = "Keylogger" ascii
condition:
(uint32(0) == 0xFEEDFACF or uint32(0) == 0xCAFEBABE) and
any of ($lib*) and any of ($beh*)
}JavaScript and Office
| Target | Filter | Notes |
|---|---|---|
| npm / Node | filesize < 1MB, package.json markers | Avoid lone postinstall |
| VS Code ext | Uncommon activationEvents, hidden file access | Not vscode.workspace alone |
| OOXML | uint32(0) == 0x504B0304 | Macro auto-exec strings, not generic VBA keywords |
JS decision tree: see string_selection_and_atoms.md.
Chrome extensions (crx)
Requires: YARA-X v1.5.0+ (permhash() v1.11.0+)
import "crx"
rule SUSP_CRX_Debugger
{
condition:
crx.is_crx and
for any perm in crx.permissions : (perm == "debugger")
}Key fields
| Field | Use |
|---|---|
crx.is_crx | Always first |
crx.permissions | High-risk: debugger, nativeMessaging, <all_urls> |
crx.host_permissions | MV3 host access |
crx.permhash() | Permission-set clustering (v1.11.0+) |
crx.signatures[].verified | Signature state |
Red-flag combinations
nativeMessaging+ broad host access → local binary bridge + web reachwebRequest+webRequestBlocking+cookies→ interception/theft potentialdebuggeron non-dev extensions → full traffic visibility
yr dump -m crx extension.crx --output-format yamlAndroid DEX (dex)
Requires: YARA-X v1.11.0+
Important: YARA-X dex API is not compatible with legacy YARA's dex module—rewrite rules.
import "dex"
rule SUSP_DEX_DynamicLoading
{
condition:
dex.is_dex and
dex.contains_class("Ldalvik/system/DexClassLoader;")
}Key APIs
| API | Purpose |
|---|---|
dex.is_dex | File type gate |
dex.contains_string(pat) | String search |
dex.contains_class(pat) | Class descriptor search |
dex.contains_method(pat) | Method name search |
dex.header.* | Version, checksum, signature |
dex.checksum() / dex.signature() | Tamper detection vs header |
Red flags
- Single-letter class names (obfuscation)
DexClassLoader/ reflection loaders- Encrypted asset references combined with dynamic load
- Checksum mismatch vs header
yr dump -m dex classes.dex --output-format yamlWhen strings fail
String extraction failed?
├─ High entropy sections → math.entropy() on section
├─ Import patterns → pe.imphash() clustering
├─ PE structure → section names, sizes, characteristics
├─ CRX → crx.permissions / permhash()
├─ DEX → dex.contains_class / method patterns
└─ Nothing unique → consider other controls (hash, behavior, sandbox)Packed samples: entropy > 7.0 or very few strings → unpack first or detect packer, not encrypted payload strings.
String Selection and Atoms
Table of contents
1. Quality checklist 2. Value tiers 3. String types 4. Modifiers and cost 5. Reject list 6. Hex vs text vs regex 7. JavaScript and supply chain 8. Multi-category grouping
Quality checklist
Before adding any string:
Is this string good enough?
├─ At least 4 bytes?
├─ Four consecutive non-trivial bytes (not 0000, 9090, FFFF)?
├─ NOT an API name (VirtualAlloc, CreateRemoteThread)?
├─ NOT a common path (C:\Windows\, cmd.exe)?
├─ NOT a format string (%s, Error: %s)?
├─ Would match Windows system or ecosystem goodware?
│ └─ YES → reject or add exclusion
├─ Unique to this malware family?
│ └─ YES → use
└─ Shared across malware families?
└─ MAYBE → combine with family-specific markerExpert heuristic: If you need more than ~6 strings, you are likely over-fitting one sample.
Value tiers
| Tier | Examples | Notes |
|---|---|---|
| Gold | Mutex names, stack strings, PDB paths | Almost always unique |
| Silver | C2 paths, config markers, custom protocol headers | Usually unique |
| Bronze | Campaign IDs, rare error messages | Require combination with gold/silver |
yarGen output: Expect to discard ~80% of suggested strings after manual review.
String types
Text
$s = "Hello World" // ASCII (default)
$s = "Hello" wide // UTF-16LE
$s = "Hello" ascii wide // Either encoding
$s = "hello" nocase // Doubles atoms — use only when proven necessary
$s = "token" fullword // Word boundariesHex
$h = { 4D 5A 90 00 } // Exact
$w = { 4D 5A ?? ?? 50 45 } // Single-byte wildcards
$j = { 4D 5A [2-4] 50 45 } // Bounded jump — always bound jumps
$a = { 4D 5A ( 90 00 | 00 00 ) } // AlternativesPrefer hex over regex when the pattern is fixed bytes.
Regular expressions
// GOOD: bounded
$url = /https?:\/\/[a-z0-9]{5,50}\.onion/
// BAD: unbounded — catastrophic backtracking
$bad = /https?:\/\/.*/YARA-X: Escape literal braces: /config\{key\}/. Run yr check on every regex.
Anchoring: Regex without a 4+ byte literal substring may evaluate at every offset. Anchor to a distinctive literal: /mshta\.exe http:\/\/.../ not /http:\/\/.../ alone.
Modifiers and cost
| Modifier | Cost | When to use |
|---|---|---|
ascii | None | Default |
wide | Low | Confirmed UTF-16 in samples |
nocase | Doubles atoms | Confirmed case variance only |
fullword | Low | Avoid substring FP |
xor(0x00-0xFF) | Very high | Almost never — find real encoding |
xor(0x41) | Moderate | Known single-byte key |
base64 | Moderate | Payload encoding; 3+ chars in YARA-X |
private | None | Helper patterns (YARA-X 1.3.0+) |
Kaspersky Applied YARA: do not usenocaseorwidewithout evidence they vary in your corpus.
Reject list
API names (PE)
// REJECT — present in most executables
"VirtualAlloc", "CreateRemoteThread", "WriteProcessMemory", "NtCreateThreadEx"Use hex at call sites plus behavioral strings, not import names alone.
Common paths and binaries
// REJECT
"C:\\Windows\\System32", "cmd.exe", "powershell.exe", "\\AppData\\Local"Format strings
// REJECT
"%s", "%d", "Error: %s"Prefer unique messages: "Beacon initialized: %s:%d with key %08X".
JavaScript (npm/browser)
// REJECT alone
"require", "fetch", "axios", "Buffer", "crypto", "process.env"Require combinations: specific env var names + exfil URL + suspicious hook.
Hex vs text vs regex
| Need | Use |
|---|---|
| Exact ASCII/Unicode | Text with ascii / wide |
| Fixed byte sequence | Hex |
| Variation in bytes | Hex with ?? or bounded jumps |
| Structured text (URL, path) | Bounded regex |
| Unknown XOR layer | Avoid broad xor(0x00-0xFF); unpack or narrow key |
JavaScript and supply chain
Writing a JavaScript rule?
├─ npm package? → package.json hooks, postinstall, exfil + env access
├─ Browser extension? → crx module (Chrome) or manifest strings
├─ Standalone JS? → obfuscation markers (_0x, eval+atob chains)
└─ Bundled/minified? → URLs, magic constants (not mangled identifiers)Good JS indicators: ERC-20 selectors { 70 a0 82 31 }, zero-width steganography bytes, campaign-specific domains.
Multi-category grouping
strings:
$lib1 = "SRWebSocket" ascii
$lib2 = "SocketRocket" ascii
$beh1 = "SSH tunnel" ascii
$beh2 = "keylogger" ascii nocase
condition:
filesize < 10MB and
(uint32(0) == 0xFEEDFACF or uint32(0) == 0xCAFEBABE) and
any of ($lib*) and any of ($beh*)Require evidence from multiple indicator classes when single categories are weak alone.
Style, Metadata, and Deployment
Table of contents
1. Naming convention 2. Category prefixes 3. Required metadata 4. Optional metadata 5. Rule structure 6. Deployment practices 7. Common mistakes
Naming convention
{CATEGORY}_{PLATFORM}_{FAMILY}_{VARIANT}_{DATE}| Component | Examples |
|---|---|
| CATEGORY | MAL, HKTL, WEBSHELL, EXPL, SUSP, GEN |
| PLATFORM | Win, Lnx, Mac, Android, CRX, Multi |
| FAMILY | Emotet, CobaltStrike, LockBit |
| VARIANT | Loader, Beacon, Config |
| DATE | Jan26, May25 (MonthYear) |
Good: MAL_Win_Emotet_Loader_Jan26, SUSP_CRX_HighRiskPerms_Jan26
Bad: malware_detector, rule1, EMOTET_RULE (missing category/platform/date)
Optional classifiers
Append when relevant: APT_, CRIME_, RANSOM_, RAT_, MINER_, STEALER_, LOADER_, C2_
Category prefixes
| Prefix | Meaning | When |
|---|---|---|
MAL_ | Confirmed malware | Verified malicious |
HKTL_ | Hacking tool | Dual-use (Mimikatz, CS) |
WEBSHELL_ | Web shell | PHP/ASP/JSP backdoors |
EXPL_ | Exploit | Exploit code/shellcode |
SUSP_ | Suspicious | Lower confidence; expect tuning |
PUA_ | Potentially unwanted | Adware, bundleware |
GEN_ | Generic | Broad category; high FP risk |
Use SUSP_ when description would say "might be" — confidence belongs in the prefix, not vague metadata.
Required metadata
meta:
description = "Detects Emotet loader via unique mutex and C2 path"
author = "Team Name <team@example.com>"
reference = "https://example.com/analysis-report"
date = "2026-01-15"Description rules
- Start with "Detects"
- 60–400 characters
- State what and how (distinguishing feature)
// Good
description = "Detects CobaltStrike beacon via watermark bytes in PE overlay"
// Bad
description = "Malware"
description = "This rule detects..."
description = "Might be malware" // use SUSP_ prefix insteadOptional metadata
meta:
hash = "sha256:abc123..."
hash = "sha256:def456..." // repeatable field
score = 75
malware_type = "trojan"
tlp = "white"
comment = "FP fix 2026-02: excluded VendorX updater path"Use comment for FP history and tuning notes.
Rule structure
rule MAL_Win_Example_Loader_Jan26
{
meta:
description = "Detects Example loader via mutex and config path"
author = "Team <team@example.com>"
reference = "https://example.com/analysis"
date = "2026-01-15"
strings:
$mutex = "Global\\ExampleMutex" ascii wide
$cfg = "/api/beacon/check" ascii
condition:
filesize < 10MB and
uint16(0) == 0x5A4D and
all of ($mutex, $cfg)
}String prefixes: group related strings ($a*, $b*, $network_*) for readable conditions.
Private helpers (YARA-X 1.3.0+):
private $helper = "internal_marker"Deployment practices
1. Repository layout — group by platform or campaign; avoid monolithic unnamed files 2. Pre-merge gates — yr check, goodware scan, peer review 3. Versioning — Git tags for ruleset releases; changelog for FP fixes 4. Monitoring — track match rate and analyst dismissals per rule ID 5. Retirement — deprecate rules that FP persistently; document in commit message
Production quality bar
| Check | Requirement |
|---|---|
| Naming | Full convention with date |
| Metadata | All required fields |
| Testing | 100% malware set, 0 goodware |
| Performance | filesize + magic before modules |
| Documentation | Reference URL to analysis |
Common mistakes
| Mistake | Bad | Good |
|---|---|---|
| API as indicator | "VirtualAlloc" | Hex at call site + unique mutex |
| Unbounded regex | /https?:\/\/.*/ | Bounded charset and length |
| No file filter | pe.imports(...) first | uint16(0)==0x5A4D and filesize... |
| Short strings | "abc" | 4+ bytes |
| Unescaped braces (YARA-X) | /config{key}/ | /config\{key\}/ |
| Generic description | "Malware" | "Detects X via Y" |
Testing, Goodware, and FP Debugging
Table of contents
1. Testing philosophy 2. Validation workflow 3. Goodware corpora 4. Interpreting matches 5. False positive investigation 6. Sample coverage 7. CI and retrohunt
Testing philosophy
Every production rule requires three validations:
| Stage | Pass criteria |
|---|---|
| Positive | Matches all intended malware samples / variants |
| Negative | Zero matches on ecosystem goodware |
| Edge | Handles packing, older variants, related families without broad FPs |
Untested rules cause alert fatigue (FP) or missed detections (FN).
Validation workflow
Write rule
→ yr check (fix until clean)
→ yr fmt --check
→ scan malware set (widen if misses)
→ scan goodware (tighten if hits)
→ peer review
→ deploy + monitor FPsyr check rule.yar
yr fmt -w rule.yar
yr scan -s rule.yar malware_samples/
yr scan -c rule.yar goodware_corpus/ # expect 0Migration: yr check --relaxed-re-syntax is diagnostic only—fix issues, do not depend on relaxed mode in production.
Goodware corpora
VT goodware is PE-heavy. Supplement for your target:
| Rule target | Minimum goodware |
|---|---|
| PE | VT goodware; Chrome, Firefox, Office, Python installer |
| JavaScript | lodash, react, express, webpack, electron |
| npm | Top 100 weekly downloads + packages with postinstall scripts |
| Chrome CRX | Top marketplace extensions by installs |
| Android DEX | Popular benign APKs from trusted vendors |
Expert baseline (Kaspersky Applied YARA): include Chrome, Firefox, and Adobe Reader for PE rules.
Interpreting matches
Goodware matches?
├─ 1–2 files → investigate; exclusion or tighten string
├─ 3–5 files → pattern too common; new indicators
├─ 6+ files → rule fundamentally broken; restart
└─ Single vendor only → not $fp_vendor or replace stringVirusTotal retrohunt
1. Upload rule to VT Intelligence hunting 2. Run against Goodware corpus 3. Treat every match as potential FP until explained
| VT goodware matches | Action |
|---|---|
| 0 | Proceed toward deploy |
| 1–2 | Review, exclude or tighten |
| 3–5 | Replace indicators |
| 6+ | Rewrite approach |
yarGen goodware DB
python db-lookup.py -f strings.txt # yarGen install pathQuery candidate strings before committing to a rule.
False positive investigation
FP reported
│
├─ 1. yr scan -s rule.yar false_positive.file
│ Which string(s) matched?
│
├─ 2. Legitimate library/vendor?
│ → not $fp_vendor_string
│
├─ 3. Common dev pattern?
│ → replace with more specific indicator
│
├─ 4. Multiple weak strings via any of?
│ → switch to all of + unique marker
│
└─ 5. Technique-level only (e.g. "uses VirtualAlloc")?
→ target family-specific implementation detailDocument FP fixes in rule changelog or meta comment field—future maintainers need context.
Sample coverage
| Samples | Confidence | Use case |
|---|---|---|
| 1 | Low (fragile) | Emergency hunt; refine quickly |
| 3–5 | Medium | Standard family rule |
| 10+ | High | Long-lived production rule |
Gather variants: imphash/ssdeep pivot on VT, C2/mutex pivot, campaign time window, unpack siblings.
Packed check:
yr dump -m math sample.exe --output-format yaml | grep entropy
strings sample.exe | wc -l| Signal | Action |
|---|---|
| Entropy > 7.0 | Unpack or detect packer |
| < 50 strings | Unpack first |
| UPX/MPRESS sig | upx -d or packer rule |
CI and retrohunt
- YARA-CI: automated goodware testing on PR
- Git: version rules with mandatory meta fields
- Production: monitor FP rate per rule; disable or fork hot rules quickly
Hunting rules: same quality bar as detection—hunting rules become production rules without rework.
YARA-X Scope and Tooling
Table of contents
1. Why YARA-X 2. Install and CLI 3. Development cycle 4. yr dump for exploration 5. Legacy migration 6. YARA-X features 7. External resources
Why YARA-X
YARA-X is the Rust-based successor to legacy YARA. It powers VirusTotal production scanning and is the recommended runtime for new rules.
| Capability | Benefit |
|---|---|
| Regex engine | 5–10× faster on regex-heavy rules |
| Validation | Stricter errors with precise source locations |
| Formatter | yr fmt for consistent style |
| Modules | crx, dex (new); improved PE/ELF/Mach-O |
| Compatibility | ~99% legacy rule compatibility after fixes |
Scope of this skill: rule authoring, review, optimization, FP debugging, and deployment—not malware RE, network IDS, or memory forensics.
Install and CLI
# macOS
brew install yara-x
# From source
cargo install yara-x
# Verify
yr --version| Command | Purpose |
|---|---|
yr check rule.yar | Syntax and semantic validation |
yr check rules/ | Validate directory |
yr fmt -w rule.yar | Format in place |
yr scan rule.yar /path/to/files | Scan corpus |
yr scan -s rule.yar file | Show matching strings |
yr scan -c rule.yar corpus/ | Count matches only |
yr dump -m pe sample.exe | Inspect module fields (YAML/JSON) |
Development cycle
Write rule → yr check → yr fmt → yr dump (if using modules)
→ scan malware set (must match) → scan goodware (must be zero)
→ peer review → deployTiming: time yr scan -s rule.yar corpus/ to spot slow rules before production.
Diagnostic advantage: When yr check reports line 15, the issue is on line 15—unlike legacy YARA's imprecise regex errors.
yr dump for exploration
Use before writing module-heavy conditions:
yr dump -m pe sample.exe --output-format yaml
yr dump -m math sample.exe --output-format yaml | grep entropy
yr dump -m crx extension.crx --output-format yaml
yr dump -m dex classes.dex --output-format yamlShows exactly what modules expose—imports, sections, permissions, DEX classes—without writing throwaway rules.
Legacy migration
# Step 1: find issues (diagnostic only)
yr check --relaxed-re-syntax rules/
# Step 2: fix each issue
# Step 3: strict validation
yr check rules/| Issue | Legacy behavior | YARA-X fix |
|---|---|---|
Literal { in regex | Often accepted | Escape: /config\{key\}/ |
| Invalid escapes | Silent literal | Fix escape or use valid class |
| Base64 modifier | Any length | String must be 3+ characters |
| Negative string index | @a[-1] | @a[#a - 1] |
| Duplicate modifiers | Allowed | Remove duplicate |
Do not ship rules that only pass under --relaxed-re-syntax.
YARA-X features
| Feature | Version | Usage |
|---|---|---|
| Private patterns | 1.3.0+ | private $helper = "x" — matches, hidden from output |
| Warning suppression | 1.4.0+ | // suppress: slow_pattern inline |
| Numeric underscores | 1.5.0+ | filesize < 10_000_000 |
| NDJSON output | — | yr scan --output-format ndjson for pipelines |
crx module | 1.5.0+ | Chrome extensions |
dex module | 1.11.0+ | Android DEX (new API, not legacy-compatible) |
External resources
| Resource | Purpose |
|---|---|
| Neo23x0/signature-base | Production rule examples |
| Elastic/protections-artifacts | Endpoint-tested rules |
| YARA Style Guide | Naming and metadata |
| YARA Performance Guidelines | Atoms and regex |
| yarGen | String extraction |
| FLOSS | Obfuscated strings |
| YARA-CI | Automated goodware testing |