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,93 @@
|
||||
---
|
||||
name: solving-malware-with-symbolic-execution
|
||||
description: 'Uses symbolic execution (angr) to solve constraint-based malware problems — finding
|
||||
inputs that reach a target branch, recovering keys/passwords from a check routine, and
|
||||
brute-forcing opaque predicates — by generating a ready-to-edit angr harness. Activates for
|
||||
requests to use symbolic execution, solve a malware unlock check with angr, or recover an input
|
||||
that reaches a code path.'
|
||||
domain: cybersecurity
|
||||
subdomain: reverse-engineering
|
||||
tags:
|
||||
- reverse-engineering
|
||||
- symbolic-execution
|
||||
- angr
|
||||
- deobfuscation
|
||||
- automation
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1027
|
||||
- T1480
|
||||
- T1140
|
||||
d3fend:
|
||||
- D3-DA
|
||||
- D3-SDA
|
||||
references:
|
||||
- 'angr documentation — https://docs.angr.io/'
|
||||
- 'MITRE ATT&CK T1480 Execution Guardrails — https://attack.mitre.org/techniques/T1480/'
|
||||
---
|
||||
|
||||
# Solving Malware With Symbolic Execution
|
||||
|
||||
## When to Use
|
||||
|
||||
- You need to find an input that reaches (or avoids) a specific address — a license/unlock check,
|
||||
an environment guardrail, or an opaque predicate — without manual constraint solving.
|
||||
- You want to recover a key/password a check routine validates, or prune bogus branches.
|
||||
|
||||
**Do not use** angr's `unicorn`/concrete execution against untrusted code outside isolation — and
|
||||
do not treat symbolic execution as a substitute for understanding the routine. This skill generates
|
||||
a harness; run it in an isolated environment.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The target binary and the relevant addresses (entry, target/avoid). The `angr` package (the
|
||||
script degrades gracefully and reports availability if absent).
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Run generated harnesses in an isolated VM; angr loads the binary — keep inputs contained.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Check availability and generate a harness
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py check
|
||||
python scripts/analyst.py harness --binary sample.bin --find 0x401234 --avoid 0x401260 \
|
||||
--out solve.py
|
||||
```
|
||||
|
||||
Emits an angr script that loads the binary, sets a symbolic stdin/argument, explores to `--find`
|
||||
while avoiding `--avoid`, and prints the solving input.
|
||||
|
||||
### Step 2: Constrain the input
|
||||
|
||||
Edit the harness to add the input's length/charset constraints (printable, specific size) so the
|
||||
solver converges quickly.
|
||||
|
||||
### Step 3: Run and interpret
|
||||
|
||||
Run the harness; the recovered concrete input is the key/password/flag that reaches the target.
|
||||
|
||||
### Step 4: Validate manually
|
||||
|
||||
Confirm the recovered input actually drives the intended path in a debugger/disassembler.
|
||||
|
||||
## Validation
|
||||
|
||||
- The harness sets explicit find/avoid addresses and a constrained symbolic input.
|
||||
- A found solution is verified to reach the target in a debugger.
|
||||
- State explosion is mitigated with constraints or `veritesting`.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- State explosion on loops/large inputs — constrain length and use `LAZY_SOLVES`/`veritesting`.
|
||||
- Symbolic-execution-hostile code (hashing, heavy crypto) that angr cannot tractably solve.
|
||||
- Wrong addresses (ASLR/base) — use the binary's load base, not a runtime address.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the harness generator.
|
||||
- angr docs and ATT&CK T1480 (linked in frontmatter).
|
||||
@@ -0,0 +1,42 @@
|
||||
# API Reference: Solving Malware With Symbolic Execution
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` generates an angr harness that loads a binary, explores to a target address
|
||||
while avoiding another, constrains a symbolic input, and prints the solving input. `check` reports
|
||||
angr availability. The tool only generates text — it does not load the binary or run the sample.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| angr | latest | runs the generated harness (optional for generation) |
|
||||
| (stdlib) | — | JSON, file IO |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `check()`
|
||||
Reports whether `angr` is importable.
|
||||
|
||||
### `harness(binary, find, avoid, out)`
|
||||
Writes an angr script using `full_init_state`, a `claripy` symbolic stdin (printable-constrained),
|
||||
`Veritesting`, and `simgr.explore(find=…, avoid=…)`.
|
||||
|
||||
## Tuning for convergence
|
||||
|
||||
- Constrain input length and charset (the skeleton sets printable bytes).
|
||||
- Use `Veritesting` (enabled) and consider `LAZY_SOLVES` for branchy code.
|
||||
- Provide accurate load-base addresses for `find`/`avoid`.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py check
|
||||
python scripts/analyst.py harness --binary sample.bin --find 0x401234 --avoid 0x401260 --out solve.py
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- angr documentation (simulation managers, exploration techniques) — https://docs.angr.io/
|
||||
- MITRE ATT&CK T1480 Execution Guardrails — https://attack.mitre.org/techniques/T1480/
|
||||
- claripy symbolic-variable and constraint API from the angr docs
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate an angr symbolic-execution harness (and report angr availability).
|
||||
|
||||
`check` reports whether the angr package is importable. `harness` writes a ready-to-edit angr
|
||||
script that loads a binary, explores to a target address while avoiding another, and prints the
|
||||
solving input. This tool only generates text — it does not load the binary or run the sample.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import angr # type: ignore # noqa: F401
|
||||
HAVE_ANGR = True
|
||||
except ImportError:
|
||||
HAVE_ANGR = False
|
||||
|
||||
|
||||
def check() -> dict:
|
||||
return {"angr_available": HAVE_ANGR,
|
||||
"note": "pip install angr to run generated harnesses" if not HAVE_ANGR else "ready"}
|
||||
|
||||
|
||||
def harness(binary: str, find: str, avoid: str | None, out: str) -> dict:
|
||||
avoid_line = (f" avoid_addr = {avoid}\n" if avoid else " avoid_addr = []\n")
|
||||
explore = ("simgr.explore(find=find_addr, avoid=avoid_addr)"
|
||||
if avoid else "simgr.explore(find=find_addr)")
|
||||
text = (
|
||||
"#!/usr/bin/env python3\n"
|
||||
"# angr symbolic-execution harness (generated skeleton). Run in an isolated VM.\n"
|
||||
"import angr\n"
|
||||
"import claripy\n\n"
|
||||
"def solve():\n"
|
||||
f" proj = angr.Project({binary!r}, auto_load_libs=False)\n"
|
||||
f" find_addr = {find}\n"
|
||||
f"{avoid_line}"
|
||||
" # Symbolic stdin of bounded length; tighten the charset/length below.\n"
|
||||
" flag_len = 32\n"
|
||||
" flag = claripy.BVS('flag', 8 * flag_len)\n"
|
||||
" state = proj.factory.full_init_state(stdin=flag)\n"
|
||||
" for i in range(flag_len):\n"
|
||||
" b = flag.get_byte(i)\n"
|
||||
" state.solver.add(b >= 0x20, b <= 0x7e) # printable\n"
|
||||
" simgr = proj.factory.simulation_manager(state)\n"
|
||||
" simgr.use_technique(angr.exploration_techniques.Veritesting())\n"
|
||||
f" {explore}\n"
|
||||
" if simgr.found:\n"
|
||||
" found = simgr.found[0]\n"
|
||||
" print(found.posix.dumps(0)) # the solving input\n"
|
||||
" else:\n"
|
||||
" print('no solution reached the target')\n\n"
|
||||
"if __name__ == '__main__':\n"
|
||||
" solve()\n"
|
||||
)
|
||||
Path(out).write_text(text, encoding="utf-8")
|
||||
return {"output": out, "find": find, "avoid": avoid, "angr_available": HAVE_ANGR}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Generate an angr harness")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
sub.add_parser("check")
|
||||
ph = sub.add_parser("harness")
|
||||
ph.add_argument("--binary", required=True)
|
||||
ph.add_argument("--find", required=True, help="target address, e.g. 0x401234")
|
||||
ph.add_argument("--avoid", help="address to avoid, e.g. 0x401260")
|
||||
ph.add_argument("--out", required=True)
|
||||
args = parser.parse_args(argv)
|
||||
if args.cmd == "check":
|
||||
print(json.dumps(check(), indent=2))
|
||||
else:
|
||||
print(json.dumps(harness(args.binary, args.find, args.avoid, args.out), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user