diff --git a/malware/DridexShellcode.yar b/malware/DridexShellcode.yar new file mode 100644 index 00000000..39ae2095 --- /dev/null +++ b/malware/DridexShellcode.yar @@ -0,0 +1,49 @@ +rule DridexStagerShellcodeRoutine +{ + meta: + description = "Detects Dridex stager shellcode instructions" + author = "Harrison Edwards" + date = "2024-11-13" + version = "4.0" + reference = "manual API resolution + PE header walk pattern" + + strings: + // --- Bucket 1: stack frame setup --- + // Wildcarded immediate so a different frame size (0x28, 0x30, etc.) + // from a recompile doesn't silently break the match. + $frame1 = { 48 89 5c 24 08 48 89 74 24 20 57 48 83 ec ?? } + + // --- Bucket 2: conditional branch on edx w/ stack store --- + $branch1 = { 48 85 d2 0f 84 ?? ?? ?? ?? 48 89 74 24 ?? } + + // --- Bucket 3: IAT-style resolved-pointer dispatch --- + // Wildcard the displacement bytes — these offsets shift whenever + // the malware author reorders their resolved-function table. + $iat1 = { 8b 4a 08 45 33 c0 ff 97 ?? ?? 00 00 48 ff ce } + $iat2 = { 8b 8f ?? ?? 00 00 33 d2 ff 97 ?? ?? 00 00 } + + // --- Bucket 4: post-call result check + buffer clear --- + $check1 = { 85 c0 74 ?? 48 83 64 24 ?? 00 } + + // --- Bucket 5: stack buffer lea pair --- + $lea1 = { 8d 44 24 ?? 48 8d 54 24 ?? } + + // --- Bucket 6: PE signature validation during manual export walk --- + $pecheck = { 81 3e 50 45 00 00 75 ?? } + + // --- Bucket 7 (new): MZ check that typically precedes $pecheck + // in the same manual-parsing routine — corroborates bucket 6 + // instead of trusting one 4-byte constant in isolation. + $mzcheck = { 66 81 3? 4d 5a } + // cmp word ptr [reg], 0x5a4d ("MZ") + + condition: + uint16(0) != 0x5a4d and // exclude on-disk PE files; this + // targets unbacked/shellcode regions + filesize < 20KB and // stagers are small; caps FP surface + // on large legitimate binaries + ( + ( $pecheck and $mzcheck ) or // strong corroborated anchor path + 3 of ($frame1, $branch1, $iat1, $iat2, $check1, $lea1) + ) +}