@jshookmcp/jshook 0.1.6 → 0.1.8
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 +145 -100
- package/README.zh.md +81 -36
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +3 -1
- package/dist/index.js +0 -0
- package/dist/modules/analyzer/QualityAnalyzer.js +1 -1
- package/dist/modules/browser/BrowserDiscovery.js +2 -2
- package/dist/modules/browser/BrowserModeManager.js +3 -3
- package/dist/modules/captcha/AICaptchaDetector.d.ts +12 -16
- package/dist/modules/captcha/AICaptchaDetector.js +229 -209
- package/dist/modules/captcha/CaptchaDetector.constants.d.ts +2 -0
- package/dist/modules/captcha/CaptchaDetector.constants.js +116 -25
- package/dist/modules/captcha/CaptchaDetector.d.ts +2 -11
- package/dist/modules/captcha/CaptchaDetector.js +102 -51
- package/dist/modules/captcha/types.d.ts +46 -0
- package/dist/modules/captcha/types.js +52 -0
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.d.ts +15 -20
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.js +66 -234
- package/dist/modules/deobfuscator/Deobfuscator.d.ts +3 -10
- package/dist/modules/deobfuscator/Deobfuscator.js +125 -404
- package/dist/modules/deobfuscator/webcrack.d.ts +13 -0
- package/dist/modules/deobfuscator/webcrack.js +164 -0
- package/dist/modules/detector/ObfuscationDetector.d.ts +6 -0
- package/dist/modules/detector/ObfuscationDetector.js +53 -2
- package/dist/modules/hook/AIHookGenerator.js +1 -1
- package/dist/modules/process/MacProcessManager.js +25 -25
- package/dist/modules/process/memory/availability.js +49 -49
- package/dist/modules/process/memory/injector.js +185 -185
- package/dist/modules/process/memory/reader.js +50 -50
- package/dist/modules/process/memory/scanner.js +165 -165
- package/dist/modules/process/memory/writer.js +55 -55
- 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/domains/analysis/definitions.js +223 -2
- package/dist/server/domains/analysis/handlers.impl.d.ts +2 -3
- package/dist/server/domains/analysis/handlers.impl.js +60 -15
- package/dist/server/domains/analysis/manifest.js +2 -5
- package/dist/server/domains/browser/definitions.tools.behavior.js +36 -24
- 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 +80 -77
- package/dist/server/domains/browser/handlers/camoufox-flow.js +0 -1
- package/dist/server/domains/browser/handlers/captcha-solver.d.ts +1 -1
- package/dist/server/domains/browser/handlers/captcha-solver.js +121 -54
- package/dist/server/domains/browser/handlers/page-navigation.js +0 -2
- package/dist/server/domains/browser/handlers.impl.d.ts +1 -1
- package/dist/server/domains/browser/handlers.impl.js +3 -3
- package/dist/server/domains/browser/manifest.js +1 -1
- package/dist/server/domains/shared/modules.d.ts +1 -0
- package/dist/server/domains/transform/handlers.impl.transform-base.js +102 -102
- package/dist/server/domains/workflow/handlers.impl.workflow-base.js +51 -51
- package/dist/types/deobfuscator.d.ts +43 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/config.js +19 -10
- package/package.json +30 -44
- package/scripts/postinstall.cjs +37 -0
- 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
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { readdir, rm, stat } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { logger } from '../../utils/logger.js';
|
|
4
|
+
const DEFAULT_OPTIONS = {
|
|
5
|
+
jsx: true,
|
|
6
|
+
mangle: false,
|
|
7
|
+
unminify: true,
|
|
8
|
+
unpack: true,
|
|
9
|
+
};
|
|
10
|
+
const MAX_BUNDLE_MODULES = 100;
|
|
11
|
+
function normalizeOptions(options) {
|
|
12
|
+
return {
|
|
13
|
+
jsx: options.jsx ?? DEFAULT_OPTIONS.jsx,
|
|
14
|
+
mangle: options.mangle ?? DEFAULT_OPTIONS.mangle,
|
|
15
|
+
unminify: options.unminify ?? DEFAULT_OPTIONS.unminify,
|
|
16
|
+
unpack: options.unpack ?? DEFAULT_OPTIONS.unpack,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function isSupportedNodeVersion() {
|
|
20
|
+
const major = Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10);
|
|
21
|
+
return Number.isFinite(major) && major >= 22;
|
|
22
|
+
}
|
|
23
|
+
function matchesRule(module, rule) {
|
|
24
|
+
const target = rule.target === 'path' ? module.path : module.code;
|
|
25
|
+
const matchType = rule.matchType ?? 'includes';
|
|
26
|
+
if (matchType === 'exact') {
|
|
27
|
+
return target === rule.pattern;
|
|
28
|
+
}
|
|
29
|
+
if (matchType === 'regex') {
|
|
30
|
+
try {
|
|
31
|
+
return new RegExp(rule.pattern, 'm').test(target);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return target.includes(rule.pattern);
|
|
38
|
+
}
|
|
39
|
+
function applyBundleMappings(bundle, mappings) {
|
|
40
|
+
const remapped = new Map();
|
|
41
|
+
if (!mappings || mappings.length === 0) {
|
|
42
|
+
return remapped;
|
|
43
|
+
}
|
|
44
|
+
for (const module of bundle.modules.values()) {
|
|
45
|
+
for (const rule of mappings) {
|
|
46
|
+
if (!rule.path || !rule.pattern) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (matchesRule(module, rule)) {
|
|
50
|
+
if (module.path !== rule.path) {
|
|
51
|
+
remapped.set(module.id, { fromPath: module.path });
|
|
52
|
+
module.path = rule.path;
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return remapped;
|
|
59
|
+
}
|
|
60
|
+
function summarizeBundle(bundle, options, remapped) {
|
|
61
|
+
const maxBundleModules = options.maxBundleModules ?? MAX_BUNDLE_MODULES;
|
|
62
|
+
const modules = Array.from(bundle.modules.values())
|
|
63
|
+
.sort((left, right) => {
|
|
64
|
+
if (left.isEntry !== right.isEntry) {
|
|
65
|
+
return left.isEntry ? -1 : 1;
|
|
66
|
+
}
|
|
67
|
+
return left.path.localeCompare(right.path);
|
|
68
|
+
})
|
|
69
|
+
.slice(0, maxBundleModules)
|
|
70
|
+
.map((module) => ({
|
|
71
|
+
id: module.id,
|
|
72
|
+
path: module.path,
|
|
73
|
+
isEntry: module.isEntry,
|
|
74
|
+
size: module.code.length,
|
|
75
|
+
code: options.includeModuleCode ? module.code : undefined,
|
|
76
|
+
mappedPathFrom: remapped.get(module.id)?.fromPath,
|
|
77
|
+
}));
|
|
78
|
+
return {
|
|
79
|
+
type: bundle.type,
|
|
80
|
+
entryId: bundle.entryId,
|
|
81
|
+
moduleCount: bundle.modules.size,
|
|
82
|
+
truncated: bundle.modules.size > maxBundleModules,
|
|
83
|
+
mappingsApplied: remapped.size,
|
|
84
|
+
modules,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
async function collectSavedArtifacts(rootDir, currentDir = rootDir) {
|
|
88
|
+
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
89
|
+
const artifacts = [];
|
|
90
|
+
for (const entry of entries) {
|
|
91
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
92
|
+
if (entry.isDirectory()) {
|
|
93
|
+
artifacts.push(...(await collectSavedArtifacts(rootDir, fullPath)));
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (!entry.isFile()) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const metadata = await stat(fullPath);
|
|
100
|
+
artifacts.push({
|
|
101
|
+
path: path.relative(rootDir, fullPath).replace(/\\/g, '/'),
|
|
102
|
+
size: metadata.size,
|
|
103
|
+
type: 'file',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return artifacts.sort((left, right) => left.path.localeCompare(right.path));
|
|
107
|
+
}
|
|
108
|
+
export async function runWebcrack(code, options) {
|
|
109
|
+
const optionsUsed = normalizeOptions(options);
|
|
110
|
+
if (!isSupportedNodeVersion()) {
|
|
111
|
+
const reason = `webcrack requires Node.js 22+; current runtime is ${process.versions.node}`;
|
|
112
|
+
logger.warn(reason);
|
|
113
|
+
return {
|
|
114
|
+
applied: false,
|
|
115
|
+
code,
|
|
116
|
+
optionsUsed,
|
|
117
|
+
reason,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const { webcrack } = (await import('webcrack'));
|
|
122
|
+
const result = await webcrack(code, {
|
|
123
|
+
jsx: optionsUsed.jsx,
|
|
124
|
+
unpack: optionsUsed.unpack,
|
|
125
|
+
deobfuscate: true,
|
|
126
|
+
unminify: optionsUsed.unminify,
|
|
127
|
+
mangle: optionsUsed.mangle,
|
|
128
|
+
});
|
|
129
|
+
const remapped = result.bundle ? applyBundleMappings(result.bundle, options.mappings) : new Map();
|
|
130
|
+
let savedTo;
|
|
131
|
+
let savedArtifacts;
|
|
132
|
+
if (typeof options.outputDir === 'string' && options.outputDir.trim().length > 0) {
|
|
133
|
+
savedTo = path.resolve(options.outputDir);
|
|
134
|
+
if (options.forceOutput) {
|
|
135
|
+
await rm(savedTo, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
await result.save(savedTo);
|
|
138
|
+
savedArtifacts = await collectSavedArtifacts(savedTo);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
applied: true,
|
|
142
|
+
code: result.code,
|
|
143
|
+
bundle: result.bundle
|
|
144
|
+
? summarizeBundle(result.bundle, {
|
|
145
|
+
includeModuleCode: options.includeModuleCode,
|
|
146
|
+
maxBundleModules: options.maxBundleModules,
|
|
147
|
+
}, remapped)
|
|
148
|
+
: undefined,
|
|
149
|
+
savedTo,
|
|
150
|
+
savedArtifacts,
|
|
151
|
+
optionsUsed,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
156
|
+
logger.warn('webcrack execution failed, falling back to legacy pipeline', error);
|
|
157
|
+
return {
|
|
158
|
+
applied: false,
|
|
159
|
+
code,
|
|
160
|
+
optionsUsed,
|
|
161
|
+
reason,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -4,12 +4,18 @@ export interface DetectionResult {
|
|
|
4
4
|
confidence: Record<ObfuscationType, number>;
|
|
5
5
|
features: string[];
|
|
6
6
|
recommendations: string[];
|
|
7
|
+
toolRecommendations: Array<{
|
|
8
|
+
tool: string;
|
|
9
|
+
reason: string;
|
|
10
|
+
suggestedArgs?: Record<string, unknown>;
|
|
11
|
+
}>;
|
|
7
12
|
vmFeatures?: VMFeatures;
|
|
8
13
|
}
|
|
9
14
|
export declare class ObfuscationDetector {
|
|
10
15
|
private jsvmpDetector;
|
|
11
16
|
constructor();
|
|
12
17
|
detect(code: string): DetectionResult;
|
|
18
|
+
private buildToolRecommendations;
|
|
13
19
|
private detectVMProtectionDetailed;
|
|
14
20
|
private detectJavaScriptObfuscator;
|
|
15
21
|
private detectWebpack;
|
|
@@ -16,13 +16,13 @@ export class ObfuscationDetector {
|
|
|
16
16
|
confidence['javascript-obfuscator'] = 0.9;
|
|
17
17
|
features.push('String array with rotation');
|
|
18
18
|
features.push('Control flow flattening');
|
|
19
|
-
recommendations.push('Use
|
|
19
|
+
recommendations.push('Use deobfuscate/advanced_deobfuscate with webcrack enabled');
|
|
20
20
|
}
|
|
21
21
|
if (this.detectWebpack(code)) {
|
|
22
22
|
types.push('webpack');
|
|
23
23
|
confidence['webpack'] = 0.85;
|
|
24
24
|
features.push('__webpack_require__');
|
|
25
|
-
recommendations.push('Use
|
|
25
|
+
recommendations.push('Use deobfuscate/advanced_deobfuscate with unpack=true to recover modules');
|
|
26
26
|
}
|
|
27
27
|
if (this.detectUglify(code)) {
|
|
28
28
|
types.push('uglify');
|
|
@@ -140,9 +140,51 @@ export class ObfuscationDetector {
|
|
|
140
140
|
confidence: confidence,
|
|
141
141
|
features,
|
|
142
142
|
recommendations,
|
|
143
|
+
toolRecommendations: this.buildToolRecommendations(types, code),
|
|
143
144
|
vmFeatures,
|
|
144
145
|
};
|
|
145
146
|
}
|
|
147
|
+
buildToolRecommendations(types, code) {
|
|
148
|
+
const recommendations = new Map();
|
|
149
|
+
const addRecommendation = (tool, reason, suggestedArgs) => {
|
|
150
|
+
if (!recommendations.has(tool)) {
|
|
151
|
+
recommendations.set(tool, { tool, reason, suggestedArgs });
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
if (types.includes('webpack') || types.includes('packer')) {
|
|
155
|
+
addRecommendation('webcrack_unpack', 'Bundle or wrapper markers detected; unpacking modules is likely the highest-value first step.', { code, unpack: true, unminify: true });
|
|
156
|
+
}
|
|
157
|
+
if (types.some((type) => [
|
|
158
|
+
'javascript-obfuscator',
|
|
159
|
+
'string-array-rotation',
|
|
160
|
+
'hex-encoding',
|
|
161
|
+
'base64-encoding',
|
|
162
|
+
'urlencoded',
|
|
163
|
+
'unknown',
|
|
164
|
+
].includes(type))) {
|
|
165
|
+
addRecommendation('deobfuscate', 'Static cleanup is likely sufficient; start with the standard webcrack-backed deobfuscation path.', { code, unminify: true });
|
|
166
|
+
}
|
|
167
|
+
if (types.some((type) => [
|
|
168
|
+
'vm-protection',
|
|
169
|
+
'control-flow-flattening',
|
|
170
|
+
'dead-code-injection',
|
|
171
|
+
'opaque-predicates',
|
|
172
|
+
'invisible-unicode',
|
|
173
|
+
'jscrambler',
|
|
174
|
+
'jsfuck',
|
|
175
|
+
'aaencode',
|
|
176
|
+
'jjencode',
|
|
177
|
+
].includes(type))) {
|
|
178
|
+
addRecommendation('advanced_deobfuscate', 'Complex protections detected; use the advanced webcrack-backed flow for deeper cleanup.', { code, detectOnly: false, unminify: true });
|
|
179
|
+
}
|
|
180
|
+
if (types.includes('eval-obfuscation') || types.includes('self-modifying')) {
|
|
181
|
+
addRecommendation('manage_hooks', 'Runtime-generated code is present; capture or hook execution points before static cleanup if analysis stalls.', { action: 'create', target: 'eval', type: 'function' });
|
|
182
|
+
}
|
|
183
|
+
if (recommendations.size === 0) {
|
|
184
|
+
addRecommendation('deobfuscate', 'No strong signature matched; start with the standard webcrack-backed path.', { code });
|
|
185
|
+
}
|
|
186
|
+
return Array.from(recommendations.values());
|
|
187
|
+
}
|
|
146
188
|
detectVMProtectionDetailed(code) {
|
|
147
189
|
try {
|
|
148
190
|
const detector = this.jsvmpDetector;
|
|
@@ -273,6 +315,15 @@ export class ObfuscationDetector {
|
|
|
273
315
|
result.recommendations.forEach((rec) => {
|
|
274
316
|
report += ` - ${rec}\n`;
|
|
275
317
|
});
|
|
318
|
+
if (result.toolRecommendations.length > 0) {
|
|
319
|
+
report += `\nSuggested Tools:\n`;
|
|
320
|
+
result.toolRecommendations.forEach((item) => {
|
|
321
|
+
report += ` - ${item.tool}: ${item.reason}\n`;
|
|
322
|
+
if (item.suggestedArgs) {
|
|
323
|
+
report += ` args: ${JSON.stringify(item.suggestedArgs)}\n`;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
276
327
|
return report;
|
|
277
328
|
}
|
|
278
329
|
}
|
|
@@ -61,7 +61,7 @@ export class AIHookGenerator {
|
|
|
61
61
|
generateFunctionHook(request, hookId) {
|
|
62
62
|
const { target, behavior, condition, customCode } = request;
|
|
63
63
|
const functionName = target.name || target.pattern || 'unknownFunction';
|
|
64
|
-
|
|
64
|
+
const code = `
|
|
65
65
|
(function() {
|
|
66
66
|
const originalFunction = window.${functionName};
|
|
67
67
|
|
|
@@ -103,31 +103,31 @@ export class MacProcessManager {
|
|
|
103
103
|
if (!process) {
|
|
104
104
|
return [];
|
|
105
105
|
}
|
|
106
|
-
const appleScript = `
|
|
107
|
-
tell application "System Events"
|
|
108
|
-
set processList to {}
|
|
109
|
-
try
|
|
110
|
-
set targetProcess to first process whose unix id is ${pid}
|
|
111
|
-
set procName to name of targetProcess
|
|
112
|
-
set windowList to {}
|
|
113
|
-
|
|
114
|
-
tell targetProcess
|
|
115
|
-
repeat with win in windows
|
|
116
|
-
set winInfo to {|
|
|
117
|
-
title:name of win,
|
|
118
|
-
className:procName,
|
|
119
|
-
processId:${pid},
|
|
120
|
-
handle:"applescript-window"
|
|
121
|
-
|}
|
|
122
|
-
set end of windowList to winInfo
|
|
123
|
-
end repeat
|
|
124
|
-
end tell
|
|
125
|
-
|
|
126
|
-
return windowList
|
|
127
|
-
on error
|
|
128
|
-
return {}
|
|
129
|
-
end try
|
|
130
|
-
end tell
|
|
106
|
+
const appleScript = `
|
|
107
|
+
tell application "System Events"
|
|
108
|
+
set processList to {}
|
|
109
|
+
try
|
|
110
|
+
set targetProcess to first process whose unix id is ${pid}
|
|
111
|
+
set procName to name of targetProcess
|
|
112
|
+
set windowList to {}
|
|
113
|
+
|
|
114
|
+
tell targetProcess
|
|
115
|
+
repeat with win in windows
|
|
116
|
+
set winInfo to {|
|
|
117
|
+
title:name of win,
|
|
118
|
+
className:procName,
|
|
119
|
+
processId:${pid},
|
|
120
|
+
handle:"applescript-window"
|
|
121
|
+
|}
|
|
122
|
+
set end of windowList to winInfo
|
|
123
|
+
end repeat
|
|
124
|
+
end tell
|
|
125
|
+
|
|
126
|
+
return windowList
|
|
127
|
+
on error
|
|
128
|
+
return {}
|
|
129
|
+
end try
|
|
130
|
+
end tell
|
|
131
131
|
`;
|
|
132
132
|
const { stdout } = await execAsync(`osascript -e '${appleScript.replace(/'/g, "'\"'\"'")}' 2>/dev/null || echo "[]"`, { timeout: 5000 });
|
|
133
133
|
const windows = [];
|
|
@@ -103,55 +103,55 @@ export async function checkDebugPort(platform, pid) {
|
|
|
103
103
|
return { success: false, error: 'Debug port check currently only implemented for Windows' };
|
|
104
104
|
}
|
|
105
105
|
try {
|
|
106
|
-
const psScript = `
|
|
107
|
-
Add-Type @"
|
|
108
|
-
using System;
|
|
109
|
-
using System.Runtime.InteropServices;
|
|
110
|
-
using System.ComponentModel;
|
|
111
|
-
|
|
112
|
-
public class DebugChecker {
|
|
113
|
-
[DllImport("ntdll.dll")]
|
|
114
|
-
public static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, out IntPtr processInformation, int processInformationLength, out int returnLength);
|
|
115
|
-
|
|
116
|
-
[DllImport("kernel32.dll", SetLastError = true)]
|
|
117
|
-
public static extern IntPtr OpenProcess(int access, bool inherit, int pid);
|
|
118
|
-
|
|
119
|
-
[DllImport("kernel32.dll", SetLastError = true)]
|
|
120
|
-
public static extern bool CloseHandle(IntPtr handle);
|
|
121
|
-
|
|
122
|
-
const int PROCESS_QUERY_INFORMATION = 0x0400;
|
|
123
|
-
const int ProcessDebugPort = 7;
|
|
124
|
-
|
|
125
|
-
public static object Check(int pid) {
|
|
126
|
-
IntPtr hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
|
|
127
|
-
if (hProcess == IntPtr.Zero) {
|
|
128
|
-
int error = Marshal.GetLastWin32Error();
|
|
129
|
-
throw new Win32Exception(error, "Failed to open process");
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
IntPtr debugPort;
|
|
134
|
-
int returnLength;
|
|
135
|
-
int status = NtQueryInformationProcess(hProcess, ProcessDebugPort, out debugPort, IntPtr.Size, out returnLength);
|
|
136
|
-
|
|
137
|
-
if (status != 0) {
|
|
138
|
-
return new { success = false, error = "NtQueryInformationProcess failed with status: 0x" + status.ToString("X") };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return new { success = true, isDebugged = debugPort != IntPtr.Zero };
|
|
142
|
-
} finally {
|
|
143
|
-
CloseHandle(hProcess);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
"@
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
$result = [DebugChecker]::Check(${pid})
|
|
151
|
-
$result | ConvertTo-Json -Compress
|
|
152
|
-
} catch {
|
|
153
|
-
@{ success = $false; error = $_.Exception.Message } | ConvertTo-Json -Compress
|
|
154
|
-
}
|
|
106
|
+
const psScript = `
|
|
107
|
+
Add-Type @"
|
|
108
|
+
using System;
|
|
109
|
+
using System.Runtime.InteropServices;
|
|
110
|
+
using System.ComponentModel;
|
|
111
|
+
|
|
112
|
+
public class DebugChecker {
|
|
113
|
+
[DllImport("ntdll.dll")]
|
|
114
|
+
public static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, out IntPtr processInformation, int processInformationLength, out int returnLength);
|
|
115
|
+
|
|
116
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
117
|
+
public static extern IntPtr OpenProcess(int access, bool inherit, int pid);
|
|
118
|
+
|
|
119
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
120
|
+
public static extern bool CloseHandle(IntPtr handle);
|
|
121
|
+
|
|
122
|
+
const int PROCESS_QUERY_INFORMATION = 0x0400;
|
|
123
|
+
const int ProcessDebugPort = 7;
|
|
124
|
+
|
|
125
|
+
public static object Check(int pid) {
|
|
126
|
+
IntPtr hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
|
|
127
|
+
if (hProcess == IntPtr.Zero) {
|
|
128
|
+
int error = Marshal.GetLastWin32Error();
|
|
129
|
+
throw new Win32Exception(error, "Failed to open process");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
IntPtr debugPort;
|
|
134
|
+
int returnLength;
|
|
135
|
+
int status = NtQueryInformationProcess(hProcess, ProcessDebugPort, out debugPort, IntPtr.Size, out returnLength);
|
|
136
|
+
|
|
137
|
+
if (status != 0) {
|
|
138
|
+
return new { success = false, error = "NtQueryInformationProcess failed with status: 0x" + status.ToString("X") };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return new { success = true, isDebugged = debugPort != IntPtr.Zero };
|
|
142
|
+
} finally {
|
|
143
|
+
CloseHandle(hProcess);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
"@
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
$result = [DebugChecker]::Check(${pid})
|
|
151
|
+
$result | ConvertTo-Json -Compress
|
|
152
|
+
} catch {
|
|
153
|
+
@{ success = $false; error = $_.Exception.Message } | ConvertTo-Json -Compress
|
|
154
|
+
}
|
|
155
155
|
`;
|
|
156
156
|
const { stdout } = await executePowerShellScript(psScript, { maxBuffer: 1024 * 1024, timeout: 10000 });
|
|
157
157
|
const _trimmed = stdout.trim();
|