rea-agents 3.2.1 → 4.0.1

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 (434) hide show
  1. package/README.md +183 -332
  2. package/bridge/ghidra/ReaGhidraBridge.java +561 -74
  3. package/bridge/hopper_bridge.py +70 -27
  4. package/bridge/native/ReaNativeUI.swift +157 -0
  5. package/dist/application/AnalysisContextQueries.js +19 -5
  6. package/dist/application/AnalysisSnapshotCache.js +30 -11
  7. package/dist/application/AppleAssetCatalogAnalysis.js +171 -0
  8. package/dist/application/ArtifactExtractionDestination.js +0 -12
  9. package/dist/application/ArtifactGraphConstruction.js +44 -11
  10. package/dist/application/ArtifactInventory/classify.js +7 -15
  11. package/dist/application/ArtifactInventory/hash.js +4 -2
  12. package/dist/application/ArtifactInventory/policy.js +2 -17
  13. package/dist/application/ArtifactInventory/reader.js +3 -3
  14. package/dist/application/ArtifactInventory/scanCanonical.js +2 -2
  15. package/dist/application/ArtifactInventory/scanReader.js +12 -4
  16. package/dist/application/ArtifactInventory/types.js +0 -3
  17. package/dist/application/BinarySession.js +28 -43
  18. package/dist/application/BinarySessionExecution.js +37 -1
  19. package/dist/application/BinaryTargetResolver.js +50 -4
  20. package/dist/application/BrowserEvidence.js +36 -7
  21. package/dist/application/BrowserObservationService.js +17 -44
  22. package/dist/application/BrowserScenarioCaptureService.js +2 -35
  23. package/dist/application/BrowserScenarioEvidence.js +13 -9
  24. package/dist/application/CapabilityClientRequirements.js +2 -11
  25. package/dist/application/CapabilityInventory.js +21 -35
  26. package/dist/application/ClientConfigurationDocument.js +84 -7
  27. package/dist/application/ClientRegistrationIdentity.js +32 -0
  28. package/dist/application/ClientRegistrationStatus.js +59 -6
  29. package/dist/application/DirectAnalysis.js +37 -58
  30. package/dist/application/DirectAnalysisStatus.js +4 -4
  31. package/dist/application/Doctor.js +89 -80
  32. package/dist/application/DoctorDiagnostics.js +2 -26
  33. package/dist/application/DoctorScope.js +8 -14
  34. package/dist/application/ElectronActiveEvidence.js +10 -27
  35. package/dist/application/ElectronActiveObservationService.js +9 -36
  36. package/dist/application/ElectronBoundaryAnalysis.js +2 -0
  37. package/dist/application/ElectronBoundaryGraphNative.js +1 -1
  38. package/dist/application/ElectronEvidence.js +8 -3
  39. package/dist/application/ElectronObservationService.js +9 -25
  40. package/dist/application/EnhancedLiteralTracing.js +4 -8
  41. package/dist/application/EnhancedTools.js +15 -35
  42. package/dist/application/FilesystemSnapshot.js +7 -8
  43. package/dist/application/JavaScriptApplicationEvidenceGraph.js +2 -1
  44. package/dist/application/JavaScriptApplicationWorkflowService.js +31 -7
  45. package/dist/application/JavaScriptArtifactAnalysis.js +6 -4
  46. package/dist/application/JavaScriptArtifactFiles.js +5 -0
  47. package/dist/application/JavaScriptArtifactPathResolution.js +179 -47
  48. package/dist/application/JavaScriptRuntimeObservationEvidence.js +6 -3
  49. package/dist/application/JavaScriptRuntimeObservationService.js +9 -25
  50. package/dist/application/JavaScriptRuntimeReconciliationService.js +45 -4
  51. package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +11 -0
  52. package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +11 -0
  53. package/dist/application/JavaScriptSemanticGraphDataProjection.js +11 -0
  54. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +14 -5
  55. package/dist/application/JsonFiles.js +1 -1
  56. package/dist/application/LazyAnalysisProvider.js +27 -6
  57. package/dist/application/ManagedMemberComparisonService.js +19 -6
  58. package/dist/application/NativeApiInspection.js +2 -0
  59. package/dist/application/NativeCallRoutes.js +72 -0
  60. package/dist/application/NativeDispatchMetadataInspection.js +33 -0
  61. package/dist/application/NativeUiActionTrace.js +73 -49
  62. package/dist/application/NativeValueTrace.js +320 -0
  63. package/dist/application/ProcessCaptureCapability.js +32 -5
  64. package/dist/application/ProcessCaptureEnvironment.js +9 -26
  65. package/dist/application/ProcessCaptureError.js +1 -5
  66. package/dist/application/ProcessCaptureJournal.js +3 -40
  67. package/dist/application/ProcessCaptureLifecycle.js +102 -111
  68. package/dist/application/ProcessCli.js +18 -25
  69. package/dist/application/ProcessEvidence.js +5 -7
  70. package/dist/application/ProcessFilesystemEffects.js +36 -0
  71. package/dist/application/ProcessHarness.js +41 -120
  72. package/dist/application/ProcessNormalization.js +2 -17
  73. package/dist/application/ProcessScenarioRuntimeValidation.js +76 -0
  74. package/dist/application/ReferenceSourceImport.js +3 -4
  75. package/dist/application/Setup.js +236 -84
  76. package/dist/application/SetupClientConfiguration.js +14 -9
  77. package/dist/application/SetupPlan.js +138 -86
  78. package/dist/application/SetupSkill.js +0 -1
  79. package/dist/application/SupportedClients.js +102 -21
  80. package/dist/application/Uninstall.js +31 -20
  81. package/dist/application/Update.js +93 -0
  82. package/dist/application/UpdateMaintenance.js +108 -0
  83. package/dist/application/UpdateRuntime.js +126 -0
  84. package/dist/application/runtime.js +13 -2
  85. package/dist/artifacts/ArtifactPaths.js +6 -8
  86. package/dist/artifacts/ArtifactProvider.js +94 -41
  87. package/dist/artifacts/AsarArtifactReader.js +19 -4
  88. package/dist/artifacts/KeyedArchiveReader.js +110 -0
  89. package/dist/artifacts/MachOSliceArtifactReader.js +53 -5
  90. package/dist/artifacts/ZipArtifactReader.js +18 -1
  91. package/dist/browser/BrowserScenarioSecrets.js +28 -1
  92. package/dist/browser/CdpBrowserProvider.js +36 -15
  93. package/dist/browser/CdpCaptureDocuments.js +22 -8
  94. package/dist/browser/CdpCaptureEventHandlers.js +14 -5
  95. package/dist/browser/CdpCaptureEventHelpers.js +13 -0
  96. package/dist/browser/CdpCaptureEvents.js +10 -1
  97. package/dist/browser/CdpElectronInspection.js +5 -10
  98. package/dist/browser/CdpElectronProvider.js +1 -5
  99. package/dist/browser/CdpElectronScripts.js +1 -1
  100. package/dist/browser/CdpEndpoint.js +3 -2
  101. package/dist/browser/CdpObservationSession.js +14 -10
  102. package/dist/browser/CdpPageCapture.js +1 -1
  103. package/dist/browser/CdpPageCaptureScripts.js +1 -1
  104. package/dist/browser/CdpSafeMetadata.js +5 -4
  105. package/dist/browser/CdpScreenshot.js +16 -5
  106. package/dist/browser/CdpTargetSession.js +25 -4
  107. package/dist/browser/CdpWebMcpDiscovery.js +93 -20
  108. package/dist/browser/JavaScriptRuntimeScope.js +28 -2
  109. package/dist/browser/PlaywrightBrowserScenarioProvider.js +9 -8
  110. package/dist/browser/PlaywrightElectronActiveActions.js +3 -10
  111. package/dist/browser/PlaywrightElectronActiveProvider.js +5 -5
  112. package/dist/browser/PlaywrightScenarioActions.js +1 -1
  113. package/dist/browser/PlaywrightScenarioArtifacts.js +13 -25
  114. package/dist/browser/PlaywrightScenarioBrowser.js +28 -25
  115. package/dist/browser/PlaywrightScenarioEvents.js +82 -93
  116. package/dist/browser/PlaywrightScenarioSession.js +27 -123
  117. package/dist/browser/PngVisualDiff.js +22 -3
  118. package/dist/browser/V8InspectorCaptureProjection.js +7 -1
  119. package/dist/browser/V8InspectorProvider.js +60 -7
  120. package/dist/browser/WebSourceMapFetcher.js +99 -53
  121. package/dist/cli/artifactCommands.js +56 -12
  122. package/dist/cli/coreAnalysisCommands.js +110 -0
  123. package/dist/cli/managedCommands.js +1 -31
  124. package/dist/cli/setupCommands.js +16 -14
  125. package/dist/cli/utilityCommands.js +61 -4
  126. package/dist/cli.js +5 -7
  127. package/dist/cliApplicationCommands.js +0 -83
  128. package/dist/cliBrowserAdvancedCommands.js +7 -11
  129. package/dist/cliBrowserCommands.js +15 -23
  130. package/dist/cliBrowserContext.js +3 -24
  131. package/dist/cliBrowserScenarioCommands.js +2 -10
  132. package/dist/cliCommandNames.js +82 -7
  133. package/dist/cliElectronCommands.js +10 -36
  134. package/dist/cliJavaScriptRuntimeCommands.js +6 -30
  135. package/dist/cliJsonInput.js +4 -2
  136. package/dist/cliLogging.js +16 -7
  137. package/dist/cliObservationOptions.js +1 -1
  138. package/dist/cliOutput.js +2 -4
  139. package/dist/cliProcessCommands.js +6 -33
  140. package/dist/cliSetup.js +90 -160
  141. package/dist/config/environment.js +2 -78
  142. package/dist/config/parseConfig.js +18 -109
  143. package/dist/config/parsers.js +0 -23
  144. package/dist/contracts/applicationToolContracts.js +1 -118
  145. package/dist/contracts/artifactToolContracts.js +11 -19
  146. package/dist/contracts/browserScenarioToolContracts.js +1 -5
  147. package/dist/contracts/browserToolContracts.js +9 -16
  148. package/dist/contracts/electronToolContracts.js +5 -6
  149. package/dist/contracts/enhancedInputs.js +3 -2
  150. package/dist/contracts/errorSchemas.js +16 -40
  151. package/dist/contracts/functionWorkflowToolContracts.js +2 -2
  152. package/dist/contracts/javascriptApplicationWorkflowExamples.js +2 -2
  153. package/dist/contracts/javascriptRuntimeObservationToolContracts.js +4 -4
  154. package/dist/contracts/javascriptRuntimeReconciliationExample.js +1 -1
  155. package/dist/contracts/managedToolContracts.js +7 -4
  156. package/dist/contracts/managedWorkflowExamples.js +0 -22
  157. package/dist/contracts/managedWorkflowToolContracts.js +16 -31
  158. package/dist/contracts/nativeToolContracts.js +9 -0
  159. package/dist/contracts/promptContracts.js +6 -6
  160. package/dist/contracts/sessionToolSchemas.js +3 -3
  161. package/dist/contracts/toolContractExamples.js +4 -3
  162. package/dist/contracts/toolContracts.js +18 -7
  163. package/dist/contracts/toolEffects.js +34 -21
  164. package/dist/contracts/toolOutputSchemaGroups.js +18 -5
  165. package/dist/contracts/toolOutputSchemaPrimitives.js +9 -6
  166. package/dist/contracts/toolSchemaMetadata.js +5 -8
  167. package/dist/doctorRuntime.js +0 -41
  168. package/dist/domain/analysisErrorPresentation.js +4 -28
  169. package/dist/domain/analysisErrorProjection.js +33 -50
  170. package/dist/domain/analysisProfile.js +1 -1
  171. package/dist/domain/analysisSnapshot.js +77 -3
  172. package/dist/domain/androidApplication.js +5 -4
  173. package/dist/domain/appleApplication.js +5 -4
  174. package/dist/domain/appleAssetCatalog.js +127 -0
  175. package/dist/domain/artifactComparison.js +7 -11
  176. package/dist/domain/artifactGraph.js +8 -6
  177. package/dist/domain/artifactInspection.js +13 -12
  178. package/dist/domain/binaryTarget.js +23 -9
  179. package/dist/domain/browserObservation.js +43 -24
  180. package/dist/domain/browserObservationSchemas.js +2 -1
  181. package/dist/domain/browserScenario.js +7 -107
  182. package/dist/domain/browserScenarioCapture.js +0 -1
  183. package/dist/domain/browserScenarioCaptureValues.js +5 -4
  184. package/dist/domain/browserScenarioDiff.js +1 -3
  185. package/dist/domain/browserScenarioValues.js +16 -107
  186. package/dist/domain/bundleComparison.js +18 -17
  187. package/dist/domain/callPathSchemas.js +17 -7
  188. package/dist/domain/changedBehavior.js +2 -3
  189. package/dist/domain/comparisonSemantics.js +19 -0
  190. package/dist/domain/completionLedgerGeneration.js +3 -2
  191. package/dist/domain/conformancePackage.js +2 -1
  192. package/dist/domain/conformanceTrustGate.js +3 -5
  193. package/dist/domain/customProtocolCapture.js +2 -2
  194. package/dist/domain/digests.js +30 -0
  195. package/dist/domain/dosMz.js +83 -0
  196. package/dist/domain/electronActiveObservation.js +7 -5
  197. package/dist/domain/electronObservation.js +3 -2
  198. package/dist/domain/electronStaticAnalysisNative.js +15 -6
  199. package/dist/domain/emptyArraySchema.js +3 -0
  200. package/dist/domain/errors.js +10 -28
  201. package/dist/domain/evidence.js +5 -3
  202. package/dist/domain/evidenceCompletionLedger.js +3 -2
  203. package/dist/domain/functionComparisonNormalization.js +24 -18
  204. package/dist/domain/functionComparisonResults.js +3 -7
  205. package/dist/domain/functionComparisonSchemas.js +3 -2
  206. package/dist/domain/hopperValues.js +108 -8
  207. package/dist/domain/javascriptApplicationAnalysis.js +4 -7
  208. package/dist/domain/javascriptApplicationEvidenceSchemas.js +4 -3
  209. package/dist/domain/javascriptApplicationGraphSchemas.js +8 -7
  210. package/dist/domain/javascriptApplicationVersionComparisonSchemas.js +9 -7
  211. package/dist/domain/javascriptApplicationVersionItems.js +12 -18
  212. package/dist/domain/javascriptAstValues.js +4 -0
  213. package/dist/domain/javascriptExportShapeComparison.js +1 -6
  214. package/dist/domain/javascriptExportShapeComparisonSchemas.js +6 -10
  215. package/dist/domain/javascriptExportShapeVariants.js +15 -2
  216. package/dist/domain/javascriptFeatureTraceSchemas.js +8 -7
  217. package/dist/domain/javascriptRuntimeObservation.js +14 -2
  218. package/dist/domain/javascriptRuntimeReconciliationMatching.js +8 -0
  219. package/dist/domain/javascriptRuntimeReconciliationParsing.js +4 -2
  220. package/dist/domain/javascriptRuntimeReconciliationResult.js +6 -4
  221. package/dist/domain/javascriptRuntimeReconciliationRuntime.js +15 -4
  222. package/dist/domain/javascriptRuntimeReconciliationSchemas.js +19 -18
  223. package/dist/domain/javascriptRuntimeStaticCandidates.js +18 -6
  224. package/dist/domain/javascriptSemanticAnalysis.js +57 -19
  225. package/dist/domain/javascriptSemanticAsyncEffects.js +39 -40
  226. package/dist/domain/javascriptSemanticCalls.js +46 -27
  227. package/dist/domain/javascriptSemanticChildProcesses.js +43 -50
  228. package/dist/domain/javascriptSemanticDataEffectHelpers.js +91 -18
  229. package/dist/domain/javascriptSemanticDataEffects.js +22 -10
  230. package/dist/domain/javascriptSemanticGraphSchemas.js +8 -6
  231. package/dist/domain/javascriptSemanticObjects.js +13 -3
  232. package/dist/domain/javascriptSemanticProjection.js +144 -38
  233. package/dist/domain/javascriptSemanticPromises.js +14 -27
  234. package/dist/domain/javascriptSemanticQuerySchemas.js +6 -9
  235. package/dist/domain/javascriptSemanticResources.js +2 -5
  236. package/dist/domain/javascriptSemanticState.js +4 -0
  237. package/dist/domain/javascriptSemanticTraceSchemas.js +2 -1
  238. package/dist/domain/javascriptSemanticTraversal.js +13 -5
  239. package/dist/domain/javascriptSemanticValues.js +10 -9
  240. package/dist/domain/javascriptSourceParser.js +5 -1
  241. package/dist/domain/javascriptStaticAnalysisCalls.js +8 -5
  242. package/dist/domain/javascriptStaticAnalysisHelpers.js +16 -7
  243. package/dist/domain/keyedArchive.js +181 -0
  244. package/dist/domain/managedApplicationGraph.js +4 -3
  245. package/dist/domain/managedArtifact.js +2 -1
  246. package/dist/domain/managedMemberComparison.js +18 -13
  247. package/dist/domain/managedMemberComparisonCoverage.js +2 -1
  248. package/dist/domain/managedMemberComparisonItems.js +27 -7
  249. package/dist/domain/managedMemberComparisonMatch.js +22 -6
  250. package/dist/domain/managedNativeVerificationMatch.js +2 -2
  251. package/dist/domain/managedNativeVerificationSchemas.js +7 -5
  252. package/dist/domain/managedReconstruction.js +6 -5
  253. package/dist/domain/nativeApiBoundary.js +7 -0
  254. package/dist/domain/nativeDataType.js +51 -0
  255. package/dist/domain/nativeInspection.js +0 -1
  256. package/dist/domain/nativeInstruction.js +82 -0
  257. package/dist/domain/nativeInvestigationGraph.js +9 -5
  258. package/dist/domain/nativeUiObservation.js +123 -0
  259. package/dist/domain/nativeValueFlow.js +17 -0
  260. package/dist/domain/nativeValueTrace.js +64 -0
  261. package/dist/domain/objcSwiftMetadata.js +39 -0
  262. package/dist/domain/peInspection.js +1 -0
  263. package/dist/domain/processCapture.js +5 -55
  264. package/dist/domain/processCaptureExample.js +0 -9
  265. package/dist/domain/processCaptureValidation.js +13 -144
  266. package/dist/domain/processComparison.js +3 -25
  267. package/dist/domain/processObservation.js +0 -29
  268. package/dist/domain/processScenario.js +28 -228
  269. package/dist/domain/processTraceDimensionProjection.js +0 -15
  270. package/dist/domain/processTraceEvaluation.js +0 -6
  271. package/dist/domain/protocolCapture.js +8 -36
  272. package/dist/domain/reconstructionCoverage.js +3 -2
  273. package/dist/domain/reconstructionObligationLedgerSchemas.js +4 -12
  274. package/dist/domain/reconstructionReadinessSchemas.js +4 -3
  275. package/dist/domain/reconstructionVerification.js +2 -1
  276. package/dist/domain/reconstructionVerificationSchemas.js +6 -13
  277. package/dist/domain/referenceSourceClassification.js +0 -110
  278. package/dist/domain/referenceSourceGraph.js +15 -5
  279. package/dist/domain/residualUnknown.js +3 -2
  280. package/dist/domain/runtimeIdentification.js +6 -5
  281. package/dist/domain/sourceMapContents.js +5 -0
  282. package/dist/domain/sourceToBundleComparison.js +3 -10
  283. package/dist/domain/sourceToBundleComparisonSchemas.js +7 -6
  284. package/dist/domain/sourceToBundleSignals.js +9 -3
  285. package/dist/domain/staticRuntimeCorrelation.js +5 -11
  286. package/dist/domain/webBundleAnalysis.js +17 -9
  287. package/dist/domain/webBundleAnalyzer.js +1 -1
  288. package/dist/domain/webCaptureDiff.js +5 -11
  289. package/dist/domain/webCaptureDiffSchemas.js +2 -1
  290. package/dist/domain/webMcpDiscovery.js +2 -1
  291. package/dist/dotnet/ManagedMemberInspectorCore.js +15 -7
  292. package/dist/dotnet/ManagedMemberInstructionDecoder.js +57 -40
  293. package/dist/dotnet/ManagedMemberRows.js +2 -0
  294. package/dist/dotnet/ManagedMethodBodyReader.js +15 -8
  295. package/dist/dotnet/ManagedPeReader.js +2 -0
  296. package/dist/generatedMcpToolCatalog.js +1 -1
  297. package/dist/generatedPackageMetadata.js +2 -2
  298. package/dist/ghidra/GhidraAnalysisProfile.js +15 -5
  299. package/dist/ghidra/GhidraClient.js +38 -8
  300. package/dist/ghidra/GhidraFunctionValues.js +56 -20
  301. package/dist/ghidra/GhidraInstallation.js +7 -4
  302. package/dist/ghidra/GhidraInventoryValues.js +44 -9
  303. package/dist/ghidra/GhidraLauncher.js +18 -3
  304. package/dist/ghidra/GhidraProvider.js +23 -2
  305. package/dist/ghidra/GhidraRequestQueue.js +45 -4
  306. package/dist/ghidra/GhidraSessionValues.js +5 -0
  307. package/dist/hopper/HopperAnalysisProfile.js +2 -0
  308. package/dist/hopper/HopperProvider.js +29 -6
  309. package/dist/hopper/HopperTargetLease.js +18 -4
  310. package/dist/logger.js +0 -13
  311. package/dist/main/messages.js +0 -1
  312. package/dist/main/reload.js +3 -32
  313. package/dist/main/shutdown.js +1 -2
  314. package/dist/main/transport.js +0 -17
  315. package/dist/main.js +0 -10
  316. package/dist/native/AppleDispatchMetadata.js +582 -0
  317. package/dist/native/AppleObjcProtocols.js +88 -0
  318. package/dist/native/AppleSwiftVtables.js +86 -0
  319. package/dist/native/CommandRunner.js +1 -1
  320. package/dist/native/NativeHostCapabilities.js +23 -0
  321. package/dist/native/NativeMacOSProvider.js +61 -9
  322. package/dist/native/NativeMachoInspection.js +39 -6
  323. package/dist/native/NativeUiHelperRuntime.js +42 -0
  324. package/dist/native/NativeUiObservation.js +174 -0
  325. package/dist/native/parsers/codesign.js +4 -1
  326. package/dist/native/parsers/dyldInfo.js +51 -25
  327. package/dist/native/parsers/lipo.js +13 -4
  328. package/dist/native/parsers/otool.js +84 -39
  329. package/dist/native/parsers/plist.js +2 -1
  330. package/dist/process/ExecFileOutput.js +5 -1
  331. package/dist/process/ProcessOwnership.js +69 -33
  332. package/dist/process/ProcessOwnershipObservation.js +31 -12
  333. package/dist/process/ProviderProcess.js +4 -2
  334. package/dist/reference/ReferenceSourceReaderEntries.js +48 -31
  335. package/dist/reference/ReferenceSourceReaderErrors.js +24 -0
  336. package/dist/reference/ReferenceSourceReaderFile.js +11 -5
  337. package/dist/reference/ReferenceSourceReaderValidate.js +22 -8
  338. package/dist/server/createServer.js +7 -62
  339. package/dist/server/registerApplicationTools/helpers.js +2 -9
  340. package/dist/server/registerApplicationTools.js +0 -4
  341. package/dist/server/registerArtifactComparisonTool.js +1 -1
  342. package/dist/server/registerBrowserScenarioTool.js +4 -2
  343. package/dist/server/registerBrowserTools.js +7 -7
  344. package/dist/server/registerBundleComparisonTool.js +1 -1
  345. package/dist/server/registerCloseLifecycleTool.js +1 -1
  346. package/dist/server/registerElectronTools.js +10 -4
  347. package/dist/server/registerEnhancedTools.js +20 -12
  348. package/dist/server/registerEvidenceTools.js +4 -28
  349. package/dist/server/registerFunctionComparisonTool.js +1 -1
  350. package/dist/server/registerInvestigationTools.js +6 -6
  351. package/dist/server/registerJavaScriptRuntimeObservationTools.js +2 -2
  352. package/dist/server/registerManagedTools.js +28 -14
  353. package/dist/server/registerManagedWorkflowTools.js +0 -2
  354. package/dist/server/registerProcessComparisonTool.js +1 -3
  355. package/dist/server/registerSessionRecordTools.js +9 -9
  356. package/dist/server/registerSessionStatusTool.js +1 -1
  357. package/dist/server/registerSessionTools.js +28 -57
  358. package/dist/server/sessionAvailabilityPolicy.js +2 -3
  359. package/dist/server/sessionToolPolicies.js +0 -4
  360. package/dist/server/toolResult.js +8 -10
  361. package/package.json +32 -22
  362. package/skills/reverse-engineer-anything/SKILL.md +15 -11
  363. package/skills/reverse-engineer-anything/references/evidence-workflows.md +3 -2
  364. package/skills/reverse-engineer-anything/references/javascript-applications.md +3 -2
  365. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +8 -6
  366. package/skills/reverse-engineer-anything/references/runtime-observation.md +15 -11
  367. package/dist/application/CommandShimReplay.js +0 -152
  368. package/dist/application/ConfiguredRoots.js +0 -19
  369. package/dist/application/InstrumentedJavaScriptReplayHost.js +0 -36
  370. package/dist/application/JavaScriptReplayPermission.js +0 -16
  371. package/dist/application/JavaScriptReplayPlanning.js +0 -207
  372. package/dist/application/JavaScriptReplayService.js +0 -274
  373. package/dist/application/LoopbackReplay.js +0 -389
  374. package/dist/application/LoopbackReplayRecorder.js +0 -129
  375. package/dist/application/ManagedRuntimeCorrelationService.js +0 -104
  376. package/dist/application/NodeRuntimeCharacterizationService.js +0 -191
  377. package/dist/application/PermissionAuthority.js +0 -268
  378. package/dist/application/PermissionConfiguration.js +0 -26
  379. package/dist/application/PermissionFailure.js +0 -5
  380. package/dist/application/ProcessCaptureAuthority.js +0 -24
  381. package/dist/application/ProcessCapturePermission.js +0 -13
  382. package/dist/application/ProcessCheckpoints.js +0 -178
  383. package/dist/application/ProcessReactiveCoordinator.js +0 -223
  384. package/dist/application/ProcessReactiveEffects.js +0 -153
  385. package/dist/application/ProcessReactiveHarness.js +0 -106
  386. package/dist/application/ProcessReactiveObservations.js +0 -105
  387. package/dist/application/ProjectPermissionStore.js +0 -199
  388. package/dist/application/RuntimeExecutableDiagnostics.js +0 -198
  389. package/dist/application/Upgrade.js +0 -134
  390. package/dist/browser/SensitiveTextCapture.js +0 -8
  391. package/dist/cliPolicyCommands.js +0 -185
  392. package/dist/config/browserScenario.js +0 -77
  393. package/dist/config/electronAutomation.js +0 -35
  394. package/dist/config/javascriptReplay.js +0 -41
  395. package/dist/config/managedRuntime.js +0 -25
  396. package/dist/config/passiveObservation.js +0 -87
  397. package/dist/config/permissions.js +0 -23
  398. package/dist/config/processCapture.js +0 -47
  399. package/dist/contracts/replayMachineExample.js +0 -37
  400. package/dist/domain/javascriptExportInstrumentation.js +0 -95
  401. package/dist/domain/javascriptReplay.js +0 -360
  402. package/dist/domain/managedRuntimeCorrelation.js +0 -181
  403. package/dist/domain/nodeRuntimeCharacterization.js +0 -63
  404. package/dist/domain/permissionPolicy.js +0 -176
  405. package/dist/domain/processCaptureReactiveSchema.js +0 -68
  406. package/dist/domain/processCaptureReactiveValidation.js +0 -180
  407. package/dist/domain/processReactiveCheckpointDataflow.js +0 -32
  408. package/dist/domain/processReactiveMatching.js +0 -196
  409. package/dist/domain/processReactiveRuntime.js +0 -116
  410. package/dist/domain/processReactiveScenario.js +0 -296
  411. package/dist/domain/processReactiveScenarioPreflight.js +0 -95
  412. package/dist/domain/processReactiveTransition.js +0 -187
  413. package/dist/domain/replayMachine.js +0 -320
  414. package/dist/domain/replayMachineRun.js +0 -168
  415. package/dist/domain/replayMachineRuntime.js +0 -296
  416. package/dist/domain/replayMachineValues.js +0 -84
  417. package/dist/domain/runtimeCharacterization.js +0 -84
  418. package/dist/replay/JavaScriptReplayWorker.js +0 -382
  419. package/dist/replay/JavaScriptReplayWorkerTypes.js +0 -1
  420. package/dist/replay/LinuxJavaScriptReplayRunner.js +0 -216
  421. package/dist/replay/LinuxJavaScriptReplaySandbox.js +0 -121
  422. package/dist/replay/LinuxRuntimeClosure.js +0 -44
  423. package/dist/replay/LinuxSeccompPolicy.js +0 -40
  424. package/dist/replay/ReplayOutcome.js +0 -104
  425. package/dist/replay/ReplayProcessLifecycle.js +0 -96
  426. package/dist/replay/ReplaySeccompFile.js +0 -29
  427. package/dist/replay/ReplayWorkerProtocol.js +0 -58
  428. package/dist/replay/SystemJavaScriptReplayHost.js +0 -153
  429. package/dist/server/ProcessCaptureElicitation.js +0 -153
  430. package/dist/server/registerApplicationTools/characterization.js +0 -46
  431. package/dist/server/registerApplicationTools/controlledReplay.js +0 -31
  432. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +0 -29
  433. package/dist/server/registerReplayMachineTool.js +0 -14
  434. package/skills/reverse-engineer-anything/references/controlled-replay.md +0 -12
@@ -48,9 +48,13 @@ import ghidra.app.decompiler.DecompInterface;
48
48
  import ghidra.app.decompiler.DecompileOptions;
49
49
  import ghidra.app.decompiler.DecompileResults;
50
50
  import ghidra.app.decompiler.DecompiledFunction;
51
+ import ghidra.app.decompiler.ClangCaseToken;
52
+ import ghidra.app.decompiler.ClangNode;
51
53
  import ghidra.framework.Application;
52
54
  import ghidra.program.model.address.Address;
53
55
  import ghidra.program.model.address.AddressSpace;
56
+ import ghidra.program.model.address.AddressRange;
57
+ import ghidra.program.model.address.AddressRangeIterator;
54
58
  import ghidra.program.model.block.BasicBlockModel;
55
59
  import ghidra.program.model.block.CodeBlock;
56
60
  import ghidra.program.model.block.CodeBlockIterator;
@@ -58,6 +62,8 @@ import ghidra.program.model.block.CodeBlockReference;
58
62
  import ghidra.program.model.block.CodeBlockReferenceIterator;
59
63
  import ghidra.program.model.data.StringDataInstance;
60
64
  import ghidra.program.model.data.DataType;
65
+ import ghidra.program.model.lang.Register;
66
+ import ghidra.program.model.scalar.Scalar;
61
67
  import ghidra.program.model.listing.CommentType;
62
68
  import ghidra.program.model.listing.Data;
63
69
  import ghidra.program.model.listing.DataIterator;
@@ -75,10 +81,13 @@ import ghidra.program.model.symbol.Symbol;
75
81
  import ghidra.program.model.symbol.SymbolIterator;
76
82
  import ghidra.program.model.pcode.FunctionPrototype;
77
83
  import ghidra.program.model.pcode.HighFunction;
84
+ import ghidra.program.model.pcode.HighParam;
78
85
  import ghidra.program.model.pcode.HighSymbol;
79
86
  import ghidra.program.model.pcode.JumpTable;
80
87
  import ghidra.program.model.pcode.PcodeOp;
81
88
  import ghidra.program.model.pcode.PcodeOpAST;
89
+ import ghidra.program.model.pcode.PcodeBlock;
90
+ import ghidra.program.model.pcode.PcodeBlockBasic;
82
91
  import ghidra.program.model.pcode.Varnode;
83
92
  import ghidra.program.model.pcode.VarnodeAST;
84
93
 
@@ -122,6 +131,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
122
131
  "procedure_info",
123
132
  "procedure_pseudo_code",
124
133
  "read_function_instructions",
134
+ "inspect_native_instruction",
135
+ "inspect_native_data_type",
136
+ "resolve_native_call_targets",
125
137
  "procedure_references",
126
138
  "xrefs"
127
139
  };
@@ -314,6 +326,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
314
326
  case "resolve_containing_procedure" -> containingProcedure(request.params);
315
327
  case "search_procedures" -> search(request.params, true);
316
328
  case "search_strings" -> search(request.params, false);
329
+ case "inspect_native_data_type" -> inspectNativeDataType(request.params);
330
+ case "inspect_native_instruction" -> inspectNativeInstruction(request.params);
331
+ case "resolve_native_call_targets" -> resolveNativeCallTargets(request.params);
317
332
  case "xrefs" -> xrefs(request.params);
318
333
  case "analyze_function" -> analyzeFunction(request.params);
319
334
  default -> throw new RequestFailure(
@@ -363,6 +378,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
363
378
  decompiler.setOptions(options);
364
379
  decompiler.toggleCCode(true);
365
380
  decompiler.toggleSyntaxTree(true);
381
+ decompiler.toggleJumpLoads(true);
366
382
  if (!decompiler.openProgram(currentProgram)) {
367
383
  throw new IllegalStateException("Ghidra decompiler could not open the imported Program");
368
384
  }
@@ -431,7 +447,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
431
447
  result.addProperty("name", procedureName(function));
432
448
  result.addProperty("entrypoint", canonicalAddress(function.getEntryPoint()));
433
449
  result.addProperty("basicblock_count", basicBlocks(function).size());
434
- result.addProperty("length", function.getBody().getNumAddresses());
450
+ JsonObject body = functionBody(function);
451
+ result.addProperty("length", body.get("total_bytes").getAsLong());
452
+ result.add("body", body);
435
453
  result.addProperty("signature", function.getPrototypeString(false, true));
436
454
  result.add("locals", functionLocals(function));
437
455
  result.add("classification", functionClassification(function));
@@ -463,6 +481,224 @@ public final class ReaGhidraBridge extends HeadlessScript {
463
481
  result.add("procedure", procedureIdentity(function));
464
482
  result.addProperty("direction", direction);
465
483
  result.add("references", edges);
484
+ result.addProperty("reference_kinds_available", true);
485
+ JsonArray unresolvedCalls = new JsonArray();
486
+ if (direction.equals("outgoing")) for (Instruction instruction : scan.instructions) {
487
+ if (!instruction.getFlowType().isCall()) continue;
488
+ boolean resolved = false;
489
+ for (Reference reference : instruction.getReferencesFrom()) if (reference.getReferenceType().isCall()) resolved = true;
490
+ if (!resolved) {
491
+ JsonObject site = new JsonObject();
492
+ site.addProperty("address", canonicalAddress(instruction.getAddress()));
493
+ site.addProperty("reason", "Instruction decoder identifies a call but ReferenceManager supplies no call destination");
494
+ unresolvedCalls.add(site);
495
+ }
496
+ }
497
+ result.add("unresolved_calls", unresolvedCalls);
498
+ return result;
499
+ }
500
+
501
+ private JsonObject inspectNativeDataType(JsonObject params) throws Exception {
502
+ requireKeys(params, Set.of("document", "type", "address"));
503
+ requireDocument(params);
504
+ boolean byType = params.has("type") && !params.get("type").isJsonNull();
505
+ boolean byAddress = params.has("address") && !params.get("address").isJsonNull();
506
+ if (byType == byAddress) throw new RequestFailure("invalid_request", "Supply exactly one of type or address");
507
+ DataType type;
508
+ Address address = null;
509
+ if (byType) type = currentProgram.getDataTypeManager().getDataType(requireString(params, "type"));
510
+ else {
511
+ address = requireAddress(params, "address");
512
+ Data data = currentProgram.getListing().getDefinedDataAt(address);
513
+ type = data == null ? null : data.getDataType();
514
+ }
515
+ JsonObject result = new JsonObject();
516
+ result.addProperty("status", type == null ? "unavailable" : "available");
517
+ result.add("reason", type == null ? GSON.toJsonTree("No exact database type or defined typed data exists at the selected identity") : JsonNull.INSTANCE);
518
+ for (String key : List.of("id", "name", "source_archive", "size_bytes", "alignment_bytes", "packing_enabled", "referenced_type", "array_count", "array_stride_bytes")) result.add(key, JsonNull.INSTANCE);
519
+ result.add("address", address == null ? JsonNull.INSTANCE : GSON.toJsonTree(canonicalAddress(address)));
520
+ result.addProperty("source", "analysis-database");
521
+ result.addProperty("kind", "unavailable");
522
+ JsonArray fields = new JsonArray();
523
+ JsonArray members = new JsonArray();
524
+ int totalFields = 0;
525
+ if (type != null) {
526
+ result.addProperty("id", type.getPathName());
527
+ result.addProperty("name", type.getName());
528
+ if (type.getSourceArchive() != null) result.addProperty("source_archive", type.getSourceArchive().getName());
529
+ if (type.getLength() >= 0) result.addProperty("size_bytes", type.getLength());
530
+ if (type.getAlignment() > 0) result.addProperty("alignment_bytes", type.getAlignment());
531
+ String kind = type instanceof ghidra.program.model.data.Structure ? "struct" : type instanceof ghidra.program.model.data.Union ? "union" : type instanceof ghidra.program.model.data.Enum ? "enum" : type instanceof ghidra.program.model.data.Pointer ? "pointer" : type instanceof ghidra.program.model.data.Array ? "array" : type instanceof ghidra.program.model.data.TypeDef ? "typedef" : type instanceof ghidra.program.model.data.BuiltInDataType ? "scalar" : "unsupported";
532
+ result.addProperty("kind", kind);
533
+ if (type instanceof ghidra.program.model.data.Composite composite) {
534
+ result.addProperty("packing_enabled", composite.isPackingEnabled());
535
+ ghidra.program.model.data.DataTypeComponent[] components = composite.getComponents();
536
+ totalFields = components.length;
537
+ for (int index = 0; index < Math.min(components.length, 20_000); index++) {
538
+ monitor.checkCancelled();
539
+ ghidra.program.model.data.DataTypeComponent component = components[index];
540
+ JsonObject field = new JsonObject();
541
+ field.addProperty("ordinal", component.getOrdinal());
542
+ field.add("name", component.getFieldName() == null ? JsonNull.INSTANCE : GSON.toJsonTree(component.getFieldName()));
543
+ field.addProperty("offset_bytes", component.getOffset());
544
+ field.add("size_bytes", component.getLength() < 0 ? JsonNull.INSTANCE : GSON.toJsonTree(component.getLength()));
545
+ field.addProperty("type_id", component.getDataType().getPathName());
546
+ field.add("bit_size", JsonNull.INSTANCE);
547
+ field.add("bit_offset", JsonNull.INSTANCE);
548
+ if (component.getDataType() instanceof ghidra.program.model.data.BitFieldDataType bitfield) {
549
+ field.addProperty("bit_size", bitfield.getBitSize());
550
+ field.addProperty("bit_offset", bitfield.getBitOffset());
551
+ }
552
+ field.addProperty("status", "observed");
553
+ field.addProperty("flexible_tail", "unknown");
554
+ fields.add(field);
555
+ }
556
+ } else if (type instanceof ghidra.program.model.data.Enum enumeration) {
557
+ String[] names = enumeration.getNames();
558
+ totalFields = names.length;
559
+ java.util.Arrays.sort(names);
560
+ for (int index = 0; index < Math.min(names.length, 20_000); index++) {
561
+ monitor.checkCancelled();
562
+ JsonObject member = new JsonObject();
563
+ member.addProperty("name", names[index]);
564
+ member.addProperty("value", Long.toString(enumeration.getValue(names[index])));
565
+ members.add(member);
566
+ }
567
+ } else if (type instanceof ghidra.program.model.data.Pointer pointer) {
568
+ if (pointer.getDataType() != null) result.addProperty("referenced_type", pointer.getDataType().getPathName());
569
+ } else if (type instanceof ghidra.program.model.data.Array array) {
570
+ result.addProperty("referenced_type", array.getDataType().getPathName());
571
+ result.addProperty("array_count", array.getNumElements());
572
+ result.addProperty("array_stride_bytes", array.getElementLength());
573
+ } else if (type instanceof ghidra.program.model.data.TypeDef alias) {
574
+ result.addProperty("referenced_type", alias.getDataType().getPathName());
575
+ }
576
+ }
577
+ result.add("fields", fields);
578
+ result.add("members", members);
579
+ result.addProperty("total_fields", totalFields);
580
+ result.addProperty("truncated", totalFields > 20_000);
581
+ result.add("limitations", GSON.toJsonTree(List.of("Layouts are Ghidra data-type database observations; original debug/source authority and flexible-tail semantics are not inferred. Nested and recursive types retain category-path identities for separate inspection.", "At most 20000 fields or enum members are serialized per type to bound provider output. total_fields and truncated report omissions. Enum values preserve Ghidra's signed long representation.")));
582
+ return result;
583
+ }
584
+
585
+ private JsonObject instructionReference(Reference reference) {
586
+ JsonObject item = new JsonObject();
587
+ RefType type = reference.getReferenceType();
588
+ item.addProperty("target_address", canonicalAddress(reference.getToAddress()));
589
+ item.addProperty("type", type.getName());
590
+ item.addProperty("call", type.isCall());
591
+ item.addProperty("jump", type.isJump());
592
+ item.addProperty("indirect", type.isIndirect());
593
+ item.addProperty("computed", type.isComputed());
594
+ item.addProperty("operand_index", reference.getOperandIndex());
595
+ return item;
596
+ }
597
+
598
+ private JsonObject inspectNativeInstruction(JsonObject params) throws Exception {
599
+ requireKeys(params, Set.of("document", "address"));
600
+ requireDocument(params);
601
+ Address address = requireAddress(params, "address");
602
+ Instruction instruction = currentProgram.getListing().getInstructionAt(address);
603
+ Function function = currentProgram.getFunctionManager().getFunctionContaining(address);
604
+ JsonObject result = new JsonObject();
605
+ result.addProperty("address", canonicalAddress(address));
606
+ result.add("procedure", function == null ? JsonNull.INSTANCE : GSON.toJsonTree(canonicalAddress(function.getEntryPoint())));
607
+ result.addProperty("architecture", currentProgram.getLanguageID().getIdAsString());
608
+ result.addProperty("mode", currentProgram.getLanguage().getLanguageDescription().getVariant());
609
+ JsonArray operands = new JsonArray();
610
+ JsonArray references = new JsonArray();
611
+ JsonArray destinations = new JsonArray();
612
+ JsonObject flow = new JsonObject();
613
+ flow.addProperty("kind", "unavailable");
614
+ flow.addProperty("conditional", false);
615
+ flow.addProperty("computed", false);
616
+ if (instruction == null) {
617
+ result.addProperty("status", !currentProgram.getMemory().contains(address) ? "outside-memory" : currentProgram.getListing().getInstructionContaining(address) != null
618
+ ? "not-instruction-boundary" : currentProgram.getListing().getDefinedDataContaining(address) != null ? "data" : "undecodable");
619
+ for (String key : List.of("bytes", "length", "mnemonic", "raw_disassembly")) result.add(key, JsonNull.INSTANCE);
620
+ } else {
621
+ result.addProperty("status", "decoded");
622
+ result.addProperty("bytes", java.util.HexFormat.of().formatHex(instruction.getBytes()));
623
+ result.addProperty("length", instruction.getLength());
624
+ result.addProperty("mnemonic", instruction.getMnemonicString());
625
+ result.addProperty("raw_disassembly", instruction.toString());
626
+ RefType type = instruction.getFlowType();
627
+ flow.addProperty("kind", type.isCall() ? "call" : type.isJump() ? "jump" : type.isTerminal() ? "terminal" : "fallthrough");
628
+ flow.addProperty("conditional", type.isConditional());
629
+ flow.addProperty("computed", type.isComputed());
630
+ if (!type.isComputed()) for (Address target : instruction.getFlows()) destinations.add(canonicalAddress(target));
631
+ for (Reference reference : instruction.getReferencesFrom()) references.add(instructionReference(reference));
632
+ for (int index = 0; index < instruction.getNumOperands(); index++) {
633
+ JsonObject operand = new JsonObject();
634
+ operand.addProperty("index", index);
635
+ operand.addProperty("raw", instruction.getDefaultOperandRepresentation(index));
636
+ operand.addProperty("provider_type", instruction.getOperandType(index));
637
+ operand.addProperty("memory_addressing", "unavailable");
638
+ JsonArray components = new JsonArray();
639
+ for (Object object : instruction.getOpObjects(index)) {
640
+ JsonObject component = new JsonObject();
641
+ component.addProperty("text", object.toString());
642
+ component.add("value", JsonNull.INSTANCE);
643
+ component.add("bit_width", JsonNull.INSTANCE);
644
+ if (object instanceof Register register) {
645
+ component.addProperty("kind", "register");
646
+ component.addProperty("value", register.getName());
647
+ component.addProperty("bit_width", register.getBitLength());
648
+ } else if (object instanceof Scalar scalar) {
649
+ component.addProperty("kind", "immediate");
650
+ component.addProperty("value", "0x" + Long.toUnsignedString(scalar.getUnsignedValue(), 16));
651
+ component.addProperty("bit_width", scalar.bitLength());
652
+ } else if (object instanceof Address target) {
653
+ component.addProperty("kind", "address");
654
+ component.addProperty("value", canonicalAddress(target));
655
+ } else component.addProperty("kind", "unknown");
656
+ components.add(component);
657
+ }
658
+ operand.add("components", components);
659
+ operands.add(operand);
660
+ }
661
+ }
662
+ flow.add("direct_destinations", destinations);
663
+ result.add("operands", operands);
664
+ result.add("flow", flow);
665
+ result.add("references", references);
666
+ result.add("limitations", GSON.toJsonTree(List.of("Operand components are Ghidra Listing decoder tokens; effective memory base/index/displacement roles and per-instruction context mode are unavailable. The mode field is the program language variant.", "Terminal flow is not classified as a return without provider evidence.")));
667
+ return result;
668
+ }
669
+
670
+ private JsonObject resolveNativeCallTargets(JsonObject params) throws Exception {
671
+ JsonObject instruction = inspectNativeInstruction(params);
672
+ JsonObject result = new JsonObject();
673
+ result.add("call_site", instruction.get("address"));
674
+ result.add("procedure", instruction.get("procedure"));
675
+ JsonArray targets = new JsonArray();
676
+ JsonObject flow = instruction.getAsJsonObject("flow");
677
+ boolean decoded = instruction.get("status").getAsString().equals("decoded");
678
+ boolean call = flow.get("kind").getAsString().equals("call");
679
+ boolean computed = flow.get("computed").getAsBoolean();
680
+ Map<String, JsonArray> byTarget = new TreeMap<>();
681
+ if (call) for (JsonElement element : instruction.getAsJsonArray("references")) {
682
+ JsonObject reference = element.getAsJsonObject();
683
+ if (reference.get("call").getAsBoolean())
684
+ byTarget.computeIfAbsent(reference.get("target_address").getAsString(), ignored -> new JsonArray()).add(reference);
685
+ }
686
+ boolean ambiguous = byTarget.size() > 1;
687
+ for (Map.Entry<String, JsonArray> entry : byTarget.entrySet()) {
688
+ JsonObject target = new JsonObject();
689
+ target.addProperty("address", entry.getKey());
690
+ Address address = tryParseAddress(entry.getKey());
691
+ Function function = address == null ? null : currentProgram.getFunctionManager().getFunctionAt(address);
692
+ target.add("procedure", function == null ? JsonNull.INSTANCE : GSON.toJsonTree(canonicalAddress(function.getEntryPoint())));
693
+ target.addProperty("status", ambiguous ? "candidate" : computed ? "resolved-indirect" : "direct");
694
+ target.addProperty("basis", "provider-reference");
695
+ target.add("references", entry.getValue());
696
+ targets.add(target);
697
+ }
698
+ result.addProperty("status", !decoded ? "unavailable" : !call ? "not-call" : ambiguous ? "ambiguous" : targets.size() == 0 ? "unresolved" : computed ? "resolved-indirect" : "direct");
699
+ result.addProperty("mechanism", !call ? "unavailable" : computed ? "computed" : "direct");
700
+ result.add("targets", targets);
701
+ result.add("limitations", GSON.toJsonTree(List.of("Targets are static Ghidra ReferenceManager call-reference observations. Computed calls without call references remain unresolved; a resolved reference is not proof of runtime execution.", "Objective-C, Swift, vtable and closure mechanisms are not identified by this reference-only boundary.")));
466
702
  return result;
467
703
  }
468
704
 
@@ -547,6 +783,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
547
783
  limitations.add(
548
784
  "Synthetic Ghidra entry-point references without actionable memory sources are omitted."
549
785
  );
786
+ if (decompilation != null && decompilation.getErrorMessage() != null &&
787
+ !decompilation.getErrorMessage().isBlank()) {
788
+ limitations.add("Ghidra decompiler diagnostic: " + decompilation.getErrorMessage());
789
+ }
550
790
  result.add("limitations", limitations);
551
791
  return result;
552
792
  }
@@ -586,6 +826,18 @@ public final class ReaGhidraBridge extends HeadlessScript {
586
826
  for (PcodeOpAST operation : selected) {
587
827
  operationIds.put(operation, pcodeOperationId(operation));
588
828
  }
829
+ JsonArray parameterUses = new JsonArray();
830
+ JsonArray parameterValues = new JsonArray();
831
+ FunctionPrototype prototype = highFunction.getFunctionPrototype();
832
+ for (int ordinal = 0; ordinal < prototype.getNumParams(); ordinal++) {
833
+ HighSymbol parameter = prototype.getParam(ordinal);
834
+ if (parameter == null) continue;
835
+ JsonObject value = new JsonObject();
836
+ value.addProperty("ordinal", ordinal);
837
+ value.addProperty("name", parameter.getName());
838
+ value.addProperty("data_type", parameter.getDataType().getPathName());
839
+ parameterValues.add(value);
840
+ }
589
841
  JsonArray operationValues = new JsonArray();
590
842
  JsonArray defUse = new JsonArray();
591
843
  JsonArray effects = new JsonArray();
@@ -603,6 +855,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
603
855
  value.addProperty("sequence", operation.getSeqnum().getTime());
604
856
  value.addProperty("opcode", operation.getMnemonic());
605
857
  value.addProperty("is_dead", operation.isDead());
858
+ value.addProperty("block_membership", operation.getParent() == null ? "detached" : "member");
606
859
  JsonArray inputs = new JsonArray();
607
860
  int inputCount = operation.getNumInputs();
608
861
  int capturedInputs = Math.min(inputCount, MAX_HIGH_PCODE_INPUTS_PER_OP);
@@ -610,6 +863,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
610
863
  Varnode input = operation.getInput(inputIndex);
611
864
  inputs.add(varnodeValue(input));
612
865
  if (input instanceof VarnodeAST astInput) {
866
+ if (astInput.getHigh() instanceof HighParam parameter) {
867
+ if (parameterUses.size() >= MAX_HIGH_PCODE_DEF_USE_EDGES) omittedEdges++;
868
+ else {
869
+ JsonObject use = new JsonObject();
870
+ use.addProperty("ordinal", parameter.getSlot());
871
+ use.addProperty("use", operationId);
872
+ use.addProperty("input_index", inputIndex);
873
+ parameterUses.add(use);
874
+ }
875
+ }
613
876
  PcodeOp definition = astInput.getDef();
614
877
  if (definition instanceof PcodeOpAST astDefinition) {
615
878
  String definitionId = operationIds.get(astDefinition);
@@ -638,6 +901,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
638
901
  result.addProperty("provenance", "ghidra-high-pcode");
639
902
  result.add("operations", operationValues);
640
903
  result.add("def_use", defUse);
904
+ result.add("parameters", parameterValues);
905
+ result.add("parameter_uses", parameterUses);
641
906
  result.add("effects", effects);
642
907
  boolean truncated = omittedOperationsLowerBound > 0 ||
643
908
  omittedInputs > 0 || omittedEdges > 0;
@@ -650,7 +915,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
650
915
  result.addProperty("known_omitted_edges", omittedEdges);
651
916
  JsonArray limitations = new JsonArray();
652
917
  limitations.add(
653
- "High p-code is a decompiler-derived intra-function representation, not original source or runtime behavior; dead operations are retained and marked."
918
+ "High p-code is decompiler-derived, not original source or runtime behavior. is_dead preserves the Java AST flag; decoded block members can retain that flag, so block_membership separately reports actual syntax-tree membership."
654
919
  );
655
920
  limitations.add(
656
921
  "Def-use links do not cross function boundaries and may omit memory aliasing, call side effects, or unresolved indirect flow."
@@ -1094,8 +1359,12 @@ public final class ReaGhidraBridge extends HeadlessScript {
1094
1359
  }
1095
1360
 
1096
1361
  JumpTable[] recoveredJumpTables = highFunction.getJumpTables();
1362
+ List<ClangNode> caseTokens = new ArrayList<>();
1363
+ if (decompilation.getCCodeMarkup() != null) {
1364
+ decompilation.getCCodeMarkup().flatten(caseTokens);
1365
+ }
1097
1366
  for (int index = 0; index < recoveredJumpTables.length; index += 1) {
1098
- jumpTables.add(inferredJumpTable(recoveredJumpTables[index]));
1367
+ jumpTables.add(inferredJumpTable(recoveredJumpTables[index], caseTokens));
1099
1368
  }
1100
1369
  return result;
1101
1370
  }
@@ -1191,16 +1460,19 @@ public final class ReaGhidraBridge extends HeadlessScript {
1191
1460
  return result;
1192
1461
  }
1193
1462
 
1194
- private JsonObject inferredJumpTable(JumpTable jumpTable) throws Exception {
1463
+ private JsonObject inferredJumpTable(
1464
+ JumpTable jumpTable,
1465
+ List<ClangNode> caseTokens
1466
+ ) throws Exception {
1195
1467
  JsonObject result = new JsonObject();
1196
1468
  result.addProperty(
1197
1469
  "dispatch_address",
1198
1470
  canonicalAddress(jumpTable.getSwitchAddress())
1199
1471
  );
1200
1472
  JumpTable.LoadTable[] loadTables = jumpTable.getLoadTables();
1201
- RecoveredAArch64ByteTable recoveredAArch64 =
1473
+ RecoveredAArch64RelativeTable recoveredAArch64 =
1202
1474
  loadTables.length == 0
1203
- ? recoverAArch64ByteTable(jumpTable)
1475
+ ? recoverAArch64RelativeTable(jumpTable)
1204
1476
  : null;
1205
1477
  List<Address> referencedDataAddresses = loadTables.length == 0
1206
1478
  ? dispatchPathDataReferences(jumpTable)
@@ -1241,8 +1513,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
1241
1513
  "address",
1242
1514
  canonicalAddress(recoveredAArch64.tableAddress)
1243
1515
  );
1244
- source.addProperty("provenance", "ghidra-aarch64-byte-table");
1245
- source.addProperty("entry_size_bytes", 1);
1516
+ source.addProperty("provenance", recoveredAArch64.entrySize == 1 ? "ghidra-aarch64-byte-table" : "ghidra-aarch64-halfword-table");
1517
+ source.addProperty("entry_size_bytes", recoveredAArch64.entrySize);
1246
1518
  source.addProperty("entry_count", recoveredAArch64.mappings.size());
1247
1519
  source.addProperty("confidence", "high");
1248
1520
  JsonArray evidence = new JsonArray();
@@ -1251,8 +1523,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
1251
1523
  "jump-table",
1252
1524
  "ghidra-instruction-and-memory",
1253
1525
  "Decoded " + recoveredAArch64.mappings.size() +
1254
- " byte entries from " + canonicalAddress(recoveredAArch64.tableAddress) +
1255
- "; the AArch64 LDRB/ADR/ADD/BR chain uses a selector guarded by an unsigned upper-bound branch."
1526
+ " entries of size " + recoveredAArch64.entrySize + " bytes from " + canonicalAddress(recoveredAArch64.tableAddress) +
1527
+ "; the AArch64 LDRB-or-LDRH/ADR/ADD/BR chain uses a selector guarded by an unsigned upper-bound branch."
1256
1528
  )
1257
1529
  );
1258
1530
  source.add("evidence", evidence);
@@ -1290,13 +1562,33 @@ public final class ReaGhidraBridge extends HeadlessScript {
1290
1562
  }
1291
1563
  result.add("data_sources", dataSources);
1292
1564
  Address[] targets = jumpTable.getCases();
1293
- Integer[] labels = jumpTable.getLabelValues();
1294
- boolean labelsAligned = labels != null && labels.length == targets.length;
1565
+ TypedJumpTableCases typedCases = typedJumpTableCases(jumpTable, caseTokens);
1295
1566
  JsonArray mappings = new JsonArray();
1567
+ JsonArray defaultTargets = new JsonArray();
1568
+ for (Address target : typedCases.defaults) {
1569
+ defaultTargets.add(typedJumpTableTarget(target, jumpTable, "Typed default label"));
1570
+ }
1571
+ Set<Address> coveredTargets = new HashSet<>(typedCases.cases.values());
1572
+ coveredTargets.addAll(typedCases.defaults);
1296
1573
  if (recoveredAArch64 != null) {
1297
1574
  for (int index = 0; index < recoveredAArch64.mappings.size(); index += 1) {
1298
1575
  monitor.checkCancelled();
1299
1576
  RecoveredAArch64Mapping recovered = recoveredAArch64.mappings.get(index);
1577
+ if (
1578
+ typedCases.unknownTargets.contains(recovered.targetAddress) ||
1579
+ typedCases.ambiguousValues.contains((long) recovered.caseValue)
1580
+ ) continue;
1581
+ Address typedTarget = typedCases.cases.get((long) recovered.caseValue);
1582
+ if (typedTarget != null && !typedTarget.equals(recovered.targetAddress)) {
1583
+ typedCases.cases.remove((long) recovered.caseValue);
1584
+ typedCases.ambiguousValues.add((long) recovered.caseValue);
1585
+ typedCases.unknownTargets.add(typedTarget);
1586
+ typedCases.unknownTargets.add(recovered.targetAddress);
1587
+ typedCases.limitations.add(
1588
+ "Typed case evidence and the verified relative table disagree on a destination; both destinations remain unknown for that case."
1589
+ );
1590
+ continue;
1591
+ }
1300
1592
  JsonObject mapping = new JsonObject();
1301
1593
  mapping.addProperty("case_value", recovered.caseValue);
1302
1594
  String targetAddress = canonicalAddress(recovered.targetAddress);
@@ -1307,64 +1599,67 @@ public final class ReaGhidraBridge extends HeadlessScript {
1307
1599
  inferenceEvidence(
1308
1600
  "jump-table",
1309
1601
  "ghidra-instruction-and-memory",
1310
- "Case value " + recovered.caseValue + " indexes byte " +
1311
- recovered.caseValue + " at " + canonicalAddress(recoveredAArch64.tableAddress) +
1312
- "; the loaded byte selects a four-byte instruction offset from " +
1602
+ "Case value " + recovered.caseValue + (recoveredAArch64.entrySize == 1 ? " indexes byte " : " indexes halfword at byte offset ") +
1603
+ (recovered.caseValue * recoveredAArch64.entrySize) + " at " + canonicalAddress(recoveredAArch64.tableAddress) +
1604
+ "; the loaded unsigned entry selects a four-byte instruction offset from " +
1313
1605
  canonicalAddress(recoveredAArch64.branchBase) + ", reaching " + targetAddress + "."
1314
1606
  )
1315
1607
  );
1316
1608
  mapping.add("evidence", evidence);
1317
- mappings.add(mapping);
1609
+ if (!typedCases.cases.containsKey((long) recovered.caseValue)) {
1610
+ mappings.add(mapping);
1611
+ }
1612
+ coveredTargets.add(recovered.targetAddress);
1318
1613
  }
1319
1614
  }
1320
- else {
1615
+ for (Map.Entry<Long, Address> entry : typedCases.cases.entrySet()) {
1616
+ JsonObject mapping = typedJumpTableTarget(
1617
+ entry.getValue(),
1618
+ jumpTable,
1619
+ "Typed case value " + entry.getKey()
1620
+ );
1621
+ mapping.addProperty("case_value", entry.getKey());
1622
+ mappings.add(mapping);
1623
+ }
1624
+ Set<Address> emittedUnknownTargets = new HashSet<>();
1321
1625
  for (int index = 0; index < targets.length; index += 1) {
1322
1626
  monitor.checkCancelled();
1627
+ boolean unresolvedToken = typedCases.unknownTargets.contains(targets[index]);
1628
+ if (coveredTargets.contains(targets[index]) && !unresolvedToken) continue;
1629
+ if (!emittedUnknownTargets.add(targets[index])) continue;
1323
1630
  JsonObject mapping = new JsonObject();
1324
- if (!labelsAligned || labels[index] == null) {
1325
- mapping.add("case_value", JsonNull.INSTANCE);
1326
- }
1327
- else {
1328
- mapping.addProperty("case_value", labels[index]);
1329
- }
1631
+ mapping.add("case_value", JsonNull.INSTANCE);
1330
1632
  String targetAddress = canonicalAddress(targets[index]);
1331
1633
  mapping.addProperty("target_address", targetAddress);
1332
1634
  // Ghidra's recovered case-to-target relation does not identify
1333
1635
  // which backing load table produced an individual case. Keep the
1334
1636
  // table inventory and recovered mappings separate instead of
1335
1637
  // attaching every table address to every target.
1336
- mapping.addProperty(
1337
- "confidence",
1338
- labelsAligned && labels[index] != null
1339
- ? "high"
1340
- : "medium"
1341
- );
1638
+ mapping.addProperty("confidence", "medium");
1342
1639
  JsonArray evidence = new JsonArray();
1343
1640
  evidence.add(
1344
1641
  inferenceEvidence(
1345
1642
  "jump-table",
1346
1643
  "ghidra-high-function",
1347
- labelsAligned && labels[index] != null
1348
- ? "Ghidra paired case value " +
1349
- labels[index] +
1350
- " with target " +
1351
- targetAddress +
1352
- " at dispatch " +
1353
- canonicalAddress(jumpTable.getSwitchAddress()) +
1354
- "."
1355
- : "Ghidra recovered target " +
1644
+ "Ghidra recovered target " +
1356
1645
  targetAddress +
1357
1646
  " at dispatch " +
1358
1647
  canonicalAddress(jumpTable.getSwitchAddress()) +
1359
- ", but no aligned case value was available."
1648
+ ", but no unambiguous, exactly representable case label or default relationship was available."
1360
1649
  )
1361
1650
  );
1362
1651
  mapping.add("evidence", evidence);
1363
1652
  mappings.add(mapping);
1364
1653
  }
1365
- }
1366
1654
  result.add("mappings", mappings);
1655
+ result.add("default_targets", defaultTargets);
1367
1656
  JsonArray limitations = new JsonArray();
1657
+ for (String limitation : typedCases.limitations) limitations.add(limitation);
1658
+ if (defaultTargets.isEmpty()) {
1659
+ limitations.add(
1660
+ "Ghidra exposed no typed default label with an unambiguous relationship to a recovered destination of this dispatch; default or out-of-range handling remains unknown."
1661
+ );
1662
+ }
1368
1663
  if (loadTables.length == 0 && referencedDataAddresses.isEmpty()) {
1369
1664
  limitations.add(
1370
1665
  "Ghidra recovered targets but exposed no backing load-table or dispatch-block data address."
@@ -1379,11 +1674,14 @@ public final class ReaGhidraBridge extends HeadlessScript {
1379
1674
  "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."
1380
1675
  );
1381
1676
  }
1382
- if (recoveredAArch64 == null && !labelsAligned) {
1677
+ if (!emittedUnknownTargets.isEmpty()) {
1383
1678
  limitations.add(
1384
- "Case-label and target counts differ or labels are unavailable; all case values are left unknown because index alignment cannot be established."
1679
+ "Some recovered destinations lack an unambiguous, exactly representable case/default relationship; their case values remain unknown. Unequal label and destination arrays are never paired by index."
1385
1680
  );
1386
1681
  }
1682
+ if (recoveredAArch64 == null && loadTables.length == 0 && currentProgram.getLanguageID().getIdAsString().startsWith("AARCH64:")) {
1683
+ limitations.add("REA did not verify a supported byte/halfword relative dispatch chain with exact unsigned bounds and register definitions; unsupported encodings and ambiguous bounds remain unknown.");
1684
+ }
1387
1685
  if (targets.length == 0) {
1388
1686
  limitations.add(
1389
1687
  "Ghidra exposed a jump-table dispatch without recovered case targets."
@@ -1393,6 +1691,124 @@ public final class ReaGhidraBridge extends HeadlessScript {
1393
1691
  return result;
1394
1692
  }
1395
1693
 
1694
+ private static final class TypedJumpTableCases {
1695
+ private final Map<Long, Address> cases = new TreeMap<>();
1696
+ private final Set<Address> defaults = new java.util.TreeSet<>();
1697
+ private final Set<Address> unknownTargets = new HashSet<>();
1698
+ private final Set<Long> ambiguousValues = new HashSet<>();
1699
+ private final Set<String> limitations = new java.util.LinkedHashSet<>();
1700
+ }
1701
+
1702
+ private TypedJumpTableCases typedJumpTableCases(
1703
+ JumpTable jumpTable,
1704
+ List<ClangNode> tokens
1705
+ ) throws Exception {
1706
+ TypedJumpTableCases result = new TypedJumpTableCases();
1707
+ Set<Address> targets = new HashSet<>();
1708
+ for (Address target : jumpTable.getCases()) targets.add(target);
1709
+ for (ClangNode node : tokens) {
1710
+ monitor.checkCancelled();
1711
+ if (!(node instanceof ClangCaseToken token)) continue;
1712
+ PcodeOp operation = token.getPcodeOp();
1713
+ if (operation == null || operation.getParent() == null) continue;
1714
+ PcodeBlockBasic block = operation.getParent();
1715
+ Address target = block.getStart();
1716
+ if (!targets.contains(target)) continue;
1717
+ PcodeOp dispatch = null;
1718
+ boolean ambiguousDispatch = false;
1719
+ boolean includesDispatch = false;
1720
+ for (int index = 0; index < block.getInSize(); index += 1) {
1721
+ PcodeBlock predecessor = block.getIn(index);
1722
+ if (!(predecessor instanceof PcodeBlockBasic basic)) continue;
1723
+ PcodeOp last = basic.getLastOp();
1724
+ if (last == null || last.getOpcode() != PcodeOp.BRANCHIND) continue;
1725
+ if (last.getSeqnum().getTarget().equals(jumpTable.getSwitchAddress())) {
1726
+ includesDispatch = true;
1727
+ }
1728
+ if (dispatch != null && dispatch != last) ambiguousDispatch = true;
1729
+ dispatch = last;
1730
+ }
1731
+ if (!includesDispatch) continue;
1732
+ if (ambiguousDispatch) {
1733
+ result.unknownTargets.add(target);
1734
+ result.limitations.add(
1735
+ "A typed case/default block has multiple indirect dispatch predecessors; REA cannot attribute its label to one switch."
1736
+ );
1737
+ continue;
1738
+ }
1739
+ // Default is a typed label role, not an OFF sentinel: Ghidra can
1740
+ // encode a default token with an ordinary in-range case value.
1741
+ if ("default".equals(token.getText())) {
1742
+ result.defaults.add(target);
1743
+ continue;
1744
+ }
1745
+ Long value = exactTypedCaseValue(token);
1746
+ if (value == null) {
1747
+ result.unknownTargets.add(target);
1748
+ result.limitations.add(
1749
+ "Typed case label " + token.getText() +
1750
+ " has encoded unsigned magnitude " + Long.toUnsignedString(token.getValue()) +
1751
+ " at recovered target " + canonicalAddress(target) +
1752
+ " for dispatch " + canonicalAddress(jumpTable.getSwitchAddress()) +
1753
+ "; the label is nonliteral, inconsistent with its encoded magnitude, or outside the exact JSON integer range, so its numeric case value remains unknown."
1754
+ );
1755
+ continue;
1756
+ }
1757
+ if (result.ambiguousValues.contains(value)) {
1758
+ result.unknownTargets.add(target);
1759
+ continue;
1760
+ }
1761
+ Address previous = result.cases.putIfAbsent(value, target);
1762
+ if (previous != null && !previous.equals(target)) {
1763
+ result.cases.remove(value);
1764
+ result.ambiguousValues.add(value);
1765
+ result.unknownTargets.add(previous);
1766
+ result.unknownTargets.add(target);
1767
+ result.limitations.add(
1768
+ "Typed tokens assign one case value to different recovered destinations; that case remains unknown."
1769
+ );
1770
+ }
1771
+ }
1772
+ return result;
1773
+ }
1774
+
1775
+ private static Long exactTypedCaseValue(ClangCaseToken token) {
1776
+ String text = token.getText();
1777
+ if (text == null || !text.matches("-?(?:0[xX][0-9a-fA-F]+|[0-9]+)")) return null;
1778
+ boolean negative = text.startsWith("-");
1779
+ String literal = negative ? text.substring(1) : text;
1780
+ BigInteger magnitude = literal.startsWith("0x") || literal.startsWith("0X")
1781
+ ? new BigInteger(literal.substring(2), 16)
1782
+ : new BigInteger(literal, 10);
1783
+ // Negative numeric tokens carry an unsigned magnitude in OFF, so
1784
+ // getValue()/getScalar() alone would incorrectly emit positive cases.
1785
+ if (!magnitude.equals(new BigInteger(Long.toUnsignedString(token.getValue())))) return null;
1786
+ BigInteger value = negative ? magnitude.negate() : magnitude;
1787
+ if (value.abs().compareTo(BigInteger.valueOf(9007199254740991L)) > 0) return null;
1788
+ return value.longValueExact();
1789
+ }
1790
+
1791
+ private static JsonObject typedJumpTableTarget(
1792
+ Address target,
1793
+ JumpTable table,
1794
+ String label
1795
+ ) {
1796
+ JsonObject result = new JsonObject();
1797
+ String address = canonicalAddress(target);
1798
+ result.addProperty("target_address", address);
1799
+ result.addProperty("confidence", "high");
1800
+ JsonArray evidence = new JsonArray();
1801
+ evidence.add(inferenceEvidence(
1802
+ "jump-table",
1803
+ "ghidra-clang-case-token",
1804
+ label + " belongs to the p-code block starting at recovered target " +
1805
+ address + "; its unique indirect dispatch predecessor is " +
1806
+ canonicalAddress(table.getSwitchAddress()) + "."
1807
+ ));
1808
+ result.add("evidence", evidence);
1809
+ return result;
1810
+ }
1811
+
1396
1812
  private static final class RecoveredAArch64Mapping {
1397
1813
  private final int caseValue;
1398
1814
  private final Address targetAddress;
@@ -1403,27 +1819,30 @@ public final class ReaGhidraBridge extends HeadlessScript {
1403
1819
  }
1404
1820
  }
1405
1821
 
1406
- private static final class RecoveredAArch64ByteTable {
1822
+ private static final class RecoveredAArch64RelativeTable {
1407
1823
  private final Address tableAddress;
1408
1824
  private final Address branchBase;
1825
+ private final int entrySize;
1409
1826
  private final List<RecoveredAArch64Mapping> mappings;
1410
1827
 
1411
- private RecoveredAArch64ByteTable(
1828
+ private RecoveredAArch64RelativeTable(
1412
1829
  Address tableAddress,
1413
1830
  Address branchBase,
1831
+ int entrySize,
1414
1832
  List<RecoveredAArch64Mapping> mappings
1415
1833
  ) {
1416
1834
  this.tableAddress = tableAddress;
1417
1835
  this.branchBase = branchBase;
1836
+ this.entrySize = entrySize;
1418
1837
  this.mappings = mappings;
1419
1838
  }
1420
1839
  }
1421
1840
 
1422
1841
  /**
1423
- * Recover only the directly verified AArch64 byte-indexed relative branch
1842
+ * Recover only the directly verified AArch64 byte/halfword-indexed relative branch
1424
1843
  * table form. Other instruction sequences keep their case labels unknown.
1425
1844
  */
1426
- private RecoveredAArch64ByteTable recoverAArch64ByteTable(
1845
+ private RecoveredAArch64RelativeTable recoverAArch64RelativeTable(
1427
1846
  JumpTable jumpTable
1428
1847
  ) throws Exception {
1429
1848
  if (!currentProgram.getLanguageID().getIdAsString().startsWith("AARCH64:")) {
@@ -1452,31 +1871,25 @@ public final class ReaGhidraBridge extends HeadlessScript {
1452
1871
  Set<String> recoveredTargetSet = new HashSet<>();
1453
1872
  for (Address target : recoveredTargets) recoveredTargetSet.add(canonicalAddress(target));
1454
1873
  List<Address> dataAddresses = dispatchPathDataReferences(jumpTable);
1455
- if (dataAddresses.isEmpty()) return null;
1456
1874
 
1457
1875
  for (int loadIndex = 0; loadIndex < branchIndex; loadIndex += 1) {
1458
1876
  Instruction load = instructions.get(loadIndex);
1459
- if (!load.getMnemonicString().equalsIgnoreCase("LDRB")) continue;
1877
+ int entrySize = load.getMnemonicString().equalsIgnoreCase("LDRB") ? 1 : load.getMnemonicString().equalsIgnoreCase("LDRH") ? 2 : 0;
1878
+ if (entrySize == 0 || branchIndex != loadIndex + 2 || loadIndex < 3 ||
1879
+ !instructions.get(loadIndex - 1).getMnemonicString().equalsIgnoreCase("ADR") ||
1880
+ !instructions.get(loadIndex - 2).getMnemonicString().equalsIgnoreCase("ADD") ||
1881
+ !instructions.get(loadIndex - 3).getMnemonicString().equalsIgnoreCase("ADRP")) continue;
1460
1882
  String loadOperands = operands(load);
1461
1883
  java.util.regex.Matcher loadMatcher = java.util.regex.Pattern
1462
- .compile("(?i)^[wx]\\d+,\\s*\\[([wx]\\d+),\\s*([wx]\\d+)\\]$")
1884
+ .compile(entrySize == 1 ? "(?i)^[wx]\\d+,\\s*\\[([wx]\\d+),\\s*([wx]\\d+)\\]$" : "(?i)^[wx]\\d+,\\s*\\[([wx]\\d+),\\s*([wx]\\d+),\\s*LSL\\s*#(?:0x)?1\\]$")
1463
1885
  .matcher(loadOperands);
1464
1886
  if (!loadMatcher.matches()) continue;
1465
1887
  String loadedRegister = normalizedRegister(loadOperands.substring(0, loadOperands.indexOf(',')));
1466
1888
  String tableBaseRegister = normalizedRegister(loadMatcher.group(1));
1467
1889
  String indexRegister = normalizedRegister(loadMatcher.group(2));
1468
1890
 
1469
- Address tableAddress = null;
1470
- for (Address candidate : dataAddresses) {
1471
- if (currentProgram.getMemory().contains(candidate)) {
1472
- tableAddress = candidate;
1473
- break;
1474
- }
1475
- }
1476
- if (tableAddress == null) continue;
1477
-
1478
1891
  Address resolvedTableAddress = null;
1479
- for (int index = 0; index < loadIndex; index += 1) {
1892
+ for (int index = loadIndex - 3; index < loadIndex - 1; index += 1) {
1480
1893
  Instruction instruction = instructions.get(index);
1481
1894
  String mnemonic = instruction.getMnemonicString().toUpperCase(Locale.ROOT);
1482
1895
  String text = operands(instruction);
@@ -1496,18 +1909,20 @@ public final class ReaGhidraBridge extends HeadlessScript {
1496
1909
  }
1497
1910
  }
1498
1911
  }
1499
- if (!tableAddress.equals(resolvedTableAddress)) continue;
1912
+ if (resolvedTableAddress == null || !currentProgram.getMemory().contains(resolvedTableAddress) ||
1913
+ (!dataAddresses.isEmpty() && !dataAddresses.contains(resolvedTableAddress))) continue;
1914
+ Address tableAddress = resolvedTableAddress;
1500
1915
 
1501
1916
  Address branchBase = null;
1502
1917
  boolean addressChain = false;
1503
- for (int index = 0; index < branchIndex; index += 1) {
1918
+ for (int index = loadIndex - 1; index < branchIndex; index += 1) {
1504
1919
  Instruction instruction = instructions.get(index);
1505
1920
  String mnemonic = instruction.getMnemonicString().toUpperCase(Locale.ROOT);
1506
1921
  String text = operands(instruction);
1507
1922
  if (index < loadIndex && mnemonic.equals("ADR") && text.matches("(?i)^[wx]\\d+,\\s*0x[0-9a-f]+$")) {
1508
1923
  int comma = text.indexOf(',');
1509
1924
  String destination = normalizedRegister(text.substring(0, comma));
1510
- if (destination.equals(tableBaseRegister)) continue;
1925
+ if (!destination.equals(normalizedRegister(operands(instructions.get(branchIndex))))) continue;
1511
1926
  branchBase = addressFromOperand(text.substring(comma + 1));
1512
1927
  }
1513
1928
  if (index > loadIndex && mnemonic.equals("ADD") && branchBase != null &&
@@ -1543,7 +1958,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
1543
1958
  if (!mnemonic.equals("B.HI")) continue;
1544
1959
  Address[] flows = guard.getFlows();
1545
1960
  if (flows.length != 1 || !recoveredTargetSet.contains(canonicalAddress(flows[0]))) continue;
1546
- boolean selectorFeedsTable = indexRegister.equals(comparedRegister);
1547
1961
  boolean supportedSequence = true;
1548
1962
  for (int sequenceIndex = compareIndex + 1; sequenceIndex < loadIndex; sequenceIndex += 1) {
1549
1963
  String sequenceMnemonic = instructions.get(sequenceIndex)
@@ -1553,17 +1967,31 @@ public final class ReaGhidraBridge extends HeadlessScript {
1553
1967
  break;
1554
1968
  }
1555
1969
  }
1556
- for (int moveIndex = compareIndex + 1; moveIndex < loadIndex && !selectorFeedsTable; moveIndex += 1) {
1557
- Instruction move = instructions.get(moveIndex);
1558
- if (!move.getMnemonicString().equalsIgnoreCase("MOV")) continue;
1559
- String[] moveParts = operands(move).split(",");
1560
- if (moveParts.length == 2 &&
1561
- normalizedRegister(moveParts[0]).equals(indexRegister) &&
1562
- normalizedRegister(moveParts[1]).equals(comparedRegister)) {
1563
- selectorFeedsTable = true;
1970
+ Set<String> selectorAliases = new HashSet<>();
1971
+ selectorAliases.add(comparedRegister);
1972
+ Set<String> boundedIndexes = new HashSet<>();
1973
+ if (compareMatcher.group(1).toLowerCase(Locale.ROOT).startsWith("x")) boundedIndexes.add(comparedRegister);
1974
+ for (int sequenceIndex = compareIndex + 1; sequenceIndex < loadIndex; sequenceIndex++) {
1975
+ Instruction instruction = instructions.get(sequenceIndex);
1976
+ String[] parts = operands(instruction).split(",");
1977
+ if (parts.length == 0) continue;
1978
+ if (instruction.getMnemonicString().equalsIgnoreCase("MOV")) {
1979
+ String destination = normalizedRegister(parts[0]);
1980
+ String source = parts.length == 2 ? normalizedRegister(parts[1]) : "";
1981
+ boolean aliasesSelector = selectorAliases.contains(source);
1982
+ boolean fullWidthBounded = boundedIndexes.contains(source) ||
1983
+ (parts[0].trim().toLowerCase(Locale.ROOT).startsWith("w") && aliasesSelector);
1984
+ if (aliasesSelector) selectorAliases.add(destination);
1985
+ else selectorAliases.remove(destination);
1986
+ if (fullWidthBounded) boundedIndexes.add(destination);
1987
+ else boundedIndexes.remove(destination);
1988
+ } else if (Set.of("ADRP", "ADD", "ADR").contains(instruction.getMnemonicString().toUpperCase(Locale.ROOT))) {
1989
+ selectorAliases.remove(normalizedRegister(parts[0]));
1990
+ boundedIndexes.remove(normalizedRegister(parts[0]));
1564
1991
  }
1565
1992
  }
1566
- if (selectorFeedsTable && supportedSequence) {
1993
+ boolean selectorFeedsTable = boundedIndexes.contains(indexRegister);
1994
+ if (compareIndex < loadIndex - 3 && selectorFeedsTable && supportedSequence) {
1567
1995
  entryCount = (int) upperBound + 1;
1568
1996
  defaultTarget = flows[0];
1569
1997
  break;
@@ -1577,7 +2005,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
1577
2005
  Set<String> mappedTargets = new HashSet<>();
1578
2006
  boolean valid = true;
1579
2007
  for (int caseValue = 0; caseValue < entryCount; caseValue += 1) {
1580
- int encodedOffset = currentProgram.getMemory().getByte(tableAddress.add(caseValue)) & 0xff;
2008
+ Address entry = tableAddress.add((long) caseValue * entrySize);
2009
+ int encodedOffset = entrySize == 1 ? currentProgram.getMemory().getByte(entry) & 0xff : currentProgram.getMemory().getShort(entry) & 0xffff;
1581
2010
  Address target = branchBase.add((long) encodedOffset * 4L);
1582
2011
  String canonicalTarget = canonicalAddress(target);
1583
2012
  if (!recoveredTargetSet.contains(canonicalTarget)) {
@@ -1591,7 +2020,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
1591
2020
  Set<String> accountedTargets = new HashSet<>(mappedTargets);
1592
2021
  accountedTargets.add(canonicalAddress(defaultTarget));
1593
2022
  if (!accountedTargets.equals(recoveredTargetSet)) continue;
1594
- return new RecoveredAArch64ByteTable(tableAddress, branchBase, List.copyOf(mappings));
2023
+ return new RecoveredAArch64RelativeTable(tableAddress, branchBase, entrySize, List.copyOf(mappings));
1595
2024
  }
1596
2025
  return null;
1597
2026
  }
@@ -2083,9 +2512,67 @@ public final class ReaGhidraBridge extends HeadlessScript {
2083
2512
  result.addProperty("address", canonicalAddress(function.getEntryPoint()));
2084
2513
  result.addProperty("name", procedureName(function));
2085
2514
  result.add("classification", functionClassification(function));
2515
+ result.add("body", functionBody(function));
2086
2516
  return result;
2087
2517
  }
2088
2518
 
2519
+ /** Observe the entire AddressSet; range endpoints are inclusive, not a guessed span. */
2520
+ private static JsonObject functionBody(Function function) {
2521
+ JsonArray ranges = new JsonArray();
2522
+ BigInteger total = BigInteger.ZERO;
2523
+ Address first = null;
2524
+ Address last = null;
2525
+ AddressSpace space = null;
2526
+ boolean multipleSpaces = false;
2527
+ AddressRangeIterator iterator = function.getBody().getAddressRanges(true);
2528
+ while (iterator.hasNext()) {
2529
+ AddressRange range = iterator.next();
2530
+ Address start = range.getMinAddress();
2531
+ Address end = range.getMaxAddress();
2532
+ if (start.getAddressSpace().getAddressableUnitSize() != 1) {
2533
+ throw new IllegalStateException("Function body byte coverage requires a byte-addressed space");
2534
+ }
2535
+ JsonObject value = new JsonObject();
2536
+ value.addProperty("start", canonicalAddress(start));
2537
+ value.addProperty("end", canonicalAddress(end));
2538
+ ranges.add(value);
2539
+ BigInteger begin = new BigInteger(Long.toUnsignedString(start.getOffset()));
2540
+ BigInteger finish = new BigInteger(Long.toUnsignedString(end.getOffset()));
2541
+ total = total.add(finish.subtract(begin).add(BigInteger.ONE));
2542
+ if (first == null) {
2543
+ first = start;
2544
+ space = start.getAddressSpace();
2545
+ } else if (!space.equals(start.getAddressSpace())) {
2546
+ multipleSpaces = true;
2547
+ }
2548
+ last = end;
2549
+ }
2550
+ JsonObject result = new JsonObject();
2551
+ result.addProperty("available", true);
2552
+ result.addProperty("provenance", "ghidra-function-body-address-set");
2553
+ result.add("ranges", ranges);
2554
+ result.addProperty("total_bytes", safeBodyByteCount(total));
2555
+ if (multipleSpaces) {
2556
+ result.add("span_bytes", JsonNull.INSTANCE);
2557
+ } else {
2558
+ BigInteger span = first == null ? BigInteger.ZERO :
2559
+ new BigInteger(Long.toUnsignedString(last.getOffset()))
2560
+ .subtract(new BigInteger(Long.toUnsignedString(first.getOffset())))
2561
+ .add(BigInteger.ONE);
2562
+ result.addProperty("span_bytes", safeBodyByteCount(span));
2563
+ }
2564
+ result.addProperty("non_contiguous", ranges.size() > 1);
2565
+ result.addProperty("contains_entry", function.getBody().contains(function.getEntryPoint()));
2566
+ return result;
2567
+ }
2568
+
2569
+ private static long safeBodyByteCount(BigInteger value) {
2570
+ if (value.signum() < 0 || value.compareTo(BigInteger.valueOf(9007199254740991L)) > 0) {
2571
+ throw new IllegalStateException("Function body byte coverage exceeds exact JSON integer range");
2572
+ }
2573
+ return value.longValueExact();
2574
+ }
2575
+
2089
2576
  private static String exclusiveEnd(MemoryBlock block) {
2090
2577
  Address next = block.getEnd().next();
2091
2578
  if (next != null && next.getAddressSpace().equals(block.getStart().getAddressSpace())) {