reverseport 0.0.0-stage → 1.2.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 (1489) hide show
  1. package/DISCLAIMER.md +136 -0
  2. package/LICENSE +22 -0
  3. package/README.md +289 -2
  4. package/RESPONSIBLE-USE.md +22 -0
  5. package/THIRD_PARTY_NOTICES.md +130 -0
  6. package/bin/reverseport.mjs +136 -0
  7. package/bridge/android/ReaJadxBridge.java +270 -0
  8. package/bridge/ghidra/ReaGhidraAnalysisOptions.java +38 -0
  9. package/bridge/ghidra/ReaGhidraApplySeeds.java +151 -0
  10. package/bridge/ghidra/ReaGhidraBridge.java +3858 -0
  11. package/bridge/ghidra/ReaGhidraBundleActivator.java +14 -0
  12. package/bridge/ghidra/ReaGhidraExtensions.java +113 -0
  13. package/bridge/ghidra/ReaGhidraMipsSoftFloat.java +176 -0
  14. package/bridge/ghidra/ReaGhidraNoReturnFix.java +123 -0
  15. package/bridge/ghidra/ReaGhidraPrepareCom.java +32 -0
  16. package/bridge/ghidra/ReaGhidraPrepareRaw.java +36 -0
  17. package/bridge/hopper_bridge.py +1162 -0
  18. package/bridge/mitmproxy/capture.py +208 -0
  19. package/bridge/native/NativeUIChildren.swift +27 -0
  20. package/bridge/native/ReaNativeUI.swift +171 -0
  21. package/bridge/native/rea_lldb_tracer.py +839 -0
  22. package/bridge/process/ProcessRunTokenReader.swift +339 -0
  23. package/bridge/process/ProcessRunTokenReaderMain.swift +35 -0
  24. package/bridge/pwndbg/core_context.py +101 -0
  25. package/bridge/pwndbg/launch.py +39 -0
  26. package/bridge/pwndbg/launch_impl.py +34 -0
  27. package/bridge/pwntools/decoder_runtime.py +59 -0
  28. package/bridge/pwntools/layout.py +39 -0
  29. package/bridge/pwntools/layout_impl.py +314 -0
  30. package/bridge/pwntools/recorded_crash.py +39 -0
  31. package/bridge/pwntools/recorded_crash_impl.py +147 -0
  32. package/dist/adb/AdbConfiguration.js +88 -0
  33. package/dist/adb/AdbDeviceOutput.js +191 -0
  34. package/dist/adb/AdbDirectoryListing.js +80 -0
  35. package/dist/adb/AdbHostFileName.js +34 -0
  36. package/dist/adb/AdbProvider.js +1227 -0
  37. package/dist/adb/AdbShellOutput.js +267 -0
  38. package/dist/android/AndroidTargetSnapshot.js +118 -0
  39. package/dist/android/JadxAnalysis.js +201 -0
  40. package/dist/android/JadxConfiguration.js +320 -0
  41. package/dist/android/JadxMcpTransport.js +188 -0
  42. package/dist/android/JadxProtocol.js +115 -0
  43. package/dist/android/JadxProvider.js +337 -0
  44. package/dist/android/JadxRelease.js +27 -0
  45. package/dist/android/JadxSession.js +172 -0
  46. package/dist/apktool/ApktoolConfiguration.js +120 -0
  47. package/dist/apktool/ApktoolDecodeOutput.js +142 -0
  48. package/dist/apktool/ApktoolProvider.js +522 -0
  49. package/dist/application/AnalysisContextQueries.js +128 -0
  50. package/dist/application/AnalysisDocument.js +32 -0
  51. package/dist/application/AnalysisProvider.js +27 -0
  52. package/dist/application/AppBundleExecutable.js +192 -0
  53. package/dist/application/BinaryTargetResolver.js +356 -0
  54. package/dist/application/BrowserEvidence.js +127 -0
  55. package/dist/application/BrowserObservationPort.js +1 -0
  56. package/dist/application/BrowserObservationService.js +86 -0
  57. package/dist/application/BrowserScenarioCapturePort.js +1 -0
  58. package/dist/application/BrowserScenarioCaptureService.js +17 -0
  59. package/dist/application/BrowserScenarioEvidence.js +28 -0
  60. package/dist/application/CallPathTracing.js +182 -0
  61. package/dist/application/CapabilityClientRequirements.js +17 -0
  62. package/dist/application/CapabilityInventory.js +525 -0
  63. package/dist/application/ClientConfigPath.js +35 -0
  64. package/dist/application/ClientConfigurationDocument.js +1196 -0
  65. package/dist/application/ClientConfigurationFiles.js +91 -0
  66. package/dist/application/ClientRegistrationEnvironment.js +39 -0
  67. package/dist/application/ClientRegistrationIdentity.js +80 -0
  68. package/dist/application/ClientRegistrationStatus.js +233 -0
  69. package/dist/application/DirectAnalysis.js +371 -0
  70. package/dist/application/DirectAnalysisCleanup.js +71 -0
  71. package/dist/application/DirectAnalysisDependencies.js +1 -0
  72. package/dist/application/DirectAnalysisStatus.js +22 -0
  73. package/dist/application/Doctor.js +349 -0
  74. package/dist/application/DoctorDiagnostics.js +169 -0
  75. package/dist/application/DoctorProviderIds.js +2 -0
  76. package/dist/application/DoctorScope.js +113 -0
  77. package/dist/application/DshCordisPatch.js +342 -0
  78. package/dist/application/DshCordisYaml.js +155 -0
  79. package/dist/application/EnhancedLiteralTracing.js +88 -0
  80. package/dist/application/EnhancedToolTypes.js +4 -0
  81. package/dist/application/EnhancedTools.js +428 -0
  82. package/dist/application/EvidenceBundleCommands.js +70 -0
  83. package/dist/application/EvidenceBundleFiles.js +79 -0
  84. package/dist/application/EvidenceInputResolver.js +30 -0
  85. package/dist/application/FunctionAnalysisEvidence.js +34 -0
  86. package/dist/application/GeminiClientSettings.js +139 -0
  87. package/dist/application/HermesClientHome.js +115 -0
  88. package/dist/application/HopperDownload.js +39 -0
  89. package/dist/application/InventoryProjectionEvidence.js +44 -0
  90. package/dist/application/InvestigationProviders.js +130 -0
  91. package/dist/application/JsonFiles.js +225 -0
  92. package/dist/application/JsonInputFile.js +410 -0
  93. package/dist/application/LinuxHopper.js +232 -0
  94. package/dist/application/MacHopper.js +235 -0
  95. package/dist/application/MipsElfMetadata.js +175 -0
  96. package/dist/application/MipsElfSharedLoad.js +164 -0
  97. package/dist/application/OptionalObservationProviders.js +3 -0
  98. package/dist/application/ProcedureAddressResolution.js +11 -0
  99. package/dist/application/ProgressReporter.js +30 -0
  100. package/dist/application/ProviderOperationExecution.js +31 -0
  101. package/dist/application/RealHopperAssertions.js +135 -0
  102. package/dist/application/ReconstructionCoverageService.js +22 -0
  103. package/dist/application/ReconstructionObligationAuthority.js +72 -0
  104. package/dist/application/ReconstructionObligationCandidates.js +286 -0
  105. package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
  106. package/dist/application/ReconstructionObligationLedgerEvaluation.js +238 -0
  107. package/dist/application/ReconstructionObligationLedgerProjection.js +15 -0
  108. package/dist/application/ReconstructionObligationLedgerService.js +54 -0
  109. package/dist/application/ReconstructionObligationOriginalCases.js +34 -0
  110. package/dist/application/ReconstructionObligationPolicies.js +132 -0
  111. package/dist/application/ReferenceSourceImport.js +200 -0
  112. package/dist/application/ReferenceSourceImportEntries.js +151 -0
  113. package/dist/application/ReferenceSourceImportPolicy.js +125 -0
  114. package/dist/application/ReferenceSourceImportTypes.js +47 -0
  115. package/dist/application/ReferenceSourceVcsAdapter.js +180 -0
  116. package/dist/application/RegularFileRead.js +120 -0
  117. package/dist/application/Setup.js +408 -0
  118. package/dist/application/SetupClientConfiguration.js +314 -0
  119. package/dist/application/SetupClients.js +44 -0
  120. package/dist/application/SetupHost.js +203 -0
  121. package/dist/application/SetupInstallFailure.js +77 -0
  122. package/dist/application/SetupPlan.js +210 -0
  123. package/dist/application/SetupRegistrationEnvironment.js +5 -0
  124. package/dist/application/SetupSkill.js +248 -0
  125. package/dist/application/SetupTypes.js +17 -0
  126. package/dist/application/SupportedClients.js +469 -0
  127. package/dist/application/TypeScriptEmittedSourceCandidates.js +23 -0
  128. package/dist/application/Uninstall.js +452 -0
  129. package/dist/application/UninstallFailure.js +14 -0
  130. package/dist/application/Update.js +137 -0
  131. package/dist/application/UpdateFailure.js +17 -0
  132. package/dist/application/UpdateMaintenance.js +111 -0
  133. package/dist/application/UpdateRuntime.js +264 -0
  134. package/dist/application/Utf8JsonInput.js +71 -0
  135. package/dist/application/ValidateSelectedWebScript.js +27 -0
  136. package/dist/application/WebModulePorts.js +1 -0
  137. package/dist/application/WebModuleTraceService.js +214 -0
  138. package/dist/application/WebNetworkCapturePort.js +1 -0
  139. package/dist/application/WebNetworkCaptureService.js +134 -0
  140. package/dist/application/WebRuntimePort.js +1 -0
  141. package/dist/application/WebRuntimeService.js +75 -0
  142. package/dist/application/WebScriptArtifactPort.js +1 -0
  143. package/dist/application/WebScriptExportService.js +214 -0
  144. package/dist/application/WebSourceLocationPorts.js +1 -0
  145. package/dist/application/WebSourceLocationService.js +177 -0
  146. package/dist/application/WindowsCapabilities.js +87 -0
  147. package/dist/application/WorkflowEvidence.js +105 -0
  148. package/dist/application/XrefsAddressResolution.js +45 -0
  149. package/dist/application/adb/AdbDeviceAnalysisPort.js +1 -0
  150. package/dist/application/adb/AdbDeviceAnalysisService.js +13 -0
  151. package/dist/application/analysisView/AnalysisViewService.js +133 -0
  152. package/dist/application/android/AndroidAnalysisPort.js +1 -0
  153. package/dist/application/android/AndroidAnalysisService.js +37 -0
  154. package/dist/application/android/AndroidApplicationService.js +17 -0
  155. package/dist/application/android/AndroidSearchEvidenceSummary.js +100 -0
  156. package/dist/application/apktool/ApktoolResourceAnalysisPort.js +1 -0
  157. package/dist/application/apktool/ApktoolResourceAnalysisService.js +13 -0
  158. package/dist/application/apple/AppleApplicationService.js +17 -0
  159. package/dist/application/artifacts/ArtifactExtractionDestination.js +5 -0
  160. package/dist/application/binary/AbortablePromise.js +28 -0
  161. package/dist/application/binary/AnalysisClientCleanup.js +15 -0
  162. package/dist/application/binary/AnalysisProviderEvaluation.js +77 -0
  163. package/dist/application/binary/AnalysisProviderRegistry.js +235 -0
  164. package/dist/application/binary/AnalysisSnapshotCache.js +232 -0
  165. package/dist/application/binary/AnalysisSnapshotFiles.js +96 -0
  166. package/dist/application/binary/BinarySession.js +605 -0
  167. package/dist/application/binary/BinarySessionComposition.js +9 -0
  168. package/dist/application/binary/BinarySessionExecution.js +66 -0
  169. package/dist/application/binary/BinarySessionOpen.js +66 -0
  170. package/dist/application/binary/BinarySessionPort.js +1 -0
  171. package/dist/application/binary/BinarySessionRecords.js +216 -0
  172. package/dist/application/binary/BinarySessionStatus.js +219 -0
  173. package/dist/application/binary/BinaryTargetSummary.js +9 -0
  174. package/dist/application/binary/CompositeProvider.js +103 -0
  175. package/dist/application/binary/LazyAnalysisProvider.js +105 -0
  176. package/dist/application/binary/SessionProviderRouter.js +118 -0
  177. package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
  178. package/dist/application/binaryDiagnostics/BinaryLayoutService.js +60 -0
  179. package/dist/application/binaryDiagnostics/PeResourcesPort.js +1 -0
  180. package/dist/application/binaryDiagnostics/PeResourcesService.js +111 -0
  181. package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
  182. package/dist/application/binaryDiagnostics/RecordedCrashService.js +63 -0
  183. package/dist/application/evm/EvmInterfacePort.js +1 -0
  184. package/dist/application/evm/EvmInterfaceService.js +67 -0
  185. package/dist/application/firmware/FirmwareAnalysisPort.js +1 -0
  186. package/dist/application/firmware/FirmwareAnalysisService.js +38 -0
  187. package/dist/application/go/GoBinaryPort.js +1 -0
  188. package/dist/application/go/GoBinaryService.js +77 -0
  189. package/dist/application/harmony/HarmonyApplicationService.js +17 -0
  190. package/dist/application/homebrew.js +67 -0
  191. package/dist/application/investigation/EvidenceLedger.js +338 -0
  192. package/dist/application/investigation/InspectEvidenceBundle.js +18 -0
  193. package/dist/application/investigation/InvestigationRecordPort.js +1 -0
  194. package/dist/application/investigation/InvestigationRecords.js +65 -0
  195. package/dist/application/investigation/UnknownEvidence.js +37 -0
  196. package/dist/application/javascript/ElectronActiveEvidence.js +29 -0
  197. package/dist/application/javascript/ElectronActiveObservationPort.js +1 -0
  198. package/dist/application/javascript/ElectronActiveObservationService.js +51 -0
  199. package/dist/application/javascript/ElectronBoundaryAnalysis.js +118 -0
  200. package/dist/application/javascript/ElectronBoundaryGraph.js +11 -0
  201. package/dist/application/javascript/ElectronBoundaryGraphCallPaths.js +134 -0
  202. package/dist/application/javascript/ElectronBoundaryGraphContext.js +63 -0
  203. package/dist/application/javascript/ElectronBoundaryGraphIpc.js +261 -0
  204. package/dist/application/javascript/ElectronBoundaryGraphNative.js +102 -0
  205. package/dist/application/javascript/ElectronBoundaryGraphWindows.js +224 -0
  206. package/dist/application/javascript/ElectronEvidence.js +37 -0
  207. package/dist/application/javascript/ElectronObservationPort.js +1 -0
  208. package/dist/application/javascript/ElectronObservationService.js +26 -0
  209. package/dist/application/javascript/JavaScriptAnalysisControl.js +34 -0
  210. package/dist/application/javascript/JavaScriptApplicationEvidence.js +49 -0
  211. package/dist/application/javascript/JavaScriptApplicationEvidenceGraph.js +173 -0
  212. package/dist/application/javascript/JavaScriptApplicationService.js +170 -0
  213. package/dist/application/javascript/JavaScriptApplicationWorkflowEvidence.js +67 -0
  214. package/dist/application/javascript/JavaScriptApplicationWorkflowService.js +194 -0
  215. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +431 -0
  216. package/dist/application/javascript/JavaScriptArtifactAnalysisTypes.js +1 -0
  217. package/dist/application/javascript/JavaScriptArtifactGraphAccumulator.js +89 -0
  218. package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +193 -0
  219. package/dist/application/javascript/JavaScriptArtifactGraphBundlers.js +229 -0
  220. package/dist/application/javascript/JavaScriptArtifactGraphContext.js +194 -0
  221. package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +160 -0
  222. package/dist/application/javascript/JavaScriptArtifactGraphEvidence.js +85 -0
  223. package/dist/application/javascript/JavaScriptArtifactGraphFindings.js +349 -0
  224. package/dist/application/javascript/JavaScriptArtifactGraphStructure.js +424 -0
  225. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +836 -0
  226. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +245 -0
  227. package/dist/application/javascript/JavaScriptArtifactReconstructionInput.js +12 -0
  228. package/dist/application/javascript/JavaScriptJsonModules.js +40 -0
  229. package/dist/application/javascript/JavaScriptModuleRelationships.js +304 -0
  230. package/dist/application/javascript/JavaScriptRecoveryPort.js +1 -0
  231. package/dist/application/javascript/JavaScriptRecoveryService.js +77 -0
  232. package/dist/application/javascript/JavaScriptRuntimeObservationEvidence.js +36 -0
  233. package/dist/application/javascript/JavaScriptRuntimeObservationPort.js +1 -0
  234. package/dist/application/javascript/JavaScriptRuntimeObservationService.js +28 -0
  235. package/dist/application/javascript/JavaScriptRuntimeReconciliationEvidence.js +24 -0
  236. package/dist/application/javascript/JavaScriptRuntimeReconciliationService.js +52 -0
  237. package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +163 -0
  238. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +527 -0
  239. package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +165 -0
  240. package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +156 -0
  241. package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +216 -0
  242. package/dist/application/javascript/JavaScriptSemanticGraphElectronProjection.js +61 -0
  243. package/dist/application/javascript/JavaScriptSemanticGraphEvidence.js +86 -0
  244. package/dist/application/javascript/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
  245. package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +183 -0
  246. package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +112 -0
  247. package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +144 -0
  248. package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +58 -0
  249. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +136 -0
  250. package/dist/application/javascript/JavaScriptSemanticTraceService.js +41 -0
  251. package/dist/application/jeb/JebAnalysisPort.js +1 -0
  252. package/dist/application/jeb/JebAnalysisService.js +13 -0
  253. package/dist/application/managed/ManagedApplicationGraphService.js +55 -0
  254. package/dist/application/managed/ManagedMemberComparisonService.js +125 -0
  255. package/dist/application/managed/ManagedNativeVerificationService.js +40 -0
  256. package/dist/application/managed/ManagedReconstructionService.js +59 -0
  257. package/dist/application/native/NativeApiInspection.js +77 -0
  258. package/dist/application/native/NativeCallRoutes.js +73 -0
  259. package/dist/application/native/NativeDispatchMetadataInspection.js +33 -0
  260. package/dist/application/native/NativeUiActionTrace.js +427 -0
  261. package/dist/application/native/NativeValueTrace.js +340 -0
  262. package/dist/application/process/ProcessCli.js +190 -0
  263. package/dist/application/process/ProcessEvidence.js +30 -0
  264. package/dist/application/wasm/WasmArtifactPort.js +1 -0
  265. package/dist/application/wasm/WasmArtifactService.js +72 -0
  266. package/dist/application/workflowInputError.js +18 -0
  267. package/dist/artifacts/ArtifactHash.js +29 -0
  268. package/dist/artifacts/ArtifactPaths.js +167 -0
  269. package/dist/artifacts/ArtifactProvider.js +280 -0
  270. package/dist/artifacts/ArtifactProviderMetadata.js +34 -0
  271. package/dist/artifacts/ArtifactReader.js +77 -0
  272. package/dist/artifacts/ArtifactResourceScope.js +103 -0
  273. package/dist/artifacts/AsarArtifactReader.js +518 -0
  274. package/dist/artifacts/AsarEntryStream.js +76 -0
  275. package/dist/artifacts/AsarHeader.js +24 -0
  276. package/dist/artifacts/DirectoryArtifactReader.js +292 -0
  277. package/dist/artifacts/MachOSliceArtifactReader.js +342 -0
  278. package/dist/artifacts/NativeDmgArtifactReader.js +508 -0
  279. package/dist/artifacts/NativeDmgOwnership.js +109 -0
  280. package/dist/artifacts/SafeOutputTree.js +394 -0
  281. package/dist/artifacts/SafeOutputTreeCleanup.js +45 -0
  282. package/dist/artifacts/SafeOutputTreeCreationFailure.js +20 -0
  283. package/dist/artifacts/SafeOutputTreeIdentity.js +42 -0
  284. package/dist/artifacts/SafeOutputTreeReadback.js +33 -0
  285. package/dist/artifacts/StreamBytes.js +10 -0
  286. package/dist/artifacts/ZipArtifactReader.js +222 -0
  287. package/dist/artifacts/apple/AppleAssetCatalogAnalysis.js +190 -0
  288. package/dist/artifacts/apple/DylibResolutionReader.js +269 -0
  289. package/dist/artifacts/apple/InterfaceBuilderAnalysis.js +500 -0
  290. package/dist/artifacts/apple/InterfaceBuilderDecodeBudget.js +233 -0
  291. package/dist/artifacts/apple/KeyedArchiveReader.js +163 -0
  292. package/dist/artifacts/apple/MachoLoadCommandReader.js +393 -0
  293. package/dist/artifacts/apple/NibArchive.js +338 -0
  294. package/dist/artifacts/apple/NibViewHierarchy.js +157 -0
  295. package/dist/artifacts/apple/PlistNumberLiterals.js +164 -0
  296. package/dist/artifacts/extraction/ArtifactExtraction.js +253 -0
  297. package/dist/artifacts/hostExecutableMarking.js +16 -0
  298. package/dist/artifacts/inventory/ArtifactGraphConstruction.js +343 -0
  299. package/dist/artifacts/inventory/ArtifactInventory.js +16 -0
  300. package/dist/artifacts/inventory/classify.js +94 -0
  301. package/dist/artifacts/inventory/hashStableRootArtifact.js +70 -0
  302. package/dist/artifacts/inventory/reader.js +40 -0
  303. package/dist/artifacts/inventory/scanCanonical.js +213 -0
  304. package/dist/artifacts/inventory/scanReader.js +169 -0
  305. package/dist/artifacts/inventory/types.js +3 -0
  306. package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +317 -0
  307. package/dist/artifacts/javascript/JavaScriptArtifactReader.js +6 -0
  308. package/dist/artifacts/javascript/JavaScriptSourceAnalysis.js +3 -0
  309. package/dist/artifacts/readStableArtifact.js +68 -0
  310. package/dist/browser/BrowserProcessStartup.js +89 -0
  311. package/dist/browser/BrowserScenarioPartialObservation.js +127 -0
  312. package/dist/browser/BrowserScenarioSecrets.js +137 -0
  313. package/dist/browser/BrowserScenarioSessionPort.js +1 -0
  314. package/dist/browser/CdpAuthorizedMainFrame.js +16 -0
  315. package/dist/browser/CdpAuthorizedTarget.js +22 -0
  316. package/dist/browser/CdpBrowserProvider.js +276 -0
  317. package/dist/browser/CdpCaptureCompleteness.js +92 -0
  318. package/dist/browser/CdpCaptureDocuments.js +375 -0
  319. package/dist/browser/CdpCaptureDomSvg.js +210 -0
  320. package/dist/browser/CdpCaptureEventBodyShapes.js +55 -0
  321. package/dist/browser/CdpCaptureEventHandlers.js +463 -0
  322. package/dist/browser/CdpCaptureEventHelpers.js +84 -0
  323. package/dist/browser/CdpCaptureEventState.js +1 -0
  324. package/dist/browser/CdpCaptureEventTypes.js +5 -0
  325. package/dist/browser/CdpCaptureEvents.js +199 -0
  326. package/dist/browser/CdpCaptureStorage.js +86 -0
  327. package/dist/browser/CdpCaptureStorageFingerprints.js +320 -0
  328. package/dist/browser/CdpCaptureValues.js +91 -0
  329. package/dist/browser/CdpCommandRejection.js +17 -0
  330. package/dist/browser/CdpConnection.js +303 -0
  331. package/dist/browser/CdpElectronInspection.js +253 -0
  332. package/dist/browser/CdpElectronProvider.js +115 -0
  333. package/dist/browser/CdpElectronScriptEvents.js +55 -0
  334. package/dist/browser/CdpElectronScripts.js +65 -0
  335. package/dist/browser/CdpElectronWorkers.js +57 -0
  336. package/dist/browser/CdpEndpoint.js +217 -0
  337. package/dist/browser/CdpObservationSession.js +332 -0
  338. package/dist/browser/CdpOptionalCommand.js +14 -0
  339. package/dist/browser/CdpPageCapture.js +278 -0
  340. package/dist/browser/CdpPageCaptureScripts.js +79 -0
  341. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  342. package/dist/browser/CdpSafeMetadata.js +284 -0
  343. package/dist/browser/CdpScreenshot.js +110 -0
  344. package/dist/browser/CdpTargetEvents.js +8 -0
  345. package/dist/browser/CdpTargetSession.js +65 -0
  346. package/dist/browser/CdpWebMcpDiscovery.js +361 -0
  347. package/dist/browser/ElectronFileScope.js +135 -0
  348. package/dist/browser/PlaywrightBrowserScenarioProvider.js +301 -0
  349. package/dist/browser/PlaywrightElectronActiveActions.js +257 -0
  350. package/dist/browser/PlaywrightElectronActiveProjection.js +141 -0
  351. package/dist/browser/PlaywrightElectronActiveProvider.js +293 -0
  352. package/dist/browser/PlaywrightExecutionBoundary.js +53 -0
  353. package/dist/browser/PlaywrightScenarioActions.js +58 -0
  354. package/dist/browser/PlaywrightScenarioArtifacts.js +162 -0
  355. package/dist/browser/PlaywrightScenarioBrowser.js +412 -0
  356. package/dist/browser/PlaywrightScenarioEvents.js +253 -0
  357. package/dist/browser/PlaywrightScenarioSession.js +279 -0
  358. package/dist/browser/PlaywrightScenarioStorage.js +507 -0
  359. package/dist/browser/PngVisualDiff.js +380 -0
  360. package/dist/browser/WebSourceMapFetcher.js +718 -0
  361. package/dist/browser/assets/LocalWebScriptArtifacts.js +89 -0
  362. package/dist/browser/assets/PageScriptCapture.js +32 -0
  363. package/dist/browser/assets/PublishWebScripts.js +140 -0
  364. package/dist/browser/assets/ScenarioScriptCapture.js +73 -0
  365. package/dist/browser/assets/ScriptCaptureAdapters.js +57 -0
  366. package/dist/browser/assets/WebArtifactReads.js +78 -0
  367. package/dist/browser/assets/WebSourceLocationArtifacts.js +41 -0
  368. package/dist/browser/execution/CdpEventListeners.js +79 -0
  369. package/dist/browser/execution/CdpExecutionCapture.js +135 -0
  370. package/dist/browser/execution/CdpExecutionCollection.js +57 -0
  371. package/dist/browser/execution/CdpExecutionWindow.js +100 -0
  372. package/dist/browser/execution/CdpListenerSelector.js +22 -0
  373. package/dist/browser/execution/CdpPreciseCoverage.js +45 -0
  374. package/dist/browser/execution/CdpRuntimeInitiator.js +18 -0
  375. package/dist/browser/execution/CdpRuntimeProtocol.js +106 -0
  376. package/dist/browser/execution/CdpRuntimeRequests.js +81 -0
  377. package/dist/browser/execution/CdpRuntimeSession.js +163 -0
  378. package/dist/browser/execution/CdpRuntimeSources.js +223 -0
  379. package/dist/browser/execution/CdpWebRuntimeProvider.js +51 -0
  380. package/dist/browser/history/CaptureFailures.js +153 -0
  381. package/dist/browser/history/CaptureFormatError.js +10 -0
  382. package/dist/browser/history/CaptureRedaction.js +68 -0
  383. package/dist/browser/history/CaptureRelease.js +23 -0
  384. package/dist/browser/history/HarCapture.js +179 -0
  385. package/dist/browser/history/HarCaptureAdapter.js +23 -0
  386. package/dist/browser/history/HarCaptureProcess.js +49 -0
  387. package/dist/browser/history/HarJson.js +92 -0
  388. package/dist/browser/history/HarSchema.js +72 -0
  389. package/dist/browser/history/HistoricalCaptureDecoder.js +198 -0
  390. package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
  391. package/dist/browser/history/MitmproxyCaptureAdapter.js +46 -0
  392. package/dist/browser/modules/NativeModuleResolver.js +277 -0
  393. package/dist/browser/modules/WebModuleArtifacts.js +41 -0
  394. package/dist/browser/network/PlaywrightNetworkContent.js +71 -0
  395. package/dist/browser/network/PlaywrightScenarioNetwork.js +180 -0
  396. package/dist/browser/providerIdentities.js +24 -0
  397. package/dist/catalogIdentity.js +65 -0
  398. package/dist/cli/adbCommands.js +337 -0
  399. package/dist/cli/analysisSelector.js +23 -0
  400. package/dist/cli/analysisViewCommands.js +74 -0
  401. package/dist/cli/androidCommands.js +91 -0
  402. package/dist/cli/apktoolCommands.js +53 -0
  403. package/dist/cli/applicationCommands.js +173 -0
  404. package/dist/cli/artifactCommands.js +160 -0
  405. package/dist/cli/binaryDiagnosticsCommands.js +57 -0
  406. package/dist/cli/browserAdvancedCommands.js +189 -0
  407. package/dist/cli/browserCommands.js +125 -0
  408. package/dist/cli/commandCancellation.js +24 -0
  409. package/dist/cli/coreAnalysisCommands.js +7 -0
  410. package/dist/cli/coreBinaryCommands.js +283 -0
  411. package/dist/cli/coreNativeCommands.js +269 -0
  412. package/dist/cli/electronCommands.js +150 -0
  413. package/dist/cli/evmCommands.js +33 -0
  414. package/dist/cli/firmwareCommands.js +61 -0
  415. package/dist/cli/goCommands.js +21 -0
  416. package/dist/cli/interactiveSetup.js +236 -0
  417. package/dist/cli/javascriptApplicationAnalysis.js +34 -0
  418. package/dist/cli/javascriptRecoveryCommands.js +59 -0
  419. package/dist/cli/jebCommands.js +99 -0
  420. package/dist/cli/jsonOutput.js +46 -0
  421. package/dist/cli/managedCommands.js +129 -0
  422. package/dist/cli/managedProjectGraphCommand.js +24 -0
  423. package/dist/cli/nativeCallCommands.js +33 -0
  424. package/dist/cli/omittableDefaults.js +50 -0
  425. package/dist/cli/options.js +33 -0
  426. package/dist/cli/processCommands.js +25 -0
  427. package/dist/cli/referenceSourceImportStatus.js +22 -0
  428. package/dist/cli/setupCommands.js +189 -0
  429. package/dist/cli/streamedJsonOutput.js +134 -0
  430. package/dist/cli/types.js +1 -0
  431. package/dist/cli/utilityCommands.js +226 -0
  432. package/dist/cli/wasmCommands.js +44 -0
  433. package/dist/cli/webModuleCommands.js +60 -0
  434. package/dist/cli/webNetworkCaptureCommands.js +40 -0
  435. package/dist/cli/webRuntimeCommands.js +60 -0
  436. package/dist/cli/webScriptCommands.js +50 -0
  437. package/dist/cli/webSourceLocationCommands.js +54 -0
  438. package/dist/cli-runtime/AnalysisProvider-CeyX2u-b.js +25 -0
  439. package/dist/cli-runtime/AnalysisSnapshotFiles-BuK_ymw5.js +523 -0
  440. package/dist/cli-runtime/ArtifactReader-BXkffc1D.js +55 -0
  441. package/dist/cli-runtime/ArtifactResourceScope-CZqLEfbb.js +108 -0
  442. package/dist/cli-runtime/AsarHeader-CnzOzYEH.js +31 -0
  443. package/dist/cli-runtime/BinaryTargetResolver-CKRfFhLj.js +965 -0
  444. package/dist/cli-runtime/BoundedFileBytes-BU30odTm.js +39 -0
  445. package/dist/cli-runtime/ClientConfigPath-c6jr6INc.js +36 -0
  446. package/dist/cli-runtime/ClientConfigurationDocument-D0ZqgTCv.js +1522 -0
  447. package/dist/cli-runtime/ClientRegistrationStatus-DGpwJ4ry.js +422 -0
  448. package/dist/cli-runtime/DirectAnalysis-DW3MvxW0.js +500 -0
  449. package/dist/cli-runtime/Doctor-CzwYS9nB.js +862 -0
  450. package/dist/cli-runtime/ElectronFileScope-C1hUEp36.js +1419 -0
  451. package/dist/cli-runtime/ExecFileOutput-Czbj-HpV.js +106 -0
  452. package/dist/cli-runtime/IdaConfiguration-rEjpuRQ1.js +854 -0
  453. package/dist/cli-runtime/InterfaceBuilderDecodeBudget-DT74BY9z.js +225 -0
  454. package/dist/cli-runtime/InventoryProjectionEvidence-Dt4Og45O.js +49 -0
  455. package/dist/cli-runtime/InvestigationProviders-D-QCy-4c.js +117 -0
  456. package/dist/cli-runtime/JavaScriptApplicationEvidenceGraph-CODAjVru.js +160 -0
  457. package/dist/cli-runtime/JavaScriptApplicationWorkflowEvidence-CCtwSg8r.js +75 -0
  458. package/dist/cli-runtime/JavaScriptWorkerMemoryLimit-CJeBSMPx.js +83 -0
  459. package/dist/cli-runtime/JsonFiles-Ce_O5NcH.js +708 -0
  460. package/dist/cli-runtime/ManagedSnapshot-DthWfmy9.js +3622 -0
  461. package/dist/cli-runtime/OwnedCommand-CnK8-h3N.js +106 -0
  462. package/dist/cli-runtime/PrivateRuntimeRoot-KNg6qLwo.js +237 -0
  463. package/dist/cli-runtime/ProcessOwnership-AOJaszxt.js +1310 -0
  464. package/dist/cli-runtime/ProviderDeadline-BSbNOmuW.js +89 -0
  465. package/dist/cli-runtime/RegularFile-CL6meBxi.js +114 -0
  466. package/dist/cli-runtime/RegularFileRead-BMh5FMXd.js +115 -0
  467. package/dist/cli-runtime/SafeOutputTree-D8z2Zrnu.js +512 -0
  468. package/dist/cli-runtime/SetupHost-CzzRF89z.js +842 -0
  469. package/dist/cli-runtime/SupportedClients-BrZ2qZVg.js +671 -0
  470. package/dist/cli-runtime/UninstallFailure-1blyKXHE.js +22 -0
  471. package/dist/cli-runtime/Update-DF55X_N2.js +143 -0
  472. package/dist/cli-runtime/UpdateFailure-BcFNxEZZ.js +24 -0
  473. package/dist/cli-runtime/WindowsBatchCommand-5gT-d7O4.js +45 -0
  474. package/dist/cli-runtime/ZipArtifactReader-Db65WuM_.js +380 -0
  475. package/dist/cli-runtime/analysisErrorCleanup-DaUQNiQd.js +32 -0
  476. package/dist/cli-runtime/analysisErrorPresentation-DZrKL8kS.js +331 -0
  477. package/dist/cli-runtime/application__DirectAnalysisStatus-OZFIFjZr.js +93 -0
  478. package/dist/cli-runtime/application__EnhancedTools-C__HAdqG.js +1728 -0
  479. package/dist/cli-runtime/application__FunctionAnalysisEvidence-fWSgvV74.js +41 -0
  480. package/dist/cli-runtime/application__ReconstructionCoverageService-DlNt-4TY.js +32 -0
  481. package/dist/cli-runtime/application__ReconstructionObligationLedgerService-CEpbUv-7.js +813 -0
  482. package/dist/cli-runtime/application__Setup-BExVGpqG.js +540 -0
  483. package/dist/cli-runtime/application__Uninstall-BVgWDCPN.js +349 -0
  484. package/dist/cli-runtime/application__UpdateRuntime-Dj6UIhHQ.js +358 -0
  485. package/dist/cli-runtime/application__android__AndroidApplicationService-CyynwW6Z.js +31 -0
  486. package/dist/cli-runtime/application__apple__AppleApplicationService-BNPcWeL8.js +31 -0
  487. package/dist/cli-runtime/application__harmony__HarmonyApplicationService-W66PwMvx.js +31 -0
  488. package/dist/cli-runtime/application__javascript__ElectronActiveObservationService-D8VbeCxz.js +85 -0
  489. package/dist/cli-runtime/application__javascript__ElectronObservationService-FmXFG4f2.js +63 -0
  490. package/dist/cli-runtime/application__javascript__JavaScriptApplicationService-D-dVrmAY.js +10467 -0
  491. package/dist/cli-runtime/application__javascript__JavaScriptApplicationWorkflowService-Cb0jmh8J.js +2284 -0
  492. package/dist/cli-runtime/application__javascript__JavaScriptRuntimeReconciliationService-D55gO7Gq.js +88 -0
  493. package/dist/cli-runtime/application__javascript__JavaScriptSemanticTraceService-DivM-Ym-.js +298 -0
  494. package/dist/cli-runtime/artifactOperationError-Wd0PRLIZ.js +27 -0
  495. package/dist/cli-runtime/artifacts__ArtifactProvider-B_4h3RAw.js +2091 -0
  496. package/dist/cli-runtime/binary-9Gt3vJ1P.js +12990 -0
  497. package/dist/cli-runtime/browserObservationError-D73SM55P.js +40 -0
  498. package/dist/cli-runtime/browserScenario-CTNr5ISp.js +5543 -0
  499. package/dist/cli-runtime/catalogIdentity-EYVhBUYJ.js +339 -0
  500. package/dist/cli-runtime/cli.js +28528 -0
  501. package/dist/cli-runtime/cliOutput-CoDmWVu5.js +98 -0
  502. package/dist/cli-runtime/cliOutput.js +5 -0
  503. package/dist/cli-runtime/composition__browserScenario-DNr3A_BM.js +2318 -0
  504. package/dist/cli-runtime/composition__electronObservation-DNw1f38M.js +550 -0
  505. package/dist/cli-runtime/composition__electronScenario-Cu7PX270.js +586 -0
  506. package/dist/cli-runtime/configurationErrors-ByuF6gMR.js +49 -0
  507. package/dist/cli-runtime/doctorRuntime-D5y43lEp.js +162 -0
  508. package/dist/cli-runtime/dosMz-pwwUuxyC.js +65 -0
  509. package/dist/cli-runtime/dotnet__ManagedStaticProvider-BSnWggeb.js +5 -0
  510. package/dist/cli-runtime/enhancedInputs-K8C_o3b4.js +56 -0
  511. package/dist/cli-runtime/javascriptApplicationGraph-DUWeec4O.js +940 -0
  512. package/dist/cli-runtime/javascriptExportReturnShapeProjection-BVbnAdOd.js +78 -0
  513. package/dist/cli-runtime/javascriptExportShapeComparisonSchemas-BzSil7ft.js +1153 -0
  514. package/dist/cli-runtime/javascriptRuntimeObservation-9xTNoKY2.js +1215 -0
  515. package/dist/cli-runtime/javascriptRuntimeReconciliation-CneBtMvx.js +1437 -0
  516. package/dist/cli-runtime/javascriptRuntimeReconciliationSchemas-D1Mrb_Y0.js +245 -0
  517. package/dist/cli-runtime/javascriptSemanticTraceSchemas-BHItrqIA.js +126 -0
  518. package/dist/cli-runtime/lipo-BCA3wVyN.js +365 -0
  519. package/dist/cli-runtime/logger-CKq3JfbZ.js +34 -0
  520. package/dist/cli-runtime/machoContainer-DxxkB2b0.js +52 -0
  521. package/dist/cli-runtime/managedApplicationGraph-BqOr01E6.js +1116 -0
  522. package/dist/cli-runtime/managedToolContracts-BSy4TRaK.js +80 -0
  523. package/dist/cli-runtime/mcpDoctor.js +342 -0
  524. package/dist/cli-runtime/mcpStartupPolicy-Bb7TMLzn.js +18 -0
  525. package/dist/cli-runtime/native__NativeMacOSProvider-BIG9iCcj.js +4415 -0
  526. package/dist/cli-runtime/plistValue-KM-SqiXe.js +47 -0
  527. package/dist/cli-runtime/propertyListKeys-DejfMb-U.js +86 -0
  528. package/dist/cli-runtime/providerAdapterError-SRSE_gzN.js +27 -0
  529. package/dist/cli-runtime/providerCleanupError-Do66qCEp.js +27 -0
  530. package/dist/cli-runtime/providerIdentities-Ao1XTvwx.js +31 -0
  531. package/dist/cli-runtime/providerOperationHealth-DSTcYPJe.js +577 -0
  532. package/dist/cli-runtime/providerStartupTimeout-Be8B1Ro8.js +16 -0
  533. package/dist/cli-runtime/readFileHandleChunks-dp60iZ6v.js +38 -0
  534. package/dist/cli-runtime/reconstructionObligationLedgerSchemas-dvp6PEnZ.js +15839 -0
  535. package/dist/cli-runtime/rolldown-runtime-DXoylpqc.js +121 -0
  536. package/dist/cli-runtime/runtimeVersion-CMGd0bDu.js +23 -0
  537. package/dist/cli-runtime/safeJson-DKKACtdy.js +25 -0
  538. package/dist/cli-runtime/scanCanonical-Bi6si9nb.js +2773 -0
  539. package/dist/cli-runtime/snapshotEnvironment-B5Ai0mha.js +678 -0
  540. package/dist/cli-runtime/streamedJsonOutput-VYlQZsM7.js +654 -0
  541. package/dist/cli-runtime/streamedJsonOutput.js +5 -0
  542. package/dist/cli-runtime/toolContracts-C7pvYKxF.js +4516 -0
  543. package/dist/cli-runtime/toolEffects-DW31yTSx.js +7004 -0
  544. package/dist/cli-runtime/workflowInputError-26EGiDpx.js +26 -0
  545. package/dist/cli.js +91 -0
  546. package/dist/cliBrowserContext.js +11 -0
  547. package/dist/cliBrowserScenarioCommands.js +73 -0
  548. package/dist/cliCommandNames.js +205 -0
  549. package/dist/cliEvidenceCommands.js +51 -0
  550. package/dist/cliJavaScriptRuntimeCommands.js +69 -0
  551. package/dist/cliJsonInput.js +129 -0
  552. package/dist/cliLogging.js +48 -0
  553. package/dist/cliObservationOptions.js +73 -0
  554. package/dist/cliOutput.js +97 -0
  555. package/dist/composition/adb.js +3 -0
  556. package/dist/composition/android.js +3 -0
  557. package/dist/composition/apktool.js +3 -0
  558. package/dist/composition/auxiliaryAnalysisProviders.js +35 -0
  559. package/dist/composition/binary.js +25 -0
  560. package/dist/composition/binaryDiagnostics.js +12 -0
  561. package/dist/composition/browserObservation.js +3 -0
  562. package/dist/composition/browserScenario.js +4 -0
  563. package/dist/composition/directAnalysis.js +44 -0
  564. package/dist/composition/electronObservation.js +3 -0
  565. package/dist/composition/electronScenario.js +3 -0
  566. package/dist/composition/evm.js +4 -0
  567. package/dist/composition/firmware.js +3 -0
  568. package/dist/composition/go.js +4 -0
  569. package/dist/composition/javascriptAnalysisWorkerProcess.js +48 -0
  570. package/dist/composition/javascriptRecovery.js +3 -0
  571. package/dist/composition/javascriptRuntimeObservation.js +3 -0
  572. package/dist/composition/jeb.js +6 -0
  573. package/dist/composition/optionalObservationProviders.js +54 -0
  574. package/dist/composition/wasm.js +4 -0
  575. package/dist/composition/webModules.js +5 -0
  576. package/dist/composition/webNetworkCaptures.js +6 -0
  577. package/dist/composition/webRuntime.js +4 -0
  578. package/dist/composition/webSourceLocations.js +5 -0
  579. package/dist/config/environment.js +73 -0
  580. package/dist/config/ghidraDisabledAnalyzers.js +25 -0
  581. package/dist/config/ghidraLanguageOverride.js +9 -0
  582. package/dist/config/homeDirectory.js +12 -0
  583. package/dist/config/mcpInputSchemaProfile.js +27 -0
  584. package/dist/config/mcpResponseBudget.js +30 -0
  585. package/dist/config/parseConfig.js +93 -0
  586. package/dist/config/parsers.js +30 -0
  587. package/dist/config/providerStartupTimeout.js +13 -0
  588. package/dist/config/types.js +1 -0
  589. package/dist/contracts/adb/adbToolContracts.js +353 -0
  590. package/dist/contracts/advertisedInputExamples.js +48 -0
  591. package/dist/contracts/analysisDetail.js +13 -0
  592. package/dist/contracts/analysisSearchContract.js +14 -0
  593. package/dist/contracts/analysisViewApiSurfaceExample.js +68 -0
  594. package/dist/contracts/analysisViewToolContracts.js +76 -0
  595. package/dist/contracts/android/androidToolContracts.js +77 -0
  596. package/dist/contracts/apktool/apktoolToolContracts.js +33 -0
  597. package/dist/contracts/applicationToolContracts.js +261 -0
  598. package/dist/contracts/artifactComparisonExample.js +86 -0
  599. package/dist/contracts/artifactToolContracts.js +68 -0
  600. package/dist/contracts/browserProviderToolContracts.js +7 -0
  601. package/dist/contracts/browserScenarioToolContracts.js +32 -0
  602. package/dist/contracts/browserToolContracts.js +256 -0
  603. package/dist/contracts/compactInputSchema.js +256 -0
  604. package/dist/contracts/enhancedInputs.js +74 -0
  605. package/dist/contracts/enhancedToolContracts.js +32 -0
  606. package/dist/contracts/errorSchemas.js +87 -0
  607. package/dist/contracts/evidenceInputContracts.js +13 -0
  608. package/dist/contracts/evm/evmToolContracts.js +20 -0
  609. package/dist/contracts/firmware/firmwareToolContracts.js +38 -0
  610. package/dist/contracts/functionComparisonExample.js +51 -0
  611. package/dist/contracts/functionInstructionContract.js +6 -0
  612. package/dist/contracts/functionWorkflowToolContracts.js +29 -0
  613. package/dist/contracts/go/goToolContracts.js +20 -0
  614. package/dist/contracts/hopperMemoryContracts.js +19 -0
  615. package/dist/contracts/inputSchemaPresentation.js +181 -0
  616. package/dist/contracts/investigationExamples.js +116 -0
  617. package/dist/contracts/javascript/applicationWorkflowInputContracts.js +52 -0
  618. package/dist/contracts/javascript/electronToolContracts.js +118 -0
  619. package/dist/contracts/javascript/javascriptApplicationWorkflowExamples.js +85 -0
  620. package/dist/contracts/javascript/javascriptExportShapeComparisonExample.js +248 -0
  621. package/dist/contracts/javascript/javascriptRecoveryToolContracts.js +35 -0
  622. package/dist/contracts/javascript/javascriptRuntimeObservationToolContracts.js +40 -0
  623. package/dist/contracts/javascript/javascriptRuntimeReconciliationExample.js +242 -0
  624. package/dist/contracts/jeb/jebToolContracts.js +64 -0
  625. package/dist/contracts/managed/managedToolContracts.js +87 -0
  626. package/dist/contracts/managed/managedWorkflowExamples.js +281 -0
  627. package/dist/contracts/managed/managedWorkflowToolContracts.js +165 -0
  628. package/dist/contracts/mobileApplicationGraphExamples.js +117 -0
  629. package/dist/contracts/native/binaryDiagnosticsToolContracts.js +65 -0
  630. package/dist/contracts/native/nativeToolContracts.js +77 -0
  631. package/dist/contracts/officialToolContracts.js +69 -0
  632. package/dist/contracts/promptContracts.js +253 -0
  633. package/dist/contracts/providerSelection.js +29 -0
  634. package/dist/contracts/reconstructionReadinessExample.js +438 -0
  635. package/dist/contracts/sessionLifecycleInputs.js +63 -0
  636. package/dist/contracts/sessionStatusContract.js +17 -0
  637. package/dist/contracts/sessionToolContracts.js +61 -0
  638. package/dist/contracts/sessionToolSchemas.js +51 -0
  639. package/dist/contracts/toolContractExamples.js +77 -0
  640. package/dist/contracts/toolContractHelpers.js +22 -0
  641. package/dist/contracts/toolContractTypes.js +18 -0
  642. package/dist/contracts/toolContracts.js +60 -0
  643. package/dist/contracts/toolEffects.js +544 -0
  644. package/dist/contracts/toolOutputSchemaGroups.js +411 -0
  645. package/dist/contracts/toolOutputSchemaPrimitives.js +532 -0
  646. package/dist/contracts/toolSchemaMetadata.js +197 -0
  647. package/dist/contracts/unknownContractExamples.js +31 -0
  648. package/dist/contracts/wasm/wasmToolContracts.js +24 -0
  649. package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
  650. package/dist/contracts/webRuntimeToolContracts.js +43 -0
  651. package/dist/contracts/webScriptToolContracts.js +68 -0
  652. package/dist/doctorRuntime.js +53 -0
  653. package/dist/domain/adb/adbDeviceAnalysis.js +583 -0
  654. package/dist/domain/analysisErrorBase.js +58 -0
  655. package/dist/domain/analysisErrorCleanup.js +29 -0
  656. package/dist/domain/analysisErrorCore.js +123 -0
  657. package/dist/domain/analysisErrorPresentation.js +372 -0
  658. package/dist/domain/analysisErrorProjection.js +454 -0
  659. package/dist/domain/analysisProfile.js +50 -0
  660. package/dist/domain/analysisSnapshot.js +538 -0
  661. package/dist/domain/analysisView/WebNetworkApiSurfaceAccumulator.js +362 -0
  662. package/dist/domain/analysisView/analysisView.js +340 -0
  663. package/dist/domain/analysisView/analysisViewCoverage.js +19 -0
  664. package/dist/domain/analysisView/binaryLayoutView.js +161 -0
  665. package/dist/domain/analysisView/javascriptApplicationView.js +245 -0
  666. package/dist/domain/analysisView/nativeFunctionView.js +155 -0
  667. package/dist/domain/analysisView/webNetworkApiSurfaceView.js +108 -0
  668. package/dist/domain/android/androidAnalysis.js +143 -0
  669. package/dist/domain/android/androidApplication.js +144 -0
  670. package/dist/domain/android/androidPartialObservation.js +36 -0
  671. package/dist/domain/apktool/apktoolResourceAnalysis.js +75 -0
  672. package/dist/domain/apple/appleApplication.js +315 -0
  673. package/dist/domain/apple/appleAssetCatalog.js +127 -0
  674. package/dist/domain/apple/appleBundleAnatomy.js +303 -0
  675. package/dist/domain/apple/dyldEnvironment.js +73 -0
  676. package/dist/domain/apple/dyldPaths.js +89 -0
  677. package/dist/domain/apple/dylibResolution.js +485 -0
  678. package/dist/domain/apple/dylibResolutionFindings.js +77 -0
  679. package/dist/domain/apple/interfaceBuilderGraph.js +403 -0
  680. package/dist/domain/apple/interfaceBuilderKeyedArchive.js +430 -0
  681. package/dist/domain/apple/keyedArchive.js +214 -0
  682. package/dist/domain/apple/machoContainer.js +54 -0
  683. package/dist/domain/apple/plistNumbers.js +76 -0
  684. package/dist/domain/apple/plistValue.js +35 -0
  685. package/dist/domain/artifactComparison.js +348 -0
  686. package/dist/domain/artifactGraph.js +213 -0
  687. package/dist/domain/artifactIdentity.js +57 -0
  688. package/dist/domain/artifactInspection.js +279 -0
  689. package/dist/domain/artifactIntegrityPolicy.js +10 -0
  690. package/dist/domain/artifactInventoryEvidence.js +166 -0
  691. package/dist/domain/artifactInventorySnapshot.js +1 -0
  692. package/dist/domain/artifactOperationError.js +20 -0
  693. package/dist/domain/artifactPartialObservation.js +1 -0
  694. package/dist/domain/artifactPathSyntax.js +75 -0
  695. package/dist/domain/binaryTarget.js +233 -0
  696. package/dist/domain/binaryTargetTypes.js +10 -0
  697. package/dist/domain/bridgeCandidateProjection.js +71 -0
  698. package/dist/domain/browserCaptureComparison.js +21 -0
  699. package/dist/domain/browserCompleteness.js +103 -0
  700. package/dist/domain/browserNetworkEvidence.js +92 -0
  701. package/dist/domain/browserObservation.js +187 -0
  702. package/dist/domain/browserObservationError.js +44 -0
  703. package/dist/domain/browserObservationErrors.js +1 -0
  704. package/dist/domain/browserObservationSchemas.js +327 -0
  705. package/dist/domain/browserScenario.js +61 -0
  706. package/dist/domain/browserScenarioCapture.js +76 -0
  707. package/dist/domain/browserScenarioCaptureValues.js +292 -0
  708. package/dist/domain/browserScenarioDiff.js +266 -0
  709. package/dist/domain/browserScenarioDiffEvidence.js +73 -0
  710. package/dist/domain/browserScenarioDiffValues.js +140 -0
  711. package/dist/domain/browserScenarioNormalization.js +40 -0
  712. package/dist/domain/browserScenarioValues.js +268 -0
  713. package/dist/domain/browserSession.js +55 -0
  714. package/dist/domain/bundleComparison.js +243 -0
  715. package/dist/domain/callPath.js +301 -0
  716. package/dist/domain/callPathSchemas.js +89 -0
  717. package/dist/domain/canonicalDigest.js +83 -0
  718. package/dist/domain/canonicalJsonDigestSteps.js +155 -0
  719. package/dist/domain/canonicalOrdering.js +3 -0
  720. package/dist/domain/changedBehavior.js +282 -0
  721. package/dist/domain/compactStringIdentity.js +9 -0
  722. package/dist/domain/comparisonSemantics.js +12 -0
  723. package/dist/domain/comparisonSourceEvidence.js +40 -0
  724. package/dist/domain/comparisonStatus.js +11 -0
  725. package/dist/domain/completionLedgerGeneration.js +186 -0
  726. package/dist/domain/configurationErrors.js +37 -0
  727. package/dist/domain/conformancePackage.js +200 -0
  728. package/dist/domain/conformanceReplay.js +132 -0
  729. package/dist/domain/conformanceTrustGate.js +319 -0
  730. package/dist/domain/digests.js +27 -0
  731. package/dist/domain/dosCom.js +8 -0
  732. package/dist/domain/dosMz.js +83 -0
  733. package/dist/domain/emptyArraySchema.js +3 -0
  734. package/dist/domain/evidence.js +305 -0
  735. package/dist/domain/evidenceBundle.js +413 -0
  736. package/dist/domain/evidenceCompletionLedger.js +113 -0
  737. package/dist/domain/evidenceErrors.js +46 -0
  738. package/dist/domain/evidenceRecordSummary.js +144 -0
  739. package/dist/domain/evm/evmInterface.js +61 -0
  740. package/dist/domain/explicitSensitiveFailure.js +108 -0
  741. package/dist/domain/explicitSensitiveValues.js +10 -0
  742. package/dist/domain/firmware/firmwareAnalysis.js +128 -0
  743. package/dist/domain/functionComparison.js +38 -0
  744. package/dist/domain/functionComparisonDimensions.js +199 -0
  745. package/dist/domain/functionComparisonNormalization.js +150 -0
  746. package/dist/domain/functionComparisonResults.js +54 -0
  747. package/dist/domain/functionComparisonSchemas.js +88 -0
  748. package/dist/domain/functionDossierEvidence.js +20 -0
  749. package/dist/domain/go/goBinary.js +96 -0
  750. package/dist/domain/harmony/harmonyApplication.js +169 -0
  751. package/dist/domain/hopperErrors.js +98 -0
  752. package/dist/domain/hopperStartupFailure.js +64 -0
  753. package/dist/domain/hopperValues.js +368 -0
  754. package/dist/domain/immutableJson.js +114 -0
  755. package/dist/domain/inputIssueProjection.js +94 -0
  756. package/dist/domain/javascript/electronActiveObservation.js +237 -0
  757. package/dist/domain/javascript/electronObservation.js +122 -0
  758. package/dist/domain/javascript/electronStaticAnalysis.js +9 -0
  759. package/dist/domain/javascript/electronStaticAnalysisBrowser.js +187 -0
  760. package/dist/domain/javascript/electronStaticAnalysisIpc.js +197 -0
  761. package/dist/domain/javascript/electronStaticAnalysisNative.js +199 -0
  762. package/dist/domain/javascript/electronStaticAnalysisTypes.js +1 -0
  763. package/dist/domain/javascript/electronStaticAnalysisValues.js +134 -0
  764. package/dist/domain/javascript/electronWrappedCallPaths.js +399 -0
  765. package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
  766. package/dist/domain/javascript/javascriptAnalysisResourceControls.js +65 -0
  767. package/dist/domain/javascript/javascriptApplicationAnalysis.js +146 -0
  768. package/dist/domain/javascript/javascriptApplicationChangeGraph.js +175 -0
  769. package/dist/domain/javascript/javascriptApplicationEvidenceSchemas.js +400 -0
  770. package/dist/domain/javascript/javascriptApplicationGraph.js +378 -0
  771. package/dist/domain/javascript/javascriptApplicationGraphSchemas.js +126 -0
  772. package/dist/domain/javascript/javascriptApplicationProjectionResources.js +43 -0
  773. package/dist/domain/javascript/javascriptApplicationVersionComparison.js +104 -0
  774. package/dist/domain/javascript/javascriptApplicationVersionComparisonSchemas.js +211 -0
  775. package/dist/domain/javascript/javascriptApplicationVersionItems.js +324 -0
  776. package/dist/domain/javascript/javascriptApplicationVersionKeys.js +199 -0
  777. package/dist/domain/javascript/javascriptArtifactFiles.js +1 -0
  778. package/dist/domain/javascript/javascriptAstFingerprint.js +153 -0
  779. package/dist/domain/javascript/javascriptAstValues.js +99 -0
  780. package/dist/domain/javascript/javascriptBootstrap.js +94 -0
  781. package/dist/domain/javascript/javascriptElectronMemberWrites.js +119 -0
  782. package/dist/domain/javascript/javascriptExportReturnShapeProjection.js +81 -0
  783. package/dist/domain/javascript/javascriptExportShapeComparison.js +161 -0
  784. package/dist/domain/javascript/javascriptExportShapeComparisonIdentity.js +16 -0
  785. package/dist/domain/javascript/javascriptExportShapeComparisonSchemas.js +302 -0
  786. package/dist/domain/javascript/javascriptExportShapeSelection.js +130 -0
  787. package/dist/domain/javascript/javascriptExportShapeVariants.js +333 -0
  788. package/dist/domain/javascript/javascriptFeatureSeed.js +193 -0
  789. package/dist/domain/javascript/javascriptFeatureTrace.js +177 -0
  790. package/dist/domain/javascript/javascriptFeatureTraceSchemas.js +131 -0
  791. package/dist/domain/javascript/javascriptFeatureTraversal.js +61 -0
  792. package/dist/domain/javascript/javascriptIntrinsicInvocation.js +93 -0
  793. package/dist/domain/javascript/javascriptModuleInterop.js +617 -0
  794. package/dist/domain/javascript/javascriptModuleSemanticIr.js +15 -0
  795. package/dist/domain/javascript/javascriptNativeHandoff.js +90 -0
  796. package/dist/domain/javascript/javascriptPackageImportAliases.js +58 -0
  797. package/dist/domain/javascript/javascriptRecovery.js +90 -0
  798. package/dist/domain/javascript/javascriptRpcEndpoints.js +253 -0
  799. package/dist/domain/javascript/javascriptRuntimeLoadState.js +98 -0
  800. package/dist/domain/javascript/javascriptRuntimeObservation.js +136 -0
  801. package/dist/domain/javascript/javascriptRuntimeReconciliation.js +46 -0
  802. package/dist/domain/javascript/javascriptRuntimeReconciliationActive.js +74 -0
  803. package/dist/domain/javascript/javascriptRuntimeReconciliationCaptureParsing.js +212 -0
  804. package/dist/domain/javascript/javascriptRuntimeReconciliationGraph.js +163 -0
  805. package/dist/domain/javascript/javascriptRuntimeReconciliationInputValidation.js +28 -0
  806. package/dist/domain/javascript/javascriptRuntimeReconciliationMatching.js +270 -0
  807. package/dist/domain/javascript/javascriptRuntimeReconciliationParsing.js +39 -0
  808. package/dist/domain/javascript/javascriptRuntimeReconciliationResult.js +126 -0
  809. package/dist/domain/javascript/javascriptRuntimeReconciliationRuntime.js +334 -0
  810. package/dist/domain/javascript/javascriptRuntimeReconciliationSchemas.js +234 -0
  811. package/dist/domain/javascript/javascriptRuntimeStaticCandidates.js +206 -0
  812. package/dist/domain/javascript/javascriptSemanticAnalysis.js +910 -0
  813. package/dist/domain/javascript/javascriptSemanticAsyncEffects.js +246 -0
  814. package/dist/domain/javascript/javascriptSemanticCallResolution.js +127 -0
  815. package/dist/domain/javascript/javascriptSemanticCallableResults.js +42 -0
  816. package/dist/domain/javascript/javascriptSemanticCalls.js +272 -0
  817. package/dist/domain/javascript/javascriptSemanticChildProcesses.js +224 -0
  818. package/dist/domain/javascript/javascriptSemanticCoverage.js +32 -0
  819. package/dist/domain/javascript/javascriptSemanticDataEffectHelpers.js +182 -0
  820. package/dist/domain/javascript/javascriptSemanticDataEffects.js +299 -0
  821. package/dist/domain/javascript/javascriptSemanticDerivedAnalysis.js +52 -0
  822. package/dist/domain/javascript/javascriptSemanticFileProjection.js +1 -0
  823. package/dist/domain/javascript/javascriptSemanticFingerprints.js +187 -0
  824. package/dist/domain/javascript/javascriptSemanticGraph.js +575 -0
  825. package/dist/domain/javascript/javascriptSemanticGraphSchemas.js +316 -0
  826. package/dist/domain/javascript/javascriptSemanticGraphSerialization.js +12 -0
  827. package/dist/domain/javascript/javascriptSemanticIr.js +31 -0
  828. package/dist/domain/javascript/javascriptSemanticIterationSources.js +139 -0
  829. package/dist/domain/javascript/javascriptSemanticMemberMutations.js +865 -0
  830. package/dist/domain/javascript/javascriptSemanticMutationInitializers.js +190 -0
  831. package/dist/domain/javascript/javascriptSemanticMutationValues.js +104 -0
  832. package/dist/domain/javascript/javascriptSemanticObjectReferences.js +381 -0
  833. package/dist/domain/javascript/javascriptSemanticObjects.js +164 -0
  834. package/dist/domain/javascript/javascriptSemanticPrimitives.js +38 -0
  835. package/dist/domain/javascript/javascriptSemanticProjection.js +571 -0
  836. package/dist/domain/javascript/javascriptSemanticPromises.js +321 -0
  837. package/dist/domain/javascript/javascriptSemanticPropertyPaths.js +100 -0
  838. package/dist/domain/javascript/javascriptSemanticProvenance.js +29 -0
  839. package/dist/domain/javascript/javascriptSemanticQuery.js +175 -0
  840. package/dist/domain/javascript/javascriptSemanticQueryAssessment.js +45 -0
  841. package/dist/domain/javascript/javascriptSemanticQueryIdentity.js +63 -0
  842. package/dist/domain/javascript/javascriptSemanticQueryRelations.js +15 -0
  843. package/dist/domain/javascript/javascriptSemanticQuerySchemas.js +95 -0
  844. package/dist/domain/javascript/javascriptSemanticRequireOrigin.js +34 -0
  845. package/dist/domain/javascript/javascriptSemanticResourceLimits.js +77 -0
  846. package/dist/domain/javascript/javascriptSemanticResources.js +129 -0
  847. package/dist/domain/javascript/javascriptSemanticReturnedReferences.js +219 -0
  848. package/dist/domain/javascript/javascriptSemanticReturns.js +159 -0
  849. package/dist/domain/javascript/javascriptSemanticSlots.js +72 -0
  850. package/dist/domain/javascript/javascriptSemanticState.js +116 -0
  851. package/dist/domain/javascript/javascriptSemanticTraceSchemas.js +15 -0
  852. package/dist/domain/javascript/javascriptSemanticTraversal.js +80 -0
  853. package/dist/domain/javascript/javascriptSemanticValueTypes.js +1 -0
  854. package/dist/domain/javascript/javascriptSemanticValues.js +581 -0
  855. package/dist/domain/javascript/javascriptSourceAnalysis.js +1 -0
  856. package/dist/domain/javascript/javascriptSourceMapPaths.js +114 -0
  857. package/dist/domain/javascript/javascriptSourceParser.js +50 -0
  858. package/dist/domain/javascript/javascriptSourceSelection.js +11 -0
  859. package/dist/domain/javascript/javascriptStaticAnalysis.js +142 -0
  860. package/dist/domain/javascript/javascriptStaticAnalysisBundler.js +239 -0
  861. package/dist/domain/javascript/javascriptStaticAnalysisCalls.js +271 -0
  862. package/dist/domain/javascript/javascriptStaticAnalysisFindings.js +86 -0
  863. package/dist/domain/javascript/javascriptStaticAnalysisHelpers.js +561 -0
  864. package/dist/domain/javascript/javascriptStaticAnalysisState.js +21 -0
  865. package/dist/domain/javascript/javascriptStaticAnalysisTypes.js +2 -0
  866. package/dist/domain/javascript/javascriptSymbolicBindings.js +795 -0
  867. package/dist/domain/javascript/javascriptSymbolicControlFlow.js +40 -0
  868. package/dist/domain/javascript/javascriptSymbolicValues.js +34 -0
  869. package/dist/domain/javascript/sourceToBundleComparison.js +261 -0
  870. package/dist/domain/javascript/sourceToBundleComparisonSchemas.js +109 -0
  871. package/dist/domain/javascript/sourceToBundleSignals.js +231 -0
  872. package/dist/domain/jeb/jebAnalysis.js +114 -0
  873. package/dist/domain/jsonSerialization.js +163 -0
  874. package/dist/domain/jsonShape.js +141 -0
  875. package/dist/domain/jsonValue.js +96 -0
  876. package/dist/domain/localPath.js +24 -0
  877. package/dist/domain/managed/managedApplicationGraph.js +221 -0
  878. package/dist/domain/managed/managedApplicationGraphCoverage.js +48 -0
  879. package/dist/domain/managed/managedApplicationGraphNodes.js +367 -0
  880. package/dist/domain/managed/managedArtifact.js +465 -0
  881. package/dist/domain/managed/managedInspectionEvidence.js +28 -0
  882. package/dist/domain/managed/managedMemberComparison.js +211 -0
  883. package/dist/domain/managed/managedMemberComparisonCoverage.js +71 -0
  884. package/dist/domain/managed/managedMemberComparisonItems.js +304 -0
  885. package/dist/domain/managed/managedMemberComparisonMatch.js +204 -0
  886. package/dist/domain/managed/managedNativeVerification.js +35 -0
  887. package/dist/domain/managed/managedNativeVerificationMatch.js +276 -0
  888. package/dist/domain/managed/managedNativeVerificationSchemas.js +180 -0
  889. package/dist/domain/managed/managedReconstruction.js +190 -0
  890. package/dist/domain/mobileApplicationGraphProjection.js +61 -0
  891. package/dist/domain/native/binaryLayout.js +354 -0
  892. package/dist/domain/native/fileOffsetPartialObservation.js +1 -0
  893. package/dist/domain/native/nativeAotPe.js +1408 -0
  894. package/dist/domain/native/nativeApiBoundary.js +140 -0
  895. package/dist/domain/native/nativeCallObservation.js +186 -0
  896. package/dist/domain/native/nativeCallPartialObservation.js +33 -0
  897. package/dist/domain/native/nativeDataType.js +53 -0
  898. package/dist/domain/native/nativeFunctionAnnotations.js +43 -0
  899. package/dist/domain/native/nativeInspection.js +174 -0
  900. package/dist/domain/native/nativeInstruction.js +125 -0
  901. package/dist/domain/native/nativeInvestigationGraph.js +364 -0
  902. package/dist/domain/native/nativeLoadImage.js +112 -0
  903. package/dist/domain/native/nativeMetadataRecovery.js +128 -0
  904. package/dist/domain/native/nativeUiObservation.js +137 -0
  905. package/dist/domain/native/nativeValueFlow.js +72 -0
  906. package/dist/domain/native/nativeValueTrace.js +64 -0
  907. package/dist/domain/native/objcSwiftMetadata.js +419 -0
  908. package/dist/domain/native/peResources.js +100 -0
  909. package/dist/domain/native/recordedCrash.js +263 -0
  910. package/dist/domain/native/recordedCrashNoteCoverage.js +73 -0
  911. package/dist/domain/nativeConvention.js +33 -0
  912. package/dist/domain/offsetDateTime.js +17 -0
  913. package/dist/domain/peMachine.js +36 -0
  914. package/dist/domain/process/processCapture.js +412 -0
  915. package/dist/domain/process/processCaptureCoverage.js +70 -0
  916. package/dist/domain/process/processCaptureExample.js +132 -0
  917. package/dist/domain/process/processCaptureParsing.js +21 -0
  918. package/dist/domain/process/processCaptureValidation.js +246 -0
  919. package/dist/domain/process/processComparison.js +315 -0
  920. package/dist/domain/process/processEvidenceProvider.js +11 -0
  921. package/dist/domain/process/processObservation.js +140 -0
  922. package/dist/domain/process/processScenario.js +247 -0
  923. package/dist/domain/process/processTraceComparison.js +34 -0
  924. package/dist/domain/process/processTraceDimensionProjection.js +35 -0
  925. package/dist/domain/process/processTraceEvaluation.js +320 -0
  926. package/dist/domain/process/processTraceSpecification.js +382 -0
  927. package/dist/domain/propertyListKeys.js +107 -0
  928. package/dist/domain/propertyListXmlText.js +19 -0
  929. package/dist/domain/providerAdapterError.js +19 -0
  930. package/dist/domain/providerCleanupError.js +18 -0
  931. package/dist/domain/providerConfigurationRefusal.js +12 -0
  932. package/dist/domain/providerOperationHealth.js +54 -0
  933. package/dist/domain/providerSelectionError.js +46 -0
  934. package/dist/domain/rawBinary.js +93 -0
  935. package/dist/domain/reconstructionCoverage.js +325 -0
  936. package/dist/domain/reconstructionCoverageEvaluation.js +234 -0
  937. package/dist/domain/reconstructionCoverageInput.js +7 -0
  938. package/dist/domain/reconstructionObligationLedgerIntegrity.js +114 -0
  939. package/dist/domain/reconstructionObligationLedgerSchemas.js +347 -0
  940. package/dist/domain/reconstructionReadiness.js +116 -0
  941. package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
  942. package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
  943. package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
  944. package/dist/domain/reconstructionReadinessSchemas.js +268 -0
  945. package/dist/domain/reconstructionUnknowns.js +91 -0
  946. package/dist/domain/reconstructionVerification.js +218 -0
  947. package/dist/domain/reconstructionVerificationSchemas.js +141 -0
  948. package/dist/domain/recordedTransportUrl.js +51 -0
  949. package/dist/domain/referenceSourceClassification.js +385 -0
  950. package/dist/domain/referenceSourceGraph.js +380 -0
  951. package/dist/domain/referenceSourceImportParsing.js +238 -0
  952. package/dist/domain/referenceSourcePathIdentity.js +22 -0
  953. package/dist/domain/referenceSourcePolicy.js +1 -0
  954. package/dist/domain/referenceSourceRequireBindings.js +137 -0
  955. package/dist/domain/relationshipCoverage.js +20 -0
  956. package/dist/domain/residualUnknown.js +252 -0
  957. package/dist/domain/result.js +4 -0
  958. package/dist/domain/runtimeVersion.js +15 -0
  959. package/dist/domain/safeJson.js +13 -0
  960. package/dist/domain/sourceMapContents.js +6 -0
  961. package/dist/domain/sourceMapEnvelope.js +44 -0
  962. package/dist/domain/staticRuntimeCorrelation.js +334 -0
  963. package/dist/domain/stringPatterns.js +6 -0
  964. package/dist/domain/swiftSymbolClassification.js +66 -0
  965. package/dist/domain/symbolAnalysis.js +122 -0
  966. package/dist/domain/unicodeCodePointOrder.js +23 -0
  967. package/dist/domain/unknownRegistryError.js +10 -0
  968. package/dist/domain/wasm/wasmArtifact.js +105 -0
  969. package/dist/domain/webBundleAnalysis.js +192 -0
  970. package/dist/domain/webBundleAnalyzer.js +15 -0
  971. package/dist/domain/webBundleAnalyzerAst.js +49 -0
  972. package/dist/domain/webBundleAnalyzerInspection.js +315 -0
  973. package/dist/domain/webBundleAnalyzerResult.js +142 -0
  974. package/dist/domain/webCaptureDiff.js +423 -0
  975. package/dist/domain/webCaptureDiffSchemas.js +84 -0
  976. package/dist/domain/webContentArtifact.js +38 -0
  977. package/dist/domain/webEventListeners.js +29 -0
  978. package/dist/domain/webExecution.js +75 -0
  979. package/dist/domain/webInventory.js +62 -0
  980. package/dist/domain/webMcpDiscovery.js +53 -0
  981. package/dist/domain/webModuleImports.js +99 -0
  982. package/dist/domain/webModuleTrace.js +101 -0
  983. package/dist/domain/webNetworkCapture.js +142 -0
  984. package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
  985. package/dist/domain/webRuntime.js +80 -0
  986. package/dist/domain/webScreenshot.js +132 -0
  987. package/dist/domain/webScriptArtifacts.js +37 -0
  988. package/dist/domain/webScriptExport.js +84 -0
  989. package/dist/domain/webScriptExportError.js +19 -0
  990. package/dist/domain/webScriptExportPlan.js +104 -0
  991. package/dist/domain/webSourceLocation.js +118 -0
  992. package/dist/domain/webSourcePosition.js +21 -0
  993. package/dist/domain/zipPackageFormat.js +52 -0
  994. package/dist/domain/zodCollectionSteps.js +24 -0
  995. package/dist/dotnet/ManagedArtifactInspector.js +308 -0
  996. package/dist/dotnet/ManagedDecodeBudget.js +138 -0
  997. package/dist/dotnet/ManagedExceptionRegionValidation.js +41 -0
  998. package/dist/dotnet/ManagedMemberInspector.js +173 -0
  999. package/dist/dotnet/ManagedMemberInspectorCore.js +446 -0
  1000. package/dist/dotnet/ManagedMemberInstructionDecoder.js +468 -0
  1001. package/dist/dotnet/ManagedMemberOwnership.js +71 -0
  1002. package/dist/dotnet/ManagedMemberPointers.js +62 -0
  1003. package/dist/dotnet/ManagedMemberRows.js +227 -0
  1004. package/dist/dotnet/ManagedMetadataHeaps.js +179 -0
  1005. package/dist/dotnet/ManagedMetadataInventory.js +99 -0
  1006. package/dist/dotnet/ManagedMetadataInventoryRows.js +295 -0
  1007. package/dist/dotnet/ManagedMetadataLayout.js +270 -0
  1008. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  1009. package/dist/dotnet/ManagedMethodBodyReader.js +155 -0
  1010. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +314 -0
  1011. package/dist/dotnet/ManagedNativeBoundaryInspector.js +128 -0
  1012. package/dist/dotnet/ManagedPeReader.js +233 -0
  1013. package/dist/dotnet/ManagedReaderFailure.js +27 -0
  1014. package/dist/dotnet/ManagedSnapshot.js +88 -0
  1015. package/dist/dotnet/ManagedStaticProvider.js +158 -0
  1016. package/dist/dotnet/ManagedStaticProviderMetadata.js +33 -0
  1017. package/dist/evaluation/AgentEvaluationFixtures.js +250 -0
  1018. package/dist/evaluation/AgentEvaluationReport.js +32 -0
  1019. package/dist/evaluation/CodexAgentEval.js +188 -0
  1020. package/dist/evaluation/KnownAnswerEvaluation.js +323 -0
  1021. package/dist/evm/EvmBytecodeCarrier.js +21 -0
  1022. package/dist/evm/EvmWorkerLimits.js +40 -0
  1023. package/dist/evm/EvmoleFailures.js +124 -0
  1024. package/dist/evm/EvmoleInterfaceAdapter.js +53 -0
  1025. package/dist/evm/EvmoleInterfaceProvider.js +339 -0
  1026. package/dist/evm/EvmoleInterfaceWorker.js +87 -0
  1027. package/dist/evm/EvmoleRelease.js +16 -0
  1028. package/dist/filesystem/OwnedDirectoryHandle.js +29 -0
  1029. package/dist/filesystem/OwnedFileHandle.js +36 -0
  1030. package/dist/filesystem/OwnedStagedOutput.js +138 -0
  1031. package/dist/filesystem/RegularFile.js +73 -0
  1032. package/dist/filesystem/StableFileHash.js +46 -0
  1033. package/dist/filesystem/readFileHandleChunks.js +35 -0
  1034. package/dist/firmware/FirmwareCommand.js +190 -0
  1035. package/dist/firmware/FirmwareFiles.js +206 -0
  1036. package/dist/firmware/FirmwareProvider.js +388 -0
  1037. package/dist/firmware/FirmwarePublication.js +105 -0
  1038. package/dist/firmware/FirmwareRelease.js +33 -0
  1039. package/dist/firmware/FirmwareReports.js +287 -0
  1040. package/dist/firmware/FirmwareVersion.js +80 -0
  1041. package/dist/generatedPackageMetadata.js +9 -0
  1042. package/dist/ghidra/GhidraAnalysisProfile.js +219 -0
  1043. package/dist/ghidra/GhidraAnalysisSeeds.js +211 -0
  1044. package/dist/ghidra/GhidraClient.js +725 -0
  1045. package/dist/ghidra/GhidraClientStartup.js +27 -0
  1046. package/dist/ghidra/GhidraClientTypes.js +1 -0
  1047. package/dist/ghidra/GhidraClientWire.js +38 -0
  1048. package/dist/ghidra/GhidraDiagnostics.js +53 -0
  1049. package/dist/ghidra/GhidraDoctor.js +88 -0
  1050. package/dist/ghidra/GhidraFunctionValues.js +328 -0
  1051. package/dist/ghidra/GhidraInstallation.js +443 -0
  1052. package/dist/ghidra/GhidraInstallationPolicy.js +140 -0
  1053. package/dist/ghidra/GhidraInventoryValues.js +262 -0
  1054. package/dist/ghidra/GhidraJavaLaunch.js +66 -0
  1055. package/dist/ghidra/GhidraLanguageCatalog.js +99 -0
  1056. package/dist/ghidra/GhidraLauncher.js +490 -0
  1057. package/dist/ghidra/GhidraLoadImageAttest.js +103 -0
  1058. package/dist/ghidra/GhidraLoadImageValues.js +260 -0
  1059. package/dist/ghidra/GhidraMipsProfile.js +187 -0
  1060. package/dist/ghidra/GhidraProvider.js +250 -0
  1061. package/dist/ghidra/GhidraProviderCapabilities.js +208 -0
  1062. package/dist/ghidra/GhidraProviderClient.js +613 -0
  1063. package/dist/ghidra/GhidraPspExtension.js +144 -0
  1064. package/dist/ghidra/GhidraPspProfile.js +50 -0
  1065. package/dist/ghidra/GhidraRawBinaryLanguage.js +18 -0
  1066. package/dist/ghidra/GhidraRequestQueue.js +115 -0
  1067. package/dist/ghidra/GhidraResponseBuffer.js +31 -0
  1068. package/dist/ghidra/GhidraResponseRouter.js +24 -0
  1069. package/dist/ghidra/GhidraSessionError.js +180 -0
  1070. package/dist/ghidra/GhidraSessionRoot.js +39 -0
  1071. package/dist/ghidra/GhidraSessionValues.js +86 -0
  1072. package/dist/ghidra/GhidraSocketConnection.js +83 -0
  1073. package/dist/ghidra/GhidraTargetSnapshot.js +79 -0
  1074. package/dist/ghidra/GhidraTransport.js +101 -0
  1075. package/dist/ghidra/extensions/GhidraExtensionFailures.js +20 -0
  1076. package/dist/ghidra/extensions/GhidraExtensions.js +177 -0
  1077. package/dist/ghidra/extensions/nativeaot/NativeAotAdapter.js +27 -0
  1078. package/dist/ghidra/extensions/nativeaot/NativeAotReport.js +100 -0
  1079. package/dist/ghidra/protocol.js +46 -0
  1080. package/dist/go/GoBinaryContainer.js +125 -0
  1081. package/dist/go/GoBinaryImage.js +172 -0
  1082. package/dist/go/GoBinaryProvider.js +137 -0
  1083. package/dist/go/GoElfImage.js +149 -0
  1084. package/dist/go/GoMachoImage.js +113 -0
  1085. package/dist/go/GoModuleText.js +217 -0
  1086. package/dist/go/GoPeImage.js +102 -0
  1087. package/dist/hopper/BridgeLauncher.js +305 -0
  1088. package/dist/hopper/HopperAnalysisProfile.js +168 -0
  1089. package/dist/hopper/HopperCleanup.js +245 -0
  1090. package/dist/hopper/HopperClient.js +767 -0
  1091. package/dist/hopper/HopperDiagnostics.js +14 -0
  1092. package/dist/hopper/HopperFileOffset.js +237 -0
  1093. package/dist/hopper/HopperMachOImage.js +200 -0
  1094. package/dist/hopper/HopperOperationStage.js +6 -0
  1095. package/dist/hopper/HopperProcessDiagnostic.js +26 -0
  1096. package/dist/hopper/HopperProvider.js +269 -0
  1097. package/dist/hopper/HopperProviderCapabilities.js +91 -0
  1098. package/dist/hopper/HopperRegexSearch.js +134 -0
  1099. package/dist/hopper/HopperRegexWorker.js +27 -0
  1100. package/dist/hopper/HopperRequestQueue.js +261 -0
  1101. package/dist/hopper/HopperResponseStream.js +44 -0
  1102. package/dist/hopper/HopperSessionValues.js +30 -0
  1103. package/dist/hopper/HopperSocketConnection.js +31 -0
  1104. package/dist/hopper/HopperTargetLease.js +233 -0
  1105. package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +64 -0
  1106. package/dist/hopper/LinuxPrivateDisplayProbe.js +224 -0
  1107. package/dist/hopper/protocol.js +88 -0
  1108. package/dist/ida/IdaBusyHealth.js +44 -0
  1109. package/dist/ida/IdaCallGraph.js +134 -0
  1110. package/dist/ida/IdaConfiguration.js +61 -0
  1111. package/dist/ida/IdaDoctor.js +53 -0
  1112. package/dist/ida/IdaInput.js +56 -0
  1113. package/dist/ida/IdaMcpConnection.js +115 -0
  1114. package/dist/ida/IdaOperationRunner.js +376 -0
  1115. package/dist/ida/IdaProtocolValues.js +208 -0
  1116. package/dist/ida/IdaProvider.js +136 -0
  1117. package/dist/ida/IdaProviderCapabilities.js +64 -0
  1118. package/dist/ida/IdaSessionClient.js +444 -0
  1119. package/dist/ida/IdaWorkspace.js +55 -0
  1120. package/dist/identity.js +25 -0
  1121. package/dist/inspector/JavaScriptRuntimeScope.js +89 -0
  1122. package/dist/inspector/V8InspectorCaptureProjection.js +197 -0
  1123. package/dist/inspector/V8InspectorEndpoint.js +70 -0
  1124. package/dist/inspector/V8InspectorProvider.js +378 -0
  1125. package/dist/inspector/providerIdentity.js +6 -0
  1126. package/dist/javascript/analysis/JavaScriptAnalysisTransfer.js +337 -0
  1127. package/dist/javascript/analysis/JavaScriptAnalysisWorker.js +574 -0
  1128. package/dist/javascript/analysis/JavaScriptAnalysisWorkerProtocol.js +64 -0
  1129. package/dist/javascript/analysis/JavaScriptAnalysisWorkerSchemas.js +558 -0
  1130. package/dist/javascript/analysis/JavaScriptAnalysisWorkerServer.js +121 -0
  1131. package/dist/javascript/recovery/RecoveryFiles.js +175 -0
  1132. package/dist/javascript/recovery/WakaruCommand.js +137 -0
  1133. package/dist/javascript/recovery/WakaruExecution.js +115 -0
  1134. package/dist/javascript/recovery/WakaruProvider.js +218 -0
  1135. package/dist/javascript/recovery/WakaruPublication.js +126 -0
  1136. package/dist/javascript/recovery/WakaruRelease.js +36 -0
  1137. package/dist/javascript/recovery/WakaruReport.js +140 -0
  1138. package/dist/javascript/recovery/WakaruVersion.js +24 -0
  1139. package/dist/javascript/sourceMaps/DecodedSourceMap.js +47 -0
  1140. package/dist/javascript/sourceMaps/SourceMapCodecProcess.js +37 -0
  1141. package/dist/javascript/sourceMaps/SourceMapCodecProtocol.js +20 -0
  1142. package/dist/javascript/sourceMaps/SourceMapCommand.js +223 -0
  1143. package/dist/javascript/sourceMaps/SourceMapDecoder.js +135 -0
  1144. package/dist/javascript/sourceMaps/SourceMapFormat.js +238 -0
  1145. package/dist/javascript/sourceMaps/TraceSourceMap.js +152 -0
  1146. package/dist/jeb/JebMcpConnection.js +186 -0
  1147. package/dist/jeb/JebMcpEndpoint.js +40 -0
  1148. package/dist/jeb/JebProvider.js +297 -0
  1149. package/dist/logger.js +14 -0
  1150. package/dist/main/messages.js +4 -0
  1151. package/dist/main/shutdown.js +102 -0
  1152. package/dist/main/startup.js +21 -0
  1153. package/dist/main/transport.js +72 -0
  1154. package/dist/main/types.js +1 -0
  1155. package/dist/main.js +92 -0
  1156. package/dist/mcp/AnalysisSnapshotFiles-DbgHi36O.js +1225 -0
  1157. package/dist/mcp/ArtifactProvider-BNxKlaKX.js +2090 -0
  1158. package/dist/mcp/ArtifactProviderMetadata--hy-wyJK.js +76 -0
  1159. package/dist/mcp/ArtifactReader-OyM03Gjn.js +55 -0
  1160. package/dist/mcp/ArtifactResourceScope-CBR5q4aK.js +108 -0
  1161. package/dist/mcp/AsarHeader-WsPgoooH.js +31 -0
  1162. package/dist/mcp/BinaryTargetResolver-DbiUzRLh.js +1073 -0
  1163. package/dist/mcp/BoundedFileBytes-CJPW_TzE.js +68 -0
  1164. package/dist/mcp/CdpConnection-CM0tkVfW.js +557 -0
  1165. package/dist/mcp/CdpOptionalCommand-H8_fL7Q_.js +120 -0
  1166. package/dist/mcp/CdpTargetSession-OYD1d6XM.js +647 -0
  1167. package/dist/mcp/DirectAnalysis-BWAu8Ro5.js +395 -0
  1168. package/dist/mcp/DirectAnalysisStatus-DofNPhKd.js +33 -0
  1169. package/dist/mcp/ElectronFileScope-DEayib6K.js +110 -0
  1170. package/dist/mcp/InterfaceBuilderDecodeBudget-CUrOtfNo.js +401 -0
  1171. package/dist/mcp/ManagedStaticProvider-CXE93Bow.js +4460 -0
  1172. package/dist/mcp/NativeMacOSProvider-BbITFLHW.js +4408 -0
  1173. package/dist/mcp/NativeMacOSProviderMetadata-mxDhaNJf.js +281 -0
  1174. package/dist/mcp/ProcedureAddressResolution-Gnjf9Hya.js +19 -0
  1175. package/dist/mcp/ProcessOwnership-nOFZsmD3.js +1411 -0
  1176. package/dist/mcp/WorkflowEvidence-DJyEy3K0.js +94 -0
  1177. package/dist/mcp/analysisErrorCore-BqyeMw3_.js +201 -0
  1178. package/dist/mcp/analysisErrorProjection-B7pHef2y.js +724 -0
  1179. package/dist/mcp/artifactOperationError-B_jxO8at.js +27 -0
  1180. package/dist/mcp/artifactToolContracts-TXegBBr1.js +159 -0
  1181. package/dist/mcp/browserObservation-BCgaDRyQ.js +4398 -0
  1182. package/dist/mcp/browserObservationError-BKBEX85K.js +40 -0
  1183. package/dist/mcp/browserObservationSchemas-yzRJ90-E.js +573 -0
  1184. package/dist/mcp/browserScenario-z7jveI9P.js +2319 -0
  1185. package/dist/mcp/browserScenarioCapture-BvnKTmtZ.js +460 -0
  1186. package/dist/mcp/canonicalDigest-Bi_0_zGi.js +71 -0
  1187. package/dist/mcp/digests-Dugt3H71.js +199 -0
  1188. package/dist/mcp/electronActiveObservation-lDvQTMR0.js +226 -0
  1189. package/dist/mcp/electronObservation-DjQQYGw4.js +556 -0
  1190. package/dist/mcp/electronObservation-DuhMHCmQ.js +112 -0
  1191. package/dist/mcp/electronScenario-9mLG6h-g.js +586 -0
  1192. package/dist/mcp/emptyArraySchema-DEEefG2V.js +140 -0
  1193. package/dist/mcp/enhancedInputs-CLdEXv88.js +56 -0
  1194. package/dist/mcp/interfaceBuilderGraph-BOKqQzjK.js +2613 -0
  1195. package/dist/mcp/javascriptRuntimeObservation-C_wglsz_.js +661 -0
  1196. package/dist/mcp/javascriptRuntimeObservation-CxQ1UT_y.js +121 -0
  1197. package/dist/mcp/lipo-Do-2eBrl.js +465 -0
  1198. package/dist/mcp/localPath-AxvW9aPe.js +19 -0
  1199. package/dist/mcp/logger-CZ4Hlc6u.js +34 -0
  1200. package/dist/mcp/machoContainer-9En6pSkd.js +813 -0
  1201. package/dist/mcp/main-WQSCuTNt.js +52791 -0
  1202. package/dist/mcp/main.js +6 -0
  1203. package/dist/mcp/managedToolContracts-o5LHFwHc.js +81 -0
  1204. package/dist/mcp/plistValue-DsU7z4yF.js +47 -0
  1205. package/dist/mcp/propertyListKeys-cX-BUv2y.js +197 -0
  1206. package/dist/mcp/providerAdapterError-CdPaQ6-Z.js +27 -0
  1207. package/dist/mcp/providerCleanupError-DvE79aUX.js +49 -0
  1208. package/dist/mcp/providerIdentities--ptAh481.js +31 -0
  1209. package/dist/mcp/providerIdentity-BCQ5n5tw.js +16 -0
  1210. package/dist/mcp/providerOperationHealth-CUeCJZiP.js +212 -0
  1211. package/dist/mcp/rolldown-runtime-BjeaiXFo.js +121 -0
  1212. package/dist/mcp/safeJson-NeGfpkrI.js +25 -0
  1213. package/dist/mcp/scanCanonical-2GN0h-BE.js +3647 -0
  1214. package/dist/mcp/serverIdentity-rOY69sxc.js +633 -0
  1215. package/dist/mcp/sourceMapEnvelope-D8Zezqyi.js +252 -0
  1216. package/dist/mcp/toolContracts-CrfYDTUz.js +14561 -0
  1217. package/dist/mcp/toolOutputSchemaGroups-ChxVLGoy.js +11114 -0
  1218. package/dist/mcp/unicodeCodePointOrder-BezCbTpg.js +26 -0
  1219. package/dist/mcp/webContentArtifact-DUBgMzVO.js +185 -0
  1220. package/dist/mcp/webRuntime-C2Tx-fgS.js +74 -0
  1221. package/dist/mcp/webSourceLocation-Dnkpf0R2.js +8532 -0
  1222. package/dist/mcpDoctor.js +360 -0
  1223. package/dist/mcpStartupPolicy.js +14 -0
  1224. package/dist/native/AppleDispatchCoverage.js +112 -0
  1225. package/dist/native/AppleDispatchDecodeFacts.js +6 -0
  1226. package/dist/native/AppleDispatchMetadata.js +176 -0
  1227. package/dist/native/AppleMachoFixups.js +372 -0
  1228. package/dist/native/AppleMachoSelection.js +234 -0
  1229. package/dist/native/AppleObjcCategories.js +63 -0
  1230. package/dist/native/AppleObjcDispatchFacets.js +260 -0
  1231. package/dist/native/AppleObjcProperties.js +79 -0
  1232. package/dist/native/AppleObjcProtocols.js +89 -0
  1233. package/dist/native/AppleSwiftDispatchFacets.js +156 -0
  1234. package/dist/native/AppleSwiftVtables.js +92 -0
  1235. package/dist/native/CommandRunner.js +240 -0
  1236. package/dist/native/LldbCallTracer.js +713 -0
  1237. package/dist/native/LldbRetainedObservations.js +178 -0
  1238. package/dist/native/NativeCallObservation.js +195 -0
  1239. package/dist/native/NativeMacOSProvider.js +701 -0
  1240. package/dist/native/NativeMacOSProviderMetadata.js +46 -0
  1241. package/dist/native/NativeMachoInspection.js +273 -0
  1242. package/dist/native/NativeUiHelperRuntime.js +66 -0
  1243. package/dist/native/NativeUiObservation.js +183 -0
  1244. package/dist/native/NativeUiOutputBudget.js +8 -0
  1245. package/dist/native/SignatureTargetBinding.js +155 -0
  1246. package/dist/native/parsers/codesign.js +93 -0
  1247. package/dist/native/parsers/demangle.js +30 -0
  1248. package/dist/native/parsers/dyldInfo.js +60 -0
  1249. package/dist/native/parsers/echoedPath.js +14 -0
  1250. package/dist/native/parsers/lipo.js +177 -0
  1251. package/dist/native/parsers/machoUuid.js +150 -0
  1252. package/dist/native/parsers/otool.js +379 -0
  1253. package/dist/native/parsers/plist.js +187 -0
  1254. package/dist/native/pe/PeResourceLayout.js +120 -0
  1255. package/dist/native/pe/PeResourceParser.js +286 -0
  1256. package/dist/native/pe/PeResourcesProvider.js +63 -0
  1257. package/dist/native/pwndbg/PwndbgCoreContext.js +180 -0
  1258. package/dist/native/pwntools/PwntoolsDecoder.js +206 -0
  1259. package/dist/native/pwntools/PwntoolsFailures.js +180 -0
  1260. package/dist/native/pwntools/PwntoolsLayoutProvider.js +36 -0
  1261. package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +75 -0
  1262. package/dist/native/pwntools/PwntoolsRelease.js +19 -0
  1263. package/dist/native/pwntools/PwntoolsResourceLimits.js +76 -0
  1264. package/dist/native/pwntools/RecordedCrashSourceBindings.js +46 -0
  1265. package/dist/native/pwntools/RecordedCrashStructureBindings.js +103 -0
  1266. package/dist/process/BoundedFileBytes.js +40 -0
  1267. package/dist/process/DarwinProcessRunTokenReader.js +376 -0
  1268. package/dist/process/ExecFileOutput.js +124 -0
  1269. package/dist/process/JavaScriptWorkerMemoryLimit.js +25 -0
  1270. package/dist/process/OwnedCommand.js +131 -0
  1271. package/dist/process/PendingOperations.js +61 -0
  1272. package/dist/process/PrivateRuntimeRoot.js +61 -0
  1273. package/dist/process/ProcessCommandIdentity.js +19 -0
  1274. package/dist/process/ProcessOwnership.js +636 -0
  1275. package/dist/process/ProcessOwnershipIdentity.js +13 -0
  1276. package/dist/process/ProcessOwnershipObservation.js +407 -0
  1277. package/dist/process/ProcessOwnershipProcessTree.js +35 -0
  1278. package/dist/process/ProcessRunTokenObservations.js +48 -0
  1279. package/dist/process/ProviderDeadline.js +91 -0
  1280. package/dist/process/ProviderDiagnosticRedaction.js +17 -0
  1281. package/dist/process/ProviderProcess.js +381 -0
  1282. package/dist/process/ProviderRunLineage.js +21 -0
  1283. package/dist/process/WindowsAuthority.js +51 -0
  1284. package/dist/process/WindowsBatchCommand.js +46 -0
  1285. package/dist/process/boundedUtf8Text.js +13 -0
  1286. package/dist/process/capture/FilesystemSnapshot.js +268 -0
  1287. package/dist/process/capture/ProcessCaptureCapability.js +65 -0
  1288. package/dist/process/capture/ProcessCaptureEnvironment.js +9 -0
  1289. package/dist/process/capture/ProcessCaptureError.js +74 -0
  1290. package/dist/process/capture/ProcessCaptureJournal.js +61 -0
  1291. package/dist/process/capture/ProcessCaptureLifecycle.js +1050 -0
  1292. package/dist/process/capture/ProcessCaptureProgress.js +62 -0
  1293. package/dist/process/capture/ProcessFilesystemEffects.js +81 -0
  1294. package/dist/process/capture/ProcessHarness.js +602 -0
  1295. package/dist/process/capture/ProcessNormalization.js +88 -0
  1296. package/dist/process/capture/ProcessSampling.js +415 -0
  1297. package/dist/process/capture/ProcessScenarioRuntimeValidation.js +79 -0
  1298. package/dist/process/capture/ProcessTimer.js +39 -0
  1299. package/dist/process/capture/TerminalRenderer.js +124 -0
  1300. package/dist/process/snapshotEnvironment.js +14 -0
  1301. package/dist/reference/ReferenceSourceReader.js +73 -0
  1302. package/dist/reference/ReferenceSourceReaderEntries.js +273 -0
  1303. package/dist/reference/ReferenceSourceReaderErrors.js +54 -0
  1304. package/dist/reference/ReferenceSourceReaderFile.js +147 -0
  1305. package/dist/reference/ReferenceSourceReaderPaths.js +8 -0
  1306. package/dist/reference/ReferenceSourceReaderTypes.js +1 -0
  1307. package/dist/reference/ReferenceSourceReaderValidate.js +81 -0
  1308. package/dist/server/EvidenceMcpServer.js +216 -0
  1309. package/dist/server/admittedToolOperation.js +9 -0
  1310. package/dist/server/analysisAdmission.js +8 -0
  1311. package/dist/server/contractToolHandler.js +21 -0
  1312. package/dist/server/createServer.js +344 -0
  1313. package/dist/server/lifecycleProgress.js +10 -0
  1314. package/dist/server/mcpClientMetadata.js +42 -0
  1315. package/dist/server/mcpProgress.js +26 -0
  1316. package/dist/server/promptCompletion.js +141 -0
  1317. package/dist/server/recordDerivedEvidence.js +10 -0
  1318. package/dist/server/registerAdbTools.js +36 -0
  1319. package/dist/server/registerAnalysisViewTool.js +10 -0
  1320. package/dist/server/registerAndroidTools.js +31 -0
  1321. package/dist/server/registerApktoolTools.js +14 -0
  1322. package/dist/server/registerApplicationTools/compareExportShapes.js +29 -0
  1323. package/dist/server/registerApplicationTools/compareSourceToBundle.js +29 -0
  1324. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  1325. package/dist/server/registerApplicationTools/coverage.js +13 -0
  1326. package/dist/server/registerApplicationTools/helpers.js +37 -0
  1327. package/dist/server/registerApplicationTools/obligationLedger.js +23 -0
  1328. package/dist/server/registerApplicationTools/projectMobileApplicationGraph.js +50 -0
  1329. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  1330. package/dist/server/registerApplicationTools/traceSemantics.js +25 -0
  1331. package/dist/server/registerApplicationTools/types.js +1 -0
  1332. package/dist/server/registerApplicationTools.js +19 -0
  1333. package/dist/server/registerArtifactComparisonTool.js +71 -0
  1334. package/dist/server/registerArtifactTools.js +14 -0
  1335. package/dist/server/registerBinaryDiagnosticsTools.js +26 -0
  1336. package/dist/server/registerBrowserScenarioTool.js +26 -0
  1337. package/dist/server/registerBrowserTools.js +58 -0
  1338. package/dist/server/registerBundleComparisonTool.js +54 -0
  1339. package/dist/server/registerCloseLifecycleTool.js +55 -0
  1340. package/dist/server/registerElectronTools.js +70 -0
  1341. package/dist/server/registerEnhancedTools.js +169 -0
  1342. package/dist/server/registerEvidenceTools.js +68 -0
  1343. package/dist/server/registerEvmTools.js +9 -0
  1344. package/dist/server/registerFirmwareTools.js +11 -0
  1345. package/dist/server/registerFunctionComparisonTool.js +86 -0
  1346. package/dist/server/registerGoTools.js +9 -0
  1347. package/dist/server/registerInvestigationTools/helpers.js +46 -0
  1348. package/dist/server/registerInvestigationTools/types.js +1 -0
  1349. package/dist/server/registerInvestigationTools.js +168 -0
  1350. package/dist/server/registerJavaScriptRecoveryTool.js +9 -0
  1351. package/dist/server/registerJavaScriptRuntimeObservationTools.js +27 -0
  1352. package/dist/server/registerJebTools.js +16 -0
  1353. package/dist/server/registerManagedTools.js +40 -0
  1354. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +53 -0
  1355. package/dist/server/registerManagedWorkflowTools/contract.js +7 -0
  1356. package/dist/server/registerManagedWorkflowTools/evidence.js +27 -0
  1357. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +27 -0
  1358. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +41 -0
  1359. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  1360. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +62 -0
  1361. package/dist/server/registerManagedWorkflowTools.js +11 -0
  1362. package/dist/server/registerOfficialTools.js +74 -0
  1363. package/dist/server/registerPeResourcesTool.js +9 -0
  1364. package/dist/server/registerProcessComparisonTool.js +156 -0
  1365. package/dist/server/registerPrompts.js +73 -0
  1366. package/dist/server/registerRecordedCrashTools.js +9 -0
  1367. package/dist/server/registerSessionRecordTools.js +90 -0
  1368. package/dist/server/registerSessionStatusTool.js +61 -0
  1369. package/dist/server/registerSessionTools.js +217 -0
  1370. package/dist/server/registerWasmTools.js +9 -0
  1371. package/dist/server/registerWebModuleTool.js +9 -0
  1372. package/dist/server/registerWebNetworkCaptureTool.js +20 -0
  1373. package/dist/server/registerWebRuntimeTools.js +27 -0
  1374. package/dist/server/registerWebScriptTool.js +18 -0
  1375. package/dist/server/registerWebSourceLocationTool.js +9 -0
  1376. package/dist/server/runDerivedOperation.js +55 -0
  1377. package/dist/server/sessionAvailabilityPolicy.js +29 -0
  1378. package/dist/server/sessionEvidence.js +11 -0
  1379. package/dist/server/toolLogging.js +11 -0
  1380. package/dist/server/toolRegistrationOptions.js +9 -0
  1381. package/dist/server/toolResult.js +119 -0
  1382. package/dist/server/toolResultEncoding.js +34 -0
  1383. package/dist/serverIdentity.js +69 -0
  1384. package/dist/wasm/WabtArtifactProvider.js +313 -0
  1385. package/dist/wasm/WabtFailures.js +62 -0
  1386. package/dist/wasm/WabtOutput.js +60 -0
  1387. package/dist/wasm/WabtRelease.js +12 -0
  1388. package/dist/wasm/WabtWat.js +184 -0
  1389. package/dist/wasm/WasmGlueAdapter.js +51 -0
  1390. package/dist/windows/WindowsNativeLoader.js +124 -0
  1391. package/dist/windows/WindowsOwnedProcess.js +265 -0
  1392. package/dist/windows/WindowsPrivateRuntime.js +143 -0
  1393. package/hooks/hooks.json +17 -0
  1394. package/install.sh +172 -0
  1395. package/licenses/Apache-2.0.txt +202 -0
  1396. package/licenses/GCC-Runtime-Library-Exception-3.1.txt +73 -0
  1397. package/licenses/GPL-3.0.txt +674 -0
  1398. package/licenses/MIT-template.txt +18 -0
  1399. package/licenses/MinGW-w64-runtime.txt +240 -0
  1400. package/licenses/Node-22.19.0-LICENSE.txt +2666 -0
  1401. package/licenses/README.md +54 -0
  1402. package/licenses/cfworker-MIT.txt +21 -0
  1403. package/licenses/digfox-MIT.txt +21 -0
  1404. package/licenses/dotnet-runtime-MIT.txt +23 -0
  1405. package/licenses/ghidra-12.1.4-DISCLAIMER.txt +13 -0
  1406. package/licenses/ghidra-12.1.4-LICENSE.txt +201 -0
  1407. package/licenses/ghidra-12.1.4-NOTICE.txt +49 -0
  1408. package/licenses/nativeaot-parser-MIT.txt +28 -0
  1409. package/licenses/npm-dependencies.txt +2812 -0
  1410. package/licenses/npm-runtime-notices.txt +6632 -0
  1411. package/licenses/rea-jev-MIT.txt +21 -0
  1412. package/licenses/rea-lab-MIT.txt +21 -0
  1413. package/licenses/upstream-sources.json +62 -0
  1414. package/licenses/xterm-MIT.txt +21 -0
  1415. package/native/windows/README.md +156 -0
  1416. package/native/windows/build/manifest.json +9 -0
  1417. package/native/windows/build/rea-windows-x64.node +0 -0
  1418. package/package.json +371 -4
  1419. package/port/cli.mjs +210 -0
  1420. package/port/hooks/guard.mjs +140 -0
  1421. package/port/knowledge/engines.json +67 -0
  1422. package/port/knowledge/footprints.json +189 -0
  1423. package/port/knowledge/maps/godot-godot-csharp.json +38 -0
  1424. package/port/knowledge/maps/godot-threejs.json +42 -0
  1425. package/port/knowledge/maps/godot-unity.json +62 -0
  1426. package/port/knowledge/maps/godot-unreal.json +48 -0
  1427. package/port/knowledge/maps/unity-godot.json +52 -0
  1428. package/port/knowledge/maps/unity-unreal.json +33 -0
  1429. package/port/knowledge/systems.json +1554 -0
  1430. package/port/mcp.mjs +62 -0
  1431. package/port/plugin/mcp-launch.mjs +19 -0
  1432. package/port/server.mjs +129 -0
  1433. package/port/src/agent.mjs +257 -0
  1434. package/port/src/archive.mjs +324 -0
  1435. package/port/src/binary.mjs +606 -0
  1436. package/port/src/blueprint.mjs +225 -0
  1437. package/port/src/compare.mjs +165 -0
  1438. package/port/src/connect.mjs +58 -0
  1439. package/port/src/convert.mjs +134 -0
  1440. package/port/src/formats.mjs +229 -0
  1441. package/port/src/graph.mjs +275 -0
  1442. package/port/src/knowledge.mjs +90 -0
  1443. package/port/src/lookup.mjs +615 -0
  1444. package/port/src/main-module.mjs +10 -0
  1445. package/port/src/plan.mjs +606 -0
  1446. package/port/src/porters.mjs +360 -0
  1447. package/port/src/proofcase.mjs +343 -0
  1448. package/port/src/relay.mjs +493 -0
  1449. package/port/src/scan.mjs +189 -0
  1450. package/port/src/scene.mjs +338 -0
  1451. package/port/src/stashpad.mjs +114 -0
  1452. package/port/src/symbols.mjs +201 -0
  1453. package/port/src/tools.mjs +198 -0
  1454. package/port/src/util.mjs +162 -0
  1455. package/port/src/workspace.mjs +39 -0
  1456. package/port/templates/unity/ReversePortSceneBuilder.cs +258 -0
  1457. package/scripts/electron-active-hook-boundaries.cjs +392 -0
  1458. package/scripts/electron-active-hook.cjs +673 -0
  1459. package/scripts/hopper-demo-x11.py +787 -0
  1460. package/scripts/prepack.mjs +60 -0
  1461. package/scripts/prepare.mjs +28 -0
  1462. package/scripts/rea.mjs +110 -0
  1463. package/scripts/verify-windows-native-artifact.mjs +39 -0
  1464. package/skills/build-inspector/SKILL.md +44 -0
  1465. package/skills/godot-port/SKILL.md +42 -0
  1466. package/skills/port-project/SKILL.md +59 -0
  1467. package/skills/port-verify/SKILL.md +35 -0
  1468. package/skills/proof-case/SKILL.md +52 -0
  1469. package/skills/rebuild/SKILL.md +66 -0
  1470. package/skills/reverse-engineer/SKILL.md +67 -0
  1471. package/skills/reverse-engineer-anything/SKILL.md +204 -0
  1472. package/skills/reverse-engineer-anything/references/android-applications.md +34 -0
  1473. package/skills/reverse-engineer-anything/references/connection-and-recovery.md +130 -0
  1474. package/skills/reverse-engineer-anything/references/evidence-workflows.md +54 -0
  1475. package/skills/reverse-engineer-anything/references/godot-source.md +37 -0
  1476. package/skills/reverse-engineer-anything/references/javascript-applications.md +175 -0
  1477. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +252 -0
  1478. package/skills/reverse-engineer-anything/references/porting-and-rebuilds.md +92 -0
  1479. package/skills/reverse-engineer-anything/references/runtime-observation.md +28 -0
  1480. package/skills/reverse-engineer-anything/references/unity-source.md +32 -0
  1481. package/skills/troubleshoot/SKILL.md +27 -0
  1482. package/skills/unity-to-godot/SKILL.md +37 -0
  1483. package/third_party/README.md +60 -0
  1484. package/third_party/evmole/LICENSE +21 -0
  1485. package/third_party/evmole/README.md +22 -0
  1486. package/third_party/pwndbg/LICENSE.md +21 -0
  1487. package/third_party/pwndbg/README.md +23 -0
  1488. package/third_party/pwntools/README.md +40 -0
  1489. package/third_party/wabt/README.md +24 -0
@@ -0,0 +1,3858 @@
1
+ // REA's packaged, read-only Ghidra headless bridge.
2
+ // @category REA
3
+
4
+ import java.io.BufferedReader;
5
+ import java.io.BufferedWriter;
6
+ import java.io.ByteArrayOutputStream;
7
+ import java.io.IOException;
8
+ import java.math.BigInteger;
9
+ import java.net.InetAddress;
10
+ import java.net.InetSocketAddress;
11
+ import java.net.StandardProtocolFamily;
12
+ import java.net.UnixDomainSocketAddress;
13
+ import java.nio.channels.Channels;
14
+ import java.nio.channels.ServerSocketChannel;
15
+ import java.nio.channels.SocketChannel;
16
+ import java.nio.charset.StandardCharsets;
17
+ import java.nio.file.AtomicMoveNotSupportedException;
18
+ import java.nio.file.Files;
19
+ import java.nio.file.LinkOption;
20
+ import java.nio.file.Path;
21
+ import java.nio.file.StandardCopyOption;
22
+ import java.nio.file.StandardOpenOption;
23
+ import java.nio.file.attribute.PosixFilePermissions;
24
+ import java.security.MessageDigest;
25
+ import java.util.ArrayList;
26
+ import java.util.Comparator;
27
+ import java.util.HashSet;
28
+ import java.util.IdentityHashMap;
29
+ import java.util.Iterator;
30
+ import java.util.LinkedHashMap;
31
+ import java.util.List;
32
+ import java.util.Locale;
33
+ import java.util.Map;
34
+ import java.util.Set;
35
+ import java.util.TreeMap;
36
+ import java.util.regex.Pattern;
37
+ import java.util.regex.PatternSyntaxException;
38
+
39
+ import com.google.gson.Gson;
40
+ import com.google.gson.GsonBuilder;
41
+ import com.google.gson.JsonArray;
42
+ import com.google.gson.JsonElement;
43
+ import com.google.gson.JsonNull;
44
+ import com.google.gson.JsonObject;
45
+ import com.google.gson.JsonParser;
46
+
47
+ import ghidra.app.util.headless.HeadlessScript;
48
+ import ghidra.app.decompiler.DecompInterface;
49
+ import ghidra.app.decompiler.DecompileOptions;
50
+ import ghidra.app.decompiler.DecompileResults;
51
+ import ghidra.app.decompiler.DecompiledFunction;
52
+ import ghidra.app.decompiler.ClangCaseToken;
53
+ import ghidra.app.decompiler.ClangNode;
54
+ import ghidra.framework.Application;
55
+ import ghidra.program.model.address.Address;
56
+ import ghidra.program.model.address.AddressIterator;
57
+ import ghidra.program.model.address.AddressSet;
58
+ import ghidra.program.model.address.AddressSpace;
59
+ import ghidra.program.model.address.AddressRange;
60
+ import ghidra.program.model.address.AddressRangeIterator;
61
+ import ghidra.program.model.block.BasicBlockModel;
62
+ import ghidra.program.model.block.CodeBlock;
63
+ import ghidra.program.model.block.CodeBlockIterator;
64
+ import ghidra.program.model.block.CodeBlockReference;
65
+ import ghidra.program.model.block.CodeBlockReferenceIterator;
66
+ import ghidra.program.model.data.StringDataInstance;
67
+ import ghidra.program.model.data.DataType;
68
+ import ghidra.program.model.lang.Register;
69
+ import ghidra.program.model.scalar.Scalar;
70
+ import ghidra.program.model.listing.CommentType;
71
+ import ghidra.program.model.listing.Data;
72
+ import ghidra.program.model.listing.DataIterator;
73
+ import ghidra.program.model.listing.Function;
74
+ import ghidra.program.model.listing.FunctionIterator;
75
+ import ghidra.program.model.listing.Instruction;
76
+ import ghidra.program.model.listing.InstructionIterator;
77
+ import ghidra.program.model.listing.Variable;
78
+ import ghidra.program.model.mem.MemoryBlock;
79
+ import ghidra.program.model.mem.MemoryBlockSourceInfo;
80
+ import ghidra.program.database.mem.FileBytes;
81
+ import ghidra.program.model.address.SegmentedAddress;
82
+ import ghidra.program.model.reloc.Relocation;
83
+ import java.io.ByteArrayOutputStream;
84
+ import ghidra.program.model.symbol.Namespace;
85
+ import ghidra.program.model.symbol.RefType;
86
+ import ghidra.program.model.symbol.Reference;
87
+ import ghidra.program.model.symbol.ReferenceIterator;
88
+ import ghidra.program.model.symbol.SourceType;
89
+ import ghidra.program.model.symbol.Symbol;
90
+ import ghidra.program.model.symbol.SymbolIterator;
91
+ import ghidra.program.model.symbol.SymbolTable;
92
+ import ghidra.program.model.pcode.FunctionPrototype;
93
+ import ghidra.program.model.pcode.HighFunction;
94
+ import ghidra.program.model.pcode.HighParam;
95
+ import ghidra.program.model.pcode.HighSymbol;
96
+ import ghidra.program.model.pcode.JumpTable;
97
+ import ghidra.program.model.pcode.PcodeOp;
98
+ import ghidra.program.model.pcode.PcodeOpAST;
99
+ import ghidra.program.model.pcode.PcodeBlock;
100
+ import ghidra.program.model.pcode.PcodeBlockBasic;
101
+ import ghidra.program.model.pcode.Varnode;
102
+ import ghidra.program.model.pcode.VarnodeAST;
103
+
104
+ public final class ReaGhidraBridge extends HeadlessScript {
105
+ private static final int MAX_HIGH_PCODE_OPS = 3000;
106
+ private static final int MAX_HIGH_PCODE_INPUTS_PER_OP = 64;
107
+ private static final int MAX_HIGH_PCODE_DEF_USE_EDGES = 12000;
108
+ private static final Gson GSON = new GsonBuilder().serializeNulls().create();
109
+ private static final Set<String> DESCRIPTOR_KEYS = Set.of(
110
+ "transport",
111
+ "endpoint_path",
112
+ "token",
113
+ "run_id",
114
+ "target_sha256",
115
+ "provider_version",
116
+ "profile_digest"
117
+ );
118
+ private static final Set<String> REQUEST_KEYS = Set.of(
119
+ "id",
120
+ "token",
121
+ "method",
122
+ "params"
123
+ );
124
+ private static final String[] CAPABILITIES = {
125
+ "annotate_native_function",
126
+ "set_address_name",
127
+ "set_addresses_names",
128
+ "inspect_native_load_image",
129
+ "read_bytes",
130
+ "address_to_file_offset",
131
+ "ping",
132
+ "shutdown",
133
+ "address_name",
134
+ "list_documents",
135
+ "list_names",
136
+ "list_procedures",
137
+ "list_segments",
138
+ "list_strings",
139
+ "procedure_address",
140
+ "resolve_containing_procedure",
141
+ "search_procedures",
142
+ "search_strings",
143
+ "analyze_function",
144
+ "procedure_assembly",
145
+ "procedure_callees",
146
+ "procedure_callers",
147
+ "procedure_info",
148
+ "procedure_pseudo_code",
149
+ "read_function_instructions",
150
+ "inspect_native_instruction",
151
+ "inspect_native_data_type",
152
+ "resolve_native_call_targets",
153
+ "procedure_references",
154
+ "xrefs"
155
+ };
156
+
157
+ private List<InventoryItem> nameInventory;
158
+ private List<FunctionEntry> procedureInventory;
159
+ private List<InventoryItem> stringInventory;
160
+ private DecompInterface decompiler;
161
+ private final ReaGhidraExtensions analysisExtensions = new ReaGhidraExtensions();
162
+ private static AddressSpace sessionDefaultAddressSpace;
163
+
164
+ @Override
165
+ public void run() throws Exception {
166
+ String[] arguments = getScriptArgs();
167
+ if (arguments.length != 1) {
168
+ throw new IllegalArgumentException("REA bridge requires one session descriptor");
169
+ }
170
+ Path descriptorPath = Path.of(arguments[0]);
171
+ SessionDescriptor descriptor = readDescriptor(descriptorPath);
172
+ // Windows retains a native immutable bearer descriptor lease until
173
+ // runtime_close; POSIX removes it after this read. Normal cleanup removes
174
+ // the Windows file, while abrupt owner death can leave private residue.
175
+ // Its protected runtime DACL also protects the bearer token while the
176
+ // bridge is running. POSIX keeps its existing consume-and-delete flow.
177
+ if (!isWindowsHost()) {
178
+ Files.deleteIfExists(descriptorPath);
179
+ }
180
+ if (currentProgram == null) {
181
+ throw new IllegalStateException("REA bridge requires an imported program");
182
+ }
183
+ sessionDefaultAddressSpace =
184
+ currentProgram.getAddressFactory().getDefaultAddressSpace();
185
+ if (currentProgram.getExecutableFormat().equals("Raw Binary")) {
186
+ boolean dosCom = currentProgram.getLanguageID().getIdAsString()
187
+ .equals("x86:LE:16:Real Mode");
188
+ String preparation = dosCom ? "REA DOS COM prepared" : "REA raw binary prepared";
189
+ if (!currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
190
+ .getBoolean(preparation, false)) {
191
+ throw new IllegalStateException(
192
+ dosCom ? "REA DOS COM entry/context preparation failed" :
193
+ "REA raw binary entry preparation failed"
194
+ );
195
+ }
196
+ }
197
+ if (!Application.getApplicationVersion().equals(descriptor.providerVersion)) {
198
+ throw new IllegalStateException("Ghidra provider version does not match the session");
199
+ }
200
+ String importedSha256 = currentProgram.getExecutableSHA256();
201
+ if (importedSha256 == null ||
202
+ !constantTimeEquals(
203
+ importedSha256.toLowerCase(Locale.ROOT),
204
+ descriptor.targetSha256
205
+ )) {
206
+ throw new IllegalStateException(
207
+ "Ghidra imported-byte digest does not match the admitted target"
208
+ );
209
+ }
210
+ // GhidraScript wraps run() in a transaction. End it before serving so
211
+ // each edit owns a real outer transaction and can roll back immediately.
212
+ end(true);
213
+ try {
214
+ if (!descriptor.transport.equals("unix-socket") && descriptor.analysisExtensions.size() > 0)
215
+ throw new IOException("Windows Ghidra P0 does not admit metadata recovery or database mutation.");
216
+ analysisExtensions.analyze(descriptor.analysisExtensions, currentProgram, monitor);
217
+ initializeDecompiler();
218
+ serve(descriptor);
219
+ }
220
+ finally {
221
+ if (decompiler != null) {
222
+ decompiler.dispose();
223
+ decompiler = null;
224
+ }
225
+ analysisExtensions.close();
226
+ }
227
+ }
228
+
229
+ private void serve(SessionDescriptor descriptor) throws Exception {
230
+ if (descriptor.transport.equals("unix-socket")) {
231
+ serveUnixSocket(descriptor);
232
+ return;
233
+ }
234
+ if (descriptor.transport.equals("authenticated-loopback-tcp")) {
235
+ serveLoopbackTcp(descriptor);
236
+ return;
237
+ }
238
+ throw new IllegalArgumentException("REA bridge transport is invalid");
239
+ }
240
+
241
+ private void serveUnixSocket(SessionDescriptor descriptor) throws Exception {
242
+ Path socketPath = Path.of(descriptor.endpointPath);
243
+ Files.deleteIfExists(socketPath);
244
+ try (ServerSocketChannel server = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) {
245
+ server.bind(UnixDomainSocketAddress.of(socketPath));
246
+ Files.setPosixFilePermissions(socketPath, PosixFilePermissions.fromString("rw-------"));
247
+ acceptClient(server, descriptor);
248
+ }
249
+ finally {
250
+ Files.deleteIfExists(socketPath);
251
+ }
252
+ }
253
+
254
+ private void serveLoopbackTcp(SessionDescriptor descriptor) throws Exception {
255
+ Path endpointPath = Path.of(descriptor.endpointPath);
256
+ Path pendingPath = endpointPath.resolveSibling(endpointPath.getFileName() + ".pending");
257
+ Files.deleteIfExists(endpointPath);
258
+ Files.deleteIfExists(pendingPath);
259
+ try (ServerSocketChannel server = ServerSocketChannel.open()) {
260
+ server.bind(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0));
261
+ InetSocketAddress address = (InetSocketAddress) server.getLocalAddress();
262
+ JsonObject endpoint = new JsonObject();
263
+ endpoint.addProperty("host", "127.0.0.1");
264
+ endpoint.addProperty("port", address.getPort());
265
+ // The Windows native reader refuses a file while a writer still
266
+ // owns it. Closing CREATE_NEW publishes the complete record without
267
+ // requesting rename rights on the retained private directory guard.
268
+ Files.writeString(
269
+ isWindowsHost() ? endpointPath : pendingPath,
270
+ GSON.toJson(endpoint) + "\n",
271
+ StandardCharsets.UTF_8,
272
+ StandardOpenOption.CREATE_NEW,
273
+ StandardOpenOption.WRITE
274
+ );
275
+ if (!isWindowsHost()) {
276
+ try {
277
+ Files.move(pendingPath, endpointPath, StandardCopyOption.ATOMIC_MOVE);
278
+ }
279
+ catch (AtomicMoveNotSupportedException exception) {
280
+ Files.move(pendingPath, endpointPath);
281
+ }
282
+ }
283
+ acceptClient(server, descriptor);
284
+ }
285
+ finally {
286
+ Files.deleteIfExists(pendingPath);
287
+ Files.deleteIfExists(endpointPath);
288
+ }
289
+ }
290
+
291
+ private static boolean isWindowsHost() {
292
+ return System.getProperty("os.name", "").startsWith("Windows");
293
+ }
294
+
295
+ private void acceptClient(
296
+ ServerSocketChannel server,
297
+ SessionDescriptor descriptor) throws Exception {
298
+ try (SocketChannel client = server.accept();
299
+ BufferedReader reader = new BufferedReader(
300
+ Channels.newReader(client, StandardCharsets.UTF_8)
301
+ );
302
+ BufferedWriter writer = new BufferedWriter(
303
+ Channels.newWriter(client, StandardCharsets.UTF_8)
304
+ )) {
305
+ serveClient(descriptor, reader, writer);
306
+ }
307
+ }
308
+
309
+ private void serveClient(
310
+ SessionDescriptor descriptor,
311
+ BufferedReader reader,
312
+ BufferedWriter writer) throws Exception {
313
+ while (true) {
314
+ String line = reader.readLine();
315
+ if (line == null) {
316
+ return;
317
+ }
318
+ Request request;
319
+ try {
320
+ request = parseRequest(line, descriptor.token);
321
+ }
322
+ catch (RuntimeException exception) {
323
+ writeFailure(writer, 1, "invalid_request", "Bridge request is invalid");
324
+ return;
325
+ }
326
+ try {
327
+ if (request.method.equals("shutdown")) {
328
+ requireKeys(request.params, Set.of());
329
+ JsonObject result = new JsonObject();
330
+ result.addProperty("shutdown", true);
331
+ result.addProperty("project_ephemeral", true);
332
+ writeSuccess(writer, request.id, result);
333
+ return;
334
+ }
335
+ writeSuccess(writer, request.id, handleRequest(request, descriptor));
336
+ }
337
+ catch (RequestFailure failure) {
338
+ writeFailure(writer, request.id, failure.code, failure.getMessage());
339
+ }
340
+ catch (Exception exception) {
341
+ writeFailure(
342
+ writer,
343
+ request.id,
344
+ "bridge_exception",
345
+ exception.getClass().getSimpleName() + ": " + safeMessage(exception)
346
+ );
347
+ }
348
+ }
349
+ }
350
+
351
+ private JsonElement handleRequest(Request request, SessionDescriptor descriptor)
352
+ throws Exception {
353
+ if (request.method.equals("ping")) {
354
+ requireKeys(request.params, Set.of());
355
+ return sessionInfo(descriptor);
356
+ }
357
+ if (analysisTimeoutOccurred()) {
358
+ throw new RequestFailure(
359
+ "analysis_incomplete",
360
+ "Ghidra auto-analysis did not complete for this Program"
361
+ );
362
+ }
363
+ return switch (request.method) {
364
+ case "inspect_native_load_image" -> inspectNativeLoadImage(request.params);
365
+ case "read_bytes" -> readMemoryBytes(request.params);
366
+ case "address_to_file_offset" -> addressToFileOffset(request.params);
367
+ case "address_name" -> addressName(request.params);
368
+ case "list_documents" -> listDocuments(request.params);
369
+ case "list_names" -> listNames(request.params);
370
+ case "list_procedures" -> listProcedures(request.params);
371
+ case "list_segments" -> listSegments(request.params);
372
+ case "list_strings" -> listStrings(request.params);
373
+ case "procedure_address" -> procedureAddress(request.params);
374
+ case "procedure_assembly" -> procedureAssembly(request.params);
375
+ case "procedure_callees" -> procedureCalls(request.params, false);
376
+ case "procedure_callers" -> procedureCalls(request.params, true);
377
+ case "procedure_info" -> procedureInfo(request.params);
378
+ case "procedure_pseudo_code" -> procedurePseudocode(request.params);
379
+ case "read_function_instructions" -> readFunctionInstructions(request.params);
380
+ case "procedure_references" -> procedureReferences(request.params);
381
+ case "resolve_containing_procedure" -> containingProcedure(request.params);
382
+ case "search_procedures" -> search(request.params, true);
383
+ case "search_strings" -> search(request.params, false);
384
+ case "inspect_native_data_type" -> inspectNativeDataType(request.params);
385
+ case "inspect_native_instruction" -> inspectNativeInstruction(request.params, true);
386
+ case "resolve_native_call_targets" -> resolveNativeCallTargets(request.params);
387
+ case "xrefs" -> xrefs(request.params);
388
+ case "analyze_function" -> {
389
+ FunctionAnalysisObservation observation = analyzeFunctionObservation(request.params);
390
+ yield functionObservation(observation.dossier(), observation.limitations());
391
+ }
392
+ case "set_address_name", "set_addresses_names" -> {
393
+ if (!descriptor.transport.equals("unix-socket"))
394
+ throw new RequestFailure("method_unavailable", "Windows P0 does not admit database mutation");
395
+ yield request.method.equals("set_address_name")
396
+ ? setAddressName(request.params) : setAddressesNames(request.params);
397
+ }
398
+ case "annotate_native_function" -> {
399
+ if (!descriptor.transport.equals("unix-socket"))
400
+ throw new RequestFailure("method_unavailable", "Windows P0 does not admit database mutation");
401
+ yield annotateNativeFunction(request.params);
402
+ }
403
+ default -> throw new RequestFailure(
404
+ "method_unavailable",
405
+ "Bridge method is unavailable"
406
+ );
407
+ };
408
+ }
409
+
410
+ private JsonObject sessionInfo(SessionDescriptor descriptor) throws Exception {
411
+ boolean timedOut = analysisTimeoutOccurred();
412
+ JsonObject provider = new JsonObject();
413
+ provider.addProperty("id", "ghidra");
414
+ provider.addProperty("version", Application.getApplicationVersion());
415
+
416
+ JsonObject target = new JsonObject();
417
+ target.addProperty("name", currentProgram.getName());
418
+ target.addProperty("language_id", currentProgram.getLanguageID().getIdAsString());
419
+ target.addProperty(
420
+ "compiler_spec_id",
421
+ currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString()
422
+ );
423
+ target.addProperty("image_base", canonicalAddress(currentProgram.getImageBase()));
424
+ target.addProperty(
425
+ "default_address_space",
426
+ currentProgram.getAddressFactory().getDefaultAddressSpace().getName()
427
+ );
428
+ target.addProperty("sha256", descriptor.targetSha256);
429
+
430
+ JsonObject result = new JsonObject();
431
+ result.addProperty("name", "REA Ghidra bridge");
432
+ result.addProperty("run_id", descriptor.runId);
433
+ result.addProperty("profile_digest", descriptor.profileDigest);
434
+ result.add("provider", provider);
435
+ boolean readOnly = !descriptor.transport.equals("unix-socket");
436
+ result.addProperty("read_only", readOnly);
437
+ result.addProperty("analysis_complete", !timedOut);
438
+ result.addProperty("analysis_timed_out", timedOut);
439
+ result.add("capabilities", GSON.toJsonTree(
440
+ java.util.Arrays.stream(CAPABILITIES)
441
+ .filter(value -> !readOnly || !(value.equals("annotate_native_function")
442
+ || value.equals("set_address_name") || value.equals("set_addresses_names")))
443
+ .toArray(String[]::new)
444
+ ));
445
+ result.add("target", target);
446
+ JsonArray extensions = analysisExtensions.reports();
447
+ if (extensions.size() > 0) result.add("analysis_extensions", extensions);
448
+ // Written by ReaGhidraApplySeeds before auto-analysis; REA checks it
449
+ // against the committed profile.
450
+ String seeds = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
451
+ .getString("REA analysis seeds", null);
452
+ if (seeds != null) result.add("analysis_seeds", JsonParser.parseString(seeds));
453
+ // Written by ReaGhidraMipsSoftFloat after analysis; REA requires it
454
+ // exactly when the committed profile declares soft-float storage.
455
+ String softFloat = currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
456
+ .getString("REA MIPS soft-float storage", null);
457
+ if (softFloat != null) result.add("mips_soft_float_storage", JsonParser.parseString(softFloat));
458
+ return result;
459
+ }
460
+
461
+ private static String bytesHex(byte[] bytes) {
462
+ return java.util.HexFormat.of().formatHex(bytes);
463
+ }
464
+
465
+ private String hashFileBytes(FileBytes source, boolean modified) throws Exception {
466
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
467
+ byte[] chunk = new byte[65536];
468
+ for (long offset = 0; offset < source.getSize();) {
469
+ monitor.checkCancelled();
470
+ int length = (int) Math.min(chunk.length, source.getSize() - offset);
471
+ int read = modified
472
+ ? source.getModifiedBytes(offset, chunk, 0, length)
473
+ : source.getOriginalBytes(offset, chunk, 0, length);
474
+ if (read != length) throw new IOException("Incomplete FileBytes read at " + offset);
475
+ digest.update(chunk, 0, read);
476
+ offset += read;
477
+ }
478
+ return bytesHex(digest.digest());
479
+ }
480
+
481
+ private String hashMemory(Address start, long length) throws Exception {
482
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
483
+ byte[] chunk = new byte[65536];
484
+ for (long offset = 0; offset < length;) {
485
+ monitor.checkCancelled();
486
+ int count = (int) Math.min(chunk.length, length - offset);
487
+ int read = currentProgram.getMemory().getBytes(start.add(offset), chunk, 0, count);
488
+ if (read != count) throw new IOException("Incomplete memory read at " + start.add(offset));
489
+ digest.update(chunk, 0, read);
490
+ offset += read;
491
+ }
492
+ return bytesHex(digest.digest());
493
+ }
494
+
495
+ private JsonObject inspectNativeLoadImage(JsonObject params) throws Exception {
496
+ requireKeys(params, Set.of());
497
+ JsonObject result = new JsonObject();
498
+ JsonArray recovery = analysisExtensions.inspectImage();
499
+ if (recovery.size() > 0) result.add("metadata_recovery", recovery);
500
+ result.addProperty("executable_format", currentProgram.getExecutableFormat());
501
+ result.addProperty("language_id", currentProgram.getLanguageID().getIdAsString());
502
+ result.addProperty("compiler_spec_id", currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString());
503
+ result.addProperty("image_base", canonicalAddress(currentProgram.getImageBase()));
504
+ result.addProperty("default_address_space", sessionDefaultAddressSpace.getName());
505
+ List<FileBytes> files = currentProgram.getMemory().getAllFileBytes();
506
+ JsonArray sources = new JsonArray();
507
+ for (FileBytes file : files) {
508
+ JsonObject source = new JsonObject();
509
+ source.addProperty("name", file.getFilename());
510
+ source.addProperty("size", file.getSize());
511
+ source.addProperty("original_sha256", hashFileBytes(file, false));
512
+ source.addProperty("modified_sha256", hashFileBytes(file, true));
513
+ sources.add(source);
514
+ }
515
+ result.add("source_files", sources);
516
+ JsonArray mappings = new JsonArray();
517
+ for (MemoryBlock block : currentProgram.getMemory().getBlocks()) {
518
+ for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
519
+ monitor.checkCancelled();
520
+ JsonObject mapping = new JsonObject();
521
+ mapping.addProperty("block", block.getName());
522
+ mapping.addProperty("start", canonicalAddress(source.getMinAddress()));
523
+ mapping.addProperty("end", canonicalAddress(source.getMaxAddress()));
524
+ mapping.addProperty("address_space", source.getMinAddress().getAddressSpace().getName());
525
+ mapping.addProperty("initialized", block.isInitialized());
526
+ mapping.addProperty("loaded", block.isLoaded());
527
+ mapping.addProperty("overlay", block.isOverlay());
528
+ mapping.addProperty("length", source.getLength());
529
+ FileBytes backing = source.getFileBytes().orElse(null);
530
+ int sourceIndex = backing == null ? -1 : files.indexOf(backing);
531
+ if (sourceIndex < 0) mapping.add("source_file_index", JsonNull.INSTANCE);
532
+ else mapping.addProperty("source_file_index", sourceIndex);
533
+ long offset = source.getFileBytesOffset();
534
+ if (offset < 0) mapping.add("file_offset", JsonNull.INSTANCE);
535
+ else mapping.addProperty("file_offset", offset);
536
+ if (block.isInitialized()) mapping.addProperty("sha256", hashMemory(source.getMinAddress(), source.getLength()));
537
+ else mapping.add("sha256", JsonNull.INSTANCE);
538
+ mappings.add(mapping);
539
+ }
540
+ }
541
+ result.add("mappings", mappings);
542
+ JsonArray relocations = new JsonArray();
543
+ Iterator<Relocation> iterator = currentProgram.getRelocationTable().getRelocations();
544
+ while (iterator.hasNext()) {
545
+ monitor.checkCancelled();
546
+ Relocation relocation = iterator.next();
547
+ Address location = relocation.getAddress();
548
+ JsonObject row = new JsonObject();
549
+ row.addProperty("address", canonicalAddress(location));
550
+ if (location instanceof SegmentedAddress segmented) {
551
+ row.addProperty("segment", segmented.getSegment());
552
+ row.addProperty("offset", segmented.getSegmentOffset());
553
+ } else {
554
+ row.add("segment", JsonNull.INSTANCE);
555
+ row.add("offset", JsonNull.INSTANCE);
556
+ }
557
+ row.addProperty("status", relocation.getStatus().toString());
558
+ row.addProperty("type", relocation.getType());
559
+ row.add("values", GSON.toJsonTree(relocation.getValues() == null ? new long[0] : relocation.getValues()));
560
+ byte[] original = relocation.getBytes();
561
+ row.add("original_bytes_hex", original == null ? JsonNull.INSTANCE : GSON.toJsonTree(bytesHex(original)));
562
+ if (original != null && original.length > 0) {
563
+ byte[] memory = new byte[original.length];
564
+ int read = currentProgram.getMemory().getBytes(location, memory);
565
+ row.add("memory_bytes_hex", read == memory.length ? GSON.toJsonTree(bytesHex(memory)) : JsonNull.INSTANCE);
566
+ } else row.add("memory_bytes_hex", JsonNull.INSTANCE);
567
+ relocations.add(row);
568
+ }
569
+ result.add("relocations", relocations);
570
+ List<Address> entries = new ArrayList<>();
571
+ var entryIterator = currentProgram.getSymbolTable().getExternalEntryPointIterator();
572
+ while (entryIterator.hasNext()) entries.add(entryIterator.next());
573
+ entries.sort(Address::compareTo);
574
+ JsonArray entryPoints = new JsonArray();
575
+ for (Address entry : entries) entryPoints.add(canonicalAddress(entry));
576
+ result.add("entry_points", entryPoints);
577
+ JsonArray contexts = new JsonArray();
578
+ for (Address entry : entries) {
579
+ JsonObject context = new JsonObject();
580
+ context.addProperty("address", canonicalAddress(entry));
581
+ JsonArray registers = new JsonArray();
582
+ for (String name : new String[] { "CS", "DS", "ES", "SS" }) {
583
+ Register register = currentProgram.getRegister(name);
584
+ if (register == null) continue;
585
+ JsonObject row = new JsonObject();
586
+ row.addProperty("name", name);
587
+ BigInteger value = currentProgram.getProgramContext().getValue(register, entry, false);
588
+ row.add("value_hex", value == null ? JsonNull.INSTANCE : GSON.toJsonTree("0x" + value.toString(16)));
589
+ registers.add(row);
590
+ }
591
+ context.add("registers", registers);
592
+ contexts.add(context);
593
+ }
594
+ result.add("entry_context", contexts);
595
+ return result;
596
+ }
597
+
598
+ private JsonObject readMemoryBytes(JsonObject params) throws Exception {
599
+ requireKeys(params, Set.of("document", "address", "length"));
600
+ requireDocument(params);
601
+ Address start = requireAddress(params, "address");
602
+ JsonElement requested = params.get("length");
603
+ long length;
604
+ try {
605
+ if (requested == null || !requested.isJsonPrimitive() || !requested.getAsJsonPrimitive().isNumber())
606
+ throw new IllegalArgumentException();
607
+ length = requested.getAsBigDecimal().longValueExact();
608
+ if (length < 1 || length > 9007199254740991L) throw new IllegalArgumentException();
609
+ } catch (RuntimeException failure) {
610
+ throw new RequestFailure("invalid_request", "Byte length must be a positive safe integer");
611
+ }
612
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
613
+ byte[] chunk = new byte[65536];
614
+ for (long offset = 0; offset < length;) {
615
+ monitor.checkCancelled();
616
+ Address at;
617
+ try { at = start.addNoWrap(offset); }
618
+ catch (Exception endOfSpace) { break; }
619
+ MemoryBlock block = currentProgram.getMemory().getBlock(at);
620
+ if (block == null || !block.isInitialized()) break;
621
+ int count = (int) Math.min(chunk.length, Math.min(length - offset, block.getEnd().subtract(at) + 1));
622
+ int read = currentProgram.getMemory().getBytes(at, chunk, 0, count);
623
+ bytes.write(chunk, 0, read);
624
+ if (read != count) break;
625
+ offset += read;
626
+ }
627
+ JsonObject result = new JsonObject();
628
+ result.addProperty("address", canonicalAddress(start));
629
+ result.addProperty("requested_bytes", length);
630
+ result.addProperty("returned_bytes", bytes.size());
631
+ result.addProperty("bytes_hex", bytesHex(bytes.toByteArray()));
632
+ result.addProperty("complete", bytes.size() == length);
633
+ return result;
634
+ }
635
+
636
+ private JsonObject addressToFileOffset(JsonObject params) {
637
+ requireKeys(params, Set.of("document", "address"));
638
+ requireDocument(params);
639
+ Address address = requireAddress(params, "address");
640
+ MemoryBlock block = currentProgram.getMemory().getBlock(address);
641
+ if (block == null || !block.isInitialized())
642
+ throw new RequestFailure("invalid_request", "Address has no initialized file-backed memory: " + canonicalAddress(address));
643
+ Long fileOffset = null;
644
+ int mappings = 0;
645
+ for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
646
+ if (!source.contains(address) || source.getFileBytes().isEmpty()) continue;
647
+ long offset = source.getFileBytesOffset(address);
648
+ if (offset < 0) continue;
649
+ fileOffset = offset;
650
+ mappings++;
651
+ }
652
+ if (mappings != 1)
653
+ throw new RequestFailure("invalid_request", "Address has " + mappings + " authoritative source mappings: " + canonicalAddress(address));
654
+ JsonObject result = new JsonObject();
655
+ result.addProperty("address", canonicalAddress(address));
656
+ result.addProperty("file_offset", fileOffset);
657
+ return result;
658
+ }
659
+
660
+ private void initializeDecompiler() {
661
+ DecompileOptions options = new DecompileOptions();
662
+ options.setMaxInstructions(Integer.MAX_VALUE);
663
+ decompiler = new DecompInterface();
664
+ decompiler.setOptions(options);
665
+ decompiler.toggleCCode(true);
666
+ decompiler.toggleSyntaxTree(true);
667
+ decompiler.toggleJumpLoads(true);
668
+ if (!decompiler.openProgram(currentProgram)) {
669
+ throw new IllegalStateException("Ghidra decompiler could not open the imported Program");
670
+ }
671
+ }
672
+
673
+ private JsonElement procedurePseudocode(JsonObject params) throws Exception {
674
+ requireKeys(params, Set.of("document", "procedure"));
675
+ requireDocument(params);
676
+ Function function = resolveProcedure(requireString(params, "procedure"));
677
+ String value = decompiledText(decompile(function));
678
+ return functionObservation(
679
+ value == null ? JsonNull.INSTANCE : GSON.toJsonTree(value),
680
+ noReturnCallLimitations(
681
+ currentProgram.getListing().getInstructions(function.getBody(), true)
682
+ )
683
+ );
684
+ }
685
+
686
+ private JsonElement procedureAssembly(JsonObject params) throws Exception {
687
+ requireKeys(params, Set.of("document", "procedure"));
688
+ requireDocument(params);
689
+ Function function = resolveProcedure(requireString(params, "procedure"));
690
+ InstructionScan scan = scanInstructions(function, Integer.MAX_VALUE);
691
+ return functionObservation(
692
+ GSON.toJsonTree(renderAssembly(scan.instructions)),
693
+ noReturnCallLimitations(scan.instructions)
694
+ );
695
+ }
696
+
697
+ private JsonObject readFunctionInstructions(JsonObject params) throws Exception {
698
+ requireKeys(params, Set.of("document", "procedure"));
699
+ requireDocument(params);
700
+ Function function = resolveProcedure(requireString(params, "procedure"));
701
+ InstructionScan scan = scanInstructions(function, Integer.MAX_VALUE);
702
+ JsonArray items = new JsonArray();
703
+ for (String line : renderAssemblyLines(scan.instructions)) {
704
+ items.add(line);
705
+ }
706
+
707
+ JsonArray limitations = new JsonArray();
708
+ limitations.add("Instruction text and ordering are Ghidra-specific representations.");
709
+ limitations.add(
710
+ "The fast path does not invoke the decompiler or scan whole-program names and strings."
711
+ );
712
+ List<String> boundaryLimitations = noReturnCallLimitations(scan.instructions);
713
+ for (String limitation : boundaryLimitations) limitations.add(limitation);
714
+ JsonObject result = new JsonObject();
715
+ result.add("procedure", procedureIdentity(function));
716
+ result.add("instructions", items);
717
+ result.add("limitations", limitations);
718
+ return functionObservation(result, boundaryLimitations);
719
+ }
720
+
721
+ private JsonArray procedureCalls(JsonObject params, boolean callers) throws Exception {
722
+ requireKeys(params, Set.of("document", "procedure"));
723
+ requireDocument(params);
724
+ Function function = resolveProcedure(requireString(params, "procedure"));
725
+ Set<Function> observed = callers
726
+ ? function.getCallingFunctions(monitor)
727
+ : function.getCalledFunctions(monitor);
728
+ List<Function> ordered = new ArrayList<>(observed);
729
+ ordered.sort(Comparator.comparing(Function::getEntryPoint));
730
+ JsonArray result = new JsonArray();
731
+ for (Function item : ordered) {
732
+ result.add(canonicalAddress(item.getEntryPoint()));
733
+ }
734
+ return result;
735
+ }
736
+
737
+ private JsonObject procedureInfo(JsonObject params) throws Exception {
738
+ requireKeys(params, Set.of("document", "procedure"));
739
+ requireDocument(params);
740
+ Function function = resolveProcedure(requireString(params, "procedure"));
741
+ JsonObject result = new JsonObject();
742
+ result.addProperty("name", procedureName(function));
743
+ result.addProperty("entrypoint", canonicalAddress(function.getEntryPoint()));
744
+ result.addProperty("basicblock_count", basicBlocks(function).size());
745
+ JsonObject body = functionBody(function);
746
+ result.addProperty("length", body.get("total_bytes").getAsLong());
747
+ result.add("body", body);
748
+ result.addProperty("signature", function.getPrototypeString(false, true));
749
+ result.add("locals", functionLocals(function));
750
+ result.add("classification", functionClassification(function));
751
+ return functionObservation(
752
+ result,
753
+ noReturnCallLimitations(
754
+ currentProgram.getListing().getInstructions(function.getBody(), true)
755
+ )
756
+ );
757
+ }
758
+
759
+ private JsonObject procedureReferences(JsonObject params) throws Exception {
760
+ requireKeys(
761
+ params,
762
+ Set.of(
763
+ "document",
764
+ "procedure",
765
+ "direction"
766
+ )
767
+ );
768
+ requireDocument(params);
769
+ Function function = resolveProcedure(requireString(params, "procedure"));
770
+ String direction = requireString(params, "direction");
771
+ if (!direction.equals("incoming") && !direction.equals("outgoing")) {
772
+ throw new RequestFailure("invalid_request", "direction must be incoming or outgoing");
773
+ }
774
+ List<Reference> references = collectReferences(function, direction);
775
+ JsonArray edges = new JsonArray();
776
+ for (Reference reference : references) {
777
+ edges.add(referenceEdge(reference));
778
+ }
779
+ JsonObject result = new JsonObject();
780
+ result.add("procedure", procedureIdentity(function));
781
+ result.addProperty("direction", direction);
782
+ result.add("references", edges);
783
+ result.addProperty("reference_kinds_available", true);
784
+ JsonArray unresolvedCalls = new JsonArray();
785
+ if (direction.equals("outgoing")) for (Instruction instruction : scanInstructions(function, Integer.MAX_VALUE).instructions) {
786
+ if (!instruction.getFlowType().isCall()) continue;
787
+ boolean resolved = false;
788
+ for (Reference reference : instruction.getReferencesFrom()) if (reference.getReferenceType().isCall()) resolved = true;
789
+ if (!resolved) {
790
+ JsonObject site = new JsonObject();
791
+ site.addProperty("address", canonicalAddress(instruction.getAddress()));
792
+ site.addProperty("reason", "Instruction decoder identifies a call but ReferenceManager supplies no call destination");
793
+ unresolvedCalls.add(site);
794
+ }
795
+ }
796
+ result.add("unresolved_calls", unresolvedCalls);
797
+ return result;
798
+ }
799
+
800
+ private JsonObject inspectNativeDataType(JsonObject params) throws Exception {
801
+ requireKeys(params, Set.of("document", "type", "address"));
802
+ requireDocument(params);
803
+ boolean byType = params.has("type") && !params.get("type").isJsonNull();
804
+ boolean byAddress = params.has("address") && !params.get("address").isJsonNull();
805
+ if (byType == byAddress) throw new RequestFailure("invalid_request", "Supply exactly one of type or address");
806
+ DataType type;
807
+ Address address = null;
808
+ if (byType) type = currentProgram.getDataTypeManager().getDataType(requireString(params, "type"));
809
+ else {
810
+ address = requireAddress(params, "address");
811
+ Data data = currentProgram.getListing().getDefinedDataAt(address);
812
+ type = data == null ? null : data.getDataType();
813
+ }
814
+ JsonObject result = new JsonObject();
815
+ result.addProperty("status", type == null ? "unavailable" : "available");
816
+ result.add("reason", type == null ? GSON.toJsonTree("No exact database type or defined typed data exists at the selected identity") : JsonNull.INSTANCE);
817
+ for (String key : List.of("id", "name", "source_archive", "size_bytes", "alignment_bytes", "packing_enabled", "referenced_type", "array_count", "array_stride_bytes")) result.add(key, JsonNull.INSTANCE);
818
+ result.add("address", address == null ? JsonNull.INSTANCE : GSON.toJsonTree(canonicalAddress(address)));
819
+ result.addProperty("source", "analysis-database");
820
+ result.addProperty("kind", "unavailable");
821
+ JsonArray fields = new JsonArray();
822
+ JsonArray members = new JsonArray();
823
+ int totalFields = 0;
824
+ if (type != null) {
825
+ result.addProperty("id", type.getPathName());
826
+ result.addProperty("name", type.getName());
827
+ if (type.getSourceArchive() != null) result.addProperty("source_archive", type.getSourceArchive().getName());
828
+ if (type.getLength() >= 0) result.addProperty("size_bytes", type.getLength());
829
+ if (type.getAlignment() > 0) result.addProperty("alignment_bytes", type.getAlignment());
830
+ String kind = type instanceof ghidra.program.model.data.Structure ? "struct" : type instanceof ghidra.program.model.data.Union ? "union" : type instanceof ghidra.program.model.data.Enum ? "enum" : type instanceof ghidra.program.model.data.Pointer ? "pointer" : type instanceof ghidra.program.model.data.Array ? "array" : type instanceof ghidra.program.model.data.TypeDef ? "typedef" : type instanceof ghidra.program.model.data.BuiltInDataType ? "scalar" : "unsupported";
831
+ result.addProperty("kind", kind);
832
+ if (type instanceof ghidra.program.model.data.Composite composite) {
833
+ result.addProperty("packing_enabled", composite.isPackingEnabled());
834
+ ghidra.program.model.data.DataTypeComponent[] components = composite.getComponents();
835
+ totalFields = components.length;
836
+ for (int index = 0; index < Math.min(components.length, 20_000); index++) {
837
+ monitor.checkCancelled();
838
+ ghidra.program.model.data.DataTypeComponent component = components[index];
839
+ JsonObject field = new JsonObject();
840
+ field.addProperty("ordinal", component.getOrdinal());
841
+ field.add("name", component.getFieldName() == null ? JsonNull.INSTANCE : GSON.toJsonTree(component.getFieldName()));
842
+ field.addProperty("offset_bytes", component.getOffset());
843
+ field.add("size_bytes", component.getLength() < 0 ? JsonNull.INSTANCE : GSON.toJsonTree(component.getLength()));
844
+ field.addProperty("type_id", component.getDataType().getPathName());
845
+ field.add("bit_size", JsonNull.INSTANCE);
846
+ field.add("bit_offset", JsonNull.INSTANCE);
847
+ if (component.getDataType() instanceof ghidra.program.model.data.BitFieldDataType bitfield) {
848
+ field.addProperty("bit_size", bitfield.getBitSize());
849
+ field.addProperty("bit_offset", bitfield.getBitOffset());
850
+ }
851
+ field.addProperty("status", "observed");
852
+ field.addProperty("flexible_tail", "unknown");
853
+ fields.add(field);
854
+ }
855
+ } else if (type instanceof ghidra.program.model.data.Enum enumeration) {
856
+ String[] names = enumeration.getNames();
857
+ totalFields = names.length;
858
+ java.util.Arrays.sort(names);
859
+ for (int index = 0; index < Math.min(names.length, 20_000); index++) {
860
+ monitor.checkCancelled();
861
+ JsonObject member = new JsonObject();
862
+ member.addProperty("name", names[index]);
863
+ member.addProperty("value", Long.toString(enumeration.getValue(names[index])));
864
+ members.add(member);
865
+ }
866
+ } else if (type instanceof ghidra.program.model.data.Pointer pointer) {
867
+ if (pointer.getDataType() != null) result.addProperty("referenced_type", pointer.getDataType().getPathName());
868
+ } else if (type instanceof ghidra.program.model.data.Array array) {
869
+ result.addProperty("referenced_type", array.getDataType().getPathName());
870
+ result.addProperty("array_count", array.getNumElements());
871
+ result.addProperty("array_stride_bytes", array.getElementLength());
872
+ } else if (type instanceof ghidra.program.model.data.TypeDef alias) {
873
+ result.addProperty("referenced_type", alias.getDataType().getPathName());
874
+ }
875
+ }
876
+ result.add("fields", fields);
877
+ result.add("members", members);
878
+ result.addProperty("total_fields", totalFields);
879
+ result.addProperty("truncated", totalFields > 20_000);
880
+ result.add("limitations", GSON.toJsonTree(List.of("Layouts are Ghidra data-type database observations; original debug/source authority and flexible-tail semantics are not inferred. Nested and recursive types retain category-path identities for separate inspection.", "At most 20000 fields or enum members are serialized per type to bound provider output. total_fields and truncated report omissions. Enum values preserve Ghidra's signed long representation.")));
881
+ if (type != null) {
882
+ JsonObject metadata = analysisExtensions.inspectDataType(currentProgram, type, address, monitor);
883
+ if (metadata != null) result.add("metadata_recovery", metadata);
884
+ }
885
+ return result;
886
+ }
887
+
888
+ private JsonObject instructionReference(Reference reference) {
889
+ JsonObject item = new JsonObject();
890
+ JsonObject kind = referenceKind(reference);
891
+ item.addProperty("source_address", canonicalAddress(reference.getFromAddress()));
892
+ item.addProperty("target_address", canonicalAddress(reference.getToAddress()));
893
+ for (String field : List.of("type", "call", "jump", "indirect", "computed",
894
+ "operand_index", "data", "read", "write", "primary", "provenance"))
895
+ item.add(field, kind.get(field));
896
+ item.addProperty("source", normalizedSource(reference.getSource()));
897
+ return item;
898
+ }
899
+
900
+ private JsonObject inspectNativeInstruction(JsonObject params, boolean includeIncoming) throws Exception {
901
+ requireKeys(params, Set.of("document", "address"));
902
+ requireDocument(params);
903
+ Address address = requireAddress(params, "address");
904
+ Instruction instruction = currentProgram.getListing().getInstructionAt(address);
905
+ Function function = currentProgram.getFunctionManager().getFunctionContaining(address);
906
+ JsonObject result = new JsonObject();
907
+ result.addProperty("address", canonicalAddress(address));
908
+ result.add("procedure", function == null ? JsonNull.INSTANCE : GSON.toJsonTree(canonicalAddress(function.getEntryPoint())));
909
+ result.addProperty("architecture", currentProgram.getLanguageID().getIdAsString());
910
+ result.addProperty("mode", currentProgram.getLanguage().getLanguageDescription().getVariant());
911
+ if (currentProgram.getLanguageID().getIdAsString().startsWith("MIPS:")) {
912
+ JsonArray decoderContext = new JsonArray();
913
+ var modeRegister = currentProgram.getRegister("ISA_MODE");
914
+ if (modeRegister != null) {
915
+ var modeValue = currentProgram.getProgramContext().getValue(modeRegister, address, false);
916
+ JsonObject context = new JsonObject();
917
+ context.addProperty("register", modeRegister.getName());
918
+ context.add("value", modeValue == null ? JsonNull.INSTANCE : GSON.toJsonTree(modeValue.toString()));
919
+ context.addProperty("source", "ghidra-program-context");
920
+ decoderContext.add(context);
921
+ }
922
+ result.add("decoder_context", decoderContext);
923
+ }
924
+ JsonArray operands = new JsonArray();
925
+ JsonArray references = new JsonArray();
926
+ JsonArray destinations = new JsonArray();
927
+ JsonArray flowEvidence = new JsonArray();
928
+ JsonObject flow = new JsonObject();
929
+ flow.addProperty("kind", "unavailable");
930
+ flow.addProperty("conditional", false);
931
+ flow.addProperty("computed", false);
932
+ if (instruction == null) {
933
+ result.addProperty("status", !currentProgram.getMemory().contains(address) ? "outside-memory" : currentProgram.getListing().getInstructionContaining(address) != null
934
+ ? "not-instruction-boundary" : currentProgram.getListing().getDefinedDataContaining(address) != null ? "data" : "undecodable");
935
+ for (String key : List.of("bytes", "length", "mnemonic", "raw_disassembly")) result.add(key, JsonNull.INSTANCE);
936
+ } else {
937
+ result.addProperty("status", "decoded");
938
+ result.addProperty("bytes", java.util.HexFormat.of().formatHex(instruction.getBytes()));
939
+ result.addProperty("length", instruction.getLength());
940
+ result.addProperty("mnemonic", instruction.getMnemonicString());
941
+ result.addProperty("raw_disassembly", instruction.toString());
942
+ RefType type = instruction.getFlowType();
943
+ JsonObject listingFlow = new JsonObject();
944
+ listingFlow.addProperty("source", "ghidra-listing-flow-type");
945
+ listingFlow.addProperty("value", type.getName());
946
+ flowEvidence.add(listingFlow);
947
+ boolean decodedReturn = false;
948
+ for (PcodeOp operation : instruction.getPcode()) {
949
+ if (operation.getOpcode() != PcodeOp.RETURN) continue;
950
+ decodedReturn = true;
951
+ JsonObject returnEvidence = new JsonObject();
952
+ returnEvidence.addProperty("source", "ghidra-instruction-pcode");
953
+ returnEvidence.addProperty("value", operation.getMnemonic());
954
+ flowEvidence.add(returnEvidence);
955
+ }
956
+ flow.addProperty("kind", type.isCall() ? "call" : type.isJump() ? "jump" : decodedReturn ? "return" : type.isTerminal() ? "terminal" : "fallthrough");
957
+ flow.addProperty("conditional", type.isConditional());
958
+ flow.addProperty("computed", type.isComputed());
959
+ if (!type.isComputed()) for (Address target : instruction.getFlows()) destinations.add(canonicalAddress(target));
960
+ for (Reference reference : instruction.getReferencesFrom()) references.add(instructionReference(reference));
961
+ for (int index = 0; index < instruction.getNumOperands(); index++) {
962
+ JsonObject operand = new JsonObject();
963
+ operand.addProperty("index", index);
964
+ operand.addProperty("raw", instruction.getDefaultOperandRepresentation(index));
965
+ operand.addProperty("provider_type", instruction.getOperandType(index));
966
+ operand.addProperty("memory_addressing", "unavailable");
967
+ JsonArray components = new JsonArray();
968
+ for (Object object : instruction.getOpObjects(index)) {
969
+ JsonObject component = new JsonObject();
970
+ component.addProperty("text", object.toString());
971
+ component.add("value", JsonNull.INSTANCE);
972
+ component.add("bit_width", JsonNull.INSTANCE);
973
+ if (object instanceof Register register) {
974
+ component.addProperty("kind", "register");
975
+ component.addProperty("value", register.getName());
976
+ component.addProperty("bit_width", register.getBitLength());
977
+ } else if (object instanceof Scalar scalar) {
978
+ component.addProperty("kind", "immediate");
979
+ component.addProperty("value", "0x" + Long.toUnsignedString(scalar.getUnsignedValue(), 16));
980
+ component.addProperty("bit_width", scalar.bitLength());
981
+ } else if (object instanceof Address target) {
982
+ component.addProperty("kind", "address");
983
+ component.addProperty("value", canonicalAddress(target));
984
+ } else component.addProperty("kind", "unknown");
985
+ components.add(component);
986
+ }
987
+ operand.add("components", components);
988
+ operands.add(operand);
989
+ }
990
+ }
991
+ flow.add("direct_destinations", destinations);
992
+ flow.add("classification_evidence", flowEvidence);
993
+ result.add("operands", operands);
994
+ result.add("flow", flow);
995
+ result.add("references", references);
996
+ if (includeIncoming) {
997
+ TreeMap<String, Reference> incoming = new TreeMap<>();
998
+ ReferenceIterator incomingIterator = currentProgram.getReferenceManager().getReferencesTo(address);
999
+ while (incomingIterator.hasNext()) {
1000
+ monitor.checkCancelled();
1001
+ addReference(incoming, incomingIterator.next());
1002
+ }
1003
+ List<Reference> orderedIncoming = new ArrayList<>(incoming.values());
1004
+ orderedIncoming.sort(REFERENCE_ORDER);
1005
+ JsonArray incomingReferences = new JsonArray();
1006
+ for (Reference reference : orderedIncoming) incomingReferences.add(instructionReference(reference));
1007
+ result.add("incoming_references", incomingReferences);
1008
+ }
1009
+ 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."));
1010
+ 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.");
1011
+ result.add("limitations", GSON.toJsonTree(limitations));
1012
+ return result;
1013
+ }
1014
+
1015
+ private JsonObject resolveNativeCallTargets(JsonObject params) throws Exception {
1016
+ JsonObject instruction = inspectNativeInstruction(params, false);
1017
+ JsonObject result = new JsonObject();
1018
+ result.add("call_site", instruction.get("address"));
1019
+ result.add("procedure", instruction.get("procedure"));
1020
+ JsonArray targets = new JsonArray();
1021
+ JsonObject flow = instruction.getAsJsonObject("flow");
1022
+ boolean decoded = instruction.get("status").getAsString().equals("decoded");
1023
+ boolean call = flow.get("kind").getAsString().equals("call");
1024
+ boolean computed = flow.get("computed").getAsBoolean();
1025
+ Map<String, JsonArray> byTarget = new TreeMap<>();
1026
+ if (call) for (JsonElement element : instruction.getAsJsonArray("references")) {
1027
+ JsonObject reference = element.getAsJsonObject();
1028
+ if (reference.get("call").getAsBoolean())
1029
+ byTarget.computeIfAbsent(reference.get("target_address").getAsString(), ignored -> new JsonArray()).add(reference);
1030
+ }
1031
+ boolean ambiguous = byTarget.size() > 1;
1032
+ for (Map.Entry<String, JsonArray> entry : byTarget.entrySet()) {
1033
+ JsonObject target = new JsonObject();
1034
+ target.addProperty("address", entry.getKey());
1035
+ Address address = tryParseAddress(entry.getKey());
1036
+ Function function = address == null ? null : currentProgram.getFunctionManager().getFunctionAt(address);
1037
+ target.add("procedure", function == null ? JsonNull.INSTANCE : GSON.toJsonTree(canonicalAddress(function.getEntryPoint())));
1038
+ target.addProperty("status", ambiguous ? "candidate" : computed ? "resolved-indirect" : "direct");
1039
+ target.addProperty("basis", "provider-reference");
1040
+ target.add("references", entry.getValue());
1041
+ targets.add(target);
1042
+ }
1043
+ result.addProperty("status", !decoded ? "unavailable" : !call ? "not-call" : ambiguous ? "ambiguous" : targets.size() == 0 ? "unresolved" : computed ? "resolved-indirect" : "direct");
1044
+ result.addProperty("mechanism", !call ? "unavailable" : computed ? "computed" : "direct");
1045
+ result.add("targets", targets);
1046
+ result.add("limitations", GSON.toJsonTree(List.of("Targets are static Ghidra ReferenceManager call-reference observations. Computed calls without call references remain unresolved; a resolved reference is not proof of runtime execution.", "Objective-C, Swift, vtable and closure mechanisms are not identified by this reference-only boundary.")));
1047
+ return result;
1048
+ }
1049
+
1050
+ private JsonArray xrefs(JsonObject params) throws Exception {
1051
+ requireKeys(params, Set.of("document", "address"));
1052
+ requireDocument(params);
1053
+ Address address = requireAddress(params, "address");
1054
+ ReferenceIterator references = currentProgram.getReferenceManager().getReferencesTo(address);
1055
+ Set<Address> sources = new HashSet<>();
1056
+ while (references.hasNext()) {
1057
+ monitor.checkCancelled();
1058
+ Reference reference = references.next();
1059
+ if (reference.isEntryPointReference()) {
1060
+ continue;
1061
+ }
1062
+ sources.add(reference.getFromAddress());
1063
+ }
1064
+ List<Address> ordered = new ArrayList<>(sources);
1065
+ ordered.sort(Address::compareTo);
1066
+ JsonArray result = new JsonArray();
1067
+ for (Address source : ordered) {
1068
+ result.add(canonicalAddress(source));
1069
+ }
1070
+ return result;
1071
+ }
1072
+
1073
+ private void invalidateAnalysisCaches() {
1074
+ nameInventory = null;
1075
+ procedureInventory = null;
1076
+ stringInventory = null;
1077
+ if (decompiler != null) decompiler.flushCache();
1078
+ }
1079
+
1080
+ private JsonObject annotationReadback(Function function) {
1081
+ Address entry = function.getEntryPoint();
1082
+ JsonObject value = new JsonObject();
1083
+ value.addProperty("address", canonicalAddress(entry));
1084
+ value.addProperty("name", procedureName(function));
1085
+ value.addProperty("comment", currentProgram.getListing().getComment(CommentType.PRE, entry));
1086
+ value.addProperty("inline_comment", currentProgram.getListing().getComment(CommentType.EOL, entry));
1087
+ return value;
1088
+ }
1089
+
1090
+ private JsonObject annotateNativeFunction(JsonObject params) throws Exception {
1091
+ if (!Set.of("procedure", "name", "comment", "inline_comment").containsAll(params.keySet()))
1092
+ throw new RequestFailure("invalid_request", "Unknown function annotation field");
1093
+ if (!params.has("name") && !params.has("comment") && !params.has("inline_comment"))
1094
+ throw new RequestFailure("invalid_request", "Supply at least one annotation change");
1095
+ Function function = resolveProcedure(requireString(params, "procedure"));
1096
+ Address entry = function.getEntryPoint();
1097
+ if (function.isExternal() || !currentProgram.getMemory().contains(entry))
1098
+ throw new RequestFailure("invalid_request", "Annotations require a local function entry: " + canonicalAddress(entry));
1099
+ for (String field : List.of("name", "comment", "inline_comment")) {
1100
+ if (params.has(field)) validateAnnotationText(requireText(params, field), field);
1101
+ }
1102
+ int transaction = currentProgram.startTransaction("REA function annotations");
1103
+ boolean commit = false;
1104
+ try {
1105
+ String leafName = params.has("name")
1106
+ ? symbolLeafName(function.getSymbol(), requireString(params, "name")) : null;
1107
+ // Validate edits through Ghidra's own setters, including name rules.
1108
+ // A later invalid name must roll back earlier comment writes.
1109
+ for (String field : List.of("comment", "inline_comment")) {
1110
+ if (params.has(field)) {
1111
+ String text = requireText(params, field);
1112
+ CommentType type = field.equals("comment") ? CommentType.PRE : CommentType.EOL;
1113
+ currentProgram.getListing().setComment(entry, type, text.isEmpty() ? null : text);
1114
+ }
1115
+ }
1116
+ if (params.has("name"))
1117
+ function.setName(leafName, SourceType.USER_DEFINED);
1118
+ monitor.checkCancelled();
1119
+ invalidateAnalysisCaches();
1120
+ JsonObject readback = annotationReadback(function);
1121
+ for (String field : List.of("name", "comment", "inline_comment")) {
1122
+ if (!params.has(field)) continue;
1123
+ String requested = requireText(params, field);
1124
+ JsonElement measured = readback.get(field);
1125
+ String expected = field.equals("name") ? leafName : requested.isEmpty() ? null : requested;
1126
+ String observed = field.equals("name") ? function.getName() :
1127
+ (measured.isJsonNull() ? null : measured.getAsString());
1128
+ if (!java.util.Objects.equals(expected, observed))
1129
+ throw new RequestFailure("annotation_readback_mismatch", "Annotation readback differs for " + field + " at " + canonicalAddress(entry));
1130
+ }
1131
+ JsonObject query = new JsonObject();
1132
+ query.addProperty("procedure", canonicalAddress(entry));
1133
+ JsonObject result = new JsonObject();
1134
+ result.add("annotations", readback);
1135
+ result.add("dossier", analyzeFunction(query));
1136
+ JsonObject effects = new JsonObject();
1137
+ effects.addProperty("scope", "session-analysis-database");
1138
+ effects.addProperty("source_bytes_modified", false);
1139
+ effects.addProperty("persists_after_close", false);
1140
+ result.add("effects", effects);
1141
+ monitor.checkCancelled();
1142
+ commit = true;
1143
+ return result;
1144
+ }
1145
+ catch (ghidra.util.exception.InvalidInputException | ghidra.util.exception.DuplicateNameException exception) {
1146
+ throw new RequestFailure("invalid_function_name", "Invalid function name at " + canonicalAddress(entry) + ": " + safeMessage(exception));
1147
+ }
1148
+ finally {
1149
+ currentProgram.endTransaction(transaction, commit);
1150
+ // Rollback also invalidates inventory and decompiler views.
1151
+ invalidateAnalysisCaches();
1152
+ }
1153
+ }
1154
+
1155
+ private String symbolLeafName(Symbol symbol, String requested) throws ghidra.util.exception.InvalidInputException {
1156
+ // Fully qualified readback must be usable as an idempotent rename input.
1157
+ if (requested.equals(symbol.getName(true))) return symbol.getName();
1158
+ String namespace = symbol.getParentNamespace().isGlobal()
1159
+ ? "" : symbol.getParentNamespace().getName(true);
1160
+ String prefix = namespace + "::";
1161
+ if (!namespace.isEmpty() && requested.startsWith(prefix)) {
1162
+ String leaf = requested.substring(prefix.length());
1163
+ if (!leaf.isEmpty()) return leaf;
1164
+ throw new ghidra.util.exception.InvalidInputException(
1165
+ "Symbol name has an empty leaf within namespace " + namespace + " at " +
1166
+ canonicalAddress(symbol.getAddress()) + ": " + requested);
1167
+ }
1168
+ return requested;
1169
+ }
1170
+
1171
+ private static String requireText(JsonObject params, String field) {
1172
+ JsonElement value = params.get(field);
1173
+ if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString())
1174
+ throw new RequestFailure("invalid_request", "Annotation " + field + " must be text");
1175
+ return value.getAsString();
1176
+ }
1177
+
1178
+ private static void validateAnnotationText(String text, String field) {
1179
+ for (int index = 0; index < text.length(); index++) {
1180
+ char character = text.charAt(index);
1181
+ if (character == 0)
1182
+ throw new RequestFailure("invalid_request", "Annotation " + field + " contains NUL at UTF-16 index " + index + "; Ghidra's decompiler cannot marshal NUL text");
1183
+ if (Character.isHighSurrogate(character) && index + 1 < text.length() &&
1184
+ Character.isLowSurrogate(text.charAt(index + 1))) {
1185
+ index++;
1186
+ continue;
1187
+ }
1188
+ if (Character.isSurrogate(character))
1189
+ throw new RequestFailure("invalid_request", "Annotation " + field + " contains an unpaired Unicode surrogate at UTF-16 index " + index + "; Ghidra cannot preserve it as annotation text");
1190
+ }
1191
+ }
1192
+
1193
+ private JsonObject analyzeFunction(JsonObject params) throws Exception {
1194
+ return analyzeFunctionObservation(params).dossier();
1195
+ }
1196
+
1197
+ private FunctionAnalysisObservation analyzeFunctionObservation(JsonObject params) throws Exception {
1198
+ requireKeys(params, Set.of("procedure"));
1199
+ Function function = resolveProcedure(requireString(params, "procedure"));
1200
+ InstructionScan scan = scanInstructions(function, Integer.MAX_VALUE);
1201
+ DecompileResults decompilation = decompile(function);
1202
+ String pseudocode = decompiledText(decompilation);
1203
+ if (pseudocode == null) {
1204
+ pseudocode = "";
1205
+ }
1206
+ List<Reference> incomingReferences = collectReferences(function, "incoming")
1207
+ .stream()
1208
+ .filter(reference -> !function.getBody().contains(reference.getFromAddress()))
1209
+ .toList();
1210
+ List<Reference> outgoingReferences = collectReferences(function, "outgoing");
1211
+ JsonArray incoming = referenceEdges(incomingReferences);
1212
+ JsonArray outgoing = referenceEdges(outgoingReferences);
1213
+ JsonArray comments = comments(scan.instructions);
1214
+ JsonArray callers = procedureIdentities(function.getCallingFunctions(monitor));
1215
+ JsonArray callees = procedureIdentities(function.getCalledFunctions(monitor));
1216
+ JsonArray referencedStrings = referencedStrings(outgoingReferences);
1217
+ JsonArray referencedNames = referencedNames(outgoingReferences);
1218
+ JsonArray blocks = basicBlockValues(function);
1219
+ JsonArray assembly = GSON.toJsonTree(renderAssemblyLines(scan.instructions))
1220
+ .getAsJsonArray();
1221
+
1222
+ JsonObject procedure = procedureIdentity(function);
1223
+ procedure.addProperty("signature", function.getPrototypeString(false, true));
1224
+ procedure.add("locals", functionLocals(function));
1225
+ JsonObject result = new JsonObject();
1226
+ result.add("procedure", procedure);
1227
+ result.addProperty("pseudocode", pseudocode);
1228
+ result.add("assembly", assembly);
1229
+ result.add("comments", comments);
1230
+ result.add("callers", callers);
1231
+ result.add("callees", callees);
1232
+ result.add("incoming_references", incoming);
1233
+ result.add("outgoing_references", outgoing);
1234
+ result.add("referenced_strings", referencedStrings);
1235
+ result.add("referenced_names", referencedNames);
1236
+ result.add("basic_blocks", blocks);
1237
+ result.add(
1238
+ "native_api",
1239
+ nativeApiBoundary(function, decompilation)
1240
+ );
1241
+ result.add("native_value_flow", nativeValueFlow(decompilation));
1242
+ JsonArray limitations = new JsonArray();
1243
+ limitations.add(
1244
+ "Unresolved computed or indirect flows without target addresses are not represented as reference edges."
1245
+ );
1246
+ limitations.add(
1247
+ "Thunk and external classifications are Ghidra FunctionManager observations; they do not resolve targetless calls."
1248
+ );
1249
+ limitations.add(
1250
+ "Pseudocode and assembly are Ghidra-specific representations, not original source or Hopper-equivalent text."
1251
+ );
1252
+ limitations.add(
1253
+ "Synthetic Ghidra entry-point references without actionable memory sources are omitted."
1254
+ );
1255
+ List<String> boundaryLimitations = noReturnCallLimitations(scan.instructions);
1256
+ for (String limitation : boundaryLimitations) {
1257
+ limitations.add(limitation);
1258
+ }
1259
+ if (decompilation != null && decompilation.getErrorMessage() != null &&
1260
+ !decompilation.getErrorMessage().isBlank()) {
1261
+ limitations.add("Ghidra decompiler diagnostic: " + decompilation.getErrorMessage());
1262
+ }
1263
+ result.add("limitations", limitations);
1264
+ return new FunctionAnalysisObservation(result, boundaryLimitations);
1265
+ }
1266
+
1267
+ private JsonObject nativeValueFlow(DecompileResults decompilation) throws Exception {
1268
+ HighFunction highFunction =
1269
+ decompilation == null ? null : decompilation.getHighFunction();
1270
+ if (highFunction == null) {
1271
+ JsonObject unavailable = new JsonObject();
1272
+ unavailable.addProperty("available", false);
1273
+ unavailable.addProperty(
1274
+ "reason",
1275
+ "Ghidra has no HighFunction p-code model for this procedure"
1276
+ );
1277
+ JsonArray limitations = new JsonArray();
1278
+ limitations.add(
1279
+ "No p-code operations or def-use relationships were recovered for this procedure."
1280
+ );
1281
+ unavailable.add("limitations", limitations);
1282
+ return unavailable;
1283
+ }
1284
+
1285
+ List<PcodeOpAST> selected = new ArrayList<>();
1286
+ int omittedOperationsLowerBound = 0;
1287
+ Iterator<PcodeOpAST> operations = highFunction.getPcodeOps();
1288
+ while (operations.hasNext()) {
1289
+ monitor.checkCancelled();
1290
+ PcodeOpAST operation = operations.next();
1291
+ if (selected.size() >= MAX_HIGH_PCODE_OPS) {
1292
+ omittedOperationsLowerBound = 1;
1293
+ break;
1294
+ }
1295
+ selected.add(operation);
1296
+ }
1297
+
1298
+ Map<PcodeOpAST, String> operationIds = new IdentityHashMap<>();
1299
+ for (PcodeOpAST operation : selected) {
1300
+ operationIds.put(operation, pcodeOperationId(operation));
1301
+ }
1302
+ JsonArray parameterUses = new JsonArray();
1303
+ JsonArray parameterValues = new JsonArray();
1304
+ FunctionPrototype prototype = highFunction.getFunctionPrototype();
1305
+ for (int ordinal = 0; ordinal < prototype.getNumParams(); ordinal++) {
1306
+ HighSymbol parameter = prototype.getParam(ordinal);
1307
+ if (parameter == null) continue;
1308
+ JsonObject value = new JsonObject();
1309
+ value.addProperty("ordinal", ordinal);
1310
+ value.addProperty("name", parameter.getName());
1311
+ value.addProperty("data_type", parameter.getDataType().getPathName());
1312
+ parameterValues.add(value);
1313
+ }
1314
+ JsonArray operationValues = new JsonArray();
1315
+ JsonArray defUse = new JsonArray();
1316
+ JsonArray effects = new JsonArray();
1317
+ int omittedInputs = 0;
1318
+ int omittedEdges = 0;
1319
+ for (PcodeOpAST operation : selected) {
1320
+ monitor.checkCancelled();
1321
+ String operationId = operationIds.get(operation);
1322
+ JsonObject value = new JsonObject();
1323
+ value.addProperty("id", operationId);
1324
+ value.addProperty(
1325
+ "address",
1326
+ canonicalAddress(operation.getSeqnum().getTarget())
1327
+ );
1328
+ value.addProperty("sequence", operation.getSeqnum().getTime());
1329
+ value.addProperty("opcode", operation.getMnemonic());
1330
+ value.addProperty("is_dead", operation.isDead());
1331
+ value.addProperty("block_membership", operation.getParent() == null ? "detached" : "member");
1332
+ JsonArray inputs = new JsonArray();
1333
+ int inputCount = operation.getNumInputs();
1334
+ int capturedInputs = Math.min(inputCount, MAX_HIGH_PCODE_INPUTS_PER_OP);
1335
+ for (int inputIndex = 0; inputIndex < capturedInputs; inputIndex += 1) {
1336
+ Varnode input = operation.getInput(inputIndex);
1337
+ inputs.add(varnodeValue(input));
1338
+ if (input instanceof VarnodeAST astInput) {
1339
+ if (astInput.getHigh() instanceof HighParam parameter) {
1340
+ if (parameterUses.size() >= MAX_HIGH_PCODE_DEF_USE_EDGES) omittedEdges++;
1341
+ else {
1342
+ JsonObject use = new JsonObject();
1343
+ use.addProperty("ordinal", parameter.getSlot());
1344
+ use.addProperty("use", operationId);
1345
+ use.addProperty("input_index", inputIndex);
1346
+ parameterUses.add(use);
1347
+ }
1348
+ }
1349
+ PcodeOp definition = astInput.getDef();
1350
+ if (definition instanceof PcodeOpAST astDefinition) {
1351
+ String definitionId = operationIds.get(astDefinition);
1352
+ if (definitionId == null || defUse.size() >= MAX_HIGH_PCODE_DEF_USE_EDGES) {
1353
+ omittedEdges += 1;
1354
+ } else {
1355
+ JsonObject edge = new JsonObject();
1356
+ edge.addProperty("definition", definitionId);
1357
+ edge.addProperty("use", operationId);
1358
+ edge.addProperty("input_index", inputIndex);
1359
+ defUse.add(edge);
1360
+ }
1361
+ }
1362
+ }
1363
+ }
1364
+ omittedInputs += inputCount - capturedInputs;
1365
+ value.add("inputs", inputs);
1366
+ Varnode output = operation.getOutput();
1367
+ value.add("output", output == null ? JsonNull.INSTANCE : varnodeValue(output));
1368
+ operationValues.add(value);
1369
+ addPcodeEffect(effects, operation, operationId);
1370
+ }
1371
+
1372
+ JsonObject result = new JsonObject();
1373
+ result.addProperty("available", true);
1374
+ result.addProperty("provenance", "ghidra-high-pcode");
1375
+ result.add("operations", operationValues);
1376
+ result.add("def_use", defUse);
1377
+ result.add("parameters", parameterValues);
1378
+ result.add("parameter_uses", parameterUses);
1379
+ result.add("effects", effects);
1380
+ boolean truncated = omittedOperationsLowerBound > 0 ||
1381
+ omittedInputs > 0 || omittedEdges > 0;
1382
+ result.addProperty("truncated", truncated);
1383
+ result.addProperty(
1384
+ "omitted_operations_lower_bound",
1385
+ omittedOperationsLowerBound
1386
+ );
1387
+ result.addProperty("known_omitted_inputs", omittedInputs);
1388
+ result.addProperty("known_omitted_edges", omittedEdges);
1389
+ JsonArray limitations = new JsonArray();
1390
+ limitations.add(
1391
+ "High p-code is decompiler-derived, not original source or runtime behavior. is_dead preserves the Java AST flag; decoded block members can retain that flag, so block_membership separately reports actual syntax-tree membership."
1392
+ );
1393
+ limitations.add(
1394
+ "Def-use links do not cross function boundaries and may omit memory aliasing, call side effects, or unresolved indirect flow."
1395
+ );
1396
+ limitations.add(
1397
+ "Memory read/write effects include stack and temporary memory operations; they do not identify persistent state or business meaning."
1398
+ );
1399
+ limitations.add(
1400
+ "Operation, input, and def-use limits can truncate large functions; inspect truncation, omitted lower bounds, and known omitted counts before drawing conclusions."
1401
+ );
1402
+ limitations.add(
1403
+ "Known omitted-edge counts cover links omitted from retained operations; omitted operations may contain additional uncounted links."
1404
+ );
1405
+ result.add("limitations", limitations);
1406
+ return result;
1407
+ }
1408
+
1409
+ private String pcodeOperationId(PcodeOpAST operation) {
1410
+ return canonicalAddress(operation.getSeqnum().getTarget()) + "#" +
1411
+ operation.getSeqnum().getTime();
1412
+ }
1413
+
1414
+ private JsonObject varnodeValue(Varnode varnode) {
1415
+ JsonObject result = new JsonObject();
1416
+ String kind = varnode.isConstant()
1417
+ ? "constant"
1418
+ : varnode.isAddress()
1419
+ ? "address"
1420
+ : varnode.isRegister()
1421
+ ? "register"
1422
+ : varnode.isUnique() ? "unique" : "other";
1423
+ result.addProperty("kind", kind);
1424
+ result.addProperty("size_bytes", varnode.getSize());
1425
+ result.add(
1426
+ "location",
1427
+ varnode.isConstant()
1428
+ ? JsonNull.INSTANCE
1429
+ : GSON.toJsonTree(canonicalAddress(varnode.getAddress()))
1430
+ );
1431
+ result.add(
1432
+ "constant_hex",
1433
+ varnode.isConstant()
1434
+ ? GSON.toJsonTree(Long.toUnsignedString(varnode.getOffset(), 16))
1435
+ : JsonNull.INSTANCE
1436
+ );
1437
+ return result;
1438
+ }
1439
+
1440
+ private void addPcodeEffect(
1441
+ JsonArray destination,
1442
+ PcodeOp operation,
1443
+ String operationId
1444
+ ) {
1445
+ String kind = null;
1446
+ Integer operandInput = null;
1447
+ Integer valueInput = null;
1448
+ switch (operation.getOpcode()) {
1449
+ case PcodeOp.LOAD -> {
1450
+ kind = "memory_read";
1451
+ operandInput = 1;
1452
+ }
1453
+ case PcodeOp.STORE -> {
1454
+ kind = "memory_write";
1455
+ operandInput = 1;
1456
+ valueInput = 2;
1457
+ }
1458
+ case PcodeOp.CBRANCH -> {
1459
+ kind = "conditional_branch";
1460
+ valueInput = 1;
1461
+ }
1462
+ case PcodeOp.BRANCHIND -> {
1463
+ kind = "indirect_branch";
1464
+ operandInput = 0;
1465
+ }
1466
+ case PcodeOp.CALL -> {
1467
+ kind = "direct_call";
1468
+ operandInput = 0;
1469
+ }
1470
+ case PcodeOp.CALLIND -> {
1471
+ kind = "indirect_call";
1472
+ operandInput = 0;
1473
+ }
1474
+ default -> { }
1475
+ }
1476
+ if (kind == null) return;
1477
+ JsonObject effect = new JsonObject();
1478
+ effect.addProperty("operation", operationId);
1479
+ effect.addProperty("kind", kind);
1480
+ effect.add(
1481
+ "operand_input",
1482
+ operandInput == null ? JsonNull.INSTANCE : GSON.toJsonTree(operandInput)
1483
+ );
1484
+ effect.add(
1485
+ "value_input",
1486
+ valueInput == null ? JsonNull.INSTANCE : GSON.toJsonTree(valueInput)
1487
+ );
1488
+ destination.add(effect);
1489
+ }
1490
+
1491
+ private JsonElement addressName(JsonObject params) {
1492
+ requireKeys(params, Set.of("document", "address"));
1493
+ requireDocument(params);
1494
+ Address address = requireAddress(params, "address");
1495
+ Symbol symbol = currentProgram.getSymbolTable().getPrimarySymbol(address);
1496
+ return symbol == null ? JsonNull.INSTANCE : GSON.toJsonTree(symbol.getName(true));
1497
+ }
1498
+
1499
+ // Analyst naming: a function entry renames the function, other addresses get a primary label.
1500
+ private boolean applyAddressName(Address address, String name) throws Exception {
1501
+ if (!currentProgram.getMemory().contains(address))
1502
+ throw new RequestFailure("invalid_request", "Address is not mapped: " + canonicalAddress(address));
1503
+ validateAnnotationText(name, "name");
1504
+ int transaction = currentProgram.startTransaction("REA address name");
1505
+ boolean commit = false;
1506
+ try {
1507
+ Function function = currentProgram.getFunctionManager().getFunctionAt(address);
1508
+ SymbolTable table = currentProgram.getSymbolTable();
1509
+ Symbol primary = function == null ? table.getPrimarySymbol(address) : function.getSymbol();
1510
+ Namespace namespace = primary == null
1511
+ ? currentProgram.getGlobalNamespace()
1512
+ : primary.getParentNamespace();
1513
+ String expected = primary == null ? name : symbolLeafName(primary, name);
1514
+ for (Symbol symbol : table.getLabelOrFunctionSymbols(expected, namespace)) {
1515
+ if (!address.equals(symbol.getAddress())) return false;
1516
+ }
1517
+ if (function != null) {
1518
+ function.setName(expected, SourceType.USER_DEFINED);
1519
+ } else {
1520
+ if (primary != null && !primary.isDynamic()) {
1521
+ primary.setName(expected, SourceType.USER_DEFINED);
1522
+ } else {
1523
+ Symbol created = table.createLabel(address, expected, SourceType.USER_DEFINED);
1524
+ created.setPrimary();
1525
+ }
1526
+ }
1527
+ invalidateAnalysisCaches();
1528
+ Symbol readback = currentProgram.getSymbolTable().getPrimarySymbol(address);
1529
+ commit = readback != null && expected.equals(readback.getName());
1530
+ return commit;
1531
+ }
1532
+ catch (ghidra.util.exception.InvalidInputException | ghidra.util.exception.DuplicateNameException exception) {
1533
+ return false;
1534
+ }
1535
+ finally {
1536
+ currentProgram.endTransaction(transaction, commit);
1537
+ }
1538
+ }
1539
+
1540
+ private JsonElement setAddressName(JsonObject params) throws Exception {
1541
+ requireKeys(params, Set.of("document", "address", "name"));
1542
+ requireDocument(params);
1543
+ return GSON.toJsonTree(applyAddressName(requireAddress(params, "address"), requireString(params, "name")));
1544
+ }
1545
+
1546
+ private JsonElement setAddressesNames(JsonObject params) throws Exception {
1547
+ requireKeys(params, Set.of("document", "names"));
1548
+ requireDocument(params);
1549
+ if (!params.has("names") || !params.get("names").isJsonObject())
1550
+ throw new RequestFailure("invalid_request", "names must be an object of address -> name");
1551
+ Map<Address, List<AddressNameRequest>> requestsByAddress = new LinkedHashMap<>();
1552
+ JsonObject result = new JsonObject();
1553
+ for (Map.Entry<String, JsonElement> entry : params.getAsJsonObject("names").entrySet()) {
1554
+ try {
1555
+ Address address = parseReaAddress(entry.getKey());
1556
+ requestsByAddress.computeIfAbsent(address, ignored -> new ArrayList<>())
1557
+ .add(new AddressNameRequest(entry.getKey(), address, entry.getValue().getAsString()));
1558
+ }
1559
+ catch (RequestFailure failure) {
1560
+ result.addProperty(entry.getKey(), false);
1561
+ }
1562
+ }
1563
+ for (List<AddressNameRequest> requests : requestsByAddress.values()) {
1564
+ String name = requests.get(0).name;
1565
+ boolean conflictingAliasNames = requests.stream()
1566
+ .anyMatch(request -> !request.name.equals(name));
1567
+ boolean success = false;
1568
+ if (!conflictingAliasNames) {
1569
+ try {
1570
+ success = applyAddressName(requests.get(0).address, name);
1571
+ }
1572
+ catch (RequestFailure failure) {
1573
+ // Keep per-address failures in the existing batch result shape.
1574
+ }
1575
+ }
1576
+ for (AddressNameRequest request : requests)
1577
+ result.addProperty(request.inputAddress, success);
1578
+ }
1579
+ return result;
1580
+ }
1581
+
1582
+ private JsonArray listDocuments(JsonObject params) {
1583
+ requireKeys(params, Set.of());
1584
+ JsonArray result = new JsonArray();
1585
+ result.add(currentProgram.getName());
1586
+ return result;
1587
+ }
1588
+
1589
+ private JsonArray listNames(JsonObject params) throws Exception {
1590
+ requireKeys(params, Set.of("document", "address"));
1591
+ requireDocument(params);
1592
+ List<InventoryItem> inventory = names();
1593
+ String requested = optionalString(params, "address");
1594
+ if (requested != null) {
1595
+ Address address = parseReaAddress(requested);
1596
+ inventory = inventory.stream()
1597
+ .filter(item -> item.address.equals(address))
1598
+ .toList();
1599
+ }
1600
+ return inventory(inventory, "symbol");
1601
+ }
1602
+
1603
+ private JsonArray listProcedures(JsonObject params) throws Exception {
1604
+ requireKeys(params, Set.of("document"));
1605
+ requireDocument(params);
1606
+ List<InventoryItem> inventory = procedures().stream()
1607
+ .map(FunctionEntry::item)
1608
+ .toList();
1609
+ return inventory(inventory, "procedure");
1610
+ }
1611
+
1612
+ private JsonArray listSegments(JsonObject params) {
1613
+ requireKeys(params, Set.of("document"));
1614
+ requireDocument(params);
1615
+ JsonArray result = new JsonArray();
1616
+ String imageBase = canonicalAddress(currentProgram.getImageBase());
1617
+ MemoryBlock[] blocks = currentProgram.getMemory().getBlocks();
1618
+ List<MemoryBlock> ordered = new ArrayList<>(List.of(blocks));
1619
+ ordered.sort(Comparator.comparing(MemoryBlock::getStart));
1620
+ for (MemoryBlock block : ordered) {
1621
+ JsonObject item = memoryRegion(block, imageBase);
1622
+ item.add("sections", new JsonArray());
1623
+ result.add(item);
1624
+ }
1625
+ return result;
1626
+ }
1627
+
1628
+ private JsonArray listStrings(JsonObject params) throws Exception {
1629
+ requireKeys(params, Set.of("document", "address"));
1630
+ requireDocument(params);
1631
+ List<InventoryItem> inventory = strings();
1632
+ String requested = optionalString(params, "address");
1633
+ if (requested != null) {
1634
+ Address address = parseReaAddress(requested);
1635
+ inventory = inventory.stream()
1636
+ .filter(item -> item.address.equals(address))
1637
+ .toList();
1638
+ }
1639
+ return inventory(inventory, "string");
1640
+ }
1641
+
1642
+ private JsonElement procedureAddress(JsonObject params) throws Exception {
1643
+ requireKeys(params, Set.of("document", "procedure"));
1644
+ requireDocument(params);
1645
+ return GSON.toJsonTree(canonicalAddress(resolveProcedure(requireString(params, "procedure")).getEntryPoint()));
1646
+ }
1647
+
1648
+ private JsonObject containingProcedure(JsonObject params) {
1649
+ requireKeys(params, Set.of("document", "address"));
1650
+ requireDocument(params);
1651
+ Address query = requireAddress(params, "address");
1652
+ Function function = currentProgram.getFunctionManager().getFunctionAt(query);
1653
+ if (function == null && query.isMemoryAddress()) {
1654
+ function = currentProgram.getFunctionManager().getFunctionContaining(query);
1655
+ }
1656
+ JsonObject result = new JsonObject();
1657
+ result.addProperty("query_address", canonicalAddress(query));
1658
+ if (function != null) {
1659
+ result.addProperty("found", true);
1660
+ result.add("procedure", procedureIdentity(function));
1661
+ return result;
1662
+ }
1663
+ result.addProperty("found", false);
1664
+ result.add("procedure", JsonNull.INSTANCE);
1665
+ boolean inside = query.isMemoryAddress() && currentProgram.getMemory().contains(query);
1666
+ result.addProperty("reason", inside ? "not_in_procedure" : "outside_segments");
1667
+ return result;
1668
+ }
1669
+
1670
+ private JsonArray search(JsonObject params, boolean procedureSearch) throws Exception {
1671
+ requireKeys(
1672
+ params,
1673
+ Set.of("pattern", "mode", "case_sensitive", "document")
1674
+ );
1675
+ requireDocument(params);
1676
+ String expression = requireString(params, "pattern");
1677
+ String mode = requireString(params, "mode");
1678
+ if (!mode.equals("literal") && !mode.equals("regex")) {
1679
+ throw new RequestFailure("invalid_request", "mode must be literal or regex");
1680
+ }
1681
+ boolean caseSensitive = requireBoolean(params, "case_sensitive");
1682
+ List<InventoryItem> inventory = procedureSearch
1683
+ ? procedures().stream().map(FunctionEntry::item).toList()
1684
+ : strings();
1685
+ ValueMatcher matcher = mode.equals("literal")
1686
+ ? literalMatcher(expression, caseSensitive)
1687
+ : regexMatcher(expression, caseSensitive);
1688
+ JsonArray items = new JsonArray();
1689
+ for (InventoryItem item : inventory) {
1690
+ monitor.checkCancelled();
1691
+ if (!matcher.matches(item.value)) {
1692
+ continue;
1693
+ }
1694
+ items.add(resultItem(item, null));
1695
+ }
1696
+ return items;
1697
+ }
1698
+
1699
+ private List<InventoryItem> names() throws Exception {
1700
+ if (nameInventory != null) {
1701
+ return nameInventory;
1702
+ }
1703
+ List<InventoryItem> result = new ArrayList<>();
1704
+ SymbolIterator symbols = currentProgram.getSymbolTable().getAllSymbols(true);
1705
+ while (symbols.hasNext()) {
1706
+ monitor.checkCancelled();
1707
+ Symbol symbol = symbols.next();
1708
+ Address address = symbol.getAddress();
1709
+ if (!address.isMemoryAddress() && !address.isExternalAddress()) {
1710
+ continue;
1711
+ }
1712
+ JsonObject facts = new JsonObject();
1713
+ facts.addProperty("primary", symbol.isPrimary());
1714
+ facts.addProperty("dynamic", symbol.isDynamic());
1715
+ facts.addProperty("external", symbol.isExternal());
1716
+ facts.addProperty("type", normalizedSymbolType(symbol));
1717
+ facts.addProperty("source", normalizedSource(symbol.getSource()));
1718
+ result.add(new InventoryItem(address, symbol.getName(true), facts));
1719
+ }
1720
+ result.sort(INVENTORY_ORDER);
1721
+ nameInventory = List.copyOf(result);
1722
+ return nameInventory;
1723
+ }
1724
+
1725
+ private List<FunctionEntry> procedures() throws Exception {
1726
+ if (procedureInventory != null) {
1727
+ return procedureInventory;
1728
+ }
1729
+ List<FunctionEntry> result = new ArrayList<>();
1730
+ Set<Address> seen = new HashSet<>();
1731
+ appendFunctions(result, seen, currentProgram.getFunctionManager().getFunctions(true));
1732
+ appendFunctions(result, seen, currentProgram.getFunctionManager().getExternalFunctions());
1733
+ result.sort((left, right) -> INVENTORY_ORDER.compare(left.item, right.item));
1734
+ procedureInventory = List.copyOf(result);
1735
+ return procedureInventory;
1736
+ }
1737
+
1738
+ private void appendFunctions(
1739
+ List<FunctionEntry> destination,
1740
+ Set<Address> seen,
1741
+ FunctionIterator functions) throws Exception {
1742
+ while (functions.hasNext()) {
1743
+ monitor.checkCancelled();
1744
+ Function function = functions.next();
1745
+ if (!seen.add(function.getEntryPoint())) {
1746
+ continue;
1747
+ }
1748
+ JsonObject facts = new JsonObject();
1749
+ facts.addProperty("external", function.isExternal());
1750
+ facts.addProperty("thunk", function.isThunk());
1751
+ Function target = function.isThunk() ? function.getThunkedFunction(false) : null;
1752
+ if (target == null) {
1753
+ facts.add("thunk_target", JsonNull.INSTANCE);
1754
+ }
1755
+ else {
1756
+ facts.addProperty("thunk_target", canonicalAddress(target.getEntryPoint()));
1757
+ }
1758
+ Symbol symbol = function.getSymbol();
1759
+ InventoryItem item = new InventoryItem(
1760
+ function.getEntryPoint(),
1761
+ symbol == null ? function.getName() : symbol.getName(true),
1762
+ facts
1763
+ );
1764
+ destination.add(new FunctionEntry(function, item));
1765
+ }
1766
+ }
1767
+
1768
+ private List<InventoryItem> strings() throws Exception {
1769
+ if (stringInventory != null) {
1770
+ return stringInventory;
1771
+ }
1772
+ List<InventoryItem> result = new ArrayList<>();
1773
+ DataIterator dataItems = currentProgram.getListing().getDefinedData(true);
1774
+ while (dataItems.hasNext()) {
1775
+ monitor.checkCancelled();
1776
+ Data data = dataItems.next();
1777
+ if (!data.hasStringValue()) {
1778
+ continue;
1779
+ }
1780
+ StringDataInstance instance = StringDataInstance.getStringDataInstance(data);
1781
+ String value = instance.getStringValue();
1782
+ if (value == null) {
1783
+ continue;
1784
+ }
1785
+ JsonObject facts = new JsonObject();
1786
+ String charset = instance.getCharsetName();
1787
+ facts.addProperty("encoding", charset == null || charset.isEmpty() ? "unknown" : charset);
1788
+ facts.addProperty(
1789
+ "termination",
1790
+ instance.isMissingNullTerminator() ? "missing" : "present_or_not_required"
1791
+ );
1792
+ facts.addProperty("byte_length", Math.max(0, data.getLength()));
1793
+ result.add(new InventoryItem(data.getAddress(), value, facts));
1794
+ }
1795
+ result.sort(INVENTORY_ORDER);
1796
+ stringInventory = List.copyOf(result);
1797
+ return stringInventory;
1798
+ }
1799
+
1800
+ private Function resolveProcedure(String value) throws Exception {
1801
+ // Explicit address syntax wins; an unprefixed identifier is first an
1802
+ // exact symbol name, even when every character is a hexadecimal digit.
1803
+ boolean explicitAddress = isExplicitReaAddress(value);
1804
+ if (!explicitAddress) {
1805
+ List<Function> matches = new ArrayList<>();
1806
+ for (FunctionEntry entry : procedures()) {
1807
+ monitor.checkCancelled();
1808
+ Function function = entry.function;
1809
+ Symbol symbol = function.getSymbol();
1810
+ String qualified = symbol == null ? function.getName() : symbol.getName(true);
1811
+ boolean matchesName = function.getName().equals(value) || qualified.equals(value);
1812
+ if (!matchesName) {
1813
+ // Imported labels (for example Mach-O _main) can share a
1814
+ // function entry without becoming its primary symbol. Only
1815
+ // exact entries qualify; an interior label is not a selector.
1816
+ for (Symbol entrySymbol : currentProgram.getSymbolTable().getSymbols(function.getEntryPoint())) {
1817
+ monitor.checkCancelled();
1818
+ if (entrySymbol.getName().equals(value) || entrySymbol.getName(true).equals(value)) {
1819
+ matchesName = true;
1820
+ break;
1821
+ }
1822
+ }
1823
+ }
1824
+ if (matchesName) matches.add(function);
1825
+ }
1826
+ if (matches.size() > 1)
1827
+ throw new RequestFailure("ambiguous", "Ghidra procedure name is ambiguous: " + value +
1828
+ "; select an exact entry address from " + matches.stream()
1829
+ .map(function -> canonicalAddress(function.getEntryPoint())).toList());
1830
+ if (matches.size() == 1) return matches.get(0);
1831
+ }
1832
+ // Retain legacy bare-hex address input only when no symbol matches.
1833
+ Address address = explicitAddress ? parseReaAddress(value) : tryParseAddress(value);
1834
+ if (address == null)
1835
+ throw new RequestFailure("not_found", "Unknown Ghidra procedure name or address: " + value);
1836
+ Function function = currentProgram.getFunctionManager().getFunctionAt(address);
1837
+ if (function == null && address.isMemoryAddress())
1838
+ function = currentProgram.getFunctionManager().getFunctionContaining(address);
1839
+ if (function == null)
1840
+ throw new RequestFailure("not_found", "No procedure exists at the requested address: " + value);
1841
+ return function;
1842
+ }
1843
+
1844
+ private JsonObject nativeApiBoundary(
1845
+ Function function,
1846
+ DecompileResults decompilation
1847
+ ) throws Exception {
1848
+ if (decompilation == null || decompilation.getHighFunction() == null) {
1849
+ return unavailableNativeApiBoundary(
1850
+ "Ghidra has no HighFunction model for an external or bodyless procedure"
1851
+ );
1852
+ }
1853
+
1854
+ HighFunction highFunction = decompilation.getHighFunction();
1855
+ FunctionPrototype prototype = highFunction.getFunctionPrototype();
1856
+ String signatureSource = function
1857
+ .getSignatureSource()
1858
+ .name()
1859
+ .toLowerCase(Locale.ROOT);
1860
+ JsonObject result = new JsonObject();
1861
+ result.addProperty("available", true);
1862
+ result.addProperty("provenance", "ghidra-high-function");
1863
+ result.addProperty("signature_source", signatureSource);
1864
+ String callingConvention = prototype.getModelName();
1865
+ result.addProperty(
1866
+ "calling_convention",
1867
+ callingConvention == null || callingConvention.isBlank()
1868
+ ? "unknown"
1869
+ : callingConvention
1870
+ );
1871
+ result.add(
1872
+ "return_type",
1873
+ inferredBoundaryType(
1874
+ "return",
1875
+ null,
1876
+ null,
1877
+ prototype.getReturnType(),
1878
+ prototype.getReturnStorage().toString(),
1879
+ false,
1880
+ signatureSource
1881
+ )
1882
+ );
1883
+
1884
+ JsonArray parameters = new JsonArray();
1885
+ result.add("parameters", parameters);
1886
+
1887
+ JsonArray jumpTables = new JsonArray();
1888
+ result.add("jump_tables", jumpTables);
1889
+
1890
+ JsonObject pseudocode = new JsonObject();
1891
+ pseudocode.addProperty("classification", "decompiler-generated-non-source");
1892
+ pseudocode.addProperty("compilable", false);
1893
+ result.add("pseudocode", pseudocode);
1894
+
1895
+ JsonArray artifacts = new JsonArray();
1896
+ artifacts.add("recovered-signature");
1897
+ artifacts.add("register-and-stack-variables");
1898
+ artifacts.add("compiler-generated-control-flow");
1899
+ artifacts.add("pointer-arithmetic");
1900
+ artifacts.add("pseudocode");
1901
+ result.add("decompiler_artifacts", artifacts);
1902
+
1903
+ JsonArray limitations = new JsonArray();
1904
+ limitations.add(
1905
+ "Types and calling conventions are Ghidra decompiler observations, not source declarations."
1906
+ );
1907
+ limitations.add(
1908
+ "Register variables, compiler-generated blocks, and pointer arithmetic remain decompiler artifacts."
1909
+ );
1910
+ limitations.add(
1911
+ "Pseudocode is neither original source nor guaranteed to compile."
1912
+ );
1913
+ result.add("limitations", limitations);
1914
+ for (int index = 0; index < prototype.getNumParams(); index += 1) {
1915
+ HighSymbol parameter = prototype.getParam(index);
1916
+ if (parameter == null) {
1917
+ continue;
1918
+ }
1919
+ parameters.add(
1920
+ inferredBoundaryType(
1921
+ "parameter",
1922
+ index,
1923
+ parameter.getName(),
1924
+ parameter.getDataType(),
1925
+ parameter.getStorage().toString(),
1926
+ parameter.isTypeLocked(),
1927
+ signatureSource
1928
+ )
1929
+ );
1930
+ }
1931
+
1932
+ JumpTable[] recoveredJumpTables = highFunction.getJumpTables();
1933
+ List<ClangNode> caseTokens = new ArrayList<>();
1934
+ if (decompilation.getCCodeMarkup() != null) {
1935
+ decompilation.getCCodeMarkup().flatten(caseTokens);
1936
+ }
1937
+ for (int index = 0; index < recoveredJumpTables.length; index += 1) {
1938
+ jumpTables.add(inferredJumpTable(recoveredJumpTables[index], caseTokens));
1939
+ }
1940
+ return result;
1941
+ }
1942
+
1943
+ private static JsonObject unavailableNativeApiBoundary(String reason) {
1944
+ JsonObject unavailable = new JsonObject();
1945
+ unavailable.addProperty("available", false);
1946
+ unavailable.addProperty("reason", reason);
1947
+ JsonArray unknowns = new JsonArray();
1948
+ unknowns.add("Return and parameter boundary types are unavailable.");
1949
+ unknowns.add("Jump-table mappings are unavailable.");
1950
+ unavailable.add("residual_unknowns", unknowns);
1951
+ return unavailable;
1952
+ }
1953
+
1954
+ private static JsonObject inferredBoundaryType(
1955
+ String role,
1956
+ Integer ordinal,
1957
+ String name,
1958
+ DataType dataType,
1959
+ String storage,
1960
+ boolean typeLocked,
1961
+ String signatureSource
1962
+ ) {
1963
+ JsonObject result = new JsonObject();
1964
+ result.addProperty("role", role);
1965
+ if (ordinal == null) {
1966
+ result.add("ordinal", JsonNull.INSTANCE);
1967
+ }
1968
+ else {
1969
+ result.addProperty("ordinal", ordinal);
1970
+ }
1971
+ if (name == null || name.isBlank()) {
1972
+ result.add("name", JsonNull.INSTANCE);
1973
+ }
1974
+ else {
1975
+ result.addProperty("name", name);
1976
+ }
1977
+ result.addProperty("data_type", dataType.getDisplayName());
1978
+ int dataTypeLength = dataType.getLength();
1979
+ if (dataTypeLength < 0) {
1980
+ result.add("size_bytes", JsonNull.INSTANCE);
1981
+ }
1982
+ else {
1983
+ result.addProperty("size_bytes", dataTypeLength);
1984
+ }
1985
+ if (storage.isBlank()) {
1986
+ result.add("storage", JsonNull.INSTANCE);
1987
+ }
1988
+ else {
1989
+ result.addProperty("storage", storage);
1990
+ }
1991
+ result.addProperty(
1992
+ "confidence",
1993
+ inferenceConfidence(signatureSource, typeLocked)
1994
+ );
1995
+
1996
+ JsonArray evidence = new JsonArray();
1997
+ evidence.add(
1998
+ inferenceEvidence(
1999
+ "signature-source",
2000
+ "ghidra-function-manager",
2001
+ "Function signature source is " + signatureSource + "."
2002
+ )
2003
+ );
2004
+ evidence.add(
2005
+ inferenceEvidence(
2006
+ "decompiler-type",
2007
+ "ghidra-high-function",
2008
+ "HighFunction recovered data type " + dataType.getDisplayName() + "."
2009
+ )
2010
+ );
2011
+ if (typeLocked) {
2012
+ evidence.add(
2013
+ inferenceEvidence(
2014
+ "type-lock",
2015
+ "ghidra-high-symbol",
2016
+ "The decompiler marks this parameter type as locked."
2017
+ )
2018
+ );
2019
+ }
2020
+ result.add("evidence", evidence);
2021
+
2022
+ JsonArray artifacts = new JsonArray();
2023
+ artifacts.add("recovered-signature");
2024
+ if (!storage.isBlank()) {
2025
+ artifacts.add("register-or-stack-storage");
2026
+ }
2027
+ if (name != null && (name.startsWith("param_") || name.startsWith("unaff_"))) {
2028
+ artifacts.add("compiler-generated-variable");
2029
+ }
2030
+ result.add("decompiler_artifacts", artifacts);
2031
+ return result;
2032
+ }
2033
+
2034
+ private JsonObject inferredJumpTable(
2035
+ JumpTable jumpTable,
2036
+ List<ClangNode> caseTokens
2037
+ ) throws Exception {
2038
+ JsonObject result = new JsonObject();
2039
+ result.addProperty(
2040
+ "dispatch_address",
2041
+ canonicalAddress(jumpTable.getSwitchAddress())
2042
+ );
2043
+ JumpTable.LoadTable[] loadTables = jumpTable.getLoadTables();
2044
+ RecoveredAArch64RelativeTable recoveredAArch64 =
2045
+ recoverAArch64RelativeTable(jumpTable);
2046
+ if (recoveredAArch64 != null && loadTables.length > 0) {
2047
+ // A decoded table only adds evidence when Ghidra's own load table
2048
+ // describes the same bytes; any disagreement keeps Ghidra's view.
2049
+ boolean agrees = false;
2050
+ for (JumpTable.LoadTable loadTable : loadTables) {
2051
+ if (loadTable.getAddress().equals(recoveredAArch64.tableAddress) &&
2052
+ loadTable.getSize() == recoveredAArch64.entrySize &&
2053
+ loadTable.getNum() == recoveredAArch64.mappings.size()) {
2054
+ agrees = true;
2055
+ }
2056
+ }
2057
+ if (!agrees) recoveredAArch64 = null;
2058
+ }
2059
+ List<Address> referencedDataAddresses = loadTables.length == 0
2060
+ ? dispatchPathDataReferences(jumpTable)
2061
+ : List.of();
2062
+ JsonArray dataSources = new JsonArray();
2063
+ for (int index = 0; index < loadTables.length; index += 1) {
2064
+ monitor.checkCancelled();
2065
+ JumpTable.LoadTable loadTable = loadTables[index];
2066
+ JsonObject source = new JsonObject();
2067
+ source.addProperty("address", canonicalAddress(loadTable.getAddress()));
2068
+ source.addProperty("provenance", "ghidra-decompiler-load-table");
2069
+ int entrySize = loadTable.getSize();
2070
+ int entryCount = loadTable.getNum();
2071
+ if (entrySize > 0) source.addProperty("entry_size_bytes", entrySize);
2072
+ else source.add("entry_size_bytes", JsonNull.INSTANCE);
2073
+ if (entryCount > 0) source.addProperty("entry_count", entryCount);
2074
+ else source.add("entry_count", JsonNull.INSTANCE);
2075
+ source.addProperty(
2076
+ "confidence",
2077
+ entrySize > 0 && entryCount > 0 ? "high" : "medium"
2078
+ );
2079
+ JsonArray evidence = new JsonArray();
2080
+ evidence.add(
2081
+ inferenceEvidence(
2082
+ "jump-table",
2083
+ "ghidra-high-function",
2084
+ "Decompiler load table starts at " +
2085
+ canonicalAddress(loadTable.getAddress()) +
2086
+ "."
2087
+ )
2088
+ );
2089
+ source.add("evidence", evidence);
2090
+ dataSources.add(source);
2091
+ }
2092
+ if (recoveredAArch64 != null && loadTables.length == 0) {
2093
+ JsonObject source = new JsonObject();
2094
+ source.addProperty(
2095
+ "address",
2096
+ canonicalAddress(recoveredAArch64.tableAddress)
2097
+ );
2098
+ source.addProperty("provenance", recoveredAArch64.entrySize == 1 ? "ghidra-aarch64-byte-table" : "ghidra-aarch64-halfword-table");
2099
+ source.addProperty("entry_size_bytes", recoveredAArch64.entrySize);
2100
+ source.addProperty("entry_count", recoveredAArch64.mappings.size());
2101
+ source.addProperty("confidence", "high");
2102
+ JsonArray evidence = new JsonArray();
2103
+ evidence.add(
2104
+ inferenceEvidence(
2105
+ "jump-table",
2106
+ "ghidra-instruction-and-memory",
2107
+ "Decoded " + recoveredAArch64.mappings.size() + " " +
2108
+ (recoveredAArch64.signedEntries ? "signed" : "unsigned") +
2109
+ " entries of size " + recoveredAArch64.entrySize + " bytes from " + canonicalAddress(recoveredAArch64.tableAddress) +
2110
+ "; the AArch64 ADRP/ADD table, indexed load, ADR base, ADD" +
2111
+ (recoveredAArch64.offsetShift == 0 ? "" : " LSL #" + recoveredAArch64.offsetShift) +
2112
+ "/BR chain uses a selector bounded by CMP #" + recoveredAArch64.compareImmediate +
2113
+ " and " + recoveredAArch64.guardMnemonic + " on one straight-line path" +
2114
+ (recoveredAArch64.nullTarget == null
2115
+ ? "."
2116
+ : "; zero entries are routed by CBZ to " + canonicalAddress(recoveredAArch64.nullTarget) + ".")
2117
+ )
2118
+ );
2119
+ source.add("evidence", evidence);
2120
+ dataSources.add(source);
2121
+ }
2122
+ for (int index = 0; index < referencedDataAddresses.size(); index += 1) {
2123
+ Address address = referencedDataAddresses.get(index);
2124
+ if (
2125
+ recoveredAArch64 != null &&
2126
+ recoveredAArch64.tableAddress.equals(address)
2127
+ ) {
2128
+ continue;
2129
+ }
2130
+ JsonObject source = new JsonObject();
2131
+ source.addProperty("address", canonicalAddress(address));
2132
+ source.addProperty(
2133
+ "provenance",
2134
+ "ghidra-dispatch-block-data-reference"
2135
+ );
2136
+ source.add("entry_size_bytes", JsonNull.INSTANCE);
2137
+ source.add("entry_count", JsonNull.INSTANCE);
2138
+ source.addProperty("confidence", "medium");
2139
+ JsonArray evidence = new JsonArray();
2140
+ evidence.add(
2141
+ inferenceEvidence(
2142
+ "jump-table",
2143
+ "ghidra-reference-manager",
2144
+ "The dispatch block or an immediate predecessor contains a typed data reference to " +
2145
+ canonicalAddress(address) +
2146
+ "."
2147
+ )
2148
+ );
2149
+ source.add("evidence", evidence);
2150
+ dataSources.add(source);
2151
+ }
2152
+ result.add("data_sources", dataSources);
2153
+ Address[] targets = jumpTable.getCases();
2154
+ TypedJumpTableCases typedCases = typedJumpTableCases(jumpTable, caseTokens);
2155
+ // Table indices identify the register bounded by CMP. Typed labels may
2156
+ // instead identify a selector before normalization (for example, SUB
2157
+ // index, selector, #10). Do not merge those distinct value identities.
2158
+ if (recoveredAArch64 != null && recoveredAArch64.priorSelectorDefinition != null) {
2159
+ typedCases.limitations.add(
2160
+ "REA decoded the relative table, but the compared selector has an earlier register definition at " +
2161
+ 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."
2162
+ );
2163
+ recoveredAArch64 = null;
2164
+ }
2165
+ if (recoveredAArch64 != null) {
2166
+ for (Map.Entry<Long, Address> typed : typedCases.cases.entrySet()) {
2167
+ long value = typed.getKey();
2168
+ if (value < 0 || value >= recoveredAArch64.mappings.size()) {
2169
+ typedCases.limitations.add("Typed case labels and decoded table indices do not use a verified common selector; Ghidra's typed labels are retained.");
2170
+ recoveredAArch64 = null;
2171
+ break;
2172
+ }
2173
+ RecoveredAArch64Mapping decoded = recoveredAArch64.mappings.get((int) value);
2174
+ boolean divertedZero = decoded.nullEntry && typed.getValue().equals(recoveredAArch64.unreachableZeroTarget);
2175
+ if (!typed.getValue().equals(decoded.targetAddress) && !divertedZero) {
2176
+ typedCases.limitations.add("Typed case evidence and the decoded relative table disagree on a destination; Ghidra's typed labels are retained.");
2177
+ recoveredAArch64 = null;
2178
+ break;
2179
+ }
2180
+ }
2181
+ }
2182
+ JsonArray mappings = new JsonArray();
2183
+ JsonArray defaultTargets = new JsonArray();
2184
+ for (Address target : typedCases.defaults) {
2185
+ defaultTargets.add(typedJumpTableTarget(target, jumpTable, "Typed default label"));
2186
+ }
2187
+ Set<Address> coveredTargets = new HashSet<>(typedCases.cases.values());
2188
+ coveredTargets.addAll(typedCases.defaults);
2189
+ if (recoveredAArch64 != null && recoveredAArch64.unreachableZeroTarget != null) {
2190
+ Address unreachable = recoveredAArch64.unreachableZeroTarget;
2191
+ coveredTargets.add(unreachable);
2192
+ for (RecoveredAArch64Mapping recovered : recoveredAArch64.mappings) {
2193
+ if (recovered.nullEntry && unreachable.equals(typedCases.cases.get((long) recovered.tableIndex))) {
2194
+ typedCases.cases.remove((long) recovered.tableIndex);
2195
+ }
2196
+ }
2197
+ typedCases.limitations.add(
2198
+ "Ghidra recovered destination " + canonicalAddress(unreachable) +
2199
+ " as the zero-offset decode of the relative table, but the CBZ before the dispatch diverts zero entries to " +
2200
+ canonicalAddress(recoveredAArch64.nullTarget) + ", so the indirect branch never reaches it for an in-range case."
2201
+ );
2202
+ }
2203
+ if (recoveredAArch64 != null) {
2204
+ for (int index = 0; index < recoveredAArch64.mappings.size(); index += 1) {
2205
+ monitor.checkCancelled();
2206
+ RecoveredAArch64Mapping recovered = recoveredAArch64.mappings.get(index);
2207
+ if (
2208
+ typedCases.unknownTargets.contains(recovered.targetAddress) ||
2209
+ typedCases.ambiguousValues.contains((long) recovered.tableIndex)
2210
+ ) continue;
2211
+ JsonObject mapping = new JsonObject();
2212
+ mapping.addProperty("case_value", recovered.tableIndex);
2213
+ String targetAddress = canonicalAddress(recovered.targetAddress);
2214
+ mapping.addProperty("target_address", targetAddress);
2215
+ mapping.addProperty("confidence", "high");
2216
+ JsonArray evidence = new JsonArray();
2217
+ evidence.add(
2218
+ inferenceEvidence(
2219
+ "jump-table",
2220
+ "ghidra-instruction-and-memory",
2221
+ "Case value " + recovered.tableIndex + (recoveredAArch64.entrySize == 1 ? " indexes byte " : " indexes halfword at byte offset ") +
2222
+ (recovered.tableIndex * recoveredAArch64.entrySize) + " at " + canonicalAddress(recoveredAArch64.tableAddress) +
2223
+ (recovered.nullEntry
2224
+ ? "; the entry is zero, so the CBZ before the dispatch branches to " + targetAddress + "."
2225
+ : "; the loaded " + (recoveredAArch64.signedEntries ? "signed" : "unsigned") +
2226
+ " entry " + recovered.encodedEntry + " selects a " +
2227
+ (recoveredAArch64.offsetShift == 2 ? "four-byte instruction" : "byte") +
2228
+ " offset from " + canonicalAddress(recoveredAArch64.branchBase) + ", reaching " + targetAddress + ".")
2229
+ )
2230
+ );
2231
+ mapping.add("evidence", evidence);
2232
+ if (!typedCases.cases.containsKey((long) recovered.tableIndex)) {
2233
+ mappings.add(mapping);
2234
+ }
2235
+ coveredTargets.add(recovered.targetAddress);
2236
+ }
2237
+ }
2238
+ for (Map.Entry<Long, Address> entry : typedCases.cases.entrySet()) {
2239
+ JsonObject mapping = typedJumpTableTarget(
2240
+ entry.getValue(),
2241
+ jumpTable,
2242
+ "Typed case value " + entry.getKey()
2243
+ );
2244
+ mapping.addProperty("case_value", entry.getKey());
2245
+ mappings.add(mapping);
2246
+ }
2247
+ Set<Address> emittedUnknownTargets = new HashSet<>();
2248
+ for (int index = 0; index < targets.length; index += 1) {
2249
+ monitor.checkCancelled();
2250
+ boolean unresolvedToken = typedCases.unknownTargets.contains(targets[index]);
2251
+ if (coveredTargets.contains(targets[index]) && !unresolvedToken) continue;
2252
+ if (!emittedUnknownTargets.add(targets[index])) continue;
2253
+ JsonObject mapping = new JsonObject();
2254
+ mapping.add("case_value", JsonNull.INSTANCE);
2255
+ String targetAddress = canonicalAddress(targets[index]);
2256
+ mapping.addProperty("target_address", targetAddress);
2257
+ // Ghidra's recovered case-to-target relation does not identify
2258
+ // which backing load table produced an individual case. Keep the
2259
+ // table inventory and recovered mappings separate instead of
2260
+ // attaching every table address to every target.
2261
+ mapping.addProperty("confidence", "medium");
2262
+ JsonArray evidence = new JsonArray();
2263
+ evidence.add(
2264
+ inferenceEvidence(
2265
+ "jump-table",
2266
+ "ghidra-high-function",
2267
+ "Ghidra recovered target " +
2268
+ targetAddress +
2269
+ " at dispatch " +
2270
+ canonicalAddress(jumpTable.getSwitchAddress()) +
2271
+ ", but no unambiguous, exactly representable case label or default relationship was available."
2272
+ )
2273
+ );
2274
+ mapping.add("evidence", evidence);
2275
+ mappings.add(mapping);
2276
+ }
2277
+ result.add("mappings", mappings);
2278
+ result.add("default_targets", defaultTargets);
2279
+ JsonArray limitations = new JsonArray();
2280
+ for (String limitation : typedCases.limitations) limitations.add(limitation);
2281
+ if (defaultTargets.isEmpty()) {
2282
+ limitations.add(
2283
+ "Ghidra exposed no typed default label with an unambiguous relationship to a recovered destination of this dispatch; default or out-of-range handling remains unknown."
2284
+ );
2285
+ }
2286
+ if (loadTables.length == 0 && referencedDataAddresses.isEmpty()) {
2287
+ limitations.add(
2288
+ "Ghidra recovered targets but exposed no backing load-table or dispatch-block data address."
2289
+ );
2290
+ }
2291
+ if (
2292
+ recoveredAArch64 == null &&
2293
+ loadTables.length == 0 &&
2294
+ !referencedDataAddresses.isEmpty()
2295
+ ) {
2296
+ limitations.add(
2297
+ "Load-table metadata was absent; table-level candidate ownership is inferred from typed data references in the dispatch block and its immediate control-flow predecessors, but candidates are not assigned to individual mappings."
2298
+ );
2299
+ }
2300
+ if (!emittedUnknownTargets.isEmpty()) {
2301
+ limitations.add(
2302
+ "Some recovered destinations lack an unambiguous, exactly representable case/default relationship; their case values remain unknown. Unequal label and destination arrays are never paired by index."
2303
+ );
2304
+ }
2305
+ if (recoveredAArch64 == null && loadTables.length == 0 && currentProgram.getLanguageID().getIdAsString().startsWith("AARCH64:")) {
2306
+ 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.");
2307
+ }
2308
+ if (targets.length == 0) {
2309
+ limitations.add(
2310
+ "Ghidra exposed a jump-table dispatch without recovered case targets."
2311
+ );
2312
+ }
2313
+ result.add("limitations", limitations);
2314
+ return result;
2315
+ }
2316
+
2317
+ private static final class TypedJumpTableCases {
2318
+ private final Map<Long, Address> cases = new TreeMap<>();
2319
+ private final Set<Address> defaults = new java.util.TreeSet<>();
2320
+ private final Set<Address> unknownTargets = new HashSet<>();
2321
+ private final Set<Long> ambiguousValues = new HashSet<>();
2322
+ private final Set<String> limitations = new java.util.LinkedHashSet<>();
2323
+ }
2324
+
2325
+ private TypedJumpTableCases typedJumpTableCases(
2326
+ JumpTable jumpTable,
2327
+ List<ClangNode> tokens
2328
+ ) throws Exception {
2329
+ TypedJumpTableCases result = new TypedJumpTableCases();
2330
+ Set<Address> targets = new HashSet<>();
2331
+ for (Address target : jumpTable.getCases()) targets.add(target);
2332
+ for (ClangNode node : tokens) {
2333
+ monitor.checkCancelled();
2334
+ if (!(node instanceof ClangCaseToken token)) continue;
2335
+ PcodeOp operation = token.getPcodeOp();
2336
+ if (operation == null || operation.getParent() == null) continue;
2337
+ PcodeBlockBasic block = operation.getParent();
2338
+ Address target = block.getStart();
2339
+ if (!targets.contains(target)) continue;
2340
+ PcodeOp dispatch = null;
2341
+ boolean ambiguousDispatch = false;
2342
+ boolean includesDispatch = false;
2343
+ for (int index = 0; index < block.getInSize(); index += 1) {
2344
+ PcodeBlock predecessor = block.getIn(index);
2345
+ if (!(predecessor instanceof PcodeBlockBasic basic)) continue;
2346
+ PcodeOp last = basic.getLastOp();
2347
+ if (last == null || last.getOpcode() != PcodeOp.BRANCHIND) continue;
2348
+ if (last.getSeqnum().getTarget().equals(jumpTable.getSwitchAddress())) {
2349
+ includesDispatch = true;
2350
+ }
2351
+ if (dispatch != null && dispatch != last) ambiguousDispatch = true;
2352
+ dispatch = last;
2353
+ }
2354
+ if (!includesDispatch) continue;
2355
+ if (ambiguousDispatch) {
2356
+ result.unknownTargets.add(target);
2357
+ result.limitations.add(
2358
+ "A typed case/default block has multiple indirect dispatch predecessors; REA cannot attribute its label to one switch."
2359
+ );
2360
+ continue;
2361
+ }
2362
+ // Default is a typed label role, not an OFF sentinel: Ghidra can
2363
+ // encode a default token with an ordinary in-range case value.
2364
+ if ("default".equals(token.getText())) {
2365
+ result.defaults.add(target);
2366
+ continue;
2367
+ }
2368
+ Long value = exactTypedCaseValue(token);
2369
+ if (value == null) {
2370
+ result.unknownTargets.add(target);
2371
+ result.limitations.add(
2372
+ "Typed case label " + token.getText() +
2373
+ " has encoded unsigned magnitude " + Long.toUnsignedString(token.getValue()) +
2374
+ " at recovered target " + canonicalAddress(target) +
2375
+ " for dispatch " + canonicalAddress(jumpTable.getSwitchAddress()) +
2376
+ "; the label is nonliteral, inconsistent with its encoded magnitude, or outside the exact JSON integer range, so its numeric case value remains unknown."
2377
+ );
2378
+ continue;
2379
+ }
2380
+ if (result.ambiguousValues.contains(value)) {
2381
+ result.unknownTargets.add(target);
2382
+ continue;
2383
+ }
2384
+ Address previous = result.cases.putIfAbsent(value, target);
2385
+ if (previous != null && !previous.equals(target)) {
2386
+ result.cases.remove(value);
2387
+ result.ambiguousValues.add(value);
2388
+ result.unknownTargets.add(previous);
2389
+ result.unknownTargets.add(target);
2390
+ result.limitations.add(
2391
+ "Typed tokens assign one case value to different recovered destinations; that case remains unknown."
2392
+ );
2393
+ }
2394
+ }
2395
+ return result;
2396
+ }
2397
+
2398
+ private static Long exactTypedCaseValue(ClangCaseToken token) {
2399
+ String text = token.getText();
2400
+ if (text == null || !text.matches("-?(?:0[xX][0-9a-fA-F]+|[0-9]+)")) return null;
2401
+ boolean negative = text.startsWith("-");
2402
+ String literal = negative ? text.substring(1) : text;
2403
+ BigInteger magnitude = literal.startsWith("0x") || literal.startsWith("0X")
2404
+ ? new BigInteger(literal.substring(2), 16)
2405
+ : new BigInteger(literal, 10);
2406
+ // Negative numeric tokens carry an unsigned magnitude in OFF, so
2407
+ // getValue()/getScalar() alone would incorrectly emit positive cases.
2408
+ if (!magnitude.equals(new BigInteger(Long.toUnsignedString(token.getValue())))) return null;
2409
+ BigInteger value = negative ? magnitude.negate() : magnitude;
2410
+ if (value.abs().compareTo(BigInteger.valueOf(9007199254740991L)) > 0) return null;
2411
+ return value.longValueExact();
2412
+ }
2413
+
2414
+ private static JsonObject typedJumpTableTarget(
2415
+ Address target,
2416
+ JumpTable table,
2417
+ String label
2418
+ ) {
2419
+ JsonObject result = new JsonObject();
2420
+ String address = canonicalAddress(target);
2421
+ result.addProperty("target_address", address);
2422
+ result.addProperty("confidence", "high");
2423
+ JsonArray evidence = new JsonArray();
2424
+ evidence.add(inferenceEvidence(
2425
+ "jump-table",
2426
+ "ghidra-clang-case-token",
2427
+ label + " belongs to the p-code block starting at recovered target " +
2428
+ address + "; its unique indirect dispatch predecessor is " +
2429
+ canonicalAddress(table.getSwitchAddress()) + "."
2430
+ ));
2431
+ result.add("evidence", evidence);
2432
+ return result;
2433
+ }
2434
+
2435
+ private static final class RecoveredAArch64Mapping {
2436
+ private final int tableIndex;
2437
+ private final Address targetAddress;
2438
+ private final long encodedEntry;
2439
+ private final boolean nullEntry;
2440
+
2441
+ private RecoveredAArch64Mapping(
2442
+ int tableIndex,
2443
+ Address targetAddress,
2444
+ long encodedEntry,
2445
+ boolean nullEntry
2446
+ ) {
2447
+ this.tableIndex = tableIndex;
2448
+ this.targetAddress = targetAddress;
2449
+ this.encodedEntry = encodedEntry;
2450
+ this.nullEntry = nullEntry;
2451
+ }
2452
+ }
2453
+
2454
+ private static final class RecoveredAArch64RelativeTable {
2455
+ private final Address tableAddress;
2456
+ private final Address branchBase;
2457
+ private final int entrySize;
2458
+ private final Address priorSelectorDefinition;
2459
+ private final boolean signedEntries;
2460
+ private final int offsetShift;
2461
+ private final String guardMnemonic;
2462
+ private final long compareImmediate;
2463
+ private final Address nullTarget;
2464
+ private final Address unreachableZeroTarget;
2465
+ private final List<RecoveredAArch64Mapping> mappings;
2466
+
2467
+ private RecoveredAArch64RelativeTable(
2468
+ Address tableAddress,
2469
+ Address branchBase,
2470
+ int entrySize,
2471
+ Address priorSelectorDefinition,
2472
+ boolean signedEntries,
2473
+ int offsetShift,
2474
+ String guardMnemonic,
2475
+ long compareImmediate,
2476
+ Address nullTarget,
2477
+ Address unreachableZeroTarget,
2478
+ List<RecoveredAArch64Mapping> mappings
2479
+ ) {
2480
+ this.tableAddress = tableAddress;
2481
+ this.branchBase = branchBase;
2482
+ this.entrySize = entrySize;
2483
+ this.priorSelectorDefinition = priorSelectorDefinition;
2484
+ this.signedEntries = signedEntries;
2485
+ this.offsetShift = offsetShift;
2486
+ this.guardMnemonic = guardMnemonic;
2487
+ this.compareImmediate = compareImmediate;
2488
+ this.nullTarget = nullTarget;
2489
+ this.unreachableZeroTarget = unreachableZeroTarget;
2490
+ this.mappings = mappings;
2491
+ }
2492
+ }
2493
+
2494
+ private static final Pattern AARCH64_DISPATCH_ADD = Pattern.compile(
2495
+ "(?i)^([wx]\\d+),\\s*([wx]\\d+),\\s*([wx]\\d+)(?:,\\s*LSL\\s*#(?:0x)?([0-9a-f]+))?$"
2496
+ );
2497
+ private static final Pattern AARCH64_TABLE_LOAD = Pattern.compile(
2498
+ "(?i)^([wx]\\d+),\\s*\\[([wx]\\d+),\\s*([wx]\\d+)(?:,\\s*(LSL|UXTW)\\s*(?:#(?:0x)?([0-9a-f]+))?)?\\s*\\]$"
2499
+ );
2500
+ private static final Pattern AARCH64_REGISTER_AND_ADDRESS = Pattern.compile(
2501
+ "(?i)^([wx]\\d+),\\s*(0x[0-9a-f]+)$"
2502
+ );
2503
+ private static final Pattern AARCH64_ADD_IMMEDIATE = Pattern.compile(
2504
+ "(?i)^([wx]\\d+),\\s*([wx]\\d+),\\s*#?(0x[0-9a-f]+|[0-9]+)$"
2505
+ );
2506
+ private static final Pattern AARCH64_COMPARE_IMMEDIATE = Pattern.compile(
2507
+ "(?i)^([wx]\\d+),\\s*#?(0x[0-9a-f]+|[0-9]+)$"
2508
+ );
2509
+ private static final Pattern AARCH64_MOVE_REGISTER = Pattern.compile(
2510
+ "(?i)^([wx]\\d+),\\s*([wx]\\d+)$"
2511
+ );
2512
+ private static final Set<String> AARCH64_FLAG_PRESERVING =
2513
+ Set.of("MOV", "ADRP", "ADR", "ADD");
2514
+
2515
+ /**
2516
+ * Recover only directly verified AArch64 relative branch tables:
2517
+ * ADRP/ADD table base, LDRB/LDRH/LDRSB/LDRSH entry load with an exact
2518
+ * index scale, ADR branch base, optional CBZ on the loaded entry, and
2519
+ * ADD (byte or LSL #2 scale)/BR, with a selector bounded by CMP #imm and an
2520
+ * unsigned B.HI or B.CS/B.HS guard on one straight-line path. Any other
2521
+ * instruction sequence keeps its case labels unknown.
2522
+ */
2523
+ private RecoveredAArch64RelativeTable recoverAArch64RelativeTable(
2524
+ JumpTable jumpTable
2525
+ ) throws Exception {
2526
+ if (!currentProgram.getLanguageID().getIdAsString().startsWith("AARCH64:")) {
2527
+ return null;
2528
+ }
2529
+ Function function = containingFunction(jumpTable.getSwitchAddress());
2530
+ if (function == null) return null;
2531
+
2532
+ List<Instruction> instructions = new ArrayList<>();
2533
+ InstructionIterator iterator = currentProgram.getListing().getInstructions(
2534
+ function.getBody(), true
2535
+ );
2536
+ while (iterator.hasNext()) {
2537
+ monitor.checkCancelled();
2538
+ Instruction instruction = iterator.next();
2539
+ instructions.add(instruction);
2540
+ if (instruction.getAddress().equals(jumpTable.getSwitchAddress())) break;
2541
+ }
2542
+ int branchIndex = instructions.size() - 1;
2543
+ if (branchIndex < 1 || !mnemonic(instructions.get(branchIndex)).equals("BR")) {
2544
+ return null;
2545
+ }
2546
+ Set<String> recoveredTargetSet = new HashSet<>();
2547
+ for (Address target : jumpTable.getCases()) recoveredTargetSet.add(canonicalAddress(target));
2548
+ if (recoveredTargetSet.isEmpty()) return null;
2549
+ return matchAArch64RelativeDispatch(
2550
+ instructions,
2551
+ branchIndex,
2552
+ recoveredTargetSet,
2553
+ dispatchPathDataReferences(jumpTable)
2554
+ );
2555
+ }
2556
+
2557
+ private RecoveredAArch64RelativeTable matchAArch64RelativeDispatch(
2558
+ List<Instruction> instructions,
2559
+ int branchIndex,
2560
+ Set<String> recoveredTargetSet,
2561
+ List<Address> dataAddresses
2562
+ ) throws Exception {
2563
+ String branchRegister = normalizedRegister(operands(instructions.get(branchIndex)));
2564
+
2565
+ // ADD Xd, Xn, Xm{, LSL #s}; BR Xd. LSL applies to Xm only.
2566
+ Instruction dispatchAdd = instructions.get(branchIndex - 1);
2567
+ if (!mnemonic(dispatchAdd).equals("ADD")) return null;
2568
+ java.util.regex.Matcher addMatcher = AARCH64_DISPATCH_ADD.matcher(operands(dispatchAdd));
2569
+ if (!addMatcher.matches() || !addMatcher.group(1).toLowerCase(Locale.ROOT).startsWith("x")) return null;
2570
+ if (!normalizedRegister(addMatcher.group(1)).equals(branchRegister)) return null;
2571
+ int offsetShift = addMatcher.group(4) == null ? 0 : Integer.parseInt(addMatcher.group(4), 16);
2572
+ if (offsetShift != 0 && offsetShift != 2) return null;
2573
+ String firstSource = normalizedRegister(addMatcher.group(2));
2574
+ String secondSource = normalizedRegister(addMatcher.group(3));
2575
+ if (firstSource.equals(secondSource)) return null;
2576
+
2577
+ // The entry register is the source whose latest definition is the
2578
+ // table load; the other source must be defined by ADR.
2579
+ int loadIndex = -1;
2580
+ String entryRegister = null;
2581
+ String baseRegister = null;
2582
+ for (String candidate : offsetShift == 0 ? List.of(secondSource, firstSource) : List.of(secondSource)) {
2583
+ int writer = latestWriter(instructions, branchIndex - 1, candidate);
2584
+ if (writer >= 0 && Set.of("LDRB", "LDRH", "LDRSB", "LDRSH").contains(mnemonic(instructions.get(writer)))) {
2585
+ loadIndex = writer;
2586
+ entryRegister = candidate;
2587
+ baseRegister = candidate.equals(firstSource) ? secondSource : firstSource;
2588
+ break;
2589
+ }
2590
+ }
2591
+ if (loadIndex < 0) return null;
2592
+
2593
+ Instruction load = instructions.get(loadIndex);
2594
+ String loadMnemonic = mnemonic(load);
2595
+ int entrySize = loadMnemonic.endsWith("B") ? 1 : 2;
2596
+ boolean signedEntries = loadMnemonic.startsWith("LDRS");
2597
+ java.util.regex.Matcher loadMatcher = AARCH64_TABLE_LOAD.matcher(operands(load));
2598
+ if (!loadMatcher.matches()) return null;
2599
+ // A signed load into a W register sign-extends only to 32 bits; the
2600
+ // 64-bit dispatch ADD would then read a zero-extended value.
2601
+ if (signedEntries && !loadMatcher.group(1).toLowerCase(Locale.ROOT).startsWith("x")) return null;
2602
+ String tableBaseRegister = normalizedRegister(loadMatcher.group(2));
2603
+ String indexRegister = normalizedRegister(loadMatcher.group(3));
2604
+ boolean wordIndex = loadMatcher.group(3).toLowerCase(Locale.ROOT).startsWith("w");
2605
+ String extend = loadMatcher.group(4) == null ? null : loadMatcher.group(4).toUpperCase(Locale.ROOT);
2606
+ int indexShift = loadMatcher.group(5) == null ? 0 : Integer.parseInt(loadMatcher.group(5), 16);
2607
+ if (wordIndex ? !"UXTW".equals(extend) : "UXTW".equals(extend)) return null;
2608
+ if (indexShift != (entrySize == 1 ? 0 : 1)) return null;
2609
+
2610
+ // Only ADR and one CBZ on the entry may sit between load and ADD.
2611
+ Address nullTarget = null;
2612
+ for (int index = loadIndex + 1; index < branchIndex - 1; index += 1) {
2613
+ Instruction instruction = instructions.get(index);
2614
+ String name = mnemonic(instruction);
2615
+ if (name.equals("ADR")) continue;
2616
+ if (name.equals("CBZ") && nullTarget == null) {
2617
+ String[] parts = operands(instruction).split(",");
2618
+ Address[] flows = instruction.getFlows();
2619
+ if (parts.length != 2 || flows.length != 1 ||
2620
+ !normalizedRegister(parts[0]).equals(entryRegister)) return null;
2621
+ nullTarget = flows[0];
2622
+ continue;
2623
+ }
2624
+ return null;
2625
+ }
2626
+
2627
+ int baseIndex = latestWriter(instructions, branchIndex - 1, baseRegister);
2628
+ if (baseIndex < 0 || !mnemonic(instructions.get(baseIndex)).equals("ADR")) return null;
2629
+ java.util.regex.Matcher baseMatcher = AARCH64_REGISTER_AND_ADDRESS.matcher(operands(instructions.get(baseIndex)));
2630
+ if (!baseMatcher.matches()) return null;
2631
+ Address branchBase = addressFromOperand(baseMatcher.group(2));
2632
+
2633
+ int tableAddIndex = latestWriter(instructions, loadIndex, tableBaseRegister);
2634
+ if (tableAddIndex < 0 || !mnemonic(instructions.get(tableAddIndex)).equals("ADD")) return null;
2635
+ java.util.regex.Matcher tableAddMatcher = AARCH64_ADD_IMMEDIATE.matcher(operands(instructions.get(tableAddIndex)));
2636
+ if (!tableAddMatcher.matches() ||
2637
+ !normalizedRegister(tableAddMatcher.group(1)).equals(tableBaseRegister) ||
2638
+ !normalizedRegister(tableAddMatcher.group(2)).equals(tableBaseRegister)) return null;
2639
+ int tablePageIndex = latestWriter(instructions, tableAddIndex, tableBaseRegister);
2640
+ if (tablePageIndex < 0 || !mnemonic(instructions.get(tablePageIndex)).equals("ADRP")) return null;
2641
+ java.util.regex.Matcher pageMatcher = AARCH64_REGISTER_AND_ADDRESS.matcher(operands(instructions.get(tablePageIndex)));
2642
+ if (!pageMatcher.matches() || !normalizedRegister(pageMatcher.group(1)).equals(tableBaseRegister)) return null;
2643
+ Address tableAddress = addressFromOperand(pageMatcher.group(2)).add(parseInteger(tableAddMatcher.group(3)));
2644
+ if (!currentProgram.getMemory().contains(tableAddress) ||
2645
+ (!dataAddresses.isEmpty() && !dataAddresses.contains(tableAddress))) return null;
2646
+
2647
+ // Unsigned guard: CMP Rs, #imm, then B.HI (imm + 1 entries) or
2648
+ // B.CS/B.HS (imm entries), with only flag-preserving moves and
2649
+ // address arithmetic between the compare and the load.
2650
+ int guardIndex = -1;
2651
+ for (int index = loadIndex - 1; index >= 0; index -= 1) {
2652
+ String name = mnemonic(instructions.get(index));
2653
+ if (name.startsWith("B.")) {
2654
+ guardIndex = index;
2655
+ break;
2656
+ }
2657
+ if (!AARCH64_FLAG_PRESERVING.contains(name)) return null;
2658
+ }
2659
+ if (guardIndex < 1) return null;
2660
+ Instruction guard = instructions.get(guardIndex);
2661
+ String guardMnemonic = mnemonic(guard);
2662
+ if (!Set.of("B.HI", "B.CS", "B.HS").contains(guardMnemonic)) return null;
2663
+ Address[] guardFlows = guard.getFlows();
2664
+ if (guardFlows.length != 1) return null;
2665
+ Address defaultTarget = guardFlows[0];
2666
+ int compareIndex = -1;
2667
+ for (int index = guardIndex - 1; index >= 0; index -= 1) {
2668
+ String name = mnemonic(instructions.get(index));
2669
+ if (name.equals("CMP")) {
2670
+ compareIndex = index;
2671
+ break;
2672
+ }
2673
+ if (!AARCH64_FLAG_PRESERVING.contains(name)) return null;
2674
+ }
2675
+ if (compareIndex < 0) return null;
2676
+ java.util.regex.Matcher compareMatcher = AARCH64_COMPARE_IMMEDIATE.matcher(operands(instructions.get(compareIndex)));
2677
+ if (!compareMatcher.matches()) return null;
2678
+ String comparedRegister = normalizedRegister(compareMatcher.group(1));
2679
+ boolean comparedFullWidth = compareMatcher.group(1).toLowerCase(Locale.ROOT).startsWith("x");
2680
+ long compareImmediate = parseInteger(compareMatcher.group(2));
2681
+ long entryCountLong = guardMnemonic.equals("B.HI") ? compareImmediate + 1 : compareImmediate;
2682
+ if (entryCountLong <= 0 || entryCountLong > 4096) return null;
2683
+ int entryCount = (int) entryCountLong;
2684
+
2685
+ // The loaded index must carry the compared value: either the compared
2686
+ // register itself, unchanged since CMP, or a MOV copy made after CMP.
2687
+ // A W compare bounds only the low 32 bits, so an X index needs a
2688
+ // zero-extending W copy; a W index is zero-extended by UXTW.
2689
+ int indexWriter = latestWriter(instructions, loadIndex, indexRegister);
2690
+ boolean bounded = false;
2691
+ if (indexWriter < compareIndex) {
2692
+ bounded = indexRegister.equals(comparedRegister) && (wordIndex || comparedFullWidth);
2693
+ }
2694
+ else if (mnemonic(instructions.get(indexWriter)).equals("MOV")) {
2695
+ java.util.regex.Matcher moveMatcher = AARCH64_MOVE_REGISTER.matcher(operands(instructions.get(indexWriter)));
2696
+ if (moveMatcher.matches() &&
2697
+ normalizedRegister(moveMatcher.group(2)).equals(comparedRegister) &&
2698
+ latestWriter(instructions, indexWriter, comparedRegister) < compareIndex) {
2699
+ boolean wordCopy = moveMatcher.group(1).toLowerCase(Locale.ROOT).startsWith("w");
2700
+ bounded = wordCopy || comparedFullWidth;
2701
+ }
2702
+ }
2703
+ if (!bounded) return null;
2704
+
2705
+ // Every fact above holds only on one straight-line fall-through path
2706
+ // with no other entry into the window.
2707
+ int windowStart = Math.min(Math.min(tablePageIndex, baseIndex), compareIndex);
2708
+ for (int index = windowStart + 1; index <= branchIndex; index += 1) {
2709
+ monitor.checkCancelled();
2710
+ Instruction previous = instructions.get(index - 1);
2711
+ Instruction current = instructions.get(index);
2712
+ Address fallThrough = previous.getFallThrough();
2713
+ if (fallThrough == null || !fallThrough.equals(current.getAddress())) return null;
2714
+ ReferenceIterator incoming = currentProgram.getReferenceManager().getReferencesTo(current.getAddress());
2715
+ while (incoming.hasNext()) {
2716
+ if (incoming.next().getReferenceType().isFlow()) return null;
2717
+ }
2718
+ }
2719
+
2720
+ List<RecoveredAArch64Mapping> mappings = new ArrayList<>();
2721
+ Set<String> mappedTargets = new HashSet<>();
2722
+ for (int tableIndex = 0; tableIndex < entryCount; tableIndex += 1) {
2723
+ monitor.checkCancelled();
2724
+ Address entry = tableAddress.add((long) tableIndex * entrySize);
2725
+ if (!currentProgram.getMemory().contains(entry)) return null;
2726
+ long encoded;
2727
+ if (entrySize == 1) {
2728
+ byte value = currentProgram.getMemory().getByte(entry);
2729
+ encoded = signedEntries ? value : value & 0xffL;
2730
+ }
2731
+ else {
2732
+ short value = currentProgram.getMemory().getShort(entry);
2733
+ encoded = signedEntries ? value : value & 0xffffL;
2734
+ }
2735
+ if (encoded == 0 && nullTarget != null) {
2736
+ mappings.add(new RecoveredAArch64Mapping(tableIndex, nullTarget, encoded, true));
2737
+ continue;
2738
+ }
2739
+ Address target;
2740
+ try {
2741
+ target = branchBase.addNoWrap(encoded << offsetShift);
2742
+ }
2743
+ catch (ghidra.program.model.address.AddressOverflowException overflow) {
2744
+ return null;
2745
+ }
2746
+ String canonicalTarget = canonicalAddress(target);
2747
+ if (!recoveredTargetSet.contains(canonicalTarget)) return null;
2748
+ mappedTargets.add(canonicalTarget);
2749
+ mappings.add(new RecoveredAArch64Mapping(tableIndex, target, encoded, false));
2750
+ }
2751
+ if (mappedTargets.contains(canonicalAddress(defaultTarget))) return null;
2752
+ // Ghidra's recovered destinations must be fully accounted for by the
2753
+ // decoded entries, the guard default, and the CBZ null path.
2754
+ Set<String> accountedTargets = new HashSet<>(mappedTargets);
2755
+ accountedTargets.add(canonicalAddress(defaultTarget));
2756
+ if (nullTarget != null) accountedTargets.add(canonicalAddress(nullTarget));
2757
+ // Ghidra does not model the CBZ, so it may decode a zero entry as a
2758
+ // branch to the base itself. Only a zero entry encodes offset 0, and
2759
+ // the CBZ diverts every zero entry, so the BR never reaches the base.
2760
+ Address unreachableZeroTarget = null;
2761
+ boolean divertedZeroEntry = mappings.stream().anyMatch(mapping -> mapping.nullEntry);
2762
+ if (divertedZeroEntry &&
2763
+ recoveredTargetSet.contains(canonicalAddress(branchBase)) &&
2764
+ !mappedTargets.contains(canonicalAddress(branchBase)) &&
2765
+ !branchBase.equals(defaultTarget) &&
2766
+ !branchBase.equals(nullTarget)) {
2767
+ unreachableZeroTarget = branchBase;
2768
+ accountedTargets.add(canonicalAddress(branchBase));
2769
+ }
2770
+ if (!accountedTargets.containsAll(recoveredTargetSet)) return null;
2771
+ int selectorWriter = latestWriter(instructions, compareIndex, comparedRegister);
2772
+ return new RecoveredAArch64RelativeTable(
2773
+ tableAddress,
2774
+ branchBase,
2775
+ entrySize,
2776
+ selectorWriter < 0 ? null : instructions.get(selectorWriter).getAddress(),
2777
+ signedEntries,
2778
+ offsetShift,
2779
+ guardMnemonic,
2780
+ compareImmediate,
2781
+ nullTarget,
2782
+ unreachableZeroTarget,
2783
+ List.copyOf(mappings)
2784
+ );
2785
+ }
2786
+
2787
+ private static String mnemonic(Instruction instruction) {
2788
+ return instruction.getMnemonicString().toUpperCase(Locale.ROOT);
2789
+ }
2790
+
2791
+ /** Index of the last instruction before {@code before} that writes {@code register}, or -1. */
2792
+ private static int latestWriter(List<Instruction> instructions, int before, String register) {
2793
+ for (int index = before - 1; index >= 0; index -= 1) {
2794
+ for (Object result : instructions.get(index).getResultObjects()) {
2795
+ if (result instanceof Register written &&
2796
+ normalizedRegister(written.getBaseRegister().getName()).equals(register)) {
2797
+ return index;
2798
+ }
2799
+ }
2800
+ }
2801
+ return -1;
2802
+ }
2803
+
2804
+ private static String operands(Instruction instruction) {
2805
+ List<String> values = new ArrayList<>();
2806
+ for (int index = 0; index < instruction.getNumOperands(); index += 1) {
2807
+ values.add(instruction.getDefaultOperandRepresentation(index));
2808
+ }
2809
+ return String.join(", ", values);
2810
+ }
2811
+
2812
+ private static String normalizedRegister(String value) {
2813
+ String register = value.trim().toLowerCase(Locale.ROOT);
2814
+ return register.matches("[wx]\\d+") ? "r" + register.substring(1) : register;
2815
+ }
2816
+
2817
+ private static long parseInteger(String value) {
2818
+ String number = value.toLowerCase(Locale.ROOT);
2819
+ return number.startsWith("0x")
2820
+ ? Long.parseUnsignedLong(number.substring(2), 16)
2821
+ : Long.parseLong(number);
2822
+ }
2823
+
2824
+ private Address addressFromOperand(String value) {
2825
+ String hex = value.trim().replaceFirst("(?i)^0x", "");
2826
+ return currentProgram.getAddressFactory().getDefaultAddressSpace()
2827
+ .getAddress(Long.parseUnsignedLong(hex, 16));
2828
+ }
2829
+
2830
+ private List<Address> dispatchPathDataReferences(JumpTable jumpTable)
2831
+ throws Exception {
2832
+ BasicBlockModel model = new BasicBlockModel(currentProgram, false);
2833
+ CodeBlock dispatchBlock = model.getFirstCodeBlockContaining(
2834
+ jumpTable.getSwitchAddress(),
2835
+ monitor
2836
+ );
2837
+ if (dispatchBlock == null) {
2838
+ return List.of();
2839
+ }
2840
+ List<CodeBlock> blocks = new ArrayList<>();
2841
+ blocks.add(dispatchBlock);
2842
+ CodeBlockReferenceIterator sources = dispatchBlock.getSources(monitor);
2843
+ while (sources.hasNext()) {
2844
+ monitor.checkCancelled();
2845
+ CodeBlock sourceBlock = sources.next().getSourceBlock();
2846
+ if (sourceBlock != null) {
2847
+ blocks.add(sourceBlock);
2848
+ }
2849
+ }
2850
+ Set<Address> addresses = new HashSet<>();
2851
+ for (CodeBlock block : blocks) {
2852
+ InstructionIterator instructions = currentProgram
2853
+ .getListing()
2854
+ .getInstructions(block, true);
2855
+ while (instructions.hasNext()) {
2856
+ monitor.checkCancelled();
2857
+ Instruction instruction = instructions.next();
2858
+ for (
2859
+ Reference reference : currentProgram
2860
+ .getReferenceManager()
2861
+ .getReferencesFrom(instruction.getAddress())
2862
+ ) {
2863
+ if (
2864
+ reference.getReferenceType().isData() &&
2865
+ currentProgram
2866
+ .getMemory()
2867
+ .getLoadedAndInitializedAddressSet()
2868
+ .contains(reference.getToAddress())
2869
+ ) {
2870
+ addresses.add(reference.getToAddress());
2871
+ }
2872
+ }
2873
+ }
2874
+ }
2875
+ List<Address> ordered = new ArrayList<>(addresses);
2876
+ ordered.sort(Address::compareTo);
2877
+ return List.copyOf(ordered);
2878
+ }
2879
+
2880
+ private static JsonObject inferenceEvidence(
2881
+ String kind,
2882
+ String source,
2883
+ String detail
2884
+ ) {
2885
+ JsonObject evidence = new JsonObject();
2886
+ evidence.addProperty("kind", kind);
2887
+ evidence.addProperty("source", source);
2888
+ evidence.addProperty("detail", detail);
2889
+ return evidence;
2890
+ }
2891
+
2892
+ private static String inferenceConfidence(
2893
+ String signatureSource,
2894
+ boolean typeLocked
2895
+ ) {
2896
+ if (
2897
+ typeLocked ||
2898
+ signatureSource.equals("user_defined") ||
2899
+ signatureSource.equals("imported")
2900
+ ) {
2901
+ return "high";
2902
+ }
2903
+ return signatureSource.equals("analysis") ? "medium" : "low";
2904
+ }
2905
+
2906
+ private DecompileResults decompile(Function function) {
2907
+ if (function.isExternal() || function.getBody().isEmpty()) {
2908
+ return null;
2909
+ }
2910
+ DecompileResults results = decompiler.decompileFunction(
2911
+ function,
2912
+ 0,
2913
+ monitor
2914
+ );
2915
+ if (results.isCancelled()) {
2916
+ throw new RequestFailure("decompile_cancelled", "Ghidra decompilation was cancelled");
2917
+ }
2918
+ if (!results.decompileCompleted()) {
2919
+ throw new RequestFailure(
2920
+ "decompile_failed",
2921
+ "Ghidra decompilation failed: " + diagnosticMessage(results.getErrorMessage())
2922
+ );
2923
+ }
2924
+ return results;
2925
+ }
2926
+
2927
+ private static String decompiledText(DecompileResults results) {
2928
+ if (results == null) {
2929
+ return null;
2930
+ }
2931
+ DecompiledFunction value = results.getDecompiledFunction();
2932
+ return value == null ? null : value.getC();
2933
+ }
2934
+
2935
+ private InstructionScan scanInstructions(Function function, int maximum) throws Exception {
2936
+ InstructionIterator iterator = currentProgram.getListing().getInstructions(
2937
+ function.getBody(),
2938
+ true
2939
+ );
2940
+ List<Instruction> instructions = new ArrayList<>();
2941
+ while (iterator.hasNext() && instructions.size() < maximum) {
2942
+ monitor.checkCancelled();
2943
+ instructions.add(iterator.next());
2944
+ }
2945
+ return new InstructionScan(List.copyOf(instructions), iterator.hasNext());
2946
+ }
2947
+
2948
+ private JsonObject functionObservation(
2949
+ JsonElement value,
2950
+ List<String> limitations) {
2951
+ JsonObject result = new JsonObject();
2952
+ result.add("value", value);
2953
+ result.add("limitations", GSON.toJsonTree(limitations));
2954
+ return result;
2955
+ }
2956
+
2957
+ private List<String> noReturnCallLimitations(Iterable<Instruction> instructions)
2958
+ throws Exception {
2959
+ List<String> limitations = new ArrayList<>();
2960
+ for (Instruction instruction : instructions) {
2961
+ monitor.checkCancelled();
2962
+ if (!instruction.getFlowType().isCall() || !instruction.getFlowType().isTerminal()) {
2963
+ continue;
2964
+ }
2965
+ Address[] destinations = instruction.getFlows();
2966
+ StringBuilder limitation = new StringBuilder();
2967
+ limitation.append("Ghidra Listing reports a terminal call at ")
2968
+ .append(canonicalAddress(instruction.getAddress()));
2969
+ if (destinations.length == 0) {
2970
+ limitation.append(" with no resolved callee; its no-return basis is unknown.");
2971
+ }
2972
+ else {
2973
+ limitation.append(" to ");
2974
+ for (int index = 0; index < destinations.length; index++) {
2975
+ if (index > 0) limitation.append(", ");
2976
+ Address destination = destinations[index];
2977
+ limitation.append(canonicalAddress(destination));
2978
+ Function target = currentProgram.getFunctionManager().getFunctionAt(destination);
2979
+ if (target == null) {
2980
+ limitation.append(" (callee identity unknown)");
2981
+ }
2982
+ else {
2983
+ limitation.append(" ").append(procedureName(target));
2984
+ limitation.append(" (external=").append(target.isExternal());
2985
+ limitation.append(", thunk=").append(target.isThunk());
2986
+ limitation.append(", hasNoReturn=").append(target.hasNoReturn()).append(")");
2987
+ }
2988
+ }
2989
+ limitation.append("; fallthrough is excluded by Ghidra's flow model.");
2990
+ Function target = destinations.length == 1
2991
+ ? currentProgram.getFunctionManager().getFunctionAt(destinations[0])
2992
+ : null;
2993
+ if (target != null && target.hasNoReturn()) {
2994
+ limitation.append(" This records Ghidra's FunctionManager flag, not an independent verification that the callee cannot return.");
2995
+ }
2996
+ else {
2997
+ limitation.append(" The reason for the terminal flow is not established by this observation.");
2998
+ }
2999
+ }
3000
+ limitations.add(limitation.toString());
3001
+ }
3002
+ return limitations;
3003
+ }
3004
+
3005
+ private static String renderAssembly(List<Instruction> instructions) {
3006
+ return String.join("\n", renderAssemblyLines(instructions));
3007
+ }
3008
+
3009
+ private static List<String> renderAssemblyLines(List<Instruction> instructions) {
3010
+ List<String> result = new ArrayList<>();
3011
+ for (Instruction instruction : instructions) {
3012
+ StringBuilder line = new StringBuilder();
3013
+ line.append(canonicalAddress(instruction.getAddress()));
3014
+ line.append(": ");
3015
+ line.append(instruction.getMnemonicString());
3016
+ for (int index = 0; index < instruction.getNumOperands(); index += 1) {
3017
+ line.append(index == 0 ? " " : ", ");
3018
+ line.append(instruction.getDefaultOperandRepresentation(index));
3019
+ }
3020
+ result.add(line.toString());
3021
+ }
3022
+ return result;
3023
+ }
3024
+
3025
+ private List<Reference> collectReferences(
3026
+ Function function,
3027
+ String direction) throws Exception {
3028
+ // References can target instruction interiors or originate in embedded
3029
+ // data. Use actual body ownership, including an external's exact entry.
3030
+ AddressSet scope = new AddressSet(function.getBody());
3031
+ scope.add(function.getEntryPoint());
3032
+ boolean outgoing = direction.equals("outgoing");
3033
+ AddressIterator addresses = outgoing
3034
+ ? currentProgram.getReferenceManager().getReferenceSourceIterator(scope, true)
3035
+ : currentProgram.getReferenceManager().getReferenceDestinationIterator(scope, true);
3036
+ TreeMap<String, Reference> observed = new TreeMap<>();
3037
+ while (addresses.hasNext()) {
3038
+ monitor.checkCancelled();
3039
+ Address address = addresses.next();
3040
+ if (outgoing) {
3041
+ for (Reference reference : currentProgram.getReferenceManager()
3042
+ .getReferencesFrom(address)) {
3043
+ monitor.checkCancelled();
3044
+ addReference(observed, reference);
3045
+ }
3046
+ }
3047
+ else {
3048
+ ReferenceIterator iterator = currentProgram.getReferenceManager()
3049
+ .getReferencesTo(address);
3050
+ while (iterator.hasNext()) {
3051
+ monitor.checkCancelled();
3052
+ addReference(observed, iterator.next());
3053
+ }
3054
+ }
3055
+ }
3056
+ List<Reference> result = new ArrayList<>(observed.values());
3057
+ result.sort(REFERENCE_ORDER);
3058
+ return result;
3059
+ }
3060
+
3061
+ private static void addReference(TreeMap<String, Reference> observed, Reference reference) {
3062
+ if (reference.isEntryPointReference()) {
3063
+ return;
3064
+ }
3065
+ String key = canonicalAddress(reference.getFromAddress()) + "\u0000" +
3066
+ canonicalAddress(reference.getToAddress()) + "\u0000" +
3067
+ reference.getReferenceType().getName() + "\u0000" +
3068
+ reference.getOperandIndex() + "\u0000" +
3069
+ reference.isPrimary();
3070
+ observed.putIfAbsent(key, reference);
3071
+ }
3072
+
3073
+ private JsonArray referenceEdges(List<Reference> references) {
3074
+ JsonArray result = new JsonArray();
3075
+ for (Reference reference : references) {
3076
+ result.add(referenceEdge(reference));
3077
+ }
3078
+ return result;
3079
+ }
3080
+
3081
+ private JsonObject referenceEdge(Reference reference) {
3082
+ JsonObject result = new JsonObject();
3083
+ result.addProperty("source_address", canonicalAddress(reference.getFromAddress()));
3084
+ result.addProperty("target_address", canonicalAddress(reference.getToAddress()));
3085
+ Function source = containingFunction(reference.getFromAddress());
3086
+ Function target = containingFunction(reference.getToAddress());
3087
+ result.add(
3088
+ "source_procedure",
3089
+ source == null ? JsonNull.INSTANCE : procedureIdentity(source)
3090
+ );
3091
+ result.add(
3092
+ "target_procedure",
3093
+ target == null ? JsonNull.INSTANCE : procedureIdentity(target)
3094
+ );
3095
+ result.add("kind", referenceKind(reference));
3096
+ return result;
3097
+ }
3098
+
3099
+ private static JsonObject referenceKind(Reference reference) {
3100
+ RefType type = reference.getReferenceType();
3101
+ JsonObject result = new JsonObject();
3102
+ result.addProperty("available", true);
3103
+ result.addProperty("provenance", "ghidra-reference-manager");
3104
+ result.addProperty("type", type.getName());
3105
+ result.addProperty("flow", type.isFlow());
3106
+ result.addProperty("call", type.isCall());
3107
+ result.addProperty("jump", type.isJump());
3108
+ result.addProperty("data", type.isData());
3109
+ result.addProperty("read", type.isRead());
3110
+ result.addProperty("write", type.isWrite());
3111
+ result.addProperty("indirect", type.isIndirect());
3112
+ result.addProperty("computed", type.isComputed());
3113
+ result.addProperty("conditional", type.isConditional());
3114
+ result.addProperty("terminal", type.isTerminal());
3115
+ result.addProperty("primary", reference.isPrimary());
3116
+ result.addProperty("operand_index", reference.getOperandIndex());
3117
+ result.addProperty("external", reference.isExternalReference());
3118
+ return result;
3119
+ }
3120
+
3121
+ private Function containingFunction(Address address) {
3122
+ Function result = currentProgram.getFunctionManager().getFunctionAt(address);
3123
+ if (result == null && address.isMemoryAddress()) {
3124
+ result = currentProgram.getFunctionManager().getFunctionContaining(address);
3125
+ }
3126
+ return result;
3127
+ }
3128
+
3129
+ private static String procedureName(Function function) {
3130
+ Symbol symbol = function.getSymbol();
3131
+ return symbol == null ? function.getName() : symbol.getName(true);
3132
+ }
3133
+
3134
+ private static JsonObject functionClassification(Function function) {
3135
+ JsonObject result = new JsonObject();
3136
+ result.addProperty("external", function.isExternal());
3137
+ result.addProperty("thunk", function.isThunk());
3138
+ Function target = function.isThunk() ? function.getThunkedFunction(false) : null;
3139
+ if (target == null) {
3140
+ result.add("thunk_target", JsonNull.INSTANCE);
3141
+ }
3142
+ else {
3143
+ result.addProperty("thunk_target", canonicalAddress(target.getEntryPoint()));
3144
+ }
3145
+ result.addProperty("provenance", "ghidra-function-manager");
3146
+ return result;
3147
+ }
3148
+
3149
+ private static JsonArray functionLocals(Function function) {
3150
+ Variable[] variables = function.getLocalVariables();
3151
+ List<Variable> ordered = new ArrayList<>(List.of(variables));
3152
+ ordered.sort(
3153
+ Comparator.comparing(Variable::getName)
3154
+ .thenComparing(variable -> variable.getVariableStorage().toString())
3155
+ );
3156
+ JsonArray result = new JsonArray();
3157
+ for (Variable variable : ordered) {
3158
+ JsonObject item = new JsonObject();
3159
+ item.addProperty(
3160
+ "description",
3161
+ variable.getDataType().getDisplayName() + " " + variable.getName() +
3162
+ " @ " + variable.getVariableStorage()
3163
+ );
3164
+ item.addProperty("provenance", "ghidra-function-database");
3165
+ result.add(item);
3166
+ }
3167
+ return result;
3168
+ }
3169
+
3170
+ private JsonArray procedureIdentities(Set<Function> functions) {
3171
+ List<Function> ordered = new ArrayList<>(functions);
3172
+ ordered.sort(Comparator.comparing(Function::getEntryPoint));
3173
+ JsonArray result = new JsonArray();
3174
+ for (Function function : ordered) {
3175
+ result.add(procedureIdentity(function));
3176
+ }
3177
+ return result;
3178
+ }
3179
+
3180
+ private List<CodeBlock> basicBlocks(Function function) throws Exception {
3181
+ BasicBlockModel model = new BasicBlockModel(currentProgram, false);
3182
+ CodeBlockIterator iterator = model.getCodeBlocksContaining(function.getBody(), monitor);
3183
+ List<CodeBlock> result = new ArrayList<>();
3184
+ while (iterator.hasNext()) {
3185
+ monitor.checkCancelled();
3186
+ result.add(iterator.next());
3187
+ }
3188
+ result.sort(Comparator.comparing(CodeBlock::getFirstStartAddress));
3189
+ return result;
3190
+ }
3191
+
3192
+ private JsonArray basicBlockValues(Function function) throws Exception {
3193
+ JsonArray result = new JsonArray();
3194
+ for (CodeBlock block : basicBlocks(function)) {
3195
+ JsonObject item = new JsonObject();
3196
+ item.addProperty("start", canonicalAddress(block.getFirstStartAddress()));
3197
+ item.addProperty("end", exclusiveAddress(block.getMaxAddress()));
3198
+ Set<Address> successors = new HashSet<>();
3199
+ CodeBlockReferenceIterator destinations = block.getDestinations(monitor);
3200
+ while (destinations.hasNext()) {
3201
+ CodeBlockReference destination = destinations.next();
3202
+ CodeBlock destinationBlock = destination.getDestinationBlock();
3203
+ if (destinationBlock == null) {
3204
+ continue;
3205
+ }
3206
+ Address address = destinationBlock.getFirstStartAddress();
3207
+ if (
3208
+ !destination.getFlowType().isCall() &&
3209
+ function.getBody().contains(address)
3210
+ ) {
3211
+ successors.add(address);
3212
+ }
3213
+ }
3214
+ List<Address> ordered = new ArrayList<>(successors);
3215
+ ordered.sort(Address::compareTo);
3216
+ JsonArray values = new JsonArray();
3217
+ for (Address address : ordered) {
3218
+ values.add(canonicalAddress(address));
3219
+ }
3220
+ item.add("successors", values);
3221
+ result.add(item);
3222
+ }
3223
+ return result;
3224
+ }
3225
+
3226
+ private static JsonArray comments(List<Instruction> instructions) {
3227
+ TreeMap<String, JsonObject> observed = new TreeMap<>();
3228
+ for (Instruction instruction : instructions) {
3229
+ for (CommentType type : CommentType.values()) {
3230
+ String text = instruction.getComment(type);
3231
+ if (text == null || text.isEmpty()) {
3232
+ continue;
3233
+ }
3234
+ String kind = type == CommentType.EOL ? "inline" : "comment";
3235
+ JsonObject item = new JsonObject();
3236
+ item.addProperty("address", canonicalAddress(instruction.getAddress()));
3237
+ item.addProperty("kind", kind);
3238
+ item.addProperty("text", text);
3239
+ String key = canonicalAddress(instruction.getAddress()) + "\u0000" +
3240
+ kind + "\u0000" + type.name() + "\u0000" + text;
3241
+ observed.put(key, item);
3242
+ }
3243
+ }
3244
+ JsonArray result = new JsonArray();
3245
+ observed.values().forEach(result::add);
3246
+ return result;
3247
+ }
3248
+
3249
+ private JsonArray referencedStrings(List<Reference> references) {
3250
+ TreeMap<String, JsonObject> observed = new TreeMap<>();
3251
+ for (Reference reference : references) {
3252
+ Data data = currentProgram.getListing().getDefinedDataContaining(
3253
+ reference.getToAddress()
3254
+ );
3255
+ if (data == null || !data.hasStringValue()) {
3256
+ continue;
3257
+ }
3258
+ String value = StringDataInstance.getStringDataInstance(data).getStringValue();
3259
+ if (value == null) {
3260
+ continue;
3261
+ }
3262
+ JsonObject item = new JsonObject();
3263
+ item.addProperty("address", canonicalAddress(data.getAddress()));
3264
+ item.addProperty("value", value);
3265
+ item.addProperty("source_address", canonicalAddress(reference.getFromAddress()));
3266
+ String key = canonicalAddress(data.getAddress()) + "\u0000" + value + "\u0000" +
3267
+ canonicalAddress(reference.getFromAddress());
3268
+ observed.put(key, item);
3269
+ }
3270
+ JsonArray result = new JsonArray();
3271
+ observed.values().forEach(result::add);
3272
+ return result;
3273
+ }
3274
+
3275
+ private JsonArray referencedNames(List<Reference> references) {
3276
+ TreeMap<String, JsonObject> observed = new TreeMap<>();
3277
+ for (Reference reference : references) {
3278
+ Symbol symbol = currentProgram.getSymbolTable().getPrimarySymbol(
3279
+ reference.getToAddress()
3280
+ );
3281
+ if (symbol == null) {
3282
+ continue;
3283
+ }
3284
+ JsonObject item = new JsonObject();
3285
+ item.addProperty("address", canonicalAddress(reference.getToAddress()));
3286
+ item.addProperty("value", symbol.getName(true));
3287
+ item.addProperty("source_address", canonicalAddress(reference.getFromAddress()));
3288
+ String key = canonicalAddress(reference.getToAddress()) + "\u0000" +
3289
+ symbol.getName(true) + "\u0000" + canonicalAddress(reference.getFromAddress());
3290
+ observed.put(key, item);
3291
+ }
3292
+ JsonArray result = new JsonArray();
3293
+ observed.values().forEach(result::add);
3294
+ return result;
3295
+ }
3296
+
3297
+ private static String exclusiveAddress(Address inclusive) {
3298
+ Address next = inclusive.next();
3299
+ if (next != null && next.getAddressSpace().equals(inclusive.getAddressSpace())) {
3300
+ return canonicalAddress(next);
3301
+ }
3302
+ BigInteger offset = new BigInteger(Long.toUnsignedString(inclusive.getOffset()));
3303
+ return canonicalOffset(inclusive.getAddressSpace(), offset.add(BigInteger.ONE));
3304
+ }
3305
+
3306
+ private static String diagnosticMessage(String value) {
3307
+ if (value == null || value.isBlank()) {
3308
+ return "native decompiler returned no detail";
3309
+ }
3310
+ return value.replaceAll("[\\r\\n]+", " ");
3311
+ }
3312
+
3313
+ private JsonArray inventory(
3314
+ List<InventoryItem> inventory,
3315
+ String factsName) throws Exception {
3316
+ JsonArray result = new JsonArray();
3317
+ for (InventoryItem item : inventory) {
3318
+ monitor.checkCancelled();
3319
+ result.add(resultItem(item, factsName));
3320
+ }
3321
+ return result;
3322
+ }
3323
+
3324
+ private static JsonObject resultItem(InventoryItem item, String factsName) {
3325
+ JsonObject result = new JsonObject();
3326
+ result.addProperty("address", canonicalAddress(item.address));
3327
+ result.addProperty("value", item.value);
3328
+ if (factsName != null) {
3329
+ result.add(factsName, item.facts.deepCopy());
3330
+ }
3331
+ return result;
3332
+ }
3333
+
3334
+ private static JsonObject memoryRegion(MemoryBlock block, String imageBase) {
3335
+ JsonObject permissions = new JsonObject();
3336
+ permissions.addProperty("available", true);
3337
+ permissions.addProperty("source", "ghidra-memory-block");
3338
+ JsonObject result = new JsonObject();
3339
+ result.addProperty("name", block.getName());
3340
+ result.addProperty("start", canonicalAddress(block.getStart()));
3341
+ result.addProperty("end", exclusiveEnd(block));
3342
+ result.addProperty("readable", block.isRead());
3343
+ result.addProperty("writable", block.isWrite());
3344
+ result.addProperty("executable", block.isExecute());
3345
+ result.add("permissions", permissions);
3346
+ result.addProperty("provenance", "ghidra-memory-block");
3347
+ result.addProperty("address_space", block.getStart().getAddressSpace().getName());
3348
+ result.addProperty("image_base", imageBase);
3349
+ result.addProperty("initialized", block.isInitialized());
3350
+ result.addProperty("overlay", block.isOverlay());
3351
+ return result;
3352
+ }
3353
+
3354
+ private static JsonObject procedureIdentity(Function function) {
3355
+ JsonObject result = new JsonObject();
3356
+ result.addProperty("address", canonicalAddress(function.getEntryPoint()));
3357
+ result.addProperty("name", procedureName(function));
3358
+ result.add("classification", functionClassification(function));
3359
+ result.add("body", functionBody(function));
3360
+ return result;
3361
+ }
3362
+
3363
+ /** Observe the entire AddressSet; range endpoints are inclusive, not a guessed span. */
3364
+ private static JsonObject functionBody(Function function) {
3365
+ JsonArray ranges = new JsonArray();
3366
+ BigInteger total = BigInteger.ZERO;
3367
+ Address first = null;
3368
+ Address last = null;
3369
+ AddressSpace space = null;
3370
+ boolean multipleSpaces = false;
3371
+ AddressRangeIterator iterator = function.getBody().getAddressRanges(true);
3372
+ while (iterator.hasNext()) {
3373
+ AddressRange range = iterator.next();
3374
+ Address start = range.getMinAddress();
3375
+ Address end = range.getMaxAddress();
3376
+ if (start.getAddressSpace().getAddressableUnitSize() != 1) {
3377
+ throw new IllegalStateException("Function body byte coverage requires a byte-addressed space");
3378
+ }
3379
+ JsonObject value = new JsonObject();
3380
+ value.addProperty("start", canonicalAddress(start));
3381
+ value.addProperty("end", canonicalAddress(end));
3382
+ ranges.add(value);
3383
+ BigInteger begin = new BigInteger(Long.toUnsignedString(start.getOffset()));
3384
+ BigInteger finish = new BigInteger(Long.toUnsignedString(end.getOffset()));
3385
+ total = total.add(finish.subtract(begin).add(BigInteger.ONE));
3386
+ if (first == null) {
3387
+ first = start;
3388
+ space = start.getAddressSpace();
3389
+ } else if (!space.equals(start.getAddressSpace())) {
3390
+ multipleSpaces = true;
3391
+ }
3392
+ last = end;
3393
+ }
3394
+ JsonObject result = new JsonObject();
3395
+ result.addProperty("available", true);
3396
+ result.addProperty("provenance", "ghidra-function-body-address-set");
3397
+ result.add("ranges", ranges);
3398
+ result.addProperty("total_bytes", safeBodyByteCount(total));
3399
+ if (multipleSpaces) {
3400
+ result.add("span_bytes", JsonNull.INSTANCE);
3401
+ } else {
3402
+ BigInteger span = first == null ? BigInteger.ZERO :
3403
+ new BigInteger(Long.toUnsignedString(last.getOffset()))
3404
+ .subtract(new BigInteger(Long.toUnsignedString(first.getOffset())))
3405
+ .add(BigInteger.ONE);
3406
+ result.addProperty("span_bytes", safeBodyByteCount(span));
3407
+ }
3408
+ result.addProperty("non_contiguous", ranges.size() > 1);
3409
+ result.addProperty("contains_entry", function.getBody().contains(function.getEntryPoint()));
3410
+ return result;
3411
+ }
3412
+
3413
+ private static long safeBodyByteCount(BigInteger value) {
3414
+ if (value.signum() < 0 || value.compareTo(BigInteger.valueOf(9007199254740991L)) > 0) {
3415
+ throw new IllegalStateException("Function body byte coverage exceeds exact JSON integer range");
3416
+ }
3417
+ return value.longValueExact();
3418
+ }
3419
+
3420
+ private static String exclusiveEnd(MemoryBlock block) {
3421
+ Address next = block.getEnd().next();
3422
+ if (next != null && next.getAddressSpace().equals(block.getStart().getAddressSpace())) {
3423
+ return canonicalAddress(next);
3424
+ }
3425
+ BigInteger start = new BigInteger(Long.toUnsignedString(block.getStart().getOffset()));
3426
+ return canonicalOffset(
3427
+ block.getStart().getAddressSpace(),
3428
+ start.add(BigInteger.valueOf(block.getSize()))
3429
+ );
3430
+ }
3431
+
3432
+ private static String canonicalAddress(Address address) {
3433
+ return canonicalOffset(
3434
+ address.getAddressSpace(),
3435
+ new BigInteger(Long.toUnsignedString(address.getOffset()))
3436
+ );
3437
+ }
3438
+
3439
+ private static String canonicalOffset(AddressSpace space, BigInteger offset) {
3440
+ String value = "0x" + offset.toString(16);
3441
+ if (space.equals(sessionDefaultAddressSpace)) {
3442
+ return value;
3443
+ }
3444
+ return encodeAddressSpace(space.getName()) + ":" + value;
3445
+ }
3446
+
3447
+ private Address requireAddress(JsonObject params, String name) {
3448
+ return parseReaAddress(requireString(params, name));
3449
+ }
3450
+
3451
+ private static boolean isExplicitReaAddress(String value) {
3452
+ if (value.regionMatches(true, 0, "0x", 0, 2) &&
3453
+ isHexadecimal(value, 2, value.length()))
3454
+ return true;
3455
+ int separator = value.toLowerCase(Locale.ROOT).lastIndexOf(":0x");
3456
+ if (separator <= 0 || !isHexadecimal(value, separator + 3, value.length()))
3457
+ return false;
3458
+ // Classify the encoded space iteratively. A repeated regex alternation
3459
+ // can exhaust Java's stack on a long ordinary symbol name.
3460
+ for (int index = 0; index < separator; index++) {
3461
+ char item = value.charAt(index);
3462
+ if ((item >= 'A' && item <= 'Z') ||
3463
+ (item >= 'a' && item <= 'z') ||
3464
+ (item >= '0' && item <= '9') ||
3465
+ item == '.' || item == '_' || item == '~' || item == '-')
3466
+ continue;
3467
+ if (item != '%' || index + 2 >= separator ||
3468
+ !isHexadecimal(value, index + 1, index + 3))
3469
+ return false;
3470
+ index += 2;
3471
+ }
3472
+ return true;
3473
+ }
3474
+
3475
+ private static boolean isHexadecimal(String value, int start, int end) {
3476
+ if (start >= end) return false;
3477
+ for (int index = start; index < end; index++) {
3478
+ char item = value.charAt(index);
3479
+ if (!((item >= '0' && item <= '9') ||
3480
+ (item >= 'a' && item <= 'f') ||
3481
+ (item >= 'A' && item <= 'F')))
3482
+ return false;
3483
+ }
3484
+ return true;
3485
+ }
3486
+
3487
+ private Address parseReaAddress(String value) {
3488
+ Address address = tryParseAddress(value);
3489
+ if (address == null) {
3490
+ throw new RequestFailure("invalid_request", "Unknown or invalid Ghidra address: " + value);
3491
+ }
3492
+ return address;
3493
+ }
3494
+
3495
+ private Address tryParseAddress(String value) {
3496
+ try {
3497
+ String spaceName = null;
3498
+ String offset = value;
3499
+ int separator = value.toLowerCase(Locale.ROOT).lastIndexOf(":0x");
3500
+ if (separator >= 0) {
3501
+ spaceName = decodeAddressSpace(value.substring(0, separator));
3502
+ offset = value.substring(separator + 3);
3503
+ }
3504
+ else if (value.startsWith("0x") || value.startsWith("0X")) {
3505
+ offset = value.substring(2);
3506
+ }
3507
+ if (!isHexadecimal(offset, 0, offset.length())) {
3508
+ return null;
3509
+ }
3510
+ AddressSpace space = spaceName == null
3511
+ ? currentProgram.getAddressFactory().getDefaultAddressSpace()
3512
+ : currentProgram.getAddressFactory().getAddressSpace(spaceName);
3513
+ if (space == null) return null;
3514
+ Address address = space.getAddress(offset);
3515
+ BigInteger requestedOffset = new BigInteger(offset, 16);
3516
+ BigInteger parsedOffset = new BigInteger(Long.toUnsignedString(address.getOffset()));
3517
+ if (!requestedOffset.equals(parsedOffset))
3518
+ throw new RequestFailure("invalid_request",
3519
+ "Ghidra address offset cannot be represented without truncation in space " +
3520
+ space.getName() + ": requested " + value + ", parsed " + canonicalAddress(address));
3521
+ return address;
3522
+ }
3523
+ catch (RequestFailure failure) { throw failure; }
3524
+ catch (Exception exception) {
3525
+ return null;
3526
+ }
3527
+ }
3528
+
3529
+ private static String encodeAddressSpace(String value) {
3530
+ StringBuilder result = new StringBuilder();
3531
+ for (byte item : value.getBytes(StandardCharsets.UTF_8)) {
3532
+ int unsigned = Byte.toUnsignedInt(item);
3533
+ if ((unsigned >= 'A' && unsigned <= 'Z') ||
3534
+ (unsigned >= 'a' && unsigned <= 'z') ||
3535
+ (unsigned >= '0' && unsigned <= '9') ||
3536
+ unsigned == '.' || unsigned == '_' || unsigned == '~' || unsigned == '-') {
3537
+ result.append((char) unsigned);
3538
+ }
3539
+ else {
3540
+ result.append('%');
3541
+ result.append(Character.toUpperCase(Character.forDigit(unsigned >>> 4, 16)));
3542
+ result.append(Character.toUpperCase(Character.forDigit(unsigned & 0xf, 16)));
3543
+ }
3544
+ }
3545
+ return result.toString();
3546
+ }
3547
+
3548
+ private static String decodeAddressSpace(String value) {
3549
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
3550
+ for (int index = 0; index < value.length();) {
3551
+ char item = value.charAt(index);
3552
+ if (item == '%') {
3553
+ if (index + 2 >= value.length()) {
3554
+ throw new RequestFailure("invalid_request", "Address space encoding is invalid");
3555
+ }
3556
+ int high = Character.digit(value.charAt(index + 1), 16);
3557
+ int low = Character.digit(value.charAt(index + 2), 16);
3558
+ if (high < 0 || low < 0) {
3559
+ throw new RequestFailure("invalid_request", "Address space encoding is invalid");
3560
+ }
3561
+ bytes.write((high << 4) | low);
3562
+ index += 3;
3563
+ }
3564
+ else if ((item >= 'A' && item <= 'Z') ||
3565
+ (item >= 'a' && item <= 'z') ||
3566
+ (item >= '0' && item <= '9') ||
3567
+ item == '.' || item == '_' || item == '~' || item == '-') {
3568
+ bytes.write((byte) item);
3569
+ index += 1;
3570
+ }
3571
+ else {
3572
+ throw new RequestFailure("invalid_request", "Address space encoding is invalid");
3573
+ }
3574
+ }
3575
+ return bytes.toString(StandardCharsets.UTF_8);
3576
+ }
3577
+
3578
+ private static String normalizedSymbolType(Symbol symbol) {
3579
+ String value = symbol.getSymbolType().toString()
3580
+ .toLowerCase(Locale.ROOT)
3581
+ .replaceAll("[^a-z0-9]+", "_")
3582
+ .replaceAll("^_+|_+$", "");
3583
+ return value.isEmpty() ? "unknown" : value;
3584
+ }
3585
+
3586
+ private static String normalizedSource(SourceType source) {
3587
+ return source.name().toLowerCase(Locale.ROOT);
3588
+ }
3589
+
3590
+ private static ValueMatcher literalMatcher(String pattern, boolean caseSensitive) {
3591
+ String needle = caseSensitive ? pattern : pattern.toLowerCase(Locale.ROOT);
3592
+ return value -> {
3593
+ String candidate = caseSensitive ? value : value.toLowerCase(Locale.ROOT);
3594
+ return candidate.contains(needle);
3595
+ };
3596
+ }
3597
+
3598
+ private static ValueMatcher regexMatcher(String expression, boolean caseSensitive) {
3599
+ Pattern pattern;
3600
+ try {
3601
+ pattern = Pattern.compile(
3602
+ expression,
3603
+ caseSensitive ? 0 : Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE
3604
+ );
3605
+ }
3606
+ catch (PatternSyntaxException exception) {
3607
+ // Java wraps compiler stack exhaustion in PatternSyntaxException
3608
+ // without retaining its cause. This producer reason is not bad syntax.
3609
+ if (exception.getDescription().equals("Stack overflow during pattern compilation"))
3610
+ throw regexStackExhausted(expression, "compiling");
3611
+ throw new RequestFailure("invalid_request", "Invalid regex pattern " + expression + ": " + exception.getDescription() + " at index " + exception.getIndex());
3612
+ }
3613
+ catch (StackOverflowError exhausted) {
3614
+ throw regexStackExhausted(expression, "compiling");
3615
+ }
3616
+ return value -> {
3617
+ try {
3618
+ return pattern.matcher(value).find();
3619
+ }
3620
+ catch (StackOverflowError exhausted) {
3621
+ throw regexStackExhausted(expression, "matching");
3622
+ }
3623
+ };
3624
+ }
3625
+
3626
+ private static RequestFailure regexStackExhausted(String expression, String stage) {
3627
+ return new RequestFailure("regex_stack_exhausted",
3628
+ "Ghidra's Java regex engine exhausted its stack while " + stage +
3629
+ " pattern " + expression + ". Use literal mode or simplify the regex; the session remains usable.");
3630
+ }
3631
+
3632
+ private static SessionDescriptor readDescriptor(Path path) throws IOException {
3633
+ if (!Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS)) {
3634
+ throw new IllegalArgumentException("REA session descriptor is invalid");
3635
+ }
3636
+ JsonElement parsed = JsonParser.parseString(Files.readString(path, StandardCharsets.UTF_8));
3637
+ Set<String> keys = new HashSet<>(DESCRIPTOR_KEYS);
3638
+ if (parsed.isJsonObject() && parsed.getAsJsonObject().has("analysis_extensions"))
3639
+ keys.add("analysis_extensions");
3640
+ JsonObject object = requireObject(parsed, keys);
3641
+ return new SessionDescriptor(
3642
+ requireString(object, "transport"),
3643
+ requireString(object, "endpoint_path"),
3644
+ requireString(object, "token"),
3645
+ requireString(object, "run_id"),
3646
+ requireSha256(object, "target_sha256"),
3647
+ requireString(object, "provider_version"),
3648
+ requireString(object, "profile_digest"),
3649
+ object.has("analysis_extensions") ? object.getAsJsonArray("analysis_extensions") : new JsonArray()
3650
+ );
3651
+ }
3652
+
3653
+ private static Request parseRequest(String line, String expectedToken) {
3654
+ JsonObject object = requireObject(JsonParser.parseString(line), REQUEST_KEYS);
3655
+ int id = requireInteger(object, "id");
3656
+ if (id <= 0 || !constantTimeEquals(requireString(object, "token"), expectedToken)) {
3657
+ throw new IllegalArgumentException("Bridge request authentication failed");
3658
+ }
3659
+ JsonElement params = object.get("params");
3660
+ if (params == null || !params.isJsonObject()) {
3661
+ throw new IllegalArgumentException("Bridge request parameters are invalid");
3662
+ }
3663
+ return new Request(id, requireString(object, "method"), params.getAsJsonObject());
3664
+ }
3665
+
3666
+ private static JsonObject requireObject(JsonElement element, Set<String> keys) {
3667
+ if (!element.isJsonObject()) {
3668
+ throw new IllegalArgumentException("JSON object required");
3669
+ }
3670
+ JsonObject object = element.getAsJsonObject();
3671
+ if (!object.keySet().equals(keys)) {
3672
+ throw new IllegalArgumentException("JSON object fields are invalid");
3673
+ }
3674
+ return object;
3675
+ }
3676
+
3677
+ private static void requireKeys(JsonObject object, Set<String> keys) {
3678
+ if (!object.keySet().equals(keys)) {
3679
+ throw new RequestFailure("invalid_request", "Bridge request fields are invalid");
3680
+ }
3681
+ }
3682
+
3683
+ private void requireDocument(JsonObject params) {
3684
+ String document = optionalString(params, "document");
3685
+ if (document != null && !document.equals(currentProgram.getName())) {
3686
+ throw new RequestFailure("not_found", "Unknown Ghidra Program identity: " + document + "; active program: " + currentProgram.getName());
3687
+ }
3688
+ }
3689
+
3690
+ private static String optionalString(JsonObject object, String name) {
3691
+ JsonElement value = object.get(name);
3692
+ if (value == null || value.isJsonNull()) {
3693
+ return null;
3694
+ }
3695
+ if (!value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString()) {
3696
+ throw new RequestFailure("invalid_request", name + " must be a string or null");
3697
+ }
3698
+ String result = value.getAsString();
3699
+ if (result.isEmpty()) {
3700
+ throw new RequestFailure("invalid_request", name + " cannot be empty");
3701
+ }
3702
+ return result;
3703
+ }
3704
+
3705
+ private static String requireString(JsonObject object, String name) {
3706
+ JsonElement value = object.get(name);
3707
+ if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString()) {
3708
+ throw new RequestFailure("invalid_request", name + " must be a string");
3709
+ }
3710
+ String result = value.getAsString();
3711
+ if (result.isEmpty()) {
3712
+ throw new RequestFailure("invalid_request", name + " cannot be empty");
3713
+ }
3714
+ return result;
3715
+ }
3716
+
3717
+ private static String requireSha256(JsonObject object, String name) {
3718
+ String value = requireString(object, name);
3719
+ if (!value.matches("[a-f0-9]{64}")) {
3720
+ throw new IllegalArgumentException(name + " must be lowercase SHA-256");
3721
+ }
3722
+ return value;
3723
+ }
3724
+
3725
+ private static int requireInteger(JsonObject object, String name) {
3726
+ JsonElement value = object.get(name);
3727
+ if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isNumber()) {
3728
+ throw new IllegalArgumentException("JSON integer required");
3729
+ }
3730
+ int result = value.getAsInt();
3731
+ if (value.getAsDouble() != result) {
3732
+ throw new IllegalArgumentException("JSON integer required");
3733
+ }
3734
+ return result;
3735
+ }
3736
+
3737
+ private static int requireBoundedInteger(
3738
+ JsonObject object,
3739
+ String name,
3740
+ int minimum,
3741
+ int maximum) {
3742
+ try {
3743
+ int value = requireInteger(object, name);
3744
+ if (value < minimum || value > maximum) {
3745
+ throw new RequestFailure(
3746
+ "invalid_request",
3747
+ name + " is outside its supported range"
3748
+ );
3749
+ }
3750
+ return value;
3751
+ }
3752
+ catch (IllegalArgumentException exception) {
3753
+ throw new RequestFailure("invalid_request", name + " must be an integer");
3754
+ }
3755
+ }
3756
+
3757
+ private static boolean requireBoolean(JsonObject object, String name) {
3758
+ JsonElement value = object.get(name);
3759
+ if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isBoolean()) {
3760
+ throw new RequestFailure("invalid_request", name + " must be a boolean");
3761
+ }
3762
+ return value.getAsBoolean();
3763
+ }
3764
+
3765
+ private static boolean constantTimeEquals(String provided, String expected) {
3766
+ return MessageDigest.isEqual(
3767
+ provided.getBytes(StandardCharsets.UTF_8),
3768
+ expected.getBytes(StandardCharsets.UTF_8)
3769
+ );
3770
+ }
3771
+
3772
+ private static void writeSuccess(BufferedWriter writer, int id, JsonElement result)
3773
+ throws IOException {
3774
+ JsonObject response = new JsonObject();
3775
+ response.addProperty("id", id);
3776
+ response.addProperty("ok", true);
3777
+ response.add("result", result);
3778
+ writeResponse(writer, response);
3779
+ }
3780
+
3781
+ private static void writeFailure(
3782
+ BufferedWriter writer,
3783
+ int id,
3784
+ String code,
3785
+ String message) throws IOException {
3786
+ JsonObject error = new JsonObject();
3787
+ error.addProperty("code", code);
3788
+ error.addProperty("message", message);
3789
+ JsonObject response = new JsonObject();
3790
+ response.addProperty("id", id);
3791
+ response.addProperty("ok", false);
3792
+ response.add("error", error);
3793
+ writeResponse(writer, response);
3794
+ }
3795
+
3796
+ private static void writeResponse(BufferedWriter writer, JsonObject response) throws IOException {
3797
+ String encoded = GSON.toJson(response);
3798
+ // Gson leaves UTF-16 surrogates literal. Escape them in JSON so even
3799
+ // diagnostics quoting ill-formed caller text remain lossless UTF-8.
3800
+ int start = 0;
3801
+ for (int index = 0; index < encoded.length(); index++) {
3802
+ if (!Character.isSurrogate(encoded.charAt(index))) continue;
3803
+ writer.write(encoded, start, index - start);
3804
+ writer.write("\\u" + Integer.toHexString(encoded.charAt(index)));
3805
+ start = index + 1;
3806
+ }
3807
+ writer.write(encoded, start, encoded.length() - start);
3808
+ writer.newLine();
3809
+ writer.flush();
3810
+ }
3811
+
3812
+ private static String safeMessage(Exception exception) {
3813
+ String message = exception.getMessage();
3814
+ return message == null || message.isBlank() ? "operation failed" : message;
3815
+ }
3816
+
3817
+ private interface ValueMatcher {
3818
+ boolean matches(String value);
3819
+ }
3820
+
3821
+ private static final Comparator<InventoryItem> INVENTORY_ORDER =
3822
+ Comparator.comparing(InventoryItem::address)
3823
+ .thenComparing(InventoryItem::value)
3824
+ .thenComparing(item -> GSON.toJson(item.facts));
3825
+ private static final Comparator<Reference> REFERENCE_ORDER =
3826
+ Comparator.comparing(Reference::getFromAddress)
3827
+ .thenComparing(Reference::getToAddress)
3828
+ .thenComparing(reference -> reference.getReferenceType().getName())
3829
+ .thenComparingInt(Reference::getOperandIndex)
3830
+ .thenComparing(Reference::isPrimary);
3831
+
3832
+ private record InventoryItem(Address address, String value, JsonObject facts) {}
3833
+ private record AddressNameRequest(String inputAddress, Address address, String name) {}
3834
+ private record FunctionEntry(Function function, InventoryItem item) {}
3835
+ private record FunctionAnalysisObservation(JsonObject dossier, List<String> limitations) {}
3836
+ private record InstructionScan(List<Instruction> instructions, boolean truncated) {}
3837
+ private record SessionDescriptor(
3838
+ String transport,
3839
+ String endpointPath,
3840
+ String token,
3841
+ String runId,
3842
+ String targetSha256,
3843
+ String providerVersion,
3844
+ String profileDigest,
3845
+ JsonArray analysisExtensions
3846
+ ) {}
3847
+ private record Request(int id, String method, JsonObject params) {}
3848
+
3849
+ private static final class RequestFailure extends RuntimeException {
3850
+ private final String code;
3851
+
3852
+ RequestFailure(String code, String message) {
3853
+ super(message);
3854
+ this.code = code;
3855
+ }
3856
+ }
3857
+
3858
+ }