Unpacking UPX-packed Binaries - Complete Guide
UPX (Ultimate Packer for eXecutables) remains one of the most prevalent executable packers in both legitimate software and malware. This comprehensive guide covers everything from basic automated unpacking to advanced manual techniques for modified and obfuscated UPX variants.
Table of Contents
- Introduction to Executable Packing
- UPX Overview and Architecture
- Identifying UPX-packed Binaries
- Automated Unpacking Methods
- Manual Unpacking Techniques
- Handling Modified UPX Variants
- Troubleshooting and Common Issues
- Advanced Analysis Techniques
- Real-World Case Studies
Introduction to Executable Packing
Executable packing is a technique that compresses or encrypts an executable file and adds a small unpacker routine. When the packed executable runs, the unpacker decompresses the original code into memory and transfers control to it.
Why Do Attackers Use Packers?
- Evasion: Bypass signature-based antivirus detection
- Obfuscation: Hide the true functionality from static analysis
- Anti-Analysis: Complicate reverse engineering efforts
- Size Reduction: Reduce file size (legitimate use case)
- IP Protection: Protect proprietary algorithms (legitimate use case)
Types of Packers
| Packer Type | Examples | Complexity |
|---|---|---|
| Compression | UPX, ASPack, PECompact | Low |
| Encryption | Armadillo, ASProtect | Medium |
| Virtualization | VMProtect, Themida | High |
| Mutation | PolyPack, Morphine | Very High |
UPX Overview and Architecture
UPX Technical Details
UPX uses LZMA compression and creates a self-extracting executable. The packed file contains:
- UPX Stub: The unpacker code
- Compressed Data: The original executable compressed
- Metadata: Information needed for decompression
UPX Section Structure
Standard UPX-packed PE file sections:
.text - Contains the UPX unpacker stub
UPX0 - Compressed original .text section
UPX1 - Compressed original data sections
.rsrc - Resources (may be compressed or intact)
UPX Unpacking Process
- Entry Point: Execution begins at UPX stub
- Memory Allocation: Allocate memory for unpacked image
- Decompression: Decompress UPX0 and UPX1 sections
- Import Resolution: Rebuild import table
- Control Transfer: Jump to Original Entry Point (OEP)
Identifying UPX-packed Binaries
Signature-Based Detection
Using PEiD:
# Download PEiD signatures database
# Load the suspicious executable
# Check for UPX signatures:
- UPX 0.89.6 - 1.02 / 1.05 - 2.90 -> Markus Oberhumer & Laszlo Molnar
- UPX 2.90 [LZMA] -> Markus Oberhumer & Laszlo Molnar
- UPX 3.00 - 3.95 -> Markus Oberhumer, Laszlo Molnar & John Reiser
Using Detect It Easy (DiE):
# Command line usage:
die.exe suspicious_file.exe
# Look for output:
Packer: UPX(3.95)[NRV,brute]
Linker: Microsoft Linker(14.0)
Library: Microsoft Visual C/C++(2015)
Overlay: Yes
Manual Identification Techniques
Section Analysis:
# Using CFF Explorer or PE-bear:
1. Open the PE file
2. Check section names:
- .text (small size, high entropy)
- UPX0 (compressed original code)
- UPX1 (compressed data)
- .rsrc (may be present)
3. Check section characteristics:
- UPX sections often have unusual flags
- Virtual size != Raw size discrepancies
Entropy Analysis:
# High entropy indicates compression/encryption
# Use tools like:
- Binwalk: binwalk -E suspicious_file.exe
- PEStudio: Check entropy graph
- DIE: Shows entropy percentages
# UPX packed files typically show:
- .text section: Low entropy (unpacker code)
- UPX0/UPX1: High entropy (compressed data)
String Analysis:
# Search for UPX-specific strings:
strings suspicious_file.exe | grep -i upx
# Common UPX strings:
- "UPX!"
- "This file is packed with the UPX executable packer"
- "$Id: UPX"
- Various UPX error messages
Import Table Analysis
# UPX-packed files typically have minimal imports:
- kernel32.dll: LoadLibraryA, GetProcAddress, VirtualProtect
- Very few API functions compared to unpacked version
- Imports are rebuilt dynamically during unpacking
Automated Unpacking Methods
Official UPX Tool
Basic Unpacking:
# Download UPX from https://upx.github.io/
# Command syntax:
upx -d packed_file.exe
# Verbose output:
upx -d -v packed_file.exe
# Force unpacking:
upx -d --force packed_file.exe
# Backup original:
upx -d --backup packed_file.exe
UPX Tool Limitations:
- Fails on modified UPX variants
- Cannot handle corrupted UPX headers
- Blocked by some anti-unpacking tricks
- Version mismatches can cause failures
Alternative Automated Tools
QUnpack:
# Universal unpacker for various packers
# Supports UPX, ASPack, PECompact, and others
# Usage:
1. Load packed file
2. Select "Quick Unpack" or "Full Unpack"
3. Configure OEP detection method
4. Run unpacking process
Universal Unpacker (UUP):
# Automated generic unpacker
# Features:
- Multiple unpacking methods
- OEP detection algorithms
- Import reconstruction
- Memory dumping capabilities
Uni-Extract:
# Can extract from various archive and packed formats
# Command line:
uniextract.exe packed_file.exe output_directory/
Manual Unpacking Techniques
OEP (Original Entry Point) Finding
Method 1: ESP Trick
1. Load packed file in x64dbg
2. Go to entry point (usually UPX stub start)
3. Note the ESP value at entry point
4. Set hardware breakpoint on memory access at ESP address
5. Run the program (F9)
6. When breakpoint hits, check if you're at OEP
7. If not, repeat until you reach the OEP
Method 2: API Breakpoints
# Set breakpoints on common APIs used by UPX:
bp VirtualAlloc # Memory allocation for unpacked code
bp VirtualProtect # Changing memory permissions
bp GetModuleHandleA # Module resolution
bp GetProcAddress # API resolution
# When VirtualAlloc hits:
1. Note the allocated memory address
2. Set breakpoint on that memory region
3. Continue execution
4. When execution reaches allocated memory = OEP found
Method 3: Single Step Through Stub
# More time-consuming but reliable:
1. Start at entry point
2. Step through UPX stub code (F8)
3. Look for characteristic OEP patterns:
- PUSH instructions setting up stack
- MOV instructions with meaningful addresses
- Jump to high memory address
4. When you see normal program prologue = OEP found
Memory Dumping Process
Using x64dbg:
1. After finding OEP, pause execution
2. Go to Memory Map tab
3. Find the main module memory region
4. Right-click -> Dump Memory to File
5. Select appropriate size (usually virtual size)
6. Save as .bin or .exe file
7. Fix PE headers if necessary
Using OllyDump Plugin:
1. Install OllyDump plugin for x64dbg
2. Right-click at OEP -> OllyDump
3. Select dumping options:
- Base address
- Size to dump
- Entry point
4. Choose reconstruction options:
- Fix imports
- Fix relocations
- Fix sections
5. Save reconstructed PE file
Import Table Reconstruction
Manual IAT Reconstruction:
1. Locate Import Address Table (IAT) in dumped file
2. Identify imported DLLs and functions
3. Use tools like ImpREC or Import REConstructor:
- Set correct OEP
- Search for IAT
- Get imports from debugger
- Fix dump with reconstructed imports
Using Scylla:
1. Load dumped file in Scylla
2. Set correct Image Base and OEP
3. Click "IAT Autosearch"
4. Click "Get Imports"
5. Verify import list
6. Click "Fix Dump" to reconstruct IAT
7. Save fixed PE file
Handling Modified UPX Variants
Common UPX Modifications
1. Magic Signature Changes:
# Original UPX magic: "UPX!"
# Modified versions may change this to:
- Random 4-byte values
- Zeroed out signatures
- Different ASCII strings
# Detection:
1. Look for section names (UPX0, UPX1)
2. Check compression algorithm indicators
3. Analyze decompression stub patterns
2. Section Name Obfuscation:
# Original: .text, UPX0, UPX1
# Modified: Random names like:
- .data, .rdata, .code
- .sdata, .idata, .tls
- Completely random: .abc, .xyz
# Detection method:
1. Check section entropy levels
2. Analyze section size ratios
3. Look for decompression code patterns
3. Stub Modification:
# Common stub modifications:
- Anti-debugging checks added
- Junk code insertion
- API call obfuscation
- Timing checks
- VM detection routines
Analysis Techniques for Modified UPX
Entropy-Based Detection:
# Use Python script for entropy analysis:
import math
from collections import Counter
def calculate_entropy(data):
if not data:
return 0
entropy = 0
for count in Counter(data).values():
p_x = count / len(data)
if p_x > 0:
entropy += - p_x * math.log2(p_x)
return entropy
# High entropy sections likely contain compressed data
# Entropy > 7.0 typically indicates compression/encryption
Pattern Recognition:
# Search for UPX decompression patterns:
# Common x86 instructions in UPX stub:
- PUSHA/PUSHAD (60)
- POPA/POPAD (61)
- LODSD (AD)
- STOSD (AB)
- LOOP instructions (E2)
# Search for these patterns in the entry section
Generic Unpacking Approach
Algorithm Independence Method:
1. Identify compressed sections by entropy
2. Set breakpoints on memory allocation APIs
3. Monitor for large memory allocations
4. Track when compressed data is written to new memory
5. Follow execution to decompressed code
6. Dump when normal execution patterns resume
Behavioral Analysis:
# Monitor for UPX-like behavior:
1. Large VirtualAlloc calls (size of original executable)
2. Memory protection changes (PAGE_EXECUTE_READWRITE)
3. Bulk memory copying operations
4. Import table reconstruction activity
5. Jump to newly allocated memory region
Troubleshooting and Common Issues
UPX Tool Failures
Error: "Not packed by UPX"
# Possible causes:
1. Modified UPX signature
2. Corrupted UPX headers
3. Additional protection layers
4. Version mismatch
# Solutions:
1. Try different UPX versions
2. Use manual unpacking methods
3. Repair headers with hex editor
4. Remove overlay data if present
Error: "Damaged UPX header"
# Repair strategy:
1. Compare with known good UPX file
2. Fix UPX signature (offset usually at end of file)
3. Restore section alignment values
4. Fix compressed data integrity markers
Dumping Issues
Incomplete Memory Dumps:
# Symptoms:
- Missing sections in dumped file
- Crashes when running unpacked file
- Import resolution failures
# Solutions:
1. Ensure all sections are unpacked before dumping
2. Check memory protection flags
3. Verify virtual vs raw sizes
4. Use process dumping instead of memory dumping
Import Table Problems:
# Common issues:
- IAT not fully reconstructed
- Incorrect API addresses
- Missing DLL dependencies
# Fixes:
1. Use multiple import reconstruction tools
2. Compare with static analysis results
3. Manual IAT patching if necessary
4. Check for delayed imports
Anti-Unpacking Countermeasures
Debugger Detection Bypass:
# If UPX stub contains anti-debugging:
1. Use ScyllaHide plugin
2. Patch IsDebuggerPresent calls
3. Modify PEB flags manually
4. Use system-level debuggers
Timing Attacks:
# If unpacking fails due to timing checks:
1. Patch RDTSC instructions
2. Modify GetTickCount returns
3. Use timing-resistant analysis environment
4. Accelerate execution in emulator
Advanced Analysis Techniques
Automated Unpacking Scripts
x64dbg Script for UPX:
// UPX unpacking script
// Set breakpoint on VirtualAlloc
bp VirtualAlloc
// Log allocation details
$breakpointcommand = log "VirtualAlloc: addr={p:arg.1}, size={p:arg.2}, prot={p:arg.4}"
// Continue until large allocation
run
// Find allocation for unpacked code (typically > 0x10000)
// Set breakpoint on allocated memory
// Continue until execution reaches unpacked code
// Dump memory region
Python Automation:
import pefile
import os
def analyze_upx_sample(filepath):
"""Automated UPX analysis"""
pe = pefile.PE(filepath)
# Check for UPX sections
upx_sections = []
for section in pe.sections:
if b'UPX' in section.Name:
upx_sections.append(section)
# Calculate entropy for each section
entropies = {}
for section in pe.sections:
data = section.get_data()
entropy = calculate_entropy(data)
entropies[section.Name] = entropy
return upx_sections, entropies
def attempt_automated_unpack(filepath):
"""Try multiple unpacking methods"""
methods = [
f"upx -d {filepath}",
f"upx -d --force {filepath}",
f"qunpack.exe {filepath}",
]
for method in methods:
try:
os.system(method)
if verify_unpacked(filepath):
return True
except:
continue
return False
Memory Forensics Approach
Process Dumping:
# Using Volatility for memory analysis:
1. Create memory dump while UPX is unpacking
2. Extract process using:
vol.py -f memory.dmp --profile=Win10x64 procdump -p -D output/
3. Analyze dumped process for unpacked code
4. Extract clean executable from process dump
API Hooking:
# Use API Monitor or custom hooks:
1. Hook VirtualAlloc/VirtualProtect
2. Log all memory operations
3. Identify when UPX allocates memory for unpacked code
4. Hook WriteProcessMemory to capture decompression
5. Extract final payload from memory
Emulation-Based Unpacking
Using QEMU with GDB:
# Set up QEMU with GDB stub:
qemu-system-i386 -gdb tcp::1234 -S -hda windows.img
# Connect GDB:
gdb
(gdb) target remote localhost:1234
(gdb) continue
# Set breakpoints on unpacking events
# Single-step through decompression
# Extract unpacked code from guest memory
Real-World Case Studies
Case Study 1: Modified UPX in Malware Campaign
Sample Details:
MD5: a1b2c3d4e5f6...
SHA1: 1a2b3c4d5e6f...
Size: 847KB
Packer: Modified UPX 3.95
Modifications Observed:
- UPX signature changed from "UPX!" to "PKR!"
- Section names changed to .text, .data, .rsrc
- Anti-debugging checks added to stub
- Junk code inserted in decompression routine
Analysis Approach:
- Entropy Analysis: Identified high-entropy sections despite name changes
- Pattern Recognition: Found UPX decompression patterns in .text section
- Anti-Debug Bypass: Used ScyllaHide to bypass protection
- Manual OEP Finding: Used ESP trick to locate original entry point
- Memory Dumping: Extracted clean payload using Scylla
Case Study 2: UPX with Overlay Data
Challenge:
Sample had additional data appended after UPX structure, causing standard tools to fail.
Solution:
1. Identified overlay using PE analysis tools
2. Calculated correct UPX data boundaries
3. Extracted clean UPX portion:
dd if=sample.exe of=clean_upx.exe bs=1 count=
4. Unpacked clean portion successfully
5. Analyzed overlay data separately (configuration/payload)
Case Study 3: Multi-Layer Protection
Protection Stack:
Layer 1: Custom crypter
Layer 2: Modified UPX packer
Layer 3: Original malware payload
Unpacking Strategy:
- Analyzed crypter layer using dynamic analysis
- Extracted UPX-packed intermediate stage
- Applied modified UPX unpacking techniques
- Recovered final malware payload
- Analyzed payload using standard techniques
Tools and Resources
Essential Tools
- UPX Official Tool: Standard unpacking utility
- x64dbg: Primary debugger for manual unpacking
- Scylla: Import reconstruction
- PEiD/DiE: Packer identification
- CFF Explorer: PE analysis
- HxD/010 Editor: Hex editing
- API Monitor: API call monitoring
- Process Monitor: System activity monitoring
Specialized Unpackers
- QUnpack: Multi-packer unpacker
- GUnPacker: Generic unpacker
- RL!dePacker: Various packers support
- Quick Unpack: OllyDbg plugin
Python Libraries
# Useful Python packages for automation:
pip install pefile # PE file analysis
pip install capstone # Disassembly engine
pip install yara-python # Pattern matching
pip install volatility3 # Memory analysis
pip install r2pipe # Radare2 integration
Best Practices and Recommendations
Analysis Environment
- Isolated VM: Always use isolated virtual machines
- Snapshots: Take clean snapshots before analysis
- Multiple Tools: Don't rely on single unpacking method
- Documentation: Document all steps for reproducibility
- Backup: Keep copies of original packed samples
Methodology
- Static Analysis First: Identify packer and modifications
- Automated Before Manual: Try automated tools first
- Multiple Approaches: Use different unpacking methods
- Verify Results: Ensure unpacked file is functional
- Clean Up: Remove unpacker artifacts from final file
Common Pitfalls
- Don't assume standard UPX: Always check for modifications
- Verify import reconstruction: Broken imports cause crashes
- Check for multiple layers: Unpacking may reveal another packer
- Watch for time bombs: Some samples have delayed execution
- Monitor network activity: Some packers communicate with C2
Conclusion
Unpacking UPX-packed binaries requires a combination of automated tools, manual techniques, and deep understanding of the UPX architecture. While standard UPX can often be unpacked automatically, modified variants require advanced analysis skills and creative problem-solving.
The key to successful unpacking is methodical analysis, using multiple approaches, and adapting techniques based on the specific modifications encountered. Modern malware often uses heavily modified packers, making manual unpacking skills essential for malware analysts.
Remember that unpacking is just the first step in malware analysis. The unpacked payload should undergo comprehensive static and dynamic analysis to understand its full functionality and impact.
Additional Learning Resources
- Official UPX Documentation
- UPX Source Code
- Malware analysis courses focusing on packing techniques
- Reverse engineering communities and forums
- Academic papers on executable packing and unpacking
Always analyze malware samples in isolated environments and follow responsible disclosure practices for any discovered vulnerabilities.