mirror of
https://github.com/morluto/rea.git
synced 2026-11-08 07:46:28 +00:00
200 lines
20 KiB
Markdown
200 lines
20 KiB
Markdown
# Static-analysis provider evaluation
|
|
|
|
Status: the Ghidra read-only analysis provider is shipped on Linux x64 and has
|
|
an experimental Windows x64 P0 for approved native PE applications. It
|
|
validates an exact bring-your-own Ghidra 12.1.4/JDK 21 environment, resolves a
|
|
provider/version/profile commitment, runs one isolated read-only headless
|
|
import, and publishes 19 operation-level capabilities after the authenticated
|
|
post-analysis handshake.
|
|
|
|
The provider-neutral target, provider registry, deterministic target binding,
|
|
analysis-profile commitment, Evidence provenance, snapshot caching, and bounded
|
|
provider-process lifecycle foundations are implemented. The Ghidra launcher,
|
|
packaged Java bridge, doctor/setup projection, bounded client lifecycle, and
|
|
multi-target real Linux verifier, curated Windows CI, and controlled real
|
|
Windows verifier are also implemented. Program identity,
|
|
procedure/string/symbol inventory, memory blocks, address/name and
|
|
containing-procedure resolution, complete inventory search, function metadata,
|
|
decompilation, assembly, resolved calls, typed references, xrefs, CFG, and
|
|
function dossiers are admitted. A provider is not a drop-in replacement for
|
|
Hopper: every capability is mapped explicitly, with truthful `unavailable`,
|
|
unknown, or degraded results where the engine cannot provide equivalent
|
|
semantics.
|
|
|
|
[ADR-0001](adr/0001-provider-selection-and-analysis-profiles.md) fixes the
|
|
provider registry, deterministic selection, target binding, analysis profile,
|
|
snapshot migration, and compatibility semantics that implementation must
|
|
follow.
|
|
|
|
## Approved Ghidra v1 boundary
|
|
|
|
- Keep the existing provider-neutral CLI and MCP tool names.
|
|
- Bind one deep-analysis provider to a target for the target's lifetime; never
|
|
fail over silently between Hopper and Ghidra.
|
|
- Start with bring-your-own Ghidra and a compatible Java runtime. Setup must not
|
|
install or upgrade Java.
|
|
- Run Ghidra headlessly in an owned process with a private temporary project,
|
|
startup and cleanup deadlines, caller cancellation, and complete replies.
|
|
- Prefer a packaged Java bridge loaded through Ghidra's script path. PyGhidra
|
|
remains useful for prototypes but is not a mandatory production dependency.
|
|
- Treat authenticated `ping` and `shutdown` as lifecycle proof only. Inventory,
|
|
xref, CFG, and decompilation claims require separately admitted operation
|
|
contracts and real-provider conformance.
|
|
- Implement read-only inventories, assembly, decompilation, function metadata,
|
|
calls, references, containment, and complete inventory search first.
|
|
- Report GUI cursor/navigation and persistent mutation operations as unavailable
|
|
until their semantics and project ownership are explicitly designed.
|
|
- Verify real claims on Linux with at least two distinct source-owned binaries.
|
|
Verify the Windows host claim with the deterministic source-owned x86-64 PE
|
|
fixture and all admitted operations; compare normalized semantic facts rather
|
|
than provider-specific pseudocode text.
|
|
|
|
## Shipped foundation boundary
|
|
|
|
`GHIDRA_INSTALL_DIR` must identify an extracted official 12.1.4 release;
|
|
optional `JAVA_HOME` must identify a 64-bit full JDK 21, otherwise doctor probes
|
|
`java`/`javac` or `java.exe`/`javac.exe` from `PATH`. Linux accepts x86, x86-64,
|
|
ARM, and ARM64 ELF, PE, and Mach-O executable targets. Windows P0 accepts only
|
|
native x86-64 PE applications and rejects DLLs, managed images, unknown roles,
|
|
other formats, and other architectures before launch.
|
|
|
|
The launcher creates one ephemeral runtime root with project,
|
|
home/cache/config/data/temp, logs, descriptor, endpoint, target snapshot, and
|
|
ownership manifest.
|
|
It passes `-readOnly` and `-deleteProject`, and uses Ghidra's default analysis
|
|
and resource settings; inherited Java option injection variables are cleared.
|
|
On Linux, the mode-0600 descriptor carries the random token without
|
|
exposing it in argv or environment and the Java bridge binds a mode-restricted
|
|
Unix socket. Windows uses authenticated IPv4 loopback with a strict token-free
|
|
endpoint record. Both transports report actual
|
|
Ghidra/language/compiler/analysis/import-digest metadata, accept only the exact
|
|
authenticated `ping`, `shutdown`, and ten
|
|
inventory plus nine function-analysis methods, and deletes the socket. Close,
|
|
cancellation, timeout,
|
|
malformed protocol, or process exit stops the owned process resources and
|
|
removes the entire runtime root. Windows P0 uses bounded `taskkill` tree
|
|
termination and explicitly does not claim Job Object, private-DACL, or
|
|
reparse-point authority; see [Windows Ghidra P0](windows-ghidra-p0.md).
|
|
|
|
The provider catalog lists only the 19 proved Ghidra operations. GUI cursor,
|
|
navigation, and mutation operations remain absent; the router therefore
|
|
reports them unavailable instead of borrowing Hopper semantics or inferring
|
|
capability from a successful import.
|
|
|
|
## Admitted inventory semantics
|
|
|
|
| Concern | Ghidra contract |
|
|
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| Program identity | One `analyzeHeadless` import produces exactly one Program; `list_documents` therefore returns exactly one name. |
|
|
| Addresses | Default memory uses lowercase `0x` hexadecimal. Non-default and external spaces use `<percent-encoded-space>:0x<hex>` and remain round-trippable. The handshake commits image base and default address-space name. |
|
|
| Symbols | `list_names` includes address-bearing memory and external symbols, including dynamic symbols, while excluding variable and no-address namespace records. Each item reports primary, dynamic, external, symbol type, and source facts. |
|
|
| Procedures | Both non-external and external functions are listed. A local thunk remains distinct from its resolved target; exact and qualified name lookup fails on ambiguity rather than guessing. |
|
|
| Strings | Only Ghidra-defined string `Data` is observed. Items report charset, byte length, and whether a required null terminator is missing. The API cannot distinguish a present terminator from fixed/Pascal layouts when no terminator is missing, so that state is named `present_or_not_required`. |
|
|
| Memory | Memory-block end addresses are exclusive. Read/write/execute, initialization, overlay, address space, and image base are direct Ghidra observations. |
|
|
| Inventory | Procedure, symbol, and string listings plus searches return the complete matching collection in one response; callers do not provide offsets or result-count limits. |
|
|
| Search | Literal and Java-regex searches scan the complete immutable inventory and return all matching entries inline. Search waits for a reply or caller cancellation; there are no caller-supplied offsets or result-count limits. |
|
|
| Analysis state | The socket is exposed only after default auto-analysis completes. Established operations wait for their reply or caller cancellation; there is no fixed response-size ceiling. |
|
|
|
|
## Admitted function-analysis semantics
|
|
|
|
| Concern | Ghidra contract |
|
|
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| Decompiler lifetime | One persistent `DecompInterface` is opened for the imported Program and disposed during bridge shutdown. Native decompilation has no fixed per-function deadline. External functions or functions without bodies return `null`; cancellation and native failure remain distinct. |
|
|
| Serialization | A FIFO sends one Program request at a time. Caller cancellation removes queued work promptly and is passed to active socket waits. This is an adapter safety commitment, not a claim that every Ghidra API is thread-safe. |
|
|
| Function identity | Every function result carries the entry address and Ghidra FunctionManager classification for external, thunk, and resolved thunk target. These are observations, not proof that unresolved targetless calls have been recovered. |
|
|
| Assembly and pseudocode | Assembly is complete Ghidra Listing text; pseudocode is Ghidra decompiler output. Neither is original source, and cross-provider comparison never treats Hopper and Ghidra text as equal or unequal semantic facts. |
|
|
| Instruction fast path | `read_function_instructions` returns every raw Listing instruction for the requested function without invoking the decompiler or whole-program name/string inventories. |
|
|
| Calls and references | Callers/callees contain only resolved functions. Reference edges preserve exact ReferenceManager type and call/jump/data/read/write/indirect/computed/conditional/terminal/external facts. Targetless computed flow remains unknown. Synthetic entry-point references without actionable memory sources are omitted explicitly. |
|
|
| CFG | Dossiers use `BasicBlockModel` and retain only non-call successors inside the function body. CFG topology is address-normalized for comparison; provider-specific block construction remains a declared difference. |
|
|
| Result extent | Function instruction scans and native API boundary observations do not use item-count caps or caller pagination. Responses contain the complete analysis result unless the caller cancels or the provider reports a failure. |
|
|
|
|
`npm run verify:ghidra` compiles the versioned C oracles into x86-64 debug and
|
|
stripped ELF, AArch64 ELF, x86-64 PE, and x86-64 Mach-O targets. It proves all
|
|
19 operations, external functions, resolved thunks, exports, stripped-name
|
|
behavior, direct and targetless indirect calls, typed references, strings/xrefs,
|
|
multi-block CFG, semantic enhanced workflows, cancellation, startup deadlines,
|
|
serialized concurrency, malformed-target rejection, profile identity, and
|
|
process/project cleanup against real Ghidra 12.1.4. Unit fixtures separately
|
|
cover startup deadlines, process exit, queued cancellation, and malformed wire
|
|
output.
|
|
|
|
`npm run verify:ghidra:windows` generates a deterministic source-owned native
|
|
x86-64 PE application, proves all 19 operations through the production Windows
|
|
launcher and loopback transport, checks target/snapshot/import SHA-256 linkage,
|
|
and requires project, endpoint, process, and runtime cleanup. Passing that lane
|
|
proves only the documented P0 boundary, not the remaining Windows security
|
|
gates.
|
|
|
|
## Shared provider-process foundation
|
|
|
|
`src/process/` now provides the mechanisms that a long-lived Hopper or Ghidra
|
|
adapter genuinely shares: POSIX run-token-authenticated process-group ownership,
|
|
ephemeral temporary runtime roots, one absolute startup deadline, correlated
|
|
request cancellation and lifecycle-deadline cleanup, bounded stdout and stderr retention with
|
|
exact byte counts, process-exit diagnostics, and bounded TERM-to-KILL shutdown.
|
|
Reusable fixtures exercise exit, timeout, cancellation, graceful termination,
|
|
forced termination, double-close, spawn failure, and resource release. Windows
|
|
P0 adds bounded process-tree termination as an explicitly weaker host backend;
|
|
Job Object ownership remains unimplemented.
|
|
|
|
The foundation does not define a bridge schema, socket framing, health payload,
|
|
analysis model, or shutdown acknowledgement. Hopper keeps its authenticated
|
|
NDJSON-over-Unix-socket protocol in `src/hopper/`; Ghidra has a separate strict
|
|
NDJSON protocol implemented by the packaged Java `HeadlessScript` and reuses
|
|
only the generic process mechanisms.
|
|
|
|
## Shortlist
|
|
|
|
| Provider | License / automation surface | What it brings | REA fit and blockers |
|
|
| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
| [Ghidra](https://github.com/NationalSecurityAgency/ghidra) | Apache-2.0 source license; `analyzeHeadless`, Java APIs, and PyGhidra | Broad static analysis, many processors and formats, scripting, and project/database workflows | Read-only analysis shipped: exact BYO checks, isolated state and provider lifecycle, serial API queue, packaged bridge, 19 inventory/function operations, and real ELF/PE/Mach-O x86-64 plus AArch64 conformance. GUI and mutation semantics remain intentionally unavailable. |
|
|
| [Rizin](https://github.com/rizinorg/rizin) / [rz-pipe](https://github.com/rizinorg/rz-pipe) | Rizin repository contains LGPL-3.0 and GPL-3.0 components; `rizin`, `rz-bin`, and language bridges through `rzpipe` | Portable CLI analysis, disassembly/debugging, many architectures and file formats, JSON command output | Good candidate for a process-backed Linux provider and fast metadata fallback. License/component inventory must be preserved; command output needs version-pinned parsers and semantic conformance before evidence is trusted. |
|
|
| [LIEF](https://github.com/lief-project/LIEF) | Apache-2.0; C++, Python, and other bindings | Deterministic parsing and modification of ELF, PE, Mach-O, COFF, and related executable formats; headers, sections, symbols, relocations, and functions | Best near-term complement, not a decompiler replacement. It can cover format metadata and artifact evidence without a long-lived analysis process; function semantics, pseudocode, CFG, and cross-reference parity remain out of scope unless separately demonstrated. |
|
|
| [Binary Ninja](https://docs.binary.ninja/dev/index.html) | API/documentation components are MIT, while the analysis product is licensed by edition; commercial, Ultimate, or Headless license is required for headless automation | Python/Core/C++/Rust APIs, headless loading, IL layers, function analysis, plugins, and configurable analysis | Strong technical fit for a native provider, especially function dossiers. Commercial licensing, license-secret handling, native runtime packaging, and multithreaded lifecycle rules are material deployment blockers. |
|
|
|
|
## Recommended order
|
|
|
|
1. Use the implemented explicit provider registry and target binding without
|
|
changing Hopper behavior.
|
|
2. Maintain the admitted Ghidra function boundary through the shared
|
|
conformance corpus and add formats or semantics only after real proof.
|
|
3. Connect Electron/native-add-on application findings to the selected native
|
|
analysis provider without introducing provider-prefixed tools.
|
|
4. Evaluate LIEF or Rizin later as complementary metadata/disassembly providers,
|
|
and Binary Ninja as an optional licensed provider, using the same admission
|
|
gate.
|
|
|
|
## Required admission gate
|
|
|
|
Before implementation, an adapter proposal must provide:
|
|
|
|
- a capability matrix covering supported, unsupported, and degraded results;
|
|
- provider identity, version, analysis profile, target digest, authority,
|
|
limitations, and deterministic locations in every Evidence record;
|
|
- bounded subprocess or library lifetime, cancellation, timeouts, and cleanup;
|
|
- actionable local diagnostics that retain paths, digests, mismatch locations,
|
|
and provider metadata while redacting credentials, authorization headers,
|
|
license secrets, and other genuine secrets;
|
|
- the same source-owned fixture corpus across architectures and formats;
|
|
- repeatable checks for function identity, addresses, strings, names, xrefs,
|
|
CFG, and pseudocode wherever those capabilities are claimed.
|
|
|
|
These gates turn the accepted direction into verified capabilities rather than
|
|
prematurely claiming equivalence or redistribution support.
|
|
|
|
## Primary-source notes
|
|
|
|
- Ghidra 12.1.4 documents headless batch mode and its Java runtime requirements
|
|
in its [release-specific Getting Started guide](https://github.com/NationalSecurityAgency/ghidra/blob/Ghidra_12.1.4_build/GhidraDocs/GettingStarted.md);
|
|
the [official release](https://github.com/NationalSecurityAgency/ghidra/releases/tag/Ghidra_12.1.4_build)
|
|
supplies the corresponding distribution and checksums.
|
|
- Rizin lists supported formats, architectures, tools, and `rzpipe` bridges in
|
|
its [repository README](https://github.com/rizinorg/rizin); `rz-pipe` documents
|
|
JSON command transport and pipe backends.
|
|
- LIEF's [format tutorial](https://lief.re/doc/stable/tutorials/01_play_with_formats.html)
|
|
and [Python API](https://lief.re/doc/latest/api/binary_abstraction/python.html)
|
|
document parsing and format-agnostic binary access.
|
|
- Binary Ninja documents its [headless automation model](https://docs.binary.ninja/dev/batch.html),
|
|
[edition licenses](https://docs.binary.ninja/about/license.html), and the
|
|
[MIT license for API/documentation components](https://docs.binary.ninja/about/open-source.html).
|