How to Use an UPX Unpacker: Step-by-Step Tutorial

Automating UPX Unpacking: Scripts and Best Practices

Overview

Automating UPX unpacking speeds analysis and triage of many UPX-packed binaries. Typical automation detects UPX-packed files, runs an appropriate unpacking command, verifies success, logs results, and handles failures for manual follow-up.

Recommended tools

  • upx (official) — packer/unpacker; supports –decompress (-d).
  • Detect It Easy (diec) or binwalk — detection of packers.
  • pefile (Python) — inspect PE headers to confirm unpacking.
  • sigscan/clamscan — optional signature checks.
  • Shell, Python, or PowerShell — scripting environments.

Example workflow (reasonable defaults)

  1. Scan files for UPX packing.
  2. Run upx -d or the appropriate decompression for the architecture.
  3. Verify unpacked binary (check PE entry point, import table).
  4. If verification fails, try alternative strategies (force unpack, run in sandbox to capture unpacked memory).
  5. Record results and move successes/failures to separate folders.

Bash script (Linux/macOS) — batch unpack and verify

bash

#!/usr/bin/env bash INPUT_DIR=”./samples” OK_DIR=”./unpacked” FAIL_DIR=”./failed” mkdir -p \(OK_DIR</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)FAIL_DIR for f in \(INPUT_DIR</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span>/*</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span class="token" style="color: rgb(0, 0, 255);">do</span><span> </span><span></span><span class="token" style="color: rgb(57, 58, 52);">file</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)f | grep -qi “executable” || continue # detect UPX if strings \(f</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">|</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">grep</span><span> -qi </span><span class="token" style="color: rgb(163, 21, 21);">"UPX"</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span class="token" style="color: rgb(0, 0, 255);">then</span><span> </span><span> </span><span class="token" style="color: rgb(57, 58, 52);">cp</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)f \(f</span><span class="token" style="color: rgb(163, 21, 21);">.orig"</span><span> </span><span> </span><span class="token" style="color: rgb(0, 0, 255);">if</span><span> upx -d -o </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">\){OK_DIR}/\((</span><span class="token" style="color: rgb(57, 58, 52);">basename</span><span class="token" style="color: rgb(54, 172, 170);"> </span><span class="token" style="color: rgb(54, 172, 170);">"</span><span class="token" style="color: rgb(54, 172, 170);">\)f) \(f</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token file-descriptor" style="color: rgb(238, 153, 0); font-weight: bold;">2</span><span class="token" style="color: rgb(57, 58, 52);">></span><span>/tmp/upx.err</span><span class="token" style="color: rgb(57, 58, 52);">;</span><span> </span><span class="token" style="color: rgb(0, 0, 255);">then</span><span> </span><span> </span><span class="token" style="color: rgb(0, 128, 0); font-style: italic;"># quick verification: check for imports (PE)</span><span> </span><span> python3 - </span><span class="token" style="color: rgb(57, 58, 52);"><<</span><span class="token" style="color: rgb(163, 21, 21);">PY </span><span class="token" style="color: rgb(163, 21, 21);">import sys, pefile </span><span class="token" style="color: rgb(163, 21, 21);">try: </span><span class="token" style="color: rgb(163, 21, 21);"> p=pefile.PE(sys.argv[1]) </span><span class="token" style="color: rgb(163, 21, 21);"> print("OK") </span><span class="token" style="color: rgb(163, 21, 21);">except Exception as e: </span><span class="token" style="color: rgb(163, 21, 21);"> print("FAIL", e) </span><span class="token" style="color: rgb(163, 21, 21);">PY</span><span> </span><span> </span><span class="token" style="color: rgb(0, 0, 255);">if</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">[</span><span> </span><span class="token" style="color: rgb(163, 21, 21);">"\)(python3 - “\(OK_DIR</span><span class="token" style="color: rgb(163, 21, 21);">/</span><span class="token" style="color: rgb(54, 172, 170);">\)(basename \(f</span><span class="token" style="color: rgb(54, 172, 170);">"</span><span class="token" style="color: rgb(54, 172, 170);">)</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span> </span><span class="token file-descriptor" style="color: rgb(238, 153, 0); font-weight: bold;">2</span><span class="token" style="color: rgb(57, 58, 52);">></span><span>/dev/null </span><span class="token" style="color: rgb(57, 58, 52);">|</span><span> </span><span class="token" style="color: rgb(57, 58, 52);">head</span><span> -n1</span><span class="token" style="color: rgb(57, 58, 52);">)</span><span class="token" style="color: rgb(163, 21, 21);">" = "</span><span>OK</span><span class="token" style="color: rgb(163, 21, 21);">" ]; then </span><span class="token" style="color: rgb(163, 21, 21);"> echo "</span><span class="token" style="color: rgb(54, 172, 170);">\)(basename \(f</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">)</span><span class="token builtin" style="color: rgb(43, 145, 175);">:</span><span> unpacked</span><span class="token" style="color: rgb(163, 21, 21);">" >> unpack.log </span><span class="token" style="color: rgb(163, 21, 21);"> else </span><span class="token" style="color: rgb(163, 21, 21);"> mv "</span><span class="token" style="color: rgb(54, 172, 170);">\){OK_DIR}/\((</span><span class="token" style="color: rgb(57, 58, 52);">basename</span><span class="token" style="color: rgb(54, 172, 170);"> </span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(163, 21, 21);">\)f)” “\(FAIL_DIR</span><span>/</span><span class="token" style="color: rgb(163, 21, 21);">" </span><span class="token" style="color: rgb(163, 21, 21);"> echo "</span><span class="token" style="color: rgb(54, 172, 170);">\)(basename \(f</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">)</span><span class="token builtin" style="color: rgb(43, 145, 175);">:</span><span> unpacked but verification failed</span><span class="token" style="color: rgb(163, 21, 21);">" >> unpack.log </span><span class="token" style="color: rgb(163, 21, 21);"> fi </span><span class="token" style="color: rgb(163, 21, 21);"> else </span><span class="token" style="color: rgb(163, 21, 21);"> echo "</span><span class="token" style="color: rgb(54, 172, 170);">\)(basename \(f</span><span class="token" style="color: rgb(163, 21, 21);">"</span><span class="token" style="color: rgb(54, 172, 170);">)</span><span class="token builtin" style="color: rgb(43, 145, 175);">:</span><span> upx -d failed</span><span class="token" style="color: rgb(163, 21, 21);">" >> unpack.log </span><span class="token" style="color: rgb(163, 21, 21);"> mv "</span><span class="token" style="color: rgb(54, 172, 170);">\)f” “$FAIL_DIR/” fi fi done

Python script (cross-platform) — detect + call UPX, log results

  • Use subprocess to invoke upx.
  • Use pefile to validate imports/entry point.
  • Use concurrent.futures for parallelism.

Best practices

  • Verify results programmatically (PE imports, entry point, entropy drop).
  • Keep originals (store .orig copies) for forensic integrity.
  • Rate-limit/parallelize to avoid resource exhaustion; use a small worker pool.
  • Handle different architectures (32 vs 64-bit) and non-PE formats.
  • Fallback strategies: try running in a sandboxed VM or emulator and dump memory after execution if static unpack fails.
  • Logging & metrics: record filename, original hash, result, error output, timestamp.
  • Security: run unpacking in isolated environment (VM, container) and avoid executing untrusted binaries on host.
  • Tool versions: pin UPX and supporting libraries; UPX formats evolve.
  • Avoid over-automation: flag ambiguous cases for manual review.

Common pitfalls

  • UPX-packed stubs altered or non-standard UPX variants — upx -d may fail.
  • False positives from strings matching “UPX”.
  • High-entropy packed data may need additional unpacking passes.
  • Packed loaders that decrypt at runtime — require dynamic unpacking.

Quick verification heuristics

  • PE import table populated (pefile shows imports).
  • Entry point located in “.text” and not a tiny stub.
  • Entropy decrease after unpacking (use binwalk/entropy tools).
  • File type still an executable.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *