* feat(pe): inspect import and delay-load tables Add inspect_pe_imports / inspect-pe-imports to read PE32/PE32+ import and delay-load directories without executing the image. The report keeps DLL names, named and ordinal functions, ILT/IAT RVAs, and a pefile import hash of the standard import table. A missing directory is observed absence, not proof the image never imported APIs. * fix(pe): default PE import fixture options Allow peImportImage() without an argument so CLI and filesystem tests can use the KERNEL32 sample without repeating an empty options object. * test(pe): keep import path cases on the dedicated MCP contract inspect_pe_imports already rejects relative MCP paths in its own boundary test. Leave the shared absolute-path matrix to tools whose handler issue projection matches that suite's expected shape. * fix(native): index virtual-to-file mapping lookups mappedFileOffset scans every mapping twice per span, so a parser that translates one span per table entry costs sections x entries. Add fileMappingLookup, which builds an O(n log n) interval index once and answers each span in O(log n) with the same accept, reject and translation results. A differential test checks it against mappedFileOffset over randomized overlapping mappings and zero fills. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g * fix(pe): report partial import evidence and exact pefile hashes - Withhold imphash with an explicit reason whenever the standard import table is partial (unreadable names or thunks, missing tables), or when pefile 2024.8.26 would hash a different row sequence: invalid or truncated names, ordinal 0, descriptors without functions, tables its bounds or repeat/spread heuristics discard, and its 8192-thunk import symbol cap. Delay-load issues do not withhold it. - Hash ordinal imports with pefile's complete ordlookup tables and its ord<decimal> fallback instead of declining unmapped ordinals. - Report a nonterminal descriptor whose FirstThunk is zero, and a delay-load descriptor whose IAT or INT RVA is zero, as malformed_entry partial coverage instead of a complete empty inventory. - Directory evidence lists the file spans actually consumed for the descriptor array and terminator; discontiguous mappings produce separate spans and separate Evidence ranges. - Carry exhausted limits as PeImportLimitFailure.limit and derive recovery from it; the fixed 16 MiB retained-text ceiling reports a fixed allowance instead of max_file_bytes advice. - Translate RVAs through the indexed mapping lookup and yield with a cancellation check every 1024 table entries. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g * fix(pe): read only own ordinal tables for DLL names DLL names such as constructor and __proto__ pass pefile's name rule but resolved inherited object members, throwing before the ord<n> fallback. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Ra7LM2NyQ7DumdssxbUq5g * fix(native): preserve PE import facts and prove hash compatibility * test(native): verify PE import workflows and measured costs * refactor(native): narrow PE facts across the container union * docs(native): record integrated PE import validation * fix(native): bound every PE import RVA span * docs(native): measure RVA guard completed workflows * fix(pe): reconcile import coverage with retained rows * fix(pe): retain partial imports at descriptor mapping gaps --------- Co-authored-by: Matt Van Horn <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: gensokyo-dev <[email protected]>
1255 lines
46 KiB
TypeScript
1255 lines
46 KiB
TypeScript
import { pcapCaptureHostSupported } from "../domain/pcap/pcapAnalysis.js";
|
|
import { createGpuCodeObjectsService } from "../composition/gpu.js";
|
|
import { registerGpuCodeObjectsTools } from "./registerGpuCodeObjectsTools.js";
|
|
import type { PacketCaptureService } from "../application/captures/PacketCaptureService.js";
|
|
import { UNTRUSTED_CONTENT_INSTRUCTIONS } from "./toolResultEncoding.js";
|
|
import {
|
|
createPacketCaptureService,
|
|
tsharkHostSupported,
|
|
} from "../composition/captures.js";
|
|
import { registerCaptureTools } from "./registerCaptureTools.js";
|
|
import type { SignalRecordingService } from "../application/signals/SignalRecordingService.js";
|
|
import {
|
|
createSignalRecordingService,
|
|
sigrokHostSupported,
|
|
} from "../composition/signals.js";
|
|
import { registerSignalTools } from "./registerSignalTools.js";
|
|
import type { WasmArtifactService } from "../application/wasm/WasmArtifactService.js";
|
|
import { wasmArtifactModule } from "../composition/wasm.js";
|
|
import { registerWasmTools } from "./registerWasmTools.js";
|
|
import type { ApkSignatureService } from "../application/apkSignature/ApkSignatureService.js";
|
|
import { apkSignatureModule } from "../composition/apkSignature.js";
|
|
import { registerApkSignatureTools } from "./registerApkSignatureTools.js";
|
|
import { snapshotEnvironment } from "../process/snapshotEnvironment.js";
|
|
import { captureProviderEnvironment } from "./providerEnvironment.js";
|
|
import {
|
|
createElfNotesService,
|
|
createPeCertificatesService,
|
|
createPeImportsService,
|
|
createNativeExportsService,
|
|
createPeResourcesService,
|
|
} from "../composition/binaryDiagnostics.js";
|
|
import type { ElfNotesService } from "../application/binaryDiagnostics/ElfNotesService.js";
|
|
import type { PeCertificatesService } from "../application/binaryDiagnostics/PeCertificatesService.js";
|
|
import type { PeImportsService } from "../application/binaryDiagnostics/PeImportsService.js";
|
|
import type { PeResourcesService } from "../application/binaryDiagnostics/PeResourcesService.js";
|
|
import type { NativeExportsService } from "../application/binaryDiagnostics/NativeExportsService.js";
|
|
import { registerElfNotesTool } from "./registerElfNotesTool.js";
|
|
import { registerPeCertificatesTool } from "./registerPeCertificatesTool.js";
|
|
import { registerPeImportsTool } from "./registerPeImportsTool.js";
|
|
import { registerPeResourcesTool } from "./registerPeResourcesTool.js";
|
|
import { registerNativeExportsTool } from "./registerNativeExportsTool.js";
|
|
import type { EvmInterfaceService } from "../application/evm/EvmInterfaceService.js";
|
|
import {
|
|
evmHostSupported,
|
|
evmInterfaceModule,
|
|
evmLimiterAvailability,
|
|
} from "../composition/evm.js";
|
|
import { registerEvmTools } from "./registerEvmTools.js";
|
|
import type { SqliteDatabaseService } from "../application/sqlite/SqliteDatabaseService.js";
|
|
import { createSqliteDatabaseService } from "../composition/sqlite.js";
|
|
import { registerSqliteTools } from "./registerSqliteTools.js";
|
|
import { registerRecordedCrashTools } from "./registerRecordedCrashTools.js";
|
|
import { createRecordedCrashService } from "../composition/binaryDiagnostics.js";
|
|
import { createDesktopApplicationService } from "../composition/desktop.js";
|
|
import { registerDesktopApplicationTool } from "./registerDesktopApplicationTool.js";
|
|
import { createGoBinaryService } from "../composition/go.js";
|
|
import { createModelArtifactService } from "../composition/model.js";
|
|
import { registerModelArtifactTools } from "./registerModelArtifactTools.js";
|
|
import { createIl2cppMetadataService } from "../composition/il2cpp.js";
|
|
import type { Il2cppMetadataService } from "../application/il2cpp/Il2cppMetadataService.js";
|
|
import type { GoBinaryService } from "../application/go/GoBinaryService.js";
|
|
import { createElectronFuseService } from "../composition/electronFuses.js";
|
|
import type { ElectronFuseService } from "../application/electron/ElectronFuseService.js";
|
|
import { registerGoTools } from "./registerGoTools.js";
|
|
import { registerIl2cppTools } from "./registerIl2cppTools.js";
|
|
import { createRustBinaryService } from "../composition/rust.js";
|
|
import type { RustBinaryService } from "../application/rust/RustBinaryService.js";
|
|
import { registerRustTools } from "./registerRustTools.js";
|
|
import { createPythonPycService } from "../composition/pythonPyc.js";
|
|
import type { PythonPycService } from "../application/python/PythonPycService.js";
|
|
import { registerPythonPycTool } from "./registerPythonPycTool.js";
|
|
import { createBeamService } from "../composition/erlang.js";
|
|
import type { BeamService } from "../application/erlang/BeamService.js";
|
|
import { registerErlangTools } from "./registerErlangTools.js";
|
|
import type { RecordedCrashService } from "../application/binaryDiagnostics/RecordedCrashService.js";
|
|
import { registerAnalysisViewTool } from "./registerAnalysisViewTool.js";
|
|
import { registerBinaryDiagnosticsTools } from "./registerBinaryDiagnosticsTools.js";
|
|
import { FridaInstrumentationManager } from "../frida/FridaInstrumentationManager.js";
|
|
import { registerFridaTools } from "./registerFridaTools.js";
|
|
import {
|
|
createByteEntropyService,
|
|
createBinaryLayoutService,
|
|
pwntoolsHostSupported,
|
|
} from "../composition/binaryDiagnostics.js";
|
|
import type { BinaryLayoutService } from "../application/binaryDiagnostics/BinaryLayoutService.js";
|
|
import { createCatalogIdentity } from "../catalogIdentity.js";
|
|
import { EvidenceMcpServer } from "./EvidenceMcpServer.js";
|
|
import {
|
|
createToolResultDelivery,
|
|
type ToolResultDelivery,
|
|
} from "./toolResult.js";
|
|
import { parseMcpResponseBudget } from "../config/mcpResponseBudget.js";
|
|
import { parseMcpOutputSchemaProfile } from "../config/mcpOutputSchemaProfile.js";
|
|
import {
|
|
compactPresentationForMcpInputSchemaProfile,
|
|
parseMcpInputSchemaProfile,
|
|
} from "../config/mcpInputSchemaProfile.js";
|
|
import type { CompactInputSchemaPresentation } from "./EvidenceMcpServer.js";
|
|
import { isAbsolute } from "node:path";
|
|
|
|
import type { BinarySessionPort } from "../application/binary/BinarySessionPort.js";
|
|
import { investigationEvidenceLookup } from "../application/investigation/InvestigationEvidence.js";
|
|
import type { BrowserObservationPort } from "../application/BrowserObservationPort.js";
|
|
import type { BrowserScenarioCapturePort } from "../application/BrowserScenarioCapturePort.js";
|
|
import type { ElectronActiveObservationPort } from "../application/javascript/ElectronActiveObservationPort.js";
|
|
import type { ElectronObservationPort } from "../application/javascript/ElectronObservationPort.js";
|
|
import type { JavaScriptRuntimeObservationPort } from "../application/javascript/JavaScriptRuntimeObservationPort.js";
|
|
import type { FridaInstrumentationPort } from "../application/frida/FridaInstrumentationPort.js";
|
|
import type { OptionalProviderLoadFailures } from "../application/OptionalObservationProviders.js";
|
|
import { PRODUCT_IDENTITY } from "../identity.js";
|
|
import { silentLogger } from "../logger.js";
|
|
import type { Logger } from "pino";
|
|
import { registerApplicationTools } from "./registerApplicationTools.js";
|
|
import { registerArtifactTools } from "./registerArtifactTools.js";
|
|
import { registerNativeTools } from "./registerNativeTools.js";
|
|
import { registerBrowserScenarioTool } from "./registerBrowserScenarioTool.js";
|
|
import { registerBrowserTools } from "./registerBrowserTools.js";
|
|
import { registerWebScriptTool } from "./registerWebScriptTool.js";
|
|
import { registerWebModuleTool } from "./registerWebModuleTool.js";
|
|
import type { WebModuleTraceService } from "../application/WebModuleTraceService.js";
|
|
import { createWebModuleTraceService } from "../composition/webModules.js";
|
|
import { createWebSourceLocationService } from "../composition/webSourceLocations.js";
|
|
import { registerWebSourceLocationTool } from "./registerWebSourceLocationTool.js";
|
|
import type { WebSourceLocationService } from "../application/WebSourceLocationService.js";
|
|
import type { WebRuntimeService } from "../application/WebRuntimeService.js";
|
|
import { createWebRuntimeService } from "../composition/webRuntime.js";
|
|
import { registerWebRuntimeTools } from "./registerWebRuntimeTools.js";
|
|
import type { WebNetworkCaptureService } from "../application/WebNetworkCaptureService.js";
|
|
import { createWebNetworkCaptureService } from "../composition/webNetworkCaptures.js";
|
|
import { registerWebNetworkCaptureTool } from "./registerWebNetworkCaptureTool.js";
|
|
import { registerJavaScriptRecoveryTool } from "./registerJavaScriptRecoveryTool.js";
|
|
import { JavaScriptRecoveryService } from "../application/javascript/JavaScriptRecoveryService.js";
|
|
import type { JavaScriptRecoveryPort } from "../application/javascript/JavaScriptRecoveryPort.js";
|
|
import {
|
|
createJavaScriptRecoveryProvider,
|
|
wakaruHostSupported,
|
|
} from "../composition/javascriptRecovery.js";
|
|
import { registerElectronTools } from "./registerElectronTools.js";
|
|
import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
|
|
import { registerEnhancedTools } from "./registerEnhancedTools.js";
|
|
import { registerJavaScriptRuntimeObservationTools } from "./registerJavaScriptRuntimeObservationTools.js";
|
|
import { registerManagedTools } from "./registerManagedTools.js";
|
|
import { registerFirmwareTools } from "./registerFirmwareTools.js";
|
|
import { FirmwareAnalysisService } from "../application/firmware/FirmwareAnalysisService.js";
|
|
import type { FirmwareAnalysisPort } from "../application/firmware/FirmwareAnalysisPort.js";
|
|
import {
|
|
createFirmwareAnalysisProvider,
|
|
firmwareHostSupported,
|
|
} from "../composition/firmware.js";
|
|
import { registerAndroidTools } from "./registerAndroidTools.js";
|
|
import { registerApktoolTools } from "./registerApktoolTools.js";
|
|
import { registerPcapTools } from "./registerPcapTools.js";
|
|
import { registerFlutterTools } from "./registerFlutterTools.js";
|
|
import { registerJebTools } from "./registerJebTools.js";
|
|
import { registerAdbTools } from "./registerAdbTools.js";
|
|
import { AndroidAnalysisService } from "../application/android/AndroidAnalysisService.js";
|
|
import { JebAnalysisService } from "../application/jeb/JebAnalysisService.js";
|
|
import type { AndroidAnalysisPort } from "../application/android/AndroidAnalysisPort.js";
|
|
import type { ApktoolResourceAnalysisPort } from "../application/apktool/ApktoolResourceAnalysisPort.js";
|
|
import type { PcapAnalysisPort } from "../application/pcap/PcapAnalysisPort.js";
|
|
import { PcapAnalysisService } from "../application/pcap/PcapAnalysisService.js";
|
|
import {
|
|
locateWiresharkTool,
|
|
selectWiresharkTool,
|
|
} from "../pcap/PcapConfiguration.js";
|
|
import { ApktoolResourceAnalysisService } from "../application/apktool/ApktoolResourceAnalysisService.js";
|
|
import type { FlutterBuildAnalysisPort } from "../application/flutter/FlutterBuildAnalysisPort.js";
|
|
import { FlutterBuildAnalysisService } from "../application/flutter/FlutterBuildAnalysisService.js";
|
|
import { createAndroidAnalysisProvider } from "../composition/android.js";
|
|
import { createApktoolResourceAnalysisProvider } from "../composition/apktool.js";
|
|
import { createPcapAnalysisProvider } from "../composition/pcap.js";
|
|
import { createFlutterBuildAnalysisProvider } from "../composition/flutter.js";
|
|
import type { JebAnalysisPort } from "../application/jeb/JebAnalysisPort.js";
|
|
import { createJebAnalysisProvider } from "../composition/jeb.js";
|
|
import type { AdbDeviceAnalysisPort } from "../application/adb/AdbDeviceAnalysisPort.js";
|
|
import { AdbDeviceAnalysisService } from "../application/adb/AdbDeviceAnalysisService.js";
|
|
import { createAdbDeviceAnalysisProvider } from "../composition/adb.js";
|
|
import { registerManagedWorkflowTools } from "./registerManagedWorkflowTools.js";
|
|
import { registerOfficialTools } from "./registerOfficialTools.js";
|
|
import { registerGuidedPrompts } from "./registerPrompts.js";
|
|
import { registerSessionTools } from "./registerSessionTools.js";
|
|
import type { AvailabilityPolicy } from "../application/CapabilityInventory.js";
|
|
import { sessionAvailabilityPolicy } from "./sessionAvailabilityPolicy.js";
|
|
import {
|
|
withAdmittedAnalysis,
|
|
type ServerAnalysisSource,
|
|
} from "./analysisAdmission.js";
|
|
import type { WithAdmittedAnalysis } from "./analysisAdmission.js";
|
|
import { createReverseEngineeringService } from "../composition/reverseEngineering.js";
|
|
import { registerReverseEngineeringTools } from "./registerReverseEngineeringTools.js";
|
|
import { CutterBridgeClient } from "../cutter/CutterBridgeClient.js";
|
|
import { CutterBridgeService } from "../cutter/CutterBridgeService.js";
|
|
import { registerCutterTools } from "./registerCutterTools.js";
|
|
import { GdbSessionManager } from "../gdb/GdbSessionManager.js";
|
|
import { registerGdbTools } from "./registerGdbTools.js";
|
|
import { RizinDebugSessionManager } from "../rizin/RizinDebugSessionManager.js";
|
|
import { registerRizinDebugTools } from "./registerRizinDebugTools.js";
|
|
|
|
const TARGET_FREE_INSTRUCTIONS =
|
|
"REA provides reverse-engineering tools for local artifacts, native binaries, managed code, browser pages, and runtimes. Use the tool that directly answers the question; discover targets or inspect inventory only when needed. Tool results include inline Evidence and report their coverage and limitations. An opened target persists across turns. When finished, export needed Evidence and call close_binary; its snapshot_path can retain analysis.";
|
|
|
|
const ACTIVE_TARGET_INSTRUCTIONS =
|
|
"REA analyzes the active reverse-engineering target. Use the analysis tool that answers the question directly. Search or list symbols when discovery is needed; analyze_function provides a function dossier, and focused procedure tools return individual facets.";
|
|
|
|
// A host deadline can discard the response before error remediation reaches the
|
|
// agent. Teach that distinction at initialization, not just in returned errors.
|
|
const RECOVERY_INSTRUCTIONS =
|
|
"Recovery: For invalid arguments, read the connected tool's input schema (refresh tools/list if stale), fix the reported fields and do not repeat the unchanged call. If the MCP connection drops, retry that call once only when it is safe to repeat or known not to have started. If it may already have changed the target, or it fails again, tell the user and ask them to restart the MCP client. If REA could not start its connection, retry once, then tell the user and ask them to restart the MCP client. Do not retry a startup failure that returns the same unchanged result. Run `rea doctor` when REA could not start or tools stay missing after that restart. A returned REA provider_timeout is a server/provider deadline: follow its remediation and reuse accessible partial Evidence. Change a request field or REA_* setting only when the returned remediation explicitly recommends that change. Restart REA when that text says the value is read at startup. A mention that a control does not raise the deadline is not a recommendation to change it. A host error such as 'timed out awaiting tools/call' without a REA result is a client deadline, not proof that work or cleanup stopped. Do not repeat the unchanged analysis. Increase the host's tool deadline with execution/cleanup/delivery headroom; when configuration is not editable, report the required setting (Codex: mcp_servers.<server>.tool_timeout_sec, seconds). Server analysis_timeout_ms, when advertised, is a separate millisecond budget; raising it cannot fix a shorter client deadline. Session state does not establish completion of every target-free operation.";
|
|
|
|
export interface CreateServerOptions {
|
|
/** Preserve the connection's creation time when materializing its runtime. */
|
|
readonly startedAt?: string;
|
|
/** Existing SDK server whose deferred registrations are being materialized. */
|
|
readonly mcpServer?: EvidenceMcpServer;
|
|
readonly environment?: Readonly<NodeJS.ProcessEnv>;
|
|
readonly delivery?: ToolResultDelivery;
|
|
readonly packetCapture?: PacketCaptureService;
|
|
readonly signalRecording?: SignalRecordingService;
|
|
readonly wasmArtifact?: WasmArtifactService;
|
|
readonly apkSignature?: ApkSignatureService;
|
|
readonly providerEnvironment?: Readonly<NodeJS.ProcessEnv>;
|
|
readonly evmInterface?: EvmInterfaceService;
|
|
readonly sqliteDatabase?: SqliteDatabaseService;
|
|
readonly logger?: Logger;
|
|
readonly binaryLayout?: BinaryLayoutService;
|
|
readonly goBinary?: GoBinaryService;
|
|
readonly il2cppMetadata?: Il2cppMetadataService;
|
|
readonly rustBinary?: RustBinaryService;
|
|
readonly pythonPyc?: PythonPycService;
|
|
readonly erlangBeam?: BeamService;
|
|
readonly electronFuses?: ElectronFuseService;
|
|
readonly recordedCrash?: RecordedCrashService;
|
|
readonly firmwareAnalysis?: FirmwareAnalysisPort;
|
|
readonly javascriptRecovery?: JavaScriptRecoveryPort;
|
|
readonly webModuleTrace?: WebModuleTraceService;
|
|
readonly webSourceLocation?: WebSourceLocationService;
|
|
readonly webRuntime?: WebRuntimeService;
|
|
readonly webNetworkCapture?: WebNetworkCaptureService;
|
|
readonly androidAnalysis?: AndroidAnalysisPort;
|
|
readonly apktoolAnalysis?: ApktoolResourceAnalysisPort;
|
|
readonly pcapAnalysis?: PcapAnalysisPort;
|
|
readonly flutterAnalysis?: FlutterBuildAnalysisPort;
|
|
readonly jebAnalysis?: JebAnalysisPort;
|
|
readonly adbDeviceAnalysis?: AdbDeviceAnalysisPort;
|
|
readonly browserObservation?: BrowserObservationPort;
|
|
readonly browserScenarioCapture?: BrowserScenarioCapturePort;
|
|
readonly electronObservation?: ElectronObservationPort;
|
|
/** Selects who closes the shared optional observation providers. */
|
|
readonly optionalObservationProviderOwner?: "server" | "caller";
|
|
readonly electronActiveObservation?: ElectronActiveObservationPort;
|
|
readonly javascriptRuntimeObservation?: JavaScriptRuntimeObservationPort;
|
|
readonly fridaInstrumentation?: FridaInstrumentationPort;
|
|
readonly availabilityPolicy?: () => AvailabilityPolicy;
|
|
readonly optionalProviderLoadFailures?: OptionalProviderLoadFailures;
|
|
}
|
|
|
|
/** Apply the shared TShark selection rule without starting the tool. */
|
|
const tsharkLocatable = (environment: Readonly<NodeJS.ProcessEnv>): boolean => {
|
|
try {
|
|
return (
|
|
locateWiresharkTool(
|
|
selectWiresharkTool(environment, "tshark"),
|
|
environment,
|
|
) !== undefined
|
|
);
|
|
} catch {
|
|
// A relative REA_TSHARK_COMMAND is configuration the tool reports at execution.
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const installSessionToolAvailability = (
|
|
session: BinarySessionPort | undefined,
|
|
options: CreateServerOptions,
|
|
environment: Readonly<NodeJS.ProcessEnv>,
|
|
fridaInstrumentation: FridaInstrumentationPort,
|
|
) => {
|
|
if (session === undefined) return undefined;
|
|
const { platform, arch } = process;
|
|
// Each provider owns its verified host; discovery and execution share it.
|
|
const verifiedHost = {
|
|
packetCapture: tsharkHostSupported(platform),
|
|
pcapCapture: pcapCaptureHostSupported(platform),
|
|
signalRecording: sigrokHostSupported(platform),
|
|
evmInterface: evmHostSupported(platform, arch),
|
|
pwntools: pwntoolsHostSupported(platform, arch),
|
|
firmware: firmwareHostSupported(platform),
|
|
javascriptRecovery: wakaruHostSupported(platform, arch),
|
|
};
|
|
const pwntools =
|
|
verifiedHost.pwntools && isAbsolute(environment.REA_PWNTOOLS_PYTHON ?? "");
|
|
// Injected services own their availability on any host. Otherwise no
|
|
// configuration lets an integration pass its execution host check.
|
|
const outsideVerifiedHost = (injected: unknown, supported: boolean) =>
|
|
injected === undefined && !supported;
|
|
const basePolicy = sessionAvailabilityPolicy(options.availabilityPolicy, {
|
|
optionalProviderLoadFailures: options.optionalProviderLoadFailures,
|
|
hostUnsupported: {
|
|
pcapCapture: outsideVerifiedHost(
|
|
options.pcapAnalysis,
|
|
verifiedHost.pcapCapture,
|
|
),
|
|
packetCapture: outsideVerifiedHost(
|
|
options.packetCapture,
|
|
verifiedHost.packetCapture,
|
|
),
|
|
signalRecording: outsideVerifiedHost(
|
|
options.signalRecording,
|
|
verifiedHost.signalRecording,
|
|
),
|
|
evmInterface: outsideVerifiedHost(
|
|
options.evmInterface,
|
|
verifiedHost.evmInterface,
|
|
),
|
|
binaryLayout: outsideVerifiedHost(
|
|
options.binaryLayout,
|
|
verifiedHost.pwntools,
|
|
),
|
|
recordedCrash: outsideVerifiedHost(
|
|
options.recordedCrash,
|
|
verifiedHost.pwntools,
|
|
),
|
|
firmwareInspection: outsideVerifiedHost(
|
|
options.firmwareAnalysis,
|
|
verifiedHost.firmware,
|
|
),
|
|
firmwareExtraction: outsideVerifiedHost(
|
|
options.firmwareAnalysis,
|
|
verifiedHost.firmware,
|
|
),
|
|
javascriptRecovery: outsideVerifiedHost(
|
|
options.javascriptRecovery,
|
|
verifiedHost.javascriptRecovery,
|
|
),
|
|
},
|
|
optionalFeatures: {
|
|
packetCaptureEnabled:
|
|
options.packetCapture !== undefined ||
|
|
(verifiedHost.packetCapture && tsharkLocatable(environment)),
|
|
signalRecordingEnabled:
|
|
options.signalRecording !== undefined ||
|
|
(verifiedHost.signalRecording &&
|
|
isAbsolute(environment.REA_SIGROK_COMMAND ?? "")),
|
|
wasmArtifactEnabled:
|
|
options.wasmArtifact !== undefined ||
|
|
isAbsolute(environment.REA_WABT_BIN_DIRECTORY ?? ""),
|
|
evmInterfaceEnabled:
|
|
options.evmInterface !== undefined || verifiedHost.evmInterface,
|
|
webModuleResolutionEnabled:
|
|
options.webModuleTrace !== undefined ||
|
|
isAbsolute(environment.REA_BROWSER_EXECUTABLE ?? ""),
|
|
recordedCrashEnabled: options.recordedCrash !== undefined || pwntools,
|
|
binaryLayoutEnabled: options.binaryLayout !== undefined || pwntools,
|
|
firmwareInspectionEnabled:
|
|
options.firmwareAnalysis !== undefined ||
|
|
(verifiedHost.firmware &&
|
|
isAbsolute(environment.REA_BINWALK_COMMAND ?? "")),
|
|
firmwareExtractionEnabled:
|
|
options.firmwareAnalysis !== undefined ||
|
|
(verifiedHost.firmware &&
|
|
isAbsolute(environment.REA_UNBLOB_COMMAND ?? "")),
|
|
browserObservationEnabled: options.browserObservation !== undefined,
|
|
javascriptRecoveryEnabled:
|
|
options.javascriptRecovery !== undefined ||
|
|
(verifiedHost.javascriptRecovery &&
|
|
isAbsolute(environment.REA_WAKARU_COMMAND ?? "")),
|
|
browserScenarioEnabled: options.browserScenarioCapture !== undefined,
|
|
electronObservationEnabled: options.electronObservation !== undefined,
|
|
electronAutomationEnabled:
|
|
options.electronActiveObservation !== undefined,
|
|
v8InspectorObservationEnabled:
|
|
options.javascriptRuntimeObservation !== undefined,
|
|
},
|
|
});
|
|
const policy = (): AvailabilityPolicy => {
|
|
const value = basePolicy();
|
|
const fridaFailure =
|
|
options.optionalProviderLoadFailures?.fridaInstrumentation ??
|
|
fridaInstrumentation.availabilityFailure?.();
|
|
return fridaFailure === undefined
|
|
? value
|
|
: {
|
|
...value,
|
|
optionalProviderLoadFailures: {
|
|
...value.optionalProviderLoadFailures,
|
|
fridaInstrumentation: fridaFailure,
|
|
},
|
|
};
|
|
};
|
|
return {
|
|
policy,
|
|
...(options.evmInterface !== undefined || !verifiedHost.evmInterface
|
|
? {}
|
|
: {
|
|
evmLimiterAvailability: (signal: AbortSignal) =>
|
|
evmLimiterAvailability(environment, { signal }),
|
|
}),
|
|
};
|
|
};
|
|
|
|
const createMcpServer = (
|
|
session: BinarySessionPort | undefined,
|
|
delivery: ToolResultDelivery,
|
|
compactPresentation: CompactInputSchemaPresentation | undefined,
|
|
omitOutputSchemas: boolean,
|
|
): EvidenceMcpServer =>
|
|
new EvidenceMcpServer(
|
|
{
|
|
name: PRODUCT_IDENTITY.mcpServerKey,
|
|
version: PRODUCT_IDENTITY.packageVersion,
|
|
},
|
|
{
|
|
capabilities: {},
|
|
instructions: `${
|
|
session === undefined
|
|
? ACTIVE_TARGET_INSTRUCTIONS
|
|
: TARGET_FREE_INSTRUCTIONS
|
|
} ${RECOVERY_INSTRUCTIONS} ${UNTRUSTED_CONTENT_INSTRUCTIONS}`,
|
|
},
|
|
session === undefined
|
|
? undefined
|
|
: (evidence) => session.recordEvidence(evidence),
|
|
delivery,
|
|
compactPresentation,
|
|
omitOutputSchemas,
|
|
createCatalogIdentity(
|
|
compactPresentation,
|
|
omitOutputSchemas ? "off" : "full",
|
|
),
|
|
);
|
|
|
|
/**
|
|
* Construct one MCP server without acquiring subprocess resources.
|
|
* Supplying a session adds target lifecycle tools; omitting it retains the
|
|
* fixed-target seam used by focused tests and embedders.
|
|
*/
|
|
export const createServer = (
|
|
source: ServerAnalysisSource,
|
|
options: CreateServerOptions = {},
|
|
): EvidenceMcpServer => {
|
|
const session = source.kind === "session" ? source.session : undefined;
|
|
const analysis = source.kind === "session" ? source.session : source.analysis;
|
|
const environment = snapshotEnvironment(options.environment ?? process.env);
|
|
const omitOutputSchemas = outputSchemasOmitted(environment);
|
|
const providerEnvironment = captureProviderEnvironment(
|
|
options.providerEnvironment,
|
|
environment,
|
|
);
|
|
const delivery = selectToolResultDelivery(environment, options.delivery);
|
|
const evmInterface =
|
|
options.evmInterface ?? evmInterfaceModule.create(environment);
|
|
const sqliteDatabase =
|
|
options.sqliteDatabase ?? createSqliteDatabaseService(environment);
|
|
const binaryLayout =
|
|
options.binaryLayout ?? createBinaryLayoutService(environment);
|
|
const recordedCrash =
|
|
options.recordedCrash ?? createRecordedCrashService(environment);
|
|
const webSourceLocation =
|
|
options.webSourceLocation ?? createWebSourceLocationService(environment);
|
|
const webModuleTrace =
|
|
options.webModuleTrace ?? createWebModuleTraceService(environment);
|
|
const desktopApplication = createDesktopApplicationService();
|
|
const goBinary = options.goBinary ?? createGoBinaryService();
|
|
const rustBinary = options.rustBinary ?? createRustBinaryService();
|
|
const erlangBeam = options.erlangBeam ?? createBeamService();
|
|
const modelArtifact = createModelArtifactService();
|
|
const gpuCodeObjects = createGpuCodeObjectsService();
|
|
const il2cppMetadata =
|
|
options.il2cppMetadata ?? createIl2cppMetadataService();
|
|
const peResources = createPeResourcesService();
|
|
const nativeExports = createNativeExportsService();
|
|
const elfNotes = createElfNotesService();
|
|
const peCertificates = createPeCertificatesService();
|
|
const peImports = createPeImportsService();
|
|
const electronFuses = options.electronFuses ?? createElectronFuseService();
|
|
const pythonPyc = options.pythonPyc ?? createPythonPycService();
|
|
const webNetworkCapture =
|
|
options.webNetworkCapture ?? createWebNetworkCaptureService(environment);
|
|
const javascriptRecovery =
|
|
options.javascriptRecovery ?? createJavaScriptRecoveryProvider(environment);
|
|
const artifactResources = new ArtifactResourceScope();
|
|
const reverseEngineering =
|
|
createReverseEngineeringService(providerEnvironment);
|
|
const firmwareAnalysis = new FirmwareAnalysisService(
|
|
options.firmwareAnalysis ?? createFirmwareAnalysisProvider(environment),
|
|
);
|
|
const selectedOptions = {
|
|
...options,
|
|
environment,
|
|
delivery,
|
|
evmInterface,
|
|
sqliteDatabase,
|
|
binaryLayout,
|
|
recordedCrash,
|
|
webSourceLocation,
|
|
webModuleTrace,
|
|
javascriptRecovery,
|
|
};
|
|
const startedAt = options.startedAt ?? new Date().toISOString();
|
|
const logger = options.logger ?? silentLogger;
|
|
const server =
|
|
options.mcpServer ??
|
|
createMcpServer(
|
|
session,
|
|
delivery,
|
|
selectCompactInputSchemaPresentation(environment),
|
|
omitOutputSchemas,
|
|
);
|
|
if (options.mcpServer !== undefined) {
|
|
server.attachRuntime(
|
|
session === undefined
|
|
? undefined
|
|
: (evidence) => session.recordEvidence(evidence),
|
|
delivery,
|
|
createCatalogIdentity(
|
|
selectCompactInputSchemaPresentation(environment),
|
|
omitOutputSchemas ? "off" : "full",
|
|
),
|
|
);
|
|
}
|
|
const gdbSessions = new GdbSessionManager({
|
|
environment: providerEnvironment,
|
|
});
|
|
const rizinDebugSessions = new RizinDebugSessionManager(providerEnvironment);
|
|
const android =
|
|
options.androidAnalysis ?? createAndroidAnalysisProvider(environment);
|
|
const pcap = options.pcapAnalysis ?? createPcapAnalysisProvider(environment);
|
|
const flutter =
|
|
options.flutterAnalysis ?? createFlutterBuildAnalysisProvider();
|
|
const fridaInstrumentation =
|
|
options.fridaInstrumentation ?? new FridaInstrumentationManager();
|
|
const wasmArtifact = wasmArtifactModule.create(
|
|
environment,
|
|
options.wasmArtifact,
|
|
);
|
|
const apkSignature = apkSignatureModule.create(
|
|
environment,
|
|
options.apkSignature,
|
|
);
|
|
const packetCapture =
|
|
options.packetCapture ?? createPacketCaptureService(environment);
|
|
const signalRecording =
|
|
options.signalRecording ?? createSignalRecordingService(environment);
|
|
const webRuntime = options.webRuntime ?? createWebRuntimeService();
|
|
const apktool =
|
|
options.apktoolAnalysis ??
|
|
createApktoolResourceAnalysisProvider(environment);
|
|
const jeb = options.jebAnalysis ?? createJebAnalysisProvider(environment);
|
|
const adbDevice =
|
|
options.adbDeviceAnalysis ?? createAdbDeviceAnalysisProvider(environment);
|
|
const cutter = new CutterBridgeService(
|
|
new CutterBridgeClient(providerEnvironment),
|
|
);
|
|
let providerClose: Promise<void> | undefined;
|
|
const closeProviders = (): Promise<void> => {
|
|
providerClose ??= Promise.allSettled([
|
|
android.close(),
|
|
cutter.close(),
|
|
fridaInstrumentation.closeAll(),
|
|
wasmArtifact.close(),
|
|
apkSignature.close(),
|
|
packetCapture.close(),
|
|
signalRecording.close(),
|
|
apktool.close(),
|
|
jeb.close(),
|
|
adbDevice.close(),
|
|
flutter.close(),
|
|
pcap.close(),
|
|
evmInterface.close(),
|
|
sqliteDatabase.close(),
|
|
binaryLayout.close(),
|
|
recordedCrash.close(),
|
|
webSourceLocation.close(),
|
|
webModuleTrace.close(),
|
|
desktopApplication.close(),
|
|
goBinary.close(),
|
|
rustBinary.close(),
|
|
erlangBeam.close(),
|
|
il2cppMetadata.close(),
|
|
modelArtifact.close(),
|
|
gpuCodeObjects.close(),
|
|
peResources.close(),
|
|
nativeExports.close(),
|
|
elfNotes.close(),
|
|
peCertificates.close(),
|
|
peImports.close(),
|
|
electronFuses.close(),
|
|
pythonPyc.close(),
|
|
webNetworkCapture.close(),
|
|
javascriptRecovery.close?.() ?? Promise.resolve(),
|
|
...(options.optionalObservationProviderOwner === "caller"
|
|
? []
|
|
: [
|
|
options.browserObservation?.close?.() ?? Promise.resolve(),
|
|
options.browserScenarioCapture?.close?.() ?? Promise.resolve(),
|
|
options.electronObservation?.close?.() ?? Promise.resolve(),
|
|
options.electronActiveObservation?.close?.() ?? Promise.resolve(),
|
|
options.javascriptRuntimeObservation?.close?.() ??
|
|
Promise.resolve(),
|
|
]),
|
|
webRuntime.provider.close?.() ?? Promise.resolve(),
|
|
artifactResources.close(),
|
|
reverseEngineering.close(),
|
|
firmwareAnalysis.close(),
|
|
gdbSessions.closeAll(),
|
|
rizinDebugSessions.closeAll(),
|
|
])
|
|
.then((results) => {
|
|
const failures = results.flatMap((result) =>
|
|
result.status === "rejected" ? [result.reason] : [],
|
|
);
|
|
if (failures.length > 0)
|
|
throw new AggregateError(failures, "REA provider cleanup failed");
|
|
})
|
|
.catch((cause: unknown) => {
|
|
providerClose = undefined;
|
|
throw cause;
|
|
});
|
|
return providerClose;
|
|
};
|
|
const availability = installSessionToolAvailability(
|
|
session,
|
|
options,
|
|
environment,
|
|
fridaInstrumentation,
|
|
);
|
|
const toolLogger = logger.child({ layer: "server" });
|
|
const {
|
|
evidenceById,
|
|
activeTarget,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
} = createSessionRecorders(server, session);
|
|
const analysisAdmission = withAdmittedAnalysis(source);
|
|
const toolContext: ServerToolContext = {
|
|
server,
|
|
session,
|
|
options: selectedOptions,
|
|
environment,
|
|
logger: toolLogger,
|
|
evidenceById,
|
|
activeTarget,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
withAdmittedAnalysis: analysisAdmission,
|
|
artifactResources,
|
|
goBinary,
|
|
rustBinary,
|
|
erlangBeam,
|
|
il2cppMetadata,
|
|
peResources,
|
|
nativeExports,
|
|
elfNotes,
|
|
peCertificates,
|
|
peImports,
|
|
electronFuses,
|
|
pythonPyc,
|
|
webNetworkCapture,
|
|
};
|
|
registerDesktopApplicationTool(
|
|
server,
|
|
desktopApplication,
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
evidenceById,
|
|
);
|
|
registerModelArtifactTools(
|
|
server,
|
|
modelArtifact,
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
evidenceById,
|
|
);
|
|
registerGpuCodeObjectsTools(
|
|
server,
|
|
gpuCodeObjects,
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
evidenceById,
|
|
);
|
|
registerBinaryAnalysisTools(toolContext);
|
|
registerReverseEngineeringTools(
|
|
server,
|
|
reverseEngineering,
|
|
toolLogger,
|
|
delivery,
|
|
session === undefined
|
|
? undefined
|
|
: (evidence) => session.recordEvidence(evidence),
|
|
analysisAdmission,
|
|
session === undefined
|
|
? undefined
|
|
: () => session.invalidateSnapshotForExternalMutation(),
|
|
session === undefined
|
|
? undefined
|
|
: (path, signal, operation, rebindFailure) =>
|
|
session.withAdmittedExternalMutation(
|
|
path,
|
|
signal,
|
|
operation,
|
|
rebindFailure,
|
|
),
|
|
evidenceById,
|
|
);
|
|
registerFridaTools(
|
|
server,
|
|
fridaInstrumentation,
|
|
toolLogger,
|
|
delivery,
|
|
session === undefined
|
|
? undefined
|
|
: (evidence) => session.recordEvidence(evidence),
|
|
evidenceById,
|
|
);
|
|
registerCutterTools(
|
|
server,
|
|
cutter,
|
|
toolLogger,
|
|
delivery,
|
|
session === undefined
|
|
? undefined
|
|
: (evidence) => session.recordEvidence(evidence),
|
|
session,
|
|
);
|
|
registerGdbTools(
|
|
server,
|
|
gdbSessions,
|
|
toolLogger,
|
|
delivery,
|
|
session === undefined
|
|
? undefined
|
|
: (evidence) => session.recordEvidence(evidence),
|
|
session,
|
|
evidenceById,
|
|
);
|
|
registerRizinDebugTools(
|
|
server,
|
|
rizinDebugSessions,
|
|
toolLogger,
|
|
delivery,
|
|
session === undefined
|
|
? undefined
|
|
: (evidence) => session.recordEvidence(evidence),
|
|
session,
|
|
);
|
|
const previousOnclose = server.server.onclose;
|
|
server.server.onclose = () => {
|
|
previousOnclose?.();
|
|
void closeProviders().catch((cause: unknown) => {
|
|
logger.error(
|
|
{ error: cause instanceof Error ? cause.message : String(cause) },
|
|
"Provider cleanup failed during server shutdown",
|
|
);
|
|
});
|
|
};
|
|
const closeServer = server.close.bind(server);
|
|
server.close = async () => {
|
|
const results = await Promise.allSettled([closeServer(), closeProviders()]);
|
|
for (const result of results)
|
|
if (result.status === "rejected") throw result.reason;
|
|
};
|
|
registerCaptureTools(
|
|
server,
|
|
packetCapture,
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
);
|
|
registerSignalTools(
|
|
server,
|
|
signalRecording,
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
);
|
|
registerConfiguredAnalysisTools(
|
|
toolContext,
|
|
android,
|
|
wasmArtifact,
|
|
apkSignature,
|
|
evmInterface,
|
|
jeb,
|
|
firmwareAnalysis,
|
|
webRuntime,
|
|
);
|
|
registerAdbTools(
|
|
server,
|
|
new AdbDeviceAnalysisService(adbDevice),
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
);
|
|
registerPcapTools(
|
|
server,
|
|
new PcapAnalysisService(pcap),
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
evidenceById,
|
|
);
|
|
registerFlutterTools(
|
|
server,
|
|
new FlutterBuildAnalysisService(flutter),
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
evidenceById,
|
|
);
|
|
registerApktoolTools(
|
|
server,
|
|
new ApktoolResourceAnalysisService(apktool),
|
|
toolLogger,
|
|
recordEvidence,
|
|
analysisAdmission,
|
|
);
|
|
registerObservationTools(toolContext);
|
|
registerGuidedPrompts(server, analysis, session);
|
|
if (session !== undefined) {
|
|
registerSessionTools(server, session, toolLogger, {
|
|
...selectedOptions,
|
|
artifactResources,
|
|
...(availability === undefined
|
|
? {}
|
|
: { availabilityPolicy: availability.policy }),
|
|
...(availability?.evmLimiterAvailability === undefined
|
|
? {}
|
|
: { evmLimiterAvailability: availability.evmLimiterAvailability }),
|
|
androidAnalysisAvailability: (signal) =>
|
|
android.inspectAvailability(signal),
|
|
adbAnalysisAvailability: (signal) =>
|
|
adbDevice.inspectAvailability(signal),
|
|
apktoolAnalysisAvailability: (signal) =>
|
|
apktool.inspectAvailability(signal),
|
|
jebAnalysisAvailability: (signal) => jeb.inspectAvailability(signal),
|
|
startedAt,
|
|
});
|
|
}
|
|
return server;
|
|
};
|
|
|
|
const selectToolResultDelivery = (
|
|
environment: Readonly<NodeJS.ProcessEnv>,
|
|
selected: ToolResultDelivery | undefined,
|
|
): ToolResultDelivery => {
|
|
if (selected !== undefined) return selected;
|
|
const configured = parseMcpResponseBudget(
|
|
environment.REA_MCP_MAX_RESPONSE_BYTES,
|
|
);
|
|
if (!configured.ok) throw configured.error;
|
|
return createToolResultDelivery(configured.value);
|
|
};
|
|
|
|
const outputSchemasOmitted = (
|
|
environment: Readonly<NodeJS.ProcessEnv>,
|
|
): boolean => {
|
|
const configured = parseMcpOutputSchemaProfile(
|
|
environment.REA_MCP_OUTPUT_SCHEMA_PROFILE,
|
|
);
|
|
if (!configured.ok) throw configured.error;
|
|
return configured.value === "off";
|
|
};
|
|
|
|
const selectCompactInputSchemaPresentation = (
|
|
environment: Readonly<NodeJS.ProcessEnv>,
|
|
): CompactInputSchemaPresentation | undefined => {
|
|
const configured = parseMcpInputSchemaProfile(
|
|
environment.REA_MCP_INPUT_SCHEMA_PROFILE,
|
|
);
|
|
if (!configured.ok) throw configured.error;
|
|
return compactPresentationForMcpInputSchemaProfile(configured.value);
|
|
};
|
|
|
|
const registerConfiguredAnalysisTools = (
|
|
{
|
|
server,
|
|
options,
|
|
environment,
|
|
logger: toolLogger,
|
|
evidenceById,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
goBinary,
|
|
rustBinary,
|
|
erlangBeam,
|
|
il2cppMetadata,
|
|
peResources,
|
|
nativeExports,
|
|
elfNotes,
|
|
peCertificates,
|
|
peImports,
|
|
pythonPyc,
|
|
webNetworkCapture,
|
|
artifactResources,
|
|
}: ServerToolContext,
|
|
android: AndroidAnalysisPort,
|
|
wasmArtifact: Pick<WasmArtifactService, "inspect" | "close">,
|
|
apkSignature: Pick<ApkSignatureService, "inspect" | "close">,
|
|
evmInterface: EvmInterfaceService,
|
|
jeb: JebAnalysisPort,
|
|
firmwareAnalysis: FirmwareAnalysisService,
|
|
webRuntime: WebRuntimeService,
|
|
): void => {
|
|
registerAndroidTools(
|
|
server,
|
|
new AndroidAnalysisService(android, recordEvidence),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
evidenceById,
|
|
);
|
|
registerJebTools(
|
|
server,
|
|
new JebAnalysisService(jeb),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerBinaryDiagnosticsTools(
|
|
server,
|
|
options.binaryLayout ?? createBinaryLayoutService(environment),
|
|
createByteEntropyService(artifactResources),
|
|
toolLogger,
|
|
recordEvidence,
|
|
evidenceById,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerAnalysisViewTool(
|
|
server,
|
|
toolLogger,
|
|
evidenceById,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
artifactResources,
|
|
);
|
|
registerPeResourcesTool(
|
|
server,
|
|
peResources,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerNativeExportsTool(
|
|
server,
|
|
nativeExports,
|
|
toolLogger,
|
|
recordEvidence,
|
|
evidenceById,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerElfNotesTool(
|
|
server,
|
|
elfNotes,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerPeCertificatesTool(
|
|
server,
|
|
peCertificates,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerPeImportsTool(
|
|
server,
|
|
peImports,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerGoTools(
|
|
server,
|
|
goBinary,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
evidenceById,
|
|
);
|
|
registerIl2cppTools(
|
|
server,
|
|
il2cppMetadata,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
evidenceById,
|
|
);
|
|
registerRustTools(
|
|
server,
|
|
rustBinary,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
evidenceById,
|
|
);
|
|
registerPythonPycTool(
|
|
server,
|
|
pythonPyc,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
evidenceById,
|
|
);
|
|
registerErlangTools(
|
|
server,
|
|
erlangBeam,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerWasmTools(
|
|
server,
|
|
wasmArtifact,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
evidenceById,
|
|
);
|
|
registerApkSignatureTools(
|
|
server,
|
|
apkSignature,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
evidenceById,
|
|
);
|
|
registerEvmTools(
|
|
server,
|
|
evmInterface,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
evidenceById,
|
|
);
|
|
registerSqliteTools(
|
|
server,
|
|
options.sqliteDatabase ?? createSqliteDatabaseService(environment),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerRecordedCrashTools(
|
|
server,
|
|
options.recordedCrash ?? createRecordedCrashService(environment),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerFirmwareTools(
|
|
server,
|
|
firmwareAnalysis,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerWebModuleTool(
|
|
server,
|
|
options.webModuleTrace ?? createWebModuleTraceService(environment),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerWebSourceLocationTool(
|
|
server,
|
|
options.webSourceLocation ?? createWebSourceLocationService(environment),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerWebRuntimeTools(
|
|
server,
|
|
webRuntime,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
registerWebNetworkCaptureTool(
|
|
server,
|
|
webNetworkCapture,
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
evidenceById,
|
|
);
|
|
registerJavaScriptRecoveryTool(
|
|
server,
|
|
new JavaScriptRecoveryService(
|
|
options.javascriptRecovery ??
|
|
createJavaScriptRecoveryProvider(environment),
|
|
),
|
|
toolLogger,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
);
|
|
};
|
|
|
|
const createSessionRecorders = (
|
|
server: EvidenceMcpServer,
|
|
session: BinarySessionPort | undefined,
|
|
) => ({
|
|
evidenceById:
|
|
session === undefined ? undefined : investigationEvidenceLookup(session),
|
|
activeTarget:
|
|
session === undefined ? undefined : () => session.activeTarget(),
|
|
recordEvidence:
|
|
session === undefined
|
|
? undefined
|
|
: (evidence: Parameters<typeof session.recordEvidence>[0]) =>
|
|
session.recordEvidence(evidence),
|
|
recordEvidenceWithUnknown:
|
|
session === undefined
|
|
? undefined
|
|
: (
|
|
evidence: Parameters<typeof session.recordEvidenceWithUnknown>[0],
|
|
input: Parameters<typeof session.recordEvidenceWithUnknown>[1],
|
|
) => session.recordEvidenceWithUnknown(evidence, input),
|
|
});
|
|
|
|
interface ServerToolContext extends ReturnType<typeof createSessionRecorders> {
|
|
readonly goBinary: GoBinaryService;
|
|
readonly rustBinary: RustBinaryService;
|
|
readonly erlangBeam: BeamService;
|
|
readonly il2cppMetadata: Il2cppMetadataService;
|
|
readonly peResources: PeResourcesService;
|
|
readonly nativeExports: NativeExportsService;
|
|
readonly elfNotes: ElfNotesService;
|
|
readonly peCertificates: PeCertificatesService;
|
|
readonly peImports: PeImportsService;
|
|
readonly electronFuses: ElectronFuseService;
|
|
readonly pythonPyc: PythonPycService;
|
|
readonly webNetworkCapture: WebNetworkCaptureService;
|
|
readonly artifactResources: ArtifactResourceScope;
|
|
readonly server: EvidenceMcpServer;
|
|
readonly session: BinarySessionPort | undefined;
|
|
readonly options: CreateServerOptions;
|
|
readonly environment: Readonly<NodeJS.ProcessEnv>;
|
|
readonly logger: Logger;
|
|
readonly withAdmittedAnalysis: WithAdmittedAnalysis;
|
|
}
|
|
|
|
const registerBinaryAnalysisTools = ({
|
|
server,
|
|
session,
|
|
logger,
|
|
activeTarget,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
evidenceById,
|
|
withAdmittedAnalysis,
|
|
}: ServerToolContext): void => {
|
|
const recordUnknown =
|
|
session === undefined
|
|
? undefined
|
|
: (input: Parameters<typeof session.recordUnknown>[0]) =>
|
|
session.recordUnknown(input);
|
|
const analysisOptions = {
|
|
logger,
|
|
activeTarget,
|
|
recordEvidence,
|
|
recordUnknown,
|
|
evidenceById,
|
|
withAdmittedAnalysis,
|
|
};
|
|
registerOfficialTools(server, analysisOptions);
|
|
registerEnhancedTools(server, {
|
|
...analysisOptions,
|
|
analysisProfile:
|
|
session === undefined
|
|
? undefined
|
|
: (operation) =>
|
|
session.analysisProfile() ?? session.analysisProfile(operation),
|
|
allowsSnapshotReplay:
|
|
session === undefined
|
|
? undefined
|
|
: (operation) => session.allowsSnapshotReplay(operation),
|
|
recordWorkflowSnapshot:
|
|
session === undefined
|
|
? undefined
|
|
: (input) => session.recordWorkflowSnapshot(input),
|
|
});
|
|
const evidenceOptions = {
|
|
logger,
|
|
activeTarget,
|
|
recordEvidence,
|
|
withAdmittedAnalysis,
|
|
};
|
|
registerNativeTools(server, { ...evidenceOptions, evidenceById });
|
|
registerArtifactTools(server, { ...evidenceOptions, evidenceById });
|
|
registerManagedTools(server, { ...evidenceOptions, evidenceById });
|
|
if (session !== undefined)
|
|
registerManagedWorkflowTools(server, {
|
|
logger,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
session,
|
|
withAdmittedAnalysis,
|
|
});
|
|
};
|
|
|
|
const registerObservationTools = ({
|
|
electronFuses,
|
|
evidenceById,
|
|
server,
|
|
options,
|
|
logger,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
withAdmittedAnalysis,
|
|
artifactResources,
|
|
environment,
|
|
}: ServerToolContext): void => {
|
|
const common = { logger, recordEvidence, withAdmittedAnalysis };
|
|
registerWebScriptTool(server, { ...common, artifactResources });
|
|
registerBrowserTools(server, {
|
|
...common,
|
|
evidenceById,
|
|
browser: options.browserObservation,
|
|
loadFailure: options.optionalProviderLoadFailures?.browserObservation,
|
|
});
|
|
registerBrowserScenarioTool(server, {
|
|
...common,
|
|
provider: options.browserScenarioCapture,
|
|
loadFailure: options.optionalProviderLoadFailures?.browserScenarioCapture,
|
|
});
|
|
registerElectronTools(server, {
|
|
...common,
|
|
artifactResources,
|
|
environment,
|
|
evidenceById,
|
|
electron: options.electronObservation,
|
|
electronActive: options.electronActiveObservation,
|
|
electronFuses,
|
|
observationLoadFailure:
|
|
options.optionalProviderLoadFailures?.electronObservation,
|
|
activeLoadFailure:
|
|
options.optionalProviderLoadFailures?.electronActiveObservation,
|
|
});
|
|
registerJavaScriptRuntimeObservationTools(server, {
|
|
...common,
|
|
runtime: options.javascriptRuntimeObservation,
|
|
loadFailure:
|
|
options.optionalProviderLoadFailures?.javascriptRuntimeObservation,
|
|
});
|
|
registerApplicationTools(server, {
|
|
artifactResources,
|
|
evidenceById,
|
|
logger,
|
|
recordEvidence,
|
|
recordEvidenceWithUnknown,
|
|
withAdmittedAnalysis,
|
|
});
|
|
};
|