rea-agents 6.3.0 → 6.5.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 (1332) hide show
  1. package/DISCLAIMER.md +136 -0
  2. package/README.md +72 -18
  3. package/THIRD_PARTY_NOTICES.md +134 -0
  4. package/bridge/android/ReaJadxBridge.java +123 -11
  5. package/bridge/cutter/rea_bridge.py +816 -0
  6. package/bridge/ghidra/ReaGhidraAnalysisOptions.java +38 -0
  7. package/bridge/ghidra/ReaGhidraApplySeeds.java +151 -0
  8. package/bridge/ghidra/ReaGhidraBridge.java +1729 -283
  9. package/bridge/ghidra/ReaGhidraBundleActivator.java +14 -0
  10. package/bridge/ghidra/ReaGhidraMipsSoftFloat.java +176 -0
  11. package/bridge/ghidra/ReaGhidraNoReturnFix.java +123 -0
  12. package/bridge/ghidra/ReaGhidraPrepareRaw.java +49 -0
  13. package/bridge/hopper_bridge.py +103 -69
  14. package/bridge/mitmproxy/capture.py +189 -13
  15. package/bridge/native/rea_lldb_tracer.py +40 -6
  16. package/bridge/process/ProcessRunTokenReader.swift +76 -34
  17. package/bridge/pwntools/layout_impl.py +5 -0
  18. package/bridge/pwntools/recorded_crash_impl.py +31 -10
  19. package/dist/adb/AdbConfiguration.js +100 -0
  20. package/dist/adb/AdbDeviceOutput.js +208 -0
  21. package/dist/adb/AdbDirectoryListing.js +80 -0
  22. package/dist/adb/AdbHostFileName.js +34 -0
  23. package/dist/adb/AdbProvider.js +1433 -0
  24. package/dist/adb/AdbShellOutput.js +307 -0
  25. package/dist/android/AndroidTargetSnapshot.js +104 -22
  26. package/dist/android/JadxAnalysis.js +32 -7
  27. package/dist/android/JadxConfiguration.js +163 -53
  28. package/dist/android/JadxDoctor.js +115 -0
  29. package/dist/android/JadxMcpTransport.js +5 -3
  30. package/dist/android/JadxProtocol.js +14 -0
  31. package/dist/android/JadxProvider.js +63 -25
  32. package/dist/android/JadxRelease.js +12 -1
  33. package/dist/android/JadxWorkspaceRoot.js +157 -0
  34. package/dist/apktool/ApktoolConfiguration.js +227 -0
  35. package/dist/apktool/ApktoolDecodeOutput.js +142 -0
  36. package/dist/apktool/ApktoolProvider.js +543 -0
  37. package/dist/application/AnalysisContextQueries.js +5 -8
  38. package/dist/application/AnalysisProvider.js +24 -14
  39. package/dist/application/AppBundleExecutable.js +30 -25
  40. package/dist/application/BinaryTargetResolver.js +210 -33
  41. package/dist/application/BrowserEvidence.js +1 -1
  42. package/dist/application/BrowserObservationService.js +13 -2
  43. package/dist/application/BrowserScenarioCaptureService.js +0 -1
  44. package/dist/application/CallPathTracing.js +52 -13
  45. package/dist/application/CapabilityInventory.js +179 -29
  46. package/dist/application/ClaudeDesktopConfigLocation.js +152 -0
  47. package/dist/application/ClientConfigurationDocument.js +154 -7
  48. package/dist/application/ClientConfigurationFiles.js +128 -0
  49. package/dist/application/ClientRegistrationEnvironment.js +41 -0
  50. package/dist/application/ClientRegistrationIdentity.js +94 -29
  51. package/dist/application/ClientRegistrationStatus.js +223 -67
  52. package/dist/application/DirectAnalysis.js +86 -31
  53. package/dist/application/DirectAnalysisCleanup.js +1 -1
  54. package/dist/application/DirectAnalysisStatus.js +14 -2
  55. package/dist/application/Doctor.js +117 -34
  56. package/dist/application/DoctorDiagnostics.js +34 -12
  57. package/dist/application/DoctorProviderIds.js +2 -0
  58. package/dist/application/DoctorScope.js +11 -2
  59. package/dist/application/DshCordisPatch.js +355 -0
  60. package/dist/application/DshCordisYaml.js +155 -0
  61. package/dist/application/EnhancedLiteralTracing.js +30 -16
  62. package/dist/application/EnhancedToolTypes.js +0 -1
  63. package/dist/application/EnhancedTools.js +164 -54
  64. package/dist/application/EvidenceBundleCommands.js +36 -19
  65. package/dist/application/EvidenceBundleFiles.js +29 -7
  66. package/dist/application/FunctionAnalysisEvidence.js +3 -0
  67. package/dist/application/GeminiClientSettings.js +151 -0
  68. package/dist/application/HermesClientHome.js +115 -0
  69. package/dist/application/HermesConfigurationYaml.js +272 -0
  70. package/dist/application/HopperDownload.js +39 -0
  71. package/dist/application/InventoryProjectionEvidence.js +0 -1
  72. package/dist/application/InvestigationProviders.js +19 -3
  73. package/dist/application/JsonFiles.js +114 -23
  74. package/dist/application/JsonInputFile.js +410 -0
  75. package/dist/application/LinuxHopper.js +130 -60
  76. package/dist/application/MacHopper.js +177 -150
  77. package/dist/application/MacHopperPaths.js +40 -0
  78. package/dist/application/MipsElfMetadata.js +201 -0
  79. package/dist/application/MipsElfSharedLoad.js +164 -0
  80. package/dist/application/MipsGnuAttributes.js +90 -0
  81. package/dist/application/MsixPackageStorage.js +133 -0
  82. package/dist/application/ProgressReporter.js +1 -1
  83. package/dist/application/ProviderOperationExecution.js +39 -0
  84. package/dist/application/ReconstructionCoverageService.js +0 -1
  85. package/dist/application/ReconstructionObligationCandidates.js +12 -5
  86. package/dist/application/ReconstructionObligationLedgerDiagnostics.js +2 -2
  87. package/dist/application/ReconstructionObligationLedgerEvaluation.js +15 -33
  88. package/dist/application/ReconstructionObligationLedgerProjection.js +3 -72
  89. package/dist/application/ReconstructionObligationLedgerService.js +3 -1
  90. package/dist/application/ReconstructionObligationOriginalCases.js +2 -1
  91. package/dist/application/ReferenceSourceImport.js +94 -31
  92. package/dist/application/ReferenceSourceImportEntries.js +40 -30
  93. package/dist/application/ReferenceSourceImportPolicy.js +99 -23
  94. package/dist/application/ReferenceSourceImportTypes.js +30 -23
  95. package/dist/application/ReferenceSourceVcsAdapter.js +163 -31
  96. package/dist/application/RegularFileRead.js +121 -7
  97. package/dist/application/Setup.js +88 -14
  98. package/dist/application/SetupClientConfiguration.js +307 -38
  99. package/dist/application/SetupClients.js +12 -6
  100. package/dist/application/SetupHost.js +158 -38
  101. package/dist/application/SetupInstallFailure.js +42 -2
  102. package/dist/application/SetupPlan.js +83 -57
  103. package/dist/application/SetupRecoveryBackup.js +66 -0
  104. package/dist/application/SetupRegistrationEnvironment.js +2 -1
  105. package/dist/application/SetupSkill.js +354 -88
  106. package/dist/application/SupportedClients.js +429 -70
  107. package/dist/application/TypeScriptEmittedSourceCandidates.js +26 -0
  108. package/dist/application/Uninstall.js +412 -73
  109. package/dist/application/UninstallFailure.js +14 -0
  110. package/dist/application/Update.js +54 -25
  111. package/dist/application/UpdateFailure.js +17 -0
  112. package/dist/application/UpdateMaintenance.js +25 -47
  113. package/dist/application/UpdateRuntime.js +141 -27
  114. package/dist/application/Utf8JsonInput.js +7 -0
  115. package/dist/application/WebModuleTraceService.js +7 -0
  116. package/dist/application/WebNetworkCaptureService.js +16 -7
  117. package/dist/application/WebScriptExportService.js +131 -65
  118. package/dist/application/WebSourceLocationService.js +22 -3
  119. package/dist/application/WindowsCapabilities.js +18 -15
  120. package/dist/application/WorkflowEvidence.js +24 -1
  121. package/dist/application/adb/AdbDeviceAnalysisPort.js +1 -0
  122. package/dist/application/adb/AdbDeviceAnalysisService.js +13 -0
  123. package/dist/application/analysisView/AnalysisViewService.js +122 -6
  124. package/dist/application/android/AndroidPackageInspection.js +25 -0
  125. package/dist/application/android/AndroidSearchEvidenceSummary.js +100 -0
  126. package/dist/application/apktool/ApktoolResourceAnalysisPort.js +1 -0
  127. package/dist/application/apktool/ApktoolResourceAnalysisService.js +13 -0
  128. package/dist/application/binary/AnalysisClientCleanup.js +0 -24
  129. package/dist/application/binary/AnalysisProviderEvaluation.js +1 -1
  130. package/dist/application/binary/AnalysisProviderRegistry.js +36 -21
  131. package/dist/application/binary/AnalysisSnapshotCache.js +41 -20
  132. package/dist/application/binary/AnalysisSnapshotFiles.js +75 -9
  133. package/dist/application/binary/BinarySession.js +250 -97
  134. package/dist/application/binary/BinarySessionExecution.js +5 -4
  135. package/dist/application/binary/BinarySessionOpen.js +13 -6
  136. package/dist/application/binary/BinarySessionRecords.js +33 -7
  137. package/dist/application/binary/BinarySessionStatus.js +93 -23
  138. package/dist/application/binary/BinaryTargetSummary.js +9 -0
  139. package/dist/application/binary/CompositeProvider.js +71 -28
  140. package/dist/application/binary/LazyAnalysisProvider.js +3 -1
  141. package/dist/application/binary/SessionProviderRouter.js +5 -2
  142. package/dist/application/binaryDiagnostics/BinaryLayoutService.js +13 -2
  143. package/dist/application/binaryDiagnostics/PeResourcesPort.js +1 -0
  144. package/dist/application/binaryDiagnostics/PeResourcesService.js +114 -0
  145. package/dist/application/binaryDiagnostics/RecordedCrashService.js +16 -4
  146. package/dist/application/captures/PacketCaptureService.js +83 -0
  147. package/dist/application/closeOwnedWebResources.js +7 -0
  148. package/dist/application/electron/ElectronFusePort.js +1 -0
  149. package/dist/application/electron/ElectronFuseService.js +80 -0
  150. package/dist/application/evm/EvmInterfaceService.js +4 -0
  151. package/dist/application/firmware/FirmwareAnalysisService.js +3 -0
  152. package/dist/application/flutter/DartAotInspection.js +26 -0
  153. package/dist/application/flutter/FlutterBuildAnalysisPort.js +1 -0
  154. package/dist/application/flutter/FlutterBuildAnalysisService.js +13 -0
  155. package/dist/application/frida/FridaInstrumentationPort.js +8 -0
  156. package/dist/application/frida/FridaInstrumentationService.js +310 -0
  157. package/dist/application/frida/fridaInputErrors.js +17 -0
  158. package/dist/application/go/GoBinaryPort.js +1 -0
  159. package/dist/application/go/GoBinaryService.js +80 -0
  160. package/dist/application/harmony/HarmonyApplicationService.js +17 -0
  161. package/dist/application/investigation/EvidenceLedger.js +40 -14
  162. package/dist/application/investigation/InspectEvidenceBundle.js +18 -0
  163. package/dist/application/investigation/InvestigationRecords.js +12 -21
  164. package/dist/application/investigation/UnknownEvidence.js +22 -18
  165. package/dist/application/javascript/ElectronActiveObservationService.js +55 -25
  166. package/dist/application/javascript/ElectronBoundaryAnalysis.js +17 -3
  167. package/dist/application/javascript/ElectronBoundaryGraph.js +2 -0
  168. package/dist/application/javascript/ElectronBoundaryGraphCallPaths.js +144 -0
  169. package/dist/application/javascript/ElectronBoundaryGraphContext.js +3 -2
  170. package/dist/application/javascript/ElectronBoundaryGraphIpc.js +28 -9
  171. package/dist/application/javascript/ElectronBoundaryGraphNative.js +20 -7
  172. package/dist/application/javascript/ElectronBoundaryGraphWindows.js +50 -12
  173. package/dist/application/javascript/ElectronObservationService.js +0 -1
  174. package/dist/application/javascript/JavaScriptApplicationEvidence.js +15 -0
  175. package/dist/application/javascript/JavaScriptApplicationService.js +44 -18
  176. package/dist/application/javascript/JavaScriptApplicationWorkflowEvidence.js +5 -6
  177. package/dist/application/javascript/JavaScriptApplicationWorkflowService.js +0 -1
  178. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +253 -60
  179. package/dist/application/javascript/JavaScriptArtifactGraphAccumulator.js +51 -23
  180. package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +93 -21
  181. package/dist/application/javascript/JavaScriptArtifactGraphBundlers.js +4 -3
  182. package/dist/application/javascript/JavaScriptArtifactGraphContext.js +35 -8
  183. package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +25 -14
  184. package/dist/application/javascript/JavaScriptArtifactGraphFindings.js +163 -54
  185. package/dist/application/javascript/JavaScriptArtifactGraphStructure.js +246 -15
  186. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +1057 -323
  187. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +134 -32
  188. package/dist/application/javascript/JavaScriptArtifactReconstructionInput.js +6 -1
  189. package/dist/application/javascript/JavaScriptModuleRelationships.js +39 -15
  190. package/dist/application/javascript/JavaScriptRecoveryService.js +32 -9
  191. package/dist/application/javascript/JavaScriptRuntimeObservationService.js +2 -1
  192. package/dist/application/javascript/JavaScriptRuntimeReconciliationService.js +0 -1
  193. package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +9 -3
  194. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +228 -74
  195. package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +12 -19
  196. package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +4 -1
  197. package/dist/application/javascript/JavaScriptSemanticGraphElectronProjection.js +67 -0
  198. package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -3
  199. package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +5 -3
  200. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +19 -8
  201. package/dist/application/javascript/JavaScriptSemanticTraceService.js +0 -1
  202. package/dist/application/jeb/JebAnalysisPort.js +1 -0
  203. package/dist/application/jeb/JebAnalysisService.js +13 -0
  204. package/dist/application/managed/ManagedApplicationGraphService.js +1 -2
  205. package/dist/application/managed/ManagedMemberComparisonService.js +72 -54
  206. package/dist/application/managed/ManagedNativeVerificationService.js +0 -1
  207. package/dist/application/managed/ManagedReconstructionService.js +0 -1
  208. package/dist/application/native/NativeCallRoutes.js +54 -34
  209. package/dist/application/native/NativeDispatchMetadataInspection.js +6 -2
  210. package/dist/application/native/NativeUiActionTrace.js +51 -30
  211. package/dist/application/native/NativeValueTrace.js +75 -24
  212. package/dist/application/pcap/PcapAnalysisPort.js +1 -0
  213. package/dist/application/pcap/PcapAnalysisService.js +24 -0
  214. package/dist/application/process/ProcessCli.js +57 -32
  215. package/dist/application/reverse/ReverseEngineeringCommandPort.js +1 -0
  216. package/dist/application/reverse/ReverseEngineeringService.js +688 -0
  217. package/dist/application/rust/RustBinaryPort.js +1 -0
  218. package/dist/application/rust/RustBinaryService.js +80 -0
  219. package/dist/application/signals/SignalRecordingService.js +83 -0
  220. package/dist/application/sqlite/SqliteDatabaseCapability.js +30 -0
  221. package/dist/application/sqlite/SqliteDatabasePort.js +1 -0
  222. package/dist/application/sqlite/SqliteDatabaseService.js +76 -0
  223. package/dist/application/wasm/WasmArtifactPort.js +1 -0
  224. package/dist/application/wasm/WasmArtifactService.js +77 -0
  225. package/dist/application/wasm/WasmInterfaceComparisonService.js +188 -0
  226. package/dist/artifacts/ArtifactHash.js +13 -2
  227. package/dist/artifacts/ArtifactPaths.js +133 -15
  228. package/dist/artifacts/ArtifactProvider.js +28 -19
  229. package/dist/artifacts/ArtifactReader.js +35 -0
  230. package/dist/artifacts/ArtifactResourceScope.js +109 -0
  231. package/dist/artifacts/AsarArtifactReader.js +295 -156
  232. package/dist/artifacts/AsarHeader.js +24 -0
  233. package/dist/artifacts/AsarListing.js +78 -0
  234. package/dist/artifacts/DirectoryArtifactReader.js +247 -74
  235. package/dist/artifacts/MachOSliceArtifactReader.js +198 -48
  236. package/dist/artifacts/NativeDmgArtifactReader.js +188 -78
  237. package/dist/artifacts/OrderedReads.js +57 -0
  238. package/dist/artifacts/SafeOutputTree.js +261 -71
  239. package/dist/artifacts/SafeOutputTreeCleanup.js +45 -0
  240. package/dist/artifacts/SafeOutputTreeCreationFailure.js +20 -0
  241. package/dist/artifacts/SafeOutputTreeIdentity.js +42 -0
  242. package/dist/artifacts/SafeOutputTreeReadback.js +33 -0
  243. package/dist/artifacts/ZipArtifactReader.js +186 -31
  244. package/dist/artifacts/apple/AppleAssetCatalogAnalysis.js +77 -68
  245. package/dist/artifacts/apple/DylibResolutionReader.js +11 -5
  246. package/dist/artifacts/apple/InterfaceBuilderAnalysis.js +114 -38
  247. package/dist/artifacts/apple/InterfaceBuilderDecodeBudget.js +125 -35
  248. package/dist/artifacts/apple/KeyedArchiveReader.js +35 -15
  249. package/dist/artifacts/apple/MachoLoadCommandReader.js +62 -61
  250. package/dist/artifacts/apple/NibArchive.js +11 -4
  251. package/dist/artifacts/apple/PlistNumberLiterals.js +2 -2
  252. package/dist/artifacts/extraction/ArtifactExtraction.js +115 -92
  253. package/dist/artifacts/hostExecutableMarking.js +16 -0
  254. package/dist/artifacts/inventory/ArtifactGraphConstruction.js +236 -69
  255. package/dist/artifacts/inventory/ArtifactInventory.js +7 -6
  256. package/dist/artifacts/inventory/classify.js +95 -35
  257. package/dist/artifacts/inventory/hashStableRootArtifact.js +48 -15
  258. package/dist/artifacts/inventory/reader.js +40 -17
  259. package/dist/artifacts/inventory/scanCanonical.js +84 -40
  260. package/dist/artifacts/inventory/scanReader.js +72 -19
  261. package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +177 -15
  262. package/dist/artifacts/javascript/JavaScriptSourceAnalysis.js +3 -0
  263. package/dist/artifacts/readStableArtifact.js +28 -4
  264. package/dist/browser/BrowserProcessStartup.js +20 -9
  265. package/dist/browser/BrowserScenarioPartialObservation.js +60 -38
  266. package/dist/browser/BrowserScenarioSecrets.js +193 -48
  267. package/dist/browser/CdpBrowserProvider.js +74 -98
  268. package/dist/browser/CdpCaptureDocuments.js +261 -65
  269. package/dist/browser/CdpCaptureDomSvg.js +243 -0
  270. package/dist/browser/CdpCaptureEventBodyShapes.js +12 -12
  271. package/dist/browser/CdpCaptureEventHandlers.js +49 -21
  272. package/dist/browser/CdpCaptureEvents.js +53 -19
  273. package/dist/browser/CdpCaptureStorage.js +22 -5
  274. package/dist/browser/CdpCaptureStorageFingerprints.js +54 -11
  275. package/dist/browser/CdpCaptureValues.js +0 -1
  276. package/dist/browser/CdpConnection.js +25 -9
  277. package/dist/browser/CdpElectronInspection.js +14 -4
  278. package/dist/browser/CdpElectronProvider.js +7 -2
  279. package/dist/browser/CdpElectronScripts.js +2 -1
  280. package/dist/browser/CdpElectronWorkers.js +7 -2
  281. package/dist/browser/CdpEndpoint.js +143 -17
  282. package/dist/browser/CdpObservationSession.js +24 -8
  283. package/dist/browser/CdpPageCapture.js +67 -17
  284. package/dist/browser/CdpPageCaptureScripts.js +4 -3
  285. package/dist/browser/CdpPageCaptureWorkers.js +5 -1
  286. package/dist/browser/CdpSafeMetadata.js +9 -5
  287. package/dist/browser/CdpScreenshot.js +5 -14
  288. package/dist/browser/CdpSnapshotStrings.js +21 -0
  289. package/dist/browser/CdpTargetSession.js +59 -36
  290. package/dist/browser/CdpWebMcpDiscovery.js +55 -16
  291. package/dist/browser/ElectronFileScope.js +99 -9
  292. package/dist/browser/PlaywrightBrowserScenarioProvider.js +64 -8
  293. package/dist/browser/PlaywrightElectronActiveActions.js +4 -4
  294. package/dist/browser/PlaywrightElectronActiveProjection.js +16 -4
  295. package/dist/browser/PlaywrightElectronActiveProvider.js +114 -36
  296. package/dist/browser/PlaywrightExecutionBoundary.js +2 -2
  297. package/dist/browser/PlaywrightScenarioArtifacts.js +5 -5
  298. package/dist/browser/PlaywrightScenarioBrowser.js +78 -13
  299. package/dist/browser/PlaywrightScenarioEvents.js +198 -36
  300. package/dist/browser/PlaywrightScenarioServiceWorkers.js +249 -0
  301. package/dist/browser/PlaywrightScenarioSession.js +180 -75
  302. package/dist/browser/PlaywrightScenarioStorage.js +507 -0
  303. package/dist/browser/PngVisualDiff.js +198 -29
  304. package/dist/browser/WebSourceMapFetcher.js +164 -81
  305. package/dist/browser/assets/LocalWebScriptArtifacts.js +27 -4
  306. package/dist/browser/assets/PublishWebScripts.js +88 -22
  307. package/dist/browser/assets/ScenarioScriptCapture.js +2 -1
  308. package/dist/browser/assets/WebArtifactReads.js +34 -4
  309. package/dist/browser/assets/WebSourceLocationArtifacts.js +39 -3
  310. package/dist/browser/execution/CdpExecutionCapture.js +3 -3
  311. package/dist/browser/execution/CdpPreciseCoverage.js +5 -8
  312. package/dist/browser/history/CaptureFailures.js +22 -8
  313. package/dist/browser/history/CaptureFormatError.js +2 -2
  314. package/dist/browser/history/{HarJson.js → CaptureJson.js} +14 -4
  315. package/dist/browser/history/CaptureRedaction.js +16 -3
  316. package/dist/browser/history/CaptureRelease.js +20 -2
  317. package/dist/browser/history/HarCapture.js +16 -9
  318. package/dist/browser/history/HarSchema.js +36 -36
  319. package/dist/browser/history/HistoricalCaptureDecoder.js +93 -25
  320. package/dist/browser/history/MitmproxyCaptureAdapter.js +3 -1
  321. package/dist/browser/history/NodeCaptureAdapter.js +48 -0
  322. package/dist/browser/history/{HarCaptureProcess.js → NodeCaptureProcess.js} +16 -15
  323. package/dist/browser/history/QlogCapture.js +385 -0
  324. package/dist/browser/history/SazCapture.js +130 -0
  325. package/dist/browser/history/SazHttp.js +61 -0
  326. package/dist/browser/history/SazMetadata.js +92 -0
  327. package/dist/browser/history/SazProjection.js +90 -0
  328. package/dist/browser/modules/NativeModuleResolver.js +177 -110
  329. package/dist/browser/modules/WebModuleArtifacts.js +41 -3
  330. package/dist/browser/network/PlaywrightNetworkContent.js +40 -5
  331. package/dist/browser/network/PlaywrightScenarioNetwork.js +125 -11
  332. package/dist/captures/TsharkCaptureProvider.js +370 -0
  333. package/dist/captures/parseFieldDiscovery.js +66 -0
  334. package/dist/captures/parsePacketOutput.js +21 -0
  335. package/dist/catalogIdentity.js +46 -35
  336. package/dist/cli/adbCommands.js +337 -0
  337. package/dist/cli/analysisViewCommands.js +39 -1
  338. package/dist/cli/androidCommands.js +11 -2
  339. package/dist/cli/apktoolCommands.js +52 -0
  340. package/dist/cli/applicationCommands.js +44 -17
  341. package/dist/cli/artifactCommands.js +9 -12
  342. package/dist/cli/binaryDiagnosticsCommands.js +27 -5
  343. package/dist/cli/browserAdvancedCommands.js +97 -55
  344. package/dist/cli/browserCommands.js +46 -29
  345. package/dist/cli/captureCommands.js +72 -0
  346. package/dist/cli/cliResultOutput.js +188 -0
  347. package/dist/cli/coreBinaryCommands.js +73 -32
  348. package/dist/cli/coreNativeCommands.js +54 -45
  349. package/dist/cli/cutterCommands.js +50 -0
  350. package/dist/cli/electronCommands.js +51 -4
  351. package/dist/cli/evmCommands.js +16 -8
  352. package/dist/cli/finishAnalysisCommand.js +44 -0
  353. package/dist/cli/firmwareCommands.js +21 -38
  354. package/dist/cli/flutterCommands.js +69 -0
  355. package/dist/cli/fridaCommands.js +211 -0
  356. package/dist/cli/goCommands.js +20 -0
  357. package/dist/cli/interactiveSetup.js +11 -1
  358. package/dist/cli/javascriptApplicationAnalysis.js +18 -2
  359. package/dist/cli/javascriptRecoveryCommands.js +29 -15
  360. package/dist/cli/jebCommands.js +99 -0
  361. package/dist/cli/jsonOutput.js +13 -6
  362. package/dist/cli/managedCommands.js +31 -3
  363. package/dist/cli/options.js +20 -2
  364. package/dist/cli/pcapCommands.js +47 -0
  365. package/dist/cli/processCommands.js +1 -1
  366. package/dist/cli/referenceSourceImportStatus.js +7 -0
  367. package/dist/cli/reverseEngineeringCommands.js +268 -0
  368. package/dist/cli/rustCommands.js +21 -0
  369. package/dist/cli/setupCommands.js +55 -15
  370. package/dist/cli/signalCommands.js +72 -0
  371. package/dist/cli/sqliteCommands.js +79 -0
  372. package/dist/cli/utilityCommands.js +179 -89
  373. package/dist/cli/wasmCommands.js +61 -0
  374. package/dist/cli/webModuleCommands.js +36 -32
  375. package/dist/cli/webNetworkCaptureCommands.js +8 -8
  376. package/dist/cli/webScriptCommands.js +20 -1
  377. package/dist/cli/webSourceLocationCommands.js +21 -9
  378. package/dist/cli-runtime/AbortablePromise-BVBemxbD.js +123 -0
  379. package/dist/cli-runtime/AnalysisProvider-D0Us2OTk.js +27 -0
  380. package/dist/cli-runtime/AnalysisSnapshotCache-BXS0wRd2.js +710 -0
  381. package/dist/cli-runtime/ArtifactReader-BXkffc1D.js +55 -0
  382. package/dist/cli-runtime/ArtifactResourceScope-DQWTcUwE.js +114 -0
  383. package/dist/cli-runtime/AsarHeader-CnzOzYEH.js +31 -0
  384. package/dist/cli-runtime/BinarySessionStatus-DnMxMk-r.js +211 -0
  385. package/dist/cli-runtime/BinaryTargetResolver-CVIJ_s1A.js +1308 -0
  386. package/dist/cli-runtime/BoundedFileBytes-BU30odTm.js +39 -0
  387. package/dist/cli-runtime/CdpTargetSession-DGX5pV9w.js +650 -0
  388. package/dist/cli-runtime/ClientConfigPath-c6jr6INc.js +36 -0
  389. package/dist/cli-runtime/ClientConfigurationDocument-ffQ9X0Se.js +1957 -0
  390. package/dist/cli-runtime/ClientRegistrationStatus-DszCyPEU.js +509 -0
  391. package/dist/cli-runtime/CurrentEntrypointPath-D5FRNdkq.js +14 -0
  392. package/dist/cli-runtime/DirectAnalysis-BgvAtpN3.js +528 -0
  393. package/dist/cli-runtime/Doctor-DIpf_I7N.js +588 -0
  394. package/dist/cli-runtime/ElectronFileScope-dpHm2nrS.js +1054 -0
  395. package/dist/cli-runtime/ExecFileOutput-BEQs-lDD.js +117 -0
  396. package/dist/cli-runtime/IdaConfiguration-CtGP7EKo.js +1094 -0
  397. package/dist/cli-runtime/InterfaceBuilderDecodeBudget-D4omRUe4.js +279 -0
  398. package/dist/cli-runtime/InventoryProjectionEvidence-DIsI4DJ6.js +49 -0
  399. package/dist/cli-runtime/InvestigationProviders-DoQRIHii.js +117 -0
  400. package/dist/cli-runtime/JavaScriptApplicationEvidenceGraph-D5aI_FSJ.js +160 -0
  401. package/dist/cli-runtime/JavaScriptApplicationWorkflowEvidence-Bz4-V0Ct.js +75 -0
  402. package/dist/cli-runtime/JavaScriptWorkerMemoryLimit-B1bwMM1a.js +86 -0
  403. package/dist/cli-runtime/JebMcpEndpoint-DUytrn5G.js +428 -0
  404. package/dist/cli-runtime/JsonFiles-DQa1PRKy.js +708 -0
  405. package/dist/cli-runtime/MacCodeSignature-MgYbRK_H.js +24 -0
  406. package/dist/cli-runtime/MacHopperPaths-CwL5lYPX.js +434 -0
  407. package/dist/cli-runtime/MachoLoadCommandReader-C7e074Ze.js +510 -0
  408. package/dist/cli-runtime/ManagedSnapshot-rqyBHcdL.js +4185 -0
  409. package/dist/cli-runtime/OwnedCommand-Cj9GhmPR.js +112 -0
  410. package/dist/cli-runtime/PrivateRuntimeRoot-DAy0PiBM.js +263 -0
  411. package/dist/cli-runtime/ProcessOwnership-DXC5-UWt.js +1378 -0
  412. package/dist/cli-runtime/ProviderDeadline-8m2pFRDc.js +122 -0
  413. package/dist/cli-runtime/RegularFile-B_6KPCNQ.js +139 -0
  414. package/dist/cli-runtime/RegularFileRead-DVlC5faM.js +125 -0
  415. package/dist/cli-runtime/SafeOutputTree-D6U15GXd.js +524 -0
  416. package/dist/cli-runtime/SetupHost-FkYWswsq.js +1098 -0
  417. package/dist/cli-runtime/SupportedClients-DaX6Lt2d.js +1049 -0
  418. package/dist/cli-runtime/UninstallFailure-1blyKXHE.js +22 -0
  419. package/dist/cli-runtime/Update-CbTC_eg1.js +143 -0
  420. package/dist/cli-runtime/UpdateFailure-BcFNxEZZ.js +24 -0
  421. package/dist/cli-runtime/ZipArtifactReader-B8ZgNe4D.js +248 -0
  422. package/dist/cli-runtime/analysisErrorCleanup-Do6YeeoD.js +32 -0
  423. package/dist/cli-runtime/analysisErrorPresentation-DGt7TdsH.js +497 -0
  424. package/dist/cli-runtime/appleArchiveFormat-tVoI2ykA.js +263 -0
  425. package/dist/cli-runtime/application__DirectAnalysisStatus-DpOejOf0.js +150 -0
  426. package/dist/cli-runtime/application__EnhancedTools-oFE2bOjs.js +1787 -0
  427. package/dist/cli-runtime/application__FunctionAnalysisEvidence-B6osuhvW.js +41 -0
  428. package/dist/cli-runtime/application__ReconstructionCoverageService-DN3wTFQj.js +32 -0
  429. package/dist/cli-runtime/application__ReconstructionObligationLedgerService-CKcTppX9.js +815 -0
  430. package/dist/cli-runtime/application__Setup-XpMaRNjy.js +514 -0
  431. package/dist/cli-runtime/application__Uninstall-_23pU2yB.js +427 -0
  432. package/dist/cli-runtime/application__UpdateRuntime-DPvY8ohd.js +345 -0
  433. package/dist/cli-runtime/application__android__AndroidApplicationService-CFgIFPM4.js +31 -0
  434. package/dist/cli-runtime/application__apple__AppleApplicationService-Db6QCsun.js +31 -0
  435. package/dist/cli-runtime/application__harmony__HarmonyApplicationService-CFjocHoK.js +31 -0
  436. package/dist/cli-runtime/application__javascript__ElectronActiveObservationService-CmVDUI_Z.js +118 -0
  437. package/dist/cli-runtime/application__javascript__ElectronObservationService-DEsR8KDN.js +63 -0
  438. package/dist/cli-runtime/application__javascript__JavaScriptApplicationService-CCkp8KBh.js +11450 -0
  439. package/dist/cli-runtime/application__javascript__JavaScriptApplicationWorkflowService-lPy0tzr3.js +2329 -0
  440. package/dist/cli-runtime/application__javascript__JavaScriptRuntimeReconciliationService-DWleA8K9.js +88 -0
  441. package/dist/cli-runtime/application__javascript__JavaScriptSemanticTraceService-DkALH4rL.js +299 -0
  442. package/dist/cli-runtime/artifactOperationError-B7n03FbD.js +27 -0
  443. package/dist/cli-runtime/artifacts__ArtifactProvider-zXI0QjSL.js +2187 -0
  444. package/dist/cli-runtime/binary-BVOr2mgE.js +14206 -0
  445. package/dist/cli-runtime/browserObservationError-Dx6xRTjb.js +40 -0
  446. package/dist/cli-runtime/catalogIdentity-DLoXgfIR.js +933 -0
  447. package/dist/cli-runtime/cli.js +41848 -0
  448. package/dist/cli-runtime/cliOutput-BC1BdAcB.js +122 -0
  449. package/dist/cli-runtime/cliOutput.js +5 -0
  450. package/dist/cli-runtime/cliResultOutput-DsRMI8CJ.js +780 -0
  451. package/dist/cli-runtime/cliResultOutput.js +5 -0
  452. package/dist/cli-runtime/composition__browserScenario-DHXC1a8n.js +2927 -0
  453. package/dist/cli-runtime/composition__electronObservation-GecK2EID.js +555 -0
  454. package/dist/cli-runtime/composition__electronScenario-CEZnVrAL.js +653 -0
  455. package/dist/cli-runtime/cutterToolContracts-TZH9EHr5.js +5134 -0
  456. package/dist/cli-runtime/doctorRuntime-BE5ZX8it.js +258 -0
  457. package/dist/cli-runtime/dotnet__ManagedStaticProvider-C8_yZCDc.js +5 -0
  458. package/dist/cli-runtime/errorSchemas-CTRmvMl3.js +601 -0
  459. package/dist/cli-runtime/javascriptApplicationAnalysis-HgMzVMQw.js +2068 -0
  460. package/dist/cli-runtime/javascriptExportReturnShapeProjection-Dg00g35t.js +78 -0
  461. package/dist/cli-runtime/javascriptExportShapeComparisonSchemas-BYnoIqIk.js +1242 -0
  462. package/dist/cli-runtime/javascriptRuntimeReconciliation-DNjDoL3S.js +1507 -0
  463. package/dist/cli-runtime/javascriptRuntimeReconciliationSchemas-BWpGWNcI.js +247 -0
  464. package/dist/cli-runtime/javascriptSemanticTraceSchemas-BTCNcvbc.js +126 -0
  465. package/dist/cli-runtime/lipo-DSWP6fv-.js +396 -0
  466. package/dist/cli-runtime/logger-CQQCI4a2.js +48 -0
  467. package/dist/cli-runtime/machoContainer-C8BIkkq3.js +172 -0
  468. package/dist/cli-runtime/managedApplicationGraph-B08jb3jq.js +1130 -0
  469. package/dist/cli-runtime/managedToolContracts-D60QmIYj.js +99 -0
  470. package/dist/cli-runtime/mcpDoctor.js +359 -0
  471. package/dist/cli-runtime/mcpStartupPolicy-CVVSOmRc.js +21 -0
  472. package/dist/cli-runtime/native__NativeMacOSProvider-YBTvUeJO.js +4875 -0
  473. package/dist/cli-runtime/officialToolContracts-CCFF0an9.js +1501 -0
  474. package/dist/cli-runtime/propertyListKeys-G8e74YWb.js +248 -0
  475. package/dist/cli-runtime/providerAdapterError-DAjhICdr.js +25 -0
  476. package/dist/cli-runtime/providerCleanupError-Ceq8f0wK.js +27 -0
  477. package/dist/cli-runtime/providerIdentities-Ao1XTvwx.js +31 -0
  478. package/dist/cli-runtime/providerStartupTimeout-B2tF2LtZ.js +17 -0
  479. package/dist/cli-runtime/readFileHandleChunks-dp60iZ6v.js +38 -0
  480. package/dist/cli-runtime/reconstructionObligationLedgerSchemas-Bao0xMr-.js +19478 -0
  481. package/dist/cli-runtime/rolldown-runtime-DXoylpqc.js +121 -0
  482. package/dist/cli-runtime/runtimeVersion-CMGd0bDu.js +23 -0
  483. package/dist/cli-runtime/safeJson-DKKACtdy.js +25 -0
  484. package/dist/cli-runtime/scanCanonical-KP4T9jSl.js +2724 -0
  485. package/dist/cli-runtime/snapshotEnvironment-D_tWyOub.js +813 -0
  486. package/dist/cli-runtime/toolContracts-D2FOG_Nb.js +3721 -0
  487. package/dist/cli-runtime/toolOutputSchemaGroups-DRiYs1je.js +12070 -0
  488. package/dist/cli-runtime/workflowInputError-n8Oo-bLA.js +26 -0
  489. package/dist/cli.js +44 -5
  490. package/dist/cliBrowserScenarioCommands.js +41 -5
  491. package/dist/cliCommandNames.js +80 -0
  492. package/dist/cliEvidenceCommands.js +0 -1
  493. package/dist/cliJavaScriptRuntimeCommands.js +16 -14
  494. package/dist/cliJsonInput.js +14 -6
  495. package/dist/cliLogging.js +50 -14
  496. package/dist/cliObservationOptions.js +9 -0
  497. package/dist/cliOutput.js +38 -5
  498. package/dist/composition/adb.js +3 -0
  499. package/dist/composition/apktool.js +3 -0
  500. package/dist/composition/binary.js +4 -0
  501. package/dist/composition/binaryDiagnostics.js +4 -0
  502. package/dist/composition/captures.js +3 -0
  503. package/dist/composition/directAnalysis.js +36 -11
  504. package/dist/composition/electronFuses.js +4 -0
  505. package/dist/composition/evm.js +12 -0
  506. package/dist/composition/flutter.js +3 -0
  507. package/dist/composition/go.js +4 -0
  508. package/dist/composition/javascriptAnalysisWorkerProcess.js +49 -0
  509. package/dist/composition/jeb.js +35 -0
  510. package/dist/composition/pcap.js +3 -0
  511. package/dist/composition/resolveWindowsProviderExecutable.js +61 -0
  512. package/dist/composition/reverseEngineering.js +29 -0
  513. package/dist/composition/rust.js +4 -0
  514. package/dist/composition/signals.js +3 -0
  515. package/dist/composition/sqlite.js +4 -0
  516. package/dist/composition/wasm.js +22 -0
  517. package/dist/composition/webNetworkCaptures.js +7 -2
  518. package/dist/config/environment.js +55 -4
  519. package/dist/config/ghidraDisabledAnalyzers.js +25 -0
  520. package/dist/config/ghidraLanguageOverride.js +9 -0
  521. package/dist/config/homeDirectory.js +9 -6
  522. package/dist/config/mcpInputSchemaProfile.js +31 -0
  523. package/dist/config/mcpOutputSchemaProfile.js +22 -0
  524. package/dist/config/parseConfig.js +36 -19
  525. package/dist/config/parsers.js +4 -3
  526. package/dist/config/{ghidraStartupTimeout.js → providerStartupTimeout.js} +7 -3
  527. package/dist/contracts/adb/adbToolContracts.js +353 -0
  528. package/dist/contracts/advertisedInputExamples.js +48 -0
  529. package/dist/contracts/analysisDetail.js +13 -0
  530. package/dist/contracts/analysisViewApiSurfaceExample.js +68 -0
  531. package/dist/contracts/analysisViewToolContracts.js +39 -3
  532. package/dist/contracts/android/androidToolContracts.js +17 -8
  533. package/dist/contracts/apktool/apktoolToolContracts.js +33 -0
  534. package/dist/contracts/applicationToolContracts.js +17 -2
  535. package/dist/contracts/artifactToolContracts.js +21 -6
  536. package/dist/contracts/browserToolContracts.js +2 -1
  537. package/dist/contracts/captures/captureToolContracts.js +42 -0
  538. package/dist/contracts/compactInputSchema.js +276 -0
  539. package/dist/contracts/cutterToolContracts.js +84 -0
  540. package/dist/contracts/enhancedToolContracts.js +2 -2
  541. package/dist/contracts/evm/evmToolContracts.js +30 -15
  542. package/dist/contracts/firmware/firmwareToolContracts.js +1 -1
  543. package/dist/contracts/flutter/flutterToolContracts.js +47 -0
  544. package/dist/contracts/fridaRemoteAddress.js +23 -0
  545. package/dist/contracts/fridaToolContracts.js +273 -0
  546. package/dist/contracts/functionComparisonExample.js +12 -1
  547. package/dist/contracts/functionWorkflowToolContracts.js +1 -1
  548. package/dist/contracts/gdbToolContracts.js +103 -0
  549. package/dist/contracts/go/goToolContracts.js +20 -0
  550. package/dist/contracts/hopperMemoryContracts.js +15 -0
  551. package/dist/contracts/inputSchemaPresentation.js +91 -24
  552. package/dist/contracts/investigationExamples.js +1 -0
  553. package/dist/contracts/javascript/electronToolContracts.js +40 -3
  554. package/dist/contracts/javascript/javascriptRecoveryToolContracts.js +13 -1
  555. package/dist/contracts/jeb/jebToolContracts.js +64 -0
  556. package/dist/contracts/managed/managedToolContracts.js +43 -4
  557. package/dist/contracts/managed/managedWorkflowExamples.js +7 -1
  558. package/dist/contracts/managed/managedWorkflowToolContracts.js +4 -30
  559. package/dist/contracts/mobileApplicationGraphExamples.js +12 -0
  560. package/dist/contracts/native/binaryDiagnosticsToolContracts.js +27 -4
  561. package/dist/contracts/native/nativeToolContracts.js +10 -6
  562. package/dist/contracts/officialToolContracts.js +9 -9
  563. package/dist/contracts/pcap/pcapToolContracts.js +33 -0
  564. package/dist/contracts/providerSelection.js +1 -0
  565. package/dist/contracts/reconstructionReadinessExample.js +126 -119
  566. package/dist/contracts/reverseEngineeringToolContracts.js +52 -0
  567. package/dist/contracts/rizinDebugToolContracts.js +96 -0
  568. package/dist/contracts/rust/rustToolContracts.js +20 -0
  569. package/dist/contracts/sessionLifecycleInputs.js +34 -2
  570. package/dist/contracts/sessionToolContracts.js +4 -4
  571. package/dist/contracts/sessionToolSchemas.js +20 -2
  572. package/dist/contracts/signals/signalToolContracts.js +40 -0
  573. package/dist/contracts/sqlite/sqliteToolContracts.js +36 -0
  574. package/dist/contracts/toolContractExamples.js +35 -0
  575. package/dist/contracts/toolContractTypes.js +5 -0
  576. package/dist/contracts/toolContracts.js +32 -0
  577. package/dist/contracts/toolEffects.js +414 -8
  578. package/dist/contracts/toolOutputSchemaGroups.js +78 -33
  579. package/dist/contracts/toolOutputSchemaPrimitives.js +111 -10
  580. package/dist/contracts/toolSchemaMetadata.js +98 -9
  581. package/dist/contracts/unknownContractExamples.js +1 -1
  582. package/dist/contracts/wasm/wasmComparisonContracts.js +52 -0
  583. package/dist/contracts/wasm/wasmToolContracts.js +33 -0
  584. package/dist/contracts/webNetworkCaptureToolContracts.js +15 -4
  585. package/dist/cutter/CutterBridgeClient.js +621 -0
  586. package/dist/cutter/CutterBridgeService.js +139 -0
  587. package/dist/doctorRuntime.js +20 -8
  588. package/dist/domain/adb/adbDeviceAnalysis.js +605 -0
  589. package/dist/domain/analysisErrorBase.js +20 -0
  590. package/dist/domain/analysisErrorCleanup.js +29 -0
  591. package/dist/domain/analysisErrorCore.js +19 -0
  592. package/dist/domain/analysisErrorPresentation.js +75 -22
  593. package/dist/domain/analysisErrorProjection.js +35 -13
  594. package/dist/domain/analysisProfile.js +7 -3
  595. package/dist/domain/analysisSnapshot.js +313 -185
  596. package/dist/domain/analysisView/WebNetworkApiSurfaceAccumulator.js +414 -0
  597. package/dist/domain/analysisView/analysisView.js +417 -45
  598. package/dist/domain/analysisView/analysisViewCoverage.js +32 -0
  599. package/dist/domain/analysisView/analysisViewErrors.js +4 -0
  600. package/dist/domain/analysisView/analysisViewParent.js +12 -0
  601. package/dist/domain/analysisView/analysisViewText.js +29 -0
  602. package/dist/domain/analysisView/androidPackageView.js +102 -0
  603. package/dist/domain/analysisView/artifactInventoryView.js +140 -0
  604. package/dist/domain/analysisView/binaryLayoutView.js +13 -4
  605. package/dist/domain/analysisView/dartAotView.js +93 -0
  606. package/dist/domain/analysisView/evmInterfaceView.js +105 -0
  607. package/dist/domain/analysisView/interfaceBuilderView.js +182 -0
  608. package/dist/domain/analysisView/javascriptApplicationView.js +29 -26
  609. package/dist/domain/analysisView/machoView.js +186 -0
  610. package/dist/domain/analysisView/managedArtifactView.js +108 -0
  611. package/dist/domain/analysisView/managedMemberView.js +209 -0
  612. package/dist/domain/analysisView/nativeFunctionView.js +150 -0
  613. package/dist/domain/analysisView/pcapCaptureView.js +264 -0
  614. package/dist/domain/analysisView/qlogCaptureView.js +198 -0
  615. package/dist/domain/analysisView/wasmArtifactView.js +162 -0
  616. package/dist/domain/analysisView/webNetworkApiSurfaceView.js +123 -0
  617. package/dist/domain/analysisView/webNetworkCaptureView.js +388 -0
  618. package/dist/domain/android/androidAnalysis.js +36 -2
  619. package/dist/domain/android/androidApplication.js +57 -80
  620. package/dist/domain/apktool/apktoolResourceAnalysis.js +89 -0
  621. package/dist/domain/apple/appleApplication.js +71 -52
  622. package/dist/domain/apple/appleArchiveFormat.js +184 -0
  623. package/dist/domain/apple/appleAssetCatalog.js +15 -0
  624. package/dist/domain/apple/appleBundleAnatomy.js +96 -29
  625. package/dist/domain/apple/dyldPaths.js +18 -3
  626. package/dist/domain/apple/dylibResolution.js +21 -11
  627. package/dist/domain/apple/dylibResolutionFindings.js +8 -9
  628. package/dist/domain/apple/interfaceBuilderGraph.js +72 -45
  629. package/dist/domain/apple/interfaceBuilderKeyedArchive.js +38 -9
  630. package/dist/domain/apple/machoContainer.js +120 -8
  631. package/dist/domain/apple/machoCpuIdentity.js +4 -0
  632. package/dist/domain/apple/plistValue.js +6 -1
  633. package/dist/domain/artifactComparison.js +130 -25
  634. package/dist/domain/artifactDecompressionBudget.js +26 -0
  635. package/dist/domain/artifactGraph.js +28 -9
  636. package/dist/domain/artifactIdentity.js +33 -2
  637. package/dist/domain/artifactInspection.js +47 -45
  638. package/dist/domain/artifactIntegrityPolicy.js +10 -0
  639. package/dist/domain/artifactInventoryEvidence.js +148 -51
  640. package/dist/domain/artifactPathSyntax.js +1 -1
  641. package/dist/domain/binaryTarget.js +253 -80
  642. package/dist/domain/binaryTargetTypes.js +21 -1
  643. package/dist/domain/browserNetworkEvidence.js +4 -0
  644. package/dist/domain/browserObservation.js +88 -29
  645. package/dist/domain/browserObservationSchemas.js +12 -5
  646. package/dist/domain/browserScenarioCapture.js +2 -1
  647. package/dist/domain/browserScenarioCaptureValues.js +26 -2
  648. package/dist/domain/browserScenarioDiff.js +120 -11
  649. package/dist/domain/browserScenarioDiffEvidence.js +46 -13
  650. package/dist/domain/browserScenarioDiffValues.js +10 -3
  651. package/dist/domain/browserScenarioNetworkComparison.js +158 -0
  652. package/dist/domain/browserScenarioNormalization.js +10 -13
  653. package/dist/domain/browserScenarioValues.js +2 -12
  654. package/dist/domain/browserSession.js +2 -2
  655. package/dist/domain/bundleComparison.js +1 -5
  656. package/dist/domain/callPath.js +40 -3
  657. package/dist/domain/canonicalJsonDigestSteps.js +34 -12
  658. package/dist/domain/captures/packetCapture.js +138 -0
  659. package/dist/domain/captures/packetFields.js +81 -0
  660. package/dist/domain/changedBehavior.js +43 -41
  661. package/dist/domain/compactStringIdentity.js +9 -0
  662. package/dist/domain/comparisonSourceEvidence.js +6 -0
  663. package/dist/domain/dosCom.js +2 -2
  664. package/dist/domain/dosMz.js +52 -14
  665. package/dist/domain/electron/electronFuses.js +360 -0
  666. package/dist/domain/elfFixedHeader.js +42 -0
  667. package/dist/domain/evidence.js +104 -38
  668. package/dist/domain/evidenceBundle.js +58 -20
  669. package/dist/domain/evidenceErrors.js +5 -0
  670. package/dist/domain/evidenceRecordSummary.js +144 -0
  671. package/dist/domain/evm/evmInterface.js +18 -4
  672. package/dist/domain/evm/solidityMetadata.js +70 -0
  673. package/dist/domain/explicitSensitiveFailure.js +42 -24
  674. package/dist/domain/firmware/firmwareAnalysis.js +29 -0
  675. package/dist/domain/flutter/flutterBuildAnalysis.js +141 -0
  676. package/dist/domain/functionCallCoverage.js +60 -0
  677. package/dist/domain/functionComparisonDimensions.js +101 -10
  678. package/dist/domain/functionComparisonNormalization.js +66 -10
  679. package/dist/domain/functionComparisonResults.js +7 -6
  680. package/dist/domain/functionComparisonSchemas.js +14 -3
  681. package/dist/domain/functionDossierEvidence.js +3 -0
  682. package/dist/domain/go/goBinary.js +96 -0
  683. package/dist/domain/harmony/harmonyApplication.js +156 -0
  684. package/dist/domain/hopperErrors.js +15 -16
  685. package/dist/domain/hopperPublisher.js +12 -0
  686. package/dist/domain/hopperValues.js +112 -34
  687. package/dist/domain/inputIssueProjection.js +16 -1
  688. package/dist/domain/javascript/electronObservation.js +4 -1
  689. package/dist/domain/javascript/electronStaticAnalysisBrowser.js +24 -24
  690. package/dist/domain/javascript/electronStaticAnalysisIpc.js +13 -20
  691. package/dist/domain/javascript/electronStaticAnalysisNative.js +85 -41
  692. package/dist/domain/javascript/electronStaticAnalysisValues.js +30 -8
  693. package/dist/domain/javascript/electronWrappedCallPaths.js +498 -0
  694. package/dist/domain/javascript/htmlArtifactReferences.js +94 -4
  695. package/dist/domain/javascript/javascriptAnalysisResourceControls.js +65 -0
  696. package/dist/domain/javascript/javascriptApplicationAnalysis.js +7 -1
  697. package/dist/domain/javascript/javascriptApplicationChangeGraph.js +13 -8
  698. package/dist/domain/javascript/javascriptApplicationEvidenceSchemas.js +1 -1
  699. package/dist/domain/javascript/javascriptApplicationGraph.js +96 -19
  700. package/dist/domain/javascript/javascriptApplicationGraphSchemas.js +1 -0
  701. package/dist/domain/javascript/javascriptApplicationProjectionResources.js +43 -0
  702. package/dist/domain/javascript/javascriptApplicationVersionItems.js +28 -12
  703. package/dist/domain/javascript/javascriptApplicationVersionKeys.js +40 -19
  704. package/dist/domain/javascript/javascriptArtifactFiles.js +2 -1
  705. package/dist/domain/javascript/javascriptAstFingerprint.js +100 -39
  706. package/dist/domain/javascript/javascriptAstValues.js +81 -0
  707. package/dist/domain/javascript/javascriptBootstrap.js +94 -0
  708. package/dist/domain/javascript/javascriptBootstrapIntegrity.js +372 -0
  709. package/dist/domain/javascript/javascriptBuiltinResourceMethods.js +8 -0
  710. package/dist/domain/javascript/javascriptBundledRequireOrigin.js +291 -0
  711. package/dist/domain/javascript/javascriptContextBridgeExposure.js +23 -0
  712. package/dist/domain/javascript/javascriptElectronMemberWrites.js +114 -0
  713. package/dist/domain/javascript/javascriptExportShapeComparisonSchemas.js +6 -5
  714. package/dist/domain/javascript/javascriptExportShapeVariants.js +1 -1
  715. package/dist/domain/javascript/javascriptFeatureSeed.js +33 -16
  716. package/dist/domain/javascript/javascriptFeatureTrace.js +9 -3
  717. package/dist/domain/javascript/javascriptFeatureTraceSchemas.js +42 -11
  718. package/dist/domain/javascript/javascriptIntrinsicInvocation.js +132 -0
  719. package/dist/domain/javascript/javascriptModuleDeclaration.js +28 -0
  720. package/dist/domain/javascript/javascriptModuleInterop.js +618 -0
  721. package/dist/domain/javascript/javascriptModuleResolutionMode.js +18 -0
  722. package/dist/domain/javascript/javascriptModuleSemanticIr.js +15 -0
  723. package/dist/domain/javascript/javascriptModuleSpecifier.js +5 -0
  724. package/dist/domain/javascript/javascriptNativeHandoff.js +6 -2
  725. package/dist/domain/javascript/javascriptPackageImportAliases.js +58 -0
  726. package/dist/domain/javascript/javascriptRecovery.js +12 -5
  727. package/dist/domain/javascript/javascriptRpcEndpoints.js +331 -0
  728. package/dist/domain/javascript/javascriptRuntimeLoadState.js +12 -3
  729. package/dist/domain/javascript/javascriptRuntimeObservation.js +4 -1
  730. package/dist/domain/javascript/javascriptRuntimeReconciliationCaptureParsing.js +10 -4
  731. package/dist/domain/javascript/javascriptRuntimeReconciliationGraph.js +7 -2
  732. package/dist/domain/javascript/javascriptRuntimeReconciliationMatching.js +15 -2
  733. package/dist/domain/javascript/javascriptRuntimeReconciliationResult.js +0 -1
  734. package/dist/domain/javascript/javascriptRuntimeReconciliationSchemas.js +5 -4
  735. package/dist/domain/javascript/javascriptRuntimeStaticCandidates.js +121 -22
  736. package/dist/domain/javascript/javascriptSemanticAmbientWrites.js +251 -0
  737. package/dist/domain/javascript/javascriptSemanticAnalysis.js +493 -104
  738. package/dist/domain/javascript/javascriptSemanticAsyncEffects.js +17 -34
  739. package/dist/domain/javascript/javascriptSemanticCallResolution.js +4 -3
  740. package/dist/domain/javascript/javascriptSemanticCallableResults.js +42 -0
  741. package/dist/domain/javascript/javascriptSemanticCallableStack.js +52 -0
  742. package/dist/domain/javascript/javascriptSemanticCalls.js +14 -12
  743. package/dist/domain/javascript/javascriptSemanticChildProcesses.js +42 -55
  744. package/dist/domain/javascript/javascriptSemanticDataEffectHelpers.js +428 -32
  745. package/dist/domain/javascript/javascriptSemanticDataEffects.js +14 -20
  746. package/dist/domain/javascript/javascriptSemanticDependencies.js +69 -0
  747. package/dist/domain/javascript/javascriptSemanticDerivedAnalysis.js +15 -4
  748. package/dist/domain/javascript/javascriptSemanticFileProjection.js +1 -0
  749. package/dist/domain/javascript/javascriptSemanticFingerprints.js +51 -53
  750. package/dist/domain/javascript/javascriptSemanticGraph.js +74 -24
  751. package/dist/domain/javascript/javascriptSemanticGraphSchemas.js +2 -11
  752. package/dist/domain/javascript/javascriptSemanticIterationSources.js +41 -8
  753. package/dist/domain/javascript/javascriptSemanticMemberMutations.js +538 -151
  754. package/dist/domain/javascript/javascriptSemanticMutationInitializers.js +199 -3
  755. package/dist/domain/javascript/javascriptSemanticMutationValues.js +78 -35
  756. package/dist/domain/javascript/javascriptSemanticObjectReferences.js +671 -0
  757. package/dist/domain/javascript/javascriptSemanticObjects.js +27 -17
  758. package/dist/domain/javascript/javascriptSemanticProjection.js +202 -56
  759. package/dist/domain/javascript/javascriptSemanticPromises.js +6 -9
  760. package/dist/domain/javascript/javascriptSemanticPropertyPaths.js +77 -0
  761. package/dist/domain/javascript/javascriptSemanticProvenance.js +5 -1
  762. package/dist/domain/javascript/javascriptSemanticQuery.js +34 -31
  763. package/dist/domain/javascript/javascriptSemanticQueryIdentity.js +1 -3
  764. package/dist/domain/javascript/javascriptSemanticReferenceSources.js +25 -0
  765. package/dist/domain/javascript/javascriptSemanticRequireOrigin.js +331 -8
  766. package/dist/domain/javascript/javascriptSemanticResources.js +6 -40
  767. package/dist/domain/javascript/javascriptSemanticReturnedReferences.js +233 -0
  768. package/dist/domain/javascript/javascriptSemanticReturns.js +49 -52
  769. package/dist/domain/javascript/javascriptSemanticState.js +61 -4
  770. package/dist/domain/javascript/javascriptSemanticTraversal.js +25 -5
  771. package/dist/domain/javascript/javascriptSemanticValues.js +260 -35
  772. package/dist/domain/javascript/javascriptSourceAnalysis.js +1 -0
  773. package/dist/domain/javascript/javascriptSourceMapPaths.js +44 -17
  774. package/dist/domain/javascript/javascriptSourceParser.js +72 -3
  775. package/dist/domain/javascript/javascriptSourceSelection.js +11 -0
  776. package/dist/domain/javascript/javascriptStaticAnalysis.js +72 -26
  777. package/dist/domain/javascript/javascriptStaticAnalysisBundler.js +100 -24
  778. package/dist/domain/javascript/javascriptStaticAnalysisCalls.js +271 -47
  779. package/dist/domain/javascript/javascriptStaticAnalysisFindings.js +8 -4
  780. package/dist/domain/javascript/javascriptStaticAnalysisHelpers.js +166 -56
  781. package/dist/domain/javascript/javascriptStaticAnalysisState.js +2 -0
  782. package/dist/domain/javascript/javascriptStaticAnalysisTypes.js +2 -1
  783. package/dist/domain/javascript/javascriptSymbolicBindings.js +836 -0
  784. package/dist/domain/javascript/javascriptSymbolicControlFlow.js +40 -0
  785. package/dist/domain/javascript/javascriptSymbolicValues.js +34 -0
  786. package/dist/domain/javascript/sourceToBundleComparison.js +3 -1
  787. package/dist/domain/javascript/sourceToBundleComparisonSchemas.js +2 -0
  788. package/dist/domain/javascript/sourceToBundleSignals.js +47 -13
  789. package/dist/domain/jeb/jebAnalysis.js +114 -0
  790. package/dist/domain/jsonShape.js +63 -52
  791. package/dist/domain/jsonValue.js +40 -12
  792. package/dist/domain/localPath.js +6 -3
  793. package/dist/domain/managed/managedApplicationGraph.js +42 -0
  794. package/dist/domain/managed/managedApplicationGraphCoverage.js +12 -2
  795. package/dist/domain/managed/managedApplicationGraphNodes.js +32 -23
  796. package/dist/domain/managed/managedArtifact.js +52 -1
  797. package/dist/domain/managed/managedInspectionEvidence.js +12 -0
  798. package/dist/domain/managed/managedMemberComparison.js +3 -0
  799. package/dist/domain/managed/managedMemberComparisonCoverage.js +23 -3
  800. package/dist/domain/managed/managedMemberComparisonItems.js +23 -19
  801. package/dist/domain/managed/managedMemberComparisonMatch.js +209 -18
  802. package/dist/domain/managed/managedNativeVerificationMatch.js +49 -26
  803. package/dist/domain/managed/managedNativeVerificationSchemas.js +13 -2
  804. package/dist/domain/managed/managedReconstruction.js +5 -8
  805. package/dist/domain/mobileApplicationGraphProjection.js +80 -0
  806. package/dist/domain/native/addressCoordinate.js +11 -0
  807. package/dist/domain/native/binaryLayout.js +2 -4
  808. package/dist/domain/native/bytePattern.js +100 -0
  809. package/dist/domain/native/nativeAotPe.js +1408 -0
  810. package/dist/domain/native/nativeInspection.js +38 -4
  811. package/dist/domain/native/nativeInstruction.js +38 -2
  812. package/dist/domain/native/nativeInvestigationGraph.js +9 -5
  813. package/dist/domain/native/nativeLoadImage.js +19 -1
  814. package/dist/domain/native/nativeMetadataRecovery.js +79 -6
  815. package/dist/domain/native/nativeUiObservation.js +5 -6
  816. package/dist/domain/native/objcSwiftMetadata.js +4 -1
  817. package/dist/domain/native/peResources.js +100 -0
  818. package/dist/domain/native/recordedCrash.js +79 -10
  819. package/dist/domain/nativeConvention.js +33 -0
  820. package/dist/domain/offsetDateTime.js +60 -0
  821. package/dist/domain/pcap/pcapAnalysis.js +91 -0
  822. package/dist/domain/pcap/pcapEvidence.js +15 -0
  823. package/dist/domain/peMachine.js +36 -0
  824. package/dist/domain/pngHeader.js +18 -0
  825. package/dist/domain/process/processCapture.js +91 -1
  826. package/dist/domain/process/processCaptureCoverage.js +26 -3
  827. package/dist/domain/process/processCaptureExample.js +26 -0
  828. package/dist/domain/process/processCaptureValidation.js +112 -8
  829. package/dist/domain/process/processComparison.js +41 -13
  830. package/dist/domain/process/processScenario.js +66 -7
  831. package/dist/domain/process/processTraceEvaluation.js +54 -50
  832. package/dist/domain/process/processTraceSpecification.js +243 -89
  833. package/dist/domain/propertyListKeys.js +214 -12
  834. package/dist/domain/propertyListXmlText.js +20 -4
  835. package/dist/domain/providerAdapterError.js +0 -5
  836. package/dist/domain/providerConfigurationRefusal.js +12 -0
  837. package/dist/domain/providerSelectionError.js +2 -1
  838. package/dist/domain/qlogCapture.js +62 -0
  839. package/dist/domain/rawBinary.js +96 -0
  840. package/dist/domain/reconstructionCoverage.js +5 -1
  841. package/dist/domain/reconstructionCoverageEvaluation.js +18 -15
  842. package/dist/domain/reconstructionCoverageVerifierResults.js +35 -0
  843. package/dist/domain/reconstructionObligationLedgerIntegrity.js +114 -0
  844. package/dist/domain/reconstructionObligationLedgerSchemas.js +104 -1
  845. package/dist/domain/reconstructionReadiness.js +4 -8
  846. package/dist/domain/reconstructionReadinessJourneyFindings.js +4 -1
  847. package/dist/domain/reconstructionUnknowns.js +9 -1
  848. package/dist/domain/reconstructionVerification.js +54 -25
  849. package/dist/domain/recordedTransportUrl.js +59 -0
  850. package/dist/domain/referenceSourceClassification.js +27 -23
  851. package/dist/domain/referenceSourceGraph.js +70 -23
  852. package/dist/domain/referenceSourceImportParsing.js +29 -16
  853. package/dist/domain/referenceSourcePathIdentity.js +32 -0
  854. package/dist/domain/referenceSourceRequireBindings.js +130 -0
  855. package/dist/domain/referenceSourceSummary.js +92 -0
  856. package/dist/domain/relationshipCoverage.js +44 -0
  857. package/dist/domain/residualUnknown.js +93 -26
  858. package/dist/domain/reverseEngineering.js +31 -0
  859. package/dist/domain/runtimeVersion.js +8 -0
  860. package/dist/domain/rust/rustBinary.js +200 -0
  861. package/dist/domain/signals/annotationTextWindow.js +31 -0
  862. package/dist/domain/signals/signalDecoders.js +69 -0
  863. package/dist/domain/signals/signalRecording.js +115 -0
  864. package/dist/domain/sourceMapContents.js +10 -3
  865. package/dist/domain/sourceMapEnvelope.js +17 -11
  866. package/dist/domain/sqlite/sqliteDatabase.js +188 -0
  867. package/dist/domain/staticRuntimeCorrelation.js +7 -9
  868. package/dist/domain/swiftSymbolClassification.js +17 -0
  869. package/dist/domain/symbolAnalysis.js +16 -4
  870. package/dist/domain/urlComponents.js +6 -0
  871. package/dist/domain/wasm/wasmArtifact.js +119 -0
  872. package/dist/domain/wasm/wasmInterface.js +89 -0
  873. package/dist/domain/webBundleAnalysis.js +14 -3
  874. package/dist/domain/webBundleAnalyzerAst.js +7 -2
  875. package/dist/domain/webBundleAnalyzerInspection.js +68 -40
  876. package/dist/domain/webBundleAnalyzerResult.js +18 -6
  877. package/dist/domain/webCaptureDiff.js +233 -34
  878. package/dist/domain/webCaptureFrameComparison.js +49 -0
  879. package/dist/domain/webMcpDiscovery.js +5 -2
  880. package/dist/domain/webModuleImports.js +22 -27
  881. package/dist/domain/webModuleTrace.js +3 -8
  882. package/dist/domain/webNetworkCapture.js +101 -39
  883. package/dist/domain/webNetworkCaptureCoordinates.js +21 -0
  884. package/dist/domain/webRuntime.js +0 -1
  885. package/dist/domain/webScreenshot.js +2 -2
  886. package/dist/domain/webScriptArtifacts.js +2 -4
  887. package/dist/domain/webScriptExport.js +6 -8
  888. package/dist/domain/webScriptExportError.js +10 -4
  889. package/dist/domain/webScriptExportPlan.js +9 -15
  890. package/dist/domain/webSourceLocation.js +20 -9
  891. package/dist/domain/zipPackageFormat.js +31 -2
  892. package/dist/domain/zodCollectionSteps.js +24 -0
  893. package/dist/dotnet/ManagedArtifactInspector.js +81 -54
  894. package/dist/dotnet/ManagedDecodeBudget.js +138 -0
  895. package/dist/dotnet/ManagedMemberInspector.js +113 -26
  896. package/dist/dotnet/ManagedMemberInspectorCore.js +356 -140
  897. package/dist/dotnet/ManagedMemberInstructionDecoder.js +112 -38
  898. package/dist/dotnet/ManagedMemberOwnership.js +65 -0
  899. package/dist/dotnet/ManagedMemberPointers.js +69 -0
  900. package/dist/dotnet/ManagedMemberRows.js +182 -44
  901. package/dist/dotnet/ManagedMetadataHeaps.js +123 -27
  902. package/dist/dotnet/ManagedMetadataInventory.js +5 -5
  903. package/dist/dotnet/ManagedMetadataInventoryRows.js +56 -63
  904. package/dist/dotnet/ManagedMetadataLayout.js +19 -5
  905. package/dist/dotnet/ManagedMetadataRowSizes.js +1 -1
  906. package/dist/dotnet/ManagedMethodBodyReader.js +34 -11
  907. package/dist/dotnet/ManagedMethodSpecs.js +78 -0
  908. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +5 -4
  909. package/dist/dotnet/ManagedNativeBoundaryInspector.js +52 -44
  910. package/dist/dotnet/ManagedPeReader.js +96 -52
  911. package/dist/dotnet/ManagedReaderFailure.js +11 -1
  912. package/dist/dotnet/ManagedSnapshot.js +88 -0
  913. package/dist/dotnet/ManagedStaticProvider.js +57 -35
  914. package/dist/dotnet/ManagedTypeMemberLists.js +105 -0
  915. package/dist/dotnet/ManagedTypeNames.js +98 -0
  916. package/dist/dotnet/ManagedUserStrings.js +75 -0
  917. package/dist/electron/ElectronFusePath.js +247 -0
  918. package/dist/electron/ElectronFuseProvider.js +113 -0
  919. package/dist/electron/ElectronFuseReader.js +317 -0
  920. package/dist/evaluation/AgentEvaluationFixtures.js +194 -11
  921. package/dist/evaluation/AgentEvaluationReport.js +9 -5
  922. package/dist/evaluation/CodexAgentEval.js +58 -49
  923. package/dist/evaluation/KnownAnswerEvaluation.js +126 -57
  924. package/dist/evm/EvmoleFailures.js +19 -16
  925. package/dist/evm/EvmoleInterfaceAdapter.js +5 -1
  926. package/dist/evm/EvmoleInterfaceProvider.js +198 -23
  927. package/dist/evm/EvmoleInterfaceWorker.js +2 -0
  928. package/dist/evm/SolidityMetadata.js +139 -0
  929. package/dist/filesystem/OwnedDirectoryHandle.js +29 -0
  930. package/dist/filesystem/OwnedFileHandle.js +36 -0
  931. package/dist/filesystem/OwnedStagedOutput.js +138 -0
  932. package/dist/filesystem/RegularFile.js +81 -5
  933. package/dist/filesystem/StableFileHash.js +46 -0
  934. package/dist/filesystem/readFileHandleChunks.js +35 -0
  935. package/dist/firmware/FirmwareCommand.js +27 -5
  936. package/dist/firmware/FirmwareFiles.js +18 -7
  937. package/dist/firmware/FirmwareProvider.js +158 -32
  938. package/dist/firmware/FirmwarePublication.js +242 -4
  939. package/dist/firmware/FirmwareRelease.js +1 -0
  940. package/dist/firmware/FirmwareReports.js +202 -12
  941. package/dist/flutter/DartElf.js +418 -0
  942. package/dist/flutter/ElfBuildId.js +137 -0
  943. package/dist/flutter/ElfLoadMap.js +91 -0
  944. package/dist/flutter/FlutterBuildProvider.js +826 -0
  945. package/dist/flutter/FlutterPayloadScan.js +180 -0
  946. package/dist/frida/FridaInstrumentationManager.js +1104 -0
  947. package/dist/gdb/GdbSessionManager.js +1451 -0
  948. package/dist/generatedPackageMetadata.js +2 -2
  949. package/dist/ghidra/GhidraAnalysisProfile.js +175 -6
  950. package/dist/ghidra/GhidraAnalysisSeeds.js +211 -0
  951. package/dist/ghidra/GhidraClient.js +208 -29
  952. package/dist/ghidra/GhidraDiagnostics.js +18 -8
  953. package/dist/ghidra/GhidraDoctor.js +15 -12
  954. package/dist/ghidra/GhidraFunctionValues.js +73 -2
  955. package/dist/ghidra/GhidraInstallation.js +216 -20
  956. package/dist/ghidra/GhidraInstallationPolicy.js +79 -29
  957. package/dist/ghidra/GhidraInventoryValues.js +53 -1
  958. package/dist/ghidra/GhidraJavaLaunch.js +91 -10
  959. package/dist/ghidra/GhidraJavaProperties.js +117 -0
  960. package/dist/ghidra/GhidraLanguageCatalog.js +169 -0
  961. package/dist/ghidra/GhidraLauncher.js +306 -88
  962. package/dist/ghidra/GhidraLoadImageAttest.js +74 -7
  963. package/dist/ghidra/GhidraLoadImageValues.js +72 -0
  964. package/dist/ghidra/GhidraMipsProfile.js +315 -0
  965. package/dist/ghidra/GhidraPeProfile.js +21 -0
  966. package/dist/ghidra/GhidraPowerPcProfile.js +51 -0
  967. package/dist/ghidra/GhidraProvider.js +106 -20
  968. package/dist/ghidra/GhidraProviderCapabilities.js +30 -19
  969. package/dist/ghidra/GhidraProviderClient.js +520 -56
  970. package/dist/ghidra/GhidraPspExtension.js +140 -0
  971. package/dist/ghidra/GhidraPspProfile.js +59 -0
  972. package/dist/ghidra/GhidraRawBinaryLanguage.js +40 -0
  973. package/dist/ghidra/GhidraRiscvProfile.js +100 -0
  974. package/dist/ghidra/GhidraSessionError.js +155 -11
  975. package/dist/ghidra/GhidraSessionValues.js +75 -2
  976. package/dist/ghidra/GhidraTransport.js +2 -1
  977. package/dist/go/GoBinaryContainer.js +140 -0
  978. package/dist/go/GoBinaryImage.js +210 -0
  979. package/dist/go/GoBinaryProvider.js +151 -0
  980. package/dist/go/GoElfImage.js +211 -0
  981. package/dist/go/GoMachoImage.js +164 -0
  982. package/dist/go/GoModuleText.js +217 -0
  983. package/dist/go/GoPeImage.js +124 -0
  984. package/dist/hopper/BridgeLauncher.js +35 -2
  985. package/dist/hopper/HopperAnalysisProfile.js +21 -9
  986. package/dist/hopper/HopperCleanup.js +15 -3
  987. package/dist/hopper/HopperClient.js +203 -42
  988. package/dist/hopper/HopperMacPublisher.js +29 -0
  989. package/dist/hopper/HopperMachOImage.js +61 -27
  990. package/dist/hopper/HopperProcessDiagnostic.js +27 -0
  991. package/dist/hopper/HopperProvider.js +68 -26
  992. package/dist/hopper/HopperProviderCapabilities.js +7 -3
  993. package/dist/hopper/HopperRequestQueue.js +17 -5
  994. package/dist/hopper/HopperSessionValues.js +10 -1
  995. package/dist/hopper/HopperTargetLease.js +66 -19
  996. package/dist/ida/IdaBusyHealth.js +44 -0
  997. package/dist/ida/IdaCallGraph.js +134 -0
  998. package/dist/ida/IdaConfiguration.js +7 -2
  999. package/dist/ida/IdaDiagnosticRedactor.js +199 -0
  1000. package/dist/ida/IdaDiagnosticWriter.js +62 -0
  1001. package/dist/ida/IdaDoctor.js +7 -5
  1002. package/dist/ida/IdaJsonDiagnostic.js +138 -0
  1003. package/dist/ida/IdaMcpConnection.js +297 -27
  1004. package/dist/ida/IdaNativeProtocol.js +93 -0
  1005. package/dist/ida/IdaOperationRunner.js +249 -40
  1006. package/dist/ida/IdaProtocolValues.js +86 -4
  1007. package/dist/ida/IdaProvider.js +34 -8
  1008. package/dist/ida/IdaProviderCapabilities.js +37 -8
  1009. package/dist/ida/IdaSessionClient.js +332 -53
  1010. package/dist/ida/IdaWorkspace.js +57 -7
  1011. package/dist/inspector/V8InspectorCaptureProjection.js +7 -2
  1012. package/dist/inspector/V8InspectorEndpoint.js +15 -13
  1013. package/dist/inspector/V8InspectorProvider.js +41 -7
  1014. package/dist/javascript/analysis/JavaScriptAnalysisTransfer.js +337 -0
  1015. package/dist/javascript/analysis/JavaScriptAnalysisWorker.js +594 -0
  1016. package/dist/javascript/analysis/JavaScriptAnalysisWorkerProtocol.js +65 -0
  1017. package/dist/javascript/analysis/JavaScriptAnalysisWorkerSchemas.js +590 -0
  1018. package/dist/javascript/analysis/JavaScriptAnalysisWorkerServer.js +126 -0
  1019. package/dist/javascript/recovery/RecoveryFiles.js +68 -12
  1020. package/dist/javascript/recovery/WakaruCommand.js +22 -9
  1021. package/dist/javascript/recovery/WakaruExecution.js +30 -18
  1022. package/dist/javascript/recovery/WakaruProvider.js +150 -24
  1023. package/dist/javascript/recovery/WakaruPublication.js +38 -14
  1024. package/dist/javascript/recovery/WakaruRelease.js +11 -4
  1025. package/dist/javascript/recovery/WakaruReport.js +15 -9
  1026. package/dist/javascript/recovery/WakaruVersion.js +24 -0
  1027. package/dist/javascript/sourceMaps/DecodedSourceMap.js +2 -16
  1028. package/dist/javascript/sourceMaps/SourceMapCommand.js +38 -7
  1029. package/dist/javascript/sourceMaps/SourceMapDecoder.js +84 -10
  1030. package/dist/javascript/sourceMaps/SourceMapFormat.js +26 -28
  1031. package/dist/javascript/sourceMaps/TraceSourceMap.js +34 -6
  1032. package/dist/jeb/JebMcpConnection.js +186 -0
  1033. package/dist/jeb/JebMcpEndpoint.js +45 -0
  1034. package/dist/jeb/JebProvider.js +297 -0
  1035. package/dist/logger.js +14 -1
  1036. package/dist/main/shutdown.js +3 -0
  1037. package/dist/main/transport.js +48 -4
  1038. package/dist/main.js +14 -3
  1039. package/dist/mcp/AnalysisSnapshotFiles-BAbiJQOc.js +1412 -0
  1040. package/dist/mcp/ArtifactProvider-DViCU_rn.js +2183 -0
  1041. package/dist/mcp/ArtifactProviderMetadata-b7pOK5_t.js +76 -0
  1042. package/dist/mcp/ArtifactReader-OyM03Gjn.js +55 -0
  1043. package/dist/mcp/ArtifactResourceScope-P3tq5z8Z.js +162 -0
  1044. package/dist/mcp/AsarHeader-WsPgoooH.js +31 -0
  1045. package/dist/mcp/BinarySessionStatus-B4P9gf-S.js +212 -0
  1046. package/dist/mcp/BinaryTargetResolver-BMaE_0H1.js +1427 -0
  1047. package/dist/mcp/BoundedFileBytes-DuczXiQZ.js +68 -0
  1048. package/dist/mcp/CdpCaptureDocuments-HDiF2O7k.js +746 -0
  1049. package/dist/mcp/CdpCaptureValues-M-j6CIh1.js +90 -0
  1050. package/dist/mcp/CdpConnection-BpDfz4MK.js +570 -0
  1051. package/dist/mcp/CdpOptionalCommand-BPjRD9Kz.js +120 -0
  1052. package/dist/mcp/CdpTargetSession-BsPoKhEU.js +86 -0
  1053. package/dist/mcp/DirectAnalysis-DrafbXke.js +409 -0
  1054. package/dist/mcp/DirectAnalysisStatus-CjjeDLzW.js +44 -0
  1055. package/dist/mcp/ElectronFileScope-BPG_YoyU.js +110 -0
  1056. package/dist/mcp/InterfaceBuilderDecodeBudget-5F2AkxJT.js +652 -0
  1057. package/dist/mcp/ManagedStaticProvider-BthZbKyZ.js +5028 -0
  1058. package/dist/mcp/NativeMacOSProvider-Bh79k8dI.js +4867 -0
  1059. package/dist/mcp/NativeMacOSProviderMetadata-N1lPOaY_.js +307 -0
  1060. package/dist/mcp/ProcedureAddressResolution-CQpuwm1h.js +19 -0
  1061. package/dist/mcp/ProcessOwnership-Bl_3ejLe.js +1502 -0
  1062. package/dist/mcp/ProviderDeadline-DskQ94YD.js +932 -0
  1063. package/dist/mcp/WorkflowEvidence-umWrB_wm.js +111 -0
  1064. package/dist/mcp/analysisErrorCore-DXRTvF_m.js +235 -0
  1065. package/dist/mcp/analysisErrorProjection-BN0EPM-3.js +892 -0
  1066. package/dist/mcp/artifactOperationError-CkHMMaf7.js +27 -0
  1067. package/dist/mcp/artifactToolContracts-Di4qZlEt.js +72 -0
  1068. package/dist/mcp/browserObservation-C0s4i8UI.js +4468 -0
  1069. package/dist/mcp/browserObservationError-CyLugqX4.js +40 -0
  1070. package/dist/mcp/browserObservationSchemas-tgqjH7aA.js +573 -0
  1071. package/dist/mcp/browserScenario-CAQ8Fns9.js +2929 -0
  1072. package/dist/mcp/browserScenarioCapture-CTNFN-FI.js +479 -0
  1073. package/dist/mcp/canonicalDigest-Bi_0_zGi.js +71 -0
  1074. package/dist/mcp/digests-BFoWUT_-.js +223 -0
  1075. package/dist/mcp/electronActiveObservation-lDvQTMR0.js +226 -0
  1076. package/dist/mcp/electronObservation-DFOFEood.js +112 -0
  1077. package/dist/mcp/electronObservation-i7LcILQ-.js +562 -0
  1078. package/dist/mcp/electronScenario-nCzE6IfJ.js +653 -0
  1079. package/dist/mcp/emptyArraySchema-tL6Qymyd.js +140 -0
  1080. package/dist/mcp/enhancedInputs-DgdEnzHy.js +56 -0
  1081. package/dist/mcp/javascriptRuntimeObservation-61NhCf7H.js +690 -0
  1082. package/dist/mcp/javascriptRuntimeObservation-PIUoLfX6.js +121 -0
  1083. package/dist/mcp/jsonSerialization-V-wWiFRE.js +152 -0
  1084. package/dist/mcp/lipo-Bnkoq_6W.js +502 -0
  1085. package/dist/mcp/localPath-AxvW9aPe.js +19 -0
  1086. package/dist/mcp/logger-BQY5ulYv.js +48 -0
  1087. package/dist/mcp/main-kBXHZ8S5.js +69390 -0
  1088. package/dist/mcp/main.js +6 -0
  1089. package/dist/mcp/managedToolContracts-CK--I_s7.js +101 -0
  1090. package/dist/mcp/nativeValueTrace-DR9UE62L.js +3439 -0
  1091. package/dist/mcp/officialToolContracts-B67NJ2tC.js +1413 -0
  1092. package/dist/mcp/pngHeader-DQ-ObyY2.js +147 -0
  1093. package/dist/mcp/propertyListKeys-DD25WsjI.js +553 -0
  1094. package/dist/mcp/providerAdapterError-sDxToNal.js +25 -0
  1095. package/dist/mcp/providerIdentities--ptAh481.js +31 -0
  1096. package/dist/mcp/providerIdentity-BCQ5n5tw.js +16 -0
  1097. package/dist/mcp/providerOperationHealth-CTYa3iLf.js +48 -0
  1098. package/dist/mcp/rolldown-runtime-BjeaiXFo.js +121 -0
  1099. package/dist/mcp/safeJson-NeGfpkrI.js +25 -0
  1100. package/dist/mcp/scanCanonical-CRXSpule.js +3982 -0
  1101. package/dist/mcp/serverIdentity-Cio4iFV3.js +1328 -0
  1102. package/dist/mcp/sourceMapEnvelope-C0uJlrOh.js +94 -0
  1103. package/dist/mcp/toolContracts-BW05wpcr.js +12200 -0
  1104. package/dist/mcp/toolOutputSchemaGroups-ColvNZ1r.js +17533 -0
  1105. package/dist/mcp/unicodeCodePointOrder-BezCbTpg.js +26 -0
  1106. package/dist/mcp/webContentArtifact-Dzjc1z-R.js +284 -0
  1107. package/dist/mcp/webRuntime-K__NYzXm.js +74 -0
  1108. package/dist/mcp/webSourceLocation-DbMAT2J9.js +11130 -0
  1109. package/dist/mcpDoctor.js +108 -76
  1110. package/dist/mcpStartupPolicy.js +3 -0
  1111. package/dist/native/AppleDispatchCoverage.js +81 -28
  1112. package/dist/native/AppleDispatchMetadata.js +20 -9
  1113. package/dist/native/AppleMachoFixups.js +253 -34
  1114. package/dist/native/AppleMachoSelection.js +44 -17
  1115. package/dist/native/AppleObjcCategories.js +1 -2
  1116. package/dist/native/AppleObjcDispatchFacets.js +76 -7
  1117. package/dist/native/CommandRunner.js +142 -32
  1118. package/dist/native/LldbCallTracer.js +5 -3
  1119. package/dist/native/MachoArchitectureSelection.js +50 -0
  1120. package/dist/native/NativeCallObservation.js +2 -1
  1121. package/dist/native/NativeMacOSProvider.js +173 -37
  1122. package/dist/native/NativeMachoInspection.js +142 -78
  1123. package/dist/native/NativeUiHelperRuntime.js +6 -1
  1124. package/dist/native/NativeUiObservation.js +6 -5
  1125. package/dist/native/SignatureTargetBinding.js +118 -55
  1126. package/dist/native/parsers/codesign.js +27 -10
  1127. package/dist/native/parsers/demangle.js +7 -1
  1128. package/dist/native/parsers/dyldInfo.js +73 -4
  1129. package/dist/native/parsers/lipo.js +53 -19
  1130. package/dist/native/parsers/machoUuid.js +150 -0
  1131. package/dist/native/parsers/otool.js +232 -29
  1132. package/dist/native/pe/PeResourceLayout.js +120 -0
  1133. package/dist/native/pe/PeResourceParser.js +286 -0
  1134. package/dist/native/pe/PeResourcesProvider.js +77 -0
  1135. package/dist/native/pwndbg/PwndbgCoreContext.js +20 -20
  1136. package/dist/native/pwntools/PwntoolsDecoder.js +264 -40
  1137. package/dist/native/pwntools/PwntoolsFailures.js +2 -1
  1138. package/dist/native/pwntools/PwntoolsInputLimit.js +27 -0
  1139. package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +27 -15
  1140. package/dist/native/pwntools/PwntoolsResourceLimits.js +3 -3
  1141. package/dist/native/pwntools/RecordedCrashSourceBindings.js +12 -8
  1142. package/dist/native/pwntools/RecordedCrashStructureBindings.js +3 -1
  1143. package/dist/objdump/ObjdumpCommand.js +21 -0
  1144. package/dist/pcap/PcapCommand.js +146 -0
  1145. package/dist/pcap/PcapConfiguration.js +93 -0
  1146. package/dist/pcap/PcapNativeBound.js +58 -0
  1147. package/dist/pcap/PcapOutput.js +240 -0
  1148. package/dist/pcap/PcapProvider.js +424 -0
  1149. package/dist/pcap/PcapSnapshot.js +171 -0
  1150. package/dist/process/ArtifactAnalysisResources.js +213 -0
  1151. package/dist/process/BoundedFileBytes.js +30 -7
  1152. package/dist/process/CurrentEntrypointPath.js +6 -0
  1153. package/dist/process/DarwinProcessRunTokenReader.js +125 -16
  1154. package/dist/process/ExecFileBoundedOutput.js +44 -0
  1155. package/dist/process/ExecFileOutput.js +30 -6
  1156. package/dist/process/JavaScriptWorkerMemoryLimit.js +25 -0
  1157. package/dist/process/LinuxResourceLimits.js +80 -0
  1158. package/dist/process/MacCodeSignature.js +3 -0
  1159. package/dist/process/OwnedCommand.js +40 -15
  1160. package/dist/process/PrivateRuntimeRoot.js +9 -19
  1161. package/dist/process/PrivateRuntimeRootUnavailableError.js +10 -0
  1162. package/dist/process/ProcessOwnership.js +173 -128
  1163. package/dist/process/ProcessOwnershipObservation.js +131 -43
  1164. package/dist/process/ProcessOwnershipProcessTree.js +4 -4
  1165. package/dist/process/ProcessRunTokenObservations.js +48 -0
  1166. package/dist/process/ProviderDeadline.js +58 -12
  1167. package/dist/process/ProviderDiagnosticRedaction.js +17 -0
  1168. package/dist/process/ProviderProcess.js +112 -14
  1169. package/dist/process/WindowsAuthority.js +48 -39
  1170. package/dist/process/WindowsBatchCommand.js +46 -0
  1171. package/dist/process/boundedUtf8Text.js +13 -0
  1172. package/dist/process/capture/FilesystemSnapshot.js +87 -12
  1173. package/dist/process/capture/ProcessCaptureClock.js +2 -0
  1174. package/dist/process/capture/ProcessCaptureError.js +22 -3
  1175. package/dist/process/capture/ProcessCaptureJournal.js +2 -1
  1176. package/dist/process/capture/ProcessCaptureLifecycle.js +433 -91
  1177. package/dist/process/capture/ProcessCaptureProgress.js +63 -0
  1178. package/dist/process/capture/ProcessHarness.js +209 -38
  1179. package/dist/process/capture/ProcessNormalization.js +278 -34
  1180. package/dist/process/capture/ProcessSampling.js +3 -2
  1181. package/dist/process/capture/ProcessScenarioRuntimeValidation.js +18 -15
  1182. package/dist/process/capture/TerminalRenderer.js +52 -14
  1183. package/dist/process/ownedDebuggerSession.js +203 -0
  1184. package/dist/reference/ReferenceSourceReader.js +22 -6
  1185. package/dist/reference/ReferenceSourceReaderEntries.js +182 -105
  1186. package/dist/reference/ReferenceSourceReaderErrors.js +17 -1
  1187. package/dist/reference/ReferenceSourceReaderFile.js +50 -11
  1188. package/dist/reference/ReferenceSourceReaderTypes.js +1 -2
  1189. package/dist/reference/ReferenceSourceReaderValidate.js +4 -5
  1190. package/dist/rizin/RizinCommand.js +14 -0
  1191. package/dist/rizin/RizinDebugSessionManager.js +1072 -0
  1192. package/dist/rust/RustAuditableReader.js +309 -0
  1193. package/dist/rust/RustBinaryImage.js +116 -0
  1194. package/dist/rust/RustBinaryProvider.js +162 -0
  1195. package/dist/server/EvidenceMcpServer.js +185 -27
  1196. package/dist/server/admittedToolOperation.js +14 -0
  1197. package/dist/server/contractToolHandler.js +28 -0
  1198. package/dist/server/createServer.js +238 -36
  1199. package/dist/server/externalMutationAdmission.js +50 -0
  1200. package/dist/server/mcpProgress.js +4 -2
  1201. package/dist/server/promptCompletion.js +19 -6
  1202. package/dist/server/providerEnvironment.js +3 -0
  1203. package/dist/server/registerAdbTools.js +36 -0
  1204. package/dist/server/registerAnalysisViewTool.js +9 -10
  1205. package/dist/server/registerAndroidTools.js +22 -13
  1206. package/dist/server/registerApktoolTools.js +14 -0
  1207. package/dist/server/registerApplicationTools/compareExportShapes.js +4 -6
  1208. package/dist/server/registerApplicationTools/compareSourceToBundle.js +4 -5
  1209. package/dist/server/registerApplicationTools/compareVersions.js +3 -2
  1210. package/dist/server/registerApplicationTools/coverage.js +3 -2
  1211. package/dist/server/registerApplicationTools/obligationLedger.js +3 -2
  1212. package/dist/server/registerApplicationTools/projectMobileApplicationGraph.js +11 -3
  1213. package/dist/server/registerApplicationTools/traceFeature.js +3 -2
  1214. package/dist/server/registerApplicationTools/traceSemantics.js +4 -5
  1215. package/dist/server/registerArtifactComparisonTool.js +5 -2
  1216. package/dist/server/registerArtifactTools.js +21 -1
  1217. package/dist/server/registerBinaryDiagnosticsTools.js +16 -4
  1218. package/dist/server/registerBrowserScenarioTool.js +10 -7
  1219. package/dist/server/registerBrowserTools.js +14 -13
  1220. package/dist/server/registerBundleComparisonTool.js +20 -6
  1221. package/dist/server/registerCaptureTools.js +12 -0
  1222. package/dist/server/registerCloseLifecycleTool.js +32 -3
  1223. package/dist/server/registerCutterTools.js +33 -0
  1224. package/dist/server/registerElectronTools.js +38 -17
  1225. package/dist/server/registerEnhancedTools.js +7 -6
  1226. package/dist/server/registerEvidenceTools.js +14 -10
  1227. package/dist/server/registerEvmTools.js +24 -11
  1228. package/dist/server/registerFirmwareTools.js +3 -11
  1229. package/dist/server/registerFlutterTools.js +17 -0
  1230. package/dist/server/registerFridaTools.js +205 -0
  1231. package/dist/server/registerFunctionComparisonTool.js +34 -11
  1232. package/dist/server/registerGdbTools.js +64 -0
  1233. package/dist/server/registerGoTools.js +9 -0
  1234. package/dist/server/registerInvestigationTools/helpers.js +2 -2
  1235. package/dist/server/registerInvestigationTools.js +12 -8
  1236. package/dist/server/registerJavaScriptRecoveryTool.js +4 -9
  1237. package/dist/server/registerJavaScriptRuntimeObservationTools.js +10 -11
  1238. package/dist/server/registerJebTools.js +16 -0
  1239. package/dist/server/registerManagedTools.js +11 -1
  1240. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +4 -6
  1241. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +3 -2
  1242. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +3 -2
  1243. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +3 -2
  1244. package/dist/server/registerNativeTools.js +14 -0
  1245. package/dist/server/registerOfficialTools.js +5 -0
  1246. package/dist/server/registerPcapTools.js +35 -0
  1247. package/dist/server/registerPeResourcesTool.js +9 -0
  1248. package/dist/server/registerProcessComparisonTool.js +14 -4
  1249. package/dist/server/registerRecordedCrashTools.js +4 -9
  1250. package/dist/server/registerReverseEngineeringTools.js +86 -0
  1251. package/dist/server/registerRizinDebugTools.js +53 -0
  1252. package/dist/server/registerRustTools.js +9 -0
  1253. package/dist/server/registerSessionRecordTools.js +21 -14
  1254. package/dist/server/registerSessionStatusTool.js +22 -4
  1255. package/dist/server/registerSessionTools.js +78 -34
  1256. package/dist/server/registerSignalTools.js +12 -0
  1257. package/dist/server/registerSqliteTools.js +9 -0
  1258. package/dist/server/registerWasmTools.js +46 -0
  1259. package/dist/server/registerWebModuleTool.js +4 -9
  1260. package/dist/server/registerWebNetworkCaptureTool.js +26 -17
  1261. package/dist/server/registerWebRuntimeTools.js +6 -5
  1262. package/dist/server/registerWebScriptTool.js +6 -3
  1263. package/dist/server/registerWebSourceLocationTool.js +4 -9
  1264. package/dist/server/retainedSummaryDelivery.js +28 -0
  1265. package/dist/server/sessionAvailabilityPolicy.js +3 -0
  1266. package/dist/server/toolRegistrationOptions.js +8 -0
  1267. package/dist/server/toolResult.js +68 -16
  1268. package/dist/server/toolResultEncoding.js +36 -3
  1269. package/dist/serverIdentity.js +17 -8
  1270. package/dist/signals/SigrokRecordingProvider.js +339 -0
  1271. package/dist/signals/parseDecoderDiscovery.js +65 -0
  1272. package/dist/sqlite/SqliteDatabaseFailures.js +134 -0
  1273. package/dist/sqlite/SqliteDatabaseInspection.js +421 -0
  1274. package/dist/sqlite/SqliteDatabaseLimits.js +16 -0
  1275. package/dist/sqlite/SqliteDatabaseProvider.js +295 -0
  1276. package/dist/sqlite/SqliteDatabaseSnapshot.js +156 -0
  1277. package/dist/sqlite/SqliteDatabaseWorker.js +46 -0
  1278. package/dist/sqlite/SqliteDatabaseWorkerExecution.js +112 -0
  1279. package/dist/wasm/WabtArtifactProvider.js +336 -0
  1280. package/dist/wasm/WabtFailures.js +101 -0
  1281. package/dist/wasm/WabtInterface.js +309 -0
  1282. package/dist/wasm/WabtOutput.js +60 -0
  1283. package/dist/wasm/WabtRelease.js +12 -0
  1284. package/dist/wasm/WabtString.js +48 -0
  1285. package/dist/wasm/WabtWat.js +147 -0
  1286. package/dist/wasm/WasmGlueAdapter.js +53 -0
  1287. package/dist/wasm/WasmInterfaceBudget.js +11 -0
  1288. package/dist/windows/WindowsNativeLoader.js +37 -7
  1289. package/dist/windows/WindowsOwnedProcess.js +150 -17
  1290. package/dist/windows/WindowsPrivateRuntime.js +34 -6
  1291. package/install.sh +157 -95
  1292. package/licenses/Apache-2.0.txt +202 -0
  1293. package/licenses/GCC-Runtime-Library-Exception-3.1.txt +73 -0
  1294. package/licenses/GPL-3.0.txt +674 -0
  1295. package/licenses/LGPL-2.0.txt +480 -0
  1296. package/licenses/MIT-template.txt +18 -0
  1297. package/licenses/MinGW-w64-runtime.txt +240 -0
  1298. package/licenses/Node-22.19.0-LICENSE.txt +2666 -0
  1299. package/licenses/README.md +68 -0
  1300. package/licenses/cfworker-MIT.txt +21 -0
  1301. package/licenses/dotnet-runtime-MIT.txt +23 -0
  1302. package/licenses/frida-17.23.2-COPYING.txt +50 -0
  1303. package/licenses/ghidra-12.1.4-DISCLAIMER.txt +13 -0
  1304. package/licenses/ghidra-12.1.4-LICENSE.txt +201 -0
  1305. package/licenses/ghidra-12.1.4-NOTICE.txt +49 -0
  1306. package/licenses/nativeaot-parser-MIT.txt +28 -0
  1307. package/licenses/npm-dependencies.txt +3612 -0
  1308. package/licenses/npm-runtime-notices.txt +8548 -0
  1309. package/licenses/upstream-sources.json +72 -0
  1310. package/licenses/xterm-MIT.txt +21 -0
  1311. package/native/windows/README.md +92 -11
  1312. package/native/windows/build/manifest.json +2 -2
  1313. package/native/windows/build/rea-windows-x64.node +0 -0
  1314. package/package.json +117 -22
  1315. package/scripts/electron-active-hook.cjs +4 -1
  1316. package/scripts/hopper-demo-x11.py +53 -3
  1317. package/scripts/prepack.mjs +5 -0
  1318. package/scripts/rea-android-workspace-guardian.mjs +46 -0
  1319. package/scripts/rea.mjs +44 -13
  1320. package/scripts/verify-windows-native-artifact.mjs +2 -2
  1321. package/skills/reverse-engineer-anything/SKILL.md +142 -20
  1322. package/skills/reverse-engineer-anything/references/connection-and-recovery.md +127 -0
  1323. package/skills/reverse-engineer-anything/references/evidence-workflows.md +31 -0
  1324. package/skills/reverse-engineer-anything/references/javascript-applications.md +118 -13
  1325. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +147 -16
  1326. package/skills/reverse-engineer-anything/references/runtime-observation.md +57 -7
  1327. package/third_party/README.md +1 -1
  1328. package/third_party/wabt/README.md +24 -0
  1329. package/dist/browser/history/HarCaptureAdapter.js +0 -20
  1330. package/dist/cli/streamedJsonOutput.js +0 -135
  1331. package/dist/cliJsonFile.js +0 -179
  1332. /package/dist/{application/binary → process}/AbortablePromise.js +0 -0
@@ -5,14 +5,18 @@ import java.io.BufferedReader;
5
5
  import java.io.BufferedWriter;
6
6
  import java.io.ByteArrayOutputStream;
7
7
  import java.io.IOException;
8
+ import java.io.InputStream;
8
9
  import java.math.BigInteger;
9
10
  import java.net.InetAddress;
10
11
  import java.net.InetSocketAddress;
12
+ import java.net.SocketTimeoutException;
11
13
  import java.net.StandardProtocolFamily;
12
14
  import java.net.UnixDomainSocketAddress;
13
15
  import java.nio.channels.Channels;
14
16
  import java.nio.channels.ServerSocketChannel;
15
17
  import java.nio.channels.SocketChannel;
18
+ import java.nio.ByteBuffer;
19
+ import java.nio.charset.CharacterCodingException;
16
20
  import java.nio.charset.StandardCharsets;
17
21
  import java.nio.file.AtomicMoveNotSupportedException;
18
22
  import java.nio.file.Files;
@@ -22,11 +26,15 @@ import java.nio.file.StandardCopyOption;
22
26
  import java.nio.file.StandardOpenOption;
23
27
  import java.nio.file.attribute.PosixFilePermissions;
24
28
  import java.security.MessageDigest;
29
+ import java.util.ArrayDeque;
25
30
  import java.util.ArrayList;
31
+ import java.util.Arrays;
26
32
  import java.util.Comparator;
33
+ import java.util.HashMap;
27
34
  import java.util.HashSet;
28
35
  import java.util.IdentityHashMap;
29
36
  import java.util.Iterator;
37
+ import java.util.LinkedHashMap;
30
38
  import java.util.List;
31
39
  import java.util.Locale;
32
40
  import java.util.Map;
@@ -51,6 +59,8 @@ import ghidra.app.decompiler.DecompiledFunction;
51
59
  import ghidra.app.decompiler.ClangCaseToken;
52
60
  import ghidra.app.decompiler.ClangNode;
53
61
  import ghidra.framework.Application;
62
+ import ghidra.app.util.bin.format.pdb.PdbParserConstants;
63
+ import ghidra.framework.options.Options;
54
64
  import ghidra.program.model.address.Address;
55
65
  import ghidra.program.model.address.AddressIterator;
56
66
  import ghidra.program.model.address.AddressSet;
@@ -73,6 +83,7 @@ import ghidra.program.model.listing.Function;
73
83
  import ghidra.program.model.listing.FunctionIterator;
74
84
  import ghidra.program.model.listing.Instruction;
75
85
  import ghidra.program.model.listing.InstructionIterator;
86
+ import ghidra.program.model.listing.Listing;
76
87
  import ghidra.program.model.listing.Variable;
77
88
  import ghidra.program.model.mem.MemoryBlock;
78
89
  import ghidra.program.model.mem.MemoryBlockSourceInfo;
@@ -80,12 +91,15 @@ import ghidra.program.database.mem.FileBytes;
80
91
  import ghidra.program.model.address.SegmentedAddress;
81
92
  import ghidra.program.model.reloc.Relocation;
82
93
  import java.io.ByteArrayOutputStream;
94
+ import ghidra.program.model.symbol.Namespace;
95
+ import ghidra.program.model.symbol.FlowType;
83
96
  import ghidra.program.model.symbol.RefType;
84
97
  import ghidra.program.model.symbol.Reference;
85
98
  import ghidra.program.model.symbol.ReferenceIterator;
86
99
  import ghidra.program.model.symbol.SourceType;
87
100
  import ghidra.program.model.symbol.Symbol;
88
101
  import ghidra.program.model.symbol.SymbolIterator;
102
+ import ghidra.program.model.symbol.SymbolTable;
89
103
  import ghidra.program.model.pcode.FunctionPrototype;
90
104
  import ghidra.program.model.pcode.HighFunction;
91
105
  import ghidra.program.model.pcode.HighParam;
@@ -97,12 +111,19 @@ import ghidra.program.model.pcode.PcodeBlock;
97
111
  import ghidra.program.model.pcode.PcodeBlockBasic;
98
112
  import ghidra.program.model.pcode.Varnode;
99
113
  import ghidra.program.model.pcode.VarnodeAST;
114
+ import ghidra.program.util.ContextEvaluatorAdapter;
115
+ import ghidra.program.util.SymbolicPropogator;
116
+ import ghidra.program.util.VarnodeContext;
100
117
 
101
118
  public final class ReaGhidraBridge extends HeadlessScript {
119
+ // At most 24 KiB after worst-case JSON escaping (six bytes per UTF-16 unit).
120
+ private static final int PDB_ATTRIBUTE_UTF16_BUDGET = 4096;
102
121
  private static final int MAX_HIGH_PCODE_OPS = 3000;
103
122
  private static final int MAX_HIGH_PCODE_INPUTS_PER_OP = 64;
104
123
  private static final int MAX_HIGH_PCODE_DEF_USE_EDGES = 12000;
105
124
  private static final Gson GSON = new GsonBuilder().serializeNulls().create();
125
+ private final Map<FileBytes, Boolean> targetSourceIdentity = new IdentityHashMap<>();
126
+ private String admittedTargetSha256;
106
127
  private static final Set<String> DESCRIPTOR_KEYS = Set.of(
107
128
  "transport",
108
129
  "endpoint_path",
@@ -120,8 +141,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
120
141
  );
121
142
  private static final String[] CAPABILITIES = {
122
143
  "annotate_native_function",
144
+ "set_address_name",
145
+ "set_addresses_names",
123
146
  "inspect_native_load_image",
124
147
  "read_bytes",
148
+ "search_bytes",
125
149
  "address_to_file_offset",
126
150
  "ping",
127
151
  "shutdown",
@@ -177,10 +201,17 @@ public final class ReaGhidraBridge extends HeadlessScript {
177
201
  }
178
202
  sessionDefaultAddressSpace =
179
203
  currentProgram.getAddressFactory().getDefaultAddressSpace();
180
- if (currentProgram.getExecutableFormat().equals("Raw Binary") &&
181
- !currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
182
- .getBoolean("REA DOS COM prepared", false)) {
183
- throw new IllegalStateException("REA DOS COM entry/context preparation failed");
204
+ if (currentProgram.getExecutableFormat().equals("Raw Binary")) {
205
+ boolean dosCom = currentProgram.getLanguageID().getIdAsString()
206
+ .equals("x86:LE:16:Real Mode");
207
+ String preparation = dosCom ? "REA DOS COM prepared" : "REA raw binary prepared";
208
+ if (!currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
209
+ .getBoolean(preparation, false)) {
210
+ throw new IllegalStateException(
211
+ dosCom ? "REA DOS COM entry/context preparation failed" :
212
+ "REA raw binary entry preparation failed"
213
+ );
214
+ }
184
215
  }
185
216
  if (!Application.getApplicationVersion().equals(descriptor.providerVersion)) {
186
217
  throw new IllegalStateException("Ghidra provider version does not match the session");
@@ -195,6 +226,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
195
226
  "Ghidra imported-byte digest does not match the admitted target"
196
227
  );
197
228
  }
229
+ admittedTargetSha256 = descriptor.targetSha256;
198
230
  // GhidraScript wraps run() in a transaction. End it before serving so
199
231
  // each edit owns a real outer transaction and can roll back immediately.
200
232
  end(true);
@@ -268,7 +300,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
268
300
  Files.move(pendingPath, endpointPath);
269
301
  }
270
302
  }
271
- acceptClient(server, descriptor);
303
+ acceptAuthenticatedLoopbackClient(server, descriptor);
272
304
  }
273
305
  finally {
274
306
  Files.deleteIfExists(pendingPath);
@@ -280,6 +312,50 @@ public final class ReaGhidraBridge extends HeadlessScript {
280
312
  return System.getProperty("os.name", "").startsWith("Windows");
281
313
  }
282
314
 
315
+ private static String readLoopbackHandshake(
316
+ SocketChannel client, int maxBytes, long timeoutMillis) throws IOException {
317
+ long deadline = System.nanoTime() + timeoutMillis * 1_000_000L;
318
+ InputStream input = client.socket().getInputStream();
319
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
320
+ while (true) {
321
+ long remaining = deadline - System.nanoTime();
322
+ if (remaining <= 0) throw new SocketTimeoutException("Bridge authentication timed out");
323
+ client.socket().setSoTimeout((int) Math.max(1,
324
+ Math.min(Integer.MAX_VALUE, (remaining + 999_999L) / 1_000_000L)));
325
+ int value = input.read();
326
+ if (value == -1) return null;
327
+ if (value == '\n') return bytes.toString(StandardCharsets.UTF_8);
328
+ if (bytes.size() == maxBytes) throw new IOException("Bridge authentication frame is too large");
329
+ bytes.write(value);
330
+ }
331
+ }
332
+
333
+ private void acceptAuthenticatedLoopbackClient(
334
+ ServerSocketChannel server, SessionDescriptor descriptor) throws Exception {
335
+ while (true) {
336
+ try (SocketChannel client = server.accept()) {
337
+ Request first;
338
+ try {
339
+ String line = readLoopbackHandshake(client, 64 * 1024, 1_000);
340
+ if (line == null) continue;
341
+ first = parseRequest(line, descriptor.token);
342
+ }
343
+ catch (IOException | RuntimeException rejected) {
344
+ // A rejected unauthenticated peer cannot consume the bridge session.
345
+ continue;
346
+ }
347
+ client.socket().setSoTimeout(0);
348
+ try (BufferedReader reader = new BufferedReader(
349
+ Channels.newReader(client, StandardCharsets.UTF_8));
350
+ BufferedWriter writer = new BufferedWriter(
351
+ Channels.newWriter(client, StandardCharsets.UTF_8))) {
352
+ serveClient(descriptor, reader, writer, first);
353
+ }
354
+ return;
355
+ }
356
+ }
357
+ }
358
+
283
359
  private void acceptClient(
284
360
  ServerSocketChannel server,
285
361
  SessionDescriptor descriptor) throws Exception {
@@ -290,26 +366,28 @@ public final class ReaGhidraBridge extends HeadlessScript {
290
366
  BufferedWriter writer = new BufferedWriter(
291
367
  Channels.newWriter(client, StandardCharsets.UTF_8)
292
368
  )) {
293
- serveClient(descriptor, reader, writer);
369
+ serveClient(descriptor, reader, writer, null);
294
370
  }
295
371
  }
296
372
 
297
373
  private void serveClient(
298
374
  SessionDescriptor descriptor,
299
375
  BufferedReader reader,
300
- BufferedWriter writer) throws Exception {
376
+ BufferedWriter writer,
377
+ Request first) throws Exception {
301
378
  while (true) {
302
- String line = reader.readLine();
303
- if (line == null) {
304
- return;
305
- }
306
- Request request;
307
- try {
308
- request = parseRequest(line, descriptor.token);
309
- }
310
- catch (RuntimeException exception) {
311
- writeFailure(writer, 1, "invalid_request", "Bridge request is invalid");
312
- return;
379
+ Request request = first;
380
+ first = null;
381
+ if (request == null) {
382
+ String line = reader.readLine();
383
+ if (line == null) return;
384
+ try {
385
+ request = parseRequest(line, descriptor.token);
386
+ }
387
+ catch (RuntimeException exception) {
388
+ writeFailure(writer, 1, "invalid_request", "Bridge request is invalid");
389
+ return;
390
+ }
313
391
  }
314
392
  try {
315
393
  if (request.method.equals("shutdown")) {
@@ -351,6 +429,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
351
429
  return switch (request.method) {
352
430
  case "inspect_native_load_image" -> inspectNativeLoadImage(request.params);
353
431
  case "read_bytes" -> readMemoryBytes(request.params);
432
+ case "search_bytes" -> searchMemoryBytes(request.params, descriptor);
354
433
  case "address_to_file_offset" -> addressToFileOffset(request.params);
355
434
  case "address_name" -> addressName(request.params);
356
435
  case "list_documents" -> listDocuments(request.params);
@@ -370,10 +449,21 @@ public final class ReaGhidraBridge extends HeadlessScript {
370
449
  case "search_procedures" -> search(request.params, true);
371
450
  case "search_strings" -> search(request.params, false);
372
451
  case "inspect_native_data_type" -> inspectNativeDataType(request.params);
373
- case "inspect_native_instruction" -> inspectNativeInstruction(request.params);
452
+ case "inspect_native_instruction" -> inspectNativeInstruction(request.params, true);
374
453
  case "resolve_native_call_targets" -> resolveNativeCallTargets(request.params);
375
454
  case "xrefs" -> xrefs(request.params);
376
- case "analyze_function" -> analyzeFunction(request.params);
455
+ case "analyze_function" -> {
456
+ FunctionAnalysisObservation observation = analyzeFunctionObservation(request.params);
457
+ JsonObject result = functionObservation(observation.dossier(), observation.limitations());
458
+ result.add("relationship_coverage", observation.relationshipCoverage());
459
+ yield result;
460
+ }
461
+ case "set_address_name", "set_addresses_names" -> {
462
+ if (!descriptor.transport.equals("unix-socket"))
463
+ throw new RequestFailure("method_unavailable", "Windows P0 does not admit database mutation");
464
+ yield request.method.equals("set_address_name")
465
+ ? setAddressName(request.params) : setAddressesNames(request.params);
466
+ }
377
467
  case "annotate_native_function" -> {
378
468
  if (!descriptor.transport.equals("unix-socket"))
379
469
  throw new RequestFailure("method_unavailable", "Windows P0 does not admit database mutation");
@@ -417,15 +507,62 @@ public final class ReaGhidraBridge extends HeadlessScript {
417
507
  result.addProperty("analysis_timed_out", timedOut);
418
508
  result.add("capabilities", GSON.toJsonTree(
419
509
  java.util.Arrays.stream(CAPABILITIES)
420
- .filter(value -> !readOnly || !value.equals("annotate_native_function"))
510
+ .filter(value -> !readOnly || !(value.equals("annotate_native_function")
511
+ || value.equals("set_address_name") || value.equals("set_addresses_names")))
421
512
  .toArray(String[]::new)
422
513
  ));
423
514
  result.add("target", target);
515
+ result.add("pdb", pdbObservation());
424
516
  JsonArray extensions = analysisExtensions.reports();
425
517
  if (extensions.size() > 0) result.add("analysis_extensions", extensions);
518
+ // Written by ReaGhidraApplySeeds before auto-analysis; REA checks it
519
+ // against the committed profile.
520
+ String seeds = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
521
+ .getString("REA analysis seeds", null);
522
+ if (seeds != null) result.add("analysis_seeds", JsonParser.parseString(seeds));
523
+ // Written by ReaGhidraMipsSoftFloat after analysis; REA requires it
524
+ // exactly when the committed profile declares soft-float storage.
525
+ String softFloat = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
526
+ .getString("REA MIPS soft-float storage", null);
527
+ if (softFloat != null) result.add("mips_soft_float_storage", JsonParser.parseString(softFloat));
426
528
  return result;
427
529
  }
428
530
 
531
+ /** Read loader/analyzer-owned facts without searching for or applying companions. */
532
+ private JsonObject pdbObservation() {
533
+ Options info = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO);
534
+ JsonObject pdb = new JsonObject();
535
+ String[] names = {"file", "guid", "age", "signature", "version"};
536
+ String[] attributes = {
537
+ PdbParserConstants.PDB_FILE, PdbParserConstants.PDB_GUID,
538
+ PdbParserConstants.PDB_AGE, PdbParserConstants.PDB_SIGNATURE,
539
+ PdbParserConstants.PDB_VERSION
540
+ };
541
+ int remaining = PDB_ATTRIBUTE_UTF16_BUDGET;
542
+ JsonArray omitted = new JsonArray();
543
+ for (int index = 0; index < names.length; index++) {
544
+ String value = info.getString(attributes[index], null);
545
+ if (value != null && value.length() > remaining) {
546
+ omitted.add(names[index]);
547
+ pdb.add(names[index], JsonNull.INSTANCE);
548
+ } else {
549
+ pdb.addProperty(names[index], value);
550
+ if (value != null) remaining -= value.length();
551
+ }
552
+ }
553
+ if (omitted.size() > 0) {
554
+ JsonObject limit = new JsonObject();
555
+ limit.addProperty("utf16_budget", PDB_ATTRIBUTE_UTF16_BUDGET);
556
+ limit.add("omitted_attributes", omitted);
557
+ pdb.add("resource_limit", limit);
558
+ }
559
+ if (info.contains(PdbParserConstants.PDB_LOADED))
560
+ pdb.addProperty("loaded", info.getBoolean(PdbParserConstants.PDB_LOADED, false));
561
+ else
562
+ pdb.add("loaded", JsonNull.INSTANCE);
563
+ return pdb;
564
+ }
565
+
429
566
  private static String bytesHex(byte[] bytes) {
430
567
  return java.util.HexFormat.of().formatHex(bytes);
431
568
  }
@@ -601,24 +738,173 @@ public final class ReaGhidraBridge extends HeadlessScript {
601
738
  return result;
602
739
  }
603
740
 
604
- private JsonObject addressToFileOffset(JsonObject params) {
741
+ private JsonObject searchMemoryBytes(JsonObject params, SessionDescriptor descriptor) throws Exception {
742
+ // mask, start, end, and cursor are omitted when absent; JSON encoding drops those keys.
743
+ Set<String> keys = params.keySet();
744
+ if (!keys.containsAll(Set.of("document", "pattern", "limit")) ||
745
+ !Set.of("document", "pattern", "mask", "start", "end", "limit", "cursor").containsAll(keys))
746
+ throw new RequestFailure("invalid_request", "Bridge request fields are invalid");
747
+ requireDocument(params);
748
+ // Public input validation owns the grammar; validate again at the socket boundary.
749
+ String pattern = requireString(params, "pattern").replaceAll("[\\s\\p{Z}\\uFEFF]", "");
750
+ String mask = params.has("mask") ? requireString(params, "mask") : null;
751
+ if (mask != null) mask = mask.replaceAll("[\\s\\p{Z}\\uFEFF]", "");
752
+ if (pattern.isEmpty() || pattern.length() % 2 != 0 ||
753
+ (mask != null && mask.length() != pattern.length()))
754
+ throw new RequestFailure("invalid_request", "Mask length must equal a nonempty byte pattern length");
755
+ byte[] bytes = new byte[pattern.length() / 2];
756
+ byte[] masks = new byte[bytes.length];
757
+ boolean constrained = false;
758
+ for (int index = 0; index < bytes.length; index++) {
759
+ monitor.checkCancelled();
760
+ String pair = pattern.substring(index * 2, index * 2 + 2);
761
+ if ((!pair.equals("??") && !isHexadecimal(pair, 0, 2)) ||
762
+ (mask != null && !isHexadecimal(mask, index * 2, index * 2 + 2)))
763
+ throw new RequestFailure("invalid_request", "Use hexadecimal byte pairs or ?? wildcards");
764
+ int selected = mask == null ? 255 : Integer.parseInt(mask.substring(index * 2, index * 2 + 2), 16);
765
+ bytes[index] = (byte) (pair.equals("??") ? 0 : Integer.parseInt(pair, 16));
766
+ masks[index] = (byte) (pair.equals("??") ? 0 : selected);
767
+ constrained |= masks[index] != 0;
768
+ }
769
+ if (!constrained) throw new RequestFailure("invalid_request", "Pattern must constrain at least one bit");
770
+ int limit = requireBoundedInteger(params, "limit", 1, 1000);
771
+ Address start = params.has("start") ? requireAddress(params, "start") : null;
772
+ Address end = params.has("end") ? requireAddress(params, "end") : null;
773
+ if (start != null && end != null &&
774
+ (!start.getAddressSpace().equals(end.getAddressSpace()) || start.compareTo(end) > 0))
775
+ throw new RequestFailure("invalid_request", "Search bounds must be ordered in one address space");
776
+ JsonArray identity = new JsonArray();
777
+ for (String value : new String[] { descriptor.targetSha256, descriptor.profileDigest,
778
+ bytesHex(bytes), bytesHex(masks), start == null ? "" : canonicalAddress(start),
779
+ end == null ? "" : canonicalAddress(end), optionalString(params, "document") }) identity.add(value);
780
+ String binding = bytesHex(MessageDigest.getInstance("SHA-256").digest(identity.toString().getBytes(StandardCharsets.UTF_8)));
781
+ String cursor = params.has("cursor") ? requireString(params, "cursor") : null;
782
+ Address resume = null;
783
+ if (cursor != null) {
784
+ if (!cursor.startsWith(binding + ":"))
785
+ throw new RequestFailure("invalid_request", "Byte search cursor does not match the target and query");
786
+ resume = parseReaAddress(cursor.substring(binding.length() + 1));
787
+ MemoryBlock resumedBlock = currentProgram.getMemory().getBlock(resume);
788
+ Address bound = start == null ? end : start;
789
+ if (resumedBlock == null || !resumedBlock.isInitialized() ||
790
+ (bound != null && !bound.getAddressSpace().equals(resume.getAddressSpace())) ||
791
+ (start != null && resume.compareTo(start) < 0) ||
792
+ (end != null && resume.compareTo(end) > 0))
793
+ throw new RequestFailure("invalid_request", "Byte search cursor lies outside initialized query memory");
794
+ }
795
+ List<MemoryBlock> ordered = new ArrayList<>(List.of(currentProgram.getMemory().getBlocks()));
796
+ ordered.sort(Comparator.comparing(MemoryBlock::getStart));
797
+ JsonArray matches = new JsonArray();
798
+ JsonArray blocks = new JsonArray();
799
+ JsonArray skipped = new JsonArray();
800
+ JsonArray notLoaded = new JsonArray();
801
+ long covered = 0;
802
+ String next = null;
803
+ for (MemoryBlock block : ordered) {
804
+ monitor.checkCancelled();
805
+ Address low = block.getStart();
806
+ Address high = block.getEnd();
807
+ Address bound = start == null ? end : start;
808
+ if (bound != null && !bound.getAddressSpace().equals(low.getAddressSpace())) continue;
809
+ // Without bounds, search loaded memory only. Non-loaded file parts
810
+ // (such as .symtab) and overlays stay reachable through explicit bounds.
811
+ if (bound == null && (!block.isLoaded() || block.isOverlay())) {
812
+ notLoaded.add(byteSearchCoverage(block, low, high));
813
+ continue;
814
+ }
815
+ if (start != null && low.compareTo(start) < 0) low = start;
816
+ if (end != null && high.compareTo(end) > 0) high = end;
817
+ if (low.compareTo(high) > 0) continue;
818
+ if (!block.isInitialized()) {
819
+ skipped.add(byteSearchCoverage(block, low, high));
820
+ continue;
821
+ }
822
+ if (next != null) continue;
823
+ if (resume != null && low.compareTo(resume) < 0) low = resume;
824
+ if (low.compareTo(high) > 0 || !low.getAddressSpace().equals(high.getAddressSpace())) continue;
825
+ Address at = low;
826
+ Address examinedEnd = high;
827
+ while (at != null && at.compareTo(high) <= 0 && high.subtract(at) >= bytes.length - 1L) {
828
+ monitor.checkCancelled();
829
+ // Ghidra's end address bounds candidate starts, not the final matched byte.
830
+ Address found = currentProgram.getMemory().findBytes(at, high.subtract(bytes.length - 1L), bytes, masks, true, monitor);
831
+ monitor.checkCancelled();
832
+ if (found == null || high.subtract(found) < bytes.length - 1L) break;
833
+ if (matches.size() == limit) {
834
+ next = binding + ":" + canonicalAddress(found);
835
+ examinedEnd = found.addNoWrap(bytes.length - 1L);
836
+ break;
837
+ }
838
+ JsonObject match = new JsonObject();
839
+ match.addProperty("address", canonicalAddress(found));
840
+ match.addProperty("segment", block.getName());
841
+ Long offset = targetFileOffset(block, found).offset();
842
+ match.add("file_offset", offset == null ? JsonNull.INSTANCE : GSON.toJsonTree(offset));
843
+ Function function = currentProgram.getFunctionManager().getFunctionContaining(found);
844
+ JsonObject procedure = new JsonObject();
845
+ if (function != null) {
846
+ procedure.addProperty("address", canonicalAddress(function.getEntryPoint()));
847
+ procedure.addProperty("name", function.getName(true));
848
+ }
849
+ match.add("procedure", function == null ? JsonNull.INSTANCE : procedure);
850
+ matches.add(match);
851
+ at = found.next();
852
+ }
853
+ JsonObject coverage = byteSearchCoverage(block, low, examinedEnd);
854
+ covered = Math.addExact(covered, coverage.get("bytes").getAsLong());
855
+ blocks.add(coverage);
856
+ }
857
+ JsonObject searched = new JsonObject();
858
+ searched.add("blocks", blocks);
859
+ searched.addProperty("bytes", covered);
860
+ searched.add("skipped_uninitialized", skipped);
861
+ searched.add("skipped_not_loaded", notLoaded);
862
+ JsonObject result = new JsonObject();
863
+ result.add("matches", matches);
864
+ result.add("searched", searched);
865
+ result.addProperty("complete", next == null);
866
+ result.add("next_cursor", next == null ? JsonNull.INSTANCE : GSON.toJsonTree(next));
867
+ return result;
868
+ }
869
+
870
+ private static JsonObject byteSearchCoverage(MemoryBlock block, Address low, Address high) {
871
+ JsonObject result = new JsonObject();
872
+ result.addProperty("segment", block.getName());
873
+ result.addProperty("start", canonicalAddress(low));
874
+ result.addProperty("end", canonicalAddress(high));
875
+ result.addProperty("bytes", high.subtract(low) + 1);
876
+ return result;
877
+ }
878
+
879
+ private record TargetFileOffset(Long offset, int mappings) {}
880
+
881
+ private TargetFileOffset targetFileOffset(MemoryBlock block, Address address) throws Exception {
882
+ Long offset = null;
883
+ int mappings = 0;
884
+ for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
885
+ if (!source.contains(address) || source.getFileBytes().isEmpty() ||
886
+ !isTargetSource(source.getFileBytes().get())) continue;
887
+ long candidate = source.getFileBytesOffset(address);
888
+ if (candidate < 0) continue;
889
+ offset = candidate;
890
+ mappings++;
891
+ }
892
+ return new TargetFileOffset(mappings == 1 && offset <= 9007199254740991L ? offset : null, mappings);
893
+ }
894
+
895
+ private JsonObject addressToFileOffset(JsonObject params) throws Exception {
605
896
  requireKeys(params, Set.of("document", "address"));
606
897
  requireDocument(params);
607
898
  Address address = requireAddress(params, "address");
608
899
  MemoryBlock block = currentProgram.getMemory().getBlock(address);
609
900
  if (block == null || !block.isInitialized())
610
901
  throw new RequestFailure("invalid_request", "Address has no initialized file-backed memory: " + canonicalAddress(address));
611
- Long fileOffset = null;
612
- int mappings = 0;
613
- for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
614
- if (!source.contains(address) || source.getFileBytes().isEmpty()) continue;
615
- long offset = source.getFileBytesOffset(address);
616
- if (offset < 0) continue;
617
- fileOffset = offset;
618
- mappings++;
619
- }
620
- if (mappings != 1)
621
- throw new RequestFailure("invalid_request", "Address has " + mappings + " authoritative source mappings: " + canonicalAddress(address));
902
+ TargetFileOffset mapping = targetFileOffset(block, address);
903
+ if (mapping.mappings() != 1)
904
+ throw new RequestFailure("invalid_request", "Address has " + mapping.mappings() + " authoritative target-file mappings: " + canonicalAddress(address));
905
+ Long fileOffset = mapping.offset();
906
+ if (fileOffset == null)
907
+ throw new RequestFailure("invalid_request", "Address lacks a safe file offset: " + canonicalAddress(address));
622
908
  JsonObject result = new JsonObject();
623
909
  result.addProperty("address", canonicalAddress(address));
624
910
  result.addProperty("file_offset", fileOffset);
@@ -641,10 +927,97 @@ public final class ReaGhidraBridge extends HeadlessScript {
641
927
  private JsonElement procedurePseudocode(JsonObject params) throws Exception {
642
928
  requireKeys(params, Set.of("document", "procedure"));
643
929
  requireDocument(params);
644
- String value = decompiledText(
645
- decompile(resolveProcedure(requireString(params, "procedure")))
646
- );
647
- return value == null ? JsonNull.INSTANCE : GSON.toJsonTree(value);
930
+ String selector = requireString(params, "procedure");
931
+ JsonObject selection = new JsonObject();
932
+ Function function = resolveDecompileProcedure(selector, selection);
933
+ try {
934
+ String value = decompiledText(decompile(function));
935
+ JsonObject result = functionObservation(
936
+ value == null ? JsonNull.INSTANCE : GSON.toJsonTree(value),
937
+ noReturnCallLimitations(
938
+ function,
939
+ currentProgram.getListing().getInstructions(function.getBody(), true)
940
+ )
941
+ );
942
+ if (selection.size() > 0) result.add("selection", selection);
943
+ return result;
944
+ } catch (RequestFailure failure) {
945
+ if (selection.size() == 0) throw failure;
946
+ // Preserve completed resolution facts without changing the backend's
947
+ // failure code or suggesting that the successful mapping was invalid.
948
+ throw new RequestFailure(failure.code, "Selector " + selector +
949
+ " mapped to analysis address " + selection.get("mapped_address").getAsString() +
950
+ " and procedure " + canonicalAddress(function.getEntryPoint()) +
951
+ "; " + failure.getMessage());
952
+ }
953
+ }
954
+
955
+ private Function resolveDecompileProcedure(String selector, JsonObject selection) throws Exception {
956
+ String prefix = "file-offset:";
957
+ boolean explicitOffset = selector.startsWith(prefix);
958
+ if (!explicitOffset) {
959
+ try {
960
+ return resolveProcedure(selector);
961
+ } catch (RequestFailure failure) {
962
+ // Existing names/addresses win. Only an unresolved numeric input
963
+ // can fall back to source coordinates; ambiguity never falls back.
964
+ if (!failure.code.equals("not_found") || !selector.matches("0x[0-9a-fA-F]+"))
965
+ throw failure;
966
+ }
967
+ }
968
+ String coordinate = explicitOffset ? selector.substring(prefix.length()) : selector;
969
+ if (!coordinate.matches("0x[0-9a-fA-F]+"))
970
+ throw new RequestFailure("invalid_request", "File offsets require hexadecimal file-offset:0x... syntax");
971
+ BigInteger parsed = new BigInteger(coordinate.substring(2), 16);
972
+ if (parsed.compareTo(BigInteger.valueOf(9007199254740991L)) > 0)
973
+ throw new RequestFailure("invalid_request", "File offset exceeds the exact JSON integer range");
974
+ long offset = parsed.longValueExact();
975
+ monitor.checkCancelled();
976
+ // Ghidra's convenience lookup spans every FileBytes and uses coordinates
977
+ // local to each object. Only a complete, digest-matched target source
978
+ // establishes that the caller's offset belongs to the admitted file.
979
+ Set<Address> mappedAddresses = new java.util.LinkedHashSet<>();
980
+ for (MemoryBlock block : currentProgram.getMemory().getBlocks()) {
981
+ for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
982
+ monitor.checkCancelled();
983
+ FileBytes backing = source.getFileBytes().orElse(null);
984
+ if (backing == null || !isTargetSource(backing)) continue;
985
+ Address mapped = source.locateAddressForFileOffset(offset);
986
+ if (mapped != null) mappedAddresses.add(mapped);
987
+ }
988
+ }
989
+ List<Address> candidates = new ArrayList<>(mappedAddresses);
990
+ if (candidates.size() > 1)
991
+ throw new RequestFailure("ambiguous", "File offset " + coordinate +
992
+ " has multiple loaded mappings: " + candidates.stream().map(ReaGhidraBridge::canonicalAddress).toList() +
993
+ "; select an explicit analysis address");
994
+ if (candidates.isEmpty())
995
+ throw new RequestFailure("not_found", "File offset " + coordinate +
996
+ " has no loaded source mapping for the admitted target file; Ghidra image base: " + canonicalAddress(currentProgram.getImageBase()) +
997
+ ". Inspect mappings with rea inspect-native-load-image <path> --provider ghidra --json or MCP inspect_native_load_image.");
998
+ Address mapped = candidates.get(0);
999
+ Function function = functionAtOrContaining(mapped);
1000
+ if (function == null)
1001
+ throw new RequestFailure("not_found", "File offset " + coordinate +
1002
+ " maps to analysis address " + canonicalAddress(mapped) +
1003
+ ", but no procedure starts at or contains that address in the current Ghidra analysis. " +
1004
+ "Choose an observed procedure with CLI rea search <path> . --kind procedures --mode regex --provider ghidra --json or MCP list_procedures.");
1005
+ selection.addProperty("file_offset", offset);
1006
+ selection.addProperty("mapped_address", canonicalAddress(mapped));
1007
+ selection.addProperty("procedure_address", canonicalAddress(function.getEntryPoint()));
1008
+ return function;
1009
+ }
1010
+
1011
+ private boolean isTargetSource(FileBytes source) throws Exception {
1012
+ Boolean observed = targetSourceIdentity.get(source);
1013
+ if (observed != null) return observed;
1014
+ // Original FileBytes are immutable even when analysis memory is edited.
1015
+ // Partial/auxiliary sources remain inspectable but cannot establish an
1016
+ // original-target offset from their object-local coordinates alone.
1017
+ boolean matched = source.getFileOffset() == 0 &&
1018
+ constantTimeEquals(hashFileBytes(source, false), admittedTargetSha256);
1019
+ targetSourceIdentity.put(source, matched);
1020
+ return matched;
648
1021
  }
649
1022
 
650
1023
  private JsonElement procedureAssembly(JsonObject params) throws Exception {
@@ -652,7 +1025,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
652
1025
  requireDocument(params);
653
1026
  Function function = resolveProcedure(requireString(params, "procedure"));
654
1027
  InstructionScan scan = scanInstructions(function, Integer.MAX_VALUE);
655
- return GSON.toJsonTree(renderAssembly(scan.instructions));
1028
+ return functionObservation(
1029
+ GSON.toJsonTree(renderAssembly(scan.instructions)),
1030
+ noReturnCallLimitations(function, scan.instructions)
1031
+ );
656
1032
  }
657
1033
 
658
1034
  private JsonObject readFunctionInstructions(JsonObject params) throws Exception {
@@ -670,14 +1046,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
670
1046
  limitations.add(
671
1047
  "The fast path does not invoke the decompiler or scan whole-program names and strings."
672
1048
  );
1049
+ List<String> boundaryLimitations = noReturnCallLimitations(function, scan.instructions);
1050
+ for (String limitation : boundaryLimitations) limitations.add(limitation);
673
1051
  JsonObject result = new JsonObject();
674
1052
  result.add("procedure", procedureIdentity(function));
675
1053
  result.add("instructions", items);
676
1054
  result.add("limitations", limitations);
677
- return result;
1055
+ return functionObservation(result, boundaryLimitations);
678
1056
  }
679
1057
 
680
- private JsonArray procedureCalls(JsonObject params, boolean callers) throws Exception {
1058
+ private JsonObject procedureCalls(JsonObject params, boolean callers) throws Exception {
681
1059
  requireKeys(params, Set.of("document", "procedure"));
682
1060
  requireDocument(params);
683
1061
  Function function = resolveProcedure(requireString(params, "procedure"));
@@ -690,7 +1068,19 @@ public final class ReaGhidraBridge extends HeadlessScript {
690
1068
  for (Function item : ordered) {
691
1069
  result.add(canonicalAddress(item.getEntryPoint()));
692
1070
  }
693
- return result;
1071
+ if (callers) {
1072
+ JsonObject observation = functionObservation(result, noReturnCallLimitations(function,
1073
+ currentProgram.getListing().getInstructions(function.getBody(), true)
1074
+ ));
1075
+ observation.add("relationship_coverage", callRelationshipCoverage(null,
1076
+ unrepresentedIncomingCalls(function, observed)));
1077
+ monitor.checkCancelled();
1078
+ return observation;
1079
+ }
1080
+ CallBoundaryObservation calls = callBoundaryObservation(
1081
+ function, currentProgram.getListing().getInstructions(function.getBody(), true), observed, false, null
1082
+ );
1083
+ return callRelationshipObservation(result, calls);
694
1084
  }
695
1085
 
696
1086
  private JsonObject procedureInfo(JsonObject params) throws Exception {
@@ -707,7 +1097,13 @@ public final class ReaGhidraBridge extends HeadlessScript {
707
1097
  result.addProperty("signature", function.getPrototypeString(false, true));
708
1098
  result.add("locals", functionLocals(function));
709
1099
  result.add("classification", functionClassification(function));
710
- return result;
1100
+ return functionObservation(
1101
+ result,
1102
+ noReturnCallLimitations(
1103
+ function,
1104
+ currentProgram.getListing().getInstructions(function.getBody(), true)
1105
+ )
1106
+ );
711
1107
  }
712
1108
 
713
1109
  private JsonObject procedureReferences(JsonObject params) throws Exception {
@@ -735,18 +1131,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
735
1131
  result.addProperty("direction", direction);
736
1132
  result.add("references", edges);
737
1133
  result.addProperty("reference_kinds_available", true);
738
- JsonArray unresolvedCalls = new JsonArray();
739
- if (direction.equals("outgoing")) for (Instruction instruction : scanInstructions(function, Integer.MAX_VALUE).instructions) {
740
- if (!instruction.getFlowType().isCall()) continue;
741
- boolean resolved = false;
742
- for (Reference reference : instruction.getReferencesFrom()) if (reference.getReferenceType().isCall()) resolved = true;
743
- if (!resolved) {
744
- JsonObject site = new JsonObject();
745
- site.addProperty("address", canonicalAddress(instruction.getAddress()));
746
- site.addProperty("reason", "Instruction decoder identifies a call but ReferenceManager supplies no call destination");
747
- unresolvedCalls.add(site);
748
- }
749
- }
1134
+ JsonArray unresolvedCalls = direction.equals("outgoing")
1135
+ ? callBoundaryObservation(
1136
+ function, currentProgram.getListing().getInstructions(function.getBody(), true), null, true, references
1137
+ ).unresolvedCalls() : new JsonArray();
750
1138
  result.add("unresolved_calls", unresolvedCalls);
751
1139
  return result;
752
1140
  }
@@ -841,18 +1229,17 @@ public final class ReaGhidraBridge extends HeadlessScript {
841
1229
 
842
1230
  private JsonObject instructionReference(Reference reference) {
843
1231
  JsonObject item = new JsonObject();
844
- RefType type = reference.getReferenceType();
1232
+ JsonObject kind = referenceKind(reference);
1233
+ item.addProperty("source_address", canonicalAddress(reference.getFromAddress()));
845
1234
  item.addProperty("target_address", canonicalAddress(reference.getToAddress()));
846
- item.addProperty("type", type.getName());
847
- item.addProperty("call", type.isCall());
848
- item.addProperty("jump", type.isJump());
849
- item.addProperty("indirect", type.isIndirect());
850
- item.addProperty("computed", type.isComputed());
851
- item.addProperty("operand_index", reference.getOperandIndex());
1235
+ for (String field : List.of("type", "call", "jump", "indirect", "computed",
1236
+ "operand_index", "data", "read", "write", "primary", "provenance"))
1237
+ item.add(field, kind.get(field));
1238
+ item.addProperty("source", normalizedSource(reference.getSource()));
852
1239
  return item;
853
1240
  }
854
1241
 
855
- private JsonObject inspectNativeInstruction(JsonObject params) throws Exception {
1242
+ private JsonObject inspectNativeInstruction(JsonObject params, boolean includeIncoming) throws Exception {
856
1243
  requireKeys(params, Set.of("document", "address"));
857
1244
  requireDocument(params);
858
1245
  Address address = requireAddress(params, "address");
@@ -863,6 +1250,19 @@ public final class ReaGhidraBridge extends HeadlessScript {
863
1250
  result.add("procedure", function == null ? JsonNull.INSTANCE : GSON.toJsonTree(canonicalAddress(function.getEntryPoint())));
864
1251
  result.addProperty("architecture", currentProgram.getLanguageID().getIdAsString());
865
1252
  result.addProperty("mode", currentProgram.getLanguage().getLanguageDescription().getVariant());
1253
+ if (currentProgram.getLanguageID().getIdAsString().startsWith("MIPS:")) {
1254
+ JsonArray decoderContext = new JsonArray();
1255
+ var modeRegister = currentProgram.getRegister("ISA_MODE");
1256
+ if (modeRegister != null) {
1257
+ var modeValue = currentProgram.getProgramContext().getValue(modeRegister, address, false);
1258
+ JsonObject context = new JsonObject();
1259
+ context.addProperty("register", modeRegister.getName());
1260
+ context.add("value", modeValue == null ? JsonNull.INSTANCE : GSON.toJsonTree(modeValue.toString()));
1261
+ context.addProperty("source", "ghidra-program-context");
1262
+ decoderContext.add(context);
1263
+ }
1264
+ result.add("decoder_context", decoderContext);
1265
+ }
866
1266
  JsonArray operands = new JsonArray();
867
1267
  JsonArray references = new JsonArray();
868
1268
  JsonArray destinations = new JsonArray();
@@ -935,6 +1335,19 @@ public final class ReaGhidraBridge extends HeadlessScript {
935
1335
  result.add("operands", operands);
936
1336
  result.add("flow", flow);
937
1337
  result.add("references", references);
1338
+ if (includeIncoming) {
1339
+ TreeMap<String, Reference> incoming = new TreeMap<>();
1340
+ ReferenceIterator incomingIterator = currentProgram.getReferenceManager().getReferencesTo(address);
1341
+ while (incomingIterator.hasNext()) {
1342
+ monitor.checkCancelled();
1343
+ addReference(incoming, incomingIterator.next());
1344
+ }
1345
+ List<Reference> orderedIncoming = new ArrayList<>(incoming.values());
1346
+ orderedIncoming.sort(REFERENCE_ORDER);
1347
+ JsonArray incomingReferences = new JsonArray();
1348
+ for (Reference reference : orderedIncoming) incomingReferences.add(instructionReference(reference));
1349
+ result.add("incoming_references", incomingReferences);
1350
+ }
938
1351
  List<String> limitations = new ArrayList<>(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."));
939
1352
  if (flow.get("kind").getAsString().equals("terminal")) limitations.add("No RETURN operation was observed in the provider's instruction p-code; terminal classification remains generic.");
940
1353
  result.add("limitations", GSON.toJsonTree(limitations));
@@ -942,7 +1355,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
942
1355
  }
943
1356
 
944
1357
  private JsonObject resolveNativeCallTargets(JsonObject params) throws Exception {
945
- JsonObject instruction = inspectNativeInstruction(params);
1358
+ JsonObject instruction = inspectNativeInstruction(params, false);
946
1359
  JsonObject result = new JsonObject();
947
1360
  result.add("call_site", instruction.get("address"));
948
1361
  result.add("procedure", instruction.get("procedure"));
@@ -1028,11 +1441,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
1028
1441
  for (String field : List.of("name", "comment", "inline_comment")) {
1029
1442
  if (params.has(field)) validateAnnotationText(requireText(params, field), field);
1030
1443
  }
1031
- String leafName = params.has("name")
1032
- ? annotationLeafName(function, requireString(params, "name")) : null;
1033
1444
  int transaction = currentProgram.startTransaction("REA function annotations");
1034
1445
  boolean commit = false;
1035
1446
  try {
1447
+ String leafName = params.has("name")
1448
+ ? symbolLeafName(function.getSymbol(), requireString(params, "name")) : null;
1036
1449
  // Validate edits through Ghidra's own setters, including name rules.
1037
1450
  // A later invalid name must roll back earlier comment writes.
1038
1451
  for (String field : List.of("comment", "inline_comment")) {
@@ -1081,18 +1494,18 @@ public final class ReaGhidraBridge extends HeadlessScript {
1081
1494
  }
1082
1495
  }
1083
1496
 
1084
- private String annotationLeafName(Function function, String requested) {
1497
+ private String symbolLeafName(Symbol symbol, String requested) throws ghidra.util.exception.InvalidInputException {
1085
1498
  // Fully qualified readback must be usable as an idempotent rename input.
1086
- if (requested.equals(procedureName(function))) return function.getName();
1087
- String namespace = function.getParentNamespace().isGlobal()
1088
- ? "" : function.getParentNamespace().getName(true);
1499
+ if (requested.equals(symbol.getName(true))) return symbol.getName();
1500
+ String namespace = symbol.getParentNamespace().isGlobal()
1501
+ ? "" : symbol.getParentNamespace().getName(true);
1089
1502
  String prefix = namespace + "::";
1090
1503
  if (!namespace.isEmpty() && requested.startsWith(prefix)) {
1091
1504
  String leaf = requested.substring(prefix.length());
1092
1505
  if (!leaf.isEmpty()) return leaf;
1093
- throw new RequestFailure("invalid_function_name",
1094
- "Function name has an empty leaf within namespace " + namespace + " at " +
1095
- canonicalAddress(function.getEntryPoint()) + ": " + requested);
1506
+ throw new ghidra.util.exception.InvalidInputException(
1507
+ "Symbol name has an empty leaf within namespace " + namespace + " at " +
1508
+ canonicalAddress(symbol.getAddress()) + ": " + requested);
1096
1509
  }
1097
1510
  return requested;
1098
1511
  }
@@ -1120,24 +1533,37 @@ public final class ReaGhidraBridge extends HeadlessScript {
1120
1533
  }
1121
1534
 
1122
1535
  private JsonObject analyzeFunction(JsonObject params) throws Exception {
1536
+ return analyzeFunctionObservation(params).dossier();
1537
+ }
1538
+
1539
+ private FunctionAnalysisObservation analyzeFunctionObservation(JsonObject params) throws Exception {
1123
1540
  requireKeys(params, Set.of("procedure"));
1124
1541
  Function function = resolveProcedure(requireString(params, "procedure"));
1125
1542
  InstructionScan scan = scanInstructions(function, Integer.MAX_VALUE);
1126
1543
  DecompileResults decompilation = decompile(function);
1127
1544
  String pseudocode = decompiledText(decompilation);
1545
+ JsonObject availability = new JsonObject();
1546
+ availability.add("pseudocode", facetAvailability(
1547
+ pseudocode != null,
1548
+ function.isExternal() ? "Ghidra does not decompile external functions."
1549
+ : function.getBody().isEmpty() ? "Ghidra has no function body to decompile."
1550
+ : "Ghidra completed decompilation without returning pseudocode."
1551
+ ));
1128
1552
  if (pseudocode == null) {
1129
1553
  pseudocode = "";
1130
1554
  }
1131
- List<Reference> incomingReferences = collectReferences(function, "incoming")
1132
- .stream()
1555
+ List<Reference> bodyIncoming = collectReferences(function, "incoming");
1556
+ List<Reference> incomingReferences = bodyIncoming.stream()
1133
1557
  .filter(reference -> !function.getBody().contains(reference.getFromAddress()))
1134
1558
  .toList();
1135
1559
  List<Reference> outgoingReferences = collectReferences(function, "outgoing");
1136
1560
  JsonArray incoming = referenceEdges(incomingReferences);
1137
1561
  JsonArray outgoing = referenceEdges(outgoingReferences);
1138
- JsonArray comments = comments(scan.instructions);
1139
- JsonArray callers = procedureIdentities(function.getCallingFunctions(monitor));
1140
- JsonArray callees = procedureIdentities(function.getCalledFunctions(monitor));
1562
+ JsonArray comments = comments(function);
1563
+ Set<Function> callerFunctions = function.getCallingFunctions(monitor);
1564
+ JsonArray callers = procedureIdentities(callerFunctions);
1565
+ Set<Function> calleeFunctions = function.getCalledFunctions(monitor);
1566
+ JsonArray callees = procedureIdentities(calleeFunctions);
1141
1567
  JsonArray referencedStrings = referencedStrings(outgoingReferences);
1142
1568
  JsonArray referencedNames = referencedNames(outgoingReferences);
1143
1569
  JsonArray blocks = basicBlockValues(function);
@@ -1158,12 +1584,28 @@ public final class ReaGhidraBridge extends HeadlessScript {
1158
1584
  result.add("outgoing_references", outgoing);
1159
1585
  result.add("referenced_strings", referencedStrings);
1160
1586
  result.add("referenced_names", referencedNames);
1587
+ boolean swift = swiftProgram();
1588
+ boolean aarch64 = currentProgram.getLanguageID().getIdAsString().startsWith("AARCH64:");
1589
+ SwiftStringLiteralDecode swiftLiterals = swift && aarch64 ? swiftStringLiterals(function) : null;
1590
+ if (swiftLiterals != null) {
1591
+ result.add("swift_string_literals", swiftLiterals.literals());
1592
+ result.add("swift_string_literal_omissions", swiftLiterals.omissions());
1593
+ }
1161
1594
  result.add("basic_blocks", blocks);
1162
1595
  result.add(
1163
1596
  "native_api",
1164
1597
  nativeApiBoundary(function, decompilation)
1165
1598
  );
1166
1599
  result.add("native_value_flow", nativeValueFlow(decompilation));
1600
+ // Successful full queries distinguish collected empty sets from missing facts.
1601
+ // Availability does not claim that Ghidra resolved every runtime relationship.
1602
+ for (String facet : List.of("comments", "callers", "callees", "references", "strings_names", "cfg")) {
1603
+ availability.add(facet, facetAvailability(true, null));
1604
+ }
1605
+ availability.add("assembly", facetAvailability(
1606
+ !scan.truncated(), "The function instruction scan was truncated."
1607
+ ));
1608
+ result.add("facet_availability", availability);
1167
1609
  JsonArray limitations = new JsonArray();
1168
1610
  limitations.add(
1169
1611
  "Unresolved computed or indirect flows without target addresses are not represented as reference edges."
@@ -1177,12 +1619,39 @@ public final class ReaGhidraBridge extends HeadlessScript {
1177
1619
  limitations.add(
1178
1620
  "Synthetic Ghidra entry-point references without actionable memory sources are omitted."
1179
1621
  );
1622
+ if (swift && aarch64) {
1623
+ limitations.add(
1624
+ "Swift String literals are decoded from constant register pairs at calls, returns and paired stores. Literals stored one word at a time, chosen by conditional selects, or built at runtime are not reported; candidate pairs at unresolved jumps are retained as unknown omissions."
1625
+ );
1626
+ if (swiftLiterals.omissions().size() > 0) {
1627
+ limitations.add(
1628
+ "Swift String literal records listed in swift_string_literal_omissions could not be retained or decoded within this dossier's remaining heap budget; later symbolic flows remain unexamined."
1629
+ );
1630
+ }
1631
+ } else if (swift) {
1632
+ limitations.add(
1633
+ "Swift String literal decoding is implemented for AArch64 register pairs only; no Swift literals were decoded for this architecture."
1634
+ );
1635
+ }
1636
+ CallBoundaryObservation calls = callBoundaryObservation(
1637
+ function, scan.instructions, calleeFunctions, true, outgoingReferences);
1638
+ result.add("unresolved_calls", calls.unresolvedCalls());
1639
+ for (String limitation : calls.limitations()) {
1640
+ limitations.add(limitation);
1641
+ }
1180
1642
  if (decompilation != null && decompilation.getErrorMessage() != null &&
1181
1643
  !decompilation.getErrorMessage().isBlank()) {
1182
1644
  limitations.add("Ghidra decompiler diagnostic: " + decompilation.getErrorMessage());
1183
1645
  }
1184
1646
  result.add("limitations", limitations);
1185
- return result;
1647
+ monitor.checkCancelled();
1648
+ int missingCallers = 0;
1649
+ for (Reference reference : bodyIncoming) {
1650
+ monitor.checkCancelled();
1651
+ if (unrepresentedCaller(reference, callerFunctions)) missingCallers++;
1652
+ }
1653
+ return new FunctionAnalysisObservation(result, calls.limitations(),
1654
+ callRelationshipCoverage(calls, missingCallers));
1186
1655
  }
1187
1656
 
1188
1657
  private JsonObject nativeValueFlow(DecompileResults decompilation) throws Exception {
@@ -1417,6 +1886,89 @@ public final class ReaGhidraBridge extends HeadlessScript {
1417
1886
  return symbol == null ? JsonNull.INSTANCE : GSON.toJsonTree(symbol.getName(true));
1418
1887
  }
1419
1888
 
1889
+ // Analyst naming: a function entry renames the function, other addresses get a primary label.
1890
+ private boolean applyAddressName(Address address, String name) throws Exception {
1891
+ if (!currentProgram.getMemory().contains(address))
1892
+ throw new RequestFailure("invalid_request", "Address is not mapped: " + canonicalAddress(address));
1893
+ validateAnnotationText(name, "name");
1894
+ int transaction = currentProgram.startTransaction("REA address name");
1895
+ boolean commit = false;
1896
+ try {
1897
+ Function function = currentProgram.getFunctionManager().getFunctionAt(address);
1898
+ SymbolTable table = currentProgram.getSymbolTable();
1899
+ Symbol primary = function == null ? table.getPrimarySymbol(address) : function.getSymbol();
1900
+ Namespace namespace = primary == null
1901
+ ? currentProgram.getGlobalNamespace()
1902
+ : primary.getParentNamespace();
1903
+ String expected = primary == null ? name : symbolLeafName(primary, name);
1904
+ for (Symbol symbol : table.getLabelOrFunctionSymbols(expected, namespace)) {
1905
+ if (!address.equals(symbol.getAddress())) return false;
1906
+ }
1907
+ if (function != null) {
1908
+ function.setName(expected, SourceType.USER_DEFINED);
1909
+ } else {
1910
+ if (primary != null && !primary.isDynamic()) {
1911
+ primary.setName(expected, SourceType.USER_DEFINED);
1912
+ } else {
1913
+ Symbol created = table.createLabel(address, expected, SourceType.USER_DEFINED);
1914
+ created.setPrimary();
1915
+ }
1916
+ }
1917
+ invalidateAnalysisCaches();
1918
+ Symbol readback = currentProgram.getSymbolTable().getPrimarySymbol(address);
1919
+ commit = readback != null && expected.equals(readback.getName());
1920
+ return commit;
1921
+ }
1922
+ catch (ghidra.util.exception.InvalidInputException | ghidra.util.exception.DuplicateNameException exception) {
1923
+ return false;
1924
+ }
1925
+ finally {
1926
+ currentProgram.endTransaction(transaction, commit);
1927
+ }
1928
+ }
1929
+
1930
+ private JsonElement setAddressName(JsonObject params) throws Exception {
1931
+ requireKeys(params, Set.of("document", "address", "name"));
1932
+ requireDocument(params);
1933
+ return GSON.toJsonTree(applyAddressName(requireAddress(params, "address"), requireString(params, "name")));
1934
+ }
1935
+
1936
+ private JsonElement setAddressesNames(JsonObject params) throws Exception {
1937
+ requireKeys(params, Set.of("document", "names"));
1938
+ requireDocument(params);
1939
+ if (!params.has("names") || !params.get("names").isJsonObject())
1940
+ throw new RequestFailure("invalid_request", "names must be an object of address -> name");
1941
+ Map<Address, List<AddressNameRequest>> requestsByAddress = new LinkedHashMap<>();
1942
+ JsonObject result = new JsonObject();
1943
+ for (Map.Entry<String, JsonElement> entry : params.getAsJsonObject("names").entrySet()) {
1944
+ try {
1945
+ Address address = parseReaAddress(entry.getKey());
1946
+ requestsByAddress.computeIfAbsent(address, ignored -> new ArrayList<>())
1947
+ .add(new AddressNameRequest(entry.getKey(), address, entry.getValue().getAsString()));
1948
+ }
1949
+ catch (RequestFailure failure) {
1950
+ result.addProperty(entry.getKey(), false);
1951
+ }
1952
+ }
1953
+ for (List<AddressNameRequest> requests : requestsByAddress.values()) {
1954
+ String name = requests.get(0).name;
1955
+ boolean conflictingAliasNames = requests.stream()
1956
+ .anyMatch(request -> !request.name.equals(name));
1957
+ boolean success = false;
1958
+ if (!conflictingAliasNames) {
1959
+ try {
1960
+ success = applyAddressName(requests.get(0).address, name);
1961
+ }
1962
+ catch (RequestFailure failure) {
1963
+ // Keep per-address failures in the existing batch result shape.
1964
+ }
1965
+ }
1966
+ for (AddressNameRequest request : requests)
1967
+ result.addProperty(request.inputAddress, success);
1968
+ }
1969
+ return result;
1970
+ }
1971
+
1420
1972
  private JsonArray listDocuments(JsonObject params) {
1421
1973
  requireKeys(params, Set.of());
1422
1974
  JsonArray result = new JsonArray();
@@ -1671,14 +2223,25 @@ public final class ReaGhidraBridge extends HeadlessScript {
1671
2223
  Address address = explicitAddress ? parseReaAddress(value) : tryParseAddress(value);
1672
2224
  if (address == null)
1673
2225
  throw new RequestFailure("not_found", "Unknown Ghidra procedure name or address: " + value);
1674
- Function function = currentProgram.getFunctionManager().getFunctionAt(address);
1675
- if (function == null && address.isMemoryAddress())
1676
- function = currentProgram.getFunctionManager().getFunctionContaining(address);
2226
+ Function function = functionAtOrContaining(address);
1677
2227
  if (function == null)
1678
- throw new RequestFailure("not_found", "No procedure exists at the requested address: " + value);
2228
+ // Other procedure operations retain their analysis-address semantics.
2229
+ throw new RequestFailure("not_found", "No procedure exists at the requested address: " + value +
2230
+ "; Ghidra image base: " + canonicalAddress(currentProgram.getImageBase()) +
2231
+ ". Choose an observed analysis address. CLI: rea search <path> . --kind procedures --mode regex --provider ghidra --json; " +
2232
+ "MCP: list_procedures. Inspect the loaded base and source mappings with CLI rea analyze <path> --provider ghidra --json " +
2233
+ "and rea inspect-native-load-image <path> --provider ghidra --json, or MCP binary_overview and inspect_native_load_image. " +
2234
+ "Repeat the original target options. For decompile, select source coordinates explicitly with --file-offset=0x... or MCP procedure file-offset:0x....");
1679
2235
  return function;
1680
2236
  }
1681
2237
 
2238
+ private Function functionAtOrContaining(Address address) {
2239
+ Function function = currentProgram.getFunctionManager().getFunctionAt(address);
2240
+ return function == null && address.isMemoryAddress()
2241
+ ? currentProgram.getFunctionManager().getFunctionContaining(address)
2242
+ : function;
2243
+ }
2244
+
1682
2245
  private JsonObject nativeApiBoundary(
1683
2246
  Function function,
1684
2247
  DecompileResults decompilation
@@ -1880,9 +2443,20 @@ public final class ReaGhidraBridge extends HeadlessScript {
1880
2443
  );
1881
2444
  JumpTable.LoadTable[] loadTables = jumpTable.getLoadTables();
1882
2445
  RecoveredAArch64RelativeTable recoveredAArch64 =
1883
- loadTables.length == 0
1884
- ? recoverAArch64RelativeTable(jumpTable)
1885
- : null;
2446
+ recoverAArch64RelativeTable(jumpTable);
2447
+ if (recoveredAArch64 != null && loadTables.length > 0) {
2448
+ // A decoded table only adds evidence when Ghidra's own load table
2449
+ // describes the same bytes; any disagreement keeps Ghidra's view.
2450
+ boolean agrees = false;
2451
+ for (JumpTable.LoadTable loadTable : loadTables) {
2452
+ if (loadTable.getAddress().equals(recoveredAArch64.tableAddress) &&
2453
+ loadTable.getSize() == recoveredAArch64.entrySize &&
2454
+ loadTable.getNum() == recoveredAArch64.mappings.size()) {
2455
+ agrees = true;
2456
+ }
2457
+ }
2458
+ if (!agrees) recoveredAArch64 = null;
2459
+ }
1886
2460
  List<Address> referencedDataAddresses = loadTables.length == 0
1887
2461
  ? dispatchPathDataReferences(jumpTable)
1888
2462
  : List.of();
@@ -1916,7 +2490,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
1916
2490
  source.add("evidence", evidence);
1917
2491
  dataSources.add(source);
1918
2492
  }
1919
- if (recoveredAArch64 != null) {
2493
+ if (recoveredAArch64 != null && loadTables.length == 0) {
1920
2494
  JsonObject source = new JsonObject();
1921
2495
  source.addProperty(
1922
2496
  "address",
@@ -1931,9 +2505,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
1931
2505
  inferenceEvidence(
1932
2506
  "jump-table",
1933
2507
  "ghidra-instruction-and-memory",
1934
- "Decoded " + recoveredAArch64.mappings.size() +
2508
+ "Decoded " + recoveredAArch64.mappings.size() + " " +
2509
+ (recoveredAArch64.signedEntries ? "signed" : "unsigned") +
1935
2510
  " entries of size " + recoveredAArch64.entrySize + " bytes from " + canonicalAddress(recoveredAArch64.tableAddress) +
1936
- "; the AArch64 LDRB-or-LDRH/ADR/ADD/BR chain uses a selector guarded by an unsigned upper-bound branch."
2511
+ "; the AArch64 ADRP/ADD table, indexed load, ADR base, ADD" +
2512
+ (recoveredAArch64.offsetShift == 0 ? "" : " LSL #" + recoveredAArch64.offsetShift) +
2513
+ "/BR chain uses a selector bounded by CMP #" + recoveredAArch64.compareImmediate +
2514
+ " and " + recoveredAArch64.guardMnemonic + " on one straight-line path" +
2515
+ (recoveredAArch64.nullTarget == null
2516
+ ? "."
2517
+ : "; zero entries are routed by CBZ to " + canonicalAddress(recoveredAArch64.nullTarget) + ".")
1937
2518
  )
1938
2519
  );
1939
2520
  source.add("evidence", evidence);
@@ -1972,6 +2553,33 @@ public final class ReaGhidraBridge extends HeadlessScript {
1972
2553
  result.add("data_sources", dataSources);
1973
2554
  Address[] targets = jumpTable.getCases();
1974
2555
  TypedJumpTableCases typedCases = typedJumpTableCases(jumpTable, caseTokens);
2556
+ // Table indices identify the register bounded by CMP. Typed labels may
2557
+ // instead identify a selector before normalization (for example, SUB
2558
+ // index, selector, #10). Do not merge those distinct value identities.
2559
+ if (recoveredAArch64 != null && recoveredAArch64.priorSelectorDefinition != null) {
2560
+ typedCases.limitations.add(
2561
+ "REA decoded the relative table, but the compared selector has an earlier register definition at " +
2562
+ canonicalAddress(recoveredAArch64.priorSelectorDefinition) + "; its table indices are not proven to use the typed switch selector's case values. Ghidra's typed labels are retained and additional case labels remain unknown."
2563
+ );
2564
+ recoveredAArch64 = null;
2565
+ }
2566
+ if (recoveredAArch64 != null) {
2567
+ for (Map.Entry<Long, Address> typed : typedCases.cases.entrySet()) {
2568
+ long value = typed.getKey();
2569
+ if (value < 0 || value >= recoveredAArch64.mappings.size()) {
2570
+ typedCases.limitations.add("Typed case labels and decoded table indices do not use a verified common selector; Ghidra's typed labels are retained.");
2571
+ recoveredAArch64 = null;
2572
+ break;
2573
+ }
2574
+ RecoveredAArch64Mapping decoded = recoveredAArch64.mappings.get((int) value);
2575
+ boolean divertedZero = decoded.nullEntry && typed.getValue().equals(recoveredAArch64.unreachableZeroTarget);
2576
+ if (!typed.getValue().equals(decoded.targetAddress) && !divertedZero) {
2577
+ typedCases.limitations.add("Typed case evidence and the decoded relative table disagree on a destination; Ghidra's typed labels are retained.");
2578
+ recoveredAArch64 = null;
2579
+ break;
2580
+ }
2581
+ }
2582
+ }
1975
2583
  JsonArray mappings = new JsonArray();
1976
2584
  JsonArray defaultTargets = new JsonArray();
1977
2585
  for (Address target : typedCases.defaults) {
@@ -1979,27 +2587,30 @@ public final class ReaGhidraBridge extends HeadlessScript {
1979
2587
  }
1980
2588
  Set<Address> coveredTargets = new HashSet<>(typedCases.cases.values());
1981
2589
  coveredTargets.addAll(typedCases.defaults);
2590
+ if (recoveredAArch64 != null && recoveredAArch64.unreachableZeroTarget != null) {
2591
+ Address unreachable = recoveredAArch64.unreachableZeroTarget;
2592
+ coveredTargets.add(unreachable);
2593
+ for (RecoveredAArch64Mapping recovered : recoveredAArch64.mappings) {
2594
+ if (recovered.nullEntry && unreachable.equals(typedCases.cases.get((long) recovered.tableIndex))) {
2595
+ typedCases.cases.remove((long) recovered.tableIndex);
2596
+ }
2597
+ }
2598
+ typedCases.limitations.add(
2599
+ "Ghidra recovered destination " + canonicalAddress(unreachable) +
2600
+ " as the zero-offset decode of the relative table, but the CBZ before the dispatch diverts zero entries to " +
2601
+ canonicalAddress(recoveredAArch64.nullTarget) + ", so the indirect branch never reaches it for an in-range case."
2602
+ );
2603
+ }
1982
2604
  if (recoveredAArch64 != null) {
1983
2605
  for (int index = 0; index < recoveredAArch64.mappings.size(); index += 1) {
1984
2606
  monitor.checkCancelled();
1985
2607
  RecoveredAArch64Mapping recovered = recoveredAArch64.mappings.get(index);
1986
2608
  if (
1987
2609
  typedCases.unknownTargets.contains(recovered.targetAddress) ||
1988
- typedCases.ambiguousValues.contains((long) recovered.caseValue)
2610
+ typedCases.ambiguousValues.contains((long) recovered.tableIndex)
1989
2611
  ) continue;
1990
- Address typedTarget = typedCases.cases.get((long) recovered.caseValue);
1991
- if (typedTarget != null && !typedTarget.equals(recovered.targetAddress)) {
1992
- typedCases.cases.remove((long) recovered.caseValue);
1993
- typedCases.ambiguousValues.add((long) recovered.caseValue);
1994
- typedCases.unknownTargets.add(typedTarget);
1995
- typedCases.unknownTargets.add(recovered.targetAddress);
1996
- typedCases.limitations.add(
1997
- "Typed case evidence and the verified relative table disagree on a destination; both destinations remain unknown for that case."
1998
- );
1999
- continue;
2000
- }
2001
2612
  JsonObject mapping = new JsonObject();
2002
- mapping.addProperty("case_value", recovered.caseValue);
2613
+ mapping.addProperty("case_value", recovered.tableIndex);
2003
2614
  String targetAddress = canonicalAddress(recovered.targetAddress);
2004
2615
  mapping.addProperty("target_address", targetAddress);
2005
2616
  mapping.addProperty("confidence", "high");
@@ -2008,14 +2619,18 @@ public final class ReaGhidraBridge extends HeadlessScript {
2008
2619
  inferenceEvidence(
2009
2620
  "jump-table",
2010
2621
  "ghidra-instruction-and-memory",
2011
- "Case value " + recovered.caseValue + (recoveredAArch64.entrySize == 1 ? " indexes byte " : " indexes halfword at byte offset ") +
2012
- (recovered.caseValue * recoveredAArch64.entrySize) + " at " + canonicalAddress(recoveredAArch64.tableAddress) +
2013
- "; the loaded unsigned entry selects a four-byte instruction offset from " +
2014
- canonicalAddress(recoveredAArch64.branchBase) + ", reaching " + targetAddress + "."
2622
+ "Case value " + recovered.tableIndex + (recoveredAArch64.entrySize == 1 ? " indexes byte " : " indexes halfword at byte offset ") +
2623
+ (recovered.tableIndex * recoveredAArch64.entrySize) + " at " + canonicalAddress(recoveredAArch64.tableAddress) +
2624
+ (recovered.nullEntry
2625
+ ? "; the entry is zero, so the CBZ before the dispatch branches to " + targetAddress + "."
2626
+ : "; the loaded " + (recoveredAArch64.signedEntries ? "signed" : "unsigned") +
2627
+ " entry " + recovered.encodedEntry + " selects a " +
2628
+ (recoveredAArch64.offsetShift == 2 ? "four-byte instruction" : "byte") +
2629
+ " offset from " + canonicalAddress(recoveredAArch64.branchBase) + ", reaching " + targetAddress + ".")
2015
2630
  )
2016
2631
  );
2017
2632
  mapping.add("evidence", evidence);
2018
- if (!typedCases.cases.containsKey((long) recovered.caseValue)) {
2633
+ if (!typedCases.cases.containsKey((long) recovered.tableIndex)) {
2019
2634
  mappings.add(mapping);
2020
2635
  }
2021
2636
  coveredTargets.add(recovered.targetAddress);
@@ -2089,7 +2704,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
2089
2704
  );
2090
2705
  }
2091
2706
  if (recoveredAArch64 == null && loadTables.length == 0 && currentProgram.getLanguageID().getIdAsString().startsWith("AARCH64:")) {
2092
- 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.");
2707
+ limitations.add("REA did not verify a supported signed or unsigned byte/halfword relative dispatch chain with exact unsigned bounds and register definitions; unsupported encodings and ambiguous bounds remain unknown.");
2093
2708
  }
2094
2709
  if (targets.length == 0) {
2095
2710
  limitations.add(
@@ -2219,12 +2834,21 @@ public final class ReaGhidraBridge extends HeadlessScript {
2219
2834
  }
2220
2835
 
2221
2836
  private static final class RecoveredAArch64Mapping {
2222
- private final int caseValue;
2837
+ private final int tableIndex;
2223
2838
  private final Address targetAddress;
2224
-
2225
- private RecoveredAArch64Mapping(int caseValue, Address targetAddress) {
2226
- this.caseValue = caseValue;
2839
+ private final long encodedEntry;
2840
+ private final boolean nullEntry;
2841
+
2842
+ private RecoveredAArch64Mapping(
2843
+ int tableIndex,
2844
+ Address targetAddress,
2845
+ long encodedEntry,
2846
+ boolean nullEntry
2847
+ ) {
2848
+ this.tableIndex = tableIndex;
2227
2849
  this.targetAddress = targetAddress;
2850
+ this.encodedEntry = encodedEntry;
2851
+ this.nullEntry = nullEntry;
2228
2852
  }
2229
2853
  }
2230
2854
 
@@ -2232,24 +2856,70 @@ public final class ReaGhidraBridge extends HeadlessScript {
2232
2856
  private final Address tableAddress;
2233
2857
  private final Address branchBase;
2234
2858
  private final int entrySize;
2859
+ private final Address priorSelectorDefinition;
2860
+ private final boolean signedEntries;
2861
+ private final int offsetShift;
2862
+ private final String guardMnemonic;
2863
+ private final long compareImmediate;
2864
+ private final Address nullTarget;
2865
+ private final Address unreachableZeroTarget;
2235
2866
  private final List<RecoveredAArch64Mapping> mappings;
2236
2867
 
2237
2868
  private RecoveredAArch64RelativeTable(
2238
2869
  Address tableAddress,
2239
2870
  Address branchBase,
2240
2871
  int entrySize,
2872
+ Address priorSelectorDefinition,
2873
+ boolean signedEntries,
2874
+ int offsetShift,
2875
+ String guardMnemonic,
2876
+ long compareImmediate,
2877
+ Address nullTarget,
2878
+ Address unreachableZeroTarget,
2241
2879
  List<RecoveredAArch64Mapping> mappings
2242
2880
  ) {
2243
2881
  this.tableAddress = tableAddress;
2244
2882
  this.branchBase = branchBase;
2245
2883
  this.entrySize = entrySize;
2884
+ this.priorSelectorDefinition = priorSelectorDefinition;
2885
+ this.signedEntries = signedEntries;
2886
+ this.offsetShift = offsetShift;
2887
+ this.guardMnemonic = guardMnemonic;
2888
+ this.compareImmediate = compareImmediate;
2889
+ this.nullTarget = nullTarget;
2890
+ this.unreachableZeroTarget = unreachableZeroTarget;
2246
2891
  this.mappings = mappings;
2247
2892
  }
2248
2893
  }
2249
2894
 
2895
+ private static final Pattern AARCH64_DISPATCH_ADD = Pattern.compile(
2896
+ "(?i)^([wx]\\d+),\\s*([wx]\\d+),\\s*([wx]\\d+)(?:,\\s*LSL\\s*#(?:0x)?([0-9a-f]+))?$"
2897
+ );
2898
+ private static final Pattern AARCH64_TABLE_LOAD = Pattern.compile(
2899
+ "(?i)^([wx]\\d+),\\s*\\[([wx]\\d+),\\s*([wx]\\d+)(?:,\\s*(LSL|UXTW)\\s*(?:#(?:0x)?([0-9a-f]+))?)?\\s*\\]$"
2900
+ );
2901
+ private static final Pattern AARCH64_REGISTER_AND_ADDRESS = Pattern.compile(
2902
+ "(?i)^([wx]\\d+),\\s*(0x[0-9a-f]+)$"
2903
+ );
2904
+ private static final Pattern AARCH64_ADD_IMMEDIATE = Pattern.compile(
2905
+ "(?i)^([wx]\\d+),\\s*([wx]\\d+),\\s*#?(0x[0-9a-f]+|[0-9]+)$"
2906
+ );
2907
+ private static final Pattern AARCH64_COMPARE_IMMEDIATE = Pattern.compile(
2908
+ "(?i)^([wx]\\d+),\\s*#?(0x[0-9a-f]+|[0-9]+)$"
2909
+ );
2910
+ private static final Pattern AARCH64_MOVE_REGISTER = Pattern.compile(
2911
+ "(?i)^([wx]\\d+),\\s*([wx]\\d+)$"
2912
+ );
2913
+ private static final Set<String> AARCH64_FLAG_PRESERVING =
2914
+ Set.of("MOV", "ADRP", "ADR", "ADD");
2915
+
2250
2916
  /**
2251
- * Recover only the directly verified AArch64 byte/halfword-indexed relative branch
2252
- * table form. Other instruction sequences keep their case labels unknown.
2917
+ * Recover only directly verified AArch64 relative branch tables:
2918
+ * ADRP/ADD table base, LDRB/LDRH/LDRSB/LDRSH entry load with an exact
2919
+ * index scale, ADR branch base, optional CBZ on the loaded entry, and
2920
+ * ADD (byte or LSL #2 scale)/BR, with a selector bounded by CMP #imm and an
2921
+ * unsigned B.HI or B.CS/B.HS guard on one straight-line path. Any other
2922
+ * instruction sequence keeps its case labels unknown.
2253
2923
  */
2254
2924
  private RecoveredAArch64RelativeTable recoverAArch64RelativeTable(
2255
2925
  JumpTable jumpTable
@@ -2271,167 +2941,265 @@ public final class ReaGhidraBridge extends HeadlessScript {
2271
2941
  if (instruction.getAddress().equals(jumpTable.getSwitchAddress())) break;
2272
2942
  }
2273
2943
  int branchIndex = instructions.size() - 1;
2274
- if (branchIndex < 1 ||
2275
- !instructions.get(branchIndex).getMnemonicString().equalsIgnoreCase("BR")) {
2944
+ if (branchIndex < 1 || !mnemonic(instructions.get(branchIndex)).equals("BR")) {
2276
2945
  return null;
2277
2946
  }
2278
-
2279
- Address[] recoveredTargets = jumpTable.getCases();
2280
2947
  Set<String> recoveredTargetSet = new HashSet<>();
2281
- for (Address target : recoveredTargets) recoveredTargetSet.add(canonicalAddress(target));
2282
- List<Address> dataAddresses = dispatchPathDataReferences(jumpTable);
2283
-
2284
- for (int loadIndex = 0; loadIndex < branchIndex; loadIndex += 1) {
2285
- Instruction load = instructions.get(loadIndex);
2286
- int entrySize = load.getMnemonicString().equalsIgnoreCase("LDRB") ? 1 : load.getMnemonicString().equalsIgnoreCase("LDRH") ? 2 : 0;
2287
- if (entrySize == 0 || branchIndex != loadIndex + 2 || loadIndex < 3 ||
2288
- !instructions.get(loadIndex - 1).getMnemonicString().equalsIgnoreCase("ADR") ||
2289
- !instructions.get(loadIndex - 2).getMnemonicString().equalsIgnoreCase("ADD") ||
2290
- !instructions.get(loadIndex - 3).getMnemonicString().equalsIgnoreCase("ADRP")) continue;
2291
- String loadOperands = operands(load);
2292
- java.util.regex.Matcher loadMatcher = java.util.regex.Pattern
2293
- .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\\]$")
2294
- .matcher(loadOperands);
2295
- if (!loadMatcher.matches()) continue;
2296
- String loadedRegister = normalizedRegister(loadOperands.substring(0, loadOperands.indexOf(',')));
2297
- String tableBaseRegister = normalizedRegister(loadMatcher.group(1));
2298
- String indexRegister = normalizedRegister(loadMatcher.group(2));
2299
-
2300
- Address resolvedTableAddress = null;
2301
- for (int index = loadIndex - 3; index < loadIndex - 1; index += 1) {
2302
- Instruction instruction = instructions.get(index);
2303
- String mnemonic = instruction.getMnemonicString().toUpperCase(Locale.ROOT);
2304
- String text = operands(instruction);
2305
- if (mnemonic.equals("ADRP") &&
2306
- text.matches("(?i)^[wx]\\d+,\\s*0x[0-9a-f]+$")) {
2307
- String[] parts = text.split(",");
2308
- if (normalizedRegister(parts[0]).equals(tableBaseRegister)) {
2309
- resolvedTableAddress = addressFromOperand(parts[1]);
2310
- }
2311
- }
2312
- else if (mnemonic.equals("ADD") && resolvedTableAddress != null &&
2313
- text.matches("(?i)^[wx]\\d+,\\s*[wx]\\d+,\\s*#?(?:0x[0-9a-f]+|[0-9]+)$")) {
2314
- String[] parts = text.split(",");
2315
- if (normalizedRegister(parts[0]).equals(tableBaseRegister) &&
2316
- normalizedRegister(parts[1]).equals(tableBaseRegister)) {
2317
- resolvedTableAddress = resolvedTableAddress.add(parseInteger(parts[2].trim().replaceFirst("^#", "")));
2318
- }
2319
- }
2948
+ for (Address target : jumpTable.getCases()) recoveredTargetSet.add(canonicalAddress(target));
2949
+ if (recoveredTargetSet.isEmpty()) return null;
2950
+ return matchAArch64RelativeDispatch(
2951
+ instructions,
2952
+ branchIndex,
2953
+ recoveredTargetSet,
2954
+ dispatchPathDataReferences(jumpTable)
2955
+ );
2956
+ }
2957
+
2958
+ private RecoveredAArch64RelativeTable matchAArch64RelativeDispatch(
2959
+ List<Instruction> instructions,
2960
+ int branchIndex,
2961
+ Set<String> recoveredTargetSet,
2962
+ List<Address> dataAddresses
2963
+ ) throws Exception {
2964
+ String branchRegister = normalizedRegister(operands(instructions.get(branchIndex)));
2965
+
2966
+ // ADD Xd, Xn, Xm{, LSL #s}; BR Xd. LSL applies to Xm only.
2967
+ Instruction dispatchAdd = instructions.get(branchIndex - 1);
2968
+ if (!mnemonic(dispatchAdd).equals("ADD")) return null;
2969
+ java.util.regex.Matcher addMatcher = AARCH64_DISPATCH_ADD.matcher(operands(dispatchAdd));
2970
+ if (!addMatcher.matches() || !addMatcher.group(1).toLowerCase(Locale.ROOT).startsWith("x")) return null;
2971
+ if (!normalizedRegister(addMatcher.group(1)).equals(branchRegister)) return null;
2972
+ int offsetShift = addMatcher.group(4) == null ? 0 : Integer.parseInt(addMatcher.group(4), 16);
2973
+ if (offsetShift != 0 && offsetShift != 2) return null;
2974
+ String firstSource = normalizedRegister(addMatcher.group(2));
2975
+ String secondSource = normalizedRegister(addMatcher.group(3));
2976
+ if (firstSource.equals(secondSource)) return null;
2977
+
2978
+ // The entry register is the source whose latest definition is the
2979
+ // table load; the other source must be defined by ADR.
2980
+ int loadIndex = -1;
2981
+ String entryRegister = null;
2982
+ String baseRegister = null;
2983
+ for (String candidate : offsetShift == 0 ? List.of(secondSource, firstSource) : List.of(secondSource)) {
2984
+ int writer = latestWriter(instructions, branchIndex - 1, candidate);
2985
+ if (writer >= 0 && Set.of("LDRB", "LDRH", "LDRSB", "LDRSH").contains(mnemonic(instructions.get(writer)))) {
2986
+ loadIndex = writer;
2987
+ entryRegister = candidate;
2988
+ baseRegister = candidate.equals(firstSource) ? secondSource : firstSource;
2989
+ break;
2320
2990
  }
2321
- if (resolvedTableAddress == null || !currentProgram.getMemory().contains(resolvedTableAddress) ||
2322
- (!dataAddresses.isEmpty() && !dataAddresses.contains(resolvedTableAddress))) continue;
2323
- Address tableAddress = resolvedTableAddress;
2324
-
2325
- Address branchBase = null;
2326
- boolean addressChain = false;
2327
- for (int index = loadIndex - 1; index < branchIndex; index += 1) {
2328
- Instruction instruction = instructions.get(index);
2329
- String mnemonic = instruction.getMnemonicString().toUpperCase(Locale.ROOT);
2330
- String text = operands(instruction);
2331
- if (index < loadIndex && mnemonic.equals("ADR") && text.matches("(?i)^[wx]\\d+,\\s*0x[0-9a-f]+$")) {
2332
- int comma = text.indexOf(',');
2333
- String destination = normalizedRegister(text.substring(0, comma));
2334
- if (!destination.equals(normalizedRegister(operands(instructions.get(branchIndex))))) continue;
2335
- branchBase = addressFromOperand(text.substring(comma + 1));
2336
- }
2337
- if (index > loadIndex && mnemonic.equals("ADD") && branchBase != null &&
2338
- text.matches("(?i)^[wx]\\d+,\\s*[wx]\\d+,\\s*[wx]\\d+,\\s*LSL\\s*#(?:0x)?2$")) {
2339
- String[] parts = text.split(",");
2340
- String destination = normalizedRegister(parts[0]);
2341
- String base = normalizedRegister(parts[1]);
2342
- String offset = normalizedRegister(parts[2]);
2343
- if (destination.equals(base) &&
2344
- base.equals(normalizedRegister(operands(instructions.get(branchIndex)))) &&
2345
- offset.equals(loadedRegister)) {
2346
- addressChain = true;
2347
- }
2348
- }
2991
+ }
2992
+ if (loadIndex < 0) return null;
2993
+
2994
+ Instruction load = instructions.get(loadIndex);
2995
+ String loadMnemonic = mnemonic(load);
2996
+ int entrySize = loadMnemonic.endsWith("B") ? 1 : 2;
2997
+ boolean signedEntries = loadMnemonic.startsWith("LDRS");
2998
+ java.util.regex.Matcher loadMatcher = AARCH64_TABLE_LOAD.matcher(operands(load));
2999
+ if (!loadMatcher.matches()) return null;
3000
+ // A signed load into a W register sign-extends only to 32 bits; the
3001
+ // 64-bit dispatch ADD would then read a zero-extended value.
3002
+ if (signedEntries && !loadMatcher.group(1).toLowerCase(Locale.ROOT).startsWith("x")) return null;
3003
+ String tableBaseRegister = normalizedRegister(loadMatcher.group(2));
3004
+ String indexRegister = normalizedRegister(loadMatcher.group(3));
3005
+ boolean wordIndex = loadMatcher.group(3).toLowerCase(Locale.ROOT).startsWith("w");
3006
+ String extend = loadMatcher.group(4) == null ? null : loadMatcher.group(4).toUpperCase(Locale.ROOT);
3007
+ int indexShift = loadMatcher.group(5) == null ? 0 : Integer.parseInt(loadMatcher.group(5), 16);
3008
+ if (wordIndex ? !"UXTW".equals(extend) : "UXTW".equals(extend)) return null;
3009
+ if (indexShift != (entrySize == 1 ? 0 : 1)) return null;
3010
+
3011
+ // Only ADR and one CBZ on the entry may sit between load and ADD.
3012
+ Address nullTarget = null;
3013
+ for (int index = loadIndex + 1; index < branchIndex - 1; index += 1) {
3014
+ Instruction instruction = instructions.get(index);
3015
+ String name = mnemonic(instruction);
3016
+ if (name.equals("ADR")) continue;
3017
+ if (name.equals("CBZ") && nullTarget == null) {
3018
+ String[] parts = operands(instruction).split(",");
3019
+ Address[] flows = instruction.getFlows();
3020
+ if (parts.length != 2 || flows.length != 1 ||
3021
+ !normalizedRegister(parts[0]).equals(entryRegister)) return null;
3022
+ nullTarget = flows[0];
3023
+ continue;
2349
3024
  }
2350
- if (!addressChain || branchBase == null) continue;
2351
-
2352
- int entryCount = -1;
2353
- Address defaultTarget = null;
2354
- for (int compareIndex = 0; compareIndex < loadIndex; compareIndex += 1) {
2355
- Instruction compare = instructions.get(compareIndex);
2356
- if (!compare.getMnemonicString().equalsIgnoreCase("CMP")) continue;
2357
- java.util.regex.Matcher compareMatcher = java.util.regex.Pattern
2358
- .compile("(?i)^([wx]\\d+),\\s*#?(0x[0-9a-f]+|[0-9]+)$")
2359
- .matcher(operands(compare));
2360
- if (!compareMatcher.matches()) continue;
2361
- String comparedRegister = normalizedRegister(compareMatcher.group(1));
2362
- long upperBound = parseInteger(compareMatcher.group(2));
2363
- if (upperBound < 0 || upperBound >= 4096) continue;
2364
- for (int guardIndex = compareIndex + 1; guardIndex < loadIndex; guardIndex += 1) {
2365
- Instruction guard = instructions.get(guardIndex);
2366
- String mnemonic = guard.getMnemonicString().toUpperCase(Locale.ROOT);
2367
- if (!mnemonic.equals("B.HI")) continue;
2368
- Address[] flows = guard.getFlows();
2369
- if (flows.length != 1 || !recoveredTargetSet.contains(canonicalAddress(flows[0]))) continue;
2370
- boolean supportedSequence = true;
2371
- for (int sequenceIndex = compareIndex + 1; sequenceIndex < loadIndex; sequenceIndex += 1) {
2372
- String sequenceMnemonic = instructions.get(sequenceIndex)
2373
- .getMnemonicString().toUpperCase(Locale.ROOT);
2374
- if (!Set.of("B.HI", "MOV", "ADRP", "ADD", "ADR").contains(sequenceMnemonic)) {
2375
- supportedSequence = false;
2376
- break;
2377
- }
2378
- }
2379
- Set<String> selectorAliases = new HashSet<>();
2380
- selectorAliases.add(comparedRegister);
2381
- Set<String> boundedIndexes = new HashSet<>();
2382
- if (compareMatcher.group(1).toLowerCase(Locale.ROOT).startsWith("x")) boundedIndexes.add(comparedRegister);
2383
- for (int sequenceIndex = compareIndex + 1; sequenceIndex < loadIndex; sequenceIndex++) {
2384
- Instruction instruction = instructions.get(sequenceIndex);
2385
- String[] parts = operands(instruction).split(",");
2386
- if (parts.length == 0) continue;
2387
- if (instruction.getMnemonicString().equalsIgnoreCase("MOV")) {
2388
- String destination = normalizedRegister(parts[0]);
2389
- String source = parts.length == 2 ? normalizedRegister(parts[1]) : "";
2390
- boolean aliasesSelector = selectorAliases.contains(source);
2391
- boolean fullWidthBounded = boundedIndexes.contains(source) ||
2392
- (parts[0].trim().toLowerCase(Locale.ROOT).startsWith("w") && aliasesSelector);
2393
- if (aliasesSelector) selectorAliases.add(destination);
2394
- else selectorAliases.remove(destination);
2395
- if (fullWidthBounded) boundedIndexes.add(destination);
2396
- else boundedIndexes.remove(destination);
2397
- } else if (Set.of("ADRP", "ADD", "ADR").contains(instruction.getMnemonicString().toUpperCase(Locale.ROOT))) {
2398
- selectorAliases.remove(normalizedRegister(parts[0]));
2399
- boundedIndexes.remove(normalizedRegister(parts[0]));
2400
- }
2401
- }
2402
- boolean selectorFeedsTable = boundedIndexes.contains(indexRegister);
2403
- if (compareIndex < loadIndex - 3 && selectorFeedsTable && supportedSequence) {
2404
- entryCount = (int) upperBound + 1;
2405
- defaultTarget = flows[0];
2406
- break;
2407
- }
2408
- }
2409
- if (entryCount > 0) break;
2410
- }
2411
- if (entryCount <= 0 || entryCount > 4096 || defaultTarget == null) continue;
2412
-
2413
- List<RecoveredAArch64Mapping> mappings = new ArrayList<>();
2414
- Set<String> mappedTargets = new HashSet<>();
2415
- boolean valid = true;
2416
- for (int caseValue = 0; caseValue < entryCount; caseValue += 1) {
2417
- Address entry = tableAddress.add((long) caseValue * entrySize);
2418
- int encodedOffset = entrySize == 1 ? currentProgram.getMemory().getByte(entry) & 0xff : currentProgram.getMemory().getShort(entry) & 0xffff;
2419
- Address target = branchBase.add((long) encodedOffset * 4L);
2420
- String canonicalTarget = canonicalAddress(target);
2421
- if (!recoveredTargetSet.contains(canonicalTarget)) {
2422
- valid = false;
2423
- break;
3025
+ return null;
3026
+ }
3027
+
3028
+ int baseIndex = latestWriter(instructions, branchIndex - 1, baseRegister);
3029
+ if (baseIndex < 0 || !mnemonic(instructions.get(baseIndex)).equals("ADR")) return null;
3030
+ java.util.regex.Matcher baseMatcher = AARCH64_REGISTER_AND_ADDRESS.matcher(operands(instructions.get(baseIndex)));
3031
+ if (!baseMatcher.matches()) return null;
3032
+ Address branchBase = addressFromOperand(baseMatcher.group(2));
3033
+
3034
+ int tableAddIndex = latestWriter(instructions, loadIndex, tableBaseRegister);
3035
+ if (tableAddIndex < 0 || !mnemonic(instructions.get(tableAddIndex)).equals("ADD")) return null;
3036
+ java.util.regex.Matcher tableAddMatcher = AARCH64_ADD_IMMEDIATE.matcher(operands(instructions.get(tableAddIndex)));
3037
+ if (!tableAddMatcher.matches() ||
3038
+ !normalizedRegister(tableAddMatcher.group(1)).equals(tableBaseRegister) ||
3039
+ !normalizedRegister(tableAddMatcher.group(2)).equals(tableBaseRegister)) return null;
3040
+ int tablePageIndex = latestWriter(instructions, tableAddIndex, tableBaseRegister);
3041
+ if (tablePageIndex < 0 || !mnemonic(instructions.get(tablePageIndex)).equals("ADRP")) return null;
3042
+ java.util.regex.Matcher pageMatcher = AARCH64_REGISTER_AND_ADDRESS.matcher(operands(instructions.get(tablePageIndex)));
3043
+ if (!pageMatcher.matches() || !normalizedRegister(pageMatcher.group(1)).equals(tableBaseRegister)) return null;
3044
+ Address tableAddress = addressFromOperand(pageMatcher.group(2)).add(parseInteger(tableAddMatcher.group(3)));
3045
+ if (!currentProgram.getMemory().contains(tableAddress) ||
3046
+ (!dataAddresses.isEmpty() && !dataAddresses.contains(tableAddress))) return null;
3047
+
3048
+ // Unsigned guard: CMP Rs, #imm, then B.HI (imm + 1 entries) or
3049
+ // B.CS/B.HS (imm entries), with only flag-preserving moves and
3050
+ // address arithmetic between the compare and the load.
3051
+ int guardIndex = -1;
3052
+ for (int index = loadIndex - 1; index >= 0; index -= 1) {
3053
+ String name = mnemonic(instructions.get(index));
3054
+ if (name.startsWith("B.")) {
3055
+ guardIndex = index;
3056
+ break;
3057
+ }
3058
+ if (!AARCH64_FLAG_PRESERVING.contains(name)) return null;
3059
+ }
3060
+ if (guardIndex < 1) return null;
3061
+ Instruction guard = instructions.get(guardIndex);
3062
+ String guardMnemonic = mnemonic(guard);
3063
+ if (!Set.of("B.HI", "B.CS", "B.HS").contains(guardMnemonic)) return null;
3064
+ Address[] guardFlows = guard.getFlows();
3065
+ if (guardFlows.length != 1) return null;
3066
+ Address defaultTarget = guardFlows[0];
3067
+ int compareIndex = -1;
3068
+ for (int index = guardIndex - 1; index >= 0; index -= 1) {
3069
+ String name = mnemonic(instructions.get(index));
3070
+ if (name.equals("CMP")) {
3071
+ compareIndex = index;
3072
+ break;
3073
+ }
3074
+ if (!AARCH64_FLAG_PRESERVING.contains(name)) return null;
3075
+ }
3076
+ if (compareIndex < 0) return null;
3077
+ java.util.regex.Matcher compareMatcher = AARCH64_COMPARE_IMMEDIATE.matcher(operands(instructions.get(compareIndex)));
3078
+ if (!compareMatcher.matches()) return null;
3079
+ String comparedRegister = normalizedRegister(compareMatcher.group(1));
3080
+ boolean comparedFullWidth = compareMatcher.group(1).toLowerCase(Locale.ROOT).startsWith("x");
3081
+ long compareImmediate = parseInteger(compareMatcher.group(2));
3082
+ long entryCountLong = guardMnemonic.equals("B.HI") ? compareImmediate + 1 : compareImmediate;
3083
+ if (entryCountLong <= 0 || entryCountLong > 4096) return null;
3084
+ int entryCount = (int) entryCountLong;
3085
+
3086
+ // The loaded index must carry the compared value: either the compared
3087
+ // register itself, unchanged since CMP, or a MOV copy made after CMP.
3088
+ // A W compare bounds only the low 32 bits, so an X index needs a
3089
+ // zero-extending W copy; a W index is zero-extended by UXTW.
3090
+ int indexWriter = latestWriter(instructions, loadIndex, indexRegister);
3091
+ boolean bounded = false;
3092
+ if (indexWriter < compareIndex) {
3093
+ bounded = indexRegister.equals(comparedRegister) && (wordIndex || comparedFullWidth);
3094
+ }
3095
+ else if (mnemonic(instructions.get(indexWriter)).equals("MOV")) {
3096
+ java.util.regex.Matcher moveMatcher = AARCH64_MOVE_REGISTER.matcher(operands(instructions.get(indexWriter)));
3097
+ if (moveMatcher.matches() &&
3098
+ normalizedRegister(moveMatcher.group(2)).equals(comparedRegister) &&
3099
+ latestWriter(instructions, indexWriter, comparedRegister) < compareIndex) {
3100
+ boolean wordCopy = moveMatcher.group(1).toLowerCase(Locale.ROOT).startsWith("w");
3101
+ bounded = wordCopy || comparedFullWidth;
3102
+ }
3103
+ }
3104
+ if (!bounded) return null;
3105
+
3106
+ // Every fact above holds only on one straight-line fall-through path
3107
+ // with no other entry into the window.
3108
+ int windowStart = Math.min(Math.min(tablePageIndex, baseIndex), compareIndex);
3109
+ for (int index = windowStart + 1; index <= branchIndex; index += 1) {
3110
+ monitor.checkCancelled();
3111
+ Instruction previous = instructions.get(index - 1);
3112
+ Instruction current = instructions.get(index);
3113
+ Address fallThrough = previous.getFallThrough();
3114
+ if (fallThrough == null || !fallThrough.equals(current.getAddress())) return null;
3115
+ ReferenceIterator incoming = currentProgram.getReferenceManager().getReferencesTo(current.getAddress());
3116
+ while (incoming.hasNext()) {
3117
+ if (incoming.next().getReferenceType().isFlow()) return null;
3118
+ }
3119
+ }
3120
+
3121
+ List<RecoveredAArch64Mapping> mappings = new ArrayList<>();
3122
+ Set<String> mappedTargets = new HashSet<>();
3123
+ for (int tableIndex = 0; tableIndex < entryCount; tableIndex += 1) {
3124
+ monitor.checkCancelled();
3125
+ Address entry = tableAddress.add((long) tableIndex * entrySize);
3126
+ if (!currentProgram.getMemory().contains(entry)) return null;
3127
+ long encoded;
3128
+ if (entrySize == 1) {
3129
+ byte value = currentProgram.getMemory().getByte(entry);
3130
+ encoded = signedEntries ? value : value & 0xffL;
3131
+ }
3132
+ else {
3133
+ short value = currentProgram.getMemory().getShort(entry);
3134
+ encoded = signedEntries ? value : value & 0xffffL;
3135
+ }
3136
+ if (encoded == 0 && nullTarget != null) {
3137
+ mappings.add(new RecoveredAArch64Mapping(tableIndex, nullTarget, encoded, true));
3138
+ continue;
3139
+ }
3140
+ Address target;
3141
+ try {
3142
+ target = branchBase.addNoWrap(encoded << offsetShift);
3143
+ }
3144
+ catch (ghidra.program.model.address.AddressOverflowException overflow) {
3145
+ return null;
3146
+ }
3147
+ String canonicalTarget = canonicalAddress(target);
3148
+ if (!recoveredTargetSet.contains(canonicalTarget)) return null;
3149
+ mappedTargets.add(canonicalTarget);
3150
+ mappings.add(new RecoveredAArch64Mapping(tableIndex, target, encoded, false));
3151
+ }
3152
+ if (mappedTargets.contains(canonicalAddress(defaultTarget))) return null;
3153
+ // Ghidra's recovered destinations must be fully accounted for by the
3154
+ // decoded entries, the guard default, and the CBZ null path.
3155
+ Set<String> accountedTargets = new HashSet<>(mappedTargets);
3156
+ accountedTargets.add(canonicalAddress(defaultTarget));
3157
+ if (nullTarget != null) accountedTargets.add(canonicalAddress(nullTarget));
3158
+ // Ghidra does not model the CBZ, so it may decode a zero entry as a
3159
+ // branch to the base itself. Only a zero entry encodes offset 0, and
3160
+ // the CBZ diverts every zero entry, so the BR never reaches the base.
3161
+ Address unreachableZeroTarget = null;
3162
+ boolean divertedZeroEntry = mappings.stream().anyMatch(mapping -> mapping.nullEntry);
3163
+ if (divertedZeroEntry &&
3164
+ recoveredTargetSet.contains(canonicalAddress(branchBase)) &&
3165
+ !mappedTargets.contains(canonicalAddress(branchBase)) &&
3166
+ !branchBase.equals(defaultTarget) &&
3167
+ !branchBase.equals(nullTarget)) {
3168
+ unreachableZeroTarget = branchBase;
3169
+ accountedTargets.add(canonicalAddress(branchBase));
3170
+ }
3171
+ if (!accountedTargets.containsAll(recoveredTargetSet)) return null;
3172
+ int selectorWriter = latestWriter(instructions, compareIndex, comparedRegister);
3173
+ return new RecoveredAArch64RelativeTable(
3174
+ tableAddress,
3175
+ branchBase,
3176
+ entrySize,
3177
+ selectorWriter < 0 ? null : instructions.get(selectorWriter).getAddress(),
3178
+ signedEntries,
3179
+ offsetShift,
3180
+ guardMnemonic,
3181
+ compareImmediate,
3182
+ nullTarget,
3183
+ unreachableZeroTarget,
3184
+ List.copyOf(mappings)
3185
+ );
3186
+ }
3187
+
3188
+ private static String mnemonic(Instruction instruction) {
3189
+ return instruction.getMnemonicString().toUpperCase(Locale.ROOT);
3190
+ }
3191
+
3192
+ /** Index of the last instruction before {@code before} that writes {@code register}, or -1. */
3193
+ private static int latestWriter(List<Instruction> instructions, int before, String register) {
3194
+ for (int index = before - 1; index >= 0; index -= 1) {
3195
+ for (Object result : instructions.get(index).getResultObjects()) {
3196
+ if (result instanceof Register written &&
3197
+ normalizedRegister(written.getBaseRegister().getName()).equals(register)) {
3198
+ return index;
2424
3199
  }
2425
- mappedTargets.add(canonicalTarget);
2426
- mappings.add(new RecoveredAArch64Mapping(caseValue, target));
2427
3200
  }
2428
- if (!valid || mappedTargets.contains(canonicalAddress(defaultTarget))) continue;
2429
- Set<String> accountedTargets = new HashSet<>(mappedTargets);
2430
- accountedTargets.add(canonicalAddress(defaultTarget));
2431
- if (!accountedTargets.equals(recoveredTargetSet)) continue;
2432
- return new RecoveredAArch64RelativeTable(tableAddress, branchBase, entrySize, List.copyOf(mappings));
2433
3201
  }
2434
- return null;
3202
+ return -1;
2435
3203
  }
2436
3204
 
2437
3205
  private static String operands(Instruction instruction) {
@@ -2578,6 +3346,285 @@ public final class ReaGhidraBridge extends HeadlessScript {
2578
3346
  return new InstructionScan(List.copyOf(instructions), iterator.hasNext());
2579
3347
  }
2580
3348
 
3349
+ private JsonObject functionObservation(
3350
+ JsonElement value,
3351
+ List<String> limitations) {
3352
+ JsonObject result = new JsonObject();
3353
+ result.add("value", value);
3354
+ result.add("limitations", GSON.toJsonTree(limitations));
3355
+ return result;
3356
+ }
3357
+
3358
+ private static JsonObject facetAvailability(boolean available, String reason) {
3359
+ JsonObject result = new JsonObject();
3360
+ result.addProperty("available", available);
3361
+ if (!available) result.addProperty("reason", reason);
3362
+ return result;
3363
+ }
3364
+
3365
+ private JsonObject callRelationshipObservation(JsonElement value, CallBoundaryObservation calls)
3366
+ throws Exception {
3367
+ monitor.checkCancelled();
3368
+ JsonObject result = functionObservation(value, calls.limitations());
3369
+ result.add("relationship_coverage", callRelationshipCoverage(calls, null));
3370
+ return result;
3371
+ }
3372
+
3373
+ private static JsonObject callRelationshipCoverage(
3374
+ CallBoundaryObservation calls, Integer missingCallers) {
3375
+ JsonObject coverage = completeResolvedCallCoverage();
3376
+ Integer omitted = calls == null ? null : calls.unrepresentedCalleeCalls();
3377
+ JsonArray directions = new JsonArray();
3378
+ if (missingCallers != null && missingCallers > 0) directions.add("callers");
3379
+ String callerLimitation = missingCallers != null && missingCallers > 0
3380
+ ? "Ghidra's caller inventory omits sources of " + missingCallers +
3381
+ " call reference(s) into this function's body." : null;
3382
+ if ((calls != null && omitted == null) || (calls == null && missingCallers == null)) {
3383
+ coverage.addProperty("status", "unknown");
3384
+ String reason = calls == null ? "No call inventory was examined."
3385
+ : "The callee inventory was not examined alongside decoded call targets.";
3386
+ coverage.addProperty("reason", reason + (callerLimitation == null ? "" : " " + callerLimitation));
3387
+ if (calls == null && missingCallers == null) directions.add("callers");
3388
+ directions.add("callees");
3389
+ coverage.add("limited_directions", directions);
3390
+ return coverage;
3391
+ }
3392
+ JsonArray limitations = new JsonArray();
3393
+ if (callerLimitation != null) limitations.add(callerLimitation);
3394
+ if (omitted != null && omitted > 0) {
3395
+ limitations.add("Ghidra's callee inventory omits targets of " + omitted +
3396
+ " call reference(s) or decoded call(s) without destinations.");
3397
+ directions.add("callees");
3398
+ }
3399
+ if (!limitations.isEmpty()) {
3400
+ coverage.addProperty("status", "partial");
3401
+ coverage.add("limitations", limitations);
3402
+ coverage.add("limited_directions", directions);
3403
+ }
3404
+ return coverage;
3405
+ }
3406
+
3407
+ private static JsonObject completeResolvedCallCoverage() {
3408
+ JsonObject coverage = new JsonObject();
3409
+ coverage.addProperty("status", "complete");
3410
+ JsonArray diagnostics = new JsonArray();
3411
+ diagnostics.add(
3412
+ "Only resolved Ghidra call references are returned; unresolved computed or indirect calls remain unknown, while function classifications distinguish thunks and externals."
3413
+ );
3414
+ coverage.add("diagnostics", diagnostics);
3415
+ return coverage;
3416
+ }
3417
+
3418
+ private boolean unrepresentedCaller(Reference reference, Set<Function> callers) {
3419
+ if (!reference.getReferenceType().isCall()) return false;
3420
+ Function caller = containingFunction(reference.getFromAddress());
3421
+ return caller == null || !callers.contains(caller);
3422
+ }
3423
+
3424
+ private int unrepresentedIncomingCalls(Function function, Set<Function> callers)
3425
+ throws Exception {
3426
+ int[] omitted = { 0 };
3427
+ visitReferences(function, "incoming", reference -> {
3428
+ if (unrepresentedCaller(reference, callers)) omitted[0]++;
3429
+ });
3430
+ return omitted[0];
3431
+ }
3432
+
3433
+ private List<String> noReturnCallLimitations(
3434
+ Function function, Iterable<Instruction> instructions)
3435
+ throws Exception {
3436
+ List<String> limitations = new ArrayList<>();
3437
+ Instruction beforePrevious = null;
3438
+ Instruction previous = null;
3439
+ for (Instruction instruction : instructions) {
3440
+ monitor.checkCancelled();
3441
+ String limitation = terminalCallLimitation(instruction);
3442
+ if (limitation != null) limitations.add(limitation);
3443
+ if (beforePrevious != null && previous != null) {
3444
+ String seh = borlandSehHandlerLimitation(
3445
+ function, beforePrevious, previous, instruction);
3446
+ if (seh != null) limitations.add(seh);
3447
+ }
3448
+ beforePrevious = previous;
3449
+ previous = instruction;
3450
+ }
3451
+ return limitations;
3452
+ }
3453
+
3454
+ private CallBoundaryObservation callBoundaryObservation(
3455
+ Function function, Iterable<Instruction> instructions, Set<Function> returnedCallees,
3456
+ boolean includeUnresolvedSites, List<Reference> observedReferences)
3457
+ throws Exception {
3458
+ List<String> limitations = new ArrayList<>();
3459
+ CallBoundaryReferences calls = new CallBoundaryReferences(returnedCallees, includeUnresolvedSites);
3460
+ Instruction beforePrevious = null;
3461
+ Instruction previous = null;
3462
+ if (observedReferences == null) visitReferences(function, "outgoing", calls::observe);
3463
+ else for (Reference reference : observedReferences) {
3464
+ monitor.checkCancelled();
3465
+ calls.observe(reference);
3466
+ }
3467
+ for (Instruction instruction : instructions) {
3468
+ monitor.checkCancelled();
3469
+ String limitation = terminalCallLimitation(instruction);
3470
+ if (limitation != null) limitations.add(limitation);
3471
+ if (beforePrevious != null && previous != null) {
3472
+ String seh = borlandSehHandlerLimitation(
3473
+ function, beforePrevious, previous, instruction);
3474
+ if (seh != null) limitations.add(seh);
3475
+ }
3476
+ beforePrevious = previous;
3477
+ previous = instruction;
3478
+ if (!instruction.getFlowType().isCall()) continue;
3479
+ boolean destination = false;
3480
+ for (Reference reference : instruction.getReferencesFrom()) {
3481
+ if (!reference.getReferenceType().isCall()) continue;
3482
+ destination = true;
3483
+ break;
3484
+ }
3485
+ if (!destination) {
3486
+ calls.unrepresented++;
3487
+ calls.unresolved(instruction.getAddress(),
3488
+ "Instruction decoder identifies a call but ReferenceManager supplies no call destination");
3489
+ }
3490
+ }
3491
+ return new CallBoundaryObservation(List.copyOf(limitations),
3492
+ returnedCallees == null ? null : calls.unrepresented, calls.unresolvedCalls());
3493
+ }
3494
+
3495
+ private String borlandSehHandlerLimitation(
3496
+ Function function, Instruction handlerPush, Instruction framePush,
3497
+ Instruction frameLink) {
3498
+ if (!immediatelyPrecedes(handlerPush, framePush) ||
3499
+ !immediatelyPrecedes(framePush, frameLink) ||
3500
+ !handlerPush.getMnemonicString().equalsIgnoreCase("PUSH") ||
3501
+ handlerPush.getNumOperands() != 1 ||
3502
+ !framePush.getMnemonicString().equalsIgnoreCase("PUSH") ||
3503
+ framePush.getNumOperands() != 1 ||
3504
+ !frameLink.getMnemonicString().equalsIgnoreCase("MOV") ||
3505
+ frameLink.getNumOperands() != 2 ||
3506
+ !sameFsMemoryOperand(framePush, frameLink) ||
3507
+ !hasRegisterOperand(frameLink, 1, "ESP"))
3508
+ return null;
3509
+ Object[] handlerOperand = handlerPush.getOpObjects(0);
3510
+ if (handlerOperand.length != 1 || !(handlerOperand[0] instanceof Scalar scalar) ||
3511
+ scalar.bitLength() != 32)
3512
+ return null;
3513
+ Address handler = currentProgram.getAddressFactory().getDefaultAddressSpace()
3514
+ .getAddress(scalar.getUnsignedValue());
3515
+ if (function.getBody().contains(handler)) return null;
3516
+ return "Ghidra Listing shows a Borland/Delphi-style SEH frame registration at " +
3517
+ canonicalAddress(frameLink.getAddress()) + " (push " + canonicalAddress(handler) +
3518
+ " followed by fs-segment stack chaining); handler " + canonicalAddress(handler) +
3519
+ " lies outside this procedure's body, so callees, strings, and cross-references may omit the handler region.";
3520
+ }
3521
+
3522
+ private static boolean immediatelyPrecedes(Instruction first, Instruction second) {
3523
+ Address fallThrough = first.getFallThrough();
3524
+ return fallThrough != null && fallThrough.equals(second.getAddress());
3525
+ }
3526
+
3527
+ private static boolean sameFsMemoryOperand(Instruction first, Instruction second) {
3528
+ String firstOperand = normalizedFsMemoryOperand(first);
3529
+ String secondOperand = normalizedFsMemoryOperand(second);
3530
+ return firstOperand != null && firstOperand.equalsIgnoreCase(secondOperand);
3531
+ }
3532
+
3533
+ private static String normalizedFsMemoryOperand(Instruction instruction) {
3534
+ String representation = instruction.getDefaultOperandRepresentation(0);
3535
+ representation = representation.replaceAll("(?i)\\b(?:byte|word|dword|qword)\\s+ptr\\s+", "")
3536
+ .replaceAll("\\s+", "");
3537
+ return representation.regionMatches(true, 0, "fs:", 0, 3) &&
3538
+ representation.indexOf('[') >= 0 ? representation : null;
3539
+ }
3540
+
3541
+ private static boolean hasRegisterOperand(
3542
+ Instruction instruction, int operand, String name) {
3543
+ for (Object object : instruction.getOpObjects(operand)) {
3544
+ if (object instanceof Register register && register.getName().equalsIgnoreCase(name))
3545
+ return true;
3546
+ }
3547
+ return false;
3548
+ }
3549
+
3550
+ private final class CallBoundaryReferences {
3551
+ private final Set<Function> returnedCallees;
3552
+ private final TreeMap<Address, String> unresolved;
3553
+ private int unrepresented;
3554
+
3555
+ CallBoundaryReferences(Set<Function> returnedCallees, boolean includeUnresolvedSites) {
3556
+ this.returnedCallees = returnedCallees;
3557
+ unresolved = includeUnresolvedSites ? new TreeMap<>() : null;
3558
+ }
3559
+
3560
+ void observe(Reference reference) {
3561
+ if (!reference.getReferenceType().isCall()) return;
3562
+ // Reference identity follows the same containing-function owner as referenceEdge.
3563
+ Function target = containingFunction(reference.getToAddress());
3564
+ if (returnedCallees != null && (target == null || !returnedCallees.contains(target)))
3565
+ unrepresented++;
3566
+ if (target == null) unresolved(reference.getFromAddress(),
3567
+ "A call destination has no known containing Ghidra procedure");
3568
+ }
3569
+
3570
+ void unresolved(Address address, String reason) {
3571
+ if (unresolved != null) unresolved.put(address, reason);
3572
+ }
3573
+
3574
+ JsonArray unresolvedCalls() {
3575
+ if (unresolved == null) return null;
3576
+ JsonArray result = new JsonArray();
3577
+ for (var item : unresolved.entrySet()) {
3578
+ JsonObject site = new JsonObject();
3579
+ site.addProperty("address", canonicalAddress(item.getKey()));
3580
+ site.addProperty("reason", item.getValue());
3581
+ result.add(site);
3582
+ }
3583
+ return result;
3584
+ }
3585
+ }
3586
+
3587
+ private String terminalCallLimitation(Instruction instruction) {
3588
+ if (!instruction.getFlowType().isCall() || !instruction.getFlowType().isTerminal())
3589
+ return null;
3590
+ Address[] destinations = instruction.getFlows();
3591
+ StringBuilder limitation = new StringBuilder();
3592
+ limitation.append("Ghidra Listing reports a terminal call at ")
3593
+ .append(canonicalAddress(instruction.getAddress()));
3594
+ if (destinations.length == 0) {
3595
+ limitation.append(" with no resolved callee; its no-return basis is unknown.");
3596
+ }
3597
+ else {
3598
+ limitation.append(" to ");
3599
+ for (int index = 0; index < destinations.length; index++) {
3600
+ if (index > 0) limitation.append(", ");
3601
+ Address destination = destinations[index];
3602
+ limitation.append(canonicalAddress(destination));
3603
+ Function target = currentProgram.getFunctionManager().getFunctionAt(destination);
3604
+ if (target == null) {
3605
+ limitation.append(" (callee identity unknown)");
3606
+ }
3607
+ else {
3608
+ limitation.append(" ").append(procedureName(target));
3609
+ limitation.append(" (external=").append(target.isExternal());
3610
+ limitation.append(", thunk=").append(target.isThunk());
3611
+ limitation.append(", hasNoReturn=").append(target.hasNoReturn()).append(")");
3612
+ }
3613
+ }
3614
+ limitation.append("; fallthrough is excluded by Ghidra's flow model.");
3615
+ Function target = destinations.length == 1
3616
+ ? currentProgram.getFunctionManager().getFunctionAt(destinations[0])
3617
+ : null;
3618
+ if (target != null && target.hasNoReturn()) {
3619
+ limitation.append(" This records Ghidra's FunctionManager flag, not an independent verification that the callee cannot return.");
3620
+ }
3621
+ else {
3622
+ limitation.append(" The reason for the terminal flow is not established by this observation.");
3623
+ }
3624
+ }
3625
+ return limitation.toString();
3626
+ }
3627
+
2581
3628
  private static String renderAssembly(List<Instruction> instructions) {
2582
3629
  return String.join("\n", renderAssemblyLines(instructions));
2583
3630
  }
@@ -2601,15 +3648,27 @@ public final class ReaGhidraBridge extends HeadlessScript {
2601
3648
  private List<Reference> collectReferences(
2602
3649
  Function function,
2603
3650
  String direction) throws Exception {
3651
+ TreeMap<String, Reference> observed = new TreeMap<>();
3652
+ visitReferences(function, direction, reference -> addReference(observed, reference));
3653
+ List<Reference> result = new ArrayList<>(observed.values());
3654
+ result.sort(REFERENCE_ORDER);
3655
+ return result;
3656
+ }
3657
+
3658
+ @FunctionalInterface
3659
+ private interface ReferenceVisitor {
3660
+ void accept(Reference reference) throws Exception;
3661
+ }
3662
+
3663
+ private void visitReferences(Function function, String direction, ReferenceVisitor visitor)
3664
+ throws Exception {
2604
3665
  // References can target instruction interiors or originate in embedded
2605
3666
  // data. Use actual body ownership, including an external's exact entry.
2606
- AddressSet scope = new AddressSet(function.getBody());
2607
- scope.add(function.getEntryPoint());
3667
+ AddressSet scope = functionObservationScope(function);
2608
3668
  boolean outgoing = direction.equals("outgoing");
2609
3669
  AddressIterator addresses = outgoing
2610
3670
  ? currentProgram.getReferenceManager().getReferenceSourceIterator(scope, true)
2611
3671
  : currentProgram.getReferenceManager().getReferenceDestinationIterator(scope, true);
2612
- TreeMap<String, Reference> observed = new TreeMap<>();
2613
3672
  while (addresses.hasNext()) {
2614
3673
  monitor.checkCancelled();
2615
3674
  Address address = addresses.next();
@@ -2617,7 +3676,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
2617
3676
  for (Reference reference : currentProgram.getReferenceManager()
2618
3677
  .getReferencesFrom(address)) {
2619
3678
  monitor.checkCancelled();
2620
- addReference(observed, reference);
3679
+ if (!reference.isEntryPointReference()) visitor.accept(reference);
2621
3680
  }
2622
3681
  }
2623
3682
  else {
@@ -2625,19 +3684,14 @@ public final class ReaGhidraBridge extends HeadlessScript {
2625
3684
  .getReferencesTo(address);
2626
3685
  while (iterator.hasNext()) {
2627
3686
  monitor.checkCancelled();
2628
- addReference(observed, iterator.next());
3687
+ Reference reference = iterator.next();
3688
+ if (!reference.isEntryPointReference()) visitor.accept(reference);
2629
3689
  }
2630
3690
  }
2631
3691
  }
2632
- List<Reference> result = new ArrayList<>(observed.values());
2633
- result.sort(REFERENCE_ORDER);
2634
- return result;
2635
3692
  }
2636
3693
 
2637
3694
  private static void addReference(TreeMap<String, Reference> observed, Reference reference) {
2638
- if (reference.isEntryPointReference()) {
2639
- return;
2640
- }
2641
3695
  String key = canonicalAddress(reference.getFromAddress()) + "\u0000" +
2642
3696
  canonicalAddress(reference.getToAddress()) + "\u0000" +
2643
3697
  reference.getReferenceType().getName() + "\u0000" +
@@ -2646,6 +3700,12 @@ public final class ReaGhidraBridge extends HeadlessScript {
2646
3700
  observed.putIfAbsent(key, reference);
2647
3701
  }
2648
3702
 
3703
+ private static AddressSet functionObservationScope(Function function) {
3704
+ AddressSet scope = new AddressSet(function.getBody());
3705
+ scope.add(function.getEntryPoint());
3706
+ return scope;
3707
+ }
3708
+
2649
3709
  private JsonArray referenceEdges(List<Reference> references) {
2650
3710
  JsonArray result = new JsonArray();
2651
3711
  for (Reference reference : references) {
@@ -2799,23 +3859,28 @@ public final class ReaGhidraBridge extends HeadlessScript {
2799
3859
  return result;
2800
3860
  }
2801
3861
 
2802
- private static JsonArray comments(List<Instruction> instructions) {
3862
+ private JsonArray comments(Function function) throws Exception {
3863
+ Listing listing = currentProgram.getListing();
3864
+ AddressSet scope = functionObservationScope(function);
3865
+ AddressIterator addresses = listing.getCommentAddressIterator(scope, true);
2803
3866
  TreeMap<String, JsonObject> observed = new TreeMap<>();
2804
- for (Instruction instruction : instructions) {
3867
+ while (addresses.hasNext()) {
3868
+ monitor.checkCancelled();
3869
+ Address address = addresses.next();
2805
3870
  for (CommentType type : CommentType.values()) {
2806
- String text = instruction.getComment(type);
3871
+ String text = listing.getComment(type, address);
2807
3872
  if (text == null || text.isEmpty()) {
2808
3873
  continue;
2809
3874
  }
2810
3875
  String kind = type == CommentType.EOL ? "inline" : "comment";
2811
3876
  JsonObject item = new JsonObject();
2812
- item.addProperty("address", canonicalAddress(instruction.getAddress()));
3877
+ item.addProperty("address", canonicalAddress(address));
2813
3878
  item.addProperty("kind", kind);
2814
3879
  item.addProperty("text", text);
2815
- String key = canonicalAddress(instruction.getAddress()) + "\u0000" +
3880
+ String key = canonicalAddress(address) + "\u0000" +
2816
3881
  kind + "\u0000" + type.name() + "\u0000" + text;
2817
3882
  observed.put(key, item);
2818
- }
3883
+ }
2819
3884
  }
2820
3885
  JsonArray result = new JsonArray();
2821
3886
  observed.values().forEach(result::add);
@@ -2870,6 +3935,376 @@ public final class ReaGhidraBridge extends HeadlessScript {
2870
3935
  return result;
2871
3936
  }
2872
3937
 
3938
+ private boolean swiftProgram() {
3939
+ for (MemoryBlock block : currentProgram.getMemory().getBlocks()) {
3940
+ String name = block.getName();
3941
+ if (name.startsWith("__swift5_") || name.startsWith("swift5_")) return true;
3942
+ }
3943
+ return false;
3944
+ }
3945
+
3946
+ /**
3947
+ * Swift passes a String as two words: small literals carry their bytes in the words and
3948
+ * immortal large literals carry their count and a biased pointer to the bytes. Ghidra keeps
3949
+ * neither as a data reference, so decode constant pairs where Swift consumes them: argument
3950
+ * and return registers at calls, tail calls and returns, and both registers of one STP.
3951
+ * The canonical empty string is a small literal with a zero count.
3952
+ */
3953
+ private SwiftStringLiteralDecode swiftStringLiterals(Function function) throws Exception {
3954
+ return swiftStringLiterals(function, new SwiftLiteralBudget());
3955
+ }
3956
+
3957
+ // An explicit budget makes capacity omissions deterministic in producer tests.
3958
+ private SwiftStringLiteralDecode swiftStringLiterals(
3959
+ Function function,
3960
+ SwiftLiteralBudget budget
3961
+ ) throws Exception {
3962
+ Register[] registers = new Register[31];
3963
+ for (int index = 0; index < registers.length; index++) {
3964
+ registers[index] = currentProgram.getRegister("x" + index);
3965
+ }
3966
+ TreeMap<String, JsonObject> observed = new TreeMap<>();
3967
+ TreeMap<String, JsonObject> omitted = new TreeMap<>();
3968
+ Map<String, Boolean> selectedValues = new HashMap<>();
3969
+ SymbolicPropogator propagator = new SymbolicPropogator(currentProgram, false);
3970
+ propagator.flowConstants(function.getEntryPoint(), function.getBody(), new ContextEvaluatorAdapter() {
3971
+ @Override
3972
+ public boolean evaluateContextBefore(VarnodeContext context, Instruction instruction) {
3973
+ // Returning true ends only the current flow, so every queued flow checks the shared stop.
3974
+ if (budget.stopped()) return true;
3975
+ FlowType flow = instruction.getFlowType();
3976
+ Address[] flowTargets = instruction.getFlows();
3977
+ boolean unresolvedJump = flow.isJump() && !flow.isConditional() && flowTargets.length == 0;
3978
+ for (Register[] pair : swiftStringPairs(function, instruction, registers)) {
3979
+ Long word = registerConstant(context, pair[0]);
3980
+ Long object = registerConstant(context, pair[1]);
3981
+ if (word == null || object == null) continue;
3982
+ SwiftLiteralLayout layout = swiftLiteralLayout(word, object);
3983
+ if (layout == null) continue;
3984
+ // A selected word can combine with a path-dependent ordinary move. Cache the
3985
+ // provenance question per site/register, charging retained cache state to the
3986
+ // same dossier budget before allocating it.
3987
+ boolean selected = false;
3988
+ boolean provenanceOverCapacity = false;
3989
+ for (Register register : pair) {
3990
+ String provenanceKey = canonicalAddress(instruction.getAddress()) + "\u0000" +
3991
+ register.getBaseRegister().getName();
3992
+ Boolean value = selectedValues.get(provenanceKey);
3993
+ if (value == null) {
3994
+ if (!budget.takeState()) {
3995
+ provenanceOverCapacity = true;
3996
+ break;
3997
+ }
3998
+ value = selectedValue(function, instruction, register);
3999
+ selectedValues.put(provenanceKey, value);
4000
+ }
4001
+ if (value) {
4002
+ selected = true;
4003
+ break;
4004
+ }
4005
+ }
4006
+ if (selected) continue;
4007
+ // Forks can reach one site with different constants, so the words are part of the key.
4008
+ String key = canonicalAddress(instruction.getAddress()) + "\u0000" + pair[0].getName() +
4009
+ "\u0000" + pair[1].getName() + "\u0000" + Long.toUnsignedString(word, 16) +
4010
+ "\u0000" + Long.toUnsignedString(object, 16);
4011
+ if (observed.containsKey(key) || omitted.containsKey(key)) continue;
4012
+ // Every record draws on the budget; the first one that does not fit is kept as an
4013
+ // omission and decoding stops, so later sites are never examined.
4014
+ String value = layout.smallValue();
4015
+ boolean overCapacity = provenanceOverCapacity || !budget.take(layout.byteLength());
4016
+ if (!overCapacity && value == null) {
4017
+ try {
4018
+ value = swiftLargeLiteralText(layout);
4019
+ } catch (OutOfMemoryError exhausted) {
4020
+ overCapacity = true;
4021
+ }
4022
+ }
4023
+ if (value == null && !overCapacity) continue;
4024
+ JsonObject item = overCapacity
4025
+ ? swiftStringOmission(layout, word, object)
4026
+ : swiftStringItem(value, layout, word, object);
4027
+ if (unresolvedJump && !overCapacity)
4028
+ item = swiftStringOmission(layout, word, object, "unresolved_jump");
4029
+ item.addProperty("source_address", canonicalAddress(instruction.getAddress()));
4030
+ JsonArray pairRegisters = new JsonArray();
4031
+ pairRegisters.add(pair[0].getName());
4032
+ pairRegisters.add(pair[1].getName());
4033
+ item.add("registers", pairRegisters);
4034
+ (overCapacity || unresolvedJump ? omitted : observed).put(key, item);
4035
+ if (overCapacity) {
4036
+ budget.stop();
4037
+ return true;
4038
+ }
4039
+ }
4040
+ return false;
4041
+ }
4042
+ }, false, monitor);
4043
+ JsonArray literals = new JsonArray();
4044
+ observed.values().forEach(literals::add);
4045
+ JsonArray omissions = new JsonArray();
4046
+ omitted.values().forEach(omissions::add);
4047
+ return new SwiftStringLiteralDecode(literals, omissions);
4048
+ }
4049
+
4050
+ private static List<Register[]> swiftStringPairs(
4051
+ Function function,
4052
+ Instruction instruction,
4053
+ Register[] registers
4054
+ ) {
4055
+ List<Register[]> pairs = new ArrayList<>();
4056
+ if (instruction.getMnemonicString().equals("stp")) {
4057
+ Register first = instruction.getRegister(0);
4058
+ Register second = instruction.getRegister(1);
4059
+ if (first != null && second != null && first.getBitLength() == 64 && second.getBitLength() == 64) {
4060
+ pairs.add(new Register[] {first, second});
4061
+ }
4062
+ return pairs;
4063
+ }
4064
+ FlowType flow = instruction.getFlowType();
4065
+ boolean returns = false;
4066
+ for (PcodeOp operation : instruction.getPcode()) {
4067
+ if (operation.getOpcode() == PcodeOp.RETURN) returns = true;
4068
+ }
4069
+ // A jump is a proven tail call only when it has resolved targets and all leave the
4070
+ // body. For unresolved targetless jumps, candidate literals remain unknown omissions.
4071
+ Address[] targets = instruction.getFlows();
4072
+ boolean tailCall = flow.isJump() && !flow.isConditional() && targets.length > 0 &&
4073
+ Arrays.stream(targets).allMatch(target -> !function.getBody().contains(target));
4074
+ boolean unresolvedJump = flow.isJump() && !flow.isConditional() && targets.length == 0;
4075
+ if (!flow.isCall() && !returns && !tailCall && !unresolvedJump) return pairs;
4076
+ // A String occupies two consecutive registers among x0-x7, or x0 and x1 when returned.
4077
+ int lastFirstRegister = returns ? 0 : 6;
4078
+ for (int index = 0; index <= lastFirstRegister; index++) {
4079
+ pairs.add(new Register[] {registers[index], registers[index + 1]});
4080
+ }
4081
+ return pairs;
4082
+ }
4083
+
4084
+ private static Long registerConstant(VarnodeContext context, Register register) {
4085
+ Varnode value = context.getRegisterVarnodeValue(register);
4086
+ return value != null && context.isConstant(value) ? value.getOffset() : null;
4087
+ }
4088
+
4089
+ // Search every predecessor path for the reaching definitions, following register copies,
4090
+ // since a select copied into an argument register or defined before a branch forks just as
4091
+ // independently as one written there directly.
4092
+ private boolean selectedValue(Function function, Instruction use, Register register) {
4093
+ ArrayDeque<Instruction> instructions = new ArrayDeque<>();
4094
+ ArrayDeque<Register> targets = new ArrayDeque<>();
4095
+ Set<String> visited = new HashSet<>();
4096
+ for (Instruction predecessor : predecessors(function, use)) {
4097
+ instructions.push(predecessor);
4098
+ targets.push(register.getBaseRegister());
4099
+ }
4100
+ while (!instructions.isEmpty()) {
4101
+ Instruction instruction = instructions.pop();
4102
+ Register target = targets.pop();
4103
+ if (!visited.add(canonicalAddress(instruction.getAddress()) + "\u0000" + target.getName())) continue;
4104
+ boolean defines = false;
4105
+ for (Object result : instruction.getResultObjects()) {
4106
+ if (result instanceof Register written && written.getBaseRegister().equals(target)) defines = true;
4107
+ }
4108
+ Register next = target;
4109
+ if (defines) {
4110
+ PcodeOp[] operations = instruction.getPcode();
4111
+ for (PcodeOp operation : operations) {
4112
+ if (operation.getOpcode() == PcodeOp.CBRANCH && operation.getInput(0).isConstant()) return true;
4113
+ }
4114
+ Register source = copiedRegister(operations);
4115
+ if (source == null) continue;
4116
+ next = source.getBaseRegister();
4117
+ }
4118
+ for (Instruction predecessor : predecessors(function, instruction)) {
4119
+ instructions.push(predecessor);
4120
+ targets.push(next);
4121
+ }
4122
+ }
4123
+ return false;
4124
+ }
4125
+
4126
+ private List<Instruction> predecessors(Function function, Instruction instruction) {
4127
+ List<Instruction> result = new ArrayList<>();
4128
+ Address from = instruction.getFallFrom();
4129
+ if (from != null) {
4130
+ Instruction previous = currentProgram.getListing().getInstructionAt(from);
4131
+ if (previous != null) result.add(previous);
4132
+ }
4133
+ for (Reference reference : currentProgram.getReferenceManager().getReferencesTo(instruction.getAddress())) {
4134
+ if (!reference.getReferenceType().isFlow() || reference.getReferenceType().isCall()) continue;
4135
+ if (!function.getBody().contains(reference.getFromAddress())) continue;
4136
+ Instruction source = currentProgram.getListing().getInstructionAt(reference.getFromAddress());
4137
+ if (source != null) result.add(source);
4138
+ }
4139
+ return result;
4140
+ }
4141
+
4142
+ private Register copiedRegister(PcodeOp[] operations) {
4143
+ if (operations.length != 1) return null;
4144
+ int opcode = operations[0].getOpcode();
4145
+ Varnode input = operations[0].getInput(0);
4146
+ if ((opcode != PcodeOp.COPY && opcode != PcodeOp.INT_ZEXT) || input == null || !input.isRegister()) return null;
4147
+ return currentProgram.getRegister(input);
4148
+ }
4149
+
4150
+ /** Validate the ABI layout of a pair; a small literal is decoded here, a large one only located. */
4151
+ private SwiftLiteralLayout swiftLiteralLayout(long word, long object) {
4152
+ int discriminator = (int) (object >>> 60);
4153
+ if (discriminator == 0xE || discriminator == 0xA) {
4154
+ int count = (int) ((object >>> 56) & 0xF);
4155
+ byte[] bytes = new byte[15];
4156
+ for (int index = 0; index < 8; index++) bytes[index] = (byte) (word >>> (8 * index));
4157
+ for (int index = 0; index < 7; index++) bytes[8 + index] = (byte) (object >>> (8 * index));
4158
+ for (int index = count; index < bytes.length; index++) {
4159
+ if (bytes[index] != 0) return null;
4160
+ }
4161
+ // Count zero is the canonical empty string (object 0xE000000000000000, word 0).
4162
+ String value = swiftStringText(bytes, count, discriminator == 0xE);
4163
+ return value == null ? null : new SwiftLiteralLayout("small", null, count, true, value);
4164
+ }
4165
+ int flags = (int) (word >>> 60);
4166
+ long count = word & 0xFFFFFFFFFFFFL;
4167
+ // Immortal literals: ASCII and NFC or neither, tail allocated, with a pointer biased by 32.
4168
+ if ((object >>> 56) != 0x80 || (flags != 0xD && flags != 0x1) || ((word >>> 48) & 0xFFF) != 0) {
4169
+ return null;
4170
+ }
4171
+ try {
4172
+ Address address = currentProgram.getAddressFactory().getDefaultAddressSpace()
4173
+ .getAddress((object & 0x00FFFFFFFFFFFFFFL) + 0x20);
4174
+ MemoryBlock block = currentProgram.getMemory().getBlock(address);
4175
+ // The bytes and their terminator must lie in one initialized, non-executable block,
4176
+ // and Java arrays bound one read.
4177
+ if (block == null || !block.isInitialized() || block.isExecute() ||
4178
+ count >= block.getEnd().subtract(address) + 1 || count >= Integer.MAX_VALUE) {
4179
+ return null;
4180
+ }
4181
+ return new SwiftLiteralLayout("large", address, count, flags == 0xD, null);
4182
+ } catch (Exception unaddressable) {
4183
+ return null;
4184
+ }
4185
+ }
4186
+
4187
+ private String swiftLargeLiteralText(SwiftLiteralLayout layout) {
4188
+ int count = (int) layout.byteLength();
4189
+ byte[] bytes = new byte[count + 1];
4190
+ try {
4191
+ if (currentProgram.getMemory().getBytes(layout.address(), bytes) != bytes.length) return null;
4192
+ } catch (Exception unreadable) {
4193
+ return null;
4194
+ }
4195
+ return bytes[count] == 0 ? swiftStringText(bytes, count, layout.ascii()) : null;
4196
+ }
4197
+
4198
+ /**
4199
+ * Every literal record of one dossier draws on half of the heap that was free when decoding
4200
+ * began. Text is charged at the worst case per UTF-8 byte: two bytes of UTF-16 text, then up
4201
+ * to six escaped JSON characters ("\\u0000") held as UTF-16 in Gson's growing writer buffer
4202
+ * (up to twice its length), again in the returned String, and once more as UTF-8 on the
4203
+ * socket. Each record also pays a fixed cost for its map key, JSON members, addresses, words
4204
+ * and their serialized form, so many empty or small literals are bounded too. Retained
4205
+ * provenance-cache entries draw on that same fixed-state allowance.
4206
+ */
4207
+ private static final class SwiftLiteralBudget {
4208
+ private static final long BYTES_PER_LITERAL_BYTE = 2 + 6 * 2 * 2 + 6 * 2 + 6;
4209
+ private static final long BYTES_PER_RECORD = 4096;
4210
+ private long remaining;
4211
+ private boolean stopped;
4212
+
4213
+ SwiftLiteralBudget() {
4214
+ Runtime runtime = Runtime.getRuntime();
4215
+ remaining = Math.max(0, (runtime.maxMemory() - runtime.totalMemory() + runtime.freeMemory()) / 2);
4216
+ }
4217
+
4218
+ SwiftLiteralBudget(long remaining) {
4219
+ if (remaining < 0) throw new IllegalArgumentException("Swift literal budget must be nonnegative");
4220
+ this.remaining = remaining;
4221
+ }
4222
+
4223
+ boolean takeState() {
4224
+ if (remaining < BYTES_PER_RECORD) return false;
4225
+ remaining -= BYTES_PER_RECORD;
4226
+ return true;
4227
+ }
4228
+
4229
+ boolean take(long bytes) {
4230
+ if (remaining < BYTES_PER_RECORD ||
4231
+ bytes > (remaining - BYTES_PER_RECORD) / BYTES_PER_LITERAL_BYTE) {
4232
+ return false;
4233
+ }
4234
+ remaining -= BYTES_PER_RECORD + bytes * BYTES_PER_LITERAL_BYTE;
4235
+ return true;
4236
+ }
4237
+
4238
+ void stop() {
4239
+ stopped = true;
4240
+ }
4241
+
4242
+ boolean stopped() {
4243
+ return stopped;
4244
+ }
4245
+ }
4246
+
4247
+ // Swift String values may contain any scalar, including controls; JSON escapes them.
4248
+ private static String swiftStringText(byte[] bytes, int length, boolean ascii) {
4249
+ if (length < 0 || length > bytes.length) return null;
4250
+ for (int index = 0; index < length; index++) {
4251
+ if (ascii && bytes[index] < 0) return null;
4252
+ }
4253
+ try {
4254
+ return StandardCharsets.UTF_8.newDecoder().decode(ByteBuffer.wrap(bytes, 0, length)).toString();
4255
+ } catch (CharacterCodingException invalid) {
4256
+ return null;
4257
+ }
4258
+ }
4259
+
4260
+ private static JsonObject swiftStringOmission(SwiftLiteralLayout layout, long word, long object) {
4261
+ return swiftStringOmission(layout, word, object, "capacity");
4262
+ }
4263
+
4264
+ private static JsonObject swiftStringOmission(
4265
+ SwiftLiteralLayout layout,
4266
+ long word,
4267
+ long object,
4268
+ String reason
4269
+ ) {
4270
+ JsonObject item = new JsonObject();
4271
+ item.addProperty("representation", layout.representation());
4272
+ if (layout.address() == null) {
4273
+ item.add("address", JsonNull.INSTANCE);
4274
+ } else {
4275
+ item.addProperty("address", canonicalAddress(layout.address()));
4276
+ }
4277
+ item.addProperty("byte_length", layout.byteLength());
4278
+ item.addProperty("reason", reason);
4279
+ item.add("words", swiftStringWords(word, object));
4280
+ return item;
4281
+ }
4282
+
4283
+ private static JsonObject swiftStringItem(
4284
+ String value,
4285
+ SwiftLiteralLayout layout,
4286
+ long word,
4287
+ long object
4288
+ ) {
4289
+ JsonObject item = new JsonObject();
4290
+ item.addProperty("value", value);
4291
+ item.addProperty("representation", layout.representation());
4292
+ if (layout.address() == null) {
4293
+ item.add("address", JsonNull.INSTANCE);
4294
+ } else {
4295
+ item.addProperty("address", canonicalAddress(layout.address()));
4296
+ }
4297
+ item.add("words", swiftStringWords(word, object));
4298
+ return item;
4299
+ }
4300
+
4301
+ private static JsonArray swiftStringWords(long word, long object) {
4302
+ JsonArray words = new JsonArray();
4303
+ words.add("0x" + Long.toUnsignedString(word, 16));
4304
+ words.add("0x" + Long.toUnsignedString(object, 16));
4305
+ return words;
4306
+ }
4307
+
2873
4308
  private static String exclusiveAddress(Address inclusive) {
2874
4309
  Address next = inclusive.next();
2875
4310
  if (next != null && next.getAddressSpace().equals(inclusive.getAddressSpace())) {
@@ -3406,7 +4841,18 @@ public final class ReaGhidraBridge extends HeadlessScript {
3406
4841
  .thenComparing(Reference::isPrimary);
3407
4842
 
3408
4843
  private record InventoryItem(Address address, String value, JsonObject facts) {}
4844
+ private record AddressNameRequest(String inputAddress, Address address, String name) {}
3409
4845
  private record FunctionEntry(Function function, InventoryItem item) {}
4846
+ private record FunctionAnalysisObservation(JsonObject dossier, List<String> limitations, JsonObject relationshipCoverage) {}
4847
+ private record CallBoundaryObservation(List<String> limitations, Integer unrepresentedCalleeCalls, JsonArray unresolvedCalls) {}
4848
+ private record SwiftStringLiteralDecode(JsonArray literals, JsonArray omissions) {}
4849
+ private record SwiftLiteralLayout(
4850
+ String representation,
4851
+ Address address,
4852
+ long byteLength,
4853
+ boolean ascii,
4854
+ String smallValue
4855
+ ) {}
3410
4856
  private record InstructionScan(List<Instruction> instructions, boolean truncated) {}
3411
4857
  private record SessionDescriptor(
3412
4858
  String transport,