Skip to content

Obfuscation and Packing

Malware hides its real logic to hinder analysis and evade signature-based detection. Two main techniques: obfuscation (transforming code/strings so they’re unreadable but functionally the same) and packing (compressing/encrypting the whole executable, which only decompresses in memory at runtime). For the analyst, deobfuscating and unpacking is the step before understanding the sample; for the defender, both techniques break simple static signatures.

# a packer wraps the original binary: on disk you see the "stub" + encrypted data;
# at runtime, the stub decompresses/decrypts the real code in memory and jumps to it (OEP)
# signs of packing (static):
- high entropy in sections (encrypted/compressed data)
- few imports (resolved at runtime), tiny IAT
- odd section names (UPX0, .packed) or RWX sections
- almost no strings statically but many at runtime
# detection tools
Detect It Easy (DIE), PEiD, PEstudio -> identify the packer
# standard packers
upx -d sample.exe # UPX (if not modified)
# custom/commercial packers (VMProtect, Themida, ...) -> dynamic:
1. run under a debugger until the OEP (after unpacking in memory)
2. dump the now-unpacked process (see rev-dynamic)
pe-sieve / Scylla / Process Hacker -> dump
3. rebuild the IAT and analyze the dump statically
# automated: sandboxes (CAPE) often dump the unpacked payload
control flow flattening flatten the flow -> the graph is unreadable
opaque predicates always-T/F conditions that confuse analysis
dead code / junk junk instructions
API hashing instead of API names, hashes resolved at runtime
-> breaks import-based analysis (you must resolve the hashes)
code virtualization a custom interpreter runs custom bytecode (the hardest)

API hashing is very common: the malware doesn’t import VirtualAlloc by name (which would be an IOC), but computes a hash of the name and resolves it at runtime, hiding which APIs it uses from static analysis.

# encrypted/stacked strings built at runtime
# static: FLOSS extracts them (see mal-static); or locate the decryption routine
# dynamic: run and dump memory -> the strings appear decrypted
# techniques: base64, concatenation, character replacement, compression (Gzip/Deflate),
# indirect invocation (IEX), environment variables, encoding
# deobfuscate:
- CyberChef (the swiss-army knife: base64, gunzip, XOR, etc. chained)
- PowerShell: replace IEX with Write-Output to SEE the script instead of running it
- box-js / malware-jail for JavaScript; olevba for VBA
# Script Block Logging (4104) captures the already-deobfuscated PowerShell (see fund-ps)
  • Behavior detection (not static signature): obfuscation/packing breaks signatures, but runtime behavior (injection, network, encryption) is still EDR-detectable.
  • AMSI / Script Block Logging: capture scripts after deobfuscation (the key moment).
  • YARA rules over memory (not just disk): the unpacked sample in memory does have signatures (YARA Rules).
  • Detection of packers/high entropy and API hashing as suspicious signals.
  • Detect packing (entropy, few imports, DIE/PEiD)
  • Unpack (UPX -d; or dynamic: OEP + dump)
  • Rebuild IAT and analyze the dump
  • Identify code obfuscation (flattening, API hashing)
  • Resolve API hashing (scripts/plugins)
  • Extract obfuscated strings (FLOSS / memory dump)
  • Deobfuscate scripts (CyberChef, olevba, Script Block Logging)
  • YARA over memory after unpacking (YARA Rules)