reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
name: analyzing-compiled-python-malware
|
||||
description: 'Analyzes Python-based malware packaged as PyInstaller/py2exe executables by detecting
|
||||
the packer, locating the embedded archive, and identifying bundled .pyc modules for extraction and
|
||||
decompilation. Activates for requests to analyze compiled Python malware, unpack a PyInstaller
|
||||
binary, or extract pyc modules from a frozen Python executable.'
|
||||
domain: cybersecurity
|
||||
subdomain: reverse-engineering
|
||||
tags:
|
||||
- reverse-engineering
|
||||
- python
|
||||
- pyinstaller
|
||||
- pyc
|
||||
- unpacking
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1027
|
||||
- T1059.006
|
||||
- T1587.001
|
||||
d3fend:
|
||||
- D3-SDA
|
||||
- D3-DA
|
||||
references:
|
||||
- 'PyInstaller archive format (CArchive/PKG) — https://pyinstaller.org/en/stable/advanced-topics.html'
|
||||
- 'Python bytecode (dis module / .pyc magic) — https://docs.python.org/3/library/dis.html'
|
||||
---
|
||||
|
||||
# Analyzing Compiled Python Malware
|
||||
|
||||
## When to Use
|
||||
|
||||
- You have an executable that is actually a frozen Python app (PyInstaller, py2exe, cx_Freeze) and
|
||||
need to identify the packer and locate the embedded Python modules.
|
||||
- You want to extract `.pyc` files for decompilation.
|
||||
|
||||
**Do not use** this to run the executable — it identifies and locates the embedded archive
|
||||
statically. Decompile extracted `.pyc` in an isolated environment.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The frozen executable (read inertly).
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Read bytes statically; treat extracted modules as malicious until reviewed.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Detect the packer
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py detect sample.exe
|
||||
```
|
||||
|
||||
Looks for PyInstaller markers (`pyi-`, `PYZ-00.pyz`, the `MEI` CArchive cookie `MEI\014\013\012\013\016`),
|
||||
py2exe (`PYTHONSCRIPT`, `zipfile.zip`), and embedded `python3x.dll` references.
|
||||
|
||||
### Step 2: Locate the embedded archive
|
||||
|
||||
Find the CArchive cookie near the end of the file and report the offset and the embedded Python
|
||||
version string (`python3.x`).
|
||||
|
||||
### Step 3: Extract and decompile
|
||||
|
||||
Use a PyInstaller extractor to dump the archive, then decompile `.pyc` (matching the detected
|
||||
Python version) for source recovery.
|
||||
|
||||
### Step 4: Analyze the source
|
||||
|
||||
Review the recovered Python for C2, persistence, and capability; map to ATT&CK.
|
||||
|
||||
## Validation
|
||||
|
||||
- The packer is identified by its specific marker, not just the presence of Python strings.
|
||||
- The CArchive cookie offset and Python version are reported when PyInstaller is present.
|
||||
- Findings distinguish the bootloader stub from the embedded Python payload.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- `.pyc` version mismatch breaking decompilation — match the interpreter version.
|
||||
- Stripped/obfuscated bytecode (e.g., custom magic) needing header repair before decompiling.
|
||||
- Encrypted PYZ archives (PyInstaller `--key`) requiring the key.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the detector.
|
||||
- PyInstaller archive and Python bytecode references (linked in frontmatter).
|
||||
@@ -0,0 +1,37 @@
|
||||
# API Reference: Analyzing Compiled Python Malware
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` detects frozen-Python packers (PyInstaller, py2exe, cx_Freeze) by their markers,
|
||||
locates the PyInstaller CArchive magic cookie, and extracts embedded Python version strings. It
|
||||
reads bytes statically and executes nothing.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | regex, JSON, file IO |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `detect(path)`
|
||||
Returns the detected packer, PyInstaller cookie offset, and Python version strings, plus the
|
||||
recommended next step.
|
||||
|
||||
## Markers
|
||||
|
||||
- PyInstaller: `_MEIPASS`, `PYZ-00.pyz`, `pyiboot01_bootstrap`, CArchive cookie `MEI\\014\\013\\012\\013\\016`.
|
||||
- py2exe: `PYTHONSCRIPT`.
|
||||
- cx_Freeze: `cx_Freeze` strings.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py detect sample.exe
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- PyInstaller archive format (CArchive/PKG, MEI cookie) — https://pyinstaller.org/en/stable/advanced-topics.html
|
||||
- Python bytecode / .pyc magic (dis module) — https://docs.python.org/3/library/dis.html
|
||||
- pyinstxtractor extraction workflow (public PyInstaller-unpacking tooling)
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Detect frozen-Python packers and locate the embedded archive.
|
||||
|
||||
Scans an executable for PyInstaller, py2exe, and cx_Freeze markers, locates the PyInstaller
|
||||
CArchive magic cookie, and extracts embedded Python version strings. Reads bytes statically;
|
||||
executes nothing.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
PYI_COOKIE = b"MEI\014\013\012\013\016"
|
||||
PYI_MARKERS = [b"pyi-windows-manifest-filename", b"PYZ-00.pyz", b"pyiboot01_bootstrap",
|
||||
b"_MEIPASS"]
|
||||
PY2EXE_MARKERS = [b"PYTHONSCRIPT", b"py2exe"]
|
||||
CXFREEZE_MARKERS = [b"cx_Freeze", b"cx_freeze__init__"]
|
||||
RE_PYVER = re.compile(rb"python(\d)\.?(\d{1,2})")
|
||||
RE_PYDLL = re.compile(rb"python(\d)(\d{1,2})\.dll", re.I)
|
||||
|
||||
|
||||
def detect(path: str) -> dict:
|
||||
data = Path(path).read_bytes()
|
||||
pyi = any(m in data for m in PYI_MARKERS) or PYI_COOKIE in data
|
||||
py2exe = any(m in data for m in PY2EXE_MARKERS)
|
||||
cxfreeze = any(m in data for m in CXFREEZE_MARKERS)
|
||||
cookie_off = data.rfind(PYI_COOKIE)
|
||||
versions = sorted({f"{m.group(1).decode()}.{m.group(2).decode()}"
|
||||
for m in RE_PYVER.finditer(data)})
|
||||
dll_versions = sorted({f"{m.group(1).decode()}.{m.group(2).decode()}"
|
||||
for m in RE_PYDLL.finditer(data)})
|
||||
packer = "PyInstaller" if pyi else "py2exe" if py2exe else \
|
||||
"cx_Freeze" if cxfreeze else "unknown"
|
||||
return {
|
||||
"is_frozen_python": pyi or py2exe or cxfreeze,
|
||||
"packer": packer,
|
||||
"pyinstaller_cookie_offset": cookie_off if cookie_off != -1 else None,
|
||||
"python_versions": versions[:5],
|
||||
"python_dll_versions": dll_versions[:5],
|
||||
"next_step": "extract the archive (e.g. pyinstxtractor) then decompile matching .pyc",
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Detect frozen-Python packers")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("detect")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(detect(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user