<!-- Copyright (c) 2026 defconxt. All rights reserved. -->
<!-- Licensed under AGPL-3.0 — see LICENSE file for details. -->
---
name: performing-yara-rule-development
description: >-
  Develop, test, and optimize YARA rules for malware detection, threat hunting,
  and automated classification across file systems and memory.
domain: cybersecurity
subdomain: malware-analysis
tags:
  - yara
  - detection-engineering
  - threat-hunting
  - pattern-matching
  - malware-classification
  - sigma
version: "1.0"
author: defconxt
license: AGPL-3.0
metadata:
  mitre-attack: ["T1587.001"]
---

# Performing YARA Rule Development

## Overview

YARA is the industry-standard pattern matching tool for malware identification and classification. YARA rules combine string patterns (text, hex, regex), file metadata conditions, and boolean logic to detect malware families, packer signatures, exploit kits, and suspicious behaviors. Effective YARA rules balance detection coverage against false positive rates through careful string selection, condition logic, and performance optimization. Rules deploy across endpoints (EDR), email gateways, sandboxes, and threat intelligence platforms.

## Prerequisites

- Python 3.10+ with `yara-python`
- YARA 4.x CLI (`yara`, `yarac`)
- Sample malware corpus for testing
- Clean file corpus for FP testing
- Understanding of PE/ELF formats
- yarGen or YARA rule generators for bootstrapping
- VirusTotal Hunting (for retrohunt validation)

## Key Concepts

### Rule Structure

```yara
rule Rule_Name {
    meta:
        description = "What this rule detects"
        author = "Your name"
        date = "2026-03-17"
        hash = "sample_hash"
        reference = "URL or CVE"
        mitre_attack = "T1055"
        severity = "critical"
        tlp = "WHITE"

    strings:
        $text = "plain text" ascii wide nocase
        $hex = { 4D 5A 90 00 03 00 00 00 }
        $regex = /https?:\/\/[a-zA-Z0-9.-]+\/[a-zA-Z0-9]+\.php/
        $b64 = "encoded" base64

    condition:
        uint16(0) == 0x5A4D and
        filesize < 1MB and
        2 of ($text, $hex, $regex)
}
```

### String Modifiers

| Modifier | Description |
|----------|-------------|
| `ascii` | Match ASCII strings (default) |
| `wide` | Match UTF-16LE encoded strings |
| `nocase` | Case-insensitive matching |
| `fullword` | Match whole words only |
| `base64` | Match Base64-encoded form |
| `xor` | Match XOR-encoded (brute all keys) |
| `xor(0x00-0xFF)` | Match XOR with specific key range |
| `private` | Don't report match (optimization) |

### Performance Considerations

- Use `filesize` filter early in condition to skip large files
- Use magic number checks (`uint16(0) == 0x5A4D`) before string matching
- Limit regex complexity (no catastrophic backtracking)
- Use `private` modifier for intermediate strings
- Prefer hex patterns over regex when possible
- Test with `--print-stats` to identify slow rules

## Workflow

### Step 1: Extract Unique Strings from Sample

```bash
# Extract candidate strings
strings -a -n 8 sample.exe | sort -u > candidate_strings.txt

# Use FLOSS for obfuscated strings
floss sample.exe -j > floss_output.json

# Use yarGen to auto-generate initial rule
python3 yarGen.py -m sample_dir/ -o generated_rules.yar --excludegood

# Find hex patterns unique to malware
python3 -c "
import pefile
pe = pefile.PE('sample.exe')
# Extract unique byte sequences from .text section
for section in pe.sections:
    if b'.text' in section.Name:
        data = section.get_data()
        # Find potential crypto constants, unique opcodes, etc.
        for i in range(0, len(data) - 16, 1):
            chunk = data[i:i+16]
            if chunk.count(0) < 4:  # Skip mostly-null regions
                print(f'{{ {\" \".join(f\"{b:02X}\" for b in chunk)} }}')
                break
"
```

### Step 2: Write YARA Rule

```yara
rule APT_Backdoor_CustomRAT_2026 {
    meta:
        description = "Detects CustomRAT backdoor used by APT group in Q1 2026"
        author = "NERF"
        date = "2026-03-17"
        hash1 = "a1b2c3d4e5f6..."
        hash2 = "f6e5d4c3b2a1..."
        reference = "https://internal-report/apt-customrat"
        mitre_attack = "T1071.001"
        severity = "critical"
        tlp = "AMBER"

    strings:
        // Unique mutex
        $mutex = "Global\\CustomRAT_Session_" ascii wide

        // C2 communication pattern
        $c2_beacon = { 50 4F 53 54 20 2F [1-20] 2E 70 68 70 }  // POST /*.php

        // Configuration decryption routine (XOR loop)
        $crypto_loop = {
            8A 0?                // mov cl, [reg]
            80 F1 ??             // xor cl, key
            88 0?                // mov [reg], cl
            4? FF C?             // inc reg
            48 3B ?? 72 F?       // cmp + jb (loop)
        }

        // Unique strings
        $str_config = "config.dat" ascii
        $str_cmd1 = "cmd::exec" ascii
        $str_cmd2 = "cmd::upload" ascii
        $str_cmd3 = "cmd::download" ascii
        $str_cmd4 = "cmd::persist" ascii

        // Anti-analysis check
        $anti_vm = "VBOX" ascii wide nocase
        $anti_dbg = "IsDebuggerPresent" ascii

    condition:
        uint16(0) == 0x5A4D and
        filesize < 2MB and
        $mutex and
        ($crypto_loop or $c2_beacon) and
        2 of ($str_cmd*) and
        not $anti_vm  // Exclude VM-aware samples (separate rule)
}

rule Ransomware_Generic_Encryption_Indicators {
    meta:
        description = "Generic ransomware indicators based on crypto API usage and ransom note patterns"
        author = "NERF"
        date = "2026-03-17"

    strings:
        $api1 = "CryptEncrypt" ascii
        $api2 = "CryptGenKey" ascii
        $api3 = "CryptImportKey" ascii
        $api4 = "BCryptEncrypt" ascii

        $note1 = "Your files have been encrypted" ascii wide nocase
        $note2 = "bitcoin" ascii wide nocase
        $note3 = "decrypt" ascii wide nocase
        $note4 = ".onion" ascii

        $ext1 = ".locked" ascii
        $ext2 = ".encrypted" ascii
        $ext3 = ".crypt" ascii

    condition:
        uint16(0) == 0x5A4D and
        filesize < 5MB and
        2 of ($api*) and
        (2 of ($note*) or 1 of ($ext*))
}
```

### Step 3: Test Rules

```bash
# Test against known malware samples
yara -r rules/custom_rules.yar samples/

# Test against clean files (false positive check)
yara -r rules/custom_rules.yar /usr/bin/
yara -r rules/custom_rules.yar /usr/lib/
yara -r rules/custom_rules.yar ~/Documents/

# Performance testing
yara --print-stats -r rules/custom_rules.yar samples/

# Compile for deployment
yarac rules/custom_rules.yar compiled_rules.yarc
yara compiled_rules.yarc samples/
```

### Step 4: Optimize and Validate

```python
#!/usr/bin/env python3
"""YARA rule testing and validation framework."""
import yara
import json
from pathlib import Path

def test_yara_rules(
    rule_file: str,
    malware_dir: str,
    clean_dir: str,
) -> dict:
    """Test YARA rules against malware and clean corpora."""
    rules = yara.compile(filepath=rule_file)

    results = {
        "rule_file": rule_file,
        "true_positives": [],
        "false_negatives": [],
        "false_positives": [],
        "stats": {"tp": 0, "fp": 0, "fn": 0, "tn": 0},
    }

    # Test against malware (expect matches)
    for sample in Path(malware_dir).rglob("*"):
        if sample.is_file():
            matches = rules.match(str(sample))
            if matches:
                results["true_positives"].append(str(sample))
                results["stats"]["tp"] += 1
            else:
                results["false_negatives"].append(str(sample))
                results["stats"]["fn"] += 1

    # Test against clean files (expect no matches)
    for clean in Path(clean_dir).rglob("*"):
        if clean.is_file():
            try:
                matches = rules.match(str(clean))
                if matches:
                    results["false_positives"].append({
                        "file": str(clean),
                        "rules": [m.rule for m in matches],
                    })
                    results["stats"]["fp"] += 1
                else:
                    results["stats"]["tn"] += 1
            except yara.Error:
                pass

    # Calculate metrics
    tp, fp, fn = results["stats"]["tp"], results["stats"]["fp"], results["stats"]["fn"]
    results["precision"] = round(tp / (tp + fp), 4) if (tp + fp) > 0 else 0
    results["recall"] = round(tp / (tp + fn), 4) if (tp + fn) > 0 else 0

    return results
```

### Step 5: Deploy Rules

```bash
# Deploy to YARA scanner
cp rules/custom_rules.yar /opt/yara-rules/custom/

# Deploy to ClamAV (convert format)
# YARA rules can be used directly with ClamAV 0.104+
cp rules/custom_rules.yar /var/lib/clamav/

# Deploy to osquery
# In osquery.conf:
# "yara": { "signatures": { "custom": ["/opt/yara-rules/custom/"] } }

# Retrohunt on VirusTotal (if available)
# Upload rule to VT Intelligence → Retrohunt
```

## Detection

```yaml
title: Yara Rule Development Detection
id: 630337c8-b250-4d33-90c2-e04b8e2abbe8
status: experimental
description: Detects suspicious activity related to performing yara rule development techniques in malware analysis context
logsource:
  category: process_creation
  product: windows
detection:
  selection:
    CommandLine: "*performing*yara*"
  condition: selection
level: high
tags:
  - attack.t1587.001
  - attack.execution
falsepositives:
  - Sandbox detonation environments analyzing submitted samples
```

## Verification

- [ ] Rules compile without errors
- [ ] True positive rate validated against known samples
- [ ] False positive rate tested against clean corpus
- [ ] Performance benchmarked (scan speed acceptable)
- [ ] Metadata complete (author, date, hash, MITRE, severity)
- [ ] Rules deployed to detection stack
- [ ] Documentation updated with rule descriptions

## References

- [YARA Documentation](https://yara.readthedocs.io/)
- [yarGen - YARA Rule Generator](https://github.com/Neo23x0/yarGen)
- [YARA-Forge - Community Rules](https://github.com/YARAHQ/yara-forge)
- [Florian Roth's Signature Base](https://github.com/Neo23x0/signature-base)
- [VirusTotal YARA Hunting](https://www.virustotal.com/gui/hunting-overview)
