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: extracting-and-classifying-strings
description: 'Extracts ASCII and Unicode strings from a binary and classifies them into
investigative categories: URLs, IPs, file paths, registry keys, mutexes, commands, and API
names, prioritizing the analyst''s attention. Activates for requests to extract strings,
classify strings output, or pull human-readable artifacts from a sample.'
domain: cybersecurity
subdomain: malware-analysis
tags:
- malware-analysis
- strings
- static-analysis
- triage
- ioc
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027
d3fend:
- D3-FCR
- D3-DA
references:
- 'GNU binutils strings — https://www.gnu.org/software/binutils/'
- 'FLOSS (FLARE Obfuscated String Solver) — https://github.com/mandiant/flare-floss'
---
# Extracting and Classifying Strings
## When to Use
- You are triaging a sample and want quick investigative leads from its readable strings.
- You need both ASCII and UTF-16LE (Unicode) strings, which many tools miss by default.
- You want strings grouped by meaning (URLs, paths, registry, commands) rather than a flat dump.
**Do not use** plaintext strings as the whole story for packed/obfuscated samples — the useful
strings may be encrypted; pair with entropy checks and deobfuscation (FLOSS) when the dump is
sparse.
## Prerequisites
- Python (stdlib) or `strings`/FLOSS; the sample handled inertly in the lab.
## Safety & Handling
- Extract strings from the inert file; never execute the sample.
- Defang any URLs/IPs before putting them in a report or ticket.
## Workflow
### Step 1: Extract ASCII and Unicode strings
Pull printable runs at a minimum length (default 4–5) for both ASCII and UTF-16LE encodings to
avoid missing Windows wide strings.
```bash
python scripts/analyst.py strings sample.bin --min-len 5
```
### Step 2: Classify by category
Bucket strings into URLs, IPs, domains, file paths, registry keys, mutex/event names, suspicious
commands, and API names so leads surface immediately.
### Step 3: Prioritize leads
Promote network indicators, command lines, and suspicious API references to the top; defang
network artifacts for safe handling.
### Step 4: Recognize obfuscation
A near-empty or junk-only dump signals packing/string obfuscation; route to entropy analysis and
FLOSS/manual deobfuscation.
## Validation
- Both ASCII and Unicode strings are extracted (Windows wide strings are not missed).
- Strings are grouped into useful categories, not a flat list.
- A sparse/garbage dump is correctly read as an obfuscation signal, not "clean".
## Pitfalls
- Extracting ASCII only and missing UTF-16LE strings.
- Treating a packed sample's empty dump as benign.
- Reporting live URLs/IPs without defanging.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the string extractor.
- binutils strings and FLOSS (linked in frontmatter).

View File

@@ -0,0 +1,37 @@
# API Reference: Extracting and Classifying Strings
## Overview
`scripts/analyst.py` extracts ASCII and UTF-16LE strings at a minimum length and classifies them
into investigative categories (urls, ips, paths, registry, commands, apis), flagging a sparse
dump as a possible obfuscation signal. It reads bytes only and never executes the sample.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | regex, JSON, file IO |
## Core functions
### `ascii_strings(data, min_len)` / `unicode_strings(data, min_len)`
Extract printable ASCII and UTF-16LE runs respectively.
### `classify(strings)`
Buckets strings into categories via per-category regexes.
### `run(path, min_len)`
Returns counts, categorized strings, and an obfuscation hint.
## Usage
```bash
python scripts/analyst.py strings sample.bin --min-len 5
```
## Sources
- GNU binutils `strings` — https://www.gnu.org/software/binutils/
- FLOSS (FLARE Obfuscated String Solver) for stacked/encoded strings —
https://github.com/mandiant/flare-floss
- UTF-16LE wide-string extraction (printable byte + 0x00), standard Windows-analysis practice

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""Extract ASCII and UTF-16LE strings from a binary and classify them.
Scans the inert file for printable ASCII runs and UTF-16LE runs at a minimum length, then buckets
results into investigative categories (urls, ips, paths, registry, mutex/event, commands, apis).
It reads bytes only and never executes the sample.
"""
from __future__ import annotations
import argparse
import json
import re
import sys
from pathlib import Path
PRINTABLE = rb"[\x20-\x7e]"
def ascii_strings(data: bytes, min_len: int) -> list[str]:
pat = re.compile(PRINTABLE + b"{%d,}" % min_len)
return [m.decode("latin-1") for m in pat.findall(data)]
def unicode_strings(data: bytes, min_len: int) -> list[str]:
# UTF-16LE: printable byte followed by 0x00, repeated
pat = re.compile(b"(?:" + PRINTABLE + b"\x00){%d,}" % min_len)
out = []
for m in pat.findall(data):
out.append(m.decode("utf-16-le", errors="replace"))
return out
CATEGORIES = {
"urls": re.compile(r"https?://[^\s\"'<>]+", re.I),
"ips": re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b"),
"paths": re.compile(r"[a-zA-Z]:\\[^\s\"'<>|]+|/(?:usr|etc|tmp|var|home)/[^\s\"'<>|]+"),
"registry": re.compile(r"(?:HKLM|HKCU|HKEY_[A-Z_]+)\\[^\s\"'<>|]+", re.I),
"commands": re.compile(r"\b(?:cmd\.exe|powershell|rundll32|regsvr32|schtasks|net\s+user)\b",
re.I),
"apis": re.compile(r"\b(?:VirtualAlloc|WriteProcessMemory|CreateRemoteThread|LoadLibrary|"
r"GetProcAddress|WinExec|URLDownloadToFile)\w*", re.I),
}
def classify(strings: list[str]) -> dict:
buckets: dict[str, set] = {k: set() for k in CATEGORIES}
for s in strings:
for cat, rx in CATEGORIES.items():
for m in rx.findall(s):
buckets[cat].add(m)
return {k: sorted(v) for k, v in buckets.items() if v}
def run(path: str, min_len: int) -> dict:
data = Path(path).read_bytes()
a = ascii_strings(data, min_len)
u = unicode_strings(data, min_len)
allstr = a + u
return {
"ascii_count": len(a),
"unicode_count": len(u),
"categorized": classify(allstr),
"obfuscation_hint": len(allstr) < 20,
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Extract and classify strings")
sub = parser.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("strings")
p.add_argument("file")
p.add_argument("--min-len", type=int, default=5)
args = parser.parse_args(argv)
print(json.dumps(run(args.file, args.min_len), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())