Unpacking UPX-packed Binaries - Complete Guide

Reverse Engineering Advanced 📅 Published: 30/09/2025

UPX (Ultimate Packer for eXecutables) remains one of the most prevalent executable packers in both legitimate software and malware. This comprehens...

Unpacking UPX-packed Binaries - Complete Guide

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

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

  1. Entry Point: Execution begins at UPX stub
  2. Memory Allocation: Allocate memory for unpacked image
  3. Decompression: Decompress UPX0 and UPX1 sections
  4. Import Resolution: Rebuild import table
  5. 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:

  1. Entropy Analysis: Identified high-entropy sections despite name changes
  2. Pattern Recognition: Found UPX decompression patterns in .text section
  3. Anti-Debug Bypass: Used ScyllaHide to bypass protection
  4. Manual OEP Finding: Used ESP trick to locate original entry point
  5. 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:

  1. Analyzed crypter layer using dynamic analysis
  2. Extracted UPX-packed intermediate stage
  3. Applied modified UPX unpacking techniques
  4. Recovered final malware payload
  5. 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

  1. Static Analysis First: Identify packer and modifications
  2. Automated Before Manual: Try automated tools first
  3. Multiple Approaches: Use different unpacking methods
  4. Verify Results: Ensure unpacked file is functional
  5. 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

Always analyze malware samples in isolated environments and follow responsible disclosure practices for any discovered vulnerabilities.