rea-agents 4.0.1 → 4.1.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/README.md +93 -30
- package/bridge/ghidra/ReaGhidraBridge.java +358 -15
- package/bridge/ghidra/ReaGhidraExtensions.java +113 -0
- package/bridge/ghidra/ReaGhidraPrepareCom.java +32 -0
- package/dist/android/AndroidTargetSnapshot.js +34 -0
- package/dist/android/JadxAnalysis.js +191 -0
- package/dist/android/JadxConfiguration.js +41 -0
- package/dist/android/JadxMcpTransport.js +145 -0
- package/dist/android/JadxProtocol.js +101 -0
- package/dist/android/JadxProvider.js +170 -0
- package/dist/android/JadxRelease.js +19 -0
- package/dist/android/JadxSession.js +97 -0
- package/dist/application/AnalysisContextQueries.js +2 -1
- package/dist/application/AnalysisProviderEvaluation.js +5 -3
- package/dist/application/AnalysisProviderRegistry.js +2 -1
- package/dist/application/AnalysisSnapshotCache.js +4 -1
- package/dist/application/AnalysisSnapshotFiles.js +1 -1
- package/dist/application/AndroidAnalysisPort.js +1 -0
- package/dist/application/AndroidAnalysisService.js +37 -0
- package/dist/application/ArtifactExtraction.js +7 -4
- package/dist/application/ArtifactGraphConstruction.js +10 -17
- package/dist/application/ArtifactInventory/scanCanonical.js +8 -7
- package/dist/application/BinarySession.js +16 -3
- package/dist/application/BinarySessionExecution.js +4 -1
- package/dist/application/BinarySessionOpen.js +3 -2
- package/dist/application/BinarySessionRecords.js +9 -3
- package/dist/application/BinaryTargetResolver.js +25 -6
- package/dist/application/BrowserObservationService.js +2 -1
- package/dist/application/BrowserScenarioCaptureService.js +2 -1
- package/dist/application/BrowserScenarioEvidence.js +2 -2
- package/dist/application/CallPathTracing.js +2 -1
- package/dist/application/CapabilityInventory.js +18 -0
- package/dist/application/ClientConfigPath.js +3 -1
- package/dist/application/ClientConfigurationDocument.js +11 -1
- package/dist/application/ClientRegistrationStatus.js +8 -1
- package/dist/application/CompositeProvider.js +1 -1
- package/dist/application/DirectAnalysis.js +23 -7
- package/dist/application/DirectAnalysisStatus.js +3 -3
- package/dist/application/Doctor.js +48 -8
- package/dist/application/DoctorDiagnostics.js +7 -5
- package/dist/application/DoctorScope.js +1 -1
- package/dist/application/ElectronActiveEvidence.js +2 -2
- package/dist/application/ElectronActiveObservationService.js +2 -1
- package/dist/application/ElectronBoundaryAnalysis.js +1 -1
- package/dist/application/ElectronObservationService.js +2 -1
- package/dist/application/EnhancedLiteralTracing.js +2 -1
- package/dist/application/EnhancedToolTypes.js +2 -1
- package/dist/application/EnhancedTools.js +3 -1
- package/dist/application/EvidenceBundleCommands.js +2 -1
- package/dist/application/EvidenceBundleFiles.js +1 -1
- package/dist/application/EvidenceInputResolver.js +30 -0
- package/dist/application/EvidenceLedger.js +2 -1
- package/dist/application/FirmwareAnalysisPort.js +1 -0
- package/dist/application/FirmwareAnalysisService.js +35 -0
- package/dist/application/FunctionAnalysisEvidence.js +32 -0
- package/dist/application/InventoryProjectionEvidence.js +2 -1
- package/dist/application/JavaScriptApplicationService.js +50 -3
- package/dist/application/JavaScriptApplicationWorkflowService.js +2 -1
- package/dist/application/JavaScriptArtifactAnalysis.js +4 -2
- package/dist/application/JavaScriptArtifactFiles.js +4 -2
- package/dist/application/JavaScriptArtifactPathResolution.js +6 -2
- package/dist/application/JavaScriptJsonModules.js +3 -2
- package/dist/application/JavaScriptModuleRelationships.js +4 -2
- package/dist/application/JavaScriptRuntimeObservationService.js +2 -1
- package/dist/application/JavaScriptRuntimeReconciliationService.js +2 -1
- package/dist/application/JavaScriptSemanticGraphProjection.js +1 -1
- package/dist/application/JavaScriptSemanticTraceService.js +2 -1
- package/dist/application/JsonFiles.js +1 -1
- package/dist/application/LazyAnalysisProvider.js +3 -1
- package/dist/application/LinuxHopper.js +12 -4
- package/dist/application/MacHopper.js +20 -7
- package/dist/application/ManagedApplicationGraphService.js +2 -1
- package/dist/application/ManagedMemberComparisonService.js +3 -1
- package/dist/application/ManagedNativeVerificationService.js +2 -1
- package/dist/application/ManagedReconstructionService.js +2 -1
- package/dist/application/NativeCallRoutes.js +1 -1
- package/dist/application/NativeDispatchMetadataInspection.js +1 -1
- package/dist/application/NativeUiActionTrace.js +2 -1
- package/dist/application/NativeValueTrace.js +1 -1
- package/dist/application/ProcessCaptureError.js +1 -1
- package/dist/application/ProcessCaptureJournal.js +3 -1
- package/dist/application/ProcessCaptureLifecycle.js +6 -3
- package/dist/application/ProcessCli.js +9 -5
- package/dist/application/ProcessEvidence.js +2 -6
- package/dist/application/ProcessHarness.js +3 -2
- package/dist/application/ProcessNormalization.js +12 -5
- package/dist/application/ProcessSampling.js +3 -1
- package/dist/application/ReconstructionCoverageService.js +2 -1
- package/dist/application/ReconstructionObligationLedgerService.js +2 -1
- package/dist/application/ReferenceSourceImportEntries.js +2 -0
- package/dist/application/ReferenceSourceImportPolicy.js +4 -2
- package/dist/application/ReferenceSourceVcsAdapter.js +35 -4
- package/dist/application/SessionProviderRouter.js +4 -1
- package/dist/application/Setup.js +1 -128
- package/dist/application/SetupClientConfiguration.js +17 -6
- package/dist/application/SetupHost.js +111 -0
- package/dist/application/SetupRegistrationEnvironment.js +1 -2
- package/dist/application/SetupSkill.js +9 -3
- package/dist/application/SetupTypes.js +17 -0
- package/dist/application/StoreFileLock.js +7 -2
- package/dist/application/SupportedClients.js +8 -0
- package/dist/application/Uninstall.js +9 -4
- package/dist/application/UpdateRuntime.js +5 -2
- package/dist/application/WindowsCapabilities.js +7 -7
- package/dist/application/runtime.js +3 -1
- package/dist/artifacts/ArtifactProvider.js +3 -1
- package/dist/artifacts/DirectoryArtifactReader.js +2 -0
- package/dist/artifacts/NativeDmgArtifactReader.js +10 -3
- package/dist/artifacts/NibArchive.js +4 -2
- package/dist/artifacts/SafeOutputTree.js +26 -10
- package/dist/artifacts/ZipArtifactReader.js +1 -0
- package/dist/browser/BrowserProcessStartup.js +3 -1
- package/dist/browser/CdpAuthorizedMainFrame.js +1 -1
- package/dist/browser/CdpBrowserProvider.js +4 -1
- package/dist/browser/CdpCaptureDocuments.js +3 -1
- package/dist/browser/CdpCaptureEventHandlers.js +6 -2
- package/dist/browser/CdpCaptureStorageFingerprints.js +1 -1
- package/dist/browser/CdpCaptureValues.js +6 -2
- package/dist/browser/CdpConnection.js +8 -6
- package/dist/browser/CdpElectronInspection.js +3 -3
- package/dist/browser/CdpElectronProvider.js +4 -1
- package/dist/browser/CdpElectronScripts.js +2 -2
- package/dist/browser/CdpEndpoint.js +22 -10
- package/dist/browser/CdpObservationSession.js +2 -1
- package/dist/browser/CdpOptionalCommand.js +1 -1
- package/dist/browser/CdpPageCapture.js +2 -1
- package/dist/browser/CdpSafeMetadata.js +53 -8
- package/dist/browser/CdpScreenshot.js +1 -1
- package/dist/browser/CdpTargetSession.js +8 -5
- package/dist/browser/CdpWebMcpDiscovery.js +1 -1
- package/dist/browser/ElectronFileScope.js +9 -3
- package/dist/browser/JavaScriptRuntimeScope.js +3 -1
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +3 -1
- package/dist/browser/PlaywrightElectronActiveActions.js +1 -1
- package/dist/browser/PlaywrightElectronActiveProvider.js +6 -2
- package/dist/browser/PlaywrightExecutionBoundary.js +1 -1
- package/dist/browser/PlaywrightScenarioArtifacts.js +10 -5
- package/dist/browser/PlaywrightScenarioBrowser.js +3 -1
- package/dist/browser/PlaywrightScenarioEvents.js +6 -2
- package/dist/browser/PlaywrightScenarioSession.js +5 -2
- package/dist/browser/V8InspectorEndpoint.js +2 -1
- package/dist/browser/V8InspectorProvider.js +6 -1
- package/dist/browser/WebSourceMapFetcher.js +24 -11
- package/dist/cli/androidCommands.js +81 -0
- package/dist/{cliApplicationCommands.js → cli/applicationCommands.js} +18 -16
- package/dist/{cliBrowserAdvancedCommands.js → cli/browserAdvancedCommands.js} +13 -16
- package/dist/{cliBrowserCommands.js → cli/browserCommands.js} +9 -9
- package/dist/cli/commandCancellation.js +24 -0
- package/dist/cli/coreAnalysisCommands.js +110 -17
- package/dist/{cliElectronCommands.js → cli/electronCommands.js} +14 -13
- package/dist/cli/firmwareCommands.js +75 -0
- package/dist/{cliSetup.js → cli/interactiveSetup.js} +1 -1
- package/dist/cli/javascriptApplicationAnalysis.js +1 -1
- package/dist/cli/managedCommands.js +1 -1
- package/dist/cli/managedProjectGraphCommand.js +1 -1
- package/dist/cli/options.js +7 -1
- package/dist/{cliProcessCommands.js → cli/processCommands.js} +3 -3
- package/dist/cli/referenceSourceImportStatus.js +10 -0
- package/dist/cli/setupCommands.js +4 -2
- package/dist/cli/utilityCommands.js +3 -2
- package/dist/cli.js +10 -15
- package/dist/cliBrowserContext.js +1 -1
- package/dist/cliBrowserScenarioCommands.js +2 -1
- package/dist/cliCommandNames.js +11 -2
- package/dist/cliEvidenceCommands.js +2 -1
- package/dist/cliJavaScriptRuntimeCommands.js +2 -1
- package/dist/cliJsonInput.js +26 -11
- package/dist/cliLogging.js +2 -1
- package/dist/cliOutput.js +15 -16
- package/dist/config/environment.js +11 -1
- package/dist/config/parseConfig.js +7 -1
- package/dist/config/parsers.js +1 -1
- package/dist/contracts/androidToolContracts.js +77 -0
- package/dist/contracts/applicationToolContracts.js +32 -5
- package/dist/contracts/applicationWorkflowInputContracts.js +13 -13
- package/dist/contracts/artifactComparisonExample.js +7 -14
- package/dist/contracts/enhancedToolContracts.js +31 -0
- package/dist/contracts/errorSchemas.js +4 -2
- package/dist/contracts/evidenceInputContracts.js +13 -0
- package/dist/contracts/firmwareToolContracts.js +38 -0
- package/dist/contracts/officialToolContracts.js +66 -0
- package/dist/contracts/promptContracts.js +1 -1
- package/dist/contracts/sessionLifecycleInputs.js +4 -0
- package/dist/contracts/sessionToolContracts.js +43 -0
- package/dist/contracts/sessionToolSchemas.js +1 -1
- package/dist/contracts/toolContractExamples.js +5 -0
- package/dist/contracts/toolContractTypes.js +2 -0
- package/dist/contracts/toolContracts.js +7 -130
- package/dist/contracts/toolEffects.js +43 -0
- package/dist/contracts/toolOutputSchemaGroups.js +4 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +1 -0
- package/dist/doctorRuntime.js +5 -1
- package/dist/domain/analysisErrorBase.js +33 -0
- package/dist/domain/analysisErrorCore.js +60 -0
- package/dist/domain/analysisErrorPresentation.js +22 -2
- package/dist/domain/analysisErrorProjection.js +24 -3
- package/dist/domain/analysisSnapshot.js +1 -0
- package/dist/domain/androidAnalysis.js +141 -0
- package/dist/domain/appleAssetCatalog.js +1 -1
- package/dist/domain/artifactGraph.js +1 -0
- package/dist/domain/artifactInspection.js +19 -17
- package/dist/domain/artifactInventoryEvidence.js +20 -25
- package/dist/domain/artifactOperationError.js +16 -0
- package/dist/domain/browserObservation.js +12 -4
- package/dist/domain/browserObservationError.js +23 -0
- package/dist/domain/browserScenario.js +1 -1
- package/dist/domain/browserScenarioCapture.js +1 -1
- package/dist/domain/browserScenarioDiff.js +1 -1
- package/dist/domain/browserScenarioNormalization.js +6 -11
- package/dist/domain/browserScenarioValues.js +3 -1
- package/dist/domain/callPath.js +47 -7
- package/dist/domain/canonicalDigest.js +65 -0
- package/dist/domain/canonicalOrdering.js +11 -0
- package/dist/domain/changedBehavior.js +8 -7
- package/dist/domain/comparisonSemantics.js +2 -4
- package/dist/domain/configurationErrors.js +25 -0
- package/dist/domain/dosCom.js +8 -0
- package/dist/domain/electronActiveObservation.js +3 -1
- package/dist/domain/electronStaticAnalysisBrowser.js +13 -23
- package/dist/domain/electronStaticAnalysisNative.js +2 -1
- package/dist/domain/electronStaticAnalysisValues.js +23 -3
- package/dist/domain/evidence.js +3 -11
- package/dist/domain/evidenceErrors.js +30 -0
- package/dist/domain/firmwareAnalysis.js +121 -0
- package/dist/domain/functionComparison.js +4 -2
- package/dist/domain/functionComparisonDimensions.js +6 -4
- package/dist/domain/functionComparisonNormalization.js +8 -1
- package/dist/domain/functionComparisonResults.js +1 -4
- package/dist/domain/hopperErrors.js +78 -0
- package/dist/domain/hopperValues.js +2 -1
- package/dist/domain/interfaceBuilderGraph.js +3 -1
- package/dist/domain/javascriptApplicationChangeGraph.js +2 -2
- package/dist/domain/javascriptApplicationGraph.js +13 -22
- package/dist/domain/javascriptApplicationVersionComparison.js +3 -11
- package/dist/domain/javascriptApplicationVersionItems.js +3 -3
- package/dist/domain/javascriptApplicationVersionKeys.js +1 -1
- package/dist/domain/javascriptAstFingerprint.js +1 -1
- package/dist/domain/javascriptExportShapeComparison.js +1 -2
- package/dist/domain/javascriptExportShapeSelection.js +1 -1
- package/dist/domain/javascriptExportShapeVariants.js +7 -3
- package/dist/domain/javascriptFeatureSeed.js +29 -9
- package/dist/domain/javascriptFeatureTrace.js +9 -13
- package/dist/domain/javascriptFeatureTraversal.js +2 -1
- package/dist/domain/javascriptNativeHandoff.js +2 -1
- package/dist/domain/javascriptRuntimeLoadState.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationActive.js +3 -10
- package/dist/domain/javascriptRuntimeReconciliationGraph.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationMatching.js +4 -3
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +8 -14
- package/dist/domain/javascriptRuntimeReconciliationResult.js +4 -4
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +5 -2
- package/dist/domain/javascriptRuntimeStaticCandidates.js +8 -4
- package/dist/domain/javascriptSemanticAnalysis.js +40 -3
- package/dist/domain/javascriptSemanticCallResolution.js +2 -1
- package/dist/domain/javascriptSemanticChildProcesses.js +2 -1
- package/dist/domain/javascriptSemanticDataEffects.js +2 -1
- package/dist/domain/javascriptSemanticFingerprints.js +1 -1
- package/dist/domain/javascriptSemanticGraph.js +23 -28
- package/dist/domain/javascriptSemanticPrimitives.js +9 -2
- package/dist/domain/javascriptSemanticProjection.js +2 -1
- package/dist/domain/javascriptSemanticPromises.js +2 -1
- package/dist/domain/javascriptSemanticProvenance.js +1 -1
- package/dist/domain/javascriptSemanticQuery.js +7 -8
- package/dist/domain/javascriptSemanticQueryAssessment.js +1 -2
- package/dist/domain/javascriptSemanticQueryIdentity.js +1 -2
- package/dist/domain/javascriptSemanticReturns.js +2 -1
- package/dist/domain/javascriptSemanticValues.js +50 -16
- package/dist/domain/javascriptSourceParser.js +4 -2
- package/dist/domain/javascriptStaticAnalysis.js +1 -1
- package/dist/domain/javascriptStaticAnalysisBundler.js +2 -1
- package/dist/domain/javascriptStaticAnalysisCalls.js +14 -6
- package/dist/domain/javascriptStaticAnalysisHelpers.js +31 -4
- package/dist/domain/jsonShape.js +6 -2
- package/dist/domain/managedMemberComparison.js +1 -0
- package/dist/domain/managedMemberComparisonItems.js +2 -0
- package/dist/domain/managedNativeVerificationMatch.js +5 -3
- package/dist/domain/nativeDataType.js +2 -0
- package/dist/domain/nativeFunctionAnnotations.js +43 -0
- package/dist/domain/nativeLoadImage.js +95 -0
- package/dist/domain/nativeMetadataRecovery.js +55 -0
- package/dist/domain/processCapture.js +0 -1
- package/dist/domain/processEvidenceProvider.js +11 -0
- package/dist/domain/processTraceComparison.js +1 -1
- package/dist/domain/providerAdapterError.js +17 -0
- package/dist/domain/providerCleanupError.js +1 -1
- package/dist/domain/providerSelectionError.js +44 -0
- package/dist/domain/reconstructionUnknowns.js +1 -1
- package/dist/domain/reconstructionVerification.js +22 -8
- package/dist/domain/referenceSourceClassification.js +5 -6
- package/dist/domain/referenceSourceGraph.js +9 -17
- package/dist/domain/referenceSourceImportParsing.js +38 -23
- package/dist/domain/runtimeIdentification.js +1 -1
- package/dist/domain/runtimeVersion.js +4 -7
- package/dist/domain/safeJson.js +13 -0
- package/dist/domain/sourceToBundleComparison.js +5 -7
- package/dist/domain/sourceToBundleSignals.js +6 -6
- package/dist/domain/staticRuntimeCorrelation.js +9 -7
- package/dist/domain/unknownRegistryError.js +10 -0
- package/dist/domain/webBundleAnalyzerAst.js +22 -5
- package/dist/domain/webBundleAnalyzerInspection.js +20 -14
- package/dist/dotnet/ManagedMemberRows.js +1 -1
- package/dist/dotnet/ManagedMetadataHeaps.js +3 -2
- package/dist/dotnet/ManagedMetadataInventoryRows.js +4 -2
- package/dist/dotnet/ManagedMethodBodyReader.js +8 -1
- package/dist/dotnet/ManagedStaticProvider.js +3 -1
- package/dist/firmware/FirmwareCommand.js +163 -0
- package/dist/firmware/FirmwareFiles.js +190 -0
- package/dist/firmware/FirmwareProvider.js +258 -0
- package/dist/firmware/FirmwarePublication.js +84 -0
- package/dist/firmware/FirmwareRelease.js +32 -0
- package/dist/firmware/FirmwareReports.js +223 -0
- package/dist/generatedMcpToolCatalog.js +1 -1
- package/dist/generatedPackageMetadata.js +5 -5
- package/dist/ghidra/GhidraAnalysisProfile.js +37 -8
- package/dist/ghidra/GhidraClient.js +52 -6
- package/dist/ghidra/GhidraDiagnostics.js +3 -0
- package/dist/ghidra/GhidraDoctor.js +38 -14
- package/dist/ghidra/GhidraFunctionValues.js +10 -4
- package/dist/ghidra/GhidraInstallation.js +10 -4
- package/dist/ghidra/GhidraInventoryValues.js +39 -2
- package/dist/ghidra/GhidraLauncher.js +91 -26
- package/dist/ghidra/GhidraLoadImageValues.js +199 -0
- package/dist/ghidra/GhidraProvider.js +129 -13
- package/dist/ghidra/GhidraProviderCapabilities.js +52 -15
- package/dist/ghidra/GhidraRequestQueue.js +2 -2
- package/dist/ghidra/GhidraSessionError.js +31 -4
- package/dist/ghidra/GhidraSessionValues.js +10 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +16 -2
- package/dist/ghidra/GhidraTransport.js +12 -0
- package/dist/ghidra/extensions/GhidraExtensionFailures.js +20 -0
- package/dist/ghidra/extensions/GhidraExtensions.js +170 -0
- package/dist/ghidra/extensions/nativeaot/NativeAotAdapter.js +21 -0
- package/dist/ghidra/extensions/nativeaot/NativeAotReport.js +84 -0
- package/dist/hopper/BridgeLauncher.js +4 -2
- package/dist/hopper/HopperAnalysisProfile.js +7 -2
- package/dist/hopper/HopperCleanup.js +55 -9
- package/dist/hopper/HopperClient.js +14 -4
- package/dist/hopper/HopperProcessDiagnostic.js +1 -1
- package/dist/hopper/HopperProvider.js +8 -4
- package/dist/hopper/HopperRequestQueue.js +7 -3
- package/dist/hopper/HopperSessionValues.js +1 -1
- package/dist/hopper/HopperSocketConnection.js +1 -1
- package/dist/hopper/HopperTargetLease.js +23 -15
- package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +7 -8
- package/dist/hopper/LinuxPrivateDisplayProbe.js +6 -2
- package/dist/hopper/protocol.js +8 -8
- package/dist/ida/IdaConfiguration.js +56 -0
- package/dist/ida/IdaDoctor.js +53 -0
- package/dist/ida/IdaInput.js +62 -0
- package/dist/ida/IdaMcpConnection.js +111 -0
- package/dist/ida/IdaOperationRunner.js +299 -0
- package/dist/ida/IdaProtocolValues.js +178 -0
- package/dist/ida/IdaProvider.js +126 -0
- package/dist/ida/IdaProviderCapabilities.js +60 -0
- package/dist/ida/IdaSessionClient.js +306 -0
- package/dist/ida/IdaWorkspace.js +54 -0
- package/dist/main/shutdown.js +21 -1
- package/dist/main/startup.js +1 -1
- package/dist/main/transport.js +8 -4
- package/dist/main.js +5 -2
- package/dist/native/AppleDispatchMetadata.js +31 -10
- package/dist/native/NativeMacOSProvider.js +87 -15
- package/dist/native/NativeMachoInspection.js +42 -12
- package/dist/native/NativeUiHelperRuntime.js +7 -1
- package/dist/native/NativeUiObservation.js +6 -2
- package/dist/native/parsers/demangle.js +1 -1
- package/dist/native/parsers/dyldInfo.js +3 -3
- package/dist/native/parsers/otool.js +4 -1
- package/dist/native/parsers/plist.js +11 -3
- package/dist/process/PrivateRuntimeRoot.js +13 -3
- package/dist/process/ProcessOwnership.js +5 -73
- package/dist/process/ProcessOwnershipObservation.js +62 -2
- package/dist/process/ProcessOwnershipProcessTree.js +13 -0
- package/dist/process/ProviderProcess.js +24 -7
- package/dist/process/ProviderRunLineage.js +1 -1
- package/dist/process/WindowsAuthority.js +20 -1
- package/dist/reference/ReferenceSourceReaderEntries.js +5 -1
- package/dist/reference/ReferenceSourceReaderFile.js +1 -0
- package/dist/server/createServer.js +25 -2
- package/dist/server/mcpProgress.js +2 -1
- package/dist/server/promptCompletion.js +3 -3
- package/dist/server/registerAndroidTools.js +30 -0
- package/dist/server/registerApplicationTools/compareExportShapes.js +5 -1
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +7 -2
- package/dist/server/registerApplicationTools/compareVersions.js +5 -1
- package/dist/server/registerApplicationTools/traceFeature.js +7 -2
- package/dist/server/registerApplicationTools/traceSemantics.js +7 -2
- package/dist/server/registerEnhancedTools.js +28 -1
- package/dist/server/registerFirmwareTools.js +22 -0
- package/dist/server/registerFunctionComparisonTool.js +1 -1
- package/dist/server/registerInvestigationTools/helpers.js +2 -1
- package/dist/server/registerManagedTools.js +1 -1
- package/dist/server/registerManagedWorkflowTools/evidence.js +1 -1
- package/dist/server/registerOfficialTools.js +4 -1
- package/dist/server/registerProcessComparisonTool.js +5 -2
- package/dist/server/registerSessionTools.js +3 -1
- package/dist/server/runDerivedOperation.js +1 -1
- package/dist/server/sessionAvailabilityPolicy.js +3 -0
- package/dist/server/sessionEvidence.js +1 -1
- package/dist/server/toolResult.js +2 -1
- package/dist/windows/WindowsNativeLoader.js +108 -0
- package/dist/windows/WindowsOwnedProcess.js +168 -0
- package/dist/windows/WindowsPrivateRuntime.js +137 -0
- package/install.sh +4 -6
- package/native/windows/README.md +102 -0
- package/native/windows/build/manifest.json +9 -0
- package/native/windows/build/rea-windows-x64.node +0 -0
- package/package.json +38 -24
- package/scripts/rea.mjs +1 -1
- package/scripts/verify-windows-native-artifact.mjs +39 -0
- package/skills/reverse-engineer-anything/SKILL.md +92 -24
- package/skills/reverse-engineer-anything/references/android-applications.md +31 -0
- package/skills/reverse-engineer-anything/references/javascript-applications.md +19 -2
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +52 -4
- package/dist/browser/CdpCanonicalJsonDigest.js +0 -9
- package/dist/domain/errors.js +0 -324
|
@@ -73,6 +73,11 @@ import ghidra.program.model.listing.Instruction;
|
|
|
73
73
|
import ghidra.program.model.listing.InstructionIterator;
|
|
74
74
|
import ghidra.program.model.listing.Variable;
|
|
75
75
|
import ghidra.program.model.mem.MemoryBlock;
|
|
76
|
+
import ghidra.program.model.mem.MemoryBlockSourceInfo;
|
|
77
|
+
import ghidra.program.database.mem.FileBytes;
|
|
78
|
+
import ghidra.program.model.address.SegmentedAddress;
|
|
79
|
+
import ghidra.program.model.reloc.Relocation;
|
|
80
|
+
import java.io.ByteArrayOutputStream;
|
|
76
81
|
import ghidra.program.model.symbol.RefType;
|
|
77
82
|
import ghidra.program.model.symbol.Reference;
|
|
78
83
|
import ghidra.program.model.symbol.ReferenceIterator;
|
|
@@ -112,6 +117,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
112
117
|
"params"
|
|
113
118
|
);
|
|
114
119
|
private static final String[] CAPABILITIES = {
|
|
120
|
+
"annotate_native_function",
|
|
121
|
+
"inspect_native_load_image",
|
|
122
|
+
"read_bytes",
|
|
123
|
+
"address_to_file_offset",
|
|
115
124
|
"ping",
|
|
116
125
|
"shutdown",
|
|
117
126
|
"address_name",
|
|
@@ -142,6 +151,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
142
151
|
private List<FunctionEntry> procedureInventory;
|
|
143
152
|
private List<InventoryItem> stringInventory;
|
|
144
153
|
private DecompInterface decompiler;
|
|
154
|
+
private final ReaGhidraExtensions analysisExtensions = new ReaGhidraExtensions();
|
|
145
155
|
private static AddressSpace sessionDefaultAddressSpace;
|
|
146
156
|
|
|
147
157
|
@Override
|
|
@@ -152,12 +162,24 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
152
162
|
}
|
|
153
163
|
Path descriptorPath = Path.of(arguments[0]);
|
|
154
164
|
SessionDescriptor descriptor = readDescriptor(descriptorPath);
|
|
155
|
-
|
|
165
|
+
// Windows retains a native immutable bearer descriptor lease until
|
|
166
|
+
// runtime_close; POSIX removes it after this read. Normal cleanup removes
|
|
167
|
+
// the Windows file, while abrupt owner death can leave private residue.
|
|
168
|
+
// Its protected runtime DACL also protects the bearer token while the
|
|
169
|
+
// bridge is running. POSIX keeps its existing consume-and-delete flow.
|
|
170
|
+
if (!isWindowsHost()) {
|
|
171
|
+
Files.deleteIfExists(descriptorPath);
|
|
172
|
+
}
|
|
156
173
|
if (currentProgram == null) {
|
|
157
174
|
throw new IllegalStateException("REA bridge requires an imported program");
|
|
158
175
|
}
|
|
159
176
|
sessionDefaultAddressSpace =
|
|
160
177
|
currentProgram.getAddressFactory().getDefaultAddressSpace();
|
|
178
|
+
if (currentProgram.getExecutableFormat().equals("Raw Binary") &&
|
|
179
|
+
!currentProgram.getOptions(ghidra.program.model.listing.Program.PROGRAM_INFO)
|
|
180
|
+
.getBoolean("REA DOS COM prepared", false)) {
|
|
181
|
+
throw new IllegalStateException("REA DOS COM entry/context preparation failed");
|
|
182
|
+
}
|
|
161
183
|
if (!Application.getApplicationVersion().equals(descriptor.providerVersion)) {
|
|
162
184
|
throw new IllegalStateException("Ghidra provider version does not match the session");
|
|
163
185
|
}
|
|
@@ -171,7 +193,13 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
171
193
|
"Ghidra imported-byte digest does not match the admitted target"
|
|
172
194
|
);
|
|
173
195
|
}
|
|
196
|
+
// GhidraScript wraps run() in a transaction. End it before serving so
|
|
197
|
+
// each edit owns a real outer transaction and can roll back immediately.
|
|
198
|
+
end(true);
|
|
174
199
|
try {
|
|
200
|
+
if (!descriptor.transport.equals("unix-socket") && descriptor.analysisExtensions.size() > 0)
|
|
201
|
+
throw new IOException("Windows Ghidra P0 does not admit metadata recovery or database mutation.");
|
|
202
|
+
analysisExtensions.analyze(descriptor.analysisExtensions, currentProgram, monitor);
|
|
175
203
|
initializeDecompiler();
|
|
176
204
|
serve(descriptor);
|
|
177
205
|
}
|
|
@@ -180,6 +208,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
180
208
|
decompiler.dispose();
|
|
181
209
|
decompiler = null;
|
|
182
210
|
}
|
|
211
|
+
analysisExtensions.close();
|
|
183
212
|
}
|
|
184
213
|
}
|
|
185
214
|
|
|
@@ -219,18 +248,23 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
219
248
|
JsonObject endpoint = new JsonObject();
|
|
220
249
|
endpoint.addProperty("host", "127.0.0.1");
|
|
221
250
|
endpoint.addProperty("port", address.getPort());
|
|
251
|
+
// The Windows native reader refuses a file while a writer still
|
|
252
|
+
// owns it. Closing CREATE_NEW publishes the complete record without
|
|
253
|
+
// requesting rename rights on the retained private directory guard.
|
|
222
254
|
Files.writeString(
|
|
223
|
-
pendingPath,
|
|
255
|
+
isWindowsHost() ? endpointPath : pendingPath,
|
|
224
256
|
GSON.toJson(endpoint) + "\n",
|
|
225
257
|
StandardCharsets.UTF_8,
|
|
226
258
|
StandardOpenOption.CREATE_NEW,
|
|
227
259
|
StandardOpenOption.WRITE
|
|
228
260
|
);
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
261
|
+
if (!isWindowsHost()) {
|
|
262
|
+
try {
|
|
263
|
+
Files.move(pendingPath, endpointPath, StandardCopyOption.ATOMIC_MOVE);
|
|
264
|
+
}
|
|
265
|
+
catch (AtomicMoveNotSupportedException exception) {
|
|
266
|
+
Files.move(pendingPath, endpointPath);
|
|
267
|
+
}
|
|
234
268
|
}
|
|
235
269
|
acceptClient(server, descriptor);
|
|
236
270
|
}
|
|
@@ -240,6 +274,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
240
274
|
}
|
|
241
275
|
}
|
|
242
276
|
|
|
277
|
+
private static boolean isWindowsHost() {
|
|
278
|
+
return System.getProperty("os.name", "").startsWith("Windows");
|
|
279
|
+
}
|
|
280
|
+
|
|
243
281
|
private void acceptClient(
|
|
244
282
|
ServerSocketChannel server,
|
|
245
283
|
SessionDescriptor descriptor) throws Exception {
|
|
@@ -309,6 +347,9 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
309
347
|
);
|
|
310
348
|
}
|
|
311
349
|
return switch (request.method) {
|
|
350
|
+
case "inspect_native_load_image" -> inspectNativeLoadImage(request.params);
|
|
351
|
+
case "read_bytes" -> readMemoryBytes(request.params);
|
|
352
|
+
case "address_to_file_offset" -> addressToFileOffset(request.params);
|
|
312
353
|
case "address_name" -> addressName(request.params);
|
|
313
354
|
case "list_documents" -> listDocuments(request.params);
|
|
314
355
|
case "list_names" -> listNames(request.params);
|
|
@@ -331,6 +372,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
331
372
|
case "resolve_native_call_targets" -> resolveNativeCallTargets(request.params);
|
|
332
373
|
case "xrefs" -> xrefs(request.params);
|
|
333
374
|
case "analyze_function" -> analyzeFunction(request.params);
|
|
375
|
+
case "annotate_native_function" -> {
|
|
376
|
+
if (!descriptor.transport.equals("unix-socket"))
|
|
377
|
+
throw new RequestFailure("method_unavailable", "Windows P0 does not admit database mutation");
|
|
378
|
+
yield annotateNativeFunction(request.params);
|
|
379
|
+
}
|
|
334
380
|
default -> throw new RequestFailure(
|
|
335
381
|
"method_unavailable",
|
|
336
382
|
"Bridge method is unavailable"
|
|
@@ -363,11 +409,217 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
363
409
|
result.addProperty("run_id", descriptor.runId);
|
|
364
410
|
result.addProperty("profile_digest", descriptor.profileDigest);
|
|
365
411
|
result.add("provider", provider);
|
|
366
|
-
|
|
412
|
+
boolean readOnly = !descriptor.transport.equals("unix-socket");
|
|
413
|
+
result.addProperty("read_only", readOnly);
|
|
367
414
|
result.addProperty("analysis_complete", !timedOut);
|
|
368
415
|
result.addProperty("analysis_timed_out", timedOut);
|
|
369
|
-
result.add("capabilities", GSON.toJsonTree(
|
|
416
|
+
result.add("capabilities", GSON.toJsonTree(
|
|
417
|
+
java.util.Arrays.stream(CAPABILITIES)
|
|
418
|
+
.filter(value -> !readOnly || !value.equals("annotate_native_function"))
|
|
419
|
+
.toArray(String[]::new)
|
|
420
|
+
));
|
|
370
421
|
result.add("target", target);
|
|
422
|
+
JsonArray extensions = analysisExtensions.reports();
|
|
423
|
+
if (extensions.size() > 0) result.add("analysis_extensions", extensions);
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private static String bytesHex(byte[] bytes) {
|
|
428
|
+
return java.util.HexFormat.of().formatHex(bytes);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private String hashFileBytes(FileBytes source, boolean modified) throws Exception {
|
|
432
|
+
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
|
433
|
+
byte[] chunk = new byte[65536];
|
|
434
|
+
for (long offset = 0; offset < source.getSize();) {
|
|
435
|
+
monitor.checkCancelled();
|
|
436
|
+
int length = (int) Math.min(chunk.length, source.getSize() - offset);
|
|
437
|
+
int read = modified
|
|
438
|
+
? source.getModifiedBytes(offset, chunk, 0, length)
|
|
439
|
+
: source.getOriginalBytes(offset, chunk, 0, length);
|
|
440
|
+
if (read != length) throw new IOException("Incomplete FileBytes read at " + offset);
|
|
441
|
+
digest.update(chunk, 0, read);
|
|
442
|
+
offset += read;
|
|
443
|
+
}
|
|
444
|
+
return bytesHex(digest.digest());
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
private String hashMemory(Address start, long length) throws Exception {
|
|
448
|
+
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
|
449
|
+
byte[] chunk = new byte[65536];
|
|
450
|
+
for (long offset = 0; offset < length;) {
|
|
451
|
+
monitor.checkCancelled();
|
|
452
|
+
int count = (int) Math.min(chunk.length, length - offset);
|
|
453
|
+
int read = currentProgram.getMemory().getBytes(start.add(offset), chunk, 0, count);
|
|
454
|
+
if (read != count) throw new IOException("Incomplete memory read at " + start.add(offset));
|
|
455
|
+
digest.update(chunk, 0, read);
|
|
456
|
+
offset += read;
|
|
457
|
+
}
|
|
458
|
+
return bytesHex(digest.digest());
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
private JsonObject inspectNativeLoadImage(JsonObject params) throws Exception {
|
|
462
|
+
requireKeys(params, Set.of());
|
|
463
|
+
JsonObject result = new JsonObject();
|
|
464
|
+
JsonArray recovery = analysisExtensions.inspectImage();
|
|
465
|
+
if (recovery.size() > 0) result.add("metadata_recovery", recovery);
|
|
466
|
+
result.addProperty("executable_format", currentProgram.getExecutableFormat());
|
|
467
|
+
result.addProperty("language_id", currentProgram.getLanguageID().getIdAsString());
|
|
468
|
+
result.addProperty("compiler_spec_id", currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString());
|
|
469
|
+
result.addProperty("image_base", canonicalAddress(currentProgram.getImageBase()));
|
|
470
|
+
result.addProperty("default_address_space", sessionDefaultAddressSpace.getName());
|
|
471
|
+
List<FileBytes> files = currentProgram.getMemory().getAllFileBytes();
|
|
472
|
+
JsonArray sources = new JsonArray();
|
|
473
|
+
for (FileBytes file : files) {
|
|
474
|
+
JsonObject source = new JsonObject();
|
|
475
|
+
source.addProperty("name", file.getFilename());
|
|
476
|
+
source.addProperty("size", file.getSize());
|
|
477
|
+
source.addProperty("original_sha256", hashFileBytes(file, false));
|
|
478
|
+
source.addProperty("modified_sha256", hashFileBytes(file, true));
|
|
479
|
+
sources.add(source);
|
|
480
|
+
}
|
|
481
|
+
result.add("source_files", sources);
|
|
482
|
+
JsonArray mappings = new JsonArray();
|
|
483
|
+
for (MemoryBlock block : currentProgram.getMemory().getBlocks()) {
|
|
484
|
+
for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
|
|
485
|
+
monitor.checkCancelled();
|
|
486
|
+
JsonObject mapping = new JsonObject();
|
|
487
|
+
mapping.addProperty("block", block.getName());
|
|
488
|
+
mapping.addProperty("start", canonicalAddress(source.getMinAddress()));
|
|
489
|
+
mapping.addProperty("end", canonicalAddress(source.getMaxAddress()));
|
|
490
|
+
mapping.addProperty("address_space", source.getMinAddress().getAddressSpace().getName());
|
|
491
|
+
mapping.addProperty("initialized", block.isInitialized());
|
|
492
|
+
mapping.addProperty("loaded", block.isLoaded());
|
|
493
|
+
mapping.addProperty("overlay", block.isOverlay());
|
|
494
|
+
mapping.addProperty("length", source.getLength());
|
|
495
|
+
FileBytes backing = source.getFileBytes().orElse(null);
|
|
496
|
+
int sourceIndex = backing == null ? -1 : files.indexOf(backing);
|
|
497
|
+
if (sourceIndex < 0) mapping.add("source_file_index", JsonNull.INSTANCE);
|
|
498
|
+
else mapping.addProperty("source_file_index", sourceIndex);
|
|
499
|
+
long offset = source.getFileBytesOffset();
|
|
500
|
+
if (offset < 0) mapping.add("file_offset", JsonNull.INSTANCE);
|
|
501
|
+
else mapping.addProperty("file_offset", offset);
|
|
502
|
+
if (block.isInitialized()) mapping.addProperty("sha256", hashMemory(source.getMinAddress(), source.getLength()));
|
|
503
|
+
else mapping.add("sha256", JsonNull.INSTANCE);
|
|
504
|
+
mappings.add(mapping);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
result.add("mappings", mappings);
|
|
508
|
+
JsonArray relocations = new JsonArray();
|
|
509
|
+
Iterator<Relocation> iterator = currentProgram.getRelocationTable().getRelocations();
|
|
510
|
+
while (iterator.hasNext()) {
|
|
511
|
+
monitor.checkCancelled();
|
|
512
|
+
Relocation relocation = iterator.next();
|
|
513
|
+
Address location = relocation.getAddress();
|
|
514
|
+
JsonObject row = new JsonObject();
|
|
515
|
+
row.addProperty("address", canonicalAddress(location));
|
|
516
|
+
if (location instanceof SegmentedAddress segmented) {
|
|
517
|
+
row.addProperty("segment", segmented.getSegment());
|
|
518
|
+
row.addProperty("offset", segmented.getSegmentOffset());
|
|
519
|
+
} else {
|
|
520
|
+
row.add("segment", JsonNull.INSTANCE);
|
|
521
|
+
row.add("offset", JsonNull.INSTANCE);
|
|
522
|
+
}
|
|
523
|
+
row.addProperty("status", relocation.getStatus().toString());
|
|
524
|
+
row.addProperty("type", relocation.getType());
|
|
525
|
+
row.add("values", GSON.toJsonTree(relocation.getValues() == null ? new long[0] : relocation.getValues()));
|
|
526
|
+
byte[] original = relocation.getBytes();
|
|
527
|
+
row.add("original_bytes_hex", original == null ? JsonNull.INSTANCE : GSON.toJsonTree(bytesHex(original)));
|
|
528
|
+
if (original != null && original.length > 0) {
|
|
529
|
+
byte[] memory = new byte[original.length];
|
|
530
|
+
int read = currentProgram.getMemory().getBytes(location, memory);
|
|
531
|
+
row.add("memory_bytes_hex", read == memory.length ? GSON.toJsonTree(bytesHex(memory)) : JsonNull.INSTANCE);
|
|
532
|
+
} else row.add("memory_bytes_hex", JsonNull.INSTANCE);
|
|
533
|
+
relocations.add(row);
|
|
534
|
+
}
|
|
535
|
+
result.add("relocations", relocations);
|
|
536
|
+
List<Address> entries = new ArrayList<>();
|
|
537
|
+
var entryIterator = currentProgram.getSymbolTable().getExternalEntryPointIterator();
|
|
538
|
+
while (entryIterator.hasNext()) entries.add(entryIterator.next());
|
|
539
|
+
entries.sort(Address::compareTo);
|
|
540
|
+
JsonArray entryPoints = new JsonArray();
|
|
541
|
+
for (Address entry : entries) entryPoints.add(canonicalAddress(entry));
|
|
542
|
+
result.add("entry_points", entryPoints);
|
|
543
|
+
JsonArray contexts = new JsonArray();
|
|
544
|
+
for (Address entry : entries) {
|
|
545
|
+
JsonObject context = new JsonObject();
|
|
546
|
+
context.addProperty("address", canonicalAddress(entry));
|
|
547
|
+
JsonArray registers = new JsonArray();
|
|
548
|
+
for (String name : new String[] { "CS", "DS", "ES", "SS" }) {
|
|
549
|
+
Register register = currentProgram.getRegister(name);
|
|
550
|
+
if (register == null) continue;
|
|
551
|
+
JsonObject row = new JsonObject();
|
|
552
|
+
row.addProperty("name", name);
|
|
553
|
+
BigInteger value = currentProgram.getProgramContext().getValue(register, entry, false);
|
|
554
|
+
row.add("value_hex", value == null ? JsonNull.INSTANCE : GSON.toJsonTree("0x" + value.toString(16)));
|
|
555
|
+
registers.add(row);
|
|
556
|
+
}
|
|
557
|
+
context.add("registers", registers);
|
|
558
|
+
contexts.add(context);
|
|
559
|
+
}
|
|
560
|
+
result.add("entry_context", contexts);
|
|
561
|
+
return result;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
private JsonObject readMemoryBytes(JsonObject params) throws Exception {
|
|
565
|
+
requireKeys(params, Set.of("document", "address", "length"));
|
|
566
|
+
requireDocument(params);
|
|
567
|
+
Address start = requireAddress(params, "address");
|
|
568
|
+
JsonElement requested = params.get("length");
|
|
569
|
+
long length;
|
|
570
|
+
try {
|
|
571
|
+
if (requested == null || !requested.isJsonPrimitive() || !requested.getAsJsonPrimitive().isNumber())
|
|
572
|
+
throw new IllegalArgumentException();
|
|
573
|
+
length = requested.getAsBigDecimal().longValueExact();
|
|
574
|
+
if (length < 1 || length > 9007199254740991L) throw new IllegalArgumentException();
|
|
575
|
+
} catch (RuntimeException failure) {
|
|
576
|
+
throw new RequestFailure("invalid_request", "Byte length must be a positive safe integer");
|
|
577
|
+
}
|
|
578
|
+
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
|
579
|
+
byte[] chunk = new byte[65536];
|
|
580
|
+
for (long offset = 0; offset < length;) {
|
|
581
|
+
monitor.checkCancelled();
|
|
582
|
+
Address at;
|
|
583
|
+
try { at = start.addNoWrap(offset); }
|
|
584
|
+
catch (Exception endOfSpace) { break; }
|
|
585
|
+
MemoryBlock block = currentProgram.getMemory().getBlock(at);
|
|
586
|
+
if (block == null || !block.isInitialized()) break;
|
|
587
|
+
int count = (int) Math.min(chunk.length, Math.min(length - offset, block.getEnd().subtract(at) + 1));
|
|
588
|
+
int read = currentProgram.getMemory().getBytes(at, chunk, 0, count);
|
|
589
|
+
bytes.write(chunk, 0, read);
|
|
590
|
+
if (read != count) break;
|
|
591
|
+
offset += read;
|
|
592
|
+
}
|
|
593
|
+
JsonObject result = new JsonObject();
|
|
594
|
+
result.addProperty("address", canonicalAddress(start));
|
|
595
|
+
result.addProperty("requested_bytes", length);
|
|
596
|
+
result.addProperty("returned_bytes", bytes.size());
|
|
597
|
+
result.addProperty("bytes_hex", bytesHex(bytes.toByteArray()));
|
|
598
|
+
result.addProperty("complete", bytes.size() == length);
|
|
599
|
+
return result;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
private JsonObject addressToFileOffset(JsonObject params) {
|
|
603
|
+
requireKeys(params, Set.of("document", "address"));
|
|
604
|
+
requireDocument(params);
|
|
605
|
+
Address address = requireAddress(params, "address");
|
|
606
|
+
MemoryBlock block = currentProgram.getMemory().getBlock(address);
|
|
607
|
+
if (block == null || !block.isInitialized())
|
|
608
|
+
throw new RequestFailure("invalid_request", "Address has no initialized file-backed memory: " + canonicalAddress(address));
|
|
609
|
+
Long fileOffset = null;
|
|
610
|
+
int mappings = 0;
|
|
611
|
+
for (MemoryBlockSourceInfo source : block.getSourceInfos()) {
|
|
612
|
+
if (!source.contains(address) || source.getFileBytes().isEmpty()) continue;
|
|
613
|
+
long offset = source.getFileBytesOffset(address);
|
|
614
|
+
if (offset < 0) continue;
|
|
615
|
+
fileOffset = offset;
|
|
616
|
+
mappings++;
|
|
617
|
+
}
|
|
618
|
+
if (mappings != 1)
|
|
619
|
+
throw new RequestFailure("invalid_request", "Address has " + mappings + " authoritative source mappings: " + canonicalAddress(address));
|
|
620
|
+
JsonObject result = new JsonObject();
|
|
621
|
+
result.addProperty("address", canonicalAddress(address));
|
|
622
|
+
result.addProperty("file_offset", fileOffset);
|
|
371
623
|
return result;
|
|
372
624
|
}
|
|
373
625
|
|
|
@@ -579,6 +831,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
579
831
|
result.addProperty("total_fields", totalFields);
|
|
580
832
|
result.addProperty("truncated", totalFields > 20_000);
|
|
581
833
|
result.add("limitations", GSON.toJsonTree(List.of("Layouts are Ghidra data-type database observations; original debug/source authority and flexible-tail semantics are not inferred. Nested and recursive types retain category-path identities for separate inspection.", "At most 20000 fields or enum members are serialized per type to bound provider output. total_fields and truncated report omissions. Enum values preserve Ghidra's signed long representation.")));
|
|
834
|
+
if (type != null) {
|
|
835
|
+
JsonObject metadata = analysisExtensions.inspectDataType(currentProgram, type, address, monitor);
|
|
836
|
+
if (metadata != null) result.add("metadata_recovery", metadata);
|
|
837
|
+
}
|
|
582
838
|
return result;
|
|
583
839
|
}
|
|
584
840
|
|
|
@@ -725,6 +981,90 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
725
981
|
return result;
|
|
726
982
|
}
|
|
727
983
|
|
|
984
|
+
private void invalidateAnalysisCaches() {
|
|
985
|
+
nameInventory = null;
|
|
986
|
+
procedureInventory = null;
|
|
987
|
+
stringInventory = null;
|
|
988
|
+
if (decompiler != null) decompiler.flushCache();
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
private JsonObject annotationReadback(Function function) {
|
|
992
|
+
Address entry = function.getEntryPoint();
|
|
993
|
+
JsonObject value = new JsonObject();
|
|
994
|
+
value.addProperty("address", canonicalAddress(entry));
|
|
995
|
+
value.addProperty("name", procedureName(function));
|
|
996
|
+
value.addProperty("comment", currentProgram.getListing().getComment(CommentType.PRE, entry));
|
|
997
|
+
value.addProperty("inline_comment", currentProgram.getListing().getComment(CommentType.EOL, entry));
|
|
998
|
+
return value;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
private JsonObject annotateNativeFunction(JsonObject params) throws Exception {
|
|
1002
|
+
if (!Set.of("procedure", "name", "comment", "inline_comment").containsAll(params.keySet()))
|
|
1003
|
+
throw new RequestFailure("invalid_request", "Unknown function annotation field");
|
|
1004
|
+
if (!params.has("name") && !params.has("comment") && !params.has("inline_comment"))
|
|
1005
|
+
throw new RequestFailure("invalid_request", "Supply at least one annotation change");
|
|
1006
|
+
Function function = resolveProcedure(requireString(params, "procedure"));
|
|
1007
|
+
Address entry = function.getEntryPoint();
|
|
1008
|
+
if (function.isExternal() || !currentProgram.getMemory().contains(entry))
|
|
1009
|
+
throw new RequestFailure("invalid_request", "Annotations require a local function entry: " + canonicalAddress(entry));
|
|
1010
|
+
int transaction = currentProgram.startTransaction("REA function annotations");
|
|
1011
|
+
boolean commit = false;
|
|
1012
|
+
try {
|
|
1013
|
+
// Validate edits through Ghidra's own setters, including name rules.
|
|
1014
|
+
// A later invalid name must roll back earlier comment writes.
|
|
1015
|
+
for (String field : List.of("comment", "inline_comment")) {
|
|
1016
|
+
if (params.has(field)) {
|
|
1017
|
+
String text = requireText(params, field);
|
|
1018
|
+
CommentType type = field.equals("comment") ? CommentType.PRE : CommentType.EOL;
|
|
1019
|
+
currentProgram.getListing().setComment(entry, type, text.isEmpty() ? null : text);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
if (params.has("name"))
|
|
1023
|
+
function.setName(requireString(params, "name"), SourceType.USER_DEFINED);
|
|
1024
|
+
monitor.checkCancelled();
|
|
1025
|
+
invalidateAnalysisCaches();
|
|
1026
|
+
JsonObject readback = annotationReadback(function);
|
|
1027
|
+
for (String field : List.of("name", "comment", "inline_comment")) {
|
|
1028
|
+
if (!params.has(field)) continue;
|
|
1029
|
+
String requested = requireText(params, field);
|
|
1030
|
+
JsonElement measured = readback.get(field);
|
|
1031
|
+
String expected = !field.equals("name") && requested.isEmpty() ? null : requested;
|
|
1032
|
+
String observed = field.equals("name") ? function.getName() :
|
|
1033
|
+
(measured.isJsonNull() ? null : measured.getAsString());
|
|
1034
|
+
if (!java.util.Objects.equals(expected, observed))
|
|
1035
|
+
throw new RequestFailure("annotation_readback_mismatch", "Annotation readback differs for " + field + " at " + canonicalAddress(entry));
|
|
1036
|
+
}
|
|
1037
|
+
JsonObject query = new JsonObject();
|
|
1038
|
+
query.addProperty("procedure", canonicalAddress(entry));
|
|
1039
|
+
JsonObject result = new JsonObject();
|
|
1040
|
+
result.add("annotations", readback);
|
|
1041
|
+
result.add("dossier", analyzeFunction(query));
|
|
1042
|
+
JsonObject effects = new JsonObject();
|
|
1043
|
+
effects.addProperty("scope", "session-analysis-database");
|
|
1044
|
+
effects.addProperty("source_bytes_modified", false);
|
|
1045
|
+
effects.addProperty("persists_after_close", false);
|
|
1046
|
+
result.add("effects", effects);
|
|
1047
|
+
monitor.checkCancelled();
|
|
1048
|
+
commit = true;
|
|
1049
|
+
return result;
|
|
1050
|
+
}
|
|
1051
|
+
catch (ghidra.util.exception.InvalidInputException | ghidra.util.exception.DuplicateNameException exception) {
|
|
1052
|
+
throw new RequestFailure("invalid_function_name", "Invalid function name at " + canonicalAddress(entry) + ": " + safeMessage(exception));
|
|
1053
|
+
}
|
|
1054
|
+
finally {
|
|
1055
|
+
currentProgram.endTransaction(transaction, commit);
|
|
1056
|
+
// Rollback also invalidates inventory and decompiler views.
|
|
1057
|
+
invalidateAnalysisCaches();
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
private static String requireText(JsonObject params, String field) {
|
|
1062
|
+
JsonElement value = params.get(field);
|
|
1063
|
+
if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString())
|
|
1064
|
+
throw new RequestFailure("invalid_request", "Annotation " + field + " must be text");
|
|
1065
|
+
return value.getAsString();
|
|
1066
|
+
}
|
|
1067
|
+
|
|
728
1068
|
private JsonObject analyzeFunction(JsonObject params) throws Exception {
|
|
729
1069
|
requireKeys(params, Set.of("procedure"));
|
|
730
1070
|
Function function = resolveProcedure(requireString(params, "procedure"));
|
|
@@ -2724,10 +3064,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
2724
3064
|
if (!Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS)) {
|
|
2725
3065
|
throw new IllegalArgumentException("REA session descriptor is invalid");
|
|
2726
3066
|
}
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
3067
|
+
JsonElement parsed = JsonParser.parseString(Files.readString(path, StandardCharsets.UTF_8));
|
|
3068
|
+
Set<String> keys = new HashSet<>(DESCRIPTOR_KEYS);
|
|
3069
|
+
if (parsed.isJsonObject() && parsed.getAsJsonObject().has("analysis_extensions"))
|
|
3070
|
+
keys.add("analysis_extensions");
|
|
3071
|
+
JsonObject object = requireObject(parsed, keys);
|
|
2731
3072
|
return new SessionDescriptor(
|
|
2732
3073
|
requireString(object, "transport"),
|
|
2733
3074
|
requireString(object, "endpoint_path"),
|
|
@@ -2735,7 +3076,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
2735
3076
|
requireString(object, "run_id"),
|
|
2736
3077
|
requireSha256(object, "target_sha256"),
|
|
2737
3078
|
requireString(object, "provider_version"),
|
|
2738
|
-
requireString(object, "profile_digest")
|
|
3079
|
+
requireString(object, "profile_digest"),
|
|
3080
|
+
object.has("analysis_extensions") ? object.getAsJsonArray("analysis_extensions") : new JsonArray()
|
|
2739
3081
|
);
|
|
2740
3082
|
}
|
|
2741
3083
|
|
|
@@ -2919,7 +3261,8 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
|
|
2919
3261
|
String runId,
|
|
2920
3262
|
String targetSha256,
|
|
2921
3263
|
String providerVersion,
|
|
2922
|
-
String profileDigest
|
|
3264
|
+
String profileDigest,
|
|
3265
|
+
JsonArray analysisExtensions
|
|
2923
3266
|
) {}
|
|
2924
3267
|
private record Request(int id, String method, JsonObject params) {}
|
|
2925
3268
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import com.google.gson.JsonArray;
|
|
2
|
+
import com.google.gson.JsonNull;
|
|
3
|
+
import com.google.gson.JsonObject;
|
|
4
|
+
import ghidra.program.model.address.Address;
|
|
5
|
+
import ghidra.program.model.data.DataType;
|
|
6
|
+
import ghidra.program.model.listing.Program;
|
|
7
|
+
import ghidra.util.exception.CancelledException;
|
|
8
|
+
import ghidra.util.task.TaskMonitor;
|
|
9
|
+
import java.lang.reflect.InvocationTargetException;
|
|
10
|
+
import java.net.URLClassLoader;
|
|
11
|
+
import java.nio.file.Files;
|
|
12
|
+
import java.nio.file.LinkOption;
|
|
13
|
+
import java.nio.file.Path;
|
|
14
|
+
import java.security.MessageDigest;
|
|
15
|
+
import java.util.ArrayList;
|
|
16
|
+
import java.util.List;
|
|
17
|
+
|
|
18
|
+
/** Session-owned extension loading; analyzer algorithms stay behind each adapter ABI. */
|
|
19
|
+
final class ReaGhidraExtensions implements AutoCloseable {
|
|
20
|
+
private record Loaded(URLClassLoader loader, Object instance, String sha256) {}
|
|
21
|
+
private final List<Loaded> loaded = new ArrayList<>();
|
|
22
|
+
private final JsonArray reports = new JsonArray();
|
|
23
|
+
|
|
24
|
+
void analyze(JsonArray descriptors, Program program, TaskMonitor monitor) throws Exception {
|
|
25
|
+
for (var item : descriptors) {
|
|
26
|
+
monitor.checkCancelled();
|
|
27
|
+
JsonObject descriptor = item.getAsJsonObject();
|
|
28
|
+
String id = descriptor.get("id").getAsString();
|
|
29
|
+
String entry = descriptor.get("entry_class").getAsString();
|
|
30
|
+
String digest = descriptor.get("sha256").getAsString();
|
|
31
|
+
JsonObject report = new JsonObject();
|
|
32
|
+
report.addProperty("id", id);
|
|
33
|
+
report.addProperty("sha256", digest);
|
|
34
|
+
URLClassLoader loader = null;
|
|
35
|
+
try {
|
|
36
|
+
if (!id.equals("nativeaot") || !entry.equals("rea.extensions.nativeaot.NativeAotExtension") ||
|
|
37
|
+
descriptor.get("integration_api").getAsInt() != 1)
|
|
38
|
+
throw new IllegalArgumentException("Unregistered Ghidra extension ABI: " + id);
|
|
39
|
+
Path path = Path.of(descriptor.get("path").getAsString());
|
|
40
|
+
if (!path.isAbsolute() || !Files.isRegularFile(path, LinkOption.NOFOLLOW_LINKS) || Files.size(path) > 8 * 1024 * 1024)
|
|
41
|
+
throw new IllegalArgumentException("Invalid private extension artifact: " + path);
|
|
42
|
+
String measured = java.util.HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(path)));
|
|
43
|
+
if (!measured.equals(digest)) throw new IllegalArgumentException("Private extension digest mismatch: " + id);
|
|
44
|
+
loader = new URLClassLoader(new java.net.URL[] { path.toUri().toURL() }, getClass().getClassLoader());
|
|
45
|
+
Class<?> type = Class.forName(entry, true, loader);
|
|
46
|
+
Object instance = type.getConstructor().newInstance();
|
|
47
|
+
Object response = type.getMethod("analyze", Program.class, TaskMonitor.class).invoke(instance, program, monitor);
|
|
48
|
+
if (!(response instanceof JsonObject result)) throw new IllegalArgumentException("Invalid Ghidra extension result: " + id);
|
|
49
|
+
report.addProperty("status", result.get("status").getAsString());
|
|
50
|
+
report.add("reason", result.get("reason").deepCopy());
|
|
51
|
+
report.add("result", result);
|
|
52
|
+
loaded.add(new Loaded(loader, instance, digest));
|
|
53
|
+
loader = null;
|
|
54
|
+
} catch (InvocationTargetException ex) {
|
|
55
|
+
if (ex.getCause() instanceof CancelledException cancelled) throw cancelled;
|
|
56
|
+
failure(report, ex.getCause());
|
|
57
|
+
} catch (Exception | LinkageError ex) {
|
|
58
|
+
failure(report, ex);
|
|
59
|
+
} finally {
|
|
60
|
+
if (loader != null) loader.close();
|
|
61
|
+
}
|
|
62
|
+
reports.add(report);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
JsonArray reports() { return reports.deepCopy(); }
|
|
67
|
+
|
|
68
|
+
JsonArray inspectImage() throws Exception {
|
|
69
|
+
JsonArray summaries = new JsonArray();
|
|
70
|
+
for (Loaded extension : loaded) {
|
|
71
|
+
Object response = extension.instance.getClass().getMethod("inspectImage").invoke(extension.instance);
|
|
72
|
+
if (!(response instanceof JsonObject summary)) throw new IllegalArgumentException("Invalid extension image summary");
|
|
73
|
+
summary.addProperty("analysis_artifact_sha256", extension.sha256);
|
|
74
|
+
summaries.add(summary);
|
|
75
|
+
}
|
|
76
|
+
return summaries;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
JsonObject inspectDataType(Program program, DataType type, Address address, TaskMonitor monitor) throws Exception {
|
|
80
|
+
for (Loaded extension : loaded) {
|
|
81
|
+
try {
|
|
82
|
+
Object response = extension.instance.getClass().getMethod("inspectDataType", Program.class, DataType.class, Address.class, TaskMonitor.class)
|
|
83
|
+
.invoke(extension.instance, program, type, address, monitor);
|
|
84
|
+
if (response instanceof JsonObject result) return result;
|
|
85
|
+
if (response != null) throw new IllegalArgumentException("Invalid extension type metadata result");
|
|
86
|
+
} catch (InvocationTargetException ex) {
|
|
87
|
+
if (ex.getCause() instanceof CancelledException cancelled) throw cancelled;
|
|
88
|
+
throw new IllegalStateException("Ghidra extension type inspection failed: " + ex.getCause(), ex.getCause());
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private static void failure(JsonObject report, Throwable ex) {
|
|
95
|
+
String reason = "Extension loading/analysis failed: " + ex;
|
|
96
|
+
if (ex.getCause() != null) reason += "; caused by " + ex.getCause();
|
|
97
|
+
report.addProperty("status", "failed");
|
|
98
|
+
report.addProperty("reason", reason);
|
|
99
|
+
JsonObject result = new JsonObject();
|
|
100
|
+
result.addProperty("loader_failure", reason);
|
|
101
|
+
report.add("result", result);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@Override public void close() throws Exception {
|
|
105
|
+
Exception failure = null;
|
|
106
|
+
for (Loaded extension : loaded) {
|
|
107
|
+
try { extension.loader.close(); }
|
|
108
|
+
catch (Exception ex) { if (failure == null) failure = ex; else failure.addSuppressed(ex); }
|
|
109
|
+
}
|
|
110
|
+
loaded.clear();
|
|
111
|
+
if (failure != null) throw failure;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import ghidra.app.script.GhidraScript;
|
|
2
|
+
import ghidra.program.model.address.Address;
|
|
3
|
+
import ghidra.program.model.listing.Program;
|
|
4
|
+
import java.math.BigInteger;
|
|
5
|
+
|
|
6
|
+
/** Seed the declared DOS COM analysis context before default auto-analysis. */
|
|
7
|
+
public final class ReaGhidraPrepareCom extends GhidraScript {
|
|
8
|
+
@Override
|
|
9
|
+
public void run() throws Exception {
|
|
10
|
+
if (!currentProgram.getLanguageID().getIdAsString().equals("x86:LE:16:Real Mode") ||
|
|
11
|
+
!currentProgram.getExecutableFormat().equals("Raw Binary")) {
|
|
12
|
+
throw new IllegalArgumentException("DOS COM preparation requires raw x86 real-mode import");
|
|
13
|
+
}
|
|
14
|
+
Address entry = currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress("1000:0100");
|
|
15
|
+
Address start = currentProgram.getMinAddress();
|
|
16
|
+
Address end = currentProgram.getMaxAddress();
|
|
17
|
+
if (!entry.equals(start) || end.subtract(start) + 1 > 0xff00) {
|
|
18
|
+
throw new IllegalArgumentException("DOS COM import must map 1..65280 bytes at 1000:0100");
|
|
19
|
+
}
|
|
20
|
+
for (String name : new String[] { "CS", "DS", "ES", "SS" }) {
|
|
21
|
+
monitor.checkCancelled();
|
|
22
|
+
currentProgram.getProgramContext().setValue(
|
|
23
|
+
currentProgram.getRegister(name), start, end, BigInteger.valueOf(0x1000)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
addEntryPoint(entry);
|
|
27
|
+
if (!disassemble(entry) || createFunction(entry, "entry") == null) {
|
|
28
|
+
throw new IllegalStateException("DOS COM entry cannot be decoded as a function");
|
|
29
|
+
}
|
|
30
|
+
currentProgram.getOptions(Program.PROGRAM_INFO).setBoolean("REA DOS COM prepared", true);
|
|
31
|
+
}
|
|
32
|
+
}
|