Analyzing UPX-Packed Malware

VerifiedCaution

Identify and unpack UPX-packed malware samples, including binaries with modified headers, to recover the original executable for static analysis.

Sby Skills Guide Bot
SecurityAdvanced
11/2/2026
Claude CodeCursorCodex
#malware-analysis#unpacking#upx#static-analysis#reverse-engineering

Recommended for

Automated summary

Guides identification of UPX-packed malware and recovery of the original executable by repairing modified UPX headers when automated decompression fails.

Strengths

  • Provides a concrete detection workflow using DIE, a UPX test, section entropy, and pefile import-table inspection.
  • Uses a graduated fallback: confirm the packer, try standard UPX decompression, then repair UPX magic bytes and headers.
  • Calls out tool prerequisites and an isolated, network-disabled analysis VM.
  • Explains common packed-binary indicators such as few imports, LoadLibrary/GetProcAddress resolution, and UPX0/UPX1 sections.

Limitations

  • Covers only UPX and simple header tampering, not custom packers or VM-based protectors such as Themida or VMProtect.
  • The supplied content stops partway through the header-repair code, so the workflow is incomplete as written.
  • Depends on external tools (UPX, DIE, pefile, PE-bear, x64dbg) and does not detail memory-dump-based unpacking.
When to use it

Use when a suspicious Windows PE shows packing indicators such as high entropy, UPX section names, or a minimal import table and you need a static-analysis copy.

When not to use it

Do not use for custom packers, VM-based protectors, or samples better handled by dynamic unpacking in a debugger.

Security analysis

Caution
Quality score84/100

The content is a legitimate defensive malware-analysis workflow: packer identification, UPX decompression, header repair, debugger-based manual unpacking, and PE validation. Code samples operate on local files and do not exfiltrate data or run obfuscated payloads. The only real risk is operational — manipulating and potentially executing live malware — which is mitigated by the stated requirement for an isolated, offline analysis VM. Hence caution rather than safe, but no inherent listing hazard.

Findings
  • •Skill requires handling and modifying live malware samples (binary patching, upx -d, pefile writes) and describes executing unpacked binaries for validation; safe only if performed inside the isolated, network-disabled VM the skill itself specifies.
  • •Includes package installation (apt install upx-ucl) and external download from upx.github.io — legitimate but requires trust in the source/package.
  • •No exfiltration, no curl|sh piping, no destructive filesystem commands, no secret access, and no attempts to override the auditor's instructions.
  • •Contains Python scripts that open and rewrite binary files (sample and repair copies) — legitimate, but the repair loop replaces every 'UPX' three-byte sequence in the file, which could corrupt the sample rather than fix a single header; recommend operating only on copies and verifying before execution.
  • •Skill involves handling and potentially running live malware samples; it correctly mandates an isolated, network-disconnected analysis VM.
  • •No exfiltration, no obfuscated payloads, no credential/token handling, no destructive host commands (no rm -rf, no curl|sh), and no attempts to disable safety controls.
  • •No prompt-injection or instruction-override attempts found in the content.
How this analysis is made

Examples

UPX decompression failure
I have a suspicious sample at /samples/invoice.exe. DIE reports UPX, but upx -d fails with 'not packed by UPX'. Walk me through identifying the packer and repairing the headers to get an unpacked copy for Ghidra.
Packed PE triage
Help me check whether this PE is packed: it has UPX0/UPX1 sections, entropy around 7.9, and only LoadLibrary/GetProcAddress imports. What should I do next in an isolated analysis VM?
Safe unpacking workflow
Create a safe workflow to confirm UPX packing, attempt decompression, repair modified UPX headers if needed, and verify the unpacked binary before disassembly.

name: analyzing-packed-malware-with-upx-unpacker description: 'Identifies and unpacks UPX-packed malware samples, including binaries with modified UPX magic bytes or headers that block automated decompression, to recover the original executable for static analysis. Use when a sample shows high entropy, minimal imports, or only LoadLibrary/GetProcAddress in its import table, or when preparing a packed binary for disassembly in Ghidra or IDA.

' domain: cybersecurity subdomain: malware-analysis tags:

  • malware
  • unpacking
  • UPX
  • packing
  • static-analysis version: 1.0.0 author: mahipal license: Apache-2.0 nist_csf:
  • DE.AE-02
  • RS.AN-03
  • ID.RA-01
  • DE.CM-01 mitre_attack:
  • T1027.002
  • T1140
  • T1620

Analyzing Packed Malware with UPX Unpacker

When to Use

  • Static analysis reveals high entropy sections and minimal imports indicating the binary is packed
  • PEiD, Detect It Easy, or PEStudio identifies UPX or another known packer
  • The import table contains only LoadLibrary and GetProcAddress (runtime import resolution typical of packed binaries)
  • You need to recover the original binary for proper disassembly and decompilation in Ghidra or IDA
  • Automated UPX decompression fails because the malware author modified UPX magic bytes or headers

Do not use when dealing with custom packers, VM-based protectors (Themida, VMProtect), or samples where dynamic unpacking via debugging is more appropriate.

Prerequisites

  • UPX (Ultimate Packer for eXecutables) installed (apt install upx-ucl or download from https://upx.github.io/)
  • Detect It Easy (DIE) for packer identification
  • Python 3.8+ with pefile library for manual header repair
  • x64dbg or x32dbg for manual unpacking when automated tools fail
  • PE-bear or CFF Explorer for PE header inspection and repair
  • Isolated analysis VM without network connectivity

Workflow

Step 1: Identify the Packer

Determine if the sample is packed and identify the packer:

# Check with Detect It Easy
diec suspect.exe

# Check with UPX (test without unpacking)
upx -t suspect.exe

# Python-based entropy and packer detection
python3 << 'PYEOF'
import pefile
import math

pe = pefile.PE("suspect.exe")

print("Section Analysis:")
for section in pe.sections:
    name = section.Name.decode().rstrip('\x00')
    entropy = section.get_entropy()
    raw = section.SizeOfRawData
    virtual = section.Misc_VirtualSize
    print(f"  {name:8s} Entropy: {entropy:.2f}  Raw: {raw:>8}  Virtual: {virtual:>8}")

# Check for UPX section names
section_names = [s.Name.decode().rstrip('\x00') for s in pe.sections]
if 'UPX0' in section_names or 'UPX1' in section_names:
    print("\n[!] UPX section names detected")
elif '.upx' in [s.lower() for s in section_names]:
    print("\n[!] UPX variant section names detected")

# Check import count (packed binaries have very few)
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
    total_imports = sum(len(e.imports) for e in pe.DIRECTORY_ENTRY_IMPORT)
    print(f"\nTotal imports: {total_imports}")
    if total_imports < 10:
        print("[!] Very few imports - likely packed")
else:
    print("\n[!] No import directory - heavily packed")
PYEOF

Step 2: Attempt Standard UPX Decompression

Try the built-in UPX decompression:

# Standard UPX decompress
upx -d suspect.exe -o unpacked.exe

# If UPX fails with "not packed by UPX" error, the headers may be modified
# Verbose output for debugging
upx -d suspect.exe -o unpacked.exe -v 2>&1

# Verify the unpacked file
file unpacked.exe
diec unpacked.exe

Step 3: Repair Modified UPX Headers

If standard decompression fails, repair tampered magic bytes:

# Repair modified UPX headers
import struct

with open("suspect.exe", "rb") as f:
    data = bytearray(f.read())

# UPX magic bytes: "UPX!" (0x55505821)
# Malware authors commonly modify these to prevent automatic unpacking

# Search for modified UPX signatures
upx_magic = b"UPX!"
modified_patterns = [b"UPX0", b"UPX\x00", b"\x00PX!", b"UPx!"]

# Find and restore section names
pe_offset = struct.unpack_from("<I", data, 0x3C)[0]
num_sections = struct.unpack_from("<H", data, pe_offset + 6)[0]
section_table_offset = pe_offset + 0x18 + struct.unpack_from("<H", data, pe_offset + 0x14)[0]

print(f"PE offset: 0x{pe_offset:X}")
print(f"Number of sections: {num_sections}")
print(f"Section table offset: 0x{section_table_offset:X}")

for i in range(num_sections):
    offset = section_table_offset + (i * 40)
    name = data[offset:offset+8]
    print(f"Section {i}: {name}")

# Restore UPX magic bytes in the binary
# Search for the UPX header signature location (typically near the end of packed data)
for i in range(len(data) - 4):
    if data[i:i+3] == b"UPX" and data[i+3] != ord("!"):
        print(f"Found modified UPX magic at offset 0x{i:X}: {data[i:i+4]}")
        data[i:i+4] = b"UPX!"
        print(f"Restored to: UPX!")

# Also restore section names if modified
for i in range(num_sections):
    offset = section_table_offset + (i * 40)
    name = data[offset:offset+8].rstrip(b'\x00')
    if name in [b"UPX0", b"UPX1", b"UPX2"]:
        continue  # Already correct
    # Check for common modifications
    if name.startswith(b"UP") or name.startswith(b"ux"):
        original = f"UPX{i}".encode().ljust(8, b'\x00')
        data[offset:offset+8] = original
        print(f"Restored section name at 0x{offset:X} to {original}")

with open("suspect_fixed.exe", "wb") as f:
    f.write(data)

print("\nFixed file written. Retry: upx -d suspect_fixed.exe -o unpacked.exe")

Step 4: Manual Unpacking with Debugger

When automated unpacking fails entirely, use dynamic unpacking:

Manual UPX Unpacking with x64dbg:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Load packed sample in x64dbg
2. Run to the entry point (system breakpoint then F9)
3. UPX unpacking stub pattern:
   a. PUSHAD (saves all registers)
   b. Decompression loop (processes packed sections)
   c. Resolves imports (LoadLibrary/GetProcAddress calls)
   d. POPAD (restores registers)
   e. JMP to OEP (original entry point)
4. Set hardware breakpoint on ESP after PUSHAD:
   - After PUSHAD, right-click ESP in registers -> Follow in Dump
   - Set hardware breakpoint on access at [ESP] address
   - Run (F9) - breaks at POPAD before JMP to OEP
5. Step forward (F7/F8) until you reach the JMP to OEP
6. At OEP: Use Scylla plugin to dump and fix imports:
   - Plugins -> Scylla -> OEP = current EIP
   - Click "IAT Autosearch" -> "Get Imports"
   - Click "Dump" to save unpacked binary
   - Click "Fix Dump" to repair import table

Step 5: Validate Unpacked Binary

Verify the unpacked sample is valid and complete:

# Verify unpacked PE is valid
python3 << 'PYEOF'
import pefile

pe = pefile.PE("unpacked.exe")

# Check sections are normal
print("Unpacked Section Analysis:")
for section in pe.sections:
    name = section.Name.decode().rstrip('\x00')
    entropy = section.get_entropy()
    print(f"  {name:8s} Entropy: {entropy:.2f}")

# Verify imports are resolved
print(f"\nImport count:")
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
    for entry in pe.DIRECTORY_ENTRY_IMPORT:
        dll = entry.dll.decode()
        count = len(entry.imports)
        print(f"  {dll}: {count} functions")
    total = sum(len(e.imports) for e in pe.DIRECTORY_ENTRY_IMPORT)
    print(f"  Total: {total} imports")

# Compare file sizes
import os
packed_size = os.path.getsize("suspect.exe")
unpacked_size = os.path.getsize("unpacked.exe")
print(f"\nPacked:   {packed_size:>10} bytes")
print(f"Unpacked: {unpacked_size:>10} bytes")
print(f"Ratio:    {unpacked_size/packed_size:.1f}x")
PYEOF

Key Concepts

| Term | Definition | |------|------------| | Packing | Compressing or encrypting executable code to reduce file size and hinder static analysis; the binary contains an unpacking stub that restores code at runtime | | UPX | Ultimate Packer for eXecutables; open-source executable packer commonly abused by malware authors because it is free and effective | | Original Entry Point (OEP) | The real starting address of the malware code before packing; the unpacking stub decompresses code then jumps to the OEP | | Import Reconstruction | Process of rebuilding the import address table after dumping an unpacked process from memory using tools like Scylla or ImpRec | | PUSHAD/POPAD | x86 instructions that save/restore all general-purpose registers; UPX uses this pattern to preserve register state during unpacking | | Section Entropy | Randomness measure of PE section data; packed sections show entropy > 7.0 while normal code sections average 5.0-6.5 | | Magic Bytes | Signature bytes within a file identifying its format; UPX uses "UPX!" which malware authors modify to prevent automated decompression |

Tools & Systems

  • UPX: Open-source executable packer with built-in decompression capability for properly packed files
  • Detect It Easy (DIE): Packer, compiler, and linker detection tool that identifies protection on PE, ELF, and Mach-O files
  • x64dbg/x32dbg: Open-source Windows debugger used for manual unpacking through dynamic execution and breakpoint-based OEP finding
  • Scylla: Import reconstruction tool integrated with x64dbg for rebuilding IAT after memory dumping
  • PE-bear: PE file viewer and editor for inspecting and repairing PE headers after unpacking

Common Scenarios

Scenario: Unpacking Malware with Modified UPX Headers

Context: A malware sample is identified as UPX-packed by section names (UPX0, UPX1) but upx -d fails with "CantUnpackException: header corrupted". The malware author modified the UPX magic bytes to prevent automated decompression.

Approach:

  1. Open the binary in a hex editor and search for the UPX header area (typically at the end of packed data)
  2. Identify the modified magic bytes (e.g., "UPX!" changed to "UPX\x00" or completely zeroed)
  3. Use the Python repair script to restore "UPX!" magic and correct section names
  4. Retry upx -d on the repaired binary
  5. If repair fails, fall back to manual unpacking with x64dbg (PUSHAD -> hardware BP on ESP -> POPAD -> JMP OEP)
  6. Validate the unpacked binary has proper imports and reasonable entropy values
  7. Import into Ghidra or IDA for full static analysis

Pitfalls:

  • Assuming UPX is the only packer; the binary may be double-packed (UPX + custom layer)
  • Modifying the original packed sample instead of working on a copy
  • Not reconstructing imports after manual memory dump (the dumped binary will crash without IAT fix)
  • Forgetting to check for overlay data appended after the UPX-packed PE sections

Output Format

UNPACKING ANALYSIS REPORT
===========================
Sample:           suspect.exe
SHA-256:          e3b0c44298fc1c149afbf4c8996fb924...
Packer:           UPX 3.96 (modified headers)

PACKED BINARY
Sections:         UPX0 (entropy: 0.00) UPX1 (entropy: 7.89) .rsrc (entropy: 3.45)
Imports:          2 (kernel32.dll: LoadLibraryA, GetProcAddress)
File Size:        98,304 bytes

UNPACKING METHOD
Method:           Header repair + UPX -d
Header Fix:       Restored UPX! magic at offset 0x1F000
Command:          upx -d suspect_fixed.exe -o unpacked.exe
Result:           SUCCESS

UNPACKED BINARY
Sections:         .text (entropy: 6.21) .rdata (entropy: 4.56) .data (entropy: 3.12) .rsrc (entropy: 3.45)
Imports:          147 (kernel32, user32, advapi32, wininet, ws2_32)
File Size:        245,760 bytes (2.5x expansion)
OEP:              0x00401000

VALIDATION
PE Valid:         Yes
Imports Resolved: Yes (147 functions across 8 DLLs)
Executable:       Yes (runs without crash in sandbox)

NEXT STEPS
- Import unpacked.exe into Ghidra for full disassembly
- Run YARA rules against unpacked binary
- Submit unpacked binary to VirusTotal for improved detection

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/analyzing-upx-packed-malware-mveraooe' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/analyzing-upx-packed-malware-mveraooe/download' --output '.skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md.part' && mv '.skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md.part' '.skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

Original excerpt: Prerequisites
  • UPX (Ultimate Packer for eXecutables) installed (apt install upx-ucl or download from https://upx.github.io/)
  • Detect It Easy (DIE) for packer identification
  • Python 3.8+ with pefile library for manual header repair
  • x64dbg or x32dbg for manual unpacking when automated tools fail
  • PE-bear or CFF Explorer for PE header inspection and repair
  • Isolated analysis VM without network connectivity

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md' && test -s '.skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/analyzing-packed-malware-with-upx-unpacker' && cp -R '.skill-downloads/analyzing-upx-packed-malware-mveraooe' '.claude/skills/analyzing-packed-malware-with-upx-unpacker'

Cursor
test -f '.skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md' && test -s '.skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md' && mkdir -p '.cursor/skills' && test ! -e '.cursor/skills/analyzing-packed-malware-with-upx-unpacker' && cp -R '.skill-downloads/analyzing-upx-packed-malware-mveraooe' '.cursor/skills/analyzing-packed-malware-with-upx-unpacker'

Codex
test -f '.skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md' && test -s '.skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md' && mkdir -p '.agents/skills' && test ! -e '.agents/skills/analyzing-packed-malware-with-upx-unpacker' && cp -R '.skill-downloads/analyzing-upx-packed-malware-mveraooe' '.agents/skills/analyzing-packed-malware-with-upx-unpacker'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/analyzing-upx-packed-malware-mveraooe'

openskills read 'analyzing-upx-packed-malware-mveraooe'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/analyzing-upx-packed-malware-mveraooe/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills