3 min read

Unpacking

Analysis

Packers compress and obfuscate executables to evade detection. Unpacking recovers the original code for analysis.

Isolate Before You Analyse

Every step below executes the sample. Never unpack unknown malware on a production or networked host: use a dedicated virtual machine with no shared folders or clipboard and with a host-only adapter or no networking, and snapshot it before execution so you can revert. Assume the sample may be evasive enough to detect the debugger or VM and destructive enough to damage the guest — the unpacking stub is often where both behaviours are decided.

UPX Unpacking

upx-unpack.sh
# UPX - Most common packer
# Detect UPX
file packed.exe
# Output: PE32 executable, UPX compressed

# Unpack with UPX
upx -d packed.exe -o unpacked.exe

# Modified UPX stubs reject -d; fall back to the manual process below

# Common UPX indicators:
# - Section names: UPX0, UPX1
# - Small .text section, large UPX1 section
# - Entry point in UPX1 section

Manual Unpacking Process

The same five steps cover almost every custom packer; only the tells in the first step change:

  1. Identify the packer. Look for section names that are not compiler defaults, sections whose entropy is near-random, an entry point outside .text, and an import table holding little more than LoadLibrary and GetProcAddress. Confirm against a signature database with Detect It Easy (DIE) or pestudio.
  2. Find the original entry point. Break on VirtualAlloc and VirtualProtect to catch the stub allocating memory and marking it executable, then follow the writes into that region. The tail jump is the transfer out of the stub and into the code it has just written.
  3. Dump at the OEP. With execution stopped at the original entry point, use the Scylla plugin inside x64dbg to dump the image from process memory rather than from disk.
  4. Rebuild the import table. The dumped image points at the stub's resolved addresses, so run IAT Autosearch, then Get Imports, then Fix Dump to write a working import directory back into the copy.
  5. Verify the result. Check section permissions and headers in PE-bear, and confirm the dump loads in a disassembler without unresolved imports.

Breakpoint Strategy

Break on the stack pointer rather than hunting for the tail jump by eye. On 32-bit packers the stub typically saves every register with PUSHAD and restores them with POPAD immediately before the jump, so a hardware breakpoint on esp taken after the save fires on the restore — which lands you at the OEP jump. PUSHAD and POPAD are 32-bit only instructions: 64-bit packers save registers one at a time instead, so break on writes to rsp in the stub and on VirtualProtect for that case.