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,89 @@
---
name: building-config-extractors
description: 'Builds reusable malware configuration extractors by applying a declarative JSON spec
(offsets, decode layers, field definitions) to a sample to produce structured, defanged config.
Activates for requests to build a config extractor, write a reusable family config parser, or run
a spec-driven configuration extraction.'
domain: cybersecurity
subdomain: reverse-engineering
tags:
- reverse-engineering
- config-extraction
- automation
- tooling
- c2
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1140
- T1027
- T1071
d3fend:
- D3-SDA
- D3-DA
references:
- 'MITRE ATT&CK T1140 Deobfuscate/Decode Files or Information — https://attack.mitre.org/techniques/T1140/'
- 'MWCP / config-extractor framework concepts — https://github.com/Defense-Cyber-Crime-Center/DC3-MWCP'
---
# Building Config Extractors
## When to Use
- You have reversed a family's config format and want a reusable, declarative extractor instead of
one-off scripts.
- You need to apply a spec (offset, XOR/base64 layers, field map) to many samples and emit
structured, defanged config.
**Do not use** a spec built for one variant blindly on another — re-verify offsets/keys per
variant. This skill reads samples statically and executes nothing.
## Prerequisites
- A reversed understanding of the config (location, decode layers, fields) expressed as a JSON
spec.
## Safety & Handling
- Read bytes statically; defang recovered endpoints in output.
## Workflow
### Step 1: Write the extraction spec
Define a JSON spec: blob `offset`/`length` (or a `marker` to search), ordered `decode` layers
(`xor` with key, `base64`), and `fields` (name, offset, length, type: `str`/`u16`/`u32`/`ipv4`).
### Step 2: Run the extractor
```bash
python scripts/analyst.py extract sample.bin --spec family.json
```
Locates the blob, applies decode layers, parses fields, and defangs URL/IP fields.
### Step 3: Validate output
Confirm fields are plausible (valid hosts, ports, IDs); adjust the spec as needed.
### Step 4: Reuse
Store the spec per family and apply across the corpus.
## Validation
- The spec round-trips on a known sample to the expected config.
- Decode layers are applied in order and produce readable output.
- URL/IP fields are defanged in the emitted config.
## Pitfalls
- Hardcoding a variant-specific offset that shifts in other builds — prefer markers.
- Wrong field endianness (`u16`/`u32` little vs big) producing garbage ports.
- Forgetting a decode layer (base64 over XOR) and parsing ciphertext as fields.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the extractor engine.
- ATT&CK T1140 and config-extractor framework concepts (linked in frontmatter).

View File

@@ -0,0 +1,48 @@
# API Reference: Building Config Extractors
## Overview
`scripts/analyst.py` is a declarative config-extraction engine: given a JSON spec describing the
config blob location, ordered decode layers, and field definitions, it extracts a structured,
defanged config from a sample. It reads bytes statically and executes nothing.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | base64, struct, JSON, file IO |
## Spec format
```json
{
"blob": {"marker": "CFG0", "length": 256},
"decode": [{"type": "xor", "key": "0x5a"}, {"type": "base64"}],
"fields": [
{"name": "c2", "offset": 0, "length": 64, "type": "str"},
{"name": "port", "offset": 64, "type": "u16"},
{"name": "ip", "offset": 66, "type": "ipv4"}
]
}
```
- **blob**: `offset`+`length` or `marker`+`length` (searches for the marker).
- **decode**: ordered `xor` (single-byte `0x..` or multi-byte string key) and `base64` layers.
- **fields**: `str`, `u16`, `u32` (little-endian), `ipv4`. URL/IP fields are defanged.
## Core functions
### `extract(path, spec)`
Locates the blob, applies decode layers, parses fields, returns the config.
## Usage
```bash
python scripts/analyst.py extract sample.bin --spec family.json
```
## Sources
- MITRE ATT&CK T1140 Deobfuscate/Decode Information — https://attack.mitre.org/techniques/T1140/
- Declarative config-extractor design (DC3-MWCP, CAPE parsers) — https://github.com/Defense-Cyber-Crime-Center/DC3-MWCP
- XOR/base64 layered config encoding from public family teardowns

View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Apply a declarative JSON spec to extract a malware config from a sample.
Spec format:
{
"blob": {"offset": 0, "length": 256} OR {"marker": "CFG0", "length": 256},
"decode": [{"type": "xor", "key": "0x5a"}, {"type": "base64"}],
"fields": [{"name": "c2", "offset": 0, "length": 64, "type": "str"},
{"name": "port", "offset": 64, "type": "u16"},
{"name": "ip", "offset": 66, "type": "ipv4"}]
}
Reads sample bytes statically; executes nothing. Defangs url/ipv4 string fields.
"""
from __future__ import annotations
import argparse
import base64
import json
import struct
import sys
from pathlib import Path
def _locate_blob(data: bytes, blob: dict) -> bytes:
length = blob.get("length")
if "marker" in blob:
marker = blob["marker"].encode("latin-1")
idx = data.find(marker)
if idx == -1:
raise ValueError("marker not found")
start = idx + len(marker)
else:
start = int(blob.get("offset", 0))
return data[start:start + length] if length else data[start:]
def _apply_decode(blob: bytes, layers: list[dict]) -> bytes:
out = blob
for layer in layers:
kind = layer["type"]
if kind == "xor":
key = layer["key"]
key_bytes = (bytes([int(key, 0)]) if isinstance(key, str) and key.startswith("0x")
else key.encode("latin-1") if isinstance(key, str) else bytes([key]))
out = bytes(b ^ key_bytes[i % len(key_bytes)] for i, b in enumerate(out))
elif kind == "base64":
out = base64.b64decode(out + b"===")
else:
raise ValueError("unknown decode layer: %s" % kind)
return out
def _defang(s: str) -> str:
return s.replace("http", "hxxp").replace(".", "[.]")
def _parse_field(blob: bytes, field: dict):
off = int(field.get("offset", 0))
ftype = field.get("type", "str")
if ftype == "str":
length = int(field.get("length", 0))
raw = blob[off:off + length] if length else blob[off:]
value = raw.split(b"\x00", 1)[0].decode("latin-1", "ignore").strip()
# Defang only string fields that carry a URL or dotted-quad endpoint.
if "http" in value or value.count(".") == 3:
value = _defang(value)
return value
if ftype == "u16":
return struct.unpack_from("<H", blob, off)[0]
if ftype == "u32":
return struct.unpack_from("<I", blob, off)[0]
if ftype == "ipv4":
return _defang(".".join(str(b) for b in blob[off:off + 4]))
raise ValueError("unknown field type: %s" % ftype)
def extract(path: str, spec: dict) -> dict:
data = Path(path).read_bytes()
blob = _apply_decode(_locate_blob(data, spec["blob"]), spec.get("decode", []))
config = {field["name"]: _parse_field(blob, field) for field in spec.get("fields", [])}
return {"config": config}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Spec-driven config extractor")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("extract")
p.add_argument("file")
p.add_argument("--spec", required=True)
args = parser.parse_args(argv)
spec = json.loads(Path(args.spec).read_text(encoding="utf-8-sig"))
print(json.dumps(extract(args.file, spec), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())