@jshookmcp/jshook 0.2.8 → 0.2.9
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 +36 -5
- package/README.zh.md +36 -5
- package/dist/{AntiCheatDetector-S8VRj-dD.mjs → AntiCheatDetector-BNk-EoBt.mjs} +3 -3
- package/dist/{CodeInjector-4Z3ngPoX.mjs → CodeInjector-Cq8q01kp.mjs} +5 -5
- package/dist/ConsoleMonitor-CPVQW1Y-.mjs +2201 -0
- package/dist/{DarwinAPI-B8hg_yhz.mjs → DarwinAPI-BNPxu0RH.mjs} +1 -1
- package/dist/DetailedDataManager-BQQcxh64.mjs +217 -0
- package/dist/EventBus-DgPmwpeu.mjs +141 -0
- package/dist/EvidenceGraphBridge-SFesNera.mjs +153 -0
- package/dist/{ExtensionManager-D5-bO9D8.mjs → ExtensionManager-CWYgw0YW.mjs} +13 -6
- package/dist/{FingerprintManager-BVxFJL2-.mjs → FingerprintManager-gzWtkKuf.mjs} +1 -1
- package/dist/{HardwareBreakpoint-DK1yjWkV.mjs → HardwareBreakpoint-B9gZCdFP.mjs} +3 -3
- package/dist/{HeapAnalyzer-CEbo10xU.mjs → HeapAnalyzer-BLDH0dCv.mjs} +4 -4
- package/dist/HookGeneratorBuilders.core.generators.storage-CtcdK78Q.mjs +639 -0
- package/dist/InstrumentationSession-CvPC7Jwy.mjs +244 -0
- package/dist/{MemoryController-DdtnBdD4.mjs → MemoryController-CbVdCIJF.mjs} +3 -3
- package/dist/{MemoryScanSession-RMixN3bX.mjs → MemoryScanSession-BsDZbLYm.mjs} +81 -78
- package/dist/{MemoryScanner-QjK4ld0B.mjs → MemoryScanner-Bcpml6II.mjs} +44 -18
- package/dist/{NativeMemoryManager.impl-CB6gJ0NM.mjs → NativeMemoryManager.impl-dZtA1ZGn.mjs} +14 -53
- package/dist/{NativeMemoryManager.utils-BML4q1ry.mjs → NativeMemoryManager.utils-B-FjA2mJ.mjs} +1 -1
- package/dist/{PEAnalyzer-CK0xe0Fs.mjs → PEAnalyzer-D1lzJ_VG.mjs} +2 -2
- package/dist/PageController-Bqm2kZ_X.mjs +417 -0
- package/dist/{PointerChainEngine-Cd73qu5b.mjs → PointerChainEngine-BOhyVsjx.mjs} +4 -4
- package/dist/PrerequisiteError-Dl33Svkz.mjs +20 -0
- package/dist/ResponseBuilder-D3iFYx2N.mjs +143 -0
- package/dist/ReverseEvidenceGraph-Dlsk94LC.mjs +269 -0
- package/dist/ScriptManager-aHHq0X7U.mjs +3000 -0
- package/dist/{Speedhack-CeF0XmEz.mjs → Speedhack-CqdIFlQl.mjs} +2 -2
- package/dist/{StructureAnalyzer-D4GkMduU.mjs → StructureAnalyzer-DhFaPvRO.mjs} +3 -3
- package/dist/ToolCatalog-C0JGZoOm.mjs +582 -0
- package/dist/ToolError-jh9whhMd.mjs +15 -0
- package/dist/ToolProbe-oC7aPrkv.mjs +45 -0
- package/dist/ToolRegistry-BjaF4oNz.mjs +131 -0
- package/dist/ToolRouter.policy-BWV67ZK-.mjs +304 -0
- package/dist/TraceRecorder-DgxyVbdQ.mjs +519 -0
- package/dist/{Win32API-Bc0QnQsN.mjs → Win32API-CePkipZY.mjs} +1 -1
- package/dist/{Win32Debug-DUHt9XUn.mjs → Win32Debug-BvKs-gxc.mjs} +2 -2
- package/dist/WorkflowEngine-CuvkZtWu.mjs +598 -0
- package/dist/analysis-CL9uACt9.mjs +463 -0
- package/dist/antidebug-CqDTB_uk.mjs +1081 -0
- package/dist/artifactRetention-CFEprwPw.mjs +591 -0
- package/dist/artifacts-Bk2-_uPq.mjs +59 -0
- package/dist/betterSqlite3-0pqusHHH.mjs +74 -0
- package/dist/binary-instrument-CXfpx6fT.mjs +979 -0
- package/dist/bind-helpers-xFfRF-qm.mjs +22 -0
- package/dist/boringssl-inspector-BH2D3VKc.mjs +180 -0
- package/dist/browser-BpOr5PEx.mjs +4082 -0
- package/dist/concurrency-Bt0yv1kJ.mjs +41 -0
- package/dist/{constants-CCvsN80K.mjs → constants-B0OANIBL.mjs} +88 -46
- package/dist/coordination-qUbyF8KU.mjs +259 -0
- package/dist/debugger-gnKxRSN0.mjs +1271 -0
- package/dist/definitions-6M-eejaT.mjs +53 -0
- package/dist/definitions-B18eyf0B.mjs +18 -0
- package/dist/definitions-B3QdlrHv.mjs +34 -0
- package/dist/definitions-B4rAvHNZ.mjs +63 -0
- package/dist/definitions-BB_4jnmy.mjs +37 -0
- package/dist/definitions-BMfYXoNC.mjs +43 -0
- package/dist/definitions-Beid2EB3.mjs +27 -0
- package/dist/definitions-C1UvM5Iy.mjs +126 -0
- package/dist/definitions-CXEI7QC72.mjs +216 -0
- package/dist/definitions-C_4r7Fo-2.mjs +14 -0
- package/dist/definitions-CkFDALoa.mjs +26 -0
- package/dist/definitions-Cke7zEb8.mjs +94 -0
- package/dist/definitions-ClJLzsJQ.mjs +25 -0
- package/dist/definitions-Cq-zroAU.mjs +28 -0
- package/dist/definitions-Cy3Sl6gV.mjs +34 -0
- package/dist/definitions-D3VsGcvz.mjs +47 -0
- package/dist/definitions-DVGfrn7y.mjs +96 -0
- package/dist/definitions-LKpC3-nL.mjs +9 -0
- package/dist/definitions-bAhHQJq9.mjs +359 -0
- package/dist/encoding-Bvz5jLRv.mjs +1065 -0
- package/dist/evidence-graph-bridge-C_fv9PuC.mjs +135 -0
- package/dist/{factory-CibqTNC8.mjs → factory-DxlGh9Xf.mjs} +37 -52
- package/dist/graphql-DYWzJ29s.mjs +1026 -0
- package/dist/handlers-9sAbfIg-.mjs +2552 -0
- package/dist/handlers-Bl8zkwz1.mjs +2716 -0
- package/dist/handlers-C67ktuRN.mjs +710 -0
- package/dist/handlers-C87g8oCe.mjs +276 -0
- package/dist/handlers-CTsDAO6p.mjs +681 -0
- package/dist/handlers-Cgyg6c0U.mjs +645 -0
- package/dist/handlers-D6j6yka7.mjs +2124 -0
- package/dist/handlers-DdFzXLvF.mjs +446 -0
- package/dist/handlers-DeLOCd5m.mjs +799 -0
- package/dist/handlers-DlCJN4Td.mjs +757 -0
- package/dist/handlers-DxGIq15_2.mjs +917 -0
- package/dist/handlers-U6L4xhuF.mjs +585 -0
- package/dist/handlers-tB9Mp9ZK.mjs +84 -0
- package/dist/handlers-tiy7EIBp.mjs +572 -0
- package/dist/handlers.impl-DS0d9fUw.mjs +761 -0
- package/dist/hooks-CzCWByww.mjs +898 -0
- package/dist/index.mjs +377 -155
- package/dist/{logger-BmWzC2lM.mjs → logger-Dh_xb7_2.mjs} +14 -6
- package/dist/maintenance-P7ePRXQC.mjs +830 -0
- package/dist/manifest-2ToTpjv8.mjs +106 -0
- package/dist/manifest-3g71z6Bg.mjs +79 -0
- package/dist/manifest-82baTv4U.mjs +45 -0
- package/dist/manifest-B3QVVeBS.mjs +82 -0
- package/dist/manifest-BB2J8IMJ.mjs +149 -0
- package/dist/manifest-BKbgbSiY.mjs +60 -0
- package/dist/manifest-Bcf-TJzH.mjs +848 -0
- package/dist/manifest-BmtZzQiQ2.mjs +45 -0
- package/dist/manifest-Bnd7kqEY.mjs +55 -0
- package/dist/manifest-BqQX6OQC2.mjs +65 -0
- package/dist/manifest-BqrQ4Tpj.mjs +81 -0
- package/dist/manifest-Br4RPFt5.mjs +370 -0
- package/dist/manifest-C5qDjysN.mjs +107 -0
- package/dist/manifest-C9RT5nk32.mjs +34 -0
- package/dist/manifest-CAhOuvSl.mjs +204 -0
- package/dist/manifest-CBYWCUBJ.mjs +51 -0
- package/dist/manifest-CFADCRa1.mjs +37 -0
- package/dist/manifest-CQVhavRF.mjs +114 -0
- package/dist/manifest-CT7zZBV1.mjs +48 -0
- package/dist/manifest-CV12bcrF.mjs +121 -0
- package/dist/manifest-CXsRWjjI.mjs +224 -0
- package/dist/manifest-CZLUCfG02.mjs +95 -0
- package/dist/manifest-D6phHKFd.mjs +131 -0
- package/dist/manifest-DCyjf4n2.mjs +294 -0
- package/dist/manifest-DHsnKgP6.mjs +60 -0
- package/dist/manifest-Df_dliIe.mjs +55 -0
- package/dist/manifest-Dh8WBmEW.mjs +129 -0
- package/dist/manifest-DhKRAT8_.mjs +92 -0
- package/dist/manifest-DlpTj4ic2.mjs +193 -0
- package/dist/manifest-DrbmZcFl2.mjs +253 -0
- package/dist/manifest-DuwHjUa5.mjs +70 -0
- package/dist/manifest-DzwvxPJX.mjs +38 -0
- package/dist/manifest-NXctwWQq.mjs +68 -0
- package/dist/manifest-Sc_0JQ13.mjs +418 -0
- package/dist/manifest-gZ4s_UtG.mjs +96 -0
- package/dist/manifest-qSleDqdO.mjs +1023 -0
- package/dist/modules-C184v-S9.mjs +11365 -0
- package/dist/mojo-ipc-B_H61Afw.mjs +525 -0
- package/dist/network-671Cw6hV.mjs +3346 -0
- package/dist/{artifacts-BbdOMET5.mjs → outputPaths-B1uGmrWZ.mjs} +219 -212
- package/dist/parse-args-BlRjqlkL.mjs +39 -0
- package/dist/platform-WmNn8Sxb.mjs +2070 -0
- package/dist/process-QcbIy5Zq.mjs +1401 -0
- package/dist/proxy-DqNs0bAd.mjs +170 -0
- package/dist/registry-D-6e18lB.mjs +34 -0
- package/dist/response-BQVP-xUn.mjs +28 -0
- package/dist/server/plugin-api.mjs +2 -2
- package/dist/shared-state-board-DV-dpHFJ.mjs +586 -0
- package/dist/sourcemap-Dq8ez8vS.mjs +650 -0
- package/dist/ssrf-policy-ZaUfvhq7.mjs +166 -0
- package/dist/streaming-BUQ0VJsg.mjs +725 -0
- package/dist/tool-builder-DCbIC5Eo.mjs +186 -0
- package/dist/transform-CiYJfNX0.mjs +1007 -0
- package/dist/types-Bx92KJfT.mjs +4 -0
- package/dist/wasm-DQTnHDs4.mjs +531 -0
- package/dist/workflow-f3xJOcjx.mjs +725 -0
- package/package.json +16 -16
- package/dist/ExtensionManager-CPTJhHFg.mjs +0 -2
- package/dist/ToolCatalog-Bq4V2sbJ.mjs +0 -67201
- package/dist/{CacheAdapters-CzFNpD9a.mjs → CacheAdapters-CDe5WPSV.mjs} +0 -0
- package/dist/{StealthVerifier-BzBCFiwx.mjs → StealthVerifier-Bo4T3bz8.mjs} +0 -0
- package/dist/{VersionDetector-CNXcvD46.mjs → VersionDetector-CwVLVdDM.mjs} +0 -0
- package/dist/{formatAddress-ChCSIRWT.mjs → formatAddress-DVkj9kpI.mjs} +0 -0
- package/dist/{types-BBjOqye-.mjs → types-CPhOReNX.mjs} +1 -1
|
@@ -0,0 +1,1401 @@
|
|
|
1
|
+
import { t as logger } from "./logger-Dh_xb7_2.mjs";
|
|
2
|
+
import { V as ENABLE_INJECTION_TOOLS, wt as MEMORY_AUDIT_TRAIL_CAPACITY } from "./constants-B0OANIBL.mjs";
|
|
3
|
+
import { i as MemoryManager, r as UnifiedProcessManager } from "./modules-C184v-S9.mjs";
|
|
4
|
+
import { o as argStringArray, r as argNumber } from "./parse-args-BlRjqlkL.mjs";
|
|
5
|
+
import { i as connectPlaywrightCdpFallback } from "./ScriptManager-aHHq0X7U.mjs";
|
|
6
|
+
import "./definitions-B4rAvHNZ.mjs";
|
|
7
|
+
//#region src/modules/process/memory/AuditTrail.ts
|
|
8
|
+
var MemoryAuditTrail = class {
|
|
9
|
+
buffer;
|
|
10
|
+
head = 0;
|
|
11
|
+
count = 0;
|
|
12
|
+
capacity;
|
|
13
|
+
constructor(capacity = MEMORY_AUDIT_TRAIL_CAPACITY) {
|
|
14
|
+
this.capacity = Number.isInteger(capacity) && capacity > 0 ? capacity : MEMORY_AUDIT_TRAIL_CAPACITY;
|
|
15
|
+
this.buffer = Array.from({ length: this.capacity });
|
|
16
|
+
}
|
|
17
|
+
record(entry) {
|
|
18
|
+
const fullEntry = {
|
|
19
|
+
...entry,
|
|
20
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21
|
+
user: process.env.USERNAME || process.env.USER || "unknown"
|
|
22
|
+
};
|
|
23
|
+
if (this.count < this.capacity) {
|
|
24
|
+
const writeIndex = (this.head + this.count) % this.capacity;
|
|
25
|
+
this.buffer[writeIndex] = fullEntry;
|
|
26
|
+
this.count += 1;
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
this.buffer[this.head] = fullEntry;
|
|
30
|
+
this.head = (this.head + 1) % this.capacity;
|
|
31
|
+
}
|
|
32
|
+
exportJson() {
|
|
33
|
+
const entries = [];
|
|
34
|
+
for (let i = 0; i < this.count; i += 1) {
|
|
35
|
+
const index = (this.head + i) % this.capacity;
|
|
36
|
+
const entry = this.buffer[index];
|
|
37
|
+
if (entry) entries.push(entry);
|
|
38
|
+
}
|
|
39
|
+
return JSON.stringify(entries, null, 2);
|
|
40
|
+
}
|
|
41
|
+
clear() {
|
|
42
|
+
this.buffer = [];
|
|
43
|
+
this.head = 0;
|
|
44
|
+
this.count = 0;
|
|
45
|
+
}
|
|
46
|
+
size() {
|
|
47
|
+
return this.count;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/server/domains/process/handlers.base.types.ts
|
|
52
|
+
const MEMORY_PATTERN_TYPES = new Set([
|
|
53
|
+
"hex",
|
|
54
|
+
"int32",
|
|
55
|
+
"int64",
|
|
56
|
+
"float",
|
|
57
|
+
"double",
|
|
58
|
+
"string"
|
|
59
|
+
]);
|
|
60
|
+
/** Validate an arg is a positive integer PID. */
|
|
61
|
+
function validatePid(value) {
|
|
62
|
+
const n = Number(value);
|
|
63
|
+
if (!Number.isInteger(n) || n <= 0) throw new Error(`Invalid PID: ${JSON.stringify(value)}`);
|
|
64
|
+
return n;
|
|
65
|
+
}
|
|
66
|
+
/** Validate an arg is a non-empty string. */
|
|
67
|
+
function requireString(value, name) {
|
|
68
|
+
if (typeof value !== "string" || value.length === 0) throw new Error(`${name} must be a non-empty string`);
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
/** Validate an arg is a positive number. */
|
|
72
|
+
function requirePositiveNumber(value, name) {
|
|
73
|
+
const n = Number(value);
|
|
74
|
+
if (!Number.isFinite(n) || n <= 0) throw new Error(`${name} must be a positive number`);
|
|
75
|
+
return n;
|
|
76
|
+
}
|
|
77
|
+
function normalizePatternType(value) {
|
|
78
|
+
if (typeof value === "string" && MEMORY_PATTERN_TYPES.has(value)) return value;
|
|
79
|
+
return "hex";
|
|
80
|
+
}
|
|
81
|
+
function getOptionalPid$1(value) {
|
|
82
|
+
const pid = Number(value);
|
|
83
|
+
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
84
|
+
}
|
|
85
|
+
function getOptionalString$1(value) {
|
|
86
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
87
|
+
}
|
|
88
|
+
function getOptionalPositiveNumber(value) {
|
|
89
|
+
const size = Number(value);
|
|
90
|
+
return Number.isFinite(size) && size > 0 ? size : void 0;
|
|
91
|
+
}
|
|
92
|
+
function getWriteSize(data, encoding) {
|
|
93
|
+
if (encoding === "hex") {
|
|
94
|
+
const normalized = data.replace(/\s+/g, "");
|
|
95
|
+
return Math.ceil(normalized.length / 2);
|
|
96
|
+
}
|
|
97
|
+
return Buffer.from(data, "base64").length;
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/server/domains/process/handlers/process-management.ts
|
|
101
|
+
/**
|
|
102
|
+
* Process management handlers — find/get/windows/findChromium/checkDebugPort/launchDebug/kill.
|
|
103
|
+
*
|
|
104
|
+
* Also provides buildMemoryDiagnostics and recordMemoryAudit helpers
|
|
105
|
+
* shared by other sub-handlers via deps.
|
|
106
|
+
*/
|
|
107
|
+
var ProcessManagementHandlers = class {
|
|
108
|
+
processManager;
|
|
109
|
+
memoryManager;
|
|
110
|
+
platform;
|
|
111
|
+
auditTrail;
|
|
112
|
+
constructor(deps) {
|
|
113
|
+
this.processManager = deps.processManager;
|
|
114
|
+
this.memoryManager = deps.memoryManager;
|
|
115
|
+
this.platform = deps.platform;
|
|
116
|
+
this.auditTrail = deps.auditTrail;
|
|
117
|
+
}
|
|
118
|
+
async buildMemoryDiagnostics(input) {
|
|
119
|
+
const recommendedActions = /* @__PURE__ */ new Set();
|
|
120
|
+
const permission = await this.memoryManager.checkAvailability();
|
|
121
|
+
if (!permission.available) recommendedActions.add("Run as administrator");
|
|
122
|
+
const processInfo = await this.resolveProcessInfo(input.pid);
|
|
123
|
+
if (input.pid !== void 0 && input.pid !== null && !processInfo) recommendedActions.add("Check if process is still running");
|
|
124
|
+
const protectionResult = await this.queryProtection(input);
|
|
125
|
+
if (protectionResult.queryFailed || protectionResult.info?.success === false) recommendedActions.add("Verify address is within valid memory region");
|
|
126
|
+
if (input.size !== void 0 && input.size !== null && protectionResult.info?.regionSize !== void 0 && protectionResult.info.regionSize !== null && input.size > protectionResult.info.regionSize) recommendedActions.add("Reduce the requested size to fit the target memory region");
|
|
127
|
+
if (input.operation === "memory_read" && protectionResult.info?.success && protectionResult.info.isReadable === false) recommendedActions.add("Ensure target memory region is readable");
|
|
128
|
+
if (input.operation === "memory_write" && protectionResult.info?.success && protectionResult.info.isWritable === false) recommendedActions.add("Ensure target memory region is writable");
|
|
129
|
+
const modulesInfo = await this.enumerateModulesSafe(input.pid);
|
|
130
|
+
if (input.pid !== void 0 && input.pid !== null && input.address) recommendedActions.add("Re-resolve the address after the process restarts because ASLR can shift module addresses");
|
|
131
|
+
const normalizedError = input.error?.toLowerCase() ?? "";
|
|
132
|
+
if (normalizedError.includes("access denied") || normalizedError.includes("permission") || normalizedError.includes("privilege") || normalizedError.includes("administrator")) recommendedActions.add("Run as administrator");
|
|
133
|
+
const aslrNote = modulesInfo.enumerated ? modulesInfo.count && modulesInfo.count > 0 ? `Enumerated ${modulesInfo.count} module(s). Treat absolute addresses as session-specific because ASLR can shift module bases between launches.` : "Module enumeration succeeded but returned no modules. Absolute addresses may still change across process launches because of ASLR." : "Module enumeration was unavailable. Assume ASLR may shift absolute addresses between launches and re-resolve addresses after restarts.";
|
|
134
|
+
return {
|
|
135
|
+
permission: {
|
|
136
|
+
available: permission.available,
|
|
137
|
+
reason: permission.reason,
|
|
138
|
+
platform: this.platform
|
|
139
|
+
},
|
|
140
|
+
process: {
|
|
141
|
+
exists: input.pid !== void 0 && input.pid !== null ? Boolean(processInfo) : null,
|
|
142
|
+
pid: input.pid ?? null,
|
|
143
|
+
name: processInfo?.name ?? null
|
|
144
|
+
},
|
|
145
|
+
address: {
|
|
146
|
+
queried: input.pid !== void 0 && input.pid !== null && Boolean(input.address),
|
|
147
|
+
valid: input.pid !== void 0 && input.pid !== null && input.address ? protectionResult.info?.success ?? null : null,
|
|
148
|
+
protection: protectionResult.info?.protection ?? null,
|
|
149
|
+
regionStart: protectionResult.info?.regionStart ?? null,
|
|
150
|
+
regionSize: protectionResult.info?.regionSize ?? null
|
|
151
|
+
},
|
|
152
|
+
aslr: {
|
|
153
|
+
heuristic: true,
|
|
154
|
+
note: aslrNote
|
|
155
|
+
},
|
|
156
|
+
recommendedActions: Array.from(recommendedActions)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
async safeBuildMemoryDiagnostics(input) {
|
|
160
|
+
try {
|
|
161
|
+
return await this.buildMemoryDiagnostics(input);
|
|
162
|
+
} catch (diagnosticError) {
|
|
163
|
+
logger.warn("Memory diagnostics generation failed:", diagnosticError);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
recordMemoryAudit(entry) {
|
|
168
|
+
try {
|
|
169
|
+
this.auditTrail.record(entry);
|
|
170
|
+
} catch (auditError) {
|
|
171
|
+
logger.warn("Memory audit trail recording failed:", auditError);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
get platformValue() {
|
|
175
|
+
return this.platform;
|
|
176
|
+
}
|
|
177
|
+
async handleProcessList(_args) {
|
|
178
|
+
try {
|
|
179
|
+
const processes = await this.processManager.findProcesses("");
|
|
180
|
+
return { content: [{
|
|
181
|
+
type: "text",
|
|
182
|
+
text: JSON.stringify({
|
|
183
|
+
success: true,
|
|
184
|
+
count: processes.length,
|
|
185
|
+
processes: processes.map((p) => ({
|
|
186
|
+
pid: p.pid,
|
|
187
|
+
name: p.name,
|
|
188
|
+
path: p.executablePath,
|
|
189
|
+
windowTitle: p.windowTitle,
|
|
190
|
+
windowHandle: p.windowHandle,
|
|
191
|
+
memoryMB: p.memoryUsage ? Math.round(p.memoryUsage / 1024 / 1024) : void 0
|
|
192
|
+
}))
|
|
193
|
+
}, null, 2)
|
|
194
|
+
}] };
|
|
195
|
+
} catch (error) {
|
|
196
|
+
logger.error("Process list failed:", error);
|
|
197
|
+
return { content: [{
|
|
198
|
+
type: "text",
|
|
199
|
+
text: JSON.stringify({
|
|
200
|
+
success: false,
|
|
201
|
+
error: error instanceof Error ? error.message : String(error)
|
|
202
|
+
}, null, 2)
|
|
203
|
+
}] };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async handleProcessFind(args) {
|
|
207
|
+
try {
|
|
208
|
+
const pattern = requireString(args.pattern, "pattern");
|
|
209
|
+
const processes = await this.processManager.findProcesses(pattern);
|
|
210
|
+
return { content: [{
|
|
211
|
+
type: "text",
|
|
212
|
+
text: JSON.stringify({
|
|
213
|
+
success: true,
|
|
214
|
+
pattern,
|
|
215
|
+
count: processes.length,
|
|
216
|
+
processes: processes.map((p) => ({
|
|
217
|
+
pid: p.pid,
|
|
218
|
+
name: p.name,
|
|
219
|
+
path: p.executablePath,
|
|
220
|
+
windowTitle: p.windowTitle,
|
|
221
|
+
windowHandle: p.windowHandle,
|
|
222
|
+
memoryMB: p.memoryUsage ? Math.round(p.memoryUsage / 1024 / 1024) : void 0
|
|
223
|
+
}))
|
|
224
|
+
}, null, 2)
|
|
225
|
+
}] };
|
|
226
|
+
} catch (error) {
|
|
227
|
+
logger.error("Process find failed:", error);
|
|
228
|
+
return { content: [{
|
|
229
|
+
type: "text",
|
|
230
|
+
text: JSON.stringify({
|
|
231
|
+
success: false,
|
|
232
|
+
error: error instanceof Error ? error.message : String(error)
|
|
233
|
+
}, null, 2)
|
|
234
|
+
}] };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async handleProcessGet(args) {
|
|
238
|
+
try {
|
|
239
|
+
const pid = validatePid(args.pid);
|
|
240
|
+
const process = await this.processManager.getProcessByPid(pid);
|
|
241
|
+
if (!process) return { content: [{
|
|
242
|
+
type: "text",
|
|
243
|
+
text: JSON.stringify({
|
|
244
|
+
success: false,
|
|
245
|
+
message: `Process with PID ${pid} not found`
|
|
246
|
+
}, null, 2)
|
|
247
|
+
}] };
|
|
248
|
+
const cmdLine = await this.processManager.getProcessCommandLine(pid);
|
|
249
|
+
const debugPort = await this.processManager.checkDebugPort(pid, { commandLine: cmdLine.commandLine });
|
|
250
|
+
return { content: [{
|
|
251
|
+
type: "text",
|
|
252
|
+
text: JSON.stringify({
|
|
253
|
+
success: true,
|
|
254
|
+
process: {
|
|
255
|
+
...process,
|
|
256
|
+
commandLine: cmdLine.commandLine,
|
|
257
|
+
parentPid: cmdLine.parentPid,
|
|
258
|
+
debugPort
|
|
259
|
+
}
|
|
260
|
+
}, null, 2)
|
|
261
|
+
}] };
|
|
262
|
+
} catch (error) {
|
|
263
|
+
logger.error("Process get failed:", error);
|
|
264
|
+
return { content: [{
|
|
265
|
+
type: "text",
|
|
266
|
+
text: JSON.stringify({
|
|
267
|
+
success: false,
|
|
268
|
+
error: error instanceof Error ? error.message : String(error)
|
|
269
|
+
}, null, 2)
|
|
270
|
+
}] };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async handleProcessWindows(args) {
|
|
274
|
+
try {
|
|
275
|
+
const pid = validatePid(args.pid);
|
|
276
|
+
const windows = await this.processManager.getProcessWindows(pid);
|
|
277
|
+
return { content: [{
|
|
278
|
+
type: "text",
|
|
279
|
+
text: JSON.stringify({
|
|
280
|
+
success: true,
|
|
281
|
+
pid,
|
|
282
|
+
windowCount: windows.length,
|
|
283
|
+
windows: windows.map((w) => ({
|
|
284
|
+
handle: w.handle,
|
|
285
|
+
title: w.title,
|
|
286
|
+
className: w.className,
|
|
287
|
+
processId: w.processId
|
|
288
|
+
}))
|
|
289
|
+
}, null, 2)
|
|
290
|
+
}] };
|
|
291
|
+
} catch (error) {
|
|
292
|
+
logger.error("Process windows failed:", error);
|
|
293
|
+
return { content: [{
|
|
294
|
+
type: "text",
|
|
295
|
+
text: JSON.stringify({
|
|
296
|
+
success: false,
|
|
297
|
+
error: error instanceof Error ? error.message : String(error)
|
|
298
|
+
}, null, 2)
|
|
299
|
+
}] };
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async handleProcessCheckDebugPort(args) {
|
|
303
|
+
try {
|
|
304
|
+
const pid = validatePid(args.pid);
|
|
305
|
+
const debugPort = await this.processManager.checkDebugPort(pid);
|
|
306
|
+
return { content: [{
|
|
307
|
+
type: "text",
|
|
308
|
+
text: JSON.stringify({
|
|
309
|
+
success: true,
|
|
310
|
+
pid,
|
|
311
|
+
debugPort,
|
|
312
|
+
canAttach: debugPort !== null,
|
|
313
|
+
attachUrl: debugPort ? `http://localhost:${debugPort}` : null
|
|
314
|
+
}, null, 2)
|
|
315
|
+
}] };
|
|
316
|
+
} catch (error) {
|
|
317
|
+
logger.error("Check debug port failed:", error);
|
|
318
|
+
return { content: [{
|
|
319
|
+
type: "text",
|
|
320
|
+
text: JSON.stringify({
|
|
321
|
+
success: false,
|
|
322
|
+
error: error instanceof Error ? error.message : String(error)
|
|
323
|
+
}, null, 2)
|
|
324
|
+
}] };
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
async handleProcessLaunchDebug(args) {
|
|
328
|
+
try {
|
|
329
|
+
const executablePath = requireString(args.executablePath, "executablePath");
|
|
330
|
+
const debugPort = argNumber(args, "debugPort", 9222);
|
|
331
|
+
const argsList = argStringArray(args, "args");
|
|
332
|
+
const process = await this.processManager.launchWithDebug(executablePath, debugPort, argsList);
|
|
333
|
+
if (!process) return { content: [{
|
|
334
|
+
type: "text",
|
|
335
|
+
text: JSON.stringify({
|
|
336
|
+
success: false,
|
|
337
|
+
message: "Failed to launch process"
|
|
338
|
+
}, null, 2)
|
|
339
|
+
}] };
|
|
340
|
+
return { content: [{
|
|
341
|
+
type: "text",
|
|
342
|
+
text: JSON.stringify({
|
|
343
|
+
success: true,
|
|
344
|
+
process: {
|
|
345
|
+
pid: process.pid,
|
|
346
|
+
name: process.name,
|
|
347
|
+
path: process.executablePath
|
|
348
|
+
},
|
|
349
|
+
debugPort,
|
|
350
|
+
attachUrl: `http://localhost:${debugPort}`
|
|
351
|
+
}, null, 2)
|
|
352
|
+
}] };
|
|
353
|
+
} catch (error) {
|
|
354
|
+
logger.error("Launch debug failed:", error);
|
|
355
|
+
return { content: [{
|
|
356
|
+
type: "text",
|
|
357
|
+
text: JSON.stringify({
|
|
358
|
+
success: false,
|
|
359
|
+
error: error instanceof Error ? error.message : String(error)
|
|
360
|
+
}, null, 2)
|
|
361
|
+
}] };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async handleProcessKill(args) {
|
|
365
|
+
try {
|
|
366
|
+
const pid = validatePid(args.pid);
|
|
367
|
+
const killed = await this.processManager.killProcess(pid);
|
|
368
|
+
return { content: [{
|
|
369
|
+
type: "text",
|
|
370
|
+
text: JSON.stringify({
|
|
371
|
+
success: killed,
|
|
372
|
+
pid,
|
|
373
|
+
message: killed ? `Process ${pid} killed successfully` : `Failed to kill process ${pid}`
|
|
374
|
+
}, null, 2)
|
|
375
|
+
}] };
|
|
376
|
+
} catch (error) {
|
|
377
|
+
logger.error("Process kill failed:", error);
|
|
378
|
+
return { content: [{
|
|
379
|
+
type: "text",
|
|
380
|
+
text: JSON.stringify({
|
|
381
|
+
success: false,
|
|
382
|
+
error: error instanceof Error ? error.message : String(error)
|
|
383
|
+
}, null, 2)
|
|
384
|
+
}] };
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async resolveProcessInfo(pid) {
|
|
388
|
+
if (pid === void 0 || pid === null) return null;
|
|
389
|
+
try {
|
|
390
|
+
const resolvedProcess = await this.processManager.getProcessByPid(pid);
|
|
391
|
+
return resolvedProcess ? {
|
|
392
|
+
pid: resolvedProcess.pid,
|
|
393
|
+
name: resolvedProcess.name,
|
|
394
|
+
executablePath: resolvedProcess.executablePath,
|
|
395
|
+
windowTitle: resolvedProcess.windowTitle,
|
|
396
|
+
windowHandle: resolvedProcess.windowHandle,
|
|
397
|
+
memoryUsage: resolvedProcess.memoryUsage
|
|
398
|
+
} : null;
|
|
399
|
+
} catch {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async queryProtection(input) {
|
|
404
|
+
if (input.pid === void 0 || input.pid === null || !input.address) return {
|
|
405
|
+
info: null,
|
|
406
|
+
queryFailed: false
|
|
407
|
+
};
|
|
408
|
+
let queryFailed = false;
|
|
409
|
+
let info = null;
|
|
410
|
+
try {
|
|
411
|
+
info = await this.memoryManager.checkMemoryProtection(input.pid, input.address);
|
|
412
|
+
} catch {
|
|
413
|
+
queryFailed = true;
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
info,
|
|
417
|
+
queryFailed
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
async enumerateModulesSafe(pid) {
|
|
421
|
+
if (pid === void 0 || pid === null) return {
|
|
422
|
+
enumerated: false,
|
|
423
|
+
count: null
|
|
424
|
+
};
|
|
425
|
+
try {
|
|
426
|
+
const modulesResult = await this.memoryManager.enumerateModules(pid);
|
|
427
|
+
return {
|
|
428
|
+
enumerated: modulesResult.success,
|
|
429
|
+
count: modulesResult.modules?.length ?? null
|
|
430
|
+
};
|
|
431
|
+
} catch {
|
|
432
|
+
return {
|
|
433
|
+
enumerated: false,
|
|
434
|
+
count: null
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region src/server/domains/process/handlers/memory-operations.ts
|
|
441
|
+
/**
|
|
442
|
+
* Memory operation handlers — read/write/scan/audit/protection/filtered/batch/dump/regions.
|
|
443
|
+
*/
|
|
444
|
+
var MemoryOperationHandlers = class {
|
|
445
|
+
memoryManager;
|
|
446
|
+
platform;
|
|
447
|
+
processMgmt;
|
|
448
|
+
constructor(deps, processMgmt) {
|
|
449
|
+
this.memoryManager = deps.memoryManager;
|
|
450
|
+
this.platform = deps.platform;
|
|
451
|
+
this.processMgmt = processMgmt;
|
|
452
|
+
}
|
|
453
|
+
async handleMemoryRead(args) {
|
|
454
|
+
const startedAt = Date.now();
|
|
455
|
+
try {
|
|
456
|
+
const pid = validatePid(args.pid);
|
|
457
|
+
const address = requireString(args.address, "address");
|
|
458
|
+
const size = requirePositiveNumber(args.size, "size");
|
|
459
|
+
const availability = await this.memoryManager.checkAvailability();
|
|
460
|
+
if (!availability.available) return await this.unavailableResponse("memory_read", pid, address, size, startedAt, availability.reason);
|
|
461
|
+
const result = await this.memoryManager.readMemory(pid, address, size);
|
|
462
|
+
const diagnostics = !result.success ? await this.processMgmt.safeBuildMemoryDiagnostics({
|
|
463
|
+
pid,
|
|
464
|
+
address,
|
|
465
|
+
size,
|
|
466
|
+
operation: "memory_read",
|
|
467
|
+
error: result.error
|
|
468
|
+
}) : void 0;
|
|
469
|
+
this.processMgmt.recordMemoryAudit({
|
|
470
|
+
operation: "memory_read",
|
|
471
|
+
pid,
|
|
472
|
+
address,
|
|
473
|
+
size,
|
|
474
|
+
result: result.success ? "success" : "failure",
|
|
475
|
+
error: result.error,
|
|
476
|
+
durationMs: Date.now() - startedAt
|
|
477
|
+
});
|
|
478
|
+
const payload = {
|
|
479
|
+
success: result.success,
|
|
480
|
+
data: result.data,
|
|
481
|
+
error: result.error,
|
|
482
|
+
pid,
|
|
483
|
+
address,
|
|
484
|
+
size,
|
|
485
|
+
platform: this.platform
|
|
486
|
+
};
|
|
487
|
+
if (!result.success) payload.diagnostics = diagnostics;
|
|
488
|
+
return { content: [{
|
|
489
|
+
type: "text",
|
|
490
|
+
text: JSON.stringify(payload, null, 2)
|
|
491
|
+
}] };
|
|
492
|
+
} catch (error) {
|
|
493
|
+
logger.error("Memory read failed:", error);
|
|
494
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
495
|
+
const pid = getOptionalPid$1(args.pid);
|
|
496
|
+
const address = getOptionalString$1(args.address);
|
|
497
|
+
const size = getOptionalPositiveNumber(args.size);
|
|
498
|
+
const diagnostics = await this.processMgmt.safeBuildMemoryDiagnostics({
|
|
499
|
+
pid,
|
|
500
|
+
address,
|
|
501
|
+
size,
|
|
502
|
+
operation: "memory_read",
|
|
503
|
+
error: errorMessage
|
|
504
|
+
});
|
|
505
|
+
this.processMgmt.recordMemoryAudit({
|
|
506
|
+
operation: "memory_read",
|
|
507
|
+
pid: pid ?? null,
|
|
508
|
+
address: address ?? null,
|
|
509
|
+
size: size ?? null,
|
|
510
|
+
result: "failure",
|
|
511
|
+
error: errorMessage,
|
|
512
|
+
durationMs: Date.now() - startedAt
|
|
513
|
+
});
|
|
514
|
+
return { content: [{
|
|
515
|
+
type: "text",
|
|
516
|
+
text: JSON.stringify({
|
|
517
|
+
success: false,
|
|
518
|
+
error: errorMessage,
|
|
519
|
+
diagnostics
|
|
520
|
+
}, null, 2)
|
|
521
|
+
}] };
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
async handleMemoryWrite(args) {
|
|
525
|
+
const startedAt = Date.now();
|
|
526
|
+
try {
|
|
527
|
+
const pid = validatePid(args.pid);
|
|
528
|
+
const address = requireString(args.address, "address");
|
|
529
|
+
const data = requireString(args.data, "data");
|
|
530
|
+
const encoding = args.encoding || "hex";
|
|
531
|
+
const size = getWriteSize(data, encoding);
|
|
532
|
+
const availability = await this.memoryManager.checkAvailability();
|
|
533
|
+
if (!availability.available) {
|
|
534
|
+
const errorMessage = availability.reason ?? "Memory operations not available";
|
|
535
|
+
const diagnostics = await this.processMgmt.safeBuildMemoryDiagnostics({
|
|
536
|
+
pid,
|
|
537
|
+
address,
|
|
538
|
+
size,
|
|
539
|
+
operation: "memory_write",
|
|
540
|
+
error: errorMessage
|
|
541
|
+
});
|
|
542
|
+
this.processMgmt.recordMemoryAudit({
|
|
543
|
+
operation: "memory_write",
|
|
544
|
+
pid,
|
|
545
|
+
address,
|
|
546
|
+
size,
|
|
547
|
+
result: "failure",
|
|
548
|
+
error: errorMessage,
|
|
549
|
+
durationMs: Date.now() - startedAt
|
|
550
|
+
});
|
|
551
|
+
return { content: [{
|
|
552
|
+
type: "text",
|
|
553
|
+
text: JSON.stringify({
|
|
554
|
+
success: false,
|
|
555
|
+
message: "Memory operations not available",
|
|
556
|
+
reason: availability.reason,
|
|
557
|
+
platform: this.platform,
|
|
558
|
+
requestedAddress: address,
|
|
559
|
+
dataLength: data !== void 0 && data !== null ? data.length : 0,
|
|
560
|
+
encoding,
|
|
561
|
+
pid,
|
|
562
|
+
diagnostics
|
|
563
|
+
}, null, 2)
|
|
564
|
+
}] };
|
|
565
|
+
}
|
|
566
|
+
const result = await this.memoryManager.writeMemory(pid, address, data, encoding);
|
|
567
|
+
const diagnostics = !result.success ? await this.processMgmt.safeBuildMemoryDiagnostics({
|
|
568
|
+
pid,
|
|
569
|
+
address,
|
|
570
|
+
size,
|
|
571
|
+
operation: "memory_write",
|
|
572
|
+
error: result.error
|
|
573
|
+
}) : void 0;
|
|
574
|
+
this.processMgmt.recordMemoryAudit({
|
|
575
|
+
operation: "memory_write",
|
|
576
|
+
pid,
|
|
577
|
+
address,
|
|
578
|
+
size,
|
|
579
|
+
result: result.success ? "success" : "failure",
|
|
580
|
+
error: result.error,
|
|
581
|
+
durationMs: Date.now() - startedAt
|
|
582
|
+
});
|
|
583
|
+
const payload = {
|
|
584
|
+
success: result.success,
|
|
585
|
+
bytesWritten: result.bytesWritten,
|
|
586
|
+
error: result.error,
|
|
587
|
+
pid,
|
|
588
|
+
address,
|
|
589
|
+
dataLength: data.length,
|
|
590
|
+
encoding,
|
|
591
|
+
platform: this.platform
|
|
592
|
+
};
|
|
593
|
+
if (!result.success) payload.diagnostics = diagnostics;
|
|
594
|
+
return { content: [{
|
|
595
|
+
type: "text",
|
|
596
|
+
text: JSON.stringify(payload, null, 2)
|
|
597
|
+
}] };
|
|
598
|
+
} catch (error) {
|
|
599
|
+
logger.error("Memory write failed:", error);
|
|
600
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
601
|
+
const pid = getOptionalPid$1(args.pid);
|
|
602
|
+
const address = getOptionalString$1(args.address);
|
|
603
|
+
const data = getOptionalString$1(args.data);
|
|
604
|
+
const encoding = args.encoding || "hex";
|
|
605
|
+
const size = data ? getWriteSize(data, encoding) : void 0;
|
|
606
|
+
const diagnostics = await this.processMgmt.safeBuildMemoryDiagnostics({
|
|
607
|
+
pid,
|
|
608
|
+
address,
|
|
609
|
+
size,
|
|
610
|
+
operation: "memory_write",
|
|
611
|
+
error: errorMessage
|
|
612
|
+
});
|
|
613
|
+
this.processMgmt.recordMemoryAudit({
|
|
614
|
+
operation: "memory_write",
|
|
615
|
+
pid: pid ?? null,
|
|
616
|
+
address: address ?? null,
|
|
617
|
+
size: size ?? null,
|
|
618
|
+
result: "failure",
|
|
619
|
+
error: errorMessage,
|
|
620
|
+
durationMs: Date.now() - startedAt
|
|
621
|
+
});
|
|
622
|
+
return { content: [{
|
|
623
|
+
type: "text",
|
|
624
|
+
text: JSON.stringify({
|
|
625
|
+
success: false,
|
|
626
|
+
error: errorMessage,
|
|
627
|
+
diagnostics
|
|
628
|
+
}, null, 2)
|
|
629
|
+
}] };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
async handleMemoryScan(args) {
|
|
633
|
+
const startedAt = Date.now();
|
|
634
|
+
try {
|
|
635
|
+
const pid = validatePid(args.pid);
|
|
636
|
+
const pattern = requireString(args.pattern, "pattern");
|
|
637
|
+
const patternType = normalizePatternType(args.patternType);
|
|
638
|
+
const suspendTarget = args.suspendTarget === true;
|
|
639
|
+
const availability = await this.memoryManager.checkAvailability();
|
|
640
|
+
if (!availability.available) {
|
|
641
|
+
const errorMessage = availability.reason ?? "Memory operations not available";
|
|
642
|
+
const diagnostics = await this.processMgmt.safeBuildMemoryDiagnostics({
|
|
643
|
+
pid,
|
|
644
|
+
operation: "memory_scan",
|
|
645
|
+
error: errorMessage
|
|
646
|
+
});
|
|
647
|
+
this.processMgmt.recordMemoryAudit({
|
|
648
|
+
operation: "memory_scan",
|
|
649
|
+
pid,
|
|
650
|
+
address: null,
|
|
651
|
+
size: null,
|
|
652
|
+
result: "failure",
|
|
653
|
+
error: errorMessage,
|
|
654
|
+
durationMs: Date.now() - startedAt
|
|
655
|
+
});
|
|
656
|
+
return { content: [{
|
|
657
|
+
type: "text",
|
|
658
|
+
text: JSON.stringify({
|
|
659
|
+
success: false,
|
|
660
|
+
message: "Memory operations not available",
|
|
661
|
+
reason: availability.reason,
|
|
662
|
+
platform: this.platform,
|
|
663
|
+
requestedPattern: pattern,
|
|
664
|
+
patternType,
|
|
665
|
+
pid,
|
|
666
|
+
diagnostics
|
|
667
|
+
}, null, 2)
|
|
668
|
+
}] };
|
|
669
|
+
}
|
|
670
|
+
const result = await this.memoryManager.scanMemory(pid, pattern, patternType, suspendTarget);
|
|
671
|
+
const diagnostics = !result.success ? await this.processMgmt.safeBuildMemoryDiagnostics({
|
|
672
|
+
pid,
|
|
673
|
+
operation: "memory_scan",
|
|
674
|
+
error: result.error
|
|
675
|
+
}) : void 0;
|
|
676
|
+
this.processMgmt.recordMemoryAudit({
|
|
677
|
+
operation: "memory_scan",
|
|
678
|
+
pid,
|
|
679
|
+
address: null,
|
|
680
|
+
size: null,
|
|
681
|
+
result: result.success ? "success" : "failure",
|
|
682
|
+
error: result.error,
|
|
683
|
+
durationMs: Date.now() - startedAt
|
|
684
|
+
});
|
|
685
|
+
const payload = {
|
|
686
|
+
success: result.success,
|
|
687
|
+
addresses: result.addresses,
|
|
688
|
+
error: result.error,
|
|
689
|
+
pid,
|
|
690
|
+
pattern,
|
|
691
|
+
patternType,
|
|
692
|
+
platform: this.platform
|
|
693
|
+
};
|
|
694
|
+
if (!result.success) payload.diagnostics = diagnostics;
|
|
695
|
+
return { content: [{
|
|
696
|
+
type: "text",
|
|
697
|
+
text: JSON.stringify(payload, null, 2)
|
|
698
|
+
}] };
|
|
699
|
+
} catch (error) {
|
|
700
|
+
logger.error("Memory scan failed:", error);
|
|
701
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
702
|
+
const pid = getOptionalPid$1(args.pid);
|
|
703
|
+
const diagnostics = await this.processMgmt.safeBuildMemoryDiagnostics({
|
|
704
|
+
pid,
|
|
705
|
+
operation: "memory_scan",
|
|
706
|
+
error: errorMessage
|
|
707
|
+
});
|
|
708
|
+
this.processMgmt.recordMemoryAudit({
|
|
709
|
+
operation: "memory_scan",
|
|
710
|
+
pid: pid ?? null,
|
|
711
|
+
address: null,
|
|
712
|
+
size: null,
|
|
713
|
+
result: "failure",
|
|
714
|
+
error: errorMessage,
|
|
715
|
+
durationMs: Date.now() - startedAt
|
|
716
|
+
});
|
|
717
|
+
return { content: [{
|
|
718
|
+
type: "text",
|
|
719
|
+
text: JSON.stringify({
|
|
720
|
+
success: false,
|
|
721
|
+
error: errorMessage,
|
|
722
|
+
diagnostics
|
|
723
|
+
}, null, 2)
|
|
724
|
+
}] };
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
async handleMemoryAuditExport(args) {
|
|
728
|
+
try {
|
|
729
|
+
const exportedJson = this.processMgmt["auditTrail"].exportJson();
|
|
730
|
+
const entries = JSON.parse(exportedJson);
|
|
731
|
+
const clear = args.clear === true;
|
|
732
|
+
const count = this.processMgmt["auditTrail"].size();
|
|
733
|
+
if (clear) this.processMgmt["auditTrail"].clear();
|
|
734
|
+
return { content: [{
|
|
735
|
+
type: "text",
|
|
736
|
+
text: JSON.stringify({
|
|
737
|
+
success: true,
|
|
738
|
+
count,
|
|
739
|
+
cleared: clear,
|
|
740
|
+
entries
|
|
741
|
+
}, null, 2)
|
|
742
|
+
}] };
|
|
743
|
+
} catch (error) {
|
|
744
|
+
logger.error("Memory audit export failed:", error);
|
|
745
|
+
return { content: [{
|
|
746
|
+
type: "text",
|
|
747
|
+
text: JSON.stringify({
|
|
748
|
+
success: false,
|
|
749
|
+
error: error instanceof Error ? error.message : String(error)
|
|
750
|
+
}, null, 2)
|
|
751
|
+
}] };
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
async handleMemoryCheckProtection(args) {
|
|
755
|
+
try {
|
|
756
|
+
const pid = validatePid(args.pid);
|
|
757
|
+
const address = requireString(args.address, "address");
|
|
758
|
+
const result = await this.memoryManager.checkMemoryProtection(pid, address);
|
|
759
|
+
return { content: [{
|
|
760
|
+
type: "text",
|
|
761
|
+
text: JSON.stringify(result, null, 2)
|
|
762
|
+
}] };
|
|
763
|
+
} catch (error) {
|
|
764
|
+
logger.error("Memory check protection failed:", error);
|
|
765
|
+
return { content: [{
|
|
766
|
+
type: "text",
|
|
767
|
+
text: JSON.stringify({
|
|
768
|
+
success: false,
|
|
769
|
+
error: error instanceof Error ? error.message : String(error)
|
|
770
|
+
}, null, 2)
|
|
771
|
+
}] };
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
async handleMemoryScanFiltered(args) {
|
|
775
|
+
try {
|
|
776
|
+
const pid = validatePid(args.pid);
|
|
777
|
+
const pattern = requireString(args.pattern, "pattern");
|
|
778
|
+
const addresses = args.addresses;
|
|
779
|
+
const patternType = normalizePatternType(args.patternType);
|
|
780
|
+
const availability = await this.memoryManager.checkAvailability();
|
|
781
|
+
if (!availability.available) return { content: [{
|
|
782
|
+
type: "text",
|
|
783
|
+
text: JSON.stringify({
|
|
784
|
+
success: false,
|
|
785
|
+
message: "Memory operations not available",
|
|
786
|
+
reason: availability.reason,
|
|
787
|
+
platform: this.platform,
|
|
788
|
+
pid
|
|
789
|
+
}, null, 2)
|
|
790
|
+
}] };
|
|
791
|
+
const result = await this.memoryManager.scanMemoryFiltered(pid, pattern, addresses, patternType);
|
|
792
|
+
return { content: [{
|
|
793
|
+
type: "text",
|
|
794
|
+
text: JSON.stringify(result, null, 2)
|
|
795
|
+
}] };
|
|
796
|
+
} catch (error) {
|
|
797
|
+
logger.error("Memory scan filtered failed:", error);
|
|
798
|
+
return { content: [{
|
|
799
|
+
type: "text",
|
|
800
|
+
text: JSON.stringify({
|
|
801
|
+
success: false,
|
|
802
|
+
error: error instanceof Error ? error.message : String(error)
|
|
803
|
+
}, null, 2)
|
|
804
|
+
}] };
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async handleMemoryBatchWrite(args) {
|
|
808
|
+
try {
|
|
809
|
+
const pid = validatePid(args.pid);
|
|
810
|
+
const patches = args.patches;
|
|
811
|
+
const availability = await this.memoryManager.checkAvailability();
|
|
812
|
+
if (!availability.available) return { content: [{
|
|
813
|
+
type: "text",
|
|
814
|
+
text: JSON.stringify({
|
|
815
|
+
success: false,
|
|
816
|
+
message: "Memory operations not available",
|
|
817
|
+
reason: availability.reason,
|
|
818
|
+
platform: this.platform,
|
|
819
|
+
pid
|
|
820
|
+
}, null, 2)
|
|
821
|
+
}] };
|
|
822
|
+
const result = await this.memoryManager.batchMemoryWrite(pid, patches);
|
|
823
|
+
return { content: [{
|
|
824
|
+
type: "text",
|
|
825
|
+
text: JSON.stringify(result, null, 2)
|
|
826
|
+
}] };
|
|
827
|
+
} catch (error) {
|
|
828
|
+
logger.error("Memory batch write failed:", error);
|
|
829
|
+
return { content: [{
|
|
830
|
+
type: "text",
|
|
831
|
+
text: JSON.stringify({
|
|
832
|
+
success: false,
|
|
833
|
+
error: error instanceof Error ? error.message : String(error)
|
|
834
|
+
}, null, 2)
|
|
835
|
+
}] };
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
async handleMemoryDumpRegion(args) {
|
|
839
|
+
try {
|
|
840
|
+
const pid = validatePid(args.pid);
|
|
841
|
+
const address = requireString(args.address, "address");
|
|
842
|
+
const size = requirePositiveNumber(args.size, "size");
|
|
843
|
+
const outputPath = requireString(args.outputPath, "outputPath");
|
|
844
|
+
if (/^[/\\]/.test(outputPath) || /\.\./.test(outputPath) || /^[A-Za-z]:/.test(outputPath)) throw new Error("outputPath must be a relative path without parent directory traversal or drive letters");
|
|
845
|
+
const result = await this.memoryManager.dumpMemoryRegion(pid, address, size, outputPath);
|
|
846
|
+
return { content: [{
|
|
847
|
+
type: "text",
|
|
848
|
+
text: JSON.stringify(result, null, 2)
|
|
849
|
+
}] };
|
|
850
|
+
} catch (error) {
|
|
851
|
+
logger.error("Memory dump region failed:", error);
|
|
852
|
+
return { content: [{
|
|
853
|
+
type: "text",
|
|
854
|
+
text: JSON.stringify({
|
|
855
|
+
success: false,
|
|
856
|
+
error: error instanceof Error ? error.message : String(error)
|
|
857
|
+
}, null, 2)
|
|
858
|
+
}] };
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
async handleMemoryListRegions(args) {
|
|
862
|
+
try {
|
|
863
|
+
const pid = validatePid(args.pid);
|
|
864
|
+
const result = await this.memoryManager.enumerateRegions(pid);
|
|
865
|
+
return { content: [{
|
|
866
|
+
type: "text",
|
|
867
|
+
text: JSON.stringify(result, null, 2)
|
|
868
|
+
}] };
|
|
869
|
+
} catch (error) {
|
|
870
|
+
logger.error("Memory list regions failed:", error);
|
|
871
|
+
return { content: [{
|
|
872
|
+
type: "text",
|
|
873
|
+
text: JSON.stringify({
|
|
874
|
+
success: false,
|
|
875
|
+
error: error instanceof Error ? error.message : String(error)
|
|
876
|
+
}, null, 2)
|
|
877
|
+
}] };
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
async unavailableResponse(operation, pid, address, size, startedAt, reason) {
|
|
881
|
+
const errorMessage = reason ?? "Memory operations not available";
|
|
882
|
+
const diagnostics = await this.processMgmt.safeBuildMemoryDiagnostics({
|
|
883
|
+
pid,
|
|
884
|
+
address,
|
|
885
|
+
size,
|
|
886
|
+
operation,
|
|
887
|
+
error: errorMessage
|
|
888
|
+
});
|
|
889
|
+
this.processMgmt.recordMemoryAudit({
|
|
890
|
+
operation,
|
|
891
|
+
pid,
|
|
892
|
+
address,
|
|
893
|
+
size,
|
|
894
|
+
result: "failure",
|
|
895
|
+
error: errorMessage,
|
|
896
|
+
durationMs: Date.now() - startedAt
|
|
897
|
+
});
|
|
898
|
+
return { content: [{
|
|
899
|
+
type: "text",
|
|
900
|
+
text: JSON.stringify({
|
|
901
|
+
success: false,
|
|
902
|
+
message: "Memory operations not available",
|
|
903
|
+
reason,
|
|
904
|
+
platform: this.platform,
|
|
905
|
+
requestedAddress: address,
|
|
906
|
+
requestedSize: size,
|
|
907
|
+
pid,
|
|
908
|
+
diagnostics
|
|
909
|
+
}, null, 2)
|
|
910
|
+
}] };
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
//#endregion
|
|
914
|
+
//#region src/server/domains/process/handlers/injection-handlers.ts
|
|
915
|
+
/**
|
|
916
|
+
* Injection handlers — DLL/shellcode injection, check_debug_port, enumerate_modules, electron_attach.
|
|
917
|
+
*/
|
|
918
|
+
const INJECTION_TOOLS_DISABLED_ERROR = "Injection tools are disabled by configuration. Set ENABLE_INJECTION_TOOLS=true before starting the server to enable DLL and shellcode injection.";
|
|
919
|
+
const INJECTION_TOOLS_ENABLE_GUIDANCE = "Set ENABLE_INJECTION_TOOLS=true before starting the server.";
|
|
920
|
+
const INJECTION_TOOLS_SECURITY_NOTICE = "Injection tools can destabilize target processes; review impact before use.";
|
|
921
|
+
function buildInjectionDisabledPayload() {
|
|
922
|
+
return {
|
|
923
|
+
success: false,
|
|
924
|
+
error: INJECTION_TOOLS_DISABLED_ERROR,
|
|
925
|
+
howToEnable: INJECTION_TOOLS_ENABLE_GUIDANCE,
|
|
926
|
+
securityNotice: INJECTION_TOOLS_SECURITY_NOTICE
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
function getOptionalPid(value) {
|
|
930
|
+
const pid = Number(value);
|
|
931
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
932
|
+
}
|
|
933
|
+
function formatUnknownError(error) {
|
|
934
|
+
if (error instanceof Error) return error.message;
|
|
935
|
+
if (typeof error === "object" && error !== null) try {
|
|
936
|
+
return JSON.stringify(error, null, 2);
|
|
937
|
+
} catch {
|
|
938
|
+
return String(error);
|
|
939
|
+
}
|
|
940
|
+
return String(error);
|
|
941
|
+
}
|
|
942
|
+
function getOptionalString(value) {
|
|
943
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
944
|
+
}
|
|
945
|
+
function getShellcodeSize(shellcode, encoding) {
|
|
946
|
+
if (encoding === "hex") {
|
|
947
|
+
const normalized = shellcode.replace(/\s+/g, "");
|
|
948
|
+
return Math.ceil(normalized.length / 2);
|
|
949
|
+
}
|
|
950
|
+
return Buffer.from(shellcode, "base64").length;
|
|
951
|
+
}
|
|
952
|
+
const ELECTRON_ATTACH_CONNECT_TIMEOUT_MS = Number(process.env.JSHOOK_ELECTRON_ATTACH_CONNECT_TIMEOUT_MS) || 5e3;
|
|
953
|
+
async function connectElectronBrowserCompatible(browserWSEndpoint) {
|
|
954
|
+
const { default: puppeteer } = await import("rebrowser-puppeteer-core");
|
|
955
|
+
try {
|
|
956
|
+
return await new Promise((resolve, reject) => {
|
|
957
|
+
let settled = false;
|
|
958
|
+
const timer = setTimeout(() => {
|
|
959
|
+
settled = true;
|
|
960
|
+
reject(/* @__PURE__ */ new Error(`Timed out after ${ELECTRON_ATTACH_CONNECT_TIMEOUT_MS}ms while connecting to Electron browser endpoint ${browserWSEndpoint}.`));
|
|
961
|
+
}, ELECTRON_ATTACH_CONNECT_TIMEOUT_MS);
|
|
962
|
+
puppeteer.connect({
|
|
963
|
+
browserWSEndpoint,
|
|
964
|
+
defaultViewport: null
|
|
965
|
+
}).then(async (browser) => {
|
|
966
|
+
if (settled) {
|
|
967
|
+
try {
|
|
968
|
+
await browser.disconnect();
|
|
969
|
+
} catch {}
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
settled = true;
|
|
973
|
+
clearTimeout(timer);
|
|
974
|
+
resolve(browser);
|
|
975
|
+
}).catch((error) => {
|
|
976
|
+
if (settled) return;
|
|
977
|
+
settled = true;
|
|
978
|
+
clearTimeout(timer);
|
|
979
|
+
reject(error);
|
|
980
|
+
});
|
|
981
|
+
});
|
|
982
|
+
} catch (primaryError) {
|
|
983
|
+
try {
|
|
984
|
+
return await connectPlaywrightCdpFallback(browserWSEndpoint, ELECTRON_ATTACH_CONNECT_TIMEOUT_MS);
|
|
985
|
+
} catch (fallbackError) {
|
|
986
|
+
throw new Error(`Failed to connect to Electron browser endpoint ${browserWSEndpoint} via both rebrowser-puppeteer and Playwright compatibility fallback. Primary error: ${formatUnknownError(primaryError)}. Fallback error: ${formatUnknownError(fallbackError)}.`, { cause: fallbackError });
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
var InjectionHandlers = class {
|
|
991
|
+
memoryManager;
|
|
992
|
+
processMgmt;
|
|
993
|
+
constructor(deps, processMgmt) {
|
|
994
|
+
this.memoryManager = deps.memoryManager;
|
|
995
|
+
this.processMgmt = processMgmt;
|
|
996
|
+
}
|
|
997
|
+
async handleInjectDll(args) {
|
|
998
|
+
const startedAt = Date.now();
|
|
999
|
+
if (!ENABLE_INJECTION_TOOLS) {
|
|
1000
|
+
this.processMgmt.recordMemoryAudit({
|
|
1001
|
+
operation: "inject_dll",
|
|
1002
|
+
pid: getOptionalPid(args.pid),
|
|
1003
|
+
address: getOptionalString(args.dllPath),
|
|
1004
|
+
size: null,
|
|
1005
|
+
result: "failure",
|
|
1006
|
+
error: INJECTION_TOOLS_DISABLED_ERROR,
|
|
1007
|
+
durationMs: Date.now() - startedAt
|
|
1008
|
+
});
|
|
1009
|
+
return { content: [{
|
|
1010
|
+
type: "text",
|
|
1011
|
+
text: JSON.stringify(buildInjectionDisabledPayload(), null, 2)
|
|
1012
|
+
}] };
|
|
1013
|
+
}
|
|
1014
|
+
try {
|
|
1015
|
+
const pid = validatePid(args.pid);
|
|
1016
|
+
const dllPath = requireString(args.dllPath, "dllPath");
|
|
1017
|
+
const result = await this.memoryManager.injectDll(pid, dllPath);
|
|
1018
|
+
this.processMgmt.recordMemoryAudit({
|
|
1019
|
+
operation: "inject_dll",
|
|
1020
|
+
pid,
|
|
1021
|
+
address: dllPath,
|
|
1022
|
+
size: null,
|
|
1023
|
+
result: result.success ? "success" : "failure",
|
|
1024
|
+
error: result.error,
|
|
1025
|
+
durationMs: Date.now() - startedAt
|
|
1026
|
+
});
|
|
1027
|
+
return { content: [{
|
|
1028
|
+
type: "text",
|
|
1029
|
+
text: JSON.stringify(result, null, 2)
|
|
1030
|
+
}] };
|
|
1031
|
+
} catch (error) {
|
|
1032
|
+
logger.error("DLL injection failed:", error);
|
|
1033
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1034
|
+
this.processMgmt.recordMemoryAudit({
|
|
1035
|
+
operation: "inject_dll",
|
|
1036
|
+
pid: getOptionalPid(args.pid),
|
|
1037
|
+
address: getOptionalString(args.dllPath),
|
|
1038
|
+
size: null,
|
|
1039
|
+
result: "failure",
|
|
1040
|
+
error: errorMessage,
|
|
1041
|
+
durationMs: Date.now() - startedAt
|
|
1042
|
+
});
|
|
1043
|
+
return { content: [{
|
|
1044
|
+
type: "text",
|
|
1045
|
+
text: JSON.stringify({
|
|
1046
|
+
success: false,
|
|
1047
|
+
error: errorMessage
|
|
1048
|
+
}, null, 2)
|
|
1049
|
+
}] };
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
async handleInjectShellcode(args) {
|
|
1053
|
+
const startedAt = Date.now();
|
|
1054
|
+
if (!ENABLE_INJECTION_TOOLS) {
|
|
1055
|
+
const shellcode = getOptionalString(args.shellcode);
|
|
1056
|
+
const encoding = args.encoding || "hex";
|
|
1057
|
+
this.processMgmt.recordMemoryAudit({
|
|
1058
|
+
operation: "inject_shellcode",
|
|
1059
|
+
pid: getOptionalPid(args.pid),
|
|
1060
|
+
address: null,
|
|
1061
|
+
size: shellcode ? getShellcodeSize(shellcode, encoding) : null,
|
|
1062
|
+
result: "failure",
|
|
1063
|
+
error: INJECTION_TOOLS_DISABLED_ERROR,
|
|
1064
|
+
durationMs: Date.now() - startedAt
|
|
1065
|
+
});
|
|
1066
|
+
return { content: [{
|
|
1067
|
+
type: "text",
|
|
1068
|
+
text: JSON.stringify(buildInjectionDisabledPayload(), null, 2)
|
|
1069
|
+
}] };
|
|
1070
|
+
}
|
|
1071
|
+
try {
|
|
1072
|
+
const pid = validatePid(args.pid);
|
|
1073
|
+
const shellcode = requireString(args.shellcode, "shellcode");
|
|
1074
|
+
const encoding = args.encoding || "hex";
|
|
1075
|
+
const size = getShellcodeSize(shellcode, encoding);
|
|
1076
|
+
const result = await this.memoryManager.injectShellcode(pid, shellcode, encoding);
|
|
1077
|
+
this.processMgmt.recordMemoryAudit({
|
|
1078
|
+
operation: "inject_shellcode",
|
|
1079
|
+
pid,
|
|
1080
|
+
address: null,
|
|
1081
|
+
size,
|
|
1082
|
+
result: result.success ? "success" : "failure",
|
|
1083
|
+
error: result.error,
|
|
1084
|
+
durationMs: Date.now() - startedAt
|
|
1085
|
+
});
|
|
1086
|
+
return { content: [{
|
|
1087
|
+
type: "text",
|
|
1088
|
+
text: JSON.stringify(result, null, 2)
|
|
1089
|
+
}] };
|
|
1090
|
+
} catch (error) {
|
|
1091
|
+
logger.error("Shellcode injection failed:", error);
|
|
1092
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1093
|
+
const shellcode = getOptionalString(args.shellcode);
|
|
1094
|
+
const encoding = args.encoding || "hex";
|
|
1095
|
+
this.processMgmt.recordMemoryAudit({
|
|
1096
|
+
operation: "inject_shellcode",
|
|
1097
|
+
pid: getOptionalPid(args.pid),
|
|
1098
|
+
address: null,
|
|
1099
|
+
size: shellcode ? getShellcodeSize(shellcode, encoding) : null,
|
|
1100
|
+
result: "failure",
|
|
1101
|
+
error: errorMessage,
|
|
1102
|
+
durationMs: Date.now() - startedAt
|
|
1103
|
+
});
|
|
1104
|
+
return { content: [{
|
|
1105
|
+
type: "text",
|
|
1106
|
+
text: JSON.stringify({
|
|
1107
|
+
success: false,
|
|
1108
|
+
error: errorMessage
|
|
1109
|
+
}, null, 2)
|
|
1110
|
+
}] };
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
async handleCheckDebugPort(args) {
|
|
1114
|
+
try {
|
|
1115
|
+
const pid = validatePid(args.pid);
|
|
1116
|
+
const result = await this.memoryManager.checkDebugPort(pid);
|
|
1117
|
+
return { content: [{
|
|
1118
|
+
type: "text",
|
|
1119
|
+
text: JSON.stringify({
|
|
1120
|
+
success: result.success,
|
|
1121
|
+
pid,
|
|
1122
|
+
isDebugged: result.isDebugged ?? null,
|
|
1123
|
+
error: result.error
|
|
1124
|
+
}, null, 2)
|
|
1125
|
+
}] };
|
|
1126
|
+
} catch (error) {
|
|
1127
|
+
logger.error("check_debug_port failed:", error);
|
|
1128
|
+
return { content: [{
|
|
1129
|
+
type: "text",
|
|
1130
|
+
text: JSON.stringify({
|
|
1131
|
+
success: false,
|
|
1132
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1133
|
+
}, null, 2)
|
|
1134
|
+
}] };
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
async handleEnumerateModules(args) {
|
|
1138
|
+
try {
|
|
1139
|
+
const pid = validatePid(args.pid);
|
|
1140
|
+
const result = await this.memoryManager.enumerateModules(pid);
|
|
1141
|
+
return { content: [{
|
|
1142
|
+
type: "text",
|
|
1143
|
+
text: JSON.stringify({
|
|
1144
|
+
success: result.success,
|
|
1145
|
+
pid,
|
|
1146
|
+
moduleCount: result.modules?.length ?? 0,
|
|
1147
|
+
modules: result.modules ?? [],
|
|
1148
|
+
error: result.error
|
|
1149
|
+
}, null, 2)
|
|
1150
|
+
}] };
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
logger.error("enumerate_modules failed:", error);
|
|
1153
|
+
return { content: [{
|
|
1154
|
+
type: "text",
|
|
1155
|
+
text: JSON.stringify({
|
|
1156
|
+
success: false,
|
|
1157
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1158
|
+
}, null, 2)
|
|
1159
|
+
}] };
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
async handleElectronAttach(args) {
|
|
1163
|
+
const rawPort = args.port ?? 9229;
|
|
1164
|
+
const port = Number(rawPort);
|
|
1165
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) return { content: [{
|
|
1166
|
+
type: "text",
|
|
1167
|
+
text: JSON.stringify({
|
|
1168
|
+
success: false,
|
|
1169
|
+
error: `Invalid port: ${JSON.stringify(rawPort)}. Must be integer 1-65535.`
|
|
1170
|
+
})
|
|
1171
|
+
}] };
|
|
1172
|
+
const wsEndpointArg = args.wsEndpoint ?? "";
|
|
1173
|
+
const evaluateExpr = args.evaluate ?? "";
|
|
1174
|
+
const pageUrl = args.pageUrl ?? "";
|
|
1175
|
+
try {
|
|
1176
|
+
const baseUrl = `http://127.0.0.1:${port}`;
|
|
1177
|
+
const targets = await this.fetchCdpTargets(baseUrl);
|
|
1178
|
+
if (!Array.isArray(targets)) throw new Error("CDP target list is not an array");
|
|
1179
|
+
const filtered = pageUrl ? targets.filter((t) => t.url.includes(pageUrl)) : targets;
|
|
1180
|
+
if (!evaluateExpr) return { content: [{
|
|
1181
|
+
type: "text",
|
|
1182
|
+
text: JSON.stringify({
|
|
1183
|
+
total: targets.length,
|
|
1184
|
+
filtered: filtered.length,
|
|
1185
|
+
pages: filtered.map((t) => ({
|
|
1186
|
+
id: t.id,
|
|
1187
|
+
title: t.title,
|
|
1188
|
+
url: t.url,
|
|
1189
|
+
type: t.type,
|
|
1190
|
+
wsUrl: t.webSocketDebuggerUrl
|
|
1191
|
+
}))
|
|
1192
|
+
}, null, 2)
|
|
1193
|
+
}] };
|
|
1194
|
+
const target = filtered[0];
|
|
1195
|
+
if (!target?.webSocketDebuggerUrl) return { content: [{
|
|
1196
|
+
type: "text",
|
|
1197
|
+
text: `No matching page found (pageUrl filter: "${pageUrl}"). Available targets:\n` + targets.map((t) => ` [${t.type}] ${t.title} — ${t.url}`).join("\n")
|
|
1198
|
+
}] };
|
|
1199
|
+
const browserWsEndpoint = await this.resolveBrowserWsEndpoint(baseUrl, wsEndpointArg, target);
|
|
1200
|
+
if (!browserWsEndpoint) throw new Error("Could not determine browser WebSocket endpoint");
|
|
1201
|
+
const browser = await connectElectronBrowserCompatible(browserWsEndpoint);
|
|
1202
|
+
let evalResult;
|
|
1203
|
+
let evalError;
|
|
1204
|
+
try {
|
|
1205
|
+
const pages = await browser.pages();
|
|
1206
|
+
const matchedPage = pages.find((p) => p.url().includes(target.url)) ?? pages[0];
|
|
1207
|
+
if (!matchedPage) throw new Error("Could not get page from connected browser");
|
|
1208
|
+
const evaluated = await matchedPage.evaluate((expression) => {
|
|
1209
|
+
try {
|
|
1210
|
+
return {
|
|
1211
|
+
ok: true,
|
|
1212
|
+
result: new Function("return (" + expression + ")")()
|
|
1213
|
+
};
|
|
1214
|
+
} catch (e) {
|
|
1215
|
+
const errorLike = typeof e === "object" && e !== null ? e : {};
|
|
1216
|
+
return {
|
|
1217
|
+
ok: false,
|
|
1218
|
+
error: {
|
|
1219
|
+
name: errorLike.name || "Error",
|
|
1220
|
+
message: String(errorLike.message || e),
|
|
1221
|
+
stack: errorLike.stack ? String(errorLike.stack) : void 0
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
}, evaluateExpr);
|
|
1226
|
+
if (!evaluated?.ok) evalError = `Evaluation failed: ${evaluated?.error?.name || "Error"}: ${evaluated?.error?.message || "Unknown error"}`;
|
|
1227
|
+
else evalResult = evaluated.result;
|
|
1228
|
+
} finally {
|
|
1229
|
+
await browser.disconnect();
|
|
1230
|
+
}
|
|
1231
|
+
if (evalError) return { content: [{
|
|
1232
|
+
type: "text",
|
|
1233
|
+
text: JSON.stringify({
|
|
1234
|
+
success: false,
|
|
1235
|
+
error: evalError,
|
|
1236
|
+
target: {
|
|
1237
|
+
title: target.title,
|
|
1238
|
+
url: target.url
|
|
1239
|
+
}
|
|
1240
|
+
}, null, 2)
|
|
1241
|
+
}] };
|
|
1242
|
+
logger.info(`electron_attach: evaluated in ${target.title}`);
|
|
1243
|
+
return { content: [{
|
|
1244
|
+
type: "text",
|
|
1245
|
+
text: JSON.stringify({
|
|
1246
|
+
success: true,
|
|
1247
|
+
target: {
|
|
1248
|
+
title: target.title,
|
|
1249
|
+
url: target.url
|
|
1250
|
+
},
|
|
1251
|
+
result: evalResult
|
|
1252
|
+
}, null, 2)
|
|
1253
|
+
}] };
|
|
1254
|
+
} catch (error) {
|
|
1255
|
+
logger.error("electron_attach failed:", error);
|
|
1256
|
+
return { content: [{
|
|
1257
|
+
type: "text",
|
|
1258
|
+
text: JSON.stringify({
|
|
1259
|
+
success: false,
|
|
1260
|
+
error: formatUnknownError(error)
|
|
1261
|
+
}, null, 2)
|
|
1262
|
+
}] };
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
async fetchCdpTargets(baseUrl) {
|
|
1266
|
+
const listUrl = `${baseUrl}/json/list`;
|
|
1267
|
+
try {
|
|
1268
|
+
const resp = await fetch(listUrl);
|
|
1269
|
+
if (!resp.ok) throw new Error(`CDP list endpoint returned HTTP ${resp.status}`);
|
|
1270
|
+
return await resp.json();
|
|
1271
|
+
} catch (listError) {
|
|
1272
|
+
try {
|
|
1273
|
+
const resp = await fetch(`${baseUrl}/json`);
|
|
1274
|
+
if (!resp.ok) throw new Error(`CDP fallback endpoint returned HTTP ${resp.status}`, { cause: listError });
|
|
1275
|
+
return await resp.json();
|
|
1276
|
+
} catch (fallbackError) {
|
|
1277
|
+
const original = formatUnknownError(fallbackError || listError);
|
|
1278
|
+
throw new Error(`Cannot connect to Electron CDP at ${baseUrl}. Ensure the target app is running with a remote debugging port (for example: process_launch_debug with debugPort=${baseUrl.split(":").pop()}), then retry electron_attach. Original error: ${original}`, { cause: fallbackError });
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
async resolveBrowserWsEndpoint(baseUrl, wsEndpointArg, target) {
|
|
1283
|
+
if (wsEndpointArg) return wsEndpointArg;
|
|
1284
|
+
try {
|
|
1285
|
+
const versionResp = await fetch(`${baseUrl}/json/version`);
|
|
1286
|
+
if (versionResp.ok) {
|
|
1287
|
+
const versionData = await versionResp.json();
|
|
1288
|
+
if (versionData.webSocketDebuggerUrl) return versionData.webSocketDebuggerUrl;
|
|
1289
|
+
}
|
|
1290
|
+
} catch {}
|
|
1291
|
+
if (target.webSocketDebuggerUrl) return target.webSocketDebuggerUrl.replace(/\/devtools\/page\/[^/]+$/, "").replace("/devtools/page", "/devtools/browser");
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
//#endregion
|
|
1295
|
+
//#region src/server/domains/process/handlers.impl.ts
|
|
1296
|
+
/**
|
|
1297
|
+
* Process tool handlers — composition facade.
|
|
1298
|
+
*
|
|
1299
|
+
* Delegates to three sub-handler modules:
|
|
1300
|
+
* - ProcessManagementHandlers: process find/get/windows/kill/debug launch
|
|
1301
|
+
* - MemoryOperationHandlers: memory read/write/scan/audit/protection/dump/regions
|
|
1302
|
+
* - InjectionHandlers: DLL/shellcode injection, check_debug_port, enumerate_modules, electron_attach
|
|
1303
|
+
*/
|
|
1304
|
+
function createDeps() {
|
|
1305
|
+
const processManager = new UnifiedProcessManager();
|
|
1306
|
+
const memoryManager = new MemoryManager();
|
|
1307
|
+
const platform = processManager.getPlatform();
|
|
1308
|
+
return {
|
|
1309
|
+
processManager,
|
|
1310
|
+
memoryManager,
|
|
1311
|
+
auditTrail: new MemoryAuditTrail(),
|
|
1312
|
+
platform
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* ProcessHandlersBase — backward-compatible class for tests.
|
|
1317
|
+
* Exposes process management + memory operation methods.
|
|
1318
|
+
* Matches the old ProcessHandlersBase which extended ProcessHandlersCore
|
|
1319
|
+
* and added all memory handlers.
|
|
1320
|
+
*/
|
|
1321
|
+
var ProcessHandlersBase = class {
|
|
1322
|
+
processMgmt;
|
|
1323
|
+
memoryOps;
|
|
1324
|
+
_deps;
|
|
1325
|
+
buildMemoryDiagnostics;
|
|
1326
|
+
safeBuildMemoryDiagnostics;
|
|
1327
|
+
recordMemoryAudit;
|
|
1328
|
+
constructor() {
|
|
1329
|
+
this._deps = createDeps();
|
|
1330
|
+
logger.info(`ProcessToolHandlers initialized for platform: ${this._deps.platform}`);
|
|
1331
|
+
this.processMgmt = new ProcessManagementHandlers(this._deps);
|
|
1332
|
+
this.memoryOps = new MemoryOperationHandlers(this._deps, this.processMgmt);
|
|
1333
|
+
this.buildMemoryDiagnostics = this.processMgmt.buildMemoryDiagnostics.bind(this.processMgmt);
|
|
1334
|
+
this.safeBuildMemoryDiagnostics = this.processMgmt.safeBuildMemoryDiagnostics.bind(this.processMgmt);
|
|
1335
|
+
this.recordMemoryAudit = this.processMgmt.recordMemoryAudit.bind(this.processMgmt);
|
|
1336
|
+
}
|
|
1337
|
+
async handleProcessWindows(args) {
|
|
1338
|
+
return this.processMgmt.handleProcessWindows(args);
|
|
1339
|
+
}
|
|
1340
|
+
async handleProcessCheckDebugPort(args) {
|
|
1341
|
+
return this.processMgmt.handleProcessCheckDebugPort(args);
|
|
1342
|
+
}
|
|
1343
|
+
async handleProcessLaunchDebug(args) {
|
|
1344
|
+
return this.processMgmt.handleProcessLaunchDebug(args);
|
|
1345
|
+
}
|
|
1346
|
+
async handleMemoryRead(args) {
|
|
1347
|
+
return this.memoryOps.handleMemoryRead(args);
|
|
1348
|
+
}
|
|
1349
|
+
async handleMemoryWrite(args) {
|
|
1350
|
+
return this.memoryOps.handleMemoryWrite(args);
|
|
1351
|
+
}
|
|
1352
|
+
async handleMemoryScan(args) {
|
|
1353
|
+
return this.memoryOps.handleMemoryScan(args);
|
|
1354
|
+
}
|
|
1355
|
+
async handleMemoryAuditExport(args) {
|
|
1356
|
+
return this.memoryOps.handleMemoryAuditExport(args);
|
|
1357
|
+
}
|
|
1358
|
+
async handleMemoryCheckProtection(args) {
|
|
1359
|
+
return this.memoryOps.handleMemoryCheckProtection(args);
|
|
1360
|
+
}
|
|
1361
|
+
async handleMemoryScanFiltered(args) {
|
|
1362
|
+
return this.memoryOps.handleMemoryScanFiltered(args);
|
|
1363
|
+
}
|
|
1364
|
+
async handleMemoryBatchWrite(args) {
|
|
1365
|
+
return this.memoryOps.handleMemoryBatchWrite(args);
|
|
1366
|
+
}
|
|
1367
|
+
async handleMemoryDumpRegion(args) {
|
|
1368
|
+
return this.memoryOps.handleMemoryDumpRegion(args);
|
|
1369
|
+
}
|
|
1370
|
+
async handleMemoryListRegions(args) {
|
|
1371
|
+
return this.memoryOps.handleMemoryListRegions(args);
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
/**
|
|
1375
|
+
* ProcessToolHandlers — main facade class used by the manifest.
|
|
1376
|
+
* Adds injection handlers on top of ProcessHandlersBase.
|
|
1377
|
+
*/
|
|
1378
|
+
var ProcessToolHandlers = class extends ProcessHandlersBase {
|
|
1379
|
+
injection;
|
|
1380
|
+
constructor() {
|
|
1381
|
+
super();
|
|
1382
|
+
this.injection = new InjectionHandlers(this._deps, this.processMgmt);
|
|
1383
|
+
}
|
|
1384
|
+
async handleInjectDll(args) {
|
|
1385
|
+
return this.injection.handleInjectDll(args);
|
|
1386
|
+
}
|
|
1387
|
+
async handleInjectShellcode(args) {
|
|
1388
|
+
return this.injection.handleInjectShellcode(args);
|
|
1389
|
+
}
|
|
1390
|
+
async handleCheckDebugPort(args) {
|
|
1391
|
+
return this.injection.handleCheckDebugPort(args);
|
|
1392
|
+
}
|
|
1393
|
+
async handleEnumerateModules(args) {
|
|
1394
|
+
return this.injection.handleEnumerateModules(args);
|
|
1395
|
+
}
|
|
1396
|
+
async handleElectronAttach(args) {
|
|
1397
|
+
return this.injection.handleElectronAttach(args);
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
//#endregion
|
|
1401
|
+
export { ProcessToolHandlers };
|