YARA Rule Writing Guide

SkillFiles & storage

Use when the user asks for a YARA rule, "write YARA for this sample/family", or `/hash-investigation` / `/malware-analysis` surfaces a sample worth a static-content rule. Pattern-matching rules for identifying malicious files.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the YARA Rule Writing Guide skill

What this skill tells your AI

The instructions your AI receives, as published by liberty91ltd/cti-skills in skills/yara-writing/SKILL.md and read by ahel’s review.

YARA rules identify and classify malware by matching byte patterns, strings, and conditions in files.

Rule Structure

rule RuleName : tag1 tag2 {
    meta:
        author = "CTI Platform"
        date = "YYYY-MM-DD"
        description = "Description of what this rule detects"
        reference = "https://source-reference.com"
        tlp = "GREEN"
        mitre_attack = "T1566"
        hash = "sha256_of_sample"
        confidence = "high"

    strings:
        $text1 = "suspicious string" ascii wide
        $hex1 = { 4D 5A 90 00 03 00 00 00 }
        $regex1 = /https?:\/\/[a-z0-9\-\.]+\.onion/

    condition:
        uint16(0) == 0x5A4D and
        filesize < 5MB and
        (2 of ($text*) or $hex1) and
        $regex1
}

String Types

Text strings

$s1 = "CreateRemoteThread"          # Exact ASCII match
$s2 = "kernel32.dll" ascii wide     # Match both ASCII and UTF-16
$s3 = "password" nocase             # Case-insensitive
$s4 = "cmd /c" ascii wide nocase    # Combined modifiers
$s5 = "C:\\Windows\\Temp\\"         # Escaped backslashes

Hex strings

$h1 = { 4D 5A 90 00 }              # Exact bytes (PE header)
$h2 = { 4D 5A ?? ?? 03 00 }        # Wildcards (?? = any byte)
$h3 = { 4D 5A [2-4] 03 00 }        # Jump (2-4 bytes between)
$h4 = { 4D 5A ( 90 00 | 00 00 ) }  # Alternation (OR)

Regular expressions

$r1 = /https?:\/\/[\w\-\.]+/       # URL pattern
$r2 = /[A-Za-z0-9+\/]{50,}={0,2}/  # Base64 encoded content
$r3 = /(\d{1,3}\.){3}\d{1,3}/      # IP address pattern

Conditions

File properties

uint16(0) == 0x5A4D                 # PE file (MZ header)
uint32(0) == 0x464C457F             # ELF file
uint32(0) == 0xBEBAFECA             # Mach-O fat binary
filesize < 10MB                     # File size limit

String matching

all of them                         # All strings must match
any of them                         # At least one string
2 of ($text*)                       # At least 2 of $text group
3 of ($s1, $s2, $s3, $s4)          # 3 of these 4 strings
$s1 at 0                            # $s1 at offset 0
$s1 in (0..1024)                    # $s1 in first 1KB
#s1 > 3                             # $s1 occurs more than 3 times

PE module (Windows executables)

import "pe"

pe.number_of_sections > 5
pe.imports("kernel32.dll", "VirtualAlloc")
pe.exports("DllRegisterServer")
pe.is_dll()
pe.timestamp > 1704067200           # Compiled after 2024-01-01

Best Practices

Do

  • Start with a PE/ELF/file type check to limit scope
  • Add a filesize constraint to avoid scanning large files
  • Use unique strings — prefer byte sequences or specific API combinations over generic strings
  • Combine multiple indicators (string + structure + behavior)
  • Include metadata with reference to source intelligence
  • Test against both malicious samples AND clean files (false positive testing)

Don't

  • Match on single, common strings (e.g., just "http://" or "cmd.exe")
  • Create rules without file type constraints (performance killer)
  • Use overly broad regex patterns
  • Forget to escape special characters in hex patterns
  • Write rules that match on packer/crypter artifacts only (will match unrelated packed software)

Common Malware Patterns

Encoded PowerShell

rule Encoded_PowerShell_Execution {
    meta:
        description = "Detects base64 encoded PowerShell commands"
        mitre_attack = "T1059.001"
    strings:
        $enc1 = "-EncodedCommand" ascii nocase
        $enc2 = "-enc " ascii nocase
        $enc3 = "FromBase64String" ascii nocase
        $b64 = /[A-Za-z0-9+\/]{100,}={0,2}/ ascii
    condition:
        any of ($enc*) and $b64
}

Suspicious PE imports

import "pe"
rule Suspicious_Injection_Imports {
    meta:
        description = "PE with process injection API imports"
        mitre_attack = "T1055"
    condition:
        pe.imports("kernel32.dll", "VirtualAllocEx") and
        pe.imports("kernel32.dll", "WriteProcessMemory") and
        pe.imports("kernel32.dll", "CreateRemoteThread")
}

Naming Convention

  • APT_<GroupName>_<MalwareName>.yar — For attributed malware
  • MAL_<MalwareName>_<Variant>.yar — For named malware families
  • SUSP_<Behavior>_<Context>.yar — For suspicious behavior patterns
  • HUNT_<Target>_<Technique>.yar — For threat hunting queries

Output Location

Write YARA rules to: data/detection-rules/yara/<malware-name>.yar

Validation against a real corpus

Before deploying a rule beyond your test set, validate it against a broader sample collection. When ReversingLabs Spectra Analyze is available:

  • /lookup-reversinglabs yara-matches <ruleset_name> — for a rule you have already deployed to RL, list the samples it has matched in the continuous-scanning corpus. Useful to confirm the rule isn't firing on goodware and to discover new variants caught by the same logic.
  • /lookup-reversinglabs search 'threatname:<family> classification:malicious' --max-results 100 — before writing a family rule, retrieve a candidate sample set to test against. Then pull each sample's hashes for inclusion in your test suite.
  • /lookup-reversinglabs containers <hash> and extracted <hash> — find parents/children of a known sample to ensure the rule fires across the dropper-payload chain, not just on one layer.

Without RL access, fall back to retro-hunting via /lookup-virustotal (premium) and /lookup-otx pulse traversal.

Related skills

  • /hash-investigation, /malware-analysis — produce the structural and behavioural intel that informs the rule
  • /lookup-reversinglabs — corpus validation, family pivots, parent/child relationships
  • /lookup-virustotal, /lookup-otx — community detection signals and pulse-driven sample collection
  • /sigma-writing — behavioural-detection counterpart for log telemetry

Signals

GitHub stars
22
Forks
9
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
yara-writing
Source
github.com/liberty91ltd/cti-skills