rea-agents 5.0.0 → 6.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 +168 -655
- package/bridge/android/ReaJadxBridge.java +14 -5
- package/bridge/ghidra/ReaGhidraBridge.java +173 -47
- package/bridge/hopper_bridge.py +310 -128
- package/bridge/mitmproxy/capture.py +208 -0
- package/bridge/native/rea_lldb_tracer.py +839 -0
- package/bridge/process/ProcessRunTokenReader.swift +297 -0
- package/bridge/process/ProcessRunTokenReaderMain.swift +35 -0
- package/bridge/pwndbg/core_context.py +101 -0
- package/bridge/pwndbg/launch.py +39 -0
- package/bridge/pwndbg/launch_impl.py +34 -0
- package/bridge/pwntools/decoder_runtime.py +59 -0
- package/bridge/pwntools/layout.py +39 -0
- package/bridge/pwntools/layout_impl.py +309 -0
- package/bridge/pwntools/recorded_crash.py +39 -0
- package/bridge/pwntools/recorded_crash_impl.py +147 -0
- package/dist/android/JadxConfiguration.js +196 -33
- package/dist/android/JadxMcpTransport.js +4 -0
- package/dist/android/JadxProtocol.js +6 -0
- package/dist/android/JadxProvider.js +20 -2
- package/dist/android/JadxSession.js +23 -4
- package/dist/application/AnalysisContextQueries.js +8 -35
- package/dist/application/AnalysisDocument.js +32 -0
- package/dist/application/AppBundleExecutable.js +67 -21
- package/dist/application/CallPathTracing.js +19 -3
- package/dist/application/CapabilityInventory.js +27 -3
- package/dist/application/ClientConfigurationDocument.js +871 -6
- package/dist/application/ClientRegistrationIdentity.js +26 -5
- package/dist/application/ClientRegistrationStatus.js +29 -14
- package/dist/application/DirectAnalysis.js +97 -33
- package/dist/application/Doctor.js +4 -2
- package/dist/application/DoctorScope.js +25 -13
- package/dist/application/EnhancedLiteralTracing.js +22 -25
- package/dist/application/EnhancedTools.js +13 -11
- package/dist/application/EvidenceBundleFiles.js +24 -12
- package/dist/application/FunctionAnalysisEvidence.js +2 -3
- package/dist/application/InventoryProjectionEvidence.js +4 -3
- package/dist/application/InvestigationProviders.js +3 -2
- package/dist/application/JsonFiles.js +50 -28
- package/dist/application/ProcedureAddressResolution.js +11 -0
- package/dist/application/ReconstructionCoverageService.js +7 -4
- package/dist/application/ReconstructionObligationCandidates.js +5 -1
- package/dist/application/ReconstructionObligationLedgerService.js +4 -3
- package/dist/application/Setup.js +13 -1
- package/dist/application/SetupClientConfiguration.js +10 -15
- package/dist/application/SetupHost.js +5 -2
- package/dist/application/SetupPlan.js +5 -3
- package/dist/application/SupportedClients.js +34 -1
- package/dist/application/Uninstall.js +4 -3
- package/dist/application/Update.js +18 -3
- package/dist/application/UpdateRuntime.js +46 -25
- package/dist/application/WebModuleTraceService.js +4 -2
- package/dist/application/WebNetworkCapturePort.js +1 -0
- package/dist/application/WebNetworkCaptureService.js +134 -0
- package/dist/application/WebRuntimeService.js +5 -3
- package/dist/application/WebScriptExportService.js +2 -2
- package/dist/application/WebSourceLocationService.js +4 -2
- package/dist/application/WindowsCapabilities.js +1 -1
- package/dist/application/WorkflowEvidence.js +82 -0
- package/dist/application/binary/AnalysisProviderEvaluation.js +21 -5
- package/dist/application/binary/AnalysisProviderRegistry.js +2 -0
- package/dist/application/binary/AnalysisSnapshotCache.js +52 -12
- package/dist/application/binary/BinarySession.js +131 -76
- package/dist/application/binary/BinarySessionOpen.js +19 -6
- package/dist/application/binary/BinarySessionRecords.js +45 -3
- package/dist/application/binary/BinarySessionStatus.js +17 -1
- package/dist/application/binary/CompositeProvider.js +13 -0
- package/dist/application/binary/LazyAnalysisProvider.js +3 -0
- package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
- package/dist/application/binaryDiagnostics/BinaryLayoutService.js +59 -0
- package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
- package/dist/application/binaryDiagnostics/RecordedCrashService.js +62 -0
- package/dist/application/evm/EvmInterfacePort.js +1 -0
- package/dist/application/evm/EvmInterfaceService.js +64 -0
- package/dist/application/investigation/EvidenceLedger.js +21 -7
- package/dist/application/investigation/InvestigationRecords.js +8 -0
- package/dist/application/javascript/JavaScriptAnalysisControl.js +34 -0
- package/dist/application/javascript/JavaScriptApplicationEvidence.js +25 -5
- package/dist/application/javascript/JavaScriptApplicationEvidenceGraph.js +19 -2
- package/dist/application/javascript/JavaScriptApplicationService.js +34 -14
- package/dist/application/javascript/JavaScriptApplicationWorkflowService.js +5 -5
- package/dist/application/javascript/JavaScriptArtifactAnalysis.js +47 -27
- package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +39 -7
- package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +26 -2
- package/dist/application/javascript/JavaScriptArtifactPathResolution.js +80 -27
- package/dist/application/javascript/JavaScriptArtifactReconstruction.js +33 -12
- package/dist/application/javascript/JavaScriptModuleRelationships.js +37 -8
- package/dist/application/javascript/JavaScriptRecoveryService.js +4 -2
- package/dist/application/javascript/JavaScriptRuntimeObservationService.js +3 -1
- package/dist/application/javascript/JavaScriptRuntimeReconciliationService.js +3 -3
- package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +7 -7
- package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +149 -72
- package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +14 -15
- package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +41 -1
- package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +7 -7
- package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -9
- package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +3 -3
- package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +118 -12
- package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +3 -3
- package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +27 -5
- package/dist/application/managed/ManagedApplicationGraphService.js +4 -3
- package/dist/application/managed/ManagedMemberComparisonService.js +7 -4
- package/dist/application/managed/ManagedNativeVerificationService.js +4 -3
- package/dist/application/managed/ManagedReconstructionService.js +4 -3
- package/dist/application/{ProcessCli.js → process/ProcessCli.js} +23 -15
- package/dist/application/{ProcessEvidence.js → process/ProcessEvidence.js} +8 -7
- package/dist/application/workflowInputError.js +18 -0
- package/dist/artifacts/ArtifactProvider.js +42 -5
- package/dist/artifacts/ArtifactProviderMetadata.js +2 -1
- package/dist/artifacts/MachOSliceArtifactReader.js +98 -14
- package/dist/artifacts/NativeDmgArtifactReader.js +13 -6
- package/dist/artifacts/ZipArtifactReader.js +3 -0
- package/dist/artifacts/apple/AppleAssetCatalogAnalysis.js +22 -3
- package/dist/artifacts/apple/DylibResolutionReader.js +259 -0
- package/dist/artifacts/apple/InterfaceBuilderAnalysis.js +176 -56
- package/dist/artifacts/apple/InterfaceBuilderDecodeBudget.js +195 -0
- package/dist/artifacts/apple/KeyedArchiveReader.js +36 -22
- package/dist/artifacts/apple/MachoLoadCommandReader.js +355 -0
- package/dist/artifacts/apple/NibArchive.js +81 -14
- package/dist/artifacts/apple/NibViewHierarchy.js +10 -3
- package/dist/artifacts/apple/PlistNumberLiterals.js +164 -0
- package/dist/{application → artifacts/extraction}/ArtifactExtraction.js +10 -10
- package/dist/{application → artifacts/inventory}/ArtifactGraphConstruction.js +16 -11
- package/dist/{application → artifacts/inventory}/ArtifactInventory.js +4 -4
- package/dist/{application/ArtifactInventory → artifacts/inventory}/classify.js +2 -2
- package/dist/{application/ArtifactInventory → artifacts/inventory}/reader.js +5 -5
- package/dist/{application/ArtifactInventory → artifacts/inventory}/scanCanonical.js +14 -17
- package/dist/{application/ArtifactInventory → artifacts/inventory}/scanReader.js +5 -5
- package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +6 -2
- package/dist/artifacts/readStableArtifact.js +20 -4
- package/dist/browser/CdpCaptureCompleteness.js +2 -0
- package/dist/browser/CdpCaptureEventHandlers.js +165 -12
- package/dist/browser/CdpCaptureEventTypes.js +5 -1
- package/dist/browser/CdpCaptureEvents.js +34 -0
- package/dist/browser/CdpConnection.js +30 -11
- package/dist/browser/CdpOptionalCommand.js +2 -3
- package/dist/browser/CdpPageCapture.js +23 -1
- package/dist/browser/PlaywrightElectronActiveActions.js +32 -23
- package/dist/browser/PlaywrightElectronActiveProjection.js +141 -0
- package/dist/browser/PlaywrightElectronActiveProvider.js +130 -122
- package/dist/browser/WebSourceMapFetcher.js +426 -72
- package/dist/browser/assets/LocalWebScriptArtifacts.js +2 -2
- package/dist/browser/history/CaptureFailures.js +137 -0
- package/dist/browser/history/CaptureFormatError.js +10 -0
- package/dist/browser/history/CaptureRedaction.js +65 -0
- package/dist/browser/history/CaptureRelease.js +17 -0
- package/dist/browser/history/HarCapture.js +165 -0
- package/dist/browser/history/HarCaptureAdapter.js +20 -0
- package/dist/browser/history/HarCaptureProcess.js +57 -0
- package/dist/browser/history/HarJson.js +92 -0
- package/dist/browser/history/HarSchema.js +72 -0
- package/dist/browser/history/HistoricalCaptureDecoder.js +188 -0
- package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
- package/dist/browser/history/MitmproxyCaptureAdapter.js +42 -0
- package/dist/cli/analysisSelector.js +23 -0
- package/dist/cli/applicationCommands.js +2 -3
- package/dist/cli/artifactCommands.js +26 -1
- package/dist/cli/binaryDiagnosticsCommands.js +39 -0
- package/dist/cli/coreAnalysisCommands.js +2 -2
- package/dist/cli/coreBinaryCommands.js +78 -20
- package/dist/cli/electronCommands.js +21 -13
- package/dist/cli/evmCommands.js +24 -0
- package/dist/cli/firmwareCommands.js +6 -3
- package/dist/cli/javascriptApplicationAnalysis.js +20 -3
- package/dist/cli/jsonOutput.js +38 -0
- package/dist/cli/nativeCallCommands.js +34 -0
- package/dist/cli/processCommands.js +1 -1
- package/dist/cli/streamedJsonOutput.js +56 -0
- package/dist/cli/utilityCommands.js +2 -0
- package/dist/cli/webNetworkCaptureCommands.js +40 -0
- package/dist/cli/webRuntimeCommands.js +8 -8
- package/dist/cli.js +11 -4
- package/dist/cliBrowserScenarioCommands.js +2 -2
- package/dist/cliCommandNames.js +6 -1
- package/dist/cliJsonInput.js +35 -1
- package/dist/cliOutput.js +21 -6
- package/dist/composition/binaryDiagnostics.js +8 -0
- package/dist/composition/evm.js +4 -0
- package/dist/composition/webNetworkCaptures.js +6 -0
- package/dist/config/environment.js +4 -0
- package/dist/config/ghidraStartupTimeout.js +17 -0
- package/dist/config/mcpResponseBudget.js +22 -0
- package/dist/config/parseConfig.js +2 -1
- package/dist/contracts/artifactToolContracts.js +6 -0
- package/dist/contracts/browserCaptureToolInputSchema.js +43 -0
- package/dist/contracts/browserToolContracts.js +4 -3
- package/dist/contracts/enhancedInputs.js +5 -3
- package/dist/contracts/enhancedToolContracts.js +3 -3
- package/dist/contracts/errorSchemas.js +8 -1
- package/dist/contracts/evm/evmToolContracts.js +20 -0
- package/dist/contracts/hopperMemoryContracts.js +2 -5
- package/dist/contracts/investigationExamples.js +2 -2
- package/dist/contracts/javascript/electronToolContracts.js +1 -1
- package/dist/contracts/managed/managedToolContracts.js +8 -6
- package/dist/contracts/native/binaryDiagnosticsToolContracts.js +42 -0
- package/dist/contracts/native/nativeToolContracts.js +21 -3
- package/dist/contracts/officialToolContracts.js +26 -24
- package/dist/contracts/process/processCaptureExample.js +2 -0
- package/dist/contracts/promptContracts.js +3 -3
- package/dist/contracts/sessionLifecycleInputs.js +18 -5
- package/dist/contracts/sessionStatusContract.js +8 -2
- package/dist/contracts/sessionToolContracts.js +8 -3
- package/dist/contracts/sessionToolSchemas.js +8 -4
- package/dist/contracts/toolContractExamples.js +1 -2
- package/dist/contracts/toolContractHelpers.js +4 -1
- package/dist/contracts/toolContracts.js +6 -0
- package/dist/contracts/toolEffects.js +30 -1
- package/dist/contracts/toolOutputSchemaGroups.js +33 -28
- package/dist/contracts/toolOutputSchemaPrimitives.js +19 -1
- package/dist/contracts/toolSchemaMetadata.js +69 -10
- package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
- package/dist/domain/analysisErrorBase.js +22 -2
- package/dist/domain/analysisErrorCore.js +61 -5
- package/dist/domain/analysisErrorPresentation.js +95 -12
- package/dist/domain/analysisErrorProjection.js +148 -19
- package/dist/domain/analysisSnapshot.js +166 -2
- package/dist/domain/android/androidAnalysis.js +2 -4
- package/dist/domain/android/androidApplication.js +17 -9
- package/dist/domain/apple/appleApplication.js +41 -14
- package/dist/domain/apple/dyldEnvironment.js +73 -0
- package/dist/domain/apple/dyldPaths.js +81 -0
- package/dist/domain/apple/dylibResolution.js +478 -0
- package/dist/domain/apple/dylibResolutionFindings.js +77 -0
- package/dist/domain/apple/interfaceBuilderGraph.js +25 -13
- package/dist/domain/apple/interfaceBuilderKeyedArchive.js +13 -3
- package/dist/domain/apple/keyedArchive.js +9 -1
- package/dist/domain/apple/machoContainer.js +54 -0
- package/dist/domain/apple/plistNumbers.js +76 -0
- package/dist/domain/artifactIdentity.js +29 -0
- package/dist/domain/artifactInventoryEvidence.js +19 -24
- package/dist/domain/artifactPathSyntax.js +1 -1
- package/dist/domain/bridgeCandidateProjection.js +71 -0
- package/dist/domain/browserCompleteness.js +1 -0
- package/dist/domain/browserObservation.js +1 -1
- package/dist/domain/browserObservationError.js +24 -5
- package/dist/domain/browserObservationSchemas.js +13 -0
- package/dist/domain/browserScenarioValues.js +5 -2
- package/dist/domain/bundleComparison.js +3 -2
- package/dist/domain/callPathSchemas.js +1 -1
- package/dist/domain/canonicalDigest.js +13 -1
- package/dist/domain/canonicalJsonDigestSteps.js +133 -0
- package/dist/domain/canonicalOrdering.js +9 -5
- package/dist/domain/changedBehavior.js +9 -9
- package/dist/domain/comparisonSourceEvidence.js +40 -0
- package/dist/domain/comparisonStatus.js +2 -30
- package/dist/domain/configurationErrors.js +1 -1
- package/dist/domain/conformanceTrustGate.js +0 -16
- package/dist/domain/evidence.js +78 -11
- package/dist/domain/evidenceBundle.js +42 -1
- package/dist/domain/evidenceErrors.js +12 -1
- package/dist/domain/evm/evmInterface.js +63 -0
- package/dist/domain/explicitSensitiveFailure.js +108 -0
- package/dist/domain/explicitSensitiveValues.js +10 -0
- package/dist/domain/firmware/firmwareAnalysis.js +10 -4
- package/dist/domain/hopperErrors.js +23 -2
- package/dist/domain/hopperValues.js +51 -8
- package/dist/domain/immutableJson.js +114 -0
- package/dist/domain/inputIssueProjection.js +3 -0
- package/dist/domain/javascript/electronActiveObservation.js +37 -6
- package/dist/domain/javascript/electronStaticAnalysisValues.js +6 -12
- package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
- package/dist/domain/javascript/javascriptApplicationAnalysis.js +71 -23
- package/dist/domain/javascript/javascriptApplicationGraph.js +13 -0
- package/dist/domain/javascript/javascriptAstValues.js +20 -0
- package/dist/domain/javascript/javascriptExportShapeComparison.js +21 -5
- package/dist/domain/javascript/javascriptExportShapeVariants.js +0 -10
- package/dist/domain/javascript/javascriptRecovery.js +3 -8
- package/dist/domain/javascript/javascriptSemanticAnalysis.js +97 -2
- package/dist/domain/javascript/javascriptSemanticCoverage.js +6 -5
- package/dist/domain/javascript/javascriptSemanticDataEffectHelpers.js +9 -17
- package/dist/domain/javascript/javascriptSemanticDataEffects.js +22 -5
- package/dist/domain/javascript/javascriptSemanticGraph.js +34 -5
- package/dist/domain/javascript/javascriptSemanticGraphSchemas.js +1 -0
- package/dist/domain/javascript/javascriptSemanticPrimitives.js +25 -17
- package/dist/domain/javascript/javascriptSemanticQuerySchemas.js +8 -4
- package/dist/domain/javascript/javascriptSemanticResourceLimits.js +77 -0
- package/dist/domain/javascript/javascriptSemanticValues.js +83 -11
- package/dist/domain/javascript/javascriptSourceParser.js +6 -2
- package/dist/domain/javascript/javascriptStaticAnalysis.js +22 -17
- package/dist/domain/javascript/javascriptStaticAnalysisBundler.js +5 -3
- package/dist/domain/javascript/javascriptStaticAnalysisCalls.js +14 -4
- package/dist/domain/javascript/javascriptStaticAnalysisFindings.js +66 -7
- package/dist/domain/javascript/javascriptStaticAnalysisHelpers.js +28 -16
- package/dist/domain/javascript/javascriptStaticAnalysisState.js +3 -2
- package/dist/domain/jsonSerialization.js +109 -0
- package/dist/domain/jsonValue.js +91 -3
- package/dist/domain/localPath.js +8 -0
- package/dist/domain/managed/managedApplicationGraph.js +10 -10
- package/dist/domain/managed/managedInspectionEvidence.js +16 -0
- package/dist/domain/managed/managedMemberComparisonMatch.js +5 -9
- package/dist/domain/managed/managedNativeVerification.js +3 -9
- package/dist/domain/native/binaryLayout.js +357 -0
- package/dist/domain/native/fileOffsetPartialObservation.js +1 -0
- package/dist/domain/native/nativeCallObservation.js +186 -0
- package/dist/domain/native/nativeCallPartialObservation.js +33 -0
- package/dist/domain/native/objcSwiftMetadata.js +0 -39
- package/dist/domain/native/recordedCrash.js +266 -0
- package/dist/domain/native/recordedCrashNoteCoverage.js +73 -0
- package/dist/domain/{processCapture.js → process/processCapture.js} +143 -2
- package/dist/domain/{processCaptureExample.js → process/processCaptureExample.js} +5 -0
- package/dist/domain/process/processCaptureParsing.js +54 -0
- package/dist/domain/{processCaptureValidation.js → process/processCaptureValidation.js} +18 -2
- package/dist/domain/{processComparison.js → process/processComparison.js} +39 -5
- package/dist/domain/{processObservation.js → process/processObservation.js} +1 -1
- package/dist/domain/{processScenario.js → process/processScenario.js} +11 -10
- package/dist/domain/{processTraceSpecification.js → process/processTraceSpecification.js} +1 -1
- package/dist/domain/propertyListKeys.js +1 -1
- package/dist/domain/propertyListXmlText.js +19 -0
- package/dist/domain/providerAdapterError.js +1 -1
- package/dist/domain/providerOperationHealth.js +54 -0
- package/dist/domain/providerSelectionError.js +4 -2
- package/dist/domain/reconstructionVerification.js +9 -46
- package/dist/domain/reconstructionVerificationSchemas.js +3 -3
- package/dist/domain/residualUnknown.js +3 -2
- package/dist/domain/staticRuntimeCorrelation.js +9 -46
- package/dist/domain/webBundleAnalysis.js +20 -8
- package/dist/domain/webBundleAnalyzerInspection.js +13 -8
- package/dist/domain/webBundleAnalyzerResult.js +41 -2
- package/dist/domain/webCaptureDiff.js +1 -0
- package/dist/domain/webNetworkCapture.js +118 -0
- package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
- package/dist/domain/webRuntime.js +19 -5
- package/dist/domain/webSourceLocation.js +1 -0
- package/dist/dotnet/ManagedExceptionRegionValidation.js +41 -0
- package/dist/dotnet/ManagedMemberInspector.js +4 -2
- package/dist/dotnet/ManagedMemberInspectorCore.js +38 -32
- package/dist/dotnet/ManagedMemberInstructionDecoder.js +23 -7
- package/dist/dotnet/ManagedMemberRows.js +14 -3
- package/dist/dotnet/ManagedMetadataHeaps.js +20 -2
- package/dist/dotnet/ManagedMetadataInventory.js +38 -28
- package/dist/dotnet/ManagedMetadataInventoryRows.js +24 -10
- package/dist/dotnet/ManagedMethodBodyReader.js +7 -1
- package/dist/dotnet/ManagedNativeBoundaryHelpers.js +15 -3
- package/dist/dotnet/ManagedNativeBoundaryInspector.js +3 -1
- package/dist/dotnet/ManagedPeReader.js +11 -0
- package/dist/evm/EvmBytecodeCarrier.js +21 -0
- package/dist/evm/EvmWorkerLimits.js +40 -0
- package/dist/evm/EvmoleFailures.js +126 -0
- package/dist/evm/EvmoleInterfaceAdapter.js +53 -0
- package/dist/evm/EvmoleInterfaceProvider.js +273 -0
- package/dist/evm/EvmoleInterfaceWorker.js +87 -0
- package/dist/evm/EvmoleRelease.js +16 -0
- package/dist/firmware/FirmwareCommand.js +1 -0
- package/dist/firmware/FirmwareFiles.js +1 -1
- package/dist/generatedPackageMetadata.js +2 -2
- package/dist/ghidra/GhidraAnalysisProfile.js +5 -0
- package/dist/ghidra/GhidraClient.js +47 -14
- package/dist/ghidra/GhidraDiagnostics.js +7 -7
- package/dist/ghidra/GhidraFunctionValues.js +10 -7
- package/dist/ghidra/GhidraInstallation.js +18 -21
- package/dist/ghidra/GhidraInventoryValues.js +14 -4
- package/dist/ghidra/GhidraJavaLaunch.js +62 -0
- package/dist/ghidra/GhidraLauncher.js +34 -16
- package/dist/ghidra/GhidraProvider.js +2 -2
- package/dist/ghidra/GhidraProviderCapabilities.js +15 -3
- package/dist/ghidra/GhidraProviderClient.js +23 -12
- package/dist/ghidra/GhidraSessionError.js +11 -7
- package/dist/ghidra/GhidraTargetSnapshot.js +47 -12
- package/dist/ghidra/GhidraTransport.js +20 -0
- package/dist/hopper/BridgeLauncher.js +33 -11
- package/dist/hopper/HopperAnalysisProfile.js +62 -4
- package/dist/hopper/HopperCleanup.js +15 -3
- package/dist/hopper/HopperClient.js +222 -14
- package/dist/hopper/HopperFileOffset.js +237 -0
- package/dist/hopper/HopperMachOImage.js +196 -0
- package/dist/hopper/HopperOperationStage.js +6 -0
- package/dist/hopper/HopperProvider.js +63 -9
- package/dist/hopper/HopperProviderCapabilities.js +19 -1
- package/dist/hopper/HopperRegexSearch.js +137 -0
- package/dist/hopper/HopperRegexWorker.js +27 -0
- package/dist/hopper/HopperRequestQueue.js +68 -19
- package/dist/hopper/HopperTargetLease.js +3 -2
- package/dist/hopper/LinuxPrivateDisplayProbe.js +4 -2
- package/dist/inspector/JavaScriptRuntimeScope.js +9 -1
- package/dist/inspector/V8InspectorCaptureProjection.js +8 -3
- package/dist/inspector/V8InspectorProvider.js +47 -11
- package/dist/javascript/recovery/WakaruCommand.js +3 -0
- package/dist/javascript/sourceMaps/DecodedSourceMap.js +47 -0
- package/dist/javascript/sourceMaps/SourceMapCommand.js +17 -0
- package/dist/javascript/sourceMaps/SourceMapFormat.js +79 -31
- package/dist/javascript/sourceMaps/TraceSourceMap.js +14 -28
- package/dist/mcpDoctor.js +62 -2
- package/dist/native/AppleDispatchCoverage.js +47 -6
- package/dist/native/AppleDispatchDecodeFacts.js +6 -0
- package/dist/native/AppleDispatchMetadata.js +6 -3
- package/dist/native/AppleMachoFixups.js +48 -24
- package/dist/native/AppleMachoSelection.js +32 -16
- package/dist/native/AppleObjcCategories.js +6 -2
- package/dist/native/AppleObjcDispatchFacets.js +5 -2
- package/dist/native/AppleObjcProperties.js +2 -1
- package/dist/native/AppleSwiftDispatchFacets.js +27 -21
- package/dist/native/AppleSwiftVtables.js +21 -15
- package/dist/native/CommandRunner.js +82 -84
- package/dist/native/LldbCallTracer.js +541 -0
- package/dist/native/LldbRetainedObservations.js +178 -0
- package/dist/native/NativeCallObservation.js +194 -0
- package/dist/native/NativeMacOSProvider.js +117 -20
- package/dist/native/NativeMacOSProviderMetadata.js +8 -3
- package/dist/native/SignatureTargetBinding.js +155 -0
- package/dist/native/parsers/lipo.js +71 -1
- package/dist/native/pwndbg/PwndbgCoreContext.js +180 -0
- package/dist/native/pwntools/PwntoolsDecoder.js +203 -0
- package/dist/native/pwntools/PwntoolsFailures.js +180 -0
- package/dist/native/pwntools/PwntoolsLayoutProvider.js +36 -0
- package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +75 -0
- package/dist/native/pwntools/PwntoolsRelease.js +19 -0
- package/dist/native/pwntools/PwntoolsResourceLimits.js +76 -0
- package/dist/native/pwntools/RecordedCrashSourceBindings.js +46 -0
- package/dist/native/pwntools/RecordedCrashStructureBindings.js +103 -0
- package/dist/process/DarwinProcessRunTokenReader.js +293 -0
- package/dist/process/ExecFileOutput.js +20 -0
- package/dist/process/OwnedCommand.js +118 -0
- package/dist/process/ProcessOwnership.js +431 -51
- package/dist/process/ProcessOwnershipObservation.js +209 -27
- package/dist/process/ProviderProcess.js +68 -10
- package/dist/{application → process/capture}/ProcessCaptureCapability.js +15 -0
- package/dist/{application → process/capture}/ProcessCaptureError.js +18 -2
- package/dist/process/capture/ProcessCaptureLifecycle.js +681 -0
- package/dist/{application → process/capture}/ProcessHarness.js +195 -29
- package/dist/{application → process/capture}/ProcessSampling.js +2 -2
- package/dist/server/EvidenceMcpServer.js +146 -0
- package/dist/server/createServer.js +37 -8
- package/dist/server/promptCompletion.js +10 -3
- package/dist/server/registerAndroidTools.js +2 -4
- package/dist/server/registerApplicationTools/helpers.js +2 -4
- package/dist/server/registerArtifactComparisonTool.js +11 -7
- package/dist/server/registerBinaryDiagnosticsTools.js +15 -0
- package/dist/server/registerBrowserScenarioTool.js +2 -4
- package/dist/server/registerBrowserTools.js +2 -4
- package/dist/server/registerBundleComparisonTool.js +2 -3
- package/dist/server/registerCloseLifecycleTool.js +8 -18
- package/dist/server/registerElectronTools.js +2 -4
- package/dist/server/registerEnhancedTools.js +18 -59
- package/dist/server/registerEvidenceTools.js +3 -5
- package/dist/server/registerEvmTools.js +15 -0
- package/dist/server/registerFirmwareTools.js +2 -4
- package/dist/server/registerFunctionComparisonTool.js +10 -6
- package/dist/server/registerInvestigationTools.js +7 -7
- package/dist/server/registerJavaScriptRecoveryTool.js +2 -4
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +2 -4
- package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +2 -4
- package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +2 -4
- package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +2 -4
- package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +2 -4
- package/dist/server/registerOfficialTools.js +2 -4
- package/dist/server/registerProcessComparisonTool.js +72 -46
- package/dist/server/registerRecordedCrashTools.js +15 -0
- package/dist/server/registerSessionRecordTools.js +5 -6
- package/dist/server/registerSessionStatusTool.js +17 -3
- package/dist/server/registerSessionTools.js +13 -5
- package/dist/server/registerWebModuleTool.js +2 -4
- package/dist/server/registerWebNetworkCaptureTool.js +26 -0
- package/dist/server/registerWebRuntimeTools.js +3 -7
- package/dist/server/registerWebScriptTool.js +2 -4
- package/dist/server/registerWebSourceLocationTool.js +2 -4
- package/dist/server/runDerivedOperation.js +15 -3
- package/dist/server/sessionAvailabilityPolicy.js +3 -1
- package/dist/server/sessionToolPolicies.js +1 -1
- package/dist/server/toolRegistrationOptions.js +2 -2
- package/dist/server/toolResult.js +63 -11
- package/dist/server/toolResultEncoding.js +33 -0
- package/install.sh +1 -1
- package/native/windows/build/manifest.json +2 -2
- package/native/windows/build/rea-windows-x64.node +0 -0
- package/package.json +42 -9
- package/scripts/electron-active-hook-boundaries.cjs +18 -20
- package/scripts/electron-active-hook.cjs +241 -37
- package/scripts/rea.mjs +11 -3
- package/skills/reverse-engineer-anything/SKILL.md +32 -4
- package/skills/reverse-engineer-anything/references/javascript-applications.md +8 -1
- package/skills/reverse-engineer-anything/references/native-and-artifacts.md +29 -0
- package/third_party/README.md +60 -0
- package/third_party/evmole/LICENSE +21 -0
- package/third_party/evmole/README.md +22 -0
- package/third_party/pwndbg/LICENSE.md +21 -0
- package/third_party/pwndbg/README.md +23 -0
- package/third_party/pwntools/README.md +40 -0
- package/dist/application/AuthorizedArtifactInventory.js +0 -7
- package/dist/application/ProcessCaptureLifecycle.js +0 -299
- package/dist/application/StoreFileLock.js +0 -124
- package/dist/contracts/processCaptureExample.js +0 -2
- package/dist/domain/bytecodeProvider.js +0 -178
- package/dist/domain/customProtocolCapture.js +0 -137
- package/dist/domain/eventProcessTree.js +0 -225
- package/dist/domain/mobileApplicationInvestigation.js +0 -159
- package/dist/domain/packageAnalysis.js +0 -123
- package/dist/domain/peInspection.js +0 -197
- package/dist/domain/processCaptureParsing.js +0 -21
- package/dist/domain/protocolCapture.js +0 -217
- package/dist/domain/runtimeIdentification.js +0 -212
- package/dist/generatedMcpToolCatalog.js +0 -37
- package/dist/ghidra/GhidraDefaults.js +0 -2
- /package/dist/application/{ArtifactExtractionDestination.js → artifacts/ArtifactExtractionDestination.js} +0 -0
- /package/dist/{application/ArtifactInventory → artifacts/inventory}/policy.js +0 -0
- /package/dist/{application/ArtifactInventory → artifacts/inventory}/types.js +0 -0
- /package/dist/domain/{processEvidenceProvider.js → process/processEvidenceProvider.js} +0 -0
- /package/dist/domain/{processTraceComparison.js → process/processTraceComparison.js} +0 -0
- /package/dist/domain/{processTraceDimensionProjection.js → process/processTraceDimensionProjection.js} +0 -0
- /package/dist/domain/{processTraceEvaluation.js → process/processTraceEvaluation.js} +0 -0
- /package/dist/{application → process/capture}/FilesystemSnapshot.js +0 -0
- /package/dist/{application → process/capture}/ProcessCaptureEnvironment.js +0 -0
- /package/dist/{application → process/capture}/ProcessCaptureJournal.js +0 -0
- /package/dist/{application → process/capture}/ProcessFilesystemEffects.js +0 -0
- /package/dist/{application → process/capture}/ProcessNormalization.js +0 -0
- /package/dist/{application → process/capture}/ProcessScenarioRuntimeValidation.js +0 -0
- /package/dist/{application → process/capture}/ProcessTimer.js +0 -0
- /package/dist/{application → process/capture}/TerminalRenderer.js +0 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { open } from "node:fs/promises";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { AnalysisAccessDeniedError, AnalysisArtifactChangedError, AnalysisCancelledError, AnalysisCapabilityUnavailableError, AnalysisOutputError, } from "../domain/analysisErrorCore.js";
|
|
6
|
+
import { BinaryTargetError } from "../domain/configurationErrors.js";
|
|
7
|
+
import { readFatSliceDeclarations, validateFatSlice, } from "../domain/apple/machoContainer.js";
|
|
8
|
+
import { err, ok } from "../domain/result.js";
|
|
9
|
+
const OPERATION = "address_to_file_offset";
|
|
10
|
+
const MAX_FAT_ARCHITECTURES = 128;
|
|
11
|
+
const PREFIX_BYTES = 8 + MAX_FAT_ARCHITECTURES * 32;
|
|
12
|
+
const observationSchema = z.strictObject({
|
|
13
|
+
address: z.string().regex(/^0x[0-9a-f]+$/u),
|
|
14
|
+
provider_file_offset: z
|
|
15
|
+
.number()
|
|
16
|
+
.int()
|
|
17
|
+
.nonnegative()
|
|
18
|
+
.max(Number.MAX_SAFE_INTEGER),
|
|
19
|
+
provider_source_path: z.string().nullable(),
|
|
20
|
+
provider_image_header_hex: z
|
|
21
|
+
.string()
|
|
22
|
+
.regex(/^[a-f0-9]{24}$/u)
|
|
23
|
+
.nullable(),
|
|
24
|
+
});
|
|
25
|
+
/** Bind native image-relative offsets to coordinates in the admitted source bytes. */
|
|
26
|
+
export async function mapHopperFileOffset(target, value, signal) {
|
|
27
|
+
const parsed = observationSchema.safeParse(value);
|
|
28
|
+
if (!parsed.success)
|
|
29
|
+
return err(new AnalysisOutputError(OPERATION, "Hopper's native file mapping observation is malformed"));
|
|
30
|
+
const observation = parsed.data;
|
|
31
|
+
const partial = (reason) => ({
|
|
32
|
+
operation: OPERATION,
|
|
33
|
+
...observation,
|
|
34
|
+
original_file_offset: { available: false, reason },
|
|
35
|
+
});
|
|
36
|
+
const unavailable = (reason) => err(new AnalysisCapabilityUnavailableError("hopper", OPERATION, reason, {
|
|
37
|
+
partialObservation: partial(reason),
|
|
38
|
+
}));
|
|
39
|
+
if (target.kind !== "executable")
|
|
40
|
+
return unavailable("The selected database digest does not identify its original executable bytes. Open that executable to verify original-file coordinates; read_bytes still exposes the database's native bytes.");
|
|
41
|
+
if (signal?.aborted)
|
|
42
|
+
return err(new AnalysisCancelledError(OPERATION, {
|
|
43
|
+
partialObservation: partial("Source verification was cancelled"),
|
|
44
|
+
}));
|
|
45
|
+
try {
|
|
46
|
+
const captured = await snapshotFromPath(target.path, signal);
|
|
47
|
+
if (!captured.ok)
|
|
48
|
+
return err(new AnalysisArtifactChangedError(OPERATION, target.path, captured.error, {
|
|
49
|
+
partialObservation: partial(captured.error),
|
|
50
|
+
}));
|
|
51
|
+
const snapshot = captured.value;
|
|
52
|
+
if (signal?.aborted)
|
|
53
|
+
return err(new AnalysisCancelledError(OPERATION, {
|
|
54
|
+
partialObservation: partial("Source verification was cancelled"),
|
|
55
|
+
}));
|
|
56
|
+
if (snapshot.sha256 !== target.sha256) {
|
|
57
|
+
const reason = `Source ${target.path} differs from the admitted artifact: expected SHA-256 ${target.sha256}, observed ${snapshot.sha256}. Reopen the changed source for a new analysis identity.`;
|
|
58
|
+
return err(new AnalysisArtifactChangedError(OPERATION, target.path, reason, {
|
|
59
|
+
partialObservation: partial(reason),
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
const mapped = originalOffset(snapshot, observation);
|
|
63
|
+
if (!mapped.ok)
|
|
64
|
+
return unavailable(mapped.error);
|
|
65
|
+
return ok({
|
|
66
|
+
address: observation.address,
|
|
67
|
+
file_offset: mapped.value + observation.provider_file_offset,
|
|
68
|
+
provider_file_offset: observation.provider_file_offset,
|
|
69
|
+
image_base_file_offset: mapped.value,
|
|
70
|
+
source_path: target.path,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
catch (cause) {
|
|
74
|
+
if (signal?.aborted)
|
|
75
|
+
return err(new AnalysisCancelledError(OPERATION, {
|
|
76
|
+
cause,
|
|
77
|
+
partialObservation: partial("Source verification was cancelled"),
|
|
78
|
+
}));
|
|
79
|
+
const reason = `Cannot verify original source bytes: ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
80
|
+
const code = typeof cause === "object" && cause !== null && "code" in cause
|
|
81
|
+
? cause.code
|
|
82
|
+
: undefined;
|
|
83
|
+
if (code === "EACCES" || code === "EPERM")
|
|
84
|
+
return err(new AnalysisAccessDeniedError(OPERATION, target.path, code, {
|
|
85
|
+
cause,
|
|
86
|
+
partialObservation: partial(reason),
|
|
87
|
+
}));
|
|
88
|
+
return err(new BinaryTargetError(target.path, reason, {
|
|
89
|
+
cause,
|
|
90
|
+
partialObservation: partial(reason),
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function snapshotFromPath(path, signal) {
|
|
95
|
+
const file = await open(path, constants.O_RDONLY | constants.O_NONBLOCK);
|
|
96
|
+
try {
|
|
97
|
+
if (!(await file.stat()).isFile())
|
|
98
|
+
return err(`Source ${path} is no longer a regular file. Restore the admitted executable or reopen a regular source.`);
|
|
99
|
+
return ok(await readSourceSnapshot(file, signal));
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
await file.close();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function fatLayout(prefix) {
|
|
106
|
+
const magic = prefix.length < 4 ? 0 : prefix.readUInt32BE(0);
|
|
107
|
+
if (![0xcafebabe, 0xbebafeca, 0xcafebabf, 0xbfbafeca].includes(magic))
|
|
108
|
+
return ok(null);
|
|
109
|
+
if (prefix.length < 8)
|
|
110
|
+
return err("The original FAT header is truncated");
|
|
111
|
+
const littleEndian = magic === 0xbebafeca || magic === 0xbfbafeca;
|
|
112
|
+
const wide = magic === 0xcafebabf || magic === 0xbfbafeca;
|
|
113
|
+
const count = littleEndian ? prefix.readUInt32LE(4) : prefix.readUInt32BE(4);
|
|
114
|
+
if (count === 0 || count > MAX_FAT_ARCHITECTURES)
|
|
115
|
+
return err("FAT architecture count is outside the supported 1..128 range");
|
|
116
|
+
const tableEnd = 8 + count * (wide ? 32 : 20);
|
|
117
|
+
const table = prefix.subarray(8, tableEnd);
|
|
118
|
+
const parsed = readFatSliceDeclarations(table, { count, wide, littleEndian });
|
|
119
|
+
if (parsed.status !== "parsed")
|
|
120
|
+
return err(parsed.reason);
|
|
121
|
+
if (wide) {
|
|
122
|
+
for (const slice of parsed.slices)
|
|
123
|
+
if ((littleEndian
|
|
124
|
+
? table.readUInt32LE(slice.index * 32 + 28)
|
|
125
|
+
: table.readUInt32BE(slice.index * 32 + 28)) !== 0)
|
|
126
|
+
return err(`FAT64 architecture ${slice.index} has a nonzero reserved field`);
|
|
127
|
+
}
|
|
128
|
+
return ok({ slices: parsed.slices, tableEnd });
|
|
129
|
+
}
|
|
130
|
+
async function readSourceSnapshot(file, signal) {
|
|
131
|
+
const prefix = Buffer.alloc(PREFIX_BYTES);
|
|
132
|
+
const hash = createHash("sha256");
|
|
133
|
+
let size = 0;
|
|
134
|
+
let layout;
|
|
135
|
+
const headers = new Map();
|
|
136
|
+
const capture = (bytes, position) => {
|
|
137
|
+
if (layout?.ok !== true || layout.value === null)
|
|
138
|
+
return;
|
|
139
|
+
for (const slice of layout.value.slices) {
|
|
140
|
+
const start = Math.max(position, slice.offset);
|
|
141
|
+
const end = Math.min(position + bytes.length, slice.offset + 12);
|
|
142
|
+
if (end <= start)
|
|
143
|
+
continue;
|
|
144
|
+
let header = headers.get(slice.index);
|
|
145
|
+
if (header === undefined) {
|
|
146
|
+
header = Buffer.alloc(12);
|
|
147
|
+
headers.set(slice.index, header);
|
|
148
|
+
}
|
|
149
|
+
bytes.copy(header, start - slice.offset, start - position, end - position);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
for await (const chunk of file.createReadStream({
|
|
153
|
+
start: 0,
|
|
154
|
+
autoClose: false,
|
|
155
|
+
...(signal === undefined ? {} : { signal }),
|
|
156
|
+
})) {
|
|
157
|
+
const bytes = Buffer.from(chunk);
|
|
158
|
+
hash.update(bytes);
|
|
159
|
+
if (size < prefix.length)
|
|
160
|
+
bytes.copy(prefix, size, 0, Math.min(bytes.length, prefix.length - size));
|
|
161
|
+
if (layout === undefined && size + bytes.length >= prefix.length) {
|
|
162
|
+
layout = fatLayout(prefix);
|
|
163
|
+
capture(prefix, 0);
|
|
164
|
+
}
|
|
165
|
+
capture(bytes, size);
|
|
166
|
+
size += bytes.length;
|
|
167
|
+
}
|
|
168
|
+
if (layout === undefined) {
|
|
169
|
+
layout = fatLayout(prefix.subarray(0, size));
|
|
170
|
+
capture(prefix.subarray(0, size), 0);
|
|
171
|
+
}
|
|
172
|
+
return { layout, headers, size, sha256: hash.digest("hex") };
|
|
173
|
+
}
|
|
174
|
+
function originalOffset(snapshot, observation) {
|
|
175
|
+
const { layout, size, headers } = snapshot;
|
|
176
|
+
if (!layout.ok)
|
|
177
|
+
return layout;
|
|
178
|
+
if (layout.value === null)
|
|
179
|
+
return observation.provider_file_offset < size
|
|
180
|
+
? ok(0)
|
|
181
|
+
: err("Provider mapping lies outside the admitted executable bytes");
|
|
182
|
+
const header = observation.provider_image_header_hex === null
|
|
183
|
+
? null
|
|
184
|
+
: Buffer.from(observation.provider_image_header_hex, "hex");
|
|
185
|
+
const loadedMagic = header?.readUInt32BE(0);
|
|
186
|
+
if (header === null ||
|
|
187
|
+
loadedMagic === undefined ||
|
|
188
|
+
![0xfeedface, 0xfeedfacf, 0xcefaedfe, 0xcffaedfe].includes(loadedMagic))
|
|
189
|
+
return err("Hopper did not expose the loaded Mach-O header needed to identify its FAT slice");
|
|
190
|
+
const little = loadedMagic === 0xcefaedfe || loadedMagic === 0xcffaedfe;
|
|
191
|
+
const cpu = little ? header.readUInt32LE(4) : header.readUInt32BE(4);
|
|
192
|
+
const subtype = little ? header.readUInt32LE(8) : header.readUInt32BE(8);
|
|
193
|
+
const matches = [];
|
|
194
|
+
const ranges = [...layout.value.slices].sort((a, b) => a.offset - b.offset);
|
|
195
|
+
for (let index = 1; index < ranges.length; index++) {
|
|
196
|
+
const previous = ranges[index - 1];
|
|
197
|
+
const current = ranges[index];
|
|
198
|
+
if (previous !== undefined &&
|
|
199
|
+
current !== undefined &&
|
|
200
|
+
current.offset < previous.offset + previous.size)
|
|
201
|
+
return err("Original FAT slices overlap");
|
|
202
|
+
}
|
|
203
|
+
for (const slice of layout.value.slices) {
|
|
204
|
+
const reason = validateFatSlice(slice, size, layout.value.tableEnd);
|
|
205
|
+
if (reason !== null)
|
|
206
|
+
return err(reason);
|
|
207
|
+
const embedded = headers.get(slice.index);
|
|
208
|
+
if (slice.size < 12 || embedded === undefined)
|
|
209
|
+
return err(`FAT architecture ${slice.index} has a truncated Mach-O header`);
|
|
210
|
+
const embeddedMagic = embedded.readUInt32BE(0);
|
|
211
|
+
if (![0xfeedface, 0xfeedfacf, 0xcefaedfe, 0xcffaedfe].includes(embeddedMagic))
|
|
212
|
+
return err(`FAT architecture ${slice.index} does not contain a Mach-O image`);
|
|
213
|
+
const embeddedLittle = embeddedMagic === 0xcefaedfe || embeddedMagic === 0xcffaedfe;
|
|
214
|
+
const identityError = validateFatSlice(slice, size, layout.value.tableEnd, {
|
|
215
|
+
cpuType: embeddedLittle
|
|
216
|
+
? embedded.readUInt32LE(4)
|
|
217
|
+
: embedded.readUInt32BE(4),
|
|
218
|
+
cpuSubtype: embeddedLittle
|
|
219
|
+
? embedded.readUInt32LE(8)
|
|
220
|
+
: embedded.readUInt32BE(8),
|
|
221
|
+
});
|
|
222
|
+
if (identityError !== null)
|
|
223
|
+
return err(identityError);
|
|
224
|
+
if (slice.cpuType === cpu && slice.cpuSubtype === subtype)
|
|
225
|
+
matches.push(slice);
|
|
226
|
+
}
|
|
227
|
+
const selected = matches.length === 1 ? matches[0] : undefined;
|
|
228
|
+
if (selected === undefined)
|
|
229
|
+
return err(`Original FAT slice matching Hopper's loaded header is ${matches.length === 0 ? "missing" : "ambiguous"}`);
|
|
230
|
+
if (headers.get(selected.index)?.equals(header) !== true)
|
|
231
|
+
return err("Original FAT slice header disagrees with Hopper's loaded header");
|
|
232
|
+
if (observation.provider_file_offset >= selected.size)
|
|
233
|
+
return err("Provider mapping lies outside the loaded FAT slice");
|
|
234
|
+
if (!Number.isSafeInteger(selected.offset + observation.provider_file_offset))
|
|
235
|
+
return err("Original file offset exceeds the exact JSON integer range");
|
|
236
|
+
return ok(selected.offset);
|
|
237
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { open } from "node:fs/promises";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { BinaryTargetError } from "../domain/configurationErrors.js";
|
|
5
|
+
import { AnalysisCancelledError } from "../domain/analysisErrorCore.js";
|
|
6
|
+
import { readFatSliceDeclarations, validateFatSlice, } from "../domain/apple/machoContainer.js";
|
|
7
|
+
import { HopperCancelledError, HopperStartError, } from "../domain/hopperErrors.js";
|
|
8
|
+
import { err, ok } from "../domain/result.js";
|
|
9
|
+
/** Stable source coordinates committed before preparing an owned thin image. */
|
|
10
|
+
export const hopperMachOImageSchema = z.strictObject({
|
|
11
|
+
method: z.literal("fat64_thin_slice"),
|
|
12
|
+
architecture_count: z.number().int().min(1).max(128),
|
|
13
|
+
container_table_sha256: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
14
|
+
source_offset: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
15
|
+
byte_length: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
16
|
+
cpu_type: z.number().int().nonnegative(),
|
|
17
|
+
cpu_subtype: z.number().int().nonnegative(),
|
|
18
|
+
});
|
|
19
|
+
/** Validate the selected FAT64 slice without depending on Apple's lipo tool. */
|
|
20
|
+
export const resolveHopperMachOImage = async (target, signal) => {
|
|
21
|
+
const invalid = (reason) => err(new BinaryTargetError(target.path, reason));
|
|
22
|
+
if (signal?.aborted)
|
|
23
|
+
return err(new AnalysisCancelledError("open_binary"));
|
|
24
|
+
let file;
|
|
25
|
+
try {
|
|
26
|
+
file = await open(target.path, "r");
|
|
27
|
+
}
|
|
28
|
+
catch (cause) {
|
|
29
|
+
return invalid(`Cannot read FAT64 source for Hopper image preparation: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const read = async (offset, length) => {
|
|
33
|
+
const bytes = Buffer.alloc(length);
|
|
34
|
+
const { bytesRead } = await file.read(bytes, 0, length, offset);
|
|
35
|
+
return bytes.subarray(0, bytesRead);
|
|
36
|
+
};
|
|
37
|
+
const header = await read(0, 8);
|
|
38
|
+
const magic = header.length === 8 ? header.readUInt32BE(0) : 0;
|
|
39
|
+
if (magic !== 0xcafebabf && magic !== 0xbfbafeca)
|
|
40
|
+
return invalid("FAT64 source header changed before Hopper image preparation");
|
|
41
|
+
const littleEndian = magic === 0xbfbafeca;
|
|
42
|
+
const count = littleEndian
|
|
43
|
+
? header.readUInt32LE(4)
|
|
44
|
+
: header.readUInt32BE(4);
|
|
45
|
+
const size = (await file.stat()).size;
|
|
46
|
+
const tableEnd = 8 + count * 32;
|
|
47
|
+
if (count === 0 || count > 128 || tableEnd > size)
|
|
48
|
+
return invalid("FAT64 architecture table is truncated or exceeds the supported 128 entries");
|
|
49
|
+
const table = await read(8, count * 32);
|
|
50
|
+
const parsed = readFatSliceDeclarations(table, {
|
|
51
|
+
count,
|
|
52
|
+
wide: true,
|
|
53
|
+
littleEndian,
|
|
54
|
+
});
|
|
55
|
+
if (parsed.status === "malformed")
|
|
56
|
+
return invalid(parsed.reason);
|
|
57
|
+
const cpuTypes = { x86: 7, x86_64: 0x1000007, arm: 12, arm64: 0x100000c };
|
|
58
|
+
const matches = [];
|
|
59
|
+
const ranges = [];
|
|
60
|
+
for (const slice of parsed.slices) {
|
|
61
|
+
if (signal?.aborted)
|
|
62
|
+
return err(new AnalysisCancelledError("open_binary"));
|
|
63
|
+
const rangeError = validateFatSlice(slice, size, tableEnd);
|
|
64
|
+
if (rangeError !== null)
|
|
65
|
+
return invalid(rangeError);
|
|
66
|
+
const reserved = littleEndian
|
|
67
|
+
? table.readUInt32LE(slice.index * 32 + 28)
|
|
68
|
+
: table.readUInt32BE(slice.index * 32 + 28);
|
|
69
|
+
if (reserved !== 0)
|
|
70
|
+
return invalid(`FAT64 architecture ${slice.index} has a nonzero reserved field`);
|
|
71
|
+
if (slice.size < 12)
|
|
72
|
+
return invalid(`FAT64 architecture ${slice.index} has a truncated Mach-O header`);
|
|
73
|
+
const embedded = await read(slice.offset, 12);
|
|
74
|
+
if (embedded.length !== 12)
|
|
75
|
+
return invalid(`FAT64 architecture ${slice.index} became truncated`);
|
|
76
|
+
const thinMagic = embedded.readUInt32BE(0);
|
|
77
|
+
if (![0xfeedface, 0xfeedfacf, 0xcefaedfe, 0xcffaedfe].includes(thinMagic))
|
|
78
|
+
return invalid(`FAT64 architecture ${slice.index} does not contain a Mach-O image`);
|
|
79
|
+
const little = thinMagic === 0xcefaedfe || thinMagic === 0xcffaedfe;
|
|
80
|
+
const identity = {
|
|
81
|
+
cpuType: little ? embedded.readUInt32LE(4) : embedded.readUInt32BE(4),
|
|
82
|
+
cpuSubtype: little
|
|
83
|
+
? embedded.readUInt32LE(8)
|
|
84
|
+
: embedded.readUInt32BE(8),
|
|
85
|
+
};
|
|
86
|
+
const identityError = validateFatSlice(slice, size, tableEnd, identity);
|
|
87
|
+
if (identityError !== null)
|
|
88
|
+
return invalid(identityError);
|
|
89
|
+
ranges.push(slice);
|
|
90
|
+
if (slice.cpuType === cpuTypes[target.architecture])
|
|
91
|
+
matches.push(slice);
|
|
92
|
+
}
|
|
93
|
+
ranges.sort((a, b) => a.offset - b.offset);
|
|
94
|
+
for (let index = 1; index < ranges.length; index++) {
|
|
95
|
+
const previous = ranges[index - 1];
|
|
96
|
+
const current = ranges[index];
|
|
97
|
+
if (previous !== undefined &&
|
|
98
|
+
current !== undefined &&
|
|
99
|
+
current.offset < previous.offset + previous.size)
|
|
100
|
+
return invalid("FAT64 slices overlap");
|
|
101
|
+
}
|
|
102
|
+
const selected = matches.length === 1 ? matches[0] : undefined;
|
|
103
|
+
if (selected === undefined)
|
|
104
|
+
return invalid(`FAT64 selection for ${target.architecture} is ${matches.length === 0 ? "missing" : "ambiguous"}; extract the exact architecture into a thin image before opening it`);
|
|
105
|
+
return ok({
|
|
106
|
+
method: "fat64_thin_slice",
|
|
107
|
+
architecture_count: count,
|
|
108
|
+
container_table_sha256: createHash("sha256")
|
|
109
|
+
.update(header)
|
|
110
|
+
.update(table)
|
|
111
|
+
.digest("hex"),
|
|
112
|
+
source_offset: selected.offset,
|
|
113
|
+
byte_length: selected.size,
|
|
114
|
+
cpu_type: selected.cpuType,
|
|
115
|
+
cpu_subtype: selected.cpuSubtype,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch (cause) {
|
|
119
|
+
return invalid(`Cannot validate FAT64 image: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
await file.close();
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
/** Copy a selected slice and verify the entire original source in the same pass. */
|
|
126
|
+
export const prepareHopperMachOImage = async (sourcePath, sourceSha256, image, destination, signal) => {
|
|
127
|
+
if (signal?.aborted)
|
|
128
|
+
return err(new HopperCancelledError());
|
|
129
|
+
try {
|
|
130
|
+
const source = await open(sourcePath, "r");
|
|
131
|
+
try {
|
|
132
|
+
const output = await open(destination, "wx", 0o600);
|
|
133
|
+
try {
|
|
134
|
+
const hash = createHash("sha256");
|
|
135
|
+
const tableHash = createHash("sha256");
|
|
136
|
+
const tableEnd = 8 + image.architecture_count * 32;
|
|
137
|
+
const thinHeader = [];
|
|
138
|
+
let position = 0;
|
|
139
|
+
let copied = 0;
|
|
140
|
+
for await (const chunk of source.createReadStream({
|
|
141
|
+
autoClose: false,
|
|
142
|
+
})) {
|
|
143
|
+
if (signal?.aborted)
|
|
144
|
+
return err(new HopperCancelledError());
|
|
145
|
+
const bytes = Buffer.from(chunk);
|
|
146
|
+
hash.update(bytes);
|
|
147
|
+
if (position < tableEnd)
|
|
148
|
+
tableHash.update(bytes.subarray(0, Math.min(bytes.length, tableEnd - position)));
|
|
149
|
+
const start = Math.max(0, image.source_offset - position);
|
|
150
|
+
const end = Math.min(bytes.length, image.source_offset + image.byte_length - position);
|
|
151
|
+
if (end > start) {
|
|
152
|
+
if (copied < 12)
|
|
153
|
+
thinHeader.push(bytes.subarray(start, Math.min(end, start + 12 - copied)));
|
|
154
|
+
await output.writeFile(bytes.subarray(start, end));
|
|
155
|
+
copied += end - start;
|
|
156
|
+
}
|
|
157
|
+
position += bytes.length;
|
|
158
|
+
}
|
|
159
|
+
const observed = hash.digest("hex");
|
|
160
|
+
const header = Buffer.concat(thinHeader);
|
|
161
|
+
const magic = header.length === 12 ? header.readUInt32BE(0) : 0;
|
|
162
|
+
const little = magic === 0xcefaedfe || magic === 0xcffaedfe;
|
|
163
|
+
const headerMatches = header.length === 12 &&
|
|
164
|
+
[0xfeedface, 0xfeedfacf, 0xcefaedfe, 0xcffaedfe].includes(magic) &&
|
|
165
|
+
(little ? header.readUInt32LE(4) : header.readUInt32BE(4)) ===
|
|
166
|
+
image.cpu_type &&
|
|
167
|
+
(little ? header.readUInt32LE(8) : header.readUInt32BE(8)) ===
|
|
168
|
+
image.cpu_subtype;
|
|
169
|
+
if (tableHash.digest("hex") !== image.container_table_sha256 ||
|
|
170
|
+
!headerMatches)
|
|
171
|
+
return err(new HopperStartError({
|
|
172
|
+
userMessage: `FAT64 source ${sourcePath} no longer agrees with its committed architecture table or selected Mach-O header. Open the stable source again.`,
|
|
173
|
+
}));
|
|
174
|
+
if (observed !== sourceSha256 || copied !== image.byte_length)
|
|
175
|
+
return err(new HopperStartError({
|
|
176
|
+
userMessage: `FAT64 source ${sourcePath} changed before launch: expected SHA-256 ${sourceSha256}, observed ${observed}; copied ${copied} of ${image.byte_length} selected bytes. Open the stable source again.`,
|
|
177
|
+
}));
|
|
178
|
+
return ok(destination);
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
await output.close();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
await source.close();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (cause) {
|
|
189
|
+
return signal?.aborted
|
|
190
|
+
? err(new HopperCancelledError())
|
|
191
|
+
: err(new HopperStartError({
|
|
192
|
+
cause,
|
|
193
|
+
userMessage: `Cannot prepare the selected FAT64 image from ${sourcePath}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
};
|
|
@@ -5,7 +5,10 @@ import { AnalysisCapabilityUnavailableError } from "../domain/analysisErrorCore.
|
|
|
5
5
|
import { err } from "../domain/result.js";
|
|
6
6
|
import { HopperApplicationLauncher } from "./BridgeLauncher.js";
|
|
7
7
|
import { hopperLoaderArgsForTarget, resolveHopperAnalysisProfile, } from "./HopperAnalysisProfile.js";
|
|
8
|
+
import { mapHopperFileOffset } from "./HopperFileOffset.js";
|
|
9
|
+
import { HopperRegexSearch } from "./HopperRegexSearch.js";
|
|
8
10
|
import { HopperClient } from "./HopperClient.js";
|
|
11
|
+
import { hopperMachOImageSchema } from "./HopperMachOImage.js";
|
|
9
12
|
import { CAPABILITIES, HOPPER_PROVIDER_IDENTITY, } from "./HopperProviderCapabilities.js";
|
|
10
13
|
export { HOPPER_PROVIDER_IDENTITY, HOPPER_OPERATIONS, } from "./HopperProviderCapabilities.js";
|
|
11
14
|
const IDENTITY = HOPPER_PROVIDER_IDENTITY;
|
|
@@ -111,7 +114,22 @@ export class HopperProvider {
|
|
|
111
114
|
execute: (operation) => Promise.resolve(err(new AnalysisCapabilityUnavailableError(IDENTITY.id, operation, `Hopper cannot open ${target.kind} targets directly. Inventory or extract the artifact first.`))),
|
|
112
115
|
close: () => Promise.resolve(),
|
|
113
116
|
};
|
|
114
|
-
const
|
|
117
|
+
const preparation = profile?.parameters.prepared_image;
|
|
118
|
+
const parsedImage = preparation === undefined
|
|
119
|
+
? undefined
|
|
120
|
+
: hopperMachOImageSchema.safeParse(preparation);
|
|
121
|
+
if (parsedImage?.success === false)
|
|
122
|
+
return {
|
|
123
|
+
execute: (operation) => Promise.resolve(err(new AnalysisCapabilityUnavailableError(IDENTITY.id, operation, "Hopper prepared-image profile is malformed; resolve the analysis profile again."))),
|
|
124
|
+
close: () => Promise.resolve(),
|
|
125
|
+
};
|
|
126
|
+
const preparedImage = parsedImage?.data;
|
|
127
|
+
const container = preparedImage === undefined
|
|
128
|
+
? profile?.parameters.macho_container
|
|
129
|
+
: "thin";
|
|
130
|
+
const derivedLoaderArgs = hopperLoaderArgsForTarget(target, container === "thin" || container === "fat32" || container === "fat64"
|
|
131
|
+
? container
|
|
132
|
+
: undefined);
|
|
115
133
|
if (!derivedLoaderArgs.ok)
|
|
116
134
|
return {
|
|
117
135
|
execute: () => Promise.resolve(err(derivedLoaderArgs.error)),
|
|
@@ -123,6 +141,14 @@ export class HopperProvider {
|
|
|
123
141
|
launcherPath: this.config.hopperLauncherPath,
|
|
124
142
|
targetPath: target.path,
|
|
125
143
|
targetKind: target.kind,
|
|
144
|
+
...(preparedImage === undefined
|
|
145
|
+
? {}
|
|
146
|
+
: {
|
|
147
|
+
preparedImage: {
|
|
148
|
+
image: preparedImage,
|
|
149
|
+
sourceSha256: target.sha256,
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
126
152
|
loaderArgs: this.config.hopperLoaderArgs.length > 0
|
|
127
153
|
? this.config.hopperLoaderArgs
|
|
128
154
|
: derivedLoaderArgs.value,
|
|
@@ -138,25 +164,46 @@ export class HopperProvider {
|
|
|
138
164
|
logger: this.logger.child({ layer: "bridge" }),
|
|
139
165
|
onDiagnostic: (diagnostic) => this.logger.info(diagnostic, "Hopper bridge reported a diagnostic"),
|
|
140
166
|
});
|
|
167
|
+
const regexSearch = new HopperRegexSearch(client);
|
|
141
168
|
return {
|
|
142
169
|
execute: async (operation, parameters, options) => {
|
|
143
|
-
const result =
|
|
144
|
-
|
|
170
|
+
const result = (operation === "search_strings" ||
|
|
171
|
+
operation === "search_procedures") &&
|
|
172
|
+
parameters.mode === "regex"
|
|
173
|
+
? await regexSearch.execute(operation, parameters, options)
|
|
174
|
+
: await client.callTool(operation, parameters, options);
|
|
175
|
+
if (!result.ok)
|
|
176
|
+
return result;
|
|
177
|
+
const mapped = operation === "address_to_file_offset"
|
|
178
|
+
? await mapHopperFileOffset(target, result.value, options?.signal)
|
|
179
|
+
: result;
|
|
180
|
+
return mapped.ok
|
|
145
181
|
? {
|
|
146
182
|
ok: true,
|
|
147
|
-
value: createAnalysisExecution(
|
|
183
|
+
value: createAnalysisExecution(mapped.value, executionProvider, {
|
|
184
|
+
rawResult: result.value,
|
|
148
185
|
...(profile === undefined ? {} : { analysisProfile: profile }),
|
|
149
186
|
limitations: [
|
|
150
|
-
|
|
187
|
+
...(preparedImage === undefined
|
|
188
|
+
? []
|
|
189
|
+
: [
|
|
190
|
+
"REA loaded a verified thin Mach-O slice from the original FAT64 source. Identity and file offsets refer to the original file. The owned temporary image and its Hopper document close together, including on MCP shutdown.",
|
|
191
|
+
]),
|
|
192
|
+
...(CAPABILITIES.find((descriptor) => descriptor.operation === operation)?.limitations ?? []),
|
|
193
|
+
...(operation === "read_bytes"
|
|
194
|
+
? [
|
|
195
|
+
"Hopper byte reads stop at the containing segment's exclusive end; a readable prefix is returned with complete=false when the requested range crosses it.",
|
|
196
|
+
]
|
|
197
|
+
: []),
|
|
151
198
|
...(operation === "procedure_references"
|
|
152
199
|
? [
|
|
153
|
-
"
|
|
200
|
+
"Native CallReference classifications and unresolved targets are preserved. Detailed reference flags and coverage beyond reported CallReference objects remain unknown.",
|
|
154
201
|
]
|
|
155
202
|
: []),
|
|
156
203
|
],
|
|
157
204
|
}),
|
|
158
205
|
}
|
|
159
|
-
:
|
|
206
|
+
: mapped;
|
|
160
207
|
},
|
|
161
208
|
runtimeLineageSnapshots: () => {
|
|
162
209
|
const observation = client.runtimeLineage();
|
|
@@ -181,8 +228,15 @@ export class HopperProvider {
|
|
|
181
228
|
},
|
|
182
229
|
];
|
|
183
230
|
},
|
|
184
|
-
|
|
185
|
-
|
|
231
|
+
operationHealthSnapshot: () => client.operationHealth(),
|
|
232
|
+
closeWithOutcome: async (options) => {
|
|
233
|
+
await regexSearch.close();
|
|
234
|
+
return client.closeWithOutcome(options);
|
|
235
|
+
},
|
|
236
|
+
close: async () => {
|
|
237
|
+
await regexSearch.close();
|
|
238
|
+
await client.close();
|
|
239
|
+
},
|
|
186
240
|
};
|
|
187
241
|
}
|
|
188
242
|
}
|
|
@@ -38,7 +38,6 @@ export const HOPPER_OPERATIONS = Object.freeze([
|
|
|
38
38
|
"set_addresses_names",
|
|
39
39
|
"set_bookmark",
|
|
40
40
|
"set_comment",
|
|
41
|
-
"set_current_document",
|
|
42
41
|
"set_inline_comment",
|
|
43
42
|
"unset_bookmark",
|
|
44
43
|
"xrefs",
|
|
@@ -69,5 +68,24 @@ export const CAPABILITIES = Object.freeze(HOPPER_OPERATIONS.map((operation) => O
|
|
|
69
68
|
}),
|
|
70
69
|
limitations: Object.freeze([
|
|
71
70
|
"Results depend on Hopper's completed static analysis.",
|
|
71
|
+
...(operation === "address_to_file_offset"
|
|
72
|
+
? [
|
|
73
|
+
"Original-file coordinates require source bytes matching the selected executable digest. A changed, unavailable, or database-only source retains native mapping facts in partial error evidence.",
|
|
74
|
+
]
|
|
75
|
+
: []),
|
|
76
|
+
...(operation === "search_strings" || operation === "search_procedures"
|
|
77
|
+
? [
|
|
78
|
+
"Regex mode uses ECMAScript Unicode semantics in a supervised worker with a five-second matching deadline. Cancellation or deadline failure stops matching without blocking Hopper; literal mode retains native casefold semantics.",
|
|
79
|
+
]
|
|
80
|
+
: []),
|
|
81
|
+
...(operation === "list_strings" ||
|
|
82
|
+
operation === "search_strings" ||
|
|
83
|
+
operation === "analyze_function"
|
|
84
|
+
? [
|
|
85
|
+
"String values cover native typed objects. Hopper may split long literals into adjacent unterminated fragments; string searches match each object independently.",
|
|
86
|
+
"Undecodable typed objects retain native display text with an explicit decoding unknown; these values do not establish decoded source literals.",
|
|
87
|
+
"Reported string encodings are inferred from typed bytes and native display; the original source's intended encoding is unknown.",
|
|
88
|
+
]
|
|
89
|
+
: []),
|
|
72
90
|
]),
|
|
73
91
|
})));
|