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,106 @@
|
||||
---
|
||||
name: reverse-engineering-binaries-with-ghidra
|
||||
description: 'Uses Ghidra to disassemble and decompile a binary, navigate to key routines via
|
||||
imports and strings, annotate decompiled code, and run headless scripts to automate
|
||||
extraction of C2, crypto, and config. Activates for requests to reverse engineer with
|
||||
Ghidra, decompile a binary, or script Ghidra headless analysis.'
|
||||
domain: cybersecurity
|
||||
subdomain: reverse-engineering
|
||||
tags:
|
||||
- reverse-engineering
|
||||
- ghidra
|
||||
- decompilation
|
||||
- disassembly
|
||||
- headless
|
||||
- static-analysis
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1027
|
||||
- T1140
|
||||
- T1480
|
||||
d3fend:
|
||||
- D3-FA
|
||||
- D3-FCR
|
||||
references:
|
||||
- 'Ghidra documentation — https://ghidra-sre.org/'
|
||||
- 'Ghidra API (FlatProgramAPI / headless) — https://ghidra.re/ghidra_docs/api/'
|
||||
---
|
||||
|
||||
# Reverse Engineering Binaries with Ghidra
|
||||
|
||||
## When to Use
|
||||
|
||||
- Static/dynamic triage has identified functionality that needs code-level understanding.
|
||||
- You need to reverse a C2 protocol, decryption routine, or custom obfuscation.
|
||||
- You want to extract hardcoded config (C2, keys, campaign IDs) from compiled code, or
|
||||
automate extraction across many samples via headless scripts.
|
||||
|
||||
**Do not use** Ghidra as a first step for unknown samples — triage statically and behaviorally
|
||||
first, and unpack before decompiling a packed binary.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Ghidra 11.x with JDK 17+.
|
||||
- An isolated analysis host.
|
||||
- Familiarity with x86/x64 (or the target arch) and OS API conventions.
|
||||
- The sample unpacked (decompiling packed code wastes effort).
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Ghidra performs static analysis and does not run the sample, but still work in the lab.
|
||||
- Treat any dumped/derived payloads as live samples.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Import and auto-analyze
|
||||
|
||||
Create a project, import the binary (Ghidra detects PE/ELF/Mach-O and arch), and run
|
||||
auto-analysis with default analyzers.
|
||||
|
||||
### Step 2: Navigate to interesting code
|
||||
|
||||
Start from imports (Symbol Tree) and strings (Defined Strings). Cross-reference (`X`) from a
|
||||
suspicious API or string to its callers to reach the relevant function quickly.
|
||||
|
||||
```text
|
||||
VirtualAlloc/WriteProcessMemory -> injection logic
|
||||
InternetOpen/WinHttp* -> C2
|
||||
CryptEncrypt / custom XOR loops -> crypto/config decode
|
||||
```
|
||||
|
||||
### Step 3: Annotate the decompiler output
|
||||
|
||||
Retype variables, rename functions, and add comments as you understand the code. Good naming
|
||||
turns the decompiler into readable pseudo-C and compounds across a session.
|
||||
|
||||
### Step 4: Automate with headless scripts
|
||||
|
||||
For repeatable extraction, run Ghidra headless with a post-script. The bundled Python uses
|
||||
`analyzeHeadless` to export functions/strings to JSON:
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py headless --ghidra /opt/ghidra --bin sample.bin --out out.json
|
||||
```
|
||||
|
||||
### Step 5: Extract the target artifact
|
||||
|
||||
Recover the C2 string, key, or config and validate it against dynamic observations.
|
||||
|
||||
## Validation
|
||||
|
||||
- Renamed/retyped routines produce coherent pseudo-C consistent with observed behavior.
|
||||
- Extracted config (C2/keys) matches what the sample uses at runtime.
|
||||
- Headless export reproduces the interactive findings.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Decompiling packed/obfuscated code — unpack first or the output is meaningless.
|
||||
- Trusting decompiler types blindly; verify against the disassembly for crypto/pointer logic.
|
||||
- Ignoring TLS callbacks/constructors that run before the entry point.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the headless wrapper.
|
||||
- Ghidra documentation and API (linked in frontmatter).
|
||||
@@ -0,0 +1,39 @@
|
||||
# API Reference: Reverse Engineering Binaries with Ghidra
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` wraps Ghidra's `analyzeHeadless` to import a binary, run auto-analysis, and
|
||||
optionally execute a post-script (such as a function/string exporter). It orchestrates Ghidra
|
||||
and does not run the sample.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | subprocess, JSON, tempfile |
|
||||
|
||||
## External tools
|
||||
|
||||
| Tool | Version | Purpose |
|
||||
|------|---------|---------|
|
||||
| Ghidra | 11.x | Disassembly, decompilation, headless scripting |
|
||||
| JDK | 17+ | Required by Ghidra |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `run_headless(ghidra_path, binary, post_script, script_path, project_dir, timeout)`
|
||||
Builds and runs the `analyzeHeadless` command (import, overwrite, delete temp project) and
|
||||
returns the command, return code, and trimmed stdout/stderr.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py headless --ghidra /opt/ghidra --bin sample.bin \
|
||||
--post-script ExportFunctions.py --script-path ./ghidra_scripts --out out.json
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- Ghidra documentation — https://ghidra-sre.org/
|
||||
- Ghidra API / headless analyzer — https://ghidra.re/ghidra_docs/api/
|
||||
- analyzeHeadless usage (Ghidra `support/analyzeHeadlessREADME.html`)
|
||||
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Wrapper for Ghidra headless analysis.
|
||||
|
||||
Drives ``support/analyzeHeadless`` to import a binary, run auto-analysis, and execute an
|
||||
optional post-script (e.g. one that exports functions/strings to JSON). This orchestrates
|
||||
Ghidra; it does not execute the sample. Ghidra performs static analysis only.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess # noqa: S404 - invoking the local Ghidra installation
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
def run_headless(ghidra_path: str, binary: str, post_script: str | None = None,
|
||||
script_path: str | None = None, project_dir: str | None = None,
|
||||
timeout: int = 1200) -> dict:
|
||||
analyze = os.path.join(ghidra_path, "support",
|
||||
"analyzeHeadless.bat" if os.name == "nt" else "analyzeHeadless")
|
||||
if not os.path.isfile(analyze):
|
||||
raise FileNotFoundError(f"analyzeHeadless not found at {analyze}")
|
||||
project_dir = project_dir or tempfile.mkdtemp(prefix="ghidra_proj_")
|
||||
cmd = [analyze, project_dir, "analyst-ai-pack",
|
||||
"-import", binary, "-overwrite", "-deleteProject"]
|
||||
if post_script:
|
||||
cmd += ["-postScript", post_script]
|
||||
if script_path:
|
||||
cmd += ["-scriptPath", script_path]
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True, # noqa: S603
|
||||
timeout=timeout, check=False)
|
||||
return {
|
||||
"command": " ".join(cmd),
|
||||
"returncode": proc.returncode,
|
||||
"stdout_tail": proc.stdout[-4000:],
|
||||
"stderr_tail": proc.stderr[-2000:],
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Ghidra headless wrapper")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("headless")
|
||||
p.add_argument("--ghidra", required=True, help="Ghidra install dir")
|
||||
p.add_argument("--bin", required=True, dest="binary")
|
||||
p.add_argument("--post-script")
|
||||
p.add_argument("--script-path")
|
||||
p.add_argument("--out")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
result = run_headless(args.ghidra, args.binary, args.post_script, args.script_path)
|
||||
text = json.dumps(result, indent=2)
|
||||
if args.out:
|
||||
with open(args.out, "w", encoding="utf-8") as fh:
|
||||
fh.write(text)
|
||||
print(text)
|
||||
return 0 if result["returncode"] == 0 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user