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-rust-malware-internals
|
||||
description: 'Analyzes Rust-compiled malware by detecting the Rust toolchain signature, demangling
|
||||
Rust v0/legacy symbol names, and identifying crate dependencies from embedded paths. Activates
|
||||
for requests to analyze Rust malware, demangle Rust symbols, or identify a Rust binary build and
|
||||
its crates.'
|
||||
domain: cybersecurity
|
||||
subdomain: reverse-engineering
|
||||
tags:
|
||||
- reverse-engineering
|
||||
- rust
|
||||
- symbol-demangling
|
||||
- crates
|
||||
- stripped-binaries
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1027
|
||||
- T1587.001
|
||||
- T1059
|
||||
d3fend:
|
||||
- D3-SDA
|
||||
- D3-DA
|
||||
references:
|
||||
- 'Rust symbol mangling (RFC 2603 v0 scheme) — https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html'
|
||||
- 'MITRE ATT&CK T1027 Obfuscated Files or Information — https://attack.mitre.org/techniques/T1027/'
|
||||
---
|
||||
|
||||
# Analyzing Rust Malware Internals
|
||||
|
||||
## When to Use
|
||||
|
||||
- You have a Rust-compiled binary and need to confirm the toolchain, demangle symbol names, and
|
||||
enumerate crate dependencies.
|
||||
- Standard analysis is hampered by Rust's name mangling and large static linking.
|
||||
|
||||
**Do not use** this for non-Rust binaries — confirm the Rust signature first. This skill reads the
|
||||
binary statically and executes nothing.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The Rust binary (read inertly).
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Read bytes statically; treat strings as untrusted.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Confirm Rust and toolchain
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py info sample.bin
|
||||
```
|
||||
|
||||
Looks for `rustc`/`cargo` markers, the `rust` panic/unwind strings, and embedded source paths
|
||||
(`/rustc/<hash>/library/...`, `~/.cargo/registry/...`).
|
||||
|
||||
### Step 2: Demangle symbol names
|
||||
|
||||
Detects v0 (`_R...`) and legacy (`_ZN...17h<hash>E`) mangled symbols and demangles them to readable
|
||||
paths.
|
||||
|
||||
### Step 3: Enumerate crates
|
||||
|
||||
Extract crate names/versions from `.cargo/registry/src/.../<crate>-<ver>/` paths to fingerprint
|
||||
capabilities (e.g., `reqwest`, `tokio`, `aes`, `winapi`).
|
||||
|
||||
### Step 4: Map and document
|
||||
|
||||
Map notable crates to behavior and ATT&CK.
|
||||
|
||||
## Validation
|
||||
|
||||
- The Rust toolchain signature is confirmed before demangling.
|
||||
- Demangled names are readable module paths (not still-mangled).
|
||||
- Crate names/versions are plausible registry entries.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Mixing v0 and legacy mangling in one binary — handle both.
|
||||
- Stripped binaries where only registry path strings remain.
|
||||
- Static crates inflating the symbol set; focus on the author's crate/`main`.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the analyzer/demangler.
|
||||
- Rust v0 mangling RFC and ATT&CK T1027 (linked in frontmatter).
|
||||
@@ -0,0 +1,39 @@
|
||||
# API Reference: Analyzing Rust Malware Internals
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` confirms a Rust build (rustc/cargo markers, `/rustc/<hash>/library/` paths,
|
||||
panic strings), heuristically demangles legacy `_ZN...E` symbols, detects v0 `_R` symbols, and
|
||||
extracts crate name/version pairs from `.cargo/registry` paths, mapping notable crates to
|
||||
capabilities. It reads bytes statically and executes nothing.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | regex, JSON, file IO |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `info(path)`
|
||||
Returns Rust detection, legacy-demangled names, v0 symbols present, crates, and capabilities.
|
||||
|
||||
### `_demangle_legacy(sym)`
|
||||
Decodes the length-prefixed segments of a legacy `_ZN...17h<hash>E` symbol into `a::b::c`.
|
||||
|
||||
## Notes
|
||||
|
||||
- v0 (`_R`) symbols are detected; full v0 demangling is delegated to a dedicated tool
|
||||
(e.g., `rustfilt`).
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py info sample.bin
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- Rust symbol mangling v0 (RFC 2603) — https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html
|
||||
- Legacy `_ZN`/Itanium-style length-prefixed mangling (public Rust ABI docs)
|
||||
- Cargo registry path layout (`registry/src/<index>/<crate>-<ver>`) from Cargo documentation
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Analyze a Rust-compiled binary: confirm Rust, demangle symbols, enumerate crates.
|
||||
|
||||
Detects Rust toolchain markers, demangles legacy (_ZN...E) Rust symbols heuristically, and extracts
|
||||
crate names/versions from embedded .cargo registry paths. Reads bytes statically; executes nothing.
|
||||
v0 (_R) demangling is reported as detected but full v0 decoding is delegated to a dedicated tool.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ASCII_RE = re.compile(rb"[\x20-\x7e]{3,}")
|
||||
RE_LEGACY = re.compile(r"_ZN([0-9A-Za-z$._]+)17h[0-9a-f]{16}E")
|
||||
RE_V0 = re.compile(r"_R[0-9A-Za-z_]{4,}")
|
||||
RE_CRATE = re.compile(r"registry/src/[^/]+/([A-Za-z0-9_-]+)-(\d+\.\d+\.\d+)")
|
||||
RE_RUSTC_PATH = re.compile(r"/rustc/[0-9a-f]{6,}/library/")
|
||||
|
||||
CAP_CRATES = {
|
||||
"reqwest": "HTTP client", "hyper": "HTTP", "tokio": "async runtime",
|
||||
"aes": "AES crypto", "rsa": "RSA crypto", "winapi": "Windows API",
|
||||
"windows": "Windows API", "sysinfo": "host enumeration", "tonic": "gRPC",
|
||||
}
|
||||
|
||||
|
||||
def _demangle_legacy(sym: str) -> str:
|
||||
# _ZN<len>name<len>name...17h<hash>E -> name::name::...
|
||||
body = sym[len("_ZN"):]
|
||||
parts = []
|
||||
i = 0
|
||||
while i < len(body):
|
||||
j = i
|
||||
while j < len(body) and body[j].isdigit():
|
||||
j += 1
|
||||
if j == i:
|
||||
break
|
||||
length = int(body[i:j])
|
||||
seg = body[j:j + length]
|
||||
if seg.startswith("17h"):
|
||||
break
|
||||
parts.append(seg)
|
||||
i = j + length
|
||||
return "::".join(parts) if parts else sym
|
||||
|
||||
|
||||
def info(path: str) -> dict:
|
||||
data = Path(path).read_bytes()
|
||||
strings = [m.decode("latin-1", "ignore") for m in ASCII_RE.findall(data)]
|
||||
blob = "\n".join(strings)
|
||||
is_rust = bool(RE_RUSTC_PATH.search(blob)) or "cargo/registry" in blob or "rustc" in blob \
|
||||
or "called `Result::unwrap()`" in blob
|
||||
legacy = sorted({_demangle_legacy("_ZN" + m) for m in RE_LEGACY.findall(blob)})[:200]
|
||||
v0_present = sorted(set(RE_V0.findall(blob)))[:50]
|
||||
crates = sorted({f"{n}-{v}" for n, v in RE_CRATE.findall(blob)})[:100]
|
||||
caps = sorted({desc for c, desc in CAP_CRATES.items()
|
||||
if any(cr.startswith(c + "-") for cr in crates)})
|
||||
return {
|
||||
"is_rust_binary": is_rust,
|
||||
"legacy_demangled": legacy,
|
||||
"v0_symbols_present": v0_present,
|
||||
"crates": crates,
|
||||
"capabilities": caps,
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Analyze a Rust binary")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("info")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(info(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user