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,114 @@
---
name: performing-static-pe-analysis
description: 'Extracts structure and indicators from a Windows PE file without executing it:
headers, sections, imports/exports, resources, entropy, and embedded strings to infer
capability and packing. Activates for requests to statically analyze a PE, EXE, or DLL, or
inspect imports and headers.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware
- static-analysis
- pe
- imports
- entropy
- windows
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027
- T1129
- T1140
d3fend:
- D3-FA
- D3-FCR
references:
- 'Microsoft PE Format specification — https://learn.microsoft.com/windows/win32/debug/pe-format'
- 'pefile library — https://github.com/erocarrera/pefile'
---
# Performing Static PE Analysis
## When to Use
- You have a Windows executable or DLL and need to infer its capabilities before (or instead
of) detonation.
- You want to assess packing, suspicious imports, timestamps, and embedded resources.
- You are gathering features for YARA authoring or detection.
**Do not use** import tables alone to conclude behavior — packed samples hide imports until
runtime. If imports are sparse and entropy is high, move to unpacking.
## Prerequisites
- `pefile` (`pip install pefile`) and optionally `capa`/PEStudio for deeper capability ID.
- The sample in neutralized form inside the lab.
## Safety & Handling
- Static only: parse the file, never run it. Keep the neutralized extension.
## Workflow
### Step 1: Parse headers and metadata
```bash
python scripts/analyst.py analyze sample.bin
```
Note the compile timestamp (often forged), subsystem (GUI/console), machine type (x86/x64),
and whether it is a DLL.
### Step 2: Review sections and entropy
Per-section entropy reveals packing. A tiny `.text` plus a huge high-entropy section, or
non-standard section names (`UPX0`, `.themida`), indicate a packer:
```text
.text entropy 6.4 (normal code)
UPX1 entropy 7.95 (packed)
```
### Step 3: Classify imports
Group imported APIs into behavioral buckets:
```text
Injection : VirtualAllocEx, WriteProcessMemory, CreateRemoteThread [T1055]
Network : InternetOpen, HttpSendRequest, WinHttpConnect [T1071]
Crypto : CryptEncrypt, CryptAcquireContext [T1486]
Persistence: RegSetValueEx, CreateService [T1547/T1543]
```
A sample importing only `LoadLibrary`/`GetProcAddress` is resolving APIs dynamically — a
packing/evasion tell.
### Step 4: Inspect resources and strings
Look for embedded PEs in resources (droppers), config blobs, and notable strings (URLs,
mutex names, paths).
### Step 5: Check signing
Verify the Authenticode signature: unsigned, self-signed, or revoked certificates are
suspicious for software claiming to be legitimate.
## Validation
- Section entropy and import counts agree (packed → few imports + high entropy).
- Suspicious import buckets correspond to plausible behavior.
- Embedded PE detection is confirmed by an `MZ`/`PE` header inside a resource.
## Pitfalls
- Trusting the compile timestamp as a real build date — it is trivially forged.
- Concluding "benign" because imports look ordinary; the import table may be a stub for a
packed payload.
- Ignoring TLS callbacks, which can run code before the entry point.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the analyzer and import
classification map.
- Microsoft PE Format spec and pefile (linked in frontmatter).

View File

@@ -0,0 +1,46 @@
# API Reference: Performing Static PE Analysis
## Overview
`scripts/analyst.py` parses a PE file and reports headers, sections with per-section entropy,
imports grouped into behavioral buckets, and TLS-callback presence. It prefers `pefile` and
falls back to a minimal built-in header walk when `pefile` is unavailable.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| pefile | >= 2023.2 | Full PE parsing (optional but recommended) |
| (stdlib) | — | struct/entropy fallback parser |
## External tools
| Tool | Purpose |
|------|---------|
| capa | Capability identification from disassembly |
| PEStudio | Interactive static PE triage |
## Core functions
### `analyze(path)`
Dispatches to `analyze_with_pefile` or the `analyze_minimal` fallback. Returns headers,
sections, entropy, and classified imports.
### `_classify(imports)`
Groups imported API names into `injection`, `network`, `crypto`, `persistence`, `evasion`.
### `entropy(data)`
Byte-level Shannon entropy (0–8).
## Usage
```bash
pip install pefile # optional, enables full analysis
python scripts/analyst.py analyze sample.bin
```
## Sources
- Microsoft PE Format specification — https://learn.microsoft.com/windows/win32/debug/pe-format
- pefile library — https://github.com/erocarrera/pefile
- Windows API behavioral groupings (standard malware-analysis import categories)

View File

@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""Static PE analysis: headers, sections, entropy, and import classification.
Uses ``pefile`` when available; falls back to a minimal built-in PE header walk so the tool
still reports sections and entropy without third-party packages. Never executes the sample.
"""
from __future__ import annotations
import argparse
import json
import math
import struct
import sys
from collections import Counter
from pathlib import Path
_IMPORT_BUCKETS = {
"injection": {"virtualallocex", "writeprocessmemory", "createremotethread",
"ntunmapviewofsection", "queueuserapc", "setthreadcontext"},
"network": {"internetopena", "internetopenw", "httpsendrequesta", "winhttpconnect",
"wsastartup", "connect", "send", "urldownloadtofilea"},
"crypto": {"cryptencrypt", "cryptdecrypt", "cryptacquirecontexta",
"cryptgenkey", "bcryptencrypt"},
"persistence": {"regsetvalueexa", "regsetvalueexw", "createservicea",
"createservicew", "schtasks"},
"evasion": {"isdebuggerpresent", "checkremotedebuggerpresent",
"gettickcount", "loadlibrarya", "getprocaddress"},
}
def entropy(data: bytes) -> float:
if not data:
return 0.0
counts = Counter(data)
n = len(data)
return -sum((c / n) * math.log2(c / n) for c in counts.values())
def analyze_with_pefile(path: str) -> dict:
import pefile # type: ignore
pe = pefile.PE(path, fast_load=True)
pe.parse_data_directories()
sections = [{
"name": s.Name.rstrip(b"\x00").decode("ascii", "replace"),
"vsize": s.Misc_VirtualSize,
"rawsize": s.SizeOfRawData,
"entropy": round(s.get_entropy(), 3),
} for s in pe.sections]
imports: list[str] = []
if hasattr(pe, "DIRECTORY_ENTRY_IMPORT"):
for entry in pe.DIRECTORY_ENTRY_IMPORT:
for imp in entry.imports:
if imp.name:
imports.append(imp.name.decode("ascii", "replace"))
buckets = _classify(imports)
return {
"is_dll": pe.is_dll(),
"machine": hex(pe.FILE_HEADER.Machine),
"timestamp": pe.FILE_HEADER.TimeDateStamp,
"sections": sections,
"import_count": len(imports),
"import_buckets": buckets,
"tls_callbacks": hasattr(pe, "DIRECTORY_ENTRY_TLS"),
}
def _classify(imports: list[str]) -> dict[str, list[str]]:
low = [i.lower() for i in imports]
return {bucket: sorted({i for i in low if i in names})
for bucket, names in _IMPORT_BUCKETS.items()
if any(i in names for i in low)}
def analyze_minimal(path: str) -> dict:
"""Fallback header walk without pefile: sections + entropy only."""
data = Path(path).read_bytes()
if data[:2] != b"MZ":
raise ValueError("not a PE (no MZ header)")
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
if data[e_lfanew:e_lfanew + 4] != b"PE\x00\x00":
raise ValueError("not a PE (no PE signature)")
num_sections = struct.unpack_from("<H", data, e_lfanew + 6)[0]
opt_size = struct.unpack_from("<H", data, e_lfanew + 20)[0]
sec_off = e_lfanew + 24 + opt_size
sections = []
for i in range(num_sections):
base = sec_off + i * 40
name = data[base:base + 8].rstrip(b"\x00").decode("ascii", "replace")
raw_size = struct.unpack_from("<I", data, base + 16)[0]
raw_ptr = struct.unpack_from("<I", data, base + 20)[0]
blob = data[raw_ptr:raw_ptr + raw_size]
sections.append({"name": name, "rawsize": raw_size,
"entropy": round(entropy(blob), 3)})
return {"sections": sections, "note": "pefile not installed; minimal parse"}
def analyze(path: str) -> dict:
try:
return analyze_with_pefile(path)
except ImportError:
return analyze_minimal(path)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Static PE analysis")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("analyze")
p.add_argument("file")
args = parser.parse_args(argv)
print(json.dumps(analyze(args.file), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())