{"id":191,"date":"2025-12-16T09:00:00","date_gmt":"2025-12-16T09:00:00","guid":{"rendered":"http:\/\/justruss.tech\/index.php\/2023\/08\/22\/writing-your-first-yara-rule-from-sample-to-signature\/"},"modified":"2026-05-15T10:34:55","modified_gmt":"2026-05-15T10:34:55","slug":"writing-your-first-yara-rule-from-sample-to-signature","status":"publish","type":"post","link":"https:\/\/justruss.tech\/index.php\/2025\/12\/16\/writing-your-first-yara-rule-from-sample-to-signature\/","title":{"rendered":"Writing Your First Yara Rule: From Sample to Signature"},"content":{"rendered":"<p>Yara is a pattern matching tool for malware identification. You define conditions based on strings, byte sequences, PE header characteristics, entropy values, or file properties, and Yara tells you whether a file matches. Writing rules from scratch against real samples is the most direct way to understand what makes a rule robust versus fragile, and this walkthrough covers the complete process from raw sample to a production-quality rule.<\/p>\n<h3>Initial sample analysis workflow<\/h3>\n<pre>\/\/ Step 1: Basic file identification\nfile Qakbot_loader.exe\n# PE32 executable (GUI) Intel 80386, for MS Windows\n\n\/\/ Calculate hashes for record-keeping and VirusTotal lookup\nsha256sum Qakbot_loader.exe\nmd5sum Qakbot_loader.exe\n\n\/\/ Step 2: Check existing coverage before writing new rules\n\/\/ Submit to VirusTotal, check if the family is already detected\n\/\/ and what names it is being detected under\n\n\/\/ Step 3: Extract strings - look for anything distinctive\nstrings -a -n 8 Qakbot_loader.exe | sort -u | \\\n    grep -v \"^[A-Za-z][a-z]*$\" | \\   \/\/ filter common words\n    head -100\n\n\/\/ Notable strings from the Qakbot obama200 sample:\n\/\/ SOFTWARE\\Microsoft\\Dtcpipe     (known Qakbot persistence key)\n\/\/ obama200                        (campaign tag, hardcoded)\n\/\/ PluginStart                     (export function name)\n\/\/ PluginStop\n\/\/ PluginCode\n\/\/ EncryptData\n\/\/ DecryptData<\/pre>\n<h3>Finding byte-level patterns with radare2<\/h3>\n<pre>\/\/ Open in radare2 for binary analysis\nr2 -A Qakbot_loader.exe\n\n\/\/ Search for the XOR key used in config decryption\n\/\/ Common Qakbot XOR keys appear as immediate values in XOR instructions\n[0x00401000]&gt; \/x 35deadc0de\n# Matches at: 0x00401890\n# 0x35 is the XOR opcode, 0xdeadc0de is the immediate key\n\n\/\/ View the decryption routine\n[0x00401890]&gt; pd 20\n\/\/ Shows the XOR loop with the key\n\n\/\/ Search for string references\n[0x00401000]&gt; iz\n\/\/ Lists all strings in the binary with their addresses\n\n\/\/ Look at cross-references to suspicious strings\n[0x00401000]&gt; axt @ [address_of_obama200_string]\n\/\/ Shows where this string is referenced from<\/pre>\n<h3>PE structure analysis<\/h3>\n<pre>python3 &lt; 7.0 suggests packed or encrypted section\n    if entropy &gt; 7.0:\n        print(f\"    *** HIGH ENTROPY - likely packed\/encrypted ***\")\n\nprint(\"\\n=== Import Directory ===\")\nif hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):\n    for entry in pe.DIRECTORY_ENTRY_IMPORT:\n        print(f\"  {entry.dll.decode()}\")\n        for imp in entry.imports:\n            if imp.name:\n                print(f\"    {imp.name.decode()}\")\nEOF<\/pre>\n<h3>Writing the rule: from indicators to condition<\/h3>\n<pre>\/\/ Full production-quality Yara rule for Qakbot obama200 loader\n\nrule Qakbot_obama200_loader {\n    meta:\n        author       = \"justruss\"\n        description  = \"Qakbot loader binary from the obama200 campaign (2023)\"\n        date         = \"2023-08-20\"\n        hash_sample  = \"3a4b5c6d7e8f...\"\n        reference    = \"https:\/\/justruss.tech\/post\/writing-yara-rules\"\n        tlp          = \"WHITE\"\n\n    strings:\n        \/\/ Registry persistence key (this path is unique to Qakbot)\n        $reg_key    = \"SOFTWARE\\\\Microsoft\\\\Dtcpipe\" wide ascii\n\n        \/\/ Campaign identifier tag hardcoded in the config blob\n        $campaign   = \"obama200\" nocase\n\n        \/\/ XOR key as immediate operand in decryption loop\n        \/\/ { 35 DE AD C0 DE } = XOR EAX, 0xDEADC0DE\n        $xor_key    = { 35 DE AD C0 DE }\n\n        \/\/ Export function names consistent across loader variants\n        $export_1   = \"PluginStart\" ascii fullword\n        $export_2   = \"PluginStop\"  ascii fullword\n        $export_3   = \"PluginCode\"  ascii fullword\n\n        \/\/ C2 communication strings\n        $c2_enc     = \"EncryptData\" ascii\n        $c2_dec     = \"DecryptData\" ascii\n\n    condition:\n        \/\/ Must be a valid PE file (MZ header + PE signature)\n        uint16(0) == 0x5A4D\n        and uint32(uint32(0x3C)) == 0x00004550\n\n        \/\/ Reasonable size range for this loader family (50KB to 2MB)\n        and filesize &gt; 50KB\n        and filesize &lt; 2MB\n\n        \/\/ Must match at least 2 of the 3 export function names\n        \/\/ (provides resilience when one is removed in a variant)\n        and 2 of ($export_1, $export_2, $export_3)\n\n        \/\/ Must match at least 1 unique family indicator\n        and 1 of ($reg_key, $campaign, $xor_key)\n}<\/pre>\n<h3>Testing and validation<\/h3>\n<pre>\/\/ Install Yara\nsudo apt install yara -y\n\n\/\/ Test against the original sample (must match)\nyara -r qakbot_obama200.yar Qakbot_loader.exe\n# Expected: Qakbot_obama200_loader Qakbot_loader.exe\n\n\/\/ False positive check against clean Windows binaries\nyara -r qakbot_obama200.yar C:\\Windows\\System32\\ 2&gt;\/dev\/null\n# Expected: (no output)\n\n\/\/ False positive check against a broader clean file set\nyara -r qakbot_obama200.yar \/usr\/bin\/ \/usr\/lib\/ 2&gt;\/dev\/null\n# Expected: (no output or very few unexpected matches)\n\n\/\/ Test against your malware corpus if you have one\nyara -r qakbot_obama200.yar \/opt\/malware_samples\/ 2&gt;\/dev\/null | grep Qakbot\n# Shows all matching samples - useful for measuring family coverage\n\n\/\/ Performance test for rules that will be used in high-throughput scanning\ntime yara qakbot_obama200.yar large_file.bin\n# Rules scanning large files should complete in milliseconds<\/pre>\n<h3>Scanning memory for Yara matches<\/h3>\n<pre>\/\/ Yara can scan live process memory (requires appropriate privileges)\n\/\/ Scan all running processes for the Qakbot rule\nyara -p 8 qakbot_obama200.yar $(ps ax -o pid= | tr -s ' ' | sed 's\/^ \/\/')\n\n\/\/ Or scan a specific process by PID\nyara qakbot_obama200.yar \/proc\/1234\/mem 2&gt;\/dev\/null\n\n\/\/ Using Volatility to run Yara against a memory dump\nvol -f memory.raw windows.vadyarascan --yara-file qakbot_obama200.yar\n\/\/ Scans each process VAD region against the rule\n\/\/ Much faster than scanning raw memory because it respects process boundaries<\/pre>\n<h3>Building a Yara rule set for your environment<\/h3>\n<pre>\/\/ Organise rules by family and campaign for maintainability\n\/\/ Directory structure:\n\/\/ rules\/\n\/\/   qakbot\/\n\/\/     qakbot_obama200_2023.yar\n\/\/     qakbot_bb_2024.yar\n\/\/   cobalt_strike\/\n\/\/     cs_default_watermarks.yar\n\/\/     cs_beacon_config.yar\n\/\/   generic\/\n\/\/     process_injection_indicators.yar\n\/\/     credential_dumping_tools.yar\n\n\/\/ Master rules file that includes all others\n\/\/ master.yar:\n\/\/ include \"rules\/qakbot\/qakbot_obama200_2023.yar\"\n\/\/ include \"rules\/cobalt_strike\/cs_default_watermarks.yar\"\n\/\/ ...\n\n\/\/ Run the master ruleset against a scan target\nyara -r master.yar --print-tags --print-meta scan_target\/<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Yara rules feel mysterious until you write one from<br \/>\nscratch against a real malware sample.<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[],"class_list":["post-191","post","type-post","status-publish","format-standard","hentry","category-threat-hunting"],"_links":{"self":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/191","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/comments?post=191"}],"version-history":[{"count":4,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/191\/revisions"}],"predecessor-version":[{"id":282,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/191\/revisions\/282"}],"wp:attachment":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/media?parent=191"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/categories?post=191"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/tags?post=191"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}