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,93 @@
---
name: scripting-ghidra-headless-analysis
description: 'Automates batch reverse engineering by generating Ghidra analyzeHeadless commands and
post-analysis script skeletons to import, auto-analyze, and export results for many samples
without the GUI. Activates for requests to script Ghidra headless analysis, batch-analyze samples
with Ghidra, or generate an analyzeHeadless command and post-script.'
domain: cybersecurity
subdomain: reverse-engineering
tags:
- reverse-engineering
- ghidra
- automation
- headless
- batch-analysis
version: 1.0.0
author: analyst-ai-pack
license: Apache-2.0
mitre_attack:
- T1027
- T1059
- T1587.001
d3fend:
- D3-DA
- D3-SDA
references:
- 'Ghidra analyzeHeadless documentation — https://ghidra-sre.org/'
- 'Ghidra API (FlatProgramAPI) — https://ghidra.re/ghidra_docs/api/'
---
# Scripting Ghidra Headless Analysis
## When to Use
- You need to auto-analyze many samples (or re-run a consistent analysis) without the Ghidra GUI.
- You want to generate the `analyzeHeadless` command and a post-script that exports functions,
strings, or decompiled output.
**Do not use** this to run untrusted samples — Ghidra analyzes binaries statically and never
executes them, but keep the import directory isolated. This skill prepares automation, it does not
run the binary.
## Prerequisites
- A Ghidra installation (`analyzeHeadless` in `support/`) and the samples to import.
## Safety & Handling
- Ghidra does not execute the sample; still store imports in an isolated, defanged directory.
## Workflow
### Step 1: Generate the headless command
```bash
python scripts/analyst.py command --project /proj --name Batch --import ./samples --script export.py
```
Builds an `analyzeHeadless` invocation with project path, import directory, post-script, and common
flags (`-recursive`, `-overwrite`, `-deleteProject` optional).
### Step 2: Generate a post-analysis script skeleton
```bash
python scripts/analyst.py postscript --out export.py --emit functions,strings
```
Emits a Ghidra Python (Jython) script that iterates functions/strings and writes JSON next to the
program.
### Step 3: Run and collect
Execute the generated command, then aggregate the per-sample JSON exports.
### Step 4: Review
Spot-check a few exports to confirm analysis completeness before scaling up.
## Validation
- The generated command references a valid project path, import dir, and existing post-script.
- The post-script uses real Ghidra API calls (`currentProgram`, `getFunctionManager`).
- Output JSON is produced per imported program.
## Pitfalls
- Forgetting `-overwrite`/`-recursive`, causing skipped or duplicate imports.
- Jython 2 syntax constraints in post-scripts (no f-strings).
- Project lock conflicts when running multiple headless jobs on one project.
## References
- See [`references/api-reference.md`](references/api-reference.md) for the generator.
- Ghidra analyzeHeadless and API docs (linked in frontmatter).

View File

@@ -0,0 +1,39 @@
# API Reference: Scripting Ghidra Headless Analysis
## Overview
`scripts/analyst.py` generates a Ghidra `analyzeHeadless` command line and a Jython-compatible
post-analysis script that exports functions and/or strings to JSON. It only generates text — it
does not run Ghidra or any sample.
## Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| (stdlib) | — | shlex, JSON, file IO |
## Core functions
### `command(project, name, import_dir, script, recursive, overwrite)`
Builds the analyzeHeadless invocation with common flags.
### `postscript(out, emit)`
Writes a Jython post-script emitting `functions` and/or `strings` to a JSON file next to the
program.
## Generated script notes
- Targets Jython 2 (no f-strings); uses `currentProgram`, `getFunctionManager`, `getListing`.
## Usage
```bash
python scripts/analyst.py command --project /proj --name Batch --import ./samples --script export.py
python scripts/analyst.py postscript --out export.py --emit functions,strings
```
## Sources
- Ghidra analyzeHeadless documentation — https://ghidra-sre.org/
- Ghidra FlatProgramAPI / program model — https://ghidra.re/ghidra_docs/api/
- Headless batch-analysis workflow from official Ghidra `support/analyzeHeadlessREADME.html`

View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""Generate Ghidra analyzeHeadless commands and post-analysis script skeletons.
`command` builds an analyzeHeadless invocation; `postscript` emits a Jython-compatible Ghidra
script that exports functions and/or strings to JSON. This tool only generates text — it does not
run Ghidra or any sample.
"""
from __future__ import annotations
import argparse
import json
import shlex
import sys
from pathlib import Path
FUNCTIONS_SNIPPET = '''
def emit_functions():
fm = currentProgram.getFunctionManager()
out = []
for f in fm.getFunctions(True):
out.append({
"name": f.getName(),
"entry": str(f.getEntryPoint()),
"size": f.getBody().getNumAddresses(),
})
return out
'''
STRINGS_SNIPPET = '''
def emit_strings():
from ghidra.program.model.data import StringDataType
out = []
listing = currentProgram.getListing()
data = listing.getDefinedData(True)
for d in data:
if d.getDataType() and "string" in d.getDataType().getName().lower():
val = d.getValue()
if val is not None:
out.append(str(val))
return out
'''
def command(project: str, name: str, import_dir: str, script: str | None,
recursive: bool, overwrite: bool) -> dict:
parts = ["analyzeHeadless", project, name, "-import", import_dir]
if recursive:
parts.append("-recursive")
if overwrite:
parts.append("-overwrite")
if script:
parts += ["-postScript", script]
cmd = " ".join(shlex.quote(p) for p in parts)
return {"command": cmd, "note": "run from Ghidra's support/ directory"}
def postscript(out: str, emit: list[str]) -> dict:
lines = ["# Ghidra post-analysis script (Jython 2). Generated skeleton.",
"import json", "import os", ""]
funcs = []
if "functions" in emit:
lines.append(FUNCTIONS_SNIPPET)
funcs.append('"functions": emit_functions()')
if "strings" in emit:
lines.append(STRINGS_SNIPPET)
funcs.append('"strings": emit_strings()')
lines.append("result = {%s}" % ", ".join(funcs) if funcs else "result = {}")
lines.append('out_path = currentProgram.getExecutablePath() + ".ghidra.json"')
lines.append('f = open(os.path.basename(out_path), "w")')
lines.append("f.write(json.dumps(result, indent=2))")
lines.append("f.close()")
Path(out).write_text("\n".join(lines), encoding="utf-8")
return {"output": out, "emits": emit}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Generate Ghidra headless automation")
sub = parser.add_subparsers(dest="cmd", required=True)
pc = sub.add_parser("command")
pc.add_argument("--project", required=True)
pc.add_argument("--name", required=True)
pc.add_argument("--import", dest="import_dir", required=True)
pc.add_argument("--script")
pc.add_argument("--recursive", action="store_true", default=True)
pc.add_argument("--overwrite", action="store_true", default=True)
pp = sub.add_parser("postscript")
pp.add_argument("--out", required=True)
pp.add_argument("--emit", default="functions,strings")
args = parser.parse_args(argv)
if args.cmd == "command":
print(json.dumps(command(args.project, args.name, args.import_dir, args.script,
args.recursive, args.overwrite), indent=2))
else:
print(json.dumps(postscript(args.out, [e.strip() for e in args.emit.split(",")]),
indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())