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>
This commit is contained in:
@@ -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 <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) |
|
||||
Reference in New Issue
Block a user