@jshookmcp/jshook 0.1.5 → 0.1.6
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/LICENSE +661 -661
- package/README.md +72 -40
- package/README.zh.md +77 -40
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +13 -1
- package/dist/modules/analyzer/IntelligentAnalyzer.js +19 -11
- package/dist/modules/browser/BrowserModeManager.d.ts +5 -0
- package/dist/modules/browser/BrowserModeManager.js +96 -10
- package/dist/modules/browser/CamoufoxBrowserManager.d.ts +4 -0
- package/dist/modules/browser/CamoufoxBrowserManager.js +64 -3
- package/dist/modules/browser/TabRegistry.js +3 -2
- package/dist/modules/browser/UnifiedBrowserManager.d.ts +5 -0
- package/dist/modules/browser/UnifiedBrowserManager.js +62 -9
- package/dist/modules/captcha/AICaptchaDetector.js +185 -185
- package/dist/modules/debugger/DebuggerSessionManager.d.ts +4 -0
- package/dist/modules/debugger/DebuggerSessionManager.js +29 -19
- package/dist/modules/debugger/ScriptManager.impl.class.d.ts +4 -0
- package/dist/modules/debugger/ScriptManager.impl.class.js +46 -21
- package/dist/modules/emulator/EnvironmentEmulator.js +2 -2
- package/dist/modules/monitor/NetworkMonitor.impl.d.ts +1 -0
- package/dist/modules/monitor/NetworkMonitor.impl.js +22 -15
- package/dist/modules/monitor/PerformanceMonitor.js +64 -32
- package/dist/modules/process/LinuxProcessManager.d.ts +3 -1
- package/dist/modules/process/LinuxProcessManager.js +7 -3
- package/dist/modules/process/MacProcessManager.d.ts +3 -1
- package/dist/modules/process/MacProcessManager.js +32 -28
- package/dist/modules/process/ProcessManager.impl.d.ts +5 -1
- package/dist/modules/process/ProcessManager.impl.js +54 -13
- package/dist/modules/process/index.d.ts +3 -1
- package/dist/modules/process/index.js +2 -2
- package/dist/modules/process/memory/AuditTrail.d.ts +25 -0
- package/dist/modules/process/memory/AuditTrail.js +44 -0
- package/dist/modules/process/memory/availability.js +49 -49
- package/dist/modules/process/memory/injector.js +185 -185
- package/dist/modules/process/memory/linux/mapsParser.d.ts +16 -0
- package/dist/modules/process/memory/linux/mapsParser.js +28 -0
- package/dist/modules/process/memory/reader.js +50 -50
- package/dist/modules/process/memory/regions.enumerate.js +45 -1
- package/dist/modules/process/memory/regions.protection.js +48 -2
- package/dist/modules/process/memory/scanner.d.ts +4 -1
- package/dist/modules/process/memory/scanner.js +383 -182
- package/dist/modules/process/memory/writer.js +54 -54
- package/dist/native/NativeMemoryManager.impl.d.ts +4 -0
- package/dist/native/NativeMemoryManager.impl.js +72 -24
- package/dist/native/NativeMemoryManager.utils.d.ts +1 -0
- package/dist/native/NativeMemoryManager.utils.js +44 -1
- package/dist/native/scripts/linux/enum-windows.sh +12 -12
- package/dist/native/scripts/macos/enum-windows.applescript +22 -22
- package/dist/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
- package/dist/native/scripts/windows/enum-windows.ps1 +44 -44
- package/dist/native/scripts/windows/inject-dll.ps1 +21 -21
- package/dist/server/MCPServer.search.d.ts +3 -0
- package/dist/server/MCPServer.search.js +21 -2
- package/dist/server/ToolCallContextGuard.d.ts +2 -0
- package/dist/server/ToolCallContextGuard.js +29 -14
- package/dist/server/ToolSearch.js +11 -5
- package/dist/server/domains/browser/definitions.tools.page-core.js +53 -53
- package/dist/server/domains/browser/definitions.tools.runtime.js +40 -40
- package/dist/server/domains/browser/definitions.tools.security.js +76 -76
- package/dist/server/domains/browser/handlers/tab-workflow.js +6 -4
- package/dist/server/domains/maintenance/handlers.extensions.js +46 -26
- package/dist/server/domains/process/definitions.js +20 -7
- package/dist/server/domains/process/handlers.impl.core.runtime.base.d.ts +35 -0
- package/dist/server/domains/process/handlers.impl.core.runtime.base.js +107 -1
- package/dist/server/domains/process/handlers.impl.core.runtime.inject.js +111 -2
- package/dist/server/domains/process/handlers.impl.core.runtime.memory.d.ts +9 -0
- package/dist/server/domains/process/handlers.impl.core.runtime.memory.js +282 -31
- package/dist/server/domains/process/manifest.js +1 -0
- package/dist/server/domains/transform/handlers.impl.transform-base.js +102 -102
- package/dist/server/domains/workflow/handlers.impl.workflow-api.js +14 -4
- package/dist/server/domains/workflow/handlers.impl.workflow-base.js +51 -51
- package/dist/server/registry/discovery.js +17 -12
- package/dist/server/registry/index.js +10 -2
- package/dist/utils/TokenBudgetManager.d.ts +1 -0
- package/dist/utils/TokenBudgetManager.js +22 -0
- package/package.json +5 -1
- package/src/native/scripts/linux/enum-windows.sh +12 -12
- package/src/native/scripts/macos/enum-windows.applescript +22 -22
- package/src/native/scripts/windows/enum-windows-by-class.ps1 +51 -51
- package/src/native/scripts/windows/enum-windows.ps1 +44 -44
- package/src/native/scripts/windows/inject-dll.ps1 +21 -21
|
@@ -233,28 +233,38 @@ async function rewriteLocalExtensionSdkDependency(installDir) {
|
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
async function findRegistryEntryBySlug(registryBase, slug) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
236
|
+
const [workflowResult, pluginResult] = await Promise.allSettled([
|
|
237
|
+
fetchJson(`${registryBase}/workflows.index.json`, { cacheKey: 'workflows' }),
|
|
238
|
+
fetchJson(`${registryBase}/plugins.index.json`, { cacheKey: 'plugins' }),
|
|
239
|
+
]);
|
|
240
|
+
if (workflowResult.status === 'fulfilled') {
|
|
241
|
+
const workflows = Array.isArray(workflowResult.value.data.workflows)
|
|
242
|
+
? workflowResult.value.data.workflows
|
|
243
|
+
: [];
|
|
244
|
+
const workflowEntry = workflows.find((item) => item.slug === slug);
|
|
240
245
|
if (workflowEntry) {
|
|
241
246
|
return { entry: workflowEntry, kind: 'workflow' };
|
|
242
247
|
}
|
|
243
248
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const pluginIndex = await fetchJson(`${registryBase}/plugins.index.json`, { cacheKey: 'plugins' });
|
|
250
|
-
const pluginEntry = pluginIndex.data.plugins.find((item) => item.slug === slug);
|
|
249
|
+
if (pluginResult.status === 'fulfilled') {
|
|
250
|
+
const plugins = Array.isArray(pluginResult.value.data.plugins)
|
|
251
|
+
? pluginResult.value.data.plugins
|
|
252
|
+
: [];
|
|
253
|
+
const pluginEntry = plugins.find((item) => item.slug === slug);
|
|
251
254
|
if (pluginEntry) {
|
|
252
255
|
return { entry: pluginEntry, kind: 'plugin' };
|
|
253
256
|
}
|
|
254
257
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
+
const workflowFetchError = workflowResult.status === 'rejected'
|
|
259
|
+
? workflowResult.reason instanceof Error
|
|
260
|
+
? workflowResult.reason
|
|
261
|
+
: new Error(String(workflowResult.reason))
|
|
262
|
+
: undefined;
|
|
263
|
+
const pluginFetchError = pluginResult.status === 'rejected'
|
|
264
|
+
? pluginResult.reason instanceof Error
|
|
265
|
+
? pluginResult.reason
|
|
266
|
+
: new Error(String(pluginResult.reason))
|
|
267
|
+
: undefined;
|
|
258
268
|
if (workflowFetchError && pluginFetchError) {
|
|
259
269
|
throw new Error(`Failed to resolve extension slug "${slug}": workflow registry error: ${workflowFetchError.message}; plugin registry error: ${pluginFetchError.message}`);
|
|
260
270
|
}
|
|
@@ -298,9 +308,19 @@ export class ExtensionManagementHandlers {
|
|
|
298
308
|
const showWorkflows = kind === 'all' || kind === 'workflow';
|
|
299
309
|
const result = { success: true };
|
|
300
310
|
let stale = false;
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
311
|
+
const pluginPromise = showPlugins
|
|
312
|
+
? fetchJson(`${registryBase}/plugins.index.json`, { cacheKey: 'plugins' })
|
|
313
|
+
: undefined;
|
|
314
|
+
const workflowPromise = showWorkflows
|
|
315
|
+
? fetchJson(`${registryBase}/workflows.index.json`, { cacheKey: 'workflows' })
|
|
316
|
+
: undefined;
|
|
317
|
+
const [pluginIndex, workflowIndex] = await Promise.all([
|
|
318
|
+
pluginPromise ?? Promise.resolve(undefined),
|
|
319
|
+
workflowPromise ?? Promise.resolve(undefined),
|
|
320
|
+
]);
|
|
321
|
+
if (pluginIndex) {
|
|
322
|
+
const plugins = Array.isArray(pluginIndex.data.plugins) ? pluginIndex.data.plugins : [];
|
|
323
|
+
result.plugins = plugins.map((p) => ({
|
|
304
324
|
slug: p.slug,
|
|
305
325
|
id: p.id,
|
|
306
326
|
name: p.meta.name,
|
|
@@ -310,13 +330,13 @@ export class ExtensionManagementHandlers {
|
|
|
310
330
|
commit: p.source.commit,
|
|
311
331
|
entry: p.source.entry,
|
|
312
332
|
}));
|
|
313
|
-
result.pluginCount =
|
|
314
|
-
result.pluginSource =
|
|
315
|
-
stale = stale ||
|
|
333
|
+
result.pluginCount = plugins.length;
|
|
334
|
+
result.pluginSource = pluginIndex.source;
|
|
335
|
+
stale = stale || pluginIndex.stale;
|
|
316
336
|
}
|
|
317
|
-
if (
|
|
318
|
-
const
|
|
319
|
-
result.workflows =
|
|
337
|
+
if (workflowIndex) {
|
|
338
|
+
const workflows = Array.isArray(workflowIndex.data.workflows) ? workflowIndex.data.workflows : [];
|
|
339
|
+
result.workflows = workflows.map((w) => ({
|
|
320
340
|
slug: w.slug,
|
|
321
341
|
id: w.id,
|
|
322
342
|
name: w.meta.name,
|
|
@@ -326,9 +346,9 @@ export class ExtensionManagementHandlers {
|
|
|
326
346
|
commit: w.source.commit,
|
|
327
347
|
entry: w.source.entry,
|
|
328
348
|
}));
|
|
329
|
-
result.workflowCount =
|
|
330
|
-
result.workflowSource =
|
|
331
|
-
stale = stale ||
|
|
349
|
+
result.workflowCount = workflows.length;
|
|
350
|
+
result.workflowSource = workflowIndex.source;
|
|
351
|
+
stale = stale || workflowIndex.stale;
|
|
332
352
|
}
|
|
333
353
|
if (stale) {
|
|
334
354
|
result.stale = true;
|
|
@@ -121,7 +121,7 @@ export const processToolDefinitions = [
|
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
name: 'memory_read',
|
|
124
|
-
description: 'Read memory from a process at a specific address.
|
|
124
|
+
description: 'Read memory from a process at a specific address. Failures include structured diagnostics for permissions, region checks, and ASLR guidance.',
|
|
125
125
|
inputSchema: {
|
|
126
126
|
type: 'object',
|
|
127
127
|
properties: {
|
|
@@ -143,7 +143,7 @@ export const processToolDefinitions = [
|
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
name: 'memory_write',
|
|
146
|
-
description: 'Write data to process memory at a specific address.
|
|
146
|
+
description: 'Write data to process memory at a specific address. Failures include structured diagnostics for permissions, region checks, and ASLR guidance.',
|
|
147
147
|
inputSchema: {
|
|
148
148
|
type: 'object',
|
|
149
149
|
properties: {
|
|
@@ -171,7 +171,7 @@ export const processToolDefinitions = [
|
|
|
171
171
|
},
|
|
172
172
|
{
|
|
173
173
|
name: 'memory_scan',
|
|
174
|
-
description: 'Scan process memory for a pattern or value.
|
|
174
|
+
description: 'Scan process memory for a pattern or value. Failures include structured diagnostics for permissions, region checks, and ASLR guidance.',
|
|
175
175
|
inputSchema: {
|
|
176
176
|
type: 'object',
|
|
177
177
|
properties: {
|
|
@@ -325,9 +325,22 @@ export const processToolDefinitions = [
|
|
|
325
325
|
required: ['pid'],
|
|
326
326
|
},
|
|
327
327
|
},
|
|
328
|
+
{
|
|
329
|
+
name: 'memory_audit_export',
|
|
330
|
+
description: 'Export the in-memory audit trail for memory operations as JSON. Supports clear=true to flush the buffer after export.',
|
|
331
|
+
inputSchema: {
|
|
332
|
+
type: 'object',
|
|
333
|
+
properties: {
|
|
334
|
+
clear: {
|
|
335
|
+
type: 'boolean',
|
|
336
|
+
description: 'Clear audit trail after export',
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
},
|
|
328
341
|
{
|
|
329
342
|
name: 'inject_dll',
|
|
330
|
-
description: 'Inject a DLL into a target process using CreateRemoteThread + LoadLibraryA. Requires administrator privileges.',
|
|
343
|
+
description: 'Inject a DLL into a target process using CreateRemoteThread + LoadLibraryA. Disabled by default; set ENABLE_INJECTION_TOOLS=true to enable. Requires administrator privileges.',
|
|
331
344
|
inputSchema: {
|
|
332
345
|
type: 'object',
|
|
333
346
|
properties: {
|
|
@@ -345,7 +358,7 @@ export const processToolDefinitions = [
|
|
|
345
358
|
},
|
|
346
359
|
{
|
|
347
360
|
name: 'module_inject_dll',
|
|
348
|
-
description: 'Alias of inject_dll.
|
|
361
|
+
description: 'Alias of inject_dll. Disabled by default; set ENABLE_INJECTION_TOOLS=true to enable.',
|
|
349
362
|
inputSchema: {
|
|
350
363
|
type: 'object',
|
|
351
364
|
properties: {
|
|
@@ -363,7 +376,7 @@ export const processToolDefinitions = [
|
|
|
363
376
|
},
|
|
364
377
|
{
|
|
365
378
|
name: 'inject_shellcode',
|
|
366
|
-
description: 'Inject and execute shellcode in a target process.
|
|
379
|
+
description: 'Inject and execute shellcode in a target process. Accepts hex or base64. Disabled by default; set ENABLE_INJECTION_TOOLS=true to enable.',
|
|
367
380
|
inputSchema: {
|
|
368
381
|
type: 'object',
|
|
369
382
|
properties: {
|
|
@@ -387,7 +400,7 @@ export const processToolDefinitions = [
|
|
|
387
400
|
},
|
|
388
401
|
{
|
|
389
402
|
name: 'module_inject_shellcode',
|
|
390
|
-
description: 'Alias of inject_shellcode.
|
|
403
|
+
description: 'Alias of inject_shellcode. Disabled by default; set ENABLE_INJECTION_TOOLS=true to enable.',
|
|
391
404
|
inputSchema: {
|
|
392
405
|
type: 'object',
|
|
393
406
|
properties: {
|
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
import { UnifiedProcessManager, MemoryManager } from '../../domains/shared/modules.js';
|
|
2
|
+
import { MemoryAuditTrail } from '../../../modules/process/memory/AuditTrail.js';
|
|
3
|
+
interface MemoryDiagnosticsInput {
|
|
4
|
+
pid?: number;
|
|
5
|
+
address?: string;
|
|
6
|
+
size?: number;
|
|
7
|
+
operation: string;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
interface MemoryDiagnostics {
|
|
11
|
+
permission: {
|
|
12
|
+
available: boolean;
|
|
13
|
+
reason?: string;
|
|
14
|
+
platform: string;
|
|
15
|
+
};
|
|
16
|
+
process: {
|
|
17
|
+
exists: boolean | null;
|
|
18
|
+
pid: number | null;
|
|
19
|
+
name: string | null;
|
|
20
|
+
};
|
|
21
|
+
address: {
|
|
22
|
+
queried: boolean;
|
|
23
|
+
valid: boolean | null;
|
|
24
|
+
protection: string | null;
|
|
25
|
+
regionStart: string | null;
|
|
26
|
+
regionSize: number | null;
|
|
27
|
+
};
|
|
28
|
+
aslr: {
|
|
29
|
+
heuristic: true;
|
|
30
|
+
note: string;
|
|
31
|
+
};
|
|
32
|
+
recommendedActions: string[];
|
|
33
|
+
}
|
|
2
34
|
export declare function validatePid(value: unknown): number;
|
|
3
35
|
export declare function requireString(value: unknown, name: string): string;
|
|
4
36
|
export declare function requirePositiveNumber(value: unknown, name: string): number;
|
|
@@ -6,7 +38,9 @@ export declare class ProcessToolHandlersBase {
|
|
|
6
38
|
protected processManager: UnifiedProcessManager;
|
|
7
39
|
protected memoryManager: MemoryManager;
|
|
8
40
|
protected platform: string;
|
|
41
|
+
protected auditTrail: MemoryAuditTrail;
|
|
9
42
|
constructor();
|
|
43
|
+
protected buildMemoryDiagnostics(input: MemoryDiagnosticsInput): Promise<MemoryDiagnostics>;
|
|
10
44
|
handleProcessFind(args: Record<string, unknown>): Promise<{
|
|
11
45
|
content: {
|
|
12
46
|
type: string;
|
|
@@ -50,3 +84,4 @@ export declare class ProcessToolHandlersBase {
|
|
|
50
84
|
}[];
|
|
51
85
|
}>;
|
|
52
86
|
}
|
|
87
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { UnifiedProcessManager, MemoryManager } from '../../domains/shared/modules.js';
|
|
2
|
+
import { MemoryAuditTrail } from '../../../modules/process/memory/AuditTrail.js';
|
|
2
3
|
import { logger } from '../../../utils/logger.js';
|
|
3
4
|
export function validatePid(value) {
|
|
4
5
|
const n = Number(value);
|
|
@@ -22,12 +23,115 @@ export class ProcessToolHandlersBase {
|
|
|
22
23
|
processManager;
|
|
23
24
|
memoryManager;
|
|
24
25
|
platform;
|
|
26
|
+
auditTrail = new MemoryAuditTrail();
|
|
25
27
|
constructor() {
|
|
26
28
|
this.processManager = new UnifiedProcessManager();
|
|
27
29
|
this.memoryManager = new MemoryManager();
|
|
28
30
|
this.platform = this.processManager.getPlatform();
|
|
29
31
|
logger.info(`ProcessToolHandlers initialized for platform: ${this.platform}`);
|
|
30
32
|
}
|
|
33
|
+
async buildMemoryDiagnostics(input) {
|
|
34
|
+
const recommendedActions = new Set();
|
|
35
|
+
const permission = await this.memoryManager.checkAvailability();
|
|
36
|
+
if (!permission.available) {
|
|
37
|
+
recommendedActions.add('Run as administrator');
|
|
38
|
+
}
|
|
39
|
+
let processInfo = null;
|
|
40
|
+
if (input.pid != null) {
|
|
41
|
+
try {
|
|
42
|
+
const resolvedProcess = await this.processManager.getProcessByPid(input.pid);
|
|
43
|
+
processInfo = resolvedProcess
|
|
44
|
+
? {
|
|
45
|
+
pid: resolvedProcess.pid,
|
|
46
|
+
name: resolvedProcess.name,
|
|
47
|
+
executablePath: resolvedProcess.executablePath,
|
|
48
|
+
windowTitle: resolvedProcess.windowTitle,
|
|
49
|
+
windowHandle: resolvedProcess.windowHandle,
|
|
50
|
+
memoryUsage: resolvedProcess.memoryUsage,
|
|
51
|
+
}
|
|
52
|
+
: null;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
processInfo = null;
|
|
56
|
+
}
|
|
57
|
+
if (!processInfo) {
|
|
58
|
+
recommendedActions.add('Check if process is still running');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
let protectionInfo = null;
|
|
62
|
+
let protectionQueryFailed = false;
|
|
63
|
+
if (input.pid != null && input.address) {
|
|
64
|
+
try {
|
|
65
|
+
protectionInfo = await this.memoryManager.checkMemoryProtection(input.pid, input.address);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
protectionQueryFailed = true;
|
|
69
|
+
}
|
|
70
|
+
if (protectionQueryFailed || protectionInfo?.success === false) {
|
|
71
|
+
recommendedActions.add('Verify address is within valid memory region');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (input.size != null && protectionInfo?.regionSize != null && input.size > protectionInfo.regionSize) {
|
|
75
|
+
recommendedActions.add('Reduce the requested size to fit the target memory region');
|
|
76
|
+
}
|
|
77
|
+
if (input.operation === 'memory_read' && protectionInfo?.success && protectionInfo.isReadable === false) {
|
|
78
|
+
recommendedActions.add('Ensure target memory region is readable');
|
|
79
|
+
}
|
|
80
|
+
if (input.operation === 'memory_write' && protectionInfo?.success && protectionInfo.isWritable === false) {
|
|
81
|
+
recommendedActions.add('Ensure target memory region is writable');
|
|
82
|
+
}
|
|
83
|
+
let modulesEnumerated = false;
|
|
84
|
+
let moduleCount = null;
|
|
85
|
+
if (input.pid != null) {
|
|
86
|
+
try {
|
|
87
|
+
const modulesResult = await this.memoryManager.enumerateModules(input.pid);
|
|
88
|
+
modulesEnumerated = modulesResult.success;
|
|
89
|
+
moduleCount = modulesResult.modules?.length ?? null;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
modulesEnumerated = false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (input.pid != null && input.address) {
|
|
96
|
+
recommendedActions.add('Re-resolve the address after the process restarts because ASLR can shift module addresses');
|
|
97
|
+
}
|
|
98
|
+
const normalizedError = input.error?.toLowerCase() ?? '';
|
|
99
|
+
if (normalizedError.includes('access denied') ||
|
|
100
|
+
normalizedError.includes('permission') ||
|
|
101
|
+
normalizedError.includes('privilege') ||
|
|
102
|
+
normalizedError.includes('administrator')) {
|
|
103
|
+
recommendedActions.add('Run as administrator');
|
|
104
|
+
}
|
|
105
|
+
const aslrNote = modulesEnumerated
|
|
106
|
+
? moduleCount && moduleCount > 0
|
|
107
|
+
? `Enumerated ${moduleCount} module(s). Treat absolute addresses as session-specific because ASLR can shift module bases between launches.`
|
|
108
|
+
: 'Module enumeration succeeded but returned no modules. Absolute addresses may still change across process launches because of ASLR.'
|
|
109
|
+
: 'Module enumeration was unavailable. Assume ASLR may shift absolute addresses between launches and re-resolve addresses after restarts.';
|
|
110
|
+
return {
|
|
111
|
+
permission: {
|
|
112
|
+
available: permission.available,
|
|
113
|
+
reason: permission.reason,
|
|
114
|
+
platform: this.platform,
|
|
115
|
+
},
|
|
116
|
+
process: {
|
|
117
|
+
exists: input.pid != null ? Boolean(processInfo) : null,
|
|
118
|
+
pid: input.pid ?? null,
|
|
119
|
+
name: processInfo?.name ?? null,
|
|
120
|
+
},
|
|
121
|
+
address: {
|
|
122
|
+
queried: input.pid != null && Boolean(input.address),
|
|
123
|
+
valid: input.pid != null && input.address ? protectionInfo?.success ?? null : null,
|
|
124
|
+
protection: protectionInfo?.protection ?? null,
|
|
125
|
+
regionStart: protectionInfo?.regionStart ?? null,
|
|
126
|
+
regionSize: protectionInfo?.regionSize ?? null,
|
|
127
|
+
},
|
|
128
|
+
aslr: {
|
|
129
|
+
heuristic: true,
|
|
130
|
+
note: aslrNote,
|
|
131
|
+
},
|
|
132
|
+
recommendedActions: Array.from(recommendedActions),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
31
135
|
async handleProcessFind(args) {
|
|
32
136
|
try {
|
|
33
137
|
const pattern = requireString(args.pattern, 'pattern');
|
|
@@ -86,7 +190,9 @@ export class ProcessToolHandlersBase {
|
|
|
86
190
|
};
|
|
87
191
|
}
|
|
88
192
|
const cmdLine = await this.processManager.getProcessCommandLine(pid);
|
|
89
|
-
const debugPort = await this.processManager.checkDebugPort(pid
|
|
193
|
+
const debugPort = await this.processManager.checkDebugPort(pid, {
|
|
194
|
+
commandLine: cmdLine.commandLine,
|
|
195
|
+
});
|
|
90
196
|
return {
|
|
91
197
|
content: [
|
|
92
198
|
{
|
|
@@ -1,12 +1,67 @@
|
|
|
1
1
|
import { logger } from '../../../utils/logger.js';
|
|
2
|
+
import { ENABLE_INJECTION_TOOLS } from '../../../constants.js';
|
|
2
3
|
import { ProcessToolHandlersMemory } from '../../domains/process/handlers.impl.core.runtime.memory.js';
|
|
3
4
|
import { requireString, validatePid } from '../../domains/process/handlers.impl.core.runtime.base.js';
|
|
5
|
+
const INJECTION_TOOLS_DISABLED_ERROR = 'Injection tools are disabled by default for safety. Set ENABLE_INJECTION_TOOLS=true before starting the server to enable DLL and shellcode injection.';
|
|
6
|
+
const INJECTION_TOOLS_ENABLE_GUIDANCE = 'Set ENABLE_INJECTION_TOOLS=true before starting the server.';
|
|
7
|
+
const INJECTION_TOOLS_SECURITY_NOTICE = 'Only enable injection tools in an authorized debugging, lab, or CTF environment.';
|
|
8
|
+
function buildInjectionDisabledPayload() {
|
|
9
|
+
return {
|
|
10
|
+
success: false,
|
|
11
|
+
error: INJECTION_TOOLS_DISABLED_ERROR,
|
|
12
|
+
howToEnable: INJECTION_TOOLS_ENABLE_GUIDANCE,
|
|
13
|
+
securityNotice: INJECTION_TOOLS_SECURITY_NOTICE,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function getOptionalPid(value) {
|
|
17
|
+
const pid = Number(value);
|
|
18
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
19
|
+
}
|
|
20
|
+
function getOptionalString(value) {
|
|
21
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
22
|
+
}
|
|
23
|
+
function getShellcodeSize(shellcode, encoding) {
|
|
24
|
+
if (encoding === 'hex') {
|
|
25
|
+
const normalized = shellcode.replace(/\s+/g, '');
|
|
26
|
+
return Math.ceil(normalized.length / 2);
|
|
27
|
+
}
|
|
28
|
+
return Buffer.from(shellcode, 'base64').length;
|
|
29
|
+
}
|
|
4
30
|
export class ProcessToolHandlersRuntime extends ProcessToolHandlersMemory {
|
|
5
31
|
async handleInjectDll(args) {
|
|
32
|
+
const startedAt = Date.now();
|
|
33
|
+
if (!ENABLE_INJECTION_TOOLS) {
|
|
34
|
+
this.recordMemoryAudit({
|
|
35
|
+
operation: 'inject_dll',
|
|
36
|
+
pid: getOptionalPid(args.pid),
|
|
37
|
+
address: getOptionalString(args.dllPath),
|
|
38
|
+
size: null,
|
|
39
|
+
result: 'failure',
|
|
40
|
+
error: INJECTION_TOOLS_DISABLED_ERROR,
|
|
41
|
+
durationMs: Date.now() - startedAt,
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
content: [
|
|
45
|
+
{
|
|
46
|
+
type: 'text',
|
|
47
|
+
text: JSON.stringify(buildInjectionDisabledPayload(), null, 2),
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
6
52
|
try {
|
|
7
53
|
const pid = validatePid(args.pid);
|
|
8
54
|
const dllPath = requireString(args.dllPath, 'dllPath');
|
|
9
55
|
const result = await this.memoryManager.injectDll(pid, dllPath);
|
|
56
|
+
this.recordMemoryAudit({
|
|
57
|
+
operation: 'inject_dll',
|
|
58
|
+
pid,
|
|
59
|
+
address: dllPath,
|
|
60
|
+
size: null,
|
|
61
|
+
result: result.success ? 'success' : 'failure',
|
|
62
|
+
error: result.error,
|
|
63
|
+
durationMs: Date.now() - startedAt,
|
|
64
|
+
});
|
|
10
65
|
return {
|
|
11
66
|
content: [
|
|
12
67
|
{
|
|
@@ -18,22 +73,64 @@ export class ProcessToolHandlersRuntime extends ProcessToolHandlersMemory {
|
|
|
18
73
|
}
|
|
19
74
|
catch (error) {
|
|
20
75
|
logger.error('DLL injection failed:', error);
|
|
76
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
77
|
+
this.recordMemoryAudit({
|
|
78
|
+
operation: 'inject_dll',
|
|
79
|
+
pid: getOptionalPid(args.pid),
|
|
80
|
+
address: getOptionalString(args.dllPath),
|
|
81
|
+
size: null,
|
|
82
|
+
result: 'failure',
|
|
83
|
+
error: errorMessage,
|
|
84
|
+
durationMs: Date.now() - startedAt,
|
|
85
|
+
});
|
|
21
86
|
return {
|
|
22
87
|
content: [
|
|
23
88
|
{
|
|
24
89
|
type: 'text',
|
|
25
|
-
text: JSON.stringify({ success: false, error:
|
|
90
|
+
text: JSON.stringify({ success: false, error: errorMessage }, null, 2),
|
|
26
91
|
},
|
|
27
92
|
],
|
|
28
93
|
};
|
|
29
94
|
}
|
|
30
95
|
}
|
|
31
96
|
async handleInjectShellcode(args) {
|
|
97
|
+
const startedAt = Date.now();
|
|
98
|
+
if (!ENABLE_INJECTION_TOOLS) {
|
|
99
|
+
const shellcode = getOptionalString(args.shellcode);
|
|
100
|
+
const encoding = args.encoding || 'hex';
|
|
101
|
+
this.recordMemoryAudit({
|
|
102
|
+
operation: 'inject_shellcode',
|
|
103
|
+
pid: getOptionalPid(args.pid),
|
|
104
|
+
address: null,
|
|
105
|
+
size: shellcode ? getShellcodeSize(shellcode, encoding) : null,
|
|
106
|
+
result: 'failure',
|
|
107
|
+
error: INJECTION_TOOLS_DISABLED_ERROR,
|
|
108
|
+
durationMs: Date.now() - startedAt,
|
|
109
|
+
});
|
|
110
|
+
return {
|
|
111
|
+
content: [
|
|
112
|
+
{
|
|
113
|
+
type: 'text',
|
|
114
|
+
text: JSON.stringify(buildInjectionDisabledPayload(), null, 2),
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
32
119
|
try {
|
|
33
120
|
const pid = validatePid(args.pid);
|
|
34
121
|
const shellcode = requireString(args.shellcode, 'shellcode');
|
|
35
122
|
const encoding = args.encoding || 'hex';
|
|
123
|
+
const size = getShellcodeSize(shellcode, encoding);
|
|
36
124
|
const result = await this.memoryManager.injectShellcode(pid, shellcode, encoding);
|
|
125
|
+
this.recordMemoryAudit({
|
|
126
|
+
operation: 'inject_shellcode',
|
|
127
|
+
pid,
|
|
128
|
+
address: null,
|
|
129
|
+
size,
|
|
130
|
+
result: result.success ? 'success' : 'failure',
|
|
131
|
+
error: result.error,
|
|
132
|
+
durationMs: Date.now() - startedAt,
|
|
133
|
+
});
|
|
37
134
|
return {
|
|
38
135
|
content: [
|
|
39
136
|
{
|
|
@@ -45,11 +142,23 @@ export class ProcessToolHandlersRuntime extends ProcessToolHandlersMemory {
|
|
|
45
142
|
}
|
|
46
143
|
catch (error) {
|
|
47
144
|
logger.error('Shellcode injection failed:', error);
|
|
145
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
146
|
+
const shellcode = getOptionalString(args.shellcode);
|
|
147
|
+
const encoding = args.encoding || 'hex';
|
|
148
|
+
this.recordMemoryAudit({
|
|
149
|
+
operation: 'inject_shellcode',
|
|
150
|
+
pid: getOptionalPid(args.pid),
|
|
151
|
+
address: null,
|
|
152
|
+
size: shellcode ? getShellcodeSize(shellcode, encoding) : null,
|
|
153
|
+
result: 'failure',
|
|
154
|
+
error: errorMessage,
|
|
155
|
+
durationMs: Date.now() - startedAt,
|
|
156
|
+
});
|
|
48
157
|
return {
|
|
49
158
|
content: [
|
|
50
159
|
{
|
|
51
160
|
type: 'text',
|
|
52
|
-
text: JSON.stringify({ success: false, error:
|
|
161
|
+
text: JSON.stringify({ success: false, error: errorMessage }, null, 2),
|
|
53
162
|
},
|
|
54
163
|
],
|
|
55
164
|
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import type { AuditEntry } from '../../../modules/process/memory/AuditTrail.js';
|
|
1
2
|
import { ProcessToolHandlersBase } from '../../domains/process/handlers.impl.core.runtime.base.js';
|
|
2
3
|
export declare class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
|
|
4
|
+
private safeBuildMemoryDiagnostics;
|
|
5
|
+
protected recordMemoryAudit(entry: Omit<AuditEntry, 'timestamp' | 'user'>): void;
|
|
3
6
|
handleMemoryRead(args: Record<string, unknown>): Promise<{
|
|
4
7
|
content: {
|
|
5
8
|
type: string;
|
|
@@ -18,6 +21,12 @@ export declare class ProcessToolHandlersMemory extends ProcessToolHandlersBase {
|
|
|
18
21
|
text: string;
|
|
19
22
|
}[];
|
|
20
23
|
}>;
|
|
24
|
+
handleMemoryAuditExport(args: Record<string, unknown>): Promise<{
|
|
25
|
+
content: {
|
|
26
|
+
type: string;
|
|
27
|
+
text: string;
|
|
28
|
+
}[];
|
|
29
|
+
}>;
|
|
21
30
|
handleMemoryCheckProtection(args: Record<string, unknown>): Promise<{
|
|
22
31
|
content: {
|
|
23
32
|
type: string;
|