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