From 0a008354932a91d9f02f14bf54b5fbad9f3b6375 Mon Sep 17 00:00:00 2001 From: tobias Date: Tue, 31 Mar 2026 08:24:45 +0200 Subject: [PATCH] Add malware patterns & recognition exam cheat sheet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers: technique identification by API sequence (process hollowing, code injection, DLL injection, .NET reflective loading, hook-based injection, resource droppers), packer recognition (UPX, entropy, section names, tail jump, breakpoint strategies), anti-analysis patterns (IsDebuggerPresent, PEB, SEH, TLS, RDTSC, tool detection), shellcode indicators (NOP sled, GetEIP, PEB walk), document malware indicators (PDF keywords, VBA triggers, RTF exploits), and two quick-reference tables mapping APIs→techniques and assembly→behavior. Co-Authored-By: Claude Opus 4.6 (1M context) --- data/exam-cheatsheets/04-malware-patterns.md | 329 +++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 data/exam-cheatsheets/04-malware-patterns.md diff --git a/data/exam-cheatsheets/04-malware-patterns.md b/data/exam-cheatsheets/04-malware-patterns.md new file mode 100644 index 0000000..d582db7 --- /dev/null +++ b/data/exam-cheatsheets/04-malware-patterns.md @@ -0,0 +1,329 @@ +# FOR610 Malware Patterns & Recognition Cheat Sheet + +## Technique Identification: API Sequences + +### Process Hollowing (T1055.012) +``` +CreateProcess(SUSPENDED) ← dwCreationFlags = 0x4 +NtUnmapViewOfSection() ← gut the target process +VirtualAllocEx() ← allocate space for payload +WriteProcessMemory() ← inject malicious code +ResumeThread() ← wake up with injected code +``` +**How to spot:** capa shows "Create Suspended Process". In Ghidra: CreateProcess with 0x4 flag, followed by NtUnmapViewOfSection (often loaded dynamically via GetProcAddress to hide from IAT). +**Lab:** WinHost32.exe (S5, Lab 5.4) + +--- + +### Classic Code Injection — CreateRemoteThread (T1055.001) +``` +OpenProcess() ← get handle to target +VirtualAllocEx(RWX) ← allocate executable memory (0x40 = PAGE_EXECUTE_READWRITE) +WriteProcessMemory() ← write shellcode to target +CreateRemoteThread() ← execute in target process +``` +**How to spot:** Search imports for CreateRemoteThread. VirtualAllocEx with flProtect=0x40 is a red flag. +**Lab:** great.exe (S4, Lab 4.9) + +--- + +### DLL Injection (T1055.001) +``` +OpenProcess() ← target process +VirtualAllocEx() ← allocate space for DLL path string +WriteProcessMemory() ← write DLL path into target +CreateRemoteThread( ← start address = LoadLibraryA + lpStartAddress=LoadLibraryA, + lpParameter=remote_dll_path) +``` +**How to spot:** CreateRemoteThread where lpStartAddress points to LoadLibrary. + +--- + +### DLL Side-Loading / Sideloading (T1574.002) +``` +Trusted.exe (signed) ← legitimate executable + → LoadLibraryW("evil.dll") ← loads malicious DLL from same directory + → DllRegisterServer() ← DLL export runs malicious code + → CreateProcessW() ← spawns payload +``` +**How to spot:** Signed executable loading DLL from non-standard path. DLL exports suspicious functions. PeStudio shows expired but valid signature. +**Lab:** Package.exe + iviewers.dll (S3, Lab 3.10) + +--- + +### .NET Reflective Loading (T1620) +```csharp +byte[] payload = DecodePayload(); // XOR/Base64 decode +Assembly asm = Assembly.Load(payload); // Load from byte[] in memory +asm.GetTypes()[0].InvokeMember(...); // Execute +``` +**How to spot:** Search ILSpy decompiled code for "Assembly.Load" or ".Load". Set breakpoint AFTER Assembly.Load in dnSpyEx → extract byte[] from Locals window. +**Lab:** chatroom.exe (S4, Lab 4.8) + +**This is the answer to "what technique is being used when a .NET binary extracts malicious code directly into the memory of its running process?"** → **Reflective Loading via Assembly.Load(byte[])** + +--- + +### .NET In-Memory Compilation +```csharp +CSharpCodeProvider csc = new CSharpCodeProvider(); +string source = Decode(encoded_source); // Base64+XOR decode +CompilerResults result = csc.CompileAssemblyFromSource(params, source); +result.CompiledAssembly.EntryPoint.Invoke(...); +``` +**How to spot:** Search for "CSharpCodeProvider" or "CompileAssemblyFromSource" in decompiled code. +**Lab:** rwvg1.exe (S3, Lab 3.12) + +--- + +### Hook-Based Injection — SetWindowsHookEx (T1056) +``` +SetWindowsHookExA( + 0x0E, ← WH_MOUSE_LL (low-level mouse hook) + hook_function, ← pointer to malicious code + GetModuleHandleA(0), ← own module + 0) ← all threads +``` +**How to spot:** SetWindowsHookExA with idHook=0x0E (mouse) or 0x0D (keyboard). Anti-sandbox: waits for real user activity before executing. +**Lab:** vbprop.exe (S5, Lab 5.5) + +--- + +### Resource Dropper Pattern +``` +FindResourceW() ← locate embedded resource +SizeofResource() ← get payload size +LoadResource() ← load into memory +LockResource() ← get pointer to data +CreateFileA() ← create output file +WriteFile() ← write payload to disk +CreateProcessA() ← execute dropped file +``` +**How to spot:** Ghidra Symbol Table shows FindResource + WriteFile + CreateProcess sequence. Resources visible in PeStudio. +**Lab:** ishelp.dll dropper (S2, Lab 2.7) + +--- + +## Packer Recognition + +### How to Identify a Packed Binary +| Indicator | What to check | Tool | +|-----------|--------------|------| +| **High entropy** | Sections >7.0 entropy | PeStudio, peframe | +| **Few imports** | Only LoadLibrary + GetProcAddress | PeStudio, peframe | +| **No readable strings** | Barely any strings extracted | strings, FLOSS | +| **Unusual section names** | UPX0, UPX1, .packed, .code | PeStudio, diec | +| **Small IAT** | Very few imported functions | PeStudio | +| **Packer signature** | Known packer detected | DIE/diec, ExeInfo PE | + +### UPX Packer Indicators +``` +Section names: UPX0 (empty), UPX1 (compressed code), .rsrc +Entry point: In UPX1 section (not .text) +Assembly: PUSHAD → [decompression loop] → POPAD → JMP OEP +Unpack: upx -d packed.exe (fails if modified) +``` + +### The Unpacking Tail Jump (assembly) +```asm +; End of unpacker — restore registers and jump to real code +POPAD ; restore all saved registers +JMP 0x140003F94 ; ← this is the OEP (Original Entry Point) +``` +**How to find it:** Set breakpoint at end of UPX1 section. Look for JMP to address in UPX0 (or .text). + +### Breakpoint Strategies for Manual Unpacking +| Strategy | When to use | How | +|----------|------------|-----| +| **Tail jump** | Known packer structure | Find JMP at end of unpacker | +| **VirtualAlloc** | Unpacker allocates new memory | BP on VirtualAlloc, watch for RWX allocation | +| **VirtualProtect** | Unpacker changes permissions | BP on VirtualProtect with 0x40 (RWX) | +| **Stack breakpoint (ESP trick)** | SEH-based packers | Set hardware BP on value written to stack after PUSH | +| **LoadLibrary** | Unpacker resolves imports | BP on LoadLibraryA when loading unexpected DLLs | +| **RtlDecompressBuffer** | Compression-based packer | BP on decompression API | + +--- + +## Anti-Analysis Recognition + +### Debugger Detection Patterns + +**IsDebuggerPresent:** +```asm +CALL IsDebuggerPresent +TEST EAX, EAX ; zero = no debugger +JNE debugger_found ; non-zero = debugger! +``` +**Bypass:** Set EAX=0 in debugger, or NOP the JNE. Lab: getdown.exe (S5) + +**PEB.BeingDebugged (avoid API call):** +```asm +MOV EAX, FS:[30h] ; get PEB address +MOVZX EAX, [EAX+2] ; offset 0x2 = BeingDebugged byte +TEST EAX, EAX +JNE debugger_found +``` +**Bypass:** Zero the byte at PEB+2 in debugger. Lab: lansrv.exe (S5) + +**NtGlobalFlag check:** +```asm +MOV EAX, FS:[30h] ; PEB +TEST [EAX+68h], 0x70 ; NtGlobalFlag (0x70 when debugger-launched) +JNE debugger_found +``` + +**Timing check (RDTSC):** +```asm +RDTSC ; read timestamp → EDX:EAX +MOV [saved], EAX ; save +; ... code to time ... +RDTSC ; read again +SUB EAX, [saved] ; compute delta +CMP EAX, threshold ; if too large → stepping detected +JA debugger_found +``` + +### SEH Anti-Debug Pattern +```asm +PUSH handler_addr ; push exception handler +PUSH FS:[0] ; push current SEH chain +MOV FS:[0], ESP ; install new handler + +XOR ECX, ECX +DIV ECX ; ← deliberate divide-by-zero exception! + ; execution jumps to handler_addr +; Analyst expects code to continue here, but it doesn't! +``` +**How to handle:** Set breakpoint on handler_addr. In x32dbg: Options → Preferences → Exceptions → "Do not break on DIV0". Lab: want.exe (S5, Lab 5.7) + +### TLS Callback Anti-Debug +``` +PE Header → TLS Directory → TLS Callback address +Code at TLS callback runs BEFORE entry point +Typically contains: IsDebuggerPresent + XOR decryption loop +``` +**How to handle:** PeStudio shows TLS section. Set hardware breakpoint at TLS callback address. Lab: lansrv.exe (S5, Lab 5.9) + +### Tool Detection Patterns +```asm +; Check for security DLLs +CALL GetModuleHandleW, "avghookx.dll" ; AVG +TEST EAX, EAX +JNE tool_found + +; Check for debugger windows +CALL FindWindowW, "OLLYDBG", NULL ; OllyDbg +CALL FindWindowW, "WinDbgFrameClass", NULL ; WinDbg +TEST EAX, EAX +JNE tool_found + +; Enumerate processes for analysis tools +CALL CreateToolhelp32Snapshot +CALL Process32FirstW / Process32NextW ; loop comparing names +``` +**Bypass:** ScyllaHide plugin hides all of these. Lab: raas.exe (S5, Lab 5.6) + +--- + +## Shellcode Indicators + +| Pattern | Hex/Assembly | Meaning | +|---------|-------------|---------| +| **NOP sled** | `90 90 90 90 90...` | Shellcode padding — buffer overflow indicator | +| **GetEIP (CALL+POP)** | `E8 00 00 00 00 58` | CALL next + POP EAX — shellcode locates itself | +| **PEB walk** | `MOV EAX, FS:[30h]` | Find kernel32.dll base address | +| **API hash loop** | Hash compare + loop | Resolve API addresses without strings | +| **XOR decode loop** | `XOR [ESI], AL; INC ESI; DEC ECX; JNZ` | Self-decrypting shellcode | + +**XORSearch detects:** `XORSearch -W -d 3 ` scans for GetEIP, kernel32 finder, NOP sled + +--- + +## Document Malware Indicators + +### PDF Suspicious Keywords +| Keyword | Risk | What it does | +|---------|------|-------------| +| `/JavaScript` | HIGH | Embedded script execution | +| `/OpenAction` | HIGH | Auto-execute on open | +| `/Launch` | HIGH | Launch external program | +| `/AA` | HIGH | Additional actions (multiple triggers) | +| `/AcroForm` | MEDIUM | Interactive form (can execute) | +| `/URI` | LOW-MED | Clickable URL (phishing) | +| `/EmbeddedFile` | MEDIUM | Embedded file content | +| `/RichMedia` | MEDIUM | Flash/media content | + +**Scan:** `pdfid.py ` — any non-zero count for HIGH keywords = investigate + +### Office VBA Auto-Execute Triggers +| Function | Trigger | +|----------|---------| +| `AutoOpen()` | Document opened (Word) | +| `Document_Open()` | Document opened (Word) | +| `Auto_Open()` | Workbook opened (Excel) | +| `Workbook_Open()` | Workbook opened (Excel) | +| `AutoExec()` | Application start | +| `AutoClose()` | Document closed | + +**Detect:** `oledump.py ` → streams marked "M" contain macros → extract with `-s -v` + +### RTF Exploit Indicators +| Indicator | What to look for | +|-----------|-----------------| +| Deeply nested groups | rtfdump.py shows nesting level >3 | +| Large hex data | Group with many hex bytes | +| `\objdata` keyword | Embedded OLE object | +| `\*\objclass` | Object class identifier | + +### PowerShell Encoded Command Pattern +``` +powershell.exe -WindowStyle Hidden -EncodedCommand +``` +Decode: `echo | base64 -d` or CyberChef (From Base64 → Decode UTF-16LE) + +--- + +## API → Technique Quick Reference + +| If you see these APIs... | Technique | +|--------------------------|-----------| +| CreateProcess(SUSPENDED) + NtUnmapViewOfSection + WriteProcessMemory + ResumeThread | **Process Hollowing** | +| VirtualAllocEx(0x40) + WriteProcessMemory + CreateRemoteThread | **Code Injection** | +| CreateRemoteThread(LoadLibraryA, dll_path) | **DLL Injection** | +| Assembly.Load(byte[]) + InvokeMember() | **.NET Reflective Loading** | +| CSharpCodeProvider + CompileAssemblyFromSource | **.NET In-Memory Compilation** | +| SetWindowsHookExA(0x0E) | **Mouse Hook Anti-Sandbox** | +| FindResource + WriteFile + CreateProcess | **Resource Dropper** | +| LoadLibraryW (DLL not in IAT, from same dir) | **DLL Side-Loading** | +| InternetOpen + HttpSendRequest + InternetReadFile | **HTTP C2** | +| RegSetValueEx("...\\Run", malware_path) | **Registry Persistence** | +| IsDebuggerPresent / FS:[30h]+2 / NtGlobalFlag | **Anti-Debugging** | +| GetModuleHandle("avghookx.dll") / FindWindow("OLLYDBG") | **Tool Detection** | +| RDTSC + delta comparison | **Timing Anti-Debug** | +| FS:[0] manipulation + intentional exception | **SEH Anti-Debug** | +| BlockInput(TRUE) | **Analyst Lockout** | +| CreateToolhelp32Snapshot + Process32First/Next | **Process Enumeration** | +| VirtualAlloc + memcpy/rep movsb + JMP | **Runtime Unpacking** | +| PUSHAD...POPAD...JMP OEP | **UPX-Style Packer** | + +--- + +## Assembly Pattern Quick Reference + +| Assembly Pattern | What It Means | +|-----------------|---------------| +| `XOR EAX, EAX` | Zero out register (common idiom) | +| `TEST EAX, EAX` then `JZ/JNZ` | Check if value is zero | +| `PUSH EBP; MOV EBP,ESP; SUB ESP,n` | Function prologue | +| `LEAVE; RET` | Function epilogue | +| `CALL; ADD ESP,n` | cdecl call (caller cleans stack) | +| `FS:[30h]` | PEB access (anti-debug or API resolution) | +| `FS:[0]` | SEH chain access | +| `XOR [reg], key` in loop | String/code decryption | +| `PUSHAD...POPAD...JMP addr` | Packer wrapper (UPX-style) | +| `MOV byte ptr [EBP-x], imm8` (repeated) | Stack string building | +| `E8 00 00 00 00` then `POP reg` | GetEIP shellcode technique | +| `REP MOVSB` (ESI→EDI) | Memory copy (memcpy) | +| `VirtualAlloc` then `REP MOVSB` then `JMP EAX` | Unpack and execute | +| `90 90 90 90...` | NOP sled (shellcode indicator) |