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