rea-agents 2.5.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -12
- package/bridge/ghidra/ReaGhidraBridge.java +477 -4
- package/bridge/hopper_bridge.py +54 -4
- package/dist/application/AnalysisContextQueries.js +142 -0
- package/dist/application/AnalysisSnapshotCache.js +4 -2
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +19 -11
- package/dist/application/ArtifactInventory/policy.js +17 -0
- package/dist/application/ArtifactInventory/reader.js +1 -4
- package/dist/application/ArtifactInventory/scanCanonical.js +0 -3
- package/dist/application/ArtifactInventory/scanReader.js +7 -7
- package/dist/application/ArtifactInventory/types.js +1 -5
- package/dist/application/BinarySession.js +19 -20
- package/dist/application/BinarySessionComposition.js +21 -0
- package/dist/application/BinarySessionRecords.js +106 -10
- package/dist/application/BinarySessionStatus.js +11 -5
- package/dist/application/BrowserEvidence.js +3 -5
- package/dist/application/CapabilityClientRequirements.js +26 -0
- package/dist/application/CapabilityInventory.js +140 -55
- package/dist/application/ClientRegistrationStatus.js +4 -1
- package/dist/application/CrossVersionInventory.js +7 -6
- package/dist/application/DirectAnalysis.js +6 -2
- package/dist/application/ElectronActiveEvidence.js +55 -0
- package/dist/application/ElectronActiveObservationPort.js +1 -0
- package/dist/application/ElectronActiveObservationService.js +81 -0
- package/dist/application/EnhancedTools.js +49 -17
- package/dist/application/EvidenceBundleCommands.js +5 -1
- package/dist/application/EvidenceLedger.js +53 -17
- package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
- package/dist/application/JavaScriptReplayPermission.js +1 -1
- package/dist/application/JavaScriptReplayService.js +23 -15
- package/dist/application/LazyAnalysisProvider.js +77 -0
- package/dist/application/LoopbackReplay.js +1 -1
- package/dist/application/ManagedRuntimeCorrelationService.js +9 -5
- package/dist/application/NativeApiInspection.js +80 -0
- package/dist/application/NodeRuntimeCharacterizationService.js +8 -9
- package/dist/application/ProcessCaptureLifecycle.js +5 -0
- package/dist/application/ProcessCli.js +2 -1
- package/dist/application/ProcessHarness.js +8 -3
- package/dist/application/ProcessNormalization.js +2 -1
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/ReferenceSourceImport.js +2 -2
- package/dist/application/SessionProviderRouter.js +6 -6
- package/dist/application/SetupClientConfiguration.js +10 -3
- package/dist/application/WindowsCapabilities.js +5 -4
- package/dist/application/runtime.js +26 -9
- package/dist/artifacts/ArtifactProvider.js +8 -10
- package/dist/artifacts/SafeOutputTree.js +7 -8
- package/dist/browser/CdpCaptureStorageFingerprints.js +10 -7
- package/dist/browser/CdpElectronInspection.js +3 -1
- package/dist/browser/JavaScriptRuntimeScope.js +5 -0
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +8 -2
- package/dist/browser/PlaywrightElectronActiveActions.js +324 -0
- package/dist/browser/PlaywrightElectronActiveProvider.js +294 -0
- package/dist/browser/PlaywrightScenarioBrowser.js +4 -4
- package/dist/browser/PlaywrightScenarioSession.js +4 -4
- package/dist/browser/SensitiveTextCapture.js +1 -1
- package/dist/browser/V8InspectorCaptureProjection.js +1 -0
- package/dist/browser/V8InspectorProvider.js +3 -3
- package/dist/catalogIdentity.js +15 -9
- package/dist/cli/artifactCommands.js +5 -3
- package/dist/cli/coreAnalysisCommands.js +41 -0
- package/dist/cli/managedCommands.js +1 -1
- package/dist/cliApplicationCommands.js +2 -2
- package/dist/cliBrowserAdvancedCommands.js +11 -5
- package/dist/cliBrowserCommands.js +14 -7
- package/dist/cliCommandNames.js +2 -0
- package/dist/cliElectronCommands.js +46 -4
- package/dist/cliJavaScriptRuntimeCommands.js +14 -6
- package/dist/cliObservationOptions.js +4 -0
- package/dist/cliPolicyCommands.js +1 -0
- package/dist/config/browserScenario.js +77 -0
- package/dist/config/electronAutomation.js +35 -0
- package/dist/config/environment.js +6 -0
- package/dist/config/javascriptReplay.js +41 -0
- package/dist/config/managedRuntime.js +25 -0
- package/dist/config/parseConfig.js +56 -200
- package/dist/config/parsers.js +10 -1
- package/dist/config/passiveObservation.js +157 -0
- package/dist/config/processCapture.js +47 -0
- package/dist/contracts/applicationToolContracts.js +2 -5
- package/dist/contracts/applicationWorkflowInputContracts.js +86 -96
- package/dist/contracts/artifactToolContracts.js +35 -37
- package/dist/contracts/browserToolContracts.js +2 -2
- package/dist/contracts/electronToolContracts.js +30 -1
- package/dist/contracts/enhancedInputs.js +21 -27
- package/dist/contracts/functionWorkflowToolContracts.js +29 -0
- package/dist/contracts/managedToolContracts.js +4 -9
- package/dist/contracts/managedWorkflowExamples.js +12 -9
- package/dist/contracts/managedWorkflowToolContracts.js +30 -27
- package/dist/contracts/nativeToolContracts.js +2 -3
- package/dist/contracts/promptContracts.js +14 -15
- package/dist/contracts/sessionToolSchemas.js +61 -0
- package/dist/contracts/toolContractExamples.js +4 -0
- package/dist/contracts/toolContractHelpers.js +23 -0
- package/dist/contracts/toolContracts.js +12 -67
- package/dist/contracts/toolEffects.js +19 -0
- package/dist/contracts/toolOutputSchemaGroups.js +66 -11
- package/dist/contracts/toolOutputSchemaPrimitives.js +76 -26
- package/dist/contracts/toolSchemaMetadata.js +128 -0
- package/dist/doctorRuntime.js +1 -1
- package/dist/domain/analysisErrorPresentation.js +8 -2
- package/dist/domain/analysisErrorProjection.js +1 -0
- package/dist/domain/androidApplication.js +2 -0
- package/dist/domain/appleApplication.js +2 -0
- package/dist/domain/binaryTarget.js +7 -5
- package/dist/domain/binaryTargetTypes.js +1 -0
- package/dist/domain/browserCaptureComparison.js +21 -69
- package/dist/domain/browserObservation.js +22 -9
- package/dist/domain/bytecodeProvider.js +177 -0
- package/dist/domain/callPath.js +3 -2
- package/dist/domain/conformancePackage.js +169 -0
- package/dist/domain/conformanceReplay.js +104 -0
- package/dist/domain/conformanceTrustGate.js +328 -0
- package/dist/domain/customProtocolCapture.js +137 -0
- package/dist/domain/electronActiveObservation.js +277 -0
- package/dist/domain/electronObservation.js +15 -11
- package/dist/domain/electronStaticAnalysisValues.js +4 -1
- package/dist/domain/eventProcessTree.js +217 -0
- package/dist/domain/evidenceBundle.js +47 -0
- package/dist/domain/functionComparisonNormalization.js +15 -8
- package/dist/domain/functionDossierEvidence.js +9 -4
- package/dist/domain/hopperStartupFailure.js +1 -1
- package/dist/domain/hopperValues.js +68 -68
- package/dist/domain/inputIssueProjection.js +11 -2
- package/dist/domain/investigationWorkspace.js +16 -15
- package/dist/domain/javascriptExportShapeComparisonSchemas.js +2 -2
- package/dist/domain/javascriptReplay.js +79 -63
- package/dist/domain/javascriptRuntimeObservation.js +1 -0
- package/dist/domain/javascriptRuntimeReconciliationActive.js +89 -0
- package/dist/domain/javascriptRuntimeReconciliationGraph.js +11 -10
- package/dist/domain/javascriptRuntimeReconciliationMatching.js +57 -49
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +5 -1
- package/dist/domain/javascriptRuntimeReconciliationResult.js +21 -14
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
- package/dist/domain/javascriptSemanticAnalysis.js +8 -12
- package/dist/domain/javascriptSourceParser.js +15 -0
- package/dist/domain/javascriptStaticAnalysis.js +8 -12
- package/dist/domain/knownPageCoverage.js +15 -7
- package/dist/domain/mobileApplicationInvestigation.js +159 -0
- package/dist/domain/nativeApiBoundary.js +138 -0
- package/dist/domain/nodeRuntimeCharacterization.js +14 -13
- package/dist/domain/objcSwiftMetadata.js +150 -0
- package/dist/domain/packageAnalysis.js +123 -0
- package/dist/domain/peInspection.js +196 -0
- package/dist/domain/processCaptureReactiveValidation.js +8 -3
- package/dist/domain/processCaptureValidation.js +7 -3
- package/dist/domain/processComparison.js +12 -7
- package/dist/domain/processObservation.js +5 -6
- package/dist/domain/processReactiveMatching.js +14 -7
- package/dist/domain/processReactiveTransition.js +9 -3
- package/dist/domain/processScenario.js +6 -3
- package/dist/domain/processTraceComparison.js +3 -2
- package/dist/domain/protocolCapture.js +245 -0
- package/dist/domain/reconstructionReadinessSchemas.js +30 -2
- package/dist/domain/referenceSourceImportParsing.js +2 -2
- package/dist/domain/replayMachineRuntime.js +5 -3
- package/dist/domain/sourceToBundleComparison.js +2 -2
- package/dist/domain/sourceToBundleComparisonSchemas.js +8 -8
- package/dist/domain/sourceToBundleSignals.js +7 -1
- package/dist/domain/staticRuntimeCorrelation.js +7 -5
- package/dist/domain/webBundleAnalysis.js +14 -14
- package/dist/domain/webCaptureDiff.js +15 -12
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +2 -3
- package/dist/dotnet/ManagedStaticProvider.js +34 -5
- package/dist/generatedMcpToolCatalog.js +37 -0
- package/dist/generatedPackageMetadata.js +4 -4
- package/dist/ghidra/GhidraClient.js +1 -1
- package/dist/ghidra/GhidraClientStartup.js +1 -1
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +44 -16
- package/dist/ghidra/GhidraInventoryValues.js +57 -47
- package/dist/ghidra/GhidraProvider.js +18 -10
- package/dist/ghidra/GhidraProviderCapabilities.js +45 -39
- package/dist/ghidra/GhidraSocketConnection.js +1 -1
- package/dist/ghidra/protocol.js +19 -42
- package/dist/hopper/BridgeLauncher.js +3 -1
- package/dist/hopper/HopperAnalysisProfile.js +2 -4
- package/dist/hopper/HopperCleanup.js +5 -6
- package/dist/hopper/HopperClient.js +27 -3
- package/dist/hopper/HopperProvider.js +42 -44
- package/dist/hopper/HopperRequestQueue.js +32 -4
- package/dist/hopper/HopperResponseStream.js +2 -2
- package/dist/hopper/LinuxPrivateDisplayProbe.js +16 -10
- package/dist/hopper/protocol.js +63 -21
- package/dist/main/reload.js +1 -6
- package/dist/main/state.js +0 -14
- package/dist/main/transport.js +31 -41
- package/dist/main.js +17 -13
- package/dist/mcpDoctor.js +4 -26
- package/dist/mcpStartupPolicy.js +14 -0
- package/dist/process/PrivateRuntimeRoot.js +17 -0
- package/dist/process/ProcessOwnership.js +64 -128
- package/dist/process/ProcessOwnershipIdentity.js +13 -0
- package/dist/process/ProcessOwnershipObservation.js +88 -0
- package/dist/process/ProcessOwnershipProcessTree.js +22 -0
- package/dist/process/ProviderDeadline.js +8 -12
- package/dist/process/WindowsAuthority.js +32 -0
- package/dist/reference/ReferenceSourceReader.js +3 -3
- package/dist/reference/ReferenceSourceReaderEntries.js +2 -2
- package/dist/replay/JavaScriptReplayWorker.js +95 -11
- package/dist/replay/JavaScriptReplayWorkerTypes.js +1 -0
- package/dist/replay/LinuxJavaScriptReplayRunner.js +10 -15
- package/dist/replay/ReplayOutcome.js +26 -15
- package/dist/replay/ReplayWorkerProtocol.js +34 -29
- package/dist/server/createServer.js +101 -144
- package/dist/server/mcpClientMetadata.js +32 -0
- package/dist/server/registerApplicationTools/characterization.js +6 -13
- package/dist/server/registerApplicationTools/compareExportShapes.js +2 -7
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +2 -7
- package/dist/server/registerApplicationTools/compareVersions.js +3 -8
- package/dist/server/registerApplicationTools/controlledReplay.js +6 -11
- package/dist/server/registerApplicationTools/coverage.js +13 -19
- package/dist/server/registerApplicationTools/obligationLedger.js +1 -6
- package/dist/server/registerApplicationTools/readiness.js +1 -6
- package/dist/server/registerApplicationTools/traceFeature.js +3 -8
- package/dist/server/registerApplicationTools/traceSemantics.js +1 -6
- package/dist/server/registerArtifactComparisonTool.js +12 -16
- package/dist/server/registerBrowserScenarioTool.js +1 -6
- package/dist/server/registerBrowserTools.js +37 -79
- package/dist/server/registerBundleComparisonTool.js +10 -14
- package/dist/server/registerCloseLifecycleTool.js +6 -14
- package/dist/server/registerElectronTools.js +30 -47
- package/dist/server/registerEnhancedTools.js +124 -122
- package/dist/server/registerEvidenceResources.js +50 -101
- package/dist/server/registerEvidenceTools.js +5 -9
- package/dist/server/registerFunctionComparisonTool.js +11 -15
- package/dist/server/registerInvestigationTools.js +42 -59
- package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +13 -27
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +4 -9
- package/dist/server/registerManagedWorkflowTools/contract.js +2 -3
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +3 -8
- package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +3 -8
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +8 -13
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +4 -9
- package/dist/server/registerOfficialTools.js +8 -9
- package/dist/server/registerProcessComparisonTool.js +19 -24
- package/dist/server/registerReconstructionObligationLedgerResource.js +0 -7
- package/dist/server/registerReconstructionReadinessResource.js +0 -7
- package/dist/server/registerReplayMachineTool.js +4 -10
- package/dist/server/registerSessionRecordTools.js +146 -47
- package/dist/server/registerSessionStatusTool.js +14 -29
- package/dist/server/registerSessionTools.js +45 -41
- package/dist/server/sessionAvailabilityPolicy.js +2 -1
- package/dist/server/sessionToolPolicies.js +1 -5
- package/dist/server/toolRegistrationOptions.js +4 -177
- package/dist/server/toolResult.js +14 -18
- package/package.json +35 -16
- package/scripts/electron-active-hook-boundaries.cjs +394 -0
- package/scripts/electron-active-hook.cjs +501 -0
- package/skills/reverse-engineer-anything/SKILL.md +10 -3
- package/dist/server/DynamicToolAvailability.js +0 -70
- package/dist/server/toolInputValidation.js +0 -77
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# REA: Reverse Engineer Anything
|
|
6
6
|
|
|
7
|
-
###
|
|
7
|
+
### Reverse engineer anything with agents, from app behavior down to native binaries.
|
|
8
8
|
|
|
9
9
|
**See a feature you like. Understand how it works, down to the binary level.**
|
|
10
10
|
|
|
@@ -89,13 +89,13 @@ REA shows how it reached its conclusions. It does not claim to recover original
|
|
|
89
89
|
### Run setup — recommended
|
|
90
90
|
|
|
91
91
|
```bash
|
|
92
|
-
npx rea-agents setup
|
|
92
|
+
npx --yes rea-agents@latest setup
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
The npm package-runner prompt, when shown, approves downloading REA for this
|
|
96
96
|
invocation; it does not approve any setup changes. The REA wizard separately
|
|
97
97
|
shows its complete plan and asks before applying it. Setup does not update
|
|
98
|
-
Homebrew, Node.js, or npm.
|
|
98
|
+
Homebrew, Node.js, or npm. The setup command opens with the work it
|
|
99
99
|
enables: investigate local apps from an agent, recover evidence through a
|
|
100
100
|
deep-analysis provider, and reuse REA's guided workflow. It summarizes the
|
|
101
101
|
detected agents, then asks which capabilities to set up: agent integration
|
|
@@ -103,6 +103,11 @@ detected agents, then asks which capabilities to set up: agent integration
|
|
|
103
103
|
Nothing is preselected. Choosing agent integration opens a second empty
|
|
104
104
|
checklist for the specific detected agents that should receive a registration.
|
|
105
105
|
|
|
106
|
+
`@latest` makes the requested release explicit and asks npm for the release
|
|
107
|
+
currently published under that tag. REA does not silently replace the package
|
|
108
|
+
version npm selected. Intentional rollbacks therefore remain available through
|
|
109
|
+
an exact package request.
|
|
110
|
+
|
|
106
111
|
REA keeps the journey inline so its history remains in the terminal. Selecting
|
|
107
112
|
a capability does not select every detected target or authorize a change.
|
|
108
113
|
Before anything changes, REA validates existing configuration, prints exact
|
|
@@ -132,7 +137,7 @@ Pass installer options after `bash -s --`, for example `--dry-run`, `--no-setup`
|
|
|
132
137
|
### With an agent — recommended
|
|
133
138
|
|
|
134
139
|
```bash
|
|
135
|
-
npx rea-agents setup
|
|
140
|
+
npx --yes rea-agents@latest setup
|
|
136
141
|
```
|
|
137
142
|
|
|
138
143
|
Choose Agent Integration in the reviewed setup plan. REA installs the pinned MCP
|
|
@@ -144,7 +149,7 @@ Review the setup plan, approve it if appropriate, then describe the app or featu
|
|
|
144
149
|
### From Terminal — no installation
|
|
145
150
|
|
|
146
151
|
```bash
|
|
147
|
-
npx rea-agents setup
|
|
152
|
+
npx --yes rea-agents@latest setup
|
|
148
153
|
npx -y rea-agents@latest doctor
|
|
149
154
|
npx -y rea-agents@latest analyze /Applications/Notes.app
|
|
150
155
|
```
|
|
@@ -374,15 +379,15 @@ REA handles the app analysis in steps 1–5. The agent performs step 6 with its
|
|
|
374
379
|
| Tool family | Count | Examples |
|
|
375
380
|
| ------------------------- | ----: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
376
381
|
| Native inspection | 36 | procedures, pseudocode, assembly, strings, names, segments, callers, callees, xrefs, annotations, bounded byte reads, file-offset translation |
|
|
377
|
-
| Investigation workflows |
|
|
382
|
+
| Investigation workflows | 13 | `binary_overview`, `analyze_function`, `inspect_native_api`, `batch_decompile`, `trace_feature`, exact string-to-code lookup, bounded call paths, call graphs, Swift and Objective-C discovery |
|
|
378
383
|
| Native macOS utilities | 5 | Mach-O metadata, code signatures, plists, architectures, Swift demangling; Hopper-free and provenance-bearing |
|
|
379
384
|
| Artifact graph | 3 | bounded provider-neutral inspection, deterministic directory/ZIP/APK/IPA/MSIX/AppX/ASAR inventory, and explicitly selected extraction into an absent owned tree |
|
|
380
385
|
| Managed PE/CLI | 8 | PE/CLI identity, metadata members, CIL hashes, P/Invoke/native-boundary declarations and verification, application-graph projection, decompiler reconstruction import, token remapping, runtime-correlation plans, and version comparison |
|
|
381
386
|
| Browser observation | 9 | exact-origin passive CDP capture, bundle and source-map analysis, WebMCP discovery, session timelines, capture diff, visual evidence, and bounded Playwright scenarios |
|
|
382
|
-
| Electron analysis |
|
|
387
|
+
| Electron analysis | 5 | passive root-confined observation, bounded static application mapping, evidence-backed static/runtime reconciliation, and separately approved provider-owned click/wait scenarios |
|
|
383
388
|
| JavaScript runtime | 2 | approved attach-only Node/Electron Inspector target discovery plus bounded script and execution-context observation without evaluation or instrumentation |
|
|
384
389
|
| Application workflows | 12 | bounded cross-layer traces, unique-only version matching, historical-source to bundle mapping, static export return-shape comparison, approved Linux-isolated extracted-module replay, managed-runtime characterization, reconstruction coverage closure, deterministic obligation ledgers, and end-to-end readiness conformance |
|
|
385
|
-
| Workspace and observation |
|
|
390
|
+
| Workspace and observation | 23 | target lifecycle, Evidence v2 bundle snapshots, retained-bundle release, aggregate navigation/address context, direct finite replay-machine evaluation, process/artifact/function comparison, evidence-linked residual-unknown lifecycle |
|
|
386
391
|
|
|
387
392
|
The public interface describes what the agent is trying to learn. Providers decide how to answer. macOS utilities handle common semantic inspection without launching Hopper; Hopper handles deeper native analysis; the process harness implements controlled behavioral capture.
|
|
388
393
|
|
|
@@ -502,14 +507,14 @@ REA is growing into a toolkit for understanding software across static artifacts
|
|
|
502
507
|
|
|
503
508
|
### Next
|
|
504
509
|
|
|
505
|
-
1. **Controlled replay conformance growth** — extend the shipped Linux extracted-module sandbox with more source-owned hostile fixtures and cross-kernel conformance; browser
|
|
510
|
+
1. **Controlled replay conformance growth** — extend the shipped Linux extracted-module sandbox with more source-owned hostile fixtures and cross-kernel conformance; browser and Electron scenarios remain separately permissioned authorities.
|
|
506
511
|
2. **Broader application graph evidence** — extend authenticated cross-layer traces with additional static extractors and separately approved runtime authorities.
|
|
507
512
|
3. **Professional managed-code analysis** — extend shipped PE/CLI triage, CIL evidence, managed/native declaration inventory, source-owned conformance, and obfuscation-resistant comparisons toward verified native-provider composition under the accepted [managed-code boundary](docs/managed-code-analysis.md).
|
|
508
513
|
4. **Deterministic behavior harnesses** — extend process ownership, protocol fixtures, filesystem observation, reconnects, and cross-version behavioral comparison.
|
|
509
514
|
|
|
510
515
|
### Later
|
|
511
516
|
|
|
512
|
-
1. **
|
|
517
|
+
1. **Broader controlled application interaction** — extend the separately authorized browser and Electron scenario surfaces beyond the current bounded click/wait actions without widening passive observation or extracted-module replay authority.
|
|
513
518
|
2. **Native runtime observation** — approval-gated LLDB, Frida, system logs, process/filesystem observers, and native API tracing.
|
|
514
519
|
3. **Additional providers and targets** — evaluate IDA/Hex-Rays, Binary Ninja, Rizin, LIEF, Windows-native providers, mobile artifacts, firmware, document formats, and other software-defined systems.
|
|
515
520
|
|
|
@@ -530,7 +535,7 @@ Setup detects Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf,
|
|
|
530
535
|
"mcpServers": {
|
|
531
536
|
"rea": {
|
|
532
537
|
"command": "npx",
|
|
533
|
-
"args": ["-y", "rea-agents@
|
|
538
|
+
"args": ["-y", "rea-agents@3.1.0", "mcp"]
|
|
534
539
|
}
|
|
535
540
|
}
|
|
536
541
|
}
|
|
@@ -787,7 +792,10 @@ Any agent that can run a local MCP server can use the manual configuration. Setu
|
|
|
787
792
|
|
|
788
793
|
## Development
|
|
789
794
|
|
|
790
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, architecture,
|
|
795
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, architecture, and release
|
|
796
|
+
instructions, and [docs/testing.md](docs/testing.md) for the behavioral test
|
|
797
|
+
depths, focused developer commands, coverage floors, and CI evidence. PR CI
|
|
798
|
+
publishes generated API documentation as the `api-docs` workflow artifact.
|
|
791
799
|
|
|
792
800
|
`npm run verify:agent` runs brandless native, JavaScript-application, managed,
|
|
793
801
|
and browser prompts through a real local Codex CLI. Its JSON report measures
|
|
@@ -55,6 +55,7 @@ import ghidra.program.model.block.CodeBlockIterator;
|
|
|
55
55
|
import ghidra.program.model.block.CodeBlockReference;
|
|
56
56
|
import ghidra.program.model.block.CodeBlockReferenceIterator;
|
|
57
57
|
import ghidra.program.model.data.StringDataInstance;
|
|
58
|
+
import ghidra.program.model.data.DataType;
|
|
58
59
|
import ghidra.program.model.listing.CommentType;
|
|
59
60
|
import ghidra.program.model.listing.Data;
|
|
60
61
|
import ghidra.program.model.listing.DataIterator;
|
|
@@ -70,15 +71,25 @@ import ghidra.program.model.symbol.ReferenceIterator;
|
|
|
70
71
|
import ghidra.program.model.symbol.SourceType;
|
|
71
72
|
import ghidra.program.model.symbol.Symbol;
|
|
72
73
|
import ghidra.program.model.symbol.SymbolIterator;
|
|
74
|
+
import ghidra.program.model.pcode.FunctionPrototype;
|
|
75
|
+
import ghidra.program.model.pcode.HighFunction;
|
|
76
|
+
import ghidra.program.model.pcode.HighSymbol;
|
|
77
|
+
import ghidra.program.model.pcode.JumpTable;
|
|
73
78
|
|
|
74
79
|
public final class ReaGhidraBridge extends HeadlessScript {
|
|
75
|
-
private static final int BRIDGE_VERSION =
|
|
80
|
+
private static final int BRIDGE_VERSION = 6;
|
|
76
81
|
private static final int MAX_DESCRIPTOR_BYTES = 16 * 1024;
|
|
77
82
|
private static final int MAX_REQUEST_CHARACTERS = 256 * 1024;
|
|
78
83
|
private static final int MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
84
|
+
private static final int RESPONSE_ENVELOPE_RESERVE_BYTES = 32 * 1024;
|
|
85
|
+
private static final int MAX_NATIVE_API_RESPONSE_BYTES = 192 * 1024;
|
|
79
86
|
private static final int MAX_INVENTORY_ITEMS = 1_000_000;
|
|
80
87
|
private static final int MAX_FUNCTION_ITEMS = 100_000;
|
|
81
88
|
private static final int MAX_FUNCTION_INSTRUCTIONS = 100_000;
|
|
89
|
+
private static final int MAX_NATIVE_API_PARAMETERS = 64;
|
|
90
|
+
private static final int MAX_NATIVE_API_JUMP_TABLES = 4;
|
|
91
|
+
private static final int MAX_NATIVE_API_LOAD_TABLES = 4;
|
|
92
|
+
private static final int MAX_NATIVE_API_MAPPINGS = 32;
|
|
82
93
|
private static final int DECOMPILE_TIMEOUT_SECONDS = 30;
|
|
83
94
|
private static final int DECOMPILE_PAYLOAD_MBYTES = 8;
|
|
84
95
|
private static final int MAX_LIST_VALUE_CODE_POINTS = 1_024;
|
|
@@ -377,7 +388,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
377
388
|
private JsonElement procedurePseudocode(JsonObject params) throws Exception {
|
|
378
389
|
requireKeys(params, Set.of("document", "procedure"));
|
|
379
390
|
requireDocument(params);
|
|
380
|
-
String value =
|
|
391
|
+
String value = decompiledText(
|
|
392
|
+
decompile(resolveProcedure(requireString(params, "procedure")))
|
|
393
|
+
);
|
|
381
394
|
return value == null ? JsonNull.INSTANCE : GSON.toJsonTree(value);
|
|
382
395
|
}
|
|
383
396
|
|
|
@@ -576,7 +589,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
576
589
|
);
|
|
577
590
|
JsonObject offsets = requireCollectionOffsets(params);
|
|
578
591
|
InstructionScan scan = scanInstructions(function, maximumInstructions);
|
|
579
|
-
|
|
592
|
+
DecompileResults decompilation = decompile(function);
|
|
593
|
+
String pseudocode = decompiledText(decompilation);
|
|
580
594
|
if (pseudocode == null) {
|
|
581
595
|
pseudocode = "";
|
|
582
596
|
}
|
|
@@ -662,6 +676,19 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
662
676
|
instructionScan.addProperty("scanned", scan.instructions.size());
|
|
663
677
|
instructionScan.addProperty("truncated", scan.truncated);
|
|
664
678
|
result.add("instruction_scan", instructionScan);
|
|
679
|
+
int nativeApiBudget = Math.min(
|
|
680
|
+
MAX_NATIVE_API_RESPONSE_BYTES,
|
|
681
|
+
Math.max(
|
|
682
|
+
0,
|
|
683
|
+
MAX_RESPONSE_BYTES -
|
|
684
|
+
encodedUtf8Length(result) -
|
|
685
|
+
RESPONSE_ENVELOPE_RESERVE_BYTES
|
|
686
|
+
)
|
|
687
|
+
);
|
|
688
|
+
result.add(
|
|
689
|
+
"native_api",
|
|
690
|
+
nativeApiBoundary(function, decompilation, nativeApiBudget)
|
|
691
|
+
);
|
|
665
692
|
JsonArray limitations = new JsonArray();
|
|
666
693
|
limitations.add(
|
|
667
694
|
"Unresolved computed or indirect flows without target addresses are not represented as reference edges."
|
|
@@ -971,7 +998,446 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
971
998
|
return matches.get(0);
|
|
972
999
|
}
|
|
973
1000
|
|
|
974
|
-
private
|
|
1001
|
+
private JsonObject nativeApiBoundary(
|
|
1002
|
+
Function function,
|
|
1003
|
+
DecompileResults decompilation,
|
|
1004
|
+
int maximumBytes
|
|
1005
|
+
) throws Exception {
|
|
1006
|
+
if (decompilation == null || decompilation.getHighFunction() == null) {
|
|
1007
|
+
return unavailableNativeApiBoundary(
|
|
1008
|
+
"Ghidra has no HighFunction model for an external or bodyless procedure"
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
HighFunction highFunction = decompilation.getHighFunction();
|
|
1013
|
+
FunctionPrototype prototype = highFunction.getFunctionPrototype();
|
|
1014
|
+
String signatureSource = function
|
|
1015
|
+
.getSignatureSource()
|
|
1016
|
+
.name()
|
|
1017
|
+
.toLowerCase(Locale.ROOT);
|
|
1018
|
+
JsonObject result = new JsonObject();
|
|
1019
|
+
result.addProperty("available", true);
|
|
1020
|
+
result.addProperty("provenance", "ghidra-high-function");
|
|
1021
|
+
result.addProperty("signature_source", signatureSource);
|
|
1022
|
+
String callingConvention = prototype.getModelName();
|
|
1023
|
+
result.addProperty(
|
|
1024
|
+
"calling_convention",
|
|
1025
|
+
callingConvention == null || callingConvention.isBlank()
|
|
1026
|
+
? "unknown"
|
|
1027
|
+
: callingConvention
|
|
1028
|
+
);
|
|
1029
|
+
result.add(
|
|
1030
|
+
"return_type",
|
|
1031
|
+
inferredBoundaryType(
|
|
1032
|
+
"return",
|
|
1033
|
+
null,
|
|
1034
|
+
null,
|
|
1035
|
+
prototype.getReturnType(),
|
|
1036
|
+
prototype.getReturnStorage().toString(),
|
|
1037
|
+
false,
|
|
1038
|
+
signatureSource
|
|
1039
|
+
)
|
|
1040
|
+
);
|
|
1041
|
+
|
|
1042
|
+
JsonArray parameters = new JsonArray();
|
|
1043
|
+
result.add("parameters", parameters);
|
|
1044
|
+
result.addProperty("parameters_truncated", false);
|
|
1045
|
+
|
|
1046
|
+
JsonArray jumpTables = new JsonArray();
|
|
1047
|
+
result.add("jump_tables", jumpTables);
|
|
1048
|
+
result.addProperty("jump_tables_truncated", false);
|
|
1049
|
+
|
|
1050
|
+
JsonObject pseudocode = new JsonObject();
|
|
1051
|
+
pseudocode.addProperty("classification", "decompiler-generated-non-source");
|
|
1052
|
+
pseudocode.addProperty("compilable", false);
|
|
1053
|
+
result.add("pseudocode", pseudocode);
|
|
1054
|
+
|
|
1055
|
+
JsonArray artifacts = new JsonArray();
|
|
1056
|
+
artifacts.add("recovered-signature");
|
|
1057
|
+
artifacts.add("register-and-stack-variables");
|
|
1058
|
+
artifacts.add("compiler-generated-control-flow");
|
|
1059
|
+
artifacts.add("pointer-arithmetic");
|
|
1060
|
+
artifacts.add("pseudocode");
|
|
1061
|
+
result.add("decompiler_artifacts", artifacts);
|
|
1062
|
+
|
|
1063
|
+
JsonArray limitations = new JsonArray();
|
|
1064
|
+
limitations.add(
|
|
1065
|
+
"Types and calling conventions are Ghidra decompiler observations, not source declarations."
|
|
1066
|
+
);
|
|
1067
|
+
limitations.add(
|
|
1068
|
+
"Register variables, compiler-generated blocks, and pointer arithmetic remain decompiler artifacts."
|
|
1069
|
+
);
|
|
1070
|
+
limitations.add(
|
|
1071
|
+
"Pseudocode is neither original source nor guaranteed to compile."
|
|
1072
|
+
);
|
|
1073
|
+
result.add("limitations", limitations);
|
|
1074
|
+
if (encodedUtf8Length(result) > maximumBytes) {
|
|
1075
|
+
return unavailableNativeApiBoundary(
|
|
1076
|
+
"The remaining Ghidra response budget cannot contain a structured native API boundary"
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
int parameterCount = Math.min(
|
|
1081
|
+
prototype.getNumParams(),
|
|
1082
|
+
MAX_NATIVE_API_PARAMETERS
|
|
1083
|
+
);
|
|
1084
|
+
for (int index = 0; index < parameterCount; index += 1) {
|
|
1085
|
+
HighSymbol parameter = prototype.getParam(index);
|
|
1086
|
+
if (parameter == null) {
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
parameters.add(
|
|
1090
|
+
inferredBoundaryType(
|
|
1091
|
+
"parameter",
|
|
1092
|
+
index,
|
|
1093
|
+
parameter.getName(),
|
|
1094
|
+
parameter.getDataType(),
|
|
1095
|
+
parameter.getStorage().toString(),
|
|
1096
|
+
parameter.isTypeLocked(),
|
|
1097
|
+
signatureSource
|
|
1098
|
+
)
|
|
1099
|
+
);
|
|
1100
|
+
if (encodedUtf8Length(result) > maximumBytes) {
|
|
1101
|
+
parameters.remove(parameters.size() - 1);
|
|
1102
|
+
break;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
result.addProperty(
|
|
1106
|
+
"parameters_truncated",
|
|
1107
|
+
prototype.getNumParams() > parameters.size()
|
|
1108
|
+
);
|
|
1109
|
+
|
|
1110
|
+
JumpTable[] recoveredJumpTables = highFunction.getJumpTables();
|
|
1111
|
+
int jumpTableCount = Math.min(
|
|
1112
|
+
recoveredJumpTables.length,
|
|
1113
|
+
MAX_NATIVE_API_JUMP_TABLES
|
|
1114
|
+
);
|
|
1115
|
+
for (int index = 0; index < jumpTableCount; index += 1) {
|
|
1116
|
+
jumpTables.add(inferredJumpTable(recoveredJumpTables[index]));
|
|
1117
|
+
if (encodedUtf8Length(result) > maximumBytes) {
|
|
1118
|
+
jumpTables.remove(jumpTables.size() - 1);
|
|
1119
|
+
break;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
result.addProperty(
|
|
1123
|
+
"jump_tables_truncated",
|
|
1124
|
+
recoveredJumpTables.length > jumpTables.size()
|
|
1125
|
+
);
|
|
1126
|
+
return result;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
private static JsonObject unavailableNativeApiBoundary(String reason) {
|
|
1130
|
+
JsonObject unavailable = new JsonObject();
|
|
1131
|
+
unavailable.addProperty("available", false);
|
|
1132
|
+
unavailable.addProperty("reason", reason);
|
|
1133
|
+
JsonArray unknowns = new JsonArray();
|
|
1134
|
+
unknowns.add("Return and parameter boundary types are unavailable.");
|
|
1135
|
+
unknowns.add("Jump-table mappings are unavailable.");
|
|
1136
|
+
unavailable.add("residual_unknowns", unknowns);
|
|
1137
|
+
return unavailable;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
private static int encodedUtf8Length(JsonElement value) {
|
|
1141
|
+
return GSON.toJson(value).getBytes(StandardCharsets.UTF_8).length;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
private static JsonObject inferredBoundaryType(
|
|
1145
|
+
String role,
|
|
1146
|
+
Integer ordinal,
|
|
1147
|
+
String name,
|
|
1148
|
+
DataType dataType,
|
|
1149
|
+
String storage,
|
|
1150
|
+
boolean typeLocked,
|
|
1151
|
+
String signatureSource
|
|
1152
|
+
) {
|
|
1153
|
+
JsonObject result = new JsonObject();
|
|
1154
|
+
result.addProperty("role", role);
|
|
1155
|
+
if (ordinal == null) {
|
|
1156
|
+
result.add("ordinal", JsonNull.INSTANCE);
|
|
1157
|
+
}
|
|
1158
|
+
else {
|
|
1159
|
+
result.addProperty("ordinal", ordinal);
|
|
1160
|
+
}
|
|
1161
|
+
if (name == null || name.isBlank()) {
|
|
1162
|
+
result.add("name", JsonNull.INSTANCE);
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
result.addProperty("name", name);
|
|
1166
|
+
}
|
|
1167
|
+
result.addProperty("data_type", dataType.getDisplayName());
|
|
1168
|
+
int dataTypeLength = dataType.getLength();
|
|
1169
|
+
if (dataTypeLength < 0) {
|
|
1170
|
+
result.add("size_bytes", JsonNull.INSTANCE);
|
|
1171
|
+
}
|
|
1172
|
+
else {
|
|
1173
|
+
result.addProperty("size_bytes", dataTypeLength);
|
|
1174
|
+
}
|
|
1175
|
+
if (storage.isBlank()) {
|
|
1176
|
+
result.add("storage", JsonNull.INSTANCE);
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
result.addProperty("storage", storage);
|
|
1180
|
+
}
|
|
1181
|
+
result.addProperty(
|
|
1182
|
+
"confidence",
|
|
1183
|
+
inferenceConfidence(signatureSource, typeLocked)
|
|
1184
|
+
);
|
|
1185
|
+
|
|
1186
|
+
JsonArray evidence = new JsonArray();
|
|
1187
|
+
evidence.add(
|
|
1188
|
+
inferenceEvidence(
|
|
1189
|
+
"signature-source",
|
|
1190
|
+
"ghidra-function-manager",
|
|
1191
|
+
"Function signature source is " + signatureSource + "."
|
|
1192
|
+
)
|
|
1193
|
+
);
|
|
1194
|
+
evidence.add(
|
|
1195
|
+
inferenceEvidence(
|
|
1196
|
+
"decompiler-type",
|
|
1197
|
+
"ghidra-high-function",
|
|
1198
|
+
"HighFunction recovered data type " + dataType.getDisplayName() + "."
|
|
1199
|
+
)
|
|
1200
|
+
);
|
|
1201
|
+
if (typeLocked) {
|
|
1202
|
+
evidence.add(
|
|
1203
|
+
inferenceEvidence(
|
|
1204
|
+
"type-lock",
|
|
1205
|
+
"ghidra-high-symbol",
|
|
1206
|
+
"The decompiler marks this parameter type as locked."
|
|
1207
|
+
)
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
result.add("evidence", evidence);
|
|
1211
|
+
|
|
1212
|
+
JsonArray artifacts = new JsonArray();
|
|
1213
|
+
artifacts.add("recovered-signature");
|
|
1214
|
+
if (!storage.isBlank()) {
|
|
1215
|
+
artifacts.add("register-or-stack-storage");
|
|
1216
|
+
}
|
|
1217
|
+
if (name != null && (name.startsWith("param_") || name.startsWith("unaff_"))) {
|
|
1218
|
+
artifacts.add("compiler-generated-variable");
|
|
1219
|
+
}
|
|
1220
|
+
result.add("decompiler_artifacts", artifacts);
|
|
1221
|
+
return result;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
private JsonObject inferredJumpTable(JumpTable jumpTable) throws Exception {
|
|
1225
|
+
JsonObject result = new JsonObject();
|
|
1226
|
+
result.addProperty(
|
|
1227
|
+
"dispatch_address",
|
|
1228
|
+
canonicalAddress(jumpTable.getSwitchAddress())
|
|
1229
|
+
);
|
|
1230
|
+
JumpTable.LoadTable[] loadTables = jumpTable.getLoadTables();
|
|
1231
|
+
List<Address> referencedDataAddresses = loadTables.length == 0
|
|
1232
|
+
? dispatchPathDataReferences(jumpTable)
|
|
1233
|
+
: List.of();
|
|
1234
|
+
JsonArray dataSources = new JsonArray();
|
|
1235
|
+
int loadTableCount = Math.min(
|
|
1236
|
+
loadTables.length,
|
|
1237
|
+
MAX_NATIVE_API_LOAD_TABLES
|
|
1238
|
+
);
|
|
1239
|
+
for (int index = 0; index < loadTableCount; index += 1) {
|
|
1240
|
+
JumpTable.LoadTable loadTable = loadTables[index];
|
|
1241
|
+
JsonObject source = new JsonObject();
|
|
1242
|
+
source.addProperty("address", canonicalAddress(loadTable.getAddress()));
|
|
1243
|
+
source.addProperty("provenance", "ghidra-decompiler-load-table");
|
|
1244
|
+
source.addProperty("entry_size_bytes", loadTable.getSize());
|
|
1245
|
+
source.addProperty("entry_count", loadTable.getNum());
|
|
1246
|
+
source.addProperty("confidence", "high");
|
|
1247
|
+
JsonArray evidence = new JsonArray();
|
|
1248
|
+
evidence.add(
|
|
1249
|
+
inferenceEvidence(
|
|
1250
|
+
"jump-table",
|
|
1251
|
+
"ghidra-high-function",
|
|
1252
|
+
"Decompiler load table starts at " +
|
|
1253
|
+
canonicalAddress(loadTable.getAddress()) +
|
|
1254
|
+
"."
|
|
1255
|
+
)
|
|
1256
|
+
);
|
|
1257
|
+
source.add("evidence", evidence);
|
|
1258
|
+
dataSources.add(source);
|
|
1259
|
+
}
|
|
1260
|
+
int referencedDataCount = Math.min(
|
|
1261
|
+
referencedDataAddresses.size(),
|
|
1262
|
+
MAX_NATIVE_API_LOAD_TABLES - loadTableCount
|
|
1263
|
+
);
|
|
1264
|
+
for (int index = 0; index < referencedDataCount; index += 1) {
|
|
1265
|
+
Address address = referencedDataAddresses.get(index);
|
|
1266
|
+
JsonObject source = new JsonObject();
|
|
1267
|
+
source.addProperty("address", canonicalAddress(address));
|
|
1268
|
+
source.addProperty(
|
|
1269
|
+
"provenance",
|
|
1270
|
+
"ghidra-dispatch-block-data-reference"
|
|
1271
|
+
);
|
|
1272
|
+
source.add("entry_size_bytes", JsonNull.INSTANCE);
|
|
1273
|
+
source.add("entry_count", JsonNull.INSTANCE);
|
|
1274
|
+
source.addProperty("confidence", "medium");
|
|
1275
|
+
JsonArray evidence = new JsonArray();
|
|
1276
|
+
evidence.add(
|
|
1277
|
+
inferenceEvidence(
|
|
1278
|
+
"jump-table",
|
|
1279
|
+
"ghidra-reference-manager",
|
|
1280
|
+
"The dispatch block or an immediate predecessor contains a typed data reference to " +
|
|
1281
|
+
canonicalAddress(address) +
|
|
1282
|
+
"."
|
|
1283
|
+
)
|
|
1284
|
+
);
|
|
1285
|
+
source.add("evidence", evidence);
|
|
1286
|
+
dataSources.add(source);
|
|
1287
|
+
}
|
|
1288
|
+
result.add("data_sources", dataSources);
|
|
1289
|
+
result.addProperty(
|
|
1290
|
+
"data_sources_truncated",
|
|
1291
|
+
loadTables.length > loadTableCount ||
|
|
1292
|
+
referencedDataAddresses.size() > referencedDataCount
|
|
1293
|
+
);
|
|
1294
|
+
|
|
1295
|
+
Address[] targets = jumpTable.getCases();
|
|
1296
|
+
Integer[] labels = jumpTable.getLabelValues();
|
|
1297
|
+
JsonArray mappings = new JsonArray();
|
|
1298
|
+
int mappingCount = Math.min(targets.length, MAX_NATIVE_API_MAPPINGS);
|
|
1299
|
+
for (int index = 0; index < mappingCount; index += 1) {
|
|
1300
|
+
JsonObject mapping = new JsonObject();
|
|
1301
|
+
if (labels == null || index >= labels.length || labels[index] == null) {
|
|
1302
|
+
mapping.add("case_value", JsonNull.INSTANCE);
|
|
1303
|
+
}
|
|
1304
|
+
else {
|
|
1305
|
+
mapping.addProperty("case_value", labels[index]);
|
|
1306
|
+
}
|
|
1307
|
+
String targetAddress = canonicalAddress(targets[index]);
|
|
1308
|
+
mapping.addProperty("target_address", targetAddress);
|
|
1309
|
+
JsonArray dataAddresses = new JsonArray();
|
|
1310
|
+
for (int sourceIndex = 0; sourceIndex < loadTableCount; sourceIndex += 1) {
|
|
1311
|
+
dataAddresses.add(
|
|
1312
|
+
canonicalAddress(loadTables[sourceIndex].getAddress())
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
mapping.add("data_addresses", dataAddresses);
|
|
1316
|
+
mapping.addProperty("confidence", "medium");
|
|
1317
|
+
JsonArray evidence = new JsonArray();
|
|
1318
|
+
evidence.add(
|
|
1319
|
+
inferenceEvidence(
|
|
1320
|
+
"jump-table",
|
|
1321
|
+
"ghidra-high-function",
|
|
1322
|
+
"Recovered target " +
|
|
1323
|
+
targetAddress +
|
|
1324
|
+
" from dispatch " +
|
|
1325
|
+
canonicalAddress(jumpTable.getSwitchAddress()) +
|
|
1326
|
+
"."
|
|
1327
|
+
)
|
|
1328
|
+
);
|
|
1329
|
+
mapping.add("evidence", evidence);
|
|
1330
|
+
mappings.add(mapping);
|
|
1331
|
+
}
|
|
1332
|
+
result.add("mappings", mappings);
|
|
1333
|
+
result.addProperty("mappings_truncated", targets.length > mappingCount);
|
|
1334
|
+
|
|
1335
|
+
JsonArray limitations = new JsonArray();
|
|
1336
|
+
if (loadTables.length == 0 && referencedDataAddresses.isEmpty()) {
|
|
1337
|
+
limitations.add(
|
|
1338
|
+
"Ghidra recovered targets but exposed no backing load-table or dispatch-block data address."
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
if (loadTables.length == 0 && !referencedDataAddresses.isEmpty()) {
|
|
1342
|
+
limitations.add(
|
|
1343
|
+
"Load-table metadata was absent; table-level candidate ownership is inferred from typed data references in the dispatch block and its immediate control-flow predecessors, but candidates are not assigned to individual mappings."
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
if (labels == null || labels.length != targets.length) {
|
|
1347
|
+
limitations.add(
|
|
1348
|
+
"Case labels were unavailable or incomplete; null labels are preserved."
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
if (
|
|
1352
|
+
loadTables.length > loadTableCount ||
|
|
1353
|
+
referencedDataAddresses.size() > referencedDataCount ||
|
|
1354
|
+
targets.length > mappingCount
|
|
1355
|
+
) {
|
|
1356
|
+
limitations.add(
|
|
1357
|
+
"Native API jump-table observations reached REA's bounded output limits."
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
result.add("limitations", limitations);
|
|
1361
|
+
return result;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
private List<Address> dispatchPathDataReferences(JumpTable jumpTable)
|
|
1365
|
+
throws Exception {
|
|
1366
|
+
BasicBlockModel model = new BasicBlockModel(currentProgram, false);
|
|
1367
|
+
CodeBlock dispatchBlock = model.getFirstCodeBlockContaining(
|
|
1368
|
+
jumpTable.getSwitchAddress(),
|
|
1369
|
+
monitor
|
|
1370
|
+
);
|
|
1371
|
+
if (dispatchBlock == null) {
|
|
1372
|
+
return List.of();
|
|
1373
|
+
}
|
|
1374
|
+
List<CodeBlock> blocks = new ArrayList<>();
|
|
1375
|
+
blocks.add(dispatchBlock);
|
|
1376
|
+
CodeBlockReferenceIterator sources = dispatchBlock.getSources(monitor);
|
|
1377
|
+
while (sources.hasNext()) {
|
|
1378
|
+
monitor.checkCancelled();
|
|
1379
|
+
CodeBlock sourceBlock = sources.next().getSourceBlock();
|
|
1380
|
+
if (sourceBlock != null) {
|
|
1381
|
+
blocks.add(sourceBlock);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
Set<Address> addresses = new HashSet<>();
|
|
1385
|
+
for (CodeBlock block : blocks) {
|
|
1386
|
+
InstructionIterator instructions = currentProgram
|
|
1387
|
+
.getListing()
|
|
1388
|
+
.getInstructions(block, true);
|
|
1389
|
+
while (instructions.hasNext()) {
|
|
1390
|
+
monitor.checkCancelled();
|
|
1391
|
+
Instruction instruction = instructions.next();
|
|
1392
|
+
for (
|
|
1393
|
+
Reference reference : currentProgram
|
|
1394
|
+
.getReferenceManager()
|
|
1395
|
+
.getReferencesFrom(instruction.getAddress())
|
|
1396
|
+
) {
|
|
1397
|
+
if (
|
|
1398
|
+
reference.getReferenceType().isData() &&
|
|
1399
|
+
currentProgram
|
|
1400
|
+
.getMemory()
|
|
1401
|
+
.getLoadedAndInitializedAddressSet()
|
|
1402
|
+
.contains(reference.getToAddress())
|
|
1403
|
+
) {
|
|
1404
|
+
addresses.add(reference.getToAddress());
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
List<Address> ordered = new ArrayList<>(addresses);
|
|
1410
|
+
ordered.sort(Address::compareTo);
|
|
1411
|
+
return List.copyOf(ordered);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
private static JsonObject inferenceEvidence(
|
|
1415
|
+
String kind,
|
|
1416
|
+
String source,
|
|
1417
|
+
String detail
|
|
1418
|
+
) {
|
|
1419
|
+
JsonObject evidence = new JsonObject();
|
|
1420
|
+
evidence.addProperty("kind", kind);
|
|
1421
|
+
evidence.addProperty("source", source);
|
|
1422
|
+
evidence.addProperty("detail", detail);
|
|
1423
|
+
return evidence;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
private static String inferenceConfidence(
|
|
1427
|
+
String signatureSource,
|
|
1428
|
+
boolean typeLocked
|
|
1429
|
+
) {
|
|
1430
|
+
if (
|
|
1431
|
+
typeLocked ||
|
|
1432
|
+
signatureSource.equals("user_defined") ||
|
|
1433
|
+
signatureSource.equals("imported")
|
|
1434
|
+
) {
|
|
1435
|
+
return "high";
|
|
1436
|
+
}
|
|
1437
|
+
return signatureSource.equals("analysis") ? "medium" : "low";
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
private DecompileResults decompile(Function function) {
|
|
975
1441
|
if (function.isExternal() || function.getBody().isEmpty()) {
|
|
976
1442
|
return null;
|
|
977
1443
|
}
|
|
@@ -995,6 +1461,13 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
995
1461
|
"Ghidra decompilation failed: " + boundedMessage(results.getErrorMessage())
|
|
996
1462
|
);
|
|
997
1463
|
}
|
|
1464
|
+
return results;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
private static String decompiledText(DecompileResults results) {
|
|
1468
|
+
if (results == null) {
|
|
1469
|
+
return null;
|
|
1470
|
+
}
|
|
998
1471
|
DecompiledFunction value = results.getDecompiledFunction();
|
|
999
1472
|
return value == null ? null : value.getC();
|
|
1000
1473
|
}
|