reverse-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok609hecl1pq6qi
2026-10-20 02:17:15 +03:00
commit da24c2bd20
2227 changed files with 443931 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
Apache License 2.0 — see the repository root LICENSE file for the full text.
Copyright the AnalystAIPack contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0

View File

@@ -0,0 +1,90 @@
---
name: manually-unpacking-a-packed-binary
description: 'Manually unpacks a runtime-packed Windows binary by finding the original entry point
(OEP) through tail-jump and entropy analysis, then guiding a memory dump at OEP. Activates for
requests to manually unpack a sample, find the OEP, or unpack a custom/unknown packer that
generic tools cannot handle.'
domain: cybersecurity
subdomain: reverse-engineering
tags:
- reverse-engineering
- unpacking
- oep
- packers
- debugging
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027.002
- T1140
- T1620
d3fend:
- D3-DA
- D3-SDA
references:
- 'MITRE ATT&CK T1027.002 Software Packing — https://attack.mitre.org/techniques/T1027/002/'
- 'PE Format — https://learn.microsoft.com/windows/win32/debug/pe-format'
---
# Manually Unpacking a Packed Binary
## When to Use
- A sample is runtime-packed by a custom/unknown packer that UPX and generic unpackers cannot
handle.
- You need to locate the original entry point (OEP) and dump the unpacked image for static
analysis.
**Do not use** generic auto-unpackers here — this is the manual path for cases where they fail.
Run the sample only inside an isolated, instrumented debugger VM.
## Prerequisites
- A debugger (x64dbg) on an isolated VM and the packed sample.
## Safety & Handling
- The sample executes during unpacking; do this only in a disposable, isolated VM with snapshots.
## Workflow
### Step 1: Characterize the packer statically
```bash
python scripts/analyst.py inspect packed.exe
```
Reports section entropy, suspicious section names (`UPX`, `.vmp`, random), small import tables,
and a write-then-execute section (classic unpacker stub).
### Step 2: Find candidate OEP heuristics
Set a memory breakpoint on the unpacked code section's execution; the packer's tail jump (a far
`jmp`/`ret` into a different section) typically transfers control to the OEP.
### Step 3: Dump at OEP
When stopped at OEP, dump the process image and proceed to import reconstruction (see the
dump-and-rebuild skill).
### Step 4: Verify the dump
Confirm the dumped image has a sane entry point, restored imports, and readable code.
## Validation
- The unpacking stub is identified by a write→execute section transition.
- The tail jump lands in a different section than the stub (OEP candidate).
- The dumped image disassembles cleanly with recognizable runtime startup code.
## Pitfalls
- Stopping at a stage-2 stub rather than the true OEP in multi-layer packers.
- Hardware-breakpoint detection by the packer — vary the breakpoint strategy.
- Dumping before the import table is fully resolved.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the inspector.
- ATT&CK T1027.002 and the PE format spec (linked in frontmatter).

View File

@@ -0,0 +1,40 @@
# API Reference: Manually Unpacking a Packed Binary
## Overview
`scripts/analyst.py` parses a PE's section table and reports per-section entropy, writable+executable
sections, known packer section names, and high-entropy sections to plan manual unpacking. It reads
bytes statically and executes nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | struct, math, Counter, JSON |
## Core functions
### `inspect(path)`
Returns per-section details, write+execute sections, known packer names, high-entropy sections,
and a `likely_packed` flag.
### `_parse_sections(data)`
Minimal PE section-table parser (name, entropy, characteristics, sizes).
## Manual unpacking signals
- Write+execute section → in-place unpacking stub.
- Section entropy > 7.0 → compressed/encrypted payload.
- Tiny import table → imports resolved at runtime after unpacking.
## Usage
```bash
python scripts/analyst.py inspect packed.exe
```
## Sources
- MITRE ATT&CK T1027.002 Software Packing — https://attack.mitre.org/techniques/T1027/002/
- PE Format (section characteristics IMAGE_SCN_MEM_*) — https://learn.microsoft.com/windows/win32/debug/pe-format
- OEP / tail-jump methodology from public manual-unpacking practice

View File

@@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""Statically characterize a packed PE to plan manual unpacking.
Parses the PE section table to report per-section entropy, writable+executable sections, unusual
names, and import-table size — signals of a runtime packer. Uses pefile if available, otherwise a
minimal built-in section parser. Reads bytes statically; executes nothing.
"""
from __future__ import annotations
import argparse
import json
import math
import struct
import sys
from collections import Counter
from pathlib import Path
KNOWN_PACKER_NAMES = ["upx", "aspack", "fsg", "mpress", "vmp", "themida", "petite", "nspack",
"pec", "mew", "enigma"]
def _entropy(chunk: bytes) -> float:
if not chunk:
return 0.0
counts = Counter(chunk)
total = len(chunk)
return round(-sum((c / total) * math.log2(c / total) for c in counts.values()), 3)
def _parse_sections(data: bytes) -> list[dict]:
if data[:2] != b"MZ":
raise ValueError("not a PE")
e_lfanew = struct.unpack_from("<I", data, 0x3c)[0]
if data[e_lfanew:e_lfanew + 4] != b"PE\x00\x00":
raise ValueError("bad PE signature")
num_sections = struct.unpack_from("<H", data, e_lfanew + 6)[0]
opt_size = struct.unpack_from("<H", data, e_lfanew + 20)[0]
sect_off = e_lfanew + 24 + opt_size
sections = []
for i in range(num_sections):
base = sect_off + i * 40
raw = data[base:base + 40]
if len(raw) < 40:
break
name = raw[:8].rstrip(b"\x00").decode("latin-1", "ignore")
vsize, vaddr, rawsize, rawptr = struct.unpack_from("<IIII", raw, 8)
chars = struct.unpack_from("<I", raw, 36)[0]
body = data[rawptr:rawptr + rawsize]
sections.append({
"name": name,
"entropy": _entropy(body),
"writable": bool(chars & 0x80000000),
"executable": bool(chars & 0x20000000),
"raw_size": rawsize,
})
return sections
def inspect(path: str) -> dict:
data = Path(path).read_bytes()
sections = _parse_sections(data)
wx = [s["name"] for s in sections if s["writable"] and s["executable"]]
odd = [s["name"] for s in sections
if any(k in s["name"].lower() for k in KNOWN_PACKER_NAMES)]
high = [s["name"] for s in sections if s["entropy"] > 7.0]
return {
"sections": sections,
"write_execute_sections": wx,
"known_packer_section_names": odd,
"high_entropy_sections": high,
"likely_packed": bool(wx or odd or len(high) >= 1),
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Characterize a packed PE")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("inspect")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(inspect(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())