docs: define initial ghidra-cli product
This commit is contained in:
commit
57597e7d6d
4 changed files with 454 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/target/
|
||||
*.log
|
||||
*.gpr
|
||||
*.rep/
|
||||
.direnv/
|
||||
.envrc
|
||||
|
||||
43
CONTEXT.md
Normal file
43
CONTEXT.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Headless Reverse Engineering
|
||||
|
||||
This context describes how users ask reproducible, read-only questions about
|
||||
binaries through Ghidra without managing Ghidra's internal project model.
|
||||
|
||||
## Language
|
||||
|
||||
**Sample**:
|
||||
An immutable sequence of input bytes identified by a cryptographic content
|
||||
digest. Identical bytes are the same Sample even when they have different file
|
||||
names or locations.
|
||||
_Avoid_: Binary, file, target
|
||||
|
||||
**Analysis Profile**:
|
||||
The declared analysis inputs other than the Sample, including the Ghidra
|
||||
version, analyzer configuration, language selection, and tool protocol version.
|
||||
_Avoid_: Configuration, settings
|
||||
|
||||
**Analysis**:
|
||||
The reusable body of facts Ghidra derives from one Sample under one Analysis
|
||||
Profile.
|
||||
_Avoid_: Project, database, workspace
|
||||
|
||||
**Query**:
|
||||
A read-only question evaluated against an Analysis. A Query cannot change the
|
||||
Sample or Analysis.
|
||||
_Avoid_: Command, job, operation
|
||||
|
||||
**Function Selector**:
|
||||
An unambiguous reference used to choose a function by address or resolved
|
||||
symbol name.
|
||||
_Avoid_: Function name, function ID
|
||||
|
||||
**Artifact**:
|
||||
A durable, schema-versioned representation of a Query result tied to its
|
||||
Analysis.
|
||||
_Avoid_: Output, report, result file
|
||||
|
||||
**Analysis Store**:
|
||||
The tool-owned collection of reusable Analyses and Artifacts. It is disposable
|
||||
and never contains the authoritative Sample.
|
||||
_Avoid_: Project directory, cache directory, workspace
|
||||
|
||||
368
PRD.md
Normal file
368
PRD.md
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
# Product Requirements Document
|
||||
|
||||
## ghidra-cli
|
||||
|
||||
- Status: Draft
|
||||
- Revision: 0.1
|
||||
- Audience: maintainers and early users
|
||||
|
||||
## Summary
|
||||
|
||||
`ghidra-cli` is a small Rust command-line interface for reproducible, read-only
|
||||
analysis and decompilation through Ghidra. It hides Ghidra project management
|
||||
and Java process details behind commands that accept a sample path directly.
|
||||
|
||||
The first release favors a narrow, reliable workflow over a broad command
|
||||
surface. It is intended to work equally well for a person at a terminal and an
|
||||
automation agent consuming structured output.
|
||||
|
||||
## Problem
|
||||
|
||||
Ghidra supports headless analysis, but its native interface exposes concepts
|
||||
such as project directories, project names, import and process modes, script
|
||||
paths, and JVM configuration. Answering a simple question about a sample
|
||||
therefore requires orchestration knowledge unrelated to reverse engineering.
|
||||
|
||||
Existing wrappers commonly address this by becoming persistent services with
|
||||
background JVMs, sockets, job queues, project lifecycle commands, custom query
|
||||
languages, and mutating operations. Those capabilities are useful in mature
|
||||
interactive systems, but they increase the number of states that can fail and
|
||||
make a small tool harder to trust.
|
||||
|
||||
## Product principles
|
||||
|
||||
1. A sample path is enough. Users do not manage Ghidra projects.
|
||||
2. Read-only by default and throughout the first release.
|
||||
3. Content determines identity. Moving or renaming a sample does not invalidate
|
||||
compatible analysis.
|
||||
4. Structured output is a public interface, not formatted log text.
|
||||
5. Every failure is explicit, typed, and actionable.
|
||||
6. No background process is required for the initial command set.
|
||||
7. Concurrency is introduced only with a concrete concurrent workflow.
|
||||
8. Ghidra remains the analysis engine; Rust owns orchestration and policy.
|
||||
|
||||
## Users
|
||||
|
||||
### Human analyst
|
||||
|
||||
Wants to inspect an unfamiliar executable, discover functions, and decompile a
|
||||
selected function without opening the Ghidra GUI or learning its project model.
|
||||
|
||||
### Automation agent
|
||||
|
||||
Needs deterministic commands, bounded output, stable JSON schemas, meaningful
|
||||
exit codes, and enough provenance to explain where a result came from.
|
||||
|
||||
### Maintainer
|
||||
|
||||
Needs a small dependency graph, reproducible fixtures, a clear Java boundary,
|
||||
and compatibility failures that are discovered by tests rather than users.
|
||||
|
||||
## Goals for the first release
|
||||
|
||||
- Diagnose whether the local Ghidra and JDK installation is usable.
|
||||
- Inspect basic sample and analysis metadata.
|
||||
- List discovered functions with stable identifiers.
|
||||
- Decompile one selected function.
|
||||
- Reuse compatible analysis without requiring user-managed project names.
|
||||
- Emit concise terminal output and versioned JSON.
|
||||
- Bound Ghidra execution by configurable time and memory limits.
|
||||
- Operate without network access after dependencies are installed.
|
||||
|
||||
## Non-goals for the first release
|
||||
|
||||
- Replacing Ghidra's decompiler or analysis engine.
|
||||
- A daemon, TCP bridge, HTTP service, or MCP server.
|
||||
- Multiple concurrent Ghidra analyses.
|
||||
- An interactive shell.
|
||||
- Binary patching or exporting modified executables.
|
||||
- Renaming symbols, applying types, or changing function signatures.
|
||||
- Arbitrary Java or Python script execution.
|
||||
- A custom filtering or query language.
|
||||
- Automatically downloading Ghidra or a JDK.
|
||||
- Managing user-visible Ghidra projects.
|
||||
- Supporting every historical Ghidra version.
|
||||
|
||||
## Command-line experience
|
||||
|
||||
### Installation diagnosis
|
||||
|
||||
```console
|
||||
$ ghidra-cli doctor
|
||||
Ghidra 12.1: ready
|
||||
JDK 21: ready
|
||||
Analysis store: writable
|
||||
```
|
||||
|
||||
`doctor` must not modify a Sample or create an Analysis. It may create and
|
||||
remove a temporary file inside the Analysis Store to verify writability.
|
||||
|
||||
### Inspect a sample
|
||||
|
||||
```console
|
||||
$ ghidra-cli inspect ./sample
|
||||
Sample: 8d969eef...
|
||||
Format: ELF
|
||||
Language: x86:LE:64
|
||||
Functions: 42
|
||||
```
|
||||
|
||||
If no compatible Analysis exists, the command creates one automatically. The
|
||||
same bytes at another path reuse the Analysis. Different bytes at the same path
|
||||
create a different Analysis.
|
||||
|
||||
### List functions
|
||||
|
||||
```console
|
||||
$ ghidra-cli functions ./sample
|
||||
ADDRESS SIZE NAME
|
||||
0x00401000 112 _start
|
||||
0x00401120 248 main
|
||||
```
|
||||
|
||||
The command supports an explicit result limit. It does not implement a
|
||||
filtering expression language in the first release; JSON consumers can use
|
||||
existing tools such as `jq`.
|
||||
|
||||
### Decompile a function
|
||||
|
||||
```console
|
||||
$ ghidra-cli decompile ./sample main
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
A function may be selected by address or a uniquely resolved symbol. An
|
||||
ambiguous symbol is an error and returns the candidates instead of silently
|
||||
choosing one.
|
||||
|
||||
### Structured output
|
||||
|
||||
```console
|
||||
$ ghidra-cli --format json inspect ./sample
|
||||
```
|
||||
|
||||
JSON uses a versioned envelope:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 1,
|
||||
"kind": "inspection",
|
||||
"provenance": {
|
||||
"sample_sha256": "<digest>",
|
||||
"ghidra_version": "<version>",
|
||||
"analysis_profile": "<digest>"
|
||||
},
|
||||
"data": {},
|
||||
"warnings": []
|
||||
}
|
||||
```
|
||||
|
||||
Standard output contains only the requested result. Diagnostics and progress
|
||||
belong on standard error.
|
||||
|
||||
## Functional requirements
|
||||
|
||||
### Sample identity
|
||||
|
||||
- Compute a SHA-256 digest from the Sample bytes.
|
||||
- Detect if the Sample changes while it is being read.
|
||||
- Treat identical bytes as the same Sample regardless of path.
|
||||
- Never write to the Sample.
|
||||
- Record the observed path as provenance, not identity.
|
||||
|
||||
### Analysis compatibility
|
||||
|
||||
An Analysis is reusable only when its Sample digest and Analysis Profile match.
|
||||
At minimum, the Analysis Profile includes:
|
||||
|
||||
- Ghidra version.
|
||||
- Ghidra language and compiler specification.
|
||||
- Enabled analyzer configuration.
|
||||
- Java adapter protocol version.
|
||||
|
||||
An incompatible Analysis is preserved until normal retention policy removes it;
|
||||
it is not overwritten in place.
|
||||
|
||||
### Analysis execution
|
||||
|
||||
- Invoke the official Ghidra headless launcher as a child process.
|
||||
- Pass requests to a small, bundled Java adapter.
|
||||
- Exchange structured request and response files rather than scrape logs.
|
||||
- Capture Ghidra logs separately for diagnostics.
|
||||
- Terminate the child process after each command in the first release.
|
||||
- Apply a configurable wall-clock timeout.
|
||||
- Report whether termination was graceful or forced.
|
||||
|
||||
### Analysis Store
|
||||
|
||||
- Use a platform-appropriate user data location by default.
|
||||
- Allow an explicit store path through one documented option.
|
||||
- Use atomic writes for manifests and Artifacts.
|
||||
- Detect incomplete Analysis creation after interruption.
|
||||
- Serialize writers for the same Sample and Analysis Profile.
|
||||
- Treat the entire store as disposable.
|
||||
|
||||
### Output behavior
|
||||
|
||||
- Default to concise human-readable output on a terminal.
|
||||
- Support explicit JSON output.
|
||||
- Never change JSON shape based on terminal detection.
|
||||
- Include schema version and provenance in every JSON response.
|
||||
- Use hexadecimal addresses with an explicit address space where necessary.
|
||||
- Place progress and logs on standard error.
|
||||
|
||||
### Errors and exit status
|
||||
|
||||
Distinct error categories must cover:
|
||||
|
||||
- Invalid arguments.
|
||||
- Missing or unreadable Sample.
|
||||
- Unsupported or ambiguous format/language.
|
||||
- Missing or incompatible Ghidra/JDK installation.
|
||||
- Analysis timeout.
|
||||
- Ghidra analysis failure.
|
||||
- Function not found or function selector ambiguous.
|
||||
- Invalid or incompatible cached Analysis.
|
||||
- Internal protocol violation.
|
||||
|
||||
The exact numeric exit-code allocation is a design task before implementation.
|
||||
|
||||
## Architecture boundary
|
||||
|
||||
### Rust CLI responsibilities
|
||||
|
||||
- Argument parsing and validation.
|
||||
- Sample hashing and stable identity.
|
||||
- Analysis Profile construction.
|
||||
- Analysis Store layout, locking, and atomicity.
|
||||
- Child-process lifecycle, timeouts, and log capture.
|
||||
- Typed request and response schemas.
|
||||
- Human and JSON rendering.
|
||||
- Exit-code policy.
|
||||
|
||||
### Java adapter responsibilities
|
||||
|
||||
- Use supported Ghidra APIs.
|
||||
- Import and analyze a Sample.
|
||||
- Resolve Function Selectors.
|
||||
- Extract program and function metadata.
|
||||
- Invoke the Ghidra decompiler.
|
||||
- Serialize only the response defined by the protocol.
|
||||
|
||||
The Java adapter must not listen on a network socket, download dependencies, or
|
||||
execute user-provided scripts.
|
||||
|
||||
## Synchronous execution decision
|
||||
|
||||
The first release uses a synchronous command lifecycle because each invocation
|
||||
coordinates one Ghidra child process and returns one result. Ghidra performs the
|
||||
CPU-intensive work; an asynchronous Rust runtime would not make that work
|
||||
faster.
|
||||
|
||||
The design must not prevent a future asynchronous frontend. A server, parallel
|
||||
batch analysis, interactive session, or responsive cancellation channel would
|
||||
constitute a concrete reason to add an async runtime.
|
||||
|
||||
## Security and safety
|
||||
|
||||
- Treat every Sample and every Ghidra-produced value as untrusted input.
|
||||
- Do not execute the Sample.
|
||||
- Do not provide arbitrary script execution.
|
||||
- Do not require network access during analysis.
|
||||
- Recommend a sandbox or container for untrusted Samples.
|
||||
- Avoid including Sample bytes or extracted secrets in routine logs.
|
||||
- Validate all paths crossing the Rust/Java boundary.
|
||||
- Apply bounded output, memory, and execution-time policies.
|
||||
- Document the supported Ghidra version and relevant security advisories.
|
||||
|
||||
## Performance expectations
|
||||
|
||||
Correctness and reproducibility take priority over startup latency in the first
|
||||
release.
|
||||
|
||||
- A compatible cached Analysis should avoid repeating auto-analysis.
|
||||
- Metadata Queries should reuse exported Artifacts where correctness permits.
|
||||
- Targeted decompilation may start a new JVM.
|
||||
- The CLI should explain whether it reused or created an Analysis when verbose
|
||||
diagnostics are enabled.
|
||||
|
||||
Persistent workers and interactive sessions are deferred until measured usage
|
||||
shows JVM startup to be the dominant usability problem.
|
||||
|
||||
## Test strategy
|
||||
|
||||
- Unit-test Sample identity, profile identity, store layout, schema validation,
|
||||
selector parsing, and error mapping without Ghidra.
|
||||
- Test process lifecycle against a controllable fake child process.
|
||||
- Maintain tiny, source-controlled fixture programs with known properties.
|
||||
- Compile fixtures reproducibly for supported architectures.
|
||||
- Run integration tests against a pinned supported Ghidra release.
|
||||
- Compare JSON against reviewed golden fixtures after normalizing unstable
|
||||
values.
|
||||
- Verify interruption leaves no Analysis that can be mistaken for complete.
|
||||
- Verify identical bytes at different paths reuse one compatible Analysis.
|
||||
- Verify changed bytes at the same path do not reuse the old Analysis.
|
||||
|
||||
## Delivery milestones
|
||||
|
||||
### Milestone 0: design
|
||||
|
||||
- Review this PRD and the domain language.
|
||||
- Choose the project and executable name.
|
||||
- Choose a license.
|
||||
- Pin the initially supported Ghidra and JDK versions.
|
||||
- Define protocol schemas and exit codes.
|
||||
|
||||
### Milestone 1: vertical inspection slice
|
||||
|
||||
- Create the Rust crate and minimal Java adapter.
|
||||
- Implement `doctor`.
|
||||
- Implement `inspect`.
|
||||
- Prove Rust to `analyzeHeadless` to Java to JSON end to end.
|
||||
|
||||
### Milestone 2: function discovery
|
||||
|
||||
- Implement `functions`.
|
||||
- Define stable address and Function Selector representations.
|
||||
- Add reproducible native fixtures.
|
||||
|
||||
### Milestone 3: targeted decompilation
|
||||
|
||||
- Implement `decompile`.
|
||||
- Add ambiguity handling and decompiler time limits.
|
||||
- Stabilize the first public JSON schema.
|
||||
|
||||
### Milestone 4: packaging
|
||||
|
||||
- Provide a reproducible Nix development and test environment.
|
||||
- Document installation and sandboxed use.
|
||||
- Publish the first pre-release.
|
||||
|
||||
## Release acceptance criteria
|
||||
|
||||
The first release is acceptable when:
|
||||
|
||||
- A new user can run `doctor`, `inspect`, `functions`, and `decompile` without
|
||||
manually creating a Ghidra project.
|
||||
- Repeating a Query against an unchanged Sample reuses compatible analysis.
|
||||
- All commands provide versioned JSON and documented exit behavior.
|
||||
- An interrupted analysis cannot be mistaken for a complete one.
|
||||
- Tests exercise a real pinned Ghidra release and known fixture binaries.
|
||||
- No first-release command mutates the Sample or Analysis.
|
||||
- No analysis command requires a network connection.
|
||||
|
||||
## Open decisions
|
||||
|
||||
- Final repository and executable name.
|
||||
- License.
|
||||
- Initially supported Ghidra patch release.
|
||||
- Whether the first supported host is Linux-only.
|
||||
- Default Analysis Store retention policy.
|
||||
- Numeric exit-code allocation.
|
||||
- JSON Schema publication and compatibility policy.
|
||||
- Whether Nix is the sole supported installation path for the first
|
||||
pre-release.
|
||||
|
||||
36
README.md
Normal file
36
README.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# ghidra-cli
|
||||
|
||||
A small, dependable command-line interface for read-only Ghidra analysis.
|
||||
|
||||
The project is currently in its design phase. Its first objective is to make
|
||||
common reverse-engineering questions easy to ask without exposing Ghidra
|
||||
project management, JVM lifecycle, or scripting details to the user.
|
||||
|
||||
Proposed usage:
|
||||
|
||||
```console
|
||||
ghidra-cli doctor
|
||||
ghidra-cli inspect ./sample
|
||||
ghidra-cli functions ./sample
|
||||
ghidra-cli decompile ./sample main
|
||||
```
|
||||
|
||||
Each command will identify the sample by its content, reuse compatible cached
|
||||
analysis, and produce human-readable or versioned JSON output.
|
||||
|
||||
## Documents
|
||||
|
||||
- [PRD.md](./PRD.md) defines the product requirements and initial delivery
|
||||
scope.
|
||||
- [CONTEXT.md](./CONTEXT.md) defines the domain language used throughout the
|
||||
project.
|
||||
|
||||
## Status
|
||||
|
||||
Documentation first. There is intentionally no Rust crate yet. Implementation
|
||||
begins after the initial requirements and interface are reviewed.
|
||||
|
||||
## License
|
||||
|
||||
No license has been selected yet. Until one is added, all rights are reserved.
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue