# Packer Unpacking

> Identify and unpack common binary packers — UPX, ASPack, Themida, VMProtect, MPRESS, PECompact, Enigma.

- Skill: `purpleailab/packer-unpacking` (Agent Skill)
- Install (CLI): `npx skillmds@latest add purpleailab/packer-unpacking`
- Raw SKILL.md: https://api.skillmd.com/api/skills/purpleailab/packer-unpacking/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: purpleailab (https://skillmd.com/u/purpleailab)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/purpleailab/packer-unpacking

---


# Packer Unpacking Playbook

Packers compress and/or obfuscate binaries to defeat static analysis.
The first job: identify which packer, then dispatch the right unpacker
(or manual unpacking strategy if no automated tool exists).

## 1. Detect packing
```bash
# Entropy quick-check (>7.0 across the binary = likely packed)
ent /tmp/sample
# or
python3 -c "
import sys, math
d = open('/tmp/sample','rb').read()
f = [0]*256
for b in d: f[b] += 1
h = -sum((c/len(d))*math.log2(c/len(d)) for c in f if c)
print(f'entropy={h:.3f}')
"

# Section-level entropy via radare2
r2 -qc "iSj" /tmp/sample | jq '.[] | "\(.name): \(.entropy)"'

# Tool-based detection
detect-it-easy /tmp/sample  # most reliable, GUI + CLI
diec /tmp/sample           # CLI for DIE
yara -r /opt/yara-rules/packers/ /tmp/sample
```

Decepticon helper:
```
bin_packer("/tmp/sample")
```

## 2. Common packer signatures

| Packer | Signature |
|---|---|
| UPX | `UPX!` magic at section header, sections named `UPX0`, `UPX1` |
| ASPack | `.aspack` section, jump after entry to packed code |
| Themida | `.themida` section, anti-debug, anti-VM heavy |
| VMProtect | `.vmp0`, `.vmp1` sections; obfuscated EP w/ virtualized handlers |
| MPRESS | `.MPRESS1`, `.MPRESS2` sections |
| PECompact | `pec1` section, encrypted sections |
| Enigma | `.enigma1`, `.enigma2` sections |
| Petite | small overlay, `.petite` section |
| FSG | tiny imports, packed sections |
| MEW | `MEW` magic in section name |
| Armadillo | runtime decryption, anti-debug (older) |

## 3. Automated unpacking

### UPX (easy)
```bash
upx -d /tmp/sample -o /tmp/unpacked
file /tmp/unpacked
```
If `upx -d` fails with "not packed by UPX", the version field has been
tampered with (anti-unpack trick). Fix:
```bash
# Patch the version byte back
python3 -c "
d = bytearray(open('/tmp/sample','rb').read())
# Find UPX! magic, fix version
import re
for m in re.finditer(b'UPX!', d):
    d[m.end()] = 0x0d  # set version field
open('/tmp/patched','wb').write(d)
"
upx -d /tmp/patched -o /tmp/unpacked
```

### ASPack
```bash
unaspack /tmp/sample  # or use ASPackDie / ASPack Stripper
```
Manual: ASPack's OEP jump is `JMP <reg>` at the end of unpack stub.
Set breakpoint there in x64dbg, dump from `Scylla` (PE only).

### MPRESS
```bash
quickunpack /tmp/sample
# Or load in x64dbg, set BP on tail jump (E9 to OEP), dump w/ Scylla
```

### PECompact / FSG
Use `unpacme` (uploads to UnpacMe service if engagement permits cloud
processing), or run in monitored sandbox + memory-dump strategy.

## 4. Manual unpacking strategy (Themida / VMProtect / Enigma)

These are commercial-grade and don't have reliable auto-unpackers.
Approach:

### Themida
1. **Static**: identify anti-debug checks, patch them or rewrite
2. **Dynamic**: x64dbg + `ScyllaHide` plugin → bypass anti-debug
3. Set hardware breakpoint on `VirtualProtect` (Themida unpacks via this)
4. When hit, walk back to find decrypted code regions
5. Dump w/ `Scylla` after OEP is reached
6. Themida often has multiple layers — repeat per layer

### VMProtect
VMProtect translates code into bytecode for a custom VM. No simple
"unpack" — you must either:
- Devirtualize (extract VM handlers + write a translator). Tools:
  `VTIL` (Vladimir's tools), `vmpfix`, manual w/ IDA + bytecode trace
- Trace + symbolic execute via `Triton` or `angr`
- Skip RE and treat as black-box (fuzz the interfaces)

### Enigma Protector
Similar to Themida. ScyllaHide handles many checks. The license / virt
machine layer is hardest. Some Enigma variants:
- v3-v5: scriptable unpack via `Enigma Static Unpacker`
- v6+: manual w/ x64dbg + Scylla, multiple decryption passes

## 5. Manual unpack technique (universal)

For any packer:
1. Disable ASLR / DEP if needed (ScyllaHide / `setdllchar`)
2. Set BP on entry point
3. Step through unpack stub; watch for:
   - Large `VirtualAlloc` (decryption region)
   - `memset` followed by decrypted code being written
   - Tail jump to OEP (often `JMP <reg>` or `RET` after PUSHAD/POPAD)
4. At suspected OEP, dump process w/ `Scylla` (PE) or `r2 -d`
5. Fix imports (Scylla auto-rebuild IAT), save dumped PE
6. Re-run static analysis on the dumped file

## 6. Anti-anti-unpacking tricks

| Anti-unpack | Counter |
|---|---|
| `IsDebuggerPresent` | ScyllaHide, or patch w/ NOP |
| `NtQueryInformationProcess`(ProcessDebugPort) | ScyllaHide |
| Timing checks (`rdtsc` measure) | x64dbg "timing" plugin or patch |
| INT3 detection (BP byte scan) | hardware BPs only |
| Self-checksum | identify check loop, patch comparison |
| TLS callbacks (run before main entry) | BP in TLS callback list (IDA: View → Open Subviews → TLS) |
| Anti-VM (CPUID hypervisor bit) | Run on bare metal or KVM w/ CPUID masking |

## 7. Promote
```
kg_add_node(kind="observation", label="packed: <packer-name>",
            props={"sample":"<sha256>","entropy":<float>,"packer":"<name>"})
kg_add_edge(src=<sample>, dst=<observation>, kind="exhibits")

# After unpack, re-run triage on the dumped file
kg_add_node(kind="artifact", label="unpacked: <sha256>",
            props={"original":"<orig-sha256>","unpacker":"<tool>"})
```

## Severity (not a vuln, but a triage gate)

| Outcome | Implication for engagement |
|---|---|
| Automated unpack succeeded → static analysis viable | Normal triage path |
| Only partial unpack (multi-layer) | Use dynamic analysis as primary |
| VMProtect / Themida heavy | Likely commercial protection — escalate effort, schedule realistically |
| Cannot unpack | Black-box fuzz + dynamic only; document static-blind constraint |

## Known exemplars
- Stuxnet: multi-layer packing including custom routines
- WannaCry: UPX + custom obfuscation
- Most commodity malware: UPX (because it's free + easy)
- Banking trojans: Themida or VMProtect common
- Cobalt Strike beacons: encrypted shellcode + reflective loader, "packer-like"

