Packer Detection & Entropy Analysis

25 minIn Progress

Packer Detection & Entropy Analysis

Packers are one of the most common obstacles in static malware analysis. They compress, encrypt, or otherwise transform executables to evade signature-based detection and hinder analysis. Detecting packing early saves you from wasting time analyzing meaningless stub code. This lesson covers how packers work, how to detect them using entropy analysis and signature scanning, and what to do when you encounter a packed sample.


1. What Are Packers?

A packer transforms an executable by compressing or encrypting its original code and data, then wrapping it with a small stub program. When the packed file runs, the stub decompresses/decrypts the original code into memory and transfers execution to it.

Original EXE:                    Packed EXE:
+------------------+             +------------------+
| .text (code)     |             | .text (stub)     |  <- Small unpacking code
| .data (data)     |  --pack-->  | .rsrc (packed     |  <- Compressed/encrypted
| .rsrc (resources)|             |   original data) |     original program
| .reloc           |             +------------------+
+------------------+
                                 At runtime: stub unpacks original into memory

Why Malware Uses Packers

  • Evade AV signatures -- The original malicious code is hidden inside compressed/encrypted data
  • Hinder static analysis -- Strings, imports, and code structure are not visible until unpacked
  • Reduce file size -- Compressed payloads are smaller for delivery
  • Impede reverse engineering -- Adds an extra layer analysts must peel back

2. Common Packers and Protectors

Packer/ProtectorTypeKey IndicatorsDifficulty to Unpack
UPXCompressorSection names UPX0/UPX1, UPX! signatureEasy (upx -d)
ASPackCompressor.aspack/.adata sections, high entropyMedium
MPRESSCompressor.MPRESS1/.MPRESS2 sectionsMedium
Themida/WinLicenseProtectorMany sections, anti-debug, VM detectionHard
VMProtectVirtualizer.vmp0/.vmp1 sections, virtualized codeVery Hard
Enigma ProtectorProtector.enigma sections, anti-tamperHard
ObsidiumProtectorRandom section names, anti-debugHard
Custom packersVariesNon-standard names, minimal importsVaries

Packers vs. Protectors vs. Crypters

CategoryPurposeExample
PackerCompress to reduce size and evade signaturesUPX, ASPack, MPRESS
ProtectorAnti-debug, anti-VM, code virtualization, license enforcementThemida, VMProtect
CrypterEncrypt payload, FUD (Fully Undetectable) focusCustom crypters from underground forums

3. Entropy Analysis

What Is Shannon Entropy?

Shannon entropy measures the randomness or information density of data, on a scale of 0 to 8 bits per byte:

Entropy RangeWhat It MeansExamples
0.0 - 1.0Very structured, repetitiveNull-filled sections, padding
1.0 - 4.5Normal text or structured dataASCII strings, configuration files
4.5 - 6.5Normal compiled codeTypical .text sections, native code
6.5 - 7.5Possibly compressed or encodedCompressed resources, encoded data
7.5 - 8.0Almost certainly encrypted or compressedPacked code, encrypted payloads

Key insight: Normal executable code has entropy around 5.0-6.5. If a section has entropy above 7.0, it is almost certainly compressed or encrypted -- a strong packing indicator.

Calculating Section Entropy with Python

import pefile
import math

def entropy(data):
    if not data:
        return 0.0
    freq = [0] * 256
    for byte in data:
        freq[byte] += 1
    length = len(data)
    return -sum(
        (f/length) * math.log2(f/length)
        for f in freq if f > 0
    )

pe = pefile.PE("suspicious.exe")
print(f"{'Section':10s} {'Entropy':>8s} {'Raw Size':>10s} {'Virt Size':>10s}  Status")
print("-" * 55)
for section in pe.sections:
    name = section.Name.decode().strip('\x00')
    ent = entropy(section.get_data())
    raw = section.SizeOfRawData
    virt = section.Misc_VirtualSize
    if ent > 7.0:
        status = "PACKED/ENCRYPTED"
    elif ent > 6.5:
        status = "SUSPICIOUS"
    else:
        status = "normal"
    print(f"{name:10s} {ent:8.2f} {raw:10d} {virt:10d}  {status}")

Example output for a normal (unpacked) binary:

Section     Entropy   Raw Size  Virt Size  Status
-------------------------------------------------------
.text         6.35      74752      74590  normal
.rdata        4.75      50176      49823  normal
.data         1.23       4096      10240  normal
.rsrc         3.89       8192       8100  normal
.reloc        5.12       4096       3856  normal

Example output for a UPX-packed binary:

Section     Entropy   Raw Size  Virt Size  Status
-------------------------------------------------------
UPX0          0.00          0     262144  normal       <- Empty on disk, expands in memory
UPX1          7.89      98304     102400  PACKED/ENCRYPTED
.rsrc         3.45       2048       2000  normal
Section entropy chart showing high-entropy packed sections
Section entropy chart showing high-entropy packed sections

Other Packing Indicators Beyond Entropy

IndicatorWhat to Look ForTool
Section namesUPX0/UPX1, .aspack, .MPRESS, .vmp0, random namesPEStudio, DiE
Virtual size >> Raw sizeSection expands dramatically in memorypefile, PEStudio
Few or no importsOnly LoadLibrary + GetProcAddressPEStudio, pefile
Entry point locationPoints to unusual section (not .text)PEStudio, DiE
Few readable stringsMost strings are garbage or absentstrings, FLOSS
Section flagsWrite + Execute on data sectionspefile
Small .text, large otherCode section tiny, data section hugePEStudio

4. Detection Tools

Detect It Easy (DiE)

The gold standard for packer identification. Maintains a signature database of 600+ packers, compilers, and protectors:

Detect It Easy identifying UPX packer on a sample
Detect It Easy identifying UPX packer on a sample
# CLI on REMnux:
$ diec suspicious.exe
PE32
Packer: UPX (3.96) [NRV2E]
Linker: Microsoft Linker (10.0)

# If not packed:
$ diec clean_sample.exe
PE64
Compiler: Microsoft Visual C/C++ (2019)
Linker: Microsoft Linker (14.29)

Exeinfo PE (Windows)

Similar to DiE but Windows-only. Provides packer hints and "Unpack Info" suggestions. Also has the ability to extract embedded files.

CAPA (Mandiant)

Detects packing-related capabilities automatically:

$ capa suspicious.exe
# Look for rules like:
# - "packed with UPX"
# - "contain anti-disassembly techniques"
# - "reference anti-VM strings"

PEiD (Legacy)

Classic packer identifier. Signature database is outdated but still useful for older samples. The YARA rules community has largely replaced PEiD.


5. Unpacking Strategies

Strategy 1: Automated Unpacking (UPX)

UPX is the most common packer and supports built-in decompression:

# Attempt to unpack
$ upx -d packed_sample.exe -o unpacked_sample.exe
                       Ultimate Packer for eXecutables
  File size         Ratio      Format      Name
  ----------   ------   -----------   -----------
    245760 <-    98304   40.00%   win32/pe   unpacked_sample.exe

# Verify: compare entropy before and after
$ python3 -c "
import pefile, math
def ent(data):
    if not data: return 0
    freq=[0]*256
    for b in data: freq[b]+=1
    l=len(data)
    return -sum((f/l)*math.log2(f/l) for f in freq if f>0)
for f in ['packed_sample.exe','unpacked_sample.exe']:
    pe=pefile.PE(f)
    for s in pe.sections:
        n=s.Name.decode().strip(chr(0))
        print(f'{f}: {n} entropy={ent(s.get_data()):.2f}')
"

Strategy 2: Dynamic Unpacking (Generic)

When automated tools fail, you can unpack at runtime:

  1. Load in x64dbg and run until the unpacking stub completes
  2. Set breakpoints on VirtualProtect or VirtualAlloc -- the unpacker changes memory permissions when writing decoded code
  3. Find the OEP (Original Entry Point) -- the jump from stub to real code
  4. Dump the process from memory using Scylla or OllyDumpEx
  5. Fix the IAT (Import Address Table) using Scylla's IAT reconstruction

Strategy 3: Sandbox-Assisted Unpacking

Submit to a sandbox (Any.Run, Hybrid Analysis) and download the memory dumps. Many sandboxes automatically dump unpacked payloads.


6. Packer Detection Workflow

  1. Run DiE/Exeinfo PE -- check for known packer signatures
  2. Analyze section entropy -- any section above 7.0?
  3. Check section names -- UPX, MPRESS, .vmp, random names?
  4. Examine imports -- only LoadLibrary/GetProcAddress?
  5. Check entry point -- does it point to .text or elsewhere?
  6. Compare virtual vs raw sizes -- dramatic expansion?
  7. Attempt automated unpacking if packer is known (upx -d)
  8. If automated fails -- proceed to dynamic unpacking or sandbox

Common Pitfall: Not all high-entropy sections indicate packing. Legitimate programs may have compressed resources (icons, images) in .rsrc with high entropy. Focus on the .text (code) section entropy.

MITRE ATT&CK: Packing maps to T1027.002 - Software Packing. Virtually all sophisticated malware families use some form of packing or protection. APT groups regularly use custom packers to evade detection.

Try it in the shell
Practise this lesson's tooling on its sample in an emulated analyst shell. Output is pre-recorded — nothing executes.

Suggested triage steps

  1. 1

    Check readable static content

    A populated strings result is supporting context, not a packing verdict by itself.

  2. 2

    Read the section entropy

    Use the measured rows and threshold summary to make the packing decision.

  3. 3

    Cross-check PE structure

    Ordinary sections and a populated import table add structural context to the entropy result.

analyst@lab:~emulated · nothing executes

MAA analyst shell — emulated. Nothing executes.

Type 'help', or click a step on the left.

$