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