Source code for architect.yara_exporter

"""YaraExporter - export patterns as YARA rules."""

import logging
from typing import Optional

logger = logging.getLogger("memdiver.architect.yara_exporter")


[docs] class YaraExporter: """Export byte patterns as YARA detection rules."""
[docs] @staticmethod def export( pattern: dict, rule_name: Optional[str] = None, description: Optional[str] = None, tags: Optional[list] = None, ) -> str: """Export a pattern dict as a YARA rule string. Args: pattern: Pattern dict from PatternGenerator.generate(). rule_name: YARA rule name (defaults to sanitized pattern name). description: Rule description. tags: Optional YARA tags. Returns: YARA rule as a string. """ name = rule_name or _sanitize_identifier(pattern.get("name", "memdiver_pattern")) desc = description or f"MemDiver pattern: {pattern.get('name', 'unknown')}" tag_str = " : " + " ".join(tags) if tags else "" wildcard = pattern.get("wildcard_pattern", "") # Convert space-separated hex to YARA format (curly braces) yara_hex = wildcard.upper() lines = [ f'rule {name}{tag_str}', '{', ' meta:', f' description = "{desc}"', f' pattern_length = {pattern.get("length", 0)}', f' static_ratio = "{pattern.get("static_ratio", 0)}"', ' generated_by = "MemDiver"', '', ' strings:', f' $key = {{ {yara_hex} }}', '', ' condition:', ' $key', '}', ] rule = "\n".join(lines) logger.info("Exported YARA rule: %s (%d bytes)", name, pattern.get("length", 0)) return rule
def _sanitize_identifier(name: str) -> str: """Convert a string to a valid YARA identifier.""" sanitized = "" for c in name: if c.isalnum() or c == "_": sanitized += c else: sanitized += "_" if sanitized and sanitized[0].isdigit(): sanitized = "r_" + sanitized return sanitized or "unnamed_rule"