rea-agents 2.4.0 → 3.0.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.
Files changed (231) hide show
  1. package/README.md +91 -19
  2. package/bridge/ghidra/ReaGhidraBridge.java +524 -4
  3. package/bridge/hopper_bridge.py +313 -28
  4. package/dist/application/AnalysisClientCleanup.js +14 -0
  5. package/dist/application/AnalysisContextQueries.js +130 -0
  6. package/dist/application/AnalysisSnapshotCache.js +4 -2
  7. package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
  8. package/dist/application/ArtifactInventory/scanCanonical.js +1 -1
  9. package/dist/application/BinarySession.js +20 -8
  10. package/dist/application/BinarySessionComposition.js +21 -0
  11. package/dist/application/BinarySessionRecords.js +79 -9
  12. package/dist/application/BinarySessionStatus.js +31 -1
  13. package/dist/application/BrowserEvidence.js +14 -0
  14. package/dist/application/BrowserScenarioCapturePort.js +1 -0
  15. package/dist/application/BrowserScenarioCaptureService.js +53 -0
  16. package/dist/application/BrowserScenarioEvidence.js +40 -0
  17. package/dist/application/CallPathTracing.js +160 -0
  18. package/dist/application/CapabilityInventory.js +84 -14
  19. package/dist/application/ClientRegistrationStatus.js +4 -1
  20. package/dist/application/CompositeProvider.js +10 -1
  21. package/dist/application/DirectAnalysis.js +13 -4
  22. package/dist/application/EnhancedLiteralTracing.js +118 -0
  23. package/dist/application/EnhancedTools.js +71 -127
  24. package/dist/application/EvidenceLedger.js +45 -16
  25. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
  26. package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
  27. package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
  28. package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
  29. package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
  30. package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
  31. package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
  32. package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
  33. package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
  34. package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
  35. package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
  36. package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
  37. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
  38. package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
  39. package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
  40. package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
  41. package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
  42. package/dist/application/LazyAnalysisProvider.js +77 -0
  43. package/dist/application/LoopbackReplay.js +1 -1
  44. package/dist/application/NativeApiInspection.js +80 -0
  45. package/dist/application/ProcessReactiveEffects.js +6 -0
  46. package/dist/application/ProcessReactiveHarness.js +16 -1
  47. package/dist/application/ProjectPermissionStore.js +1 -0
  48. package/dist/application/ReconstructionObligationAuthority.js +72 -0
  49. package/dist/application/ReconstructionObligationCandidates.js +265 -0
  50. package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
  51. package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
  52. package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
  53. package/dist/application/ReconstructionObligationLedgerService.js +72 -0
  54. package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
  55. package/dist/application/ReconstructionObligationPolicies.js +132 -0
  56. package/dist/application/ReconstructionReadinessService.js +48 -0
  57. package/dist/application/SessionProviderRouter.js +6 -6
  58. package/dist/application/SetupClientConfiguration.js +4 -2
  59. package/dist/application/runtime.js +26 -9
  60. package/dist/artifacts/ArtifactProvider.js +87 -22
  61. package/dist/browser/BrowserScenarioSecrets.js +53 -0
  62. package/dist/browser/BrowserScenarioSessionPort.js +1 -0
  63. package/dist/browser/CdpBrowserProvider.js +2 -2
  64. package/dist/browser/CdpCaptureDocuments.js +57 -0
  65. package/dist/browser/CdpCaptureStorage.js +71 -29
  66. package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
  67. package/dist/browser/CdpEndpoint.js +8 -6
  68. package/dist/browser/CdpPageCapture.js +2 -0
  69. package/dist/browser/JavaScriptRuntimeScope.js +38 -0
  70. package/dist/browser/PlaywrightBrowserScenarioProvider.js +259 -0
  71. package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
  72. package/dist/browser/PlaywrightScenarioActions.js +62 -0
  73. package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
  74. package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
  75. package/dist/browser/PlaywrightScenarioEvents.js +301 -0
  76. package/dist/browser/PlaywrightScenarioSession.js +300 -0
  77. package/dist/browser/V8InspectorCaptureProjection.js +110 -0
  78. package/dist/browser/V8InspectorEndpoint.js +72 -0
  79. package/dist/browser/V8InspectorProvider.js +267 -0
  80. package/dist/catalogIdentity.js +11 -3
  81. package/dist/cli/artifactCommands.js +44 -3
  82. package/dist/cli/coreAnalysisCommands.js +78 -0
  83. package/dist/cli.js +4 -0
  84. package/dist/cliApplicationCommands.js +30 -2
  85. package/dist/cliBrowserAdvancedCommands.js +17 -5
  86. package/dist/cliBrowserCommands.js +1 -0
  87. package/dist/cliBrowserScenarioCommands.js +43 -0
  88. package/dist/cliCommandNames.js +9 -0
  89. package/dist/cliJavaScriptRuntimeCommands.js +164 -0
  90. package/dist/cliObservationOptions.js +4 -0
  91. package/dist/cliPolicyCommands.js +1 -0
  92. package/dist/config/environment.js +12 -0
  93. package/dist/config/parseConfig.js +88 -17
  94. package/dist/contracts/analysisSearchContract.js +14 -0
  95. package/dist/contracts/applicationToolContracts.js +86 -2
  96. package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
  97. package/dist/contracts/artifactToolContracts.js +29 -2
  98. package/dist/contracts/browserProviderToolContracts.js +7 -0
  99. package/dist/contracts/browserScenarioToolContracts.js +81 -0
  100. package/dist/contracts/browserToolContracts.js +88 -98
  101. package/dist/contracts/enhancedInputs.js +35 -6
  102. package/dist/contracts/functionInstructionContract.js +8 -0
  103. package/dist/contracts/functionWorkflowToolContracts.js +30 -0
  104. package/dist/contracts/hopperMemoryContracts.js +22 -0
  105. package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
  106. package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
  107. package/dist/contracts/promptContracts.js +14 -15
  108. package/dist/contracts/reconstructionReadinessExample.js +436 -0
  109. package/dist/contracts/toolContractExamples.js +8 -0
  110. package/dist/contracts/toolContractTypes.js +1 -0
  111. package/dist/contracts/toolContracts.js +34 -25
  112. package/dist/contracts/toolEffects.js +25 -0
  113. package/dist/contracts/toolOutputSchemaGroups.js +120 -30
  114. package/dist/contracts/toolOutputSchemaPrimitives.js +37 -0
  115. package/dist/domain/analysisErrorPresentation.js +8 -2
  116. package/dist/domain/analysisErrorProjection.js +9 -0
  117. package/dist/domain/artifactInspection.js +347 -0
  118. package/dist/domain/browserCaptureComparison.js +82 -0
  119. package/dist/domain/browserObservation.js +8 -1
  120. package/dist/domain/browserObservationErrors.js +1 -0
  121. package/dist/domain/browserObservationSchemas.js +26 -0
  122. package/dist/domain/browserScenario.js +165 -0
  123. package/dist/domain/browserScenarioCapture.js +57 -0
  124. package/dist/domain/browserScenarioCaptureValues.js +204 -0
  125. package/dist/domain/browserScenarioDiff.js +324 -0
  126. package/dist/domain/browserScenarioDiffValues.js +157 -0
  127. package/dist/domain/browserScenarioNormalization.js +45 -0
  128. package/dist/domain/browserScenarioValues.js +372 -0
  129. package/dist/domain/bytecodeProvider.js +179 -0
  130. package/dist/domain/conformancePackage.js +169 -0
  131. package/dist/domain/conformanceReplay.js +104 -0
  132. package/dist/domain/conformanceTrustGate.js +193 -0
  133. package/dist/domain/customProtocolCapture.js +137 -0
  134. package/dist/domain/eventProcessTree.js +214 -0
  135. package/dist/domain/evidenceBundle.js +47 -0
  136. package/dist/domain/hopperStartupFailure.js +1 -1
  137. package/dist/domain/hopperValues.js +14 -1
  138. package/dist/domain/inputIssueProjection.js +7 -1
  139. package/dist/domain/javascriptRuntimeObservation.js +170 -0
  140. package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
  141. package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
  142. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
  143. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
  144. package/dist/domain/javascriptSemanticAnalysis.js +13 -16
  145. package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
  146. package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
  147. package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
  148. package/dist/domain/javascriptSemanticDataEffects.js +284 -0
  149. package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
  150. package/dist/domain/javascriptSemanticFingerprints.js +174 -0
  151. package/dist/domain/javascriptSemanticIr.js +21 -1
  152. package/dist/domain/javascriptSemanticObjects.js +115 -0
  153. package/dist/domain/javascriptSemanticPromises.js +290 -0
  154. package/dist/domain/javascriptSemanticResources.js +136 -0
  155. package/dist/domain/javascriptSourceParser.js +15 -0
  156. package/dist/domain/javascriptStaticAnalysis.js +12 -15
  157. package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
  158. package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
  159. package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
  160. package/dist/domain/mobileApplicationInvestigation.js +159 -0
  161. package/dist/domain/nativeApiBoundary.js +130 -0
  162. package/dist/domain/objcSwiftMetadata.js +150 -0
  163. package/dist/domain/packageAnalysis.js +123 -0
  164. package/dist/domain/peInspection.js +196 -0
  165. package/dist/domain/processCapture.js +8 -2
  166. package/dist/domain/processReactiveScenario.js +1 -0
  167. package/dist/domain/processReactiveTransition.js +9 -4
  168. package/dist/domain/protocolCapture.js +243 -0
  169. package/dist/domain/providerCleanupError.js +11 -0
  170. package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
  171. package/dist/domain/reconstructionReadiness.js +121 -0
  172. package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
  173. package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
  174. package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
  175. package/dist/domain/reconstructionReadinessSchemas.js +240 -0
  176. package/dist/domain/sourceToBundleComparison.js +335 -0
  177. package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
  178. package/dist/domain/sourceToBundleSignals.js +202 -0
  179. package/dist/domain/webCaptureDiff.js +141 -27
  180. package/dist/domain/webScreenshot.js +1 -1
  181. package/dist/dotnet/ManagedStaticProvider.js +34 -5
  182. package/dist/generatedMcpToolCatalog.js +37 -0
  183. package/dist/generatedPackageMetadata.js +4 -4
  184. package/dist/ghidra/GhidraDefaults.js +1 -1
  185. package/dist/ghidra/GhidraFunctionValues.js +40 -2
  186. package/dist/ghidra/GhidraProviderCapabilities.js +47 -38
  187. package/dist/hopper/HopperCleanup.js +158 -0
  188. package/dist/hopper/HopperClient.js +93 -99
  189. package/dist/hopper/HopperProvider.js +61 -30
  190. package/dist/hopper/HopperRequestQueue.js +215 -0
  191. package/dist/hopper/HopperResponseStream.js +46 -0
  192. package/dist/hopper/protocol.js +63 -20
  193. package/dist/main/transport.js +24 -27
  194. package/dist/main.js +14 -6
  195. package/dist/mcpDoctor.js +27 -4
  196. package/dist/mcpStartupPolicy.js +14 -0
  197. package/dist/process/ProcessCommandIdentity.js +19 -0
  198. package/dist/process/ProcessOwnership.js +11 -8
  199. package/dist/server/DynamicToolAvailability.js +70 -0
  200. package/dist/server/LazyToolCatalog.js +134 -0
  201. package/dist/server/createServer.js +92 -154
  202. package/dist/server/hydrateServerTools.js +191 -0
  203. package/dist/server/lifecycleProgress.js +10 -0
  204. package/dist/server/permissionFailure.js +12 -0
  205. package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
  206. package/dist/server/registerApplicationTools/helpers.js +11 -7
  207. package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
  208. package/dist/server/registerApplicationTools/readiness.js +55 -0
  209. package/dist/server/registerApplicationTools.js +6 -0
  210. package/dist/server/registerArtifactTools.js +9 -1
  211. package/dist/server/registerBrowserScenarioTool.js +25 -0
  212. package/dist/server/registerBrowserTools.js +2 -2
  213. package/dist/server/registerCloseLifecycleTool.js +57 -0
  214. package/dist/server/registerEnhancedTools.js +28 -7
  215. package/dist/server/registerEvidenceResources.js +54 -101
  216. package/dist/server/registerEvidenceTools.js +9 -2
  217. package/dist/server/registerInvestigationTools.js +1 -2
  218. package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
  219. package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
  220. package/dist/server/registerReconstructionObligationLedgerResource.js +32 -0
  221. package/dist/server/registerReconstructionReadinessResource.js +32 -0
  222. package/dist/server/registerSessionRecordTools.js +82 -12
  223. package/dist/server/registerSessionStatusTool.js +11 -8
  224. package/dist/server/registerSessionTools.js +32 -58
  225. package/dist/server/sessionAvailabilityPolicy.js +10 -8
  226. package/dist/server/toolRegistrationOptions.js +21 -6
  227. package/dist/server/toolResult.js +12 -0
  228. package/package.json +28 -11
  229. package/scripts/package-runner-bootstrap.mjs +72 -0
  230. package/scripts/rea.mjs +11 -1
  231. package/skills/reverse-engineer-anything/SKILL.md +10 -3
@@ -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 = 4;
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;
@@ -123,6 +134,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
123
134
  "procedure_callers",
124
135
  "procedure_info",
125
136
  "procedure_pseudo_code",
137
+ "read_function_instructions",
126
138
  "procedure_references",
127
139
  "xrefs"
128
140
  };
@@ -311,6 +323,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
311
323
  case "procedure_callers" -> procedureCalls(request.params, true);
312
324
  case "procedure_info" -> procedureInfo(request.params);
313
325
  case "procedure_pseudo_code" -> procedurePseudocode(request.params);
326
+ case "read_function_instructions" -> readFunctionInstructions(request.params);
314
327
  case "procedure_references" -> procedureReferences(request.params);
315
328
  case "resolve_containing_procedure" -> containingProcedure(request.params);
316
329
  case "search_procedures" -> search(request.params, true);
@@ -375,7 +388,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
375
388
  private JsonElement procedurePseudocode(JsonObject params) throws Exception {
376
389
  requireKeys(params, Set.of("document", "procedure"));
377
390
  requireDocument(params);
378
- String value = decompile(resolveProcedure(requireString(params, "procedure")));
391
+ String value = decompiledText(
392
+ decompile(resolveProcedure(requireString(params, "procedure")))
393
+ );
379
394
  return value == null ? JsonNull.INSTANCE : GSON.toJsonTree(value);
380
395
  }
381
396
 
@@ -390,6 +405,51 @@ public final class ReaGhidraBridge extends HeadlessScript {
390
405
  return GSON.toJsonTree(renderAssembly(scan.instructions));
391
406
  }
392
407
 
408
+ private JsonObject readFunctionInstructions(JsonObject params) throws Exception {
409
+ requireKeys(params, Set.of("document", "procedure", "offset", "limit"));
410
+ requireDocument(params);
411
+ Function function = resolveProcedure(requireString(params, "procedure"));
412
+ int offset = requireBoundedInteger(params, "offset", 0, 100_000);
413
+ int limit = requireBoundedInteger(params, "limit", 1, 500);
414
+ InstructionScan scan = scanInstructions(function, offset + limit + 1);
415
+ int start = Math.min(offset, scan.instructions.size());
416
+ int end = Math.min(scan.instructions.size(), start + limit);
417
+ JsonArray items = new JsonArray();
418
+ for (String line : renderAssemblyLines(scan.instructions.subList(start, end))) {
419
+ items.add(line);
420
+ }
421
+ boolean hasMore = scan.truncated || end < scan.instructions.size();
422
+ JsonObject instructions = new JsonObject();
423
+ instructions.add("items", items);
424
+ if (scan.truncated) {
425
+ instructions.add("total", JsonNull.INSTANCE);
426
+ }
427
+ else {
428
+ instructions.addProperty("total", scan.instructions.size());
429
+ }
430
+ instructions.addProperty("returned", items.size());
431
+ instructions.addProperty("truncated", hasMore);
432
+ if (hasMore) {
433
+ instructions.addProperty("next_offset", end);
434
+ }
435
+ else {
436
+ instructions.add("next_offset", JsonNull.INSTANCE);
437
+ }
438
+
439
+ JsonArray limitations = new JsonArray();
440
+ limitations.add("Instruction text and ordering are Ghidra-specific representations.");
441
+ limitations.add(
442
+ "The fast path does not invoke the decompiler or scan whole-program names and strings."
443
+ );
444
+ JsonObject result = new JsonObject();
445
+ result.add("procedure", procedureIdentity(function));
446
+ result.add("instructions", instructions);
447
+ result.addProperty("instructions_scanned", scan.instructions.size());
448
+ result.addProperty("instruction_scan_truncated", scan.truncated);
449
+ result.add("limitations", limitations);
450
+ return result;
451
+ }
452
+
393
453
  private JsonArray procedureCalls(JsonObject params, boolean callers) throws Exception {
394
454
  requireKeys(params, Set.of("document", "procedure"));
395
455
  requireDocument(params);
@@ -529,7 +589,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
529
589
  );
530
590
  JsonObject offsets = requireCollectionOffsets(params);
531
591
  InstructionScan scan = scanInstructions(function, maximumInstructions);
532
- String pseudocode = decompile(function);
592
+ DecompileResults decompilation = decompile(function);
593
+ String pseudocode = decompiledText(decompilation);
533
594
  if (pseudocode == null) {
534
595
  pseudocode = "";
535
596
  }
@@ -615,6 +676,19 @@ public final class ReaGhidraBridge extends HeadlessScript {
615
676
  instructionScan.addProperty("scanned", scan.instructions.size());
616
677
  instructionScan.addProperty("truncated", scan.truncated);
617
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
+ );
618
692
  JsonArray limitations = new JsonArray();
619
693
  limitations.add(
620
694
  "Unresolved computed or indirect flows without target addresses are not represented as reference edges."
@@ -924,7 +998,446 @@ public final class ReaGhidraBridge extends HeadlessScript {
924
998
  return matches.get(0);
925
999
  }
926
1000
 
927
- private String decompile(Function function) {
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) {
928
1441
  if (function.isExternal() || function.getBody().isEmpty()) {
929
1442
  return null;
930
1443
  }
@@ -948,6 +1461,13 @@ public final class ReaGhidraBridge extends HeadlessScript {
948
1461
  "Ghidra decompilation failed: " + boundedMessage(results.getErrorMessage())
949
1462
  );
950
1463
  }
1464
+ return results;
1465
+ }
1466
+
1467
+ private static String decompiledText(DecompileResults results) {
1468
+ if (results == null) {
1469
+ return null;
1470
+ }
951
1471
  DecompiledFunction value = results.getDecompiledFunction();
952
1472
  return value == null ? null : value.getC();
953
1473
  }