Files
tobias 0a00835493 Add malware patterns & recognition exam cheat sheet
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) <noreply@anthropic.com>
2026-03-31 08:24:45 +02:00

330 lines
12 KiB
Markdown

# 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 <file>` 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 <file>` — 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 <file>` → streams marked "M" contain macros → extract with `-s <n> -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 <Base64>
```
Decode: `echo <Base64> | 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) |