PackDetect — Packer Detection & Unpacking Tool

Packers are used by malware authors to compress and obfuscate PE binaries, making static analysis harder and evading signature-based AV detection. PackDetect is a command-line tool that identifies packed Windows executables using three layered detection techniques — entropy analysis, signature scanning, and structural heuristics — and attempts automatic unpacking for supported packers.

The tool was tested against a real UPX-packed malware sample obtained from MalwareBazaar, successfully detecting the packer, reporting per-section entropy, and unpacking the binary automatically.


Prerequisites


Installation

git clone https://github.com/you/packdetect
cd packdetect

python -m venv .venv
.venv\Scripts\activate        # Windows
source .venv/bin/activate     # Mac / Linux

pip install -e .

Verify installation:

packdetect --help

How It Works

Detection runs in three independent layers, each contributing to a weighted confidence score:

┌─────────────────┐   ┌──────────────────┐   ┌──────────────────────┐
│   Layer 1       │   │   Layer 2        │   │   Layer 3            │
│ Entropy         │──▶│ Signature scan   │──▶│ Heuristics           │
│ Shannon H(x)    │   │ magic bytes +    │   │ EP location, import  │
│ per section     │   │ section names    │   │ count, size ratios   │
└─────────────────┘   └──────────────────┘   └──────────────────────┘
                                   │
                          Verdict + confidence%

Layer 1 — Shannon entropy measures randomness per PE section (0–8 bits/byte). Normal compiled code sits between 4.5–6.5. Packed or encrypted sections exceed 7.0.

Layer 2 — Signature scan searches for packer magic bytes (e.g. UPX!) and known section names (e.g. .MPRESS1). Covers UPX, MPRESS, ASPack, PECompact, Themida, VMProtect, FSG, Petite, and NSIS.

Layer 3 — Heuristics fires on structural anomalies regardless of signature presence — catching custom and unknown packers:

Heuristic What it detects
Virtual-only section raw_size ≈ 0, virtual_size >> 0 — decompression placeholder
EP outside .text Entry point landing in an unexpected section
No import table Packed binaries rebuild their IAT dynamically at runtime
Low raw/virtual ratio Large runtime expansion = decompression
Non-standard section names Cleared or renamed sections

Step 1: Scan a Binary

packdetect scan malware.exe

Output includes the file info panel, verdict with confidence %, per-section entropy bars, signature hits, and heuristic findings.

For plain ASCII output without Rich:

packdetect scan malware.exe --plain

Step 2: Test Against the Included Malware Sample

A real UPX-packed malware sample is included in malware-lab/ for immediate testing.

[!WARNING]
Do not execute the sample. Keep it in an isolated folder or VM. It was obtained from MalwareBazaar for static analysis purposes only.

packdetect scan ..\..\malware-lab\7d7655e9446fd41dc1ae859435f39c250964532bc604c9bf6d737992430d645e.exe

Expected verdict:

╭──────────────────────── Verdict ──────────────────────────╮
│  ⚠  PACKED                                                 │
│                                                             │
│  Confidence : ██████████  97%                              │
│  Risk level : HIGH                                         │
│  Packer     : UPX                                          │
╰─────────────────────────────────────────────────────────────╯

  Section    Entropy    Bar                        Flag
  ─────────  ─────────  ─────────────────────────  ──────────
  UPX0       0.0000     ░░░░░░░░░░░░░░░░░░░░░░░░   NORMAL
  UPX1       7.88xx     ████████████████████████   HIGH
  .rsrc      3.4xxx     ██████████░░░░░░░░░░░░░░   NORMAL

  [HIT]  UPX — magic bytes "UPX!" found in stub header

Tip: The UPX0 section has near-zero entropy because it is empty on disk — it is the virtual placeholder that gets populated with decompressed code at runtime. UPX1 holds the compressed original binary with entropy ~7.88.


Step 3: Export a JSON Report

packdetect scan malware.exe --json
packdetect scan malware.exe --save-json

--json prints to stdout (pipe-friendly). --save-json writes malware.packdetect.json alongside the input file.

Sample output:

{
  "file": {
    "path": "malware.exe",
    "size": 45056,
    "md5": "...",
    "sha256": "...",
    "arch": "x86",
    "entry_point": "0x00001000",
    "entry_point_section": "UPX1"
  },
  "verdict": {
    "verdict": "packed",
    "packer": "UPX",
    "confidence": 97,
    "risk": "HIGH",
    "unpack_supported": true,
    "unpack_command": "upx -d malware.exe -o malware_unpacked.exe"
  }
}

Step 4: Auto-Unpack

For UPX and MPRESS samples, PackDetect can call the unpacker automatically.

Install UPX first (upx.github.io), then:

packdetect unpack ..\..\malware-lab\7d7655e9446fd41dc1ae859435f39c250964532bc604c9bf6d737992430d645e.exe

This calls upx -d under the hood and writes a _unpacked.exe next to the original. Re-scan to confirm entropy dropped back to normal:

packdetect scan ..\..\malware-lab\7d7655e9446fd41dc1ae859435f39c250964532bc604c9bf6d737992430d645e_unpacked.exe

The unpacked binary should show .text entropy in the 5.x range — confirming a clean decompression.

Tip: If the unpacker is not on PATH, PackDetect reports the missing tool gracefully without crashing, and suggests the manual alternative using x64dbg + Scylla for unsupported packers.


Step 5: Batch Scan a Directory

packdetect batch ./samples/
packdetect batch ./samples/ --all         # include non-PE extensions
packdetect batch ./samples/ --save-json   # saves packdetect_batch.json

Produces a summary table showing verdict, packer, entropy, confidence, and risk level for every file scanned.


Step 6: Run the Test Suite

The project includes 26 unit tests covering the full engine. They use only synthetic in-memory PE binaries built with struct — no real malware required.

pip install pytest
pytest tests/ -v

Expected output:

tests/test_engine.py::TestShannonEntropy::test_all_zeros              PASSED
tests/test_engine.py::TestShannonEntropy::test_uniform_distribution   PASSED
tests/test_engine.py::TestPEParser::test_parses_minimal_pe            PASSED
tests/test_engine.py::TestPEParser::test_rejects_non_pe               PASSED
tests/test_engine.py::TestSignatureScanner::test_upx_magic_detected   PASSED
tests/test_engine.py::TestSignatureScanner::test_mpress_section_name_detected  PASSED
tests/test_engine.py::TestHeuristics::test_virtual_only_section_flagged        PASSED
tests/test_engine.py::TestHeuristics::test_high_entropy_exec_section_flagged   PASSED
tests/test_engine.py::TestVerdictComputation::test_signature_hit_gives_packed  PASSED
tests/test_engine.py::TestVerdictComputation::test_no_sig_high_heuristic_gives_unknown PASSED
tests/test_engine.py::TestAnalyseIntegration::test_upx_magic_in_file  PASSED
...
26 passed in 0.22s

With coverage:

pip install pytest-cov
pytest tests/ -v --cov=packdetect --cov-report=term-missing

Exit Codes

Designed for scripting integration:

Code Meaning
0 Clean / success
2 Packed binary detected
3 Suspicious (inconclusive)
4 Unpack not supported
5 Unpack attempted but failed
packdetect scan "$file" --plain
if [ $? -eq 2 ]; then
    packdetect unpack "$file"
fi

Project Structure

packdetect/
├── packdetect/
│   ├── __init__.py       version string
│   ├── __main__.py       CLI — argparse, scan / unpack / batch commands
│   ├── engine.py         PE parser, entropy, signatures, heuristics, unpacker
│   └── output.py         Rich renderer, plain renderer, JSON serialiser
├── malware-lab/
│   └── 7d7655e9...exe    real UPX-packed sample (MalwareBazaar)
├── tests/
│   └── test_engine.py    26 unit tests
├── pyproject.toml
└── requirements.txt

🛠️ Tools & Techniques


Resources