@jshookmcp/jshook 0.1.7 → 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/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/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 +209 -189
- 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/memory/writer.js +1 -1
- 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.security.js +13 -10
- 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/types/deobfuscator.d.ts +43 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/config.js +19 -10
- package/package.json +6 -3
- package/scripts/postinstall.cjs +37 -0
|
@@ -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
|
|
|
@@ -149,7 +149,7 @@ export async function writeMemory(platform, pid, address, data, encoding = 'hex'
|
|
|
149
149
|
buffer = Buffer.from(cleanHex, 'hex');
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
-
catch (
|
|
152
|
+
catch (_e) {
|
|
153
153
|
return { success: false, error: `Invalid ${encoding} data` };
|
|
154
154
|
}
|
|
155
155
|
if (platform === 'win32' && isKoffiAvailable()) {
|
|
@@ -138,7 +138,7 @@ export const coreTools = [
|
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
name: 'deobfuscate',
|
|
141
|
-
description: 'Run
|
|
141
|
+
description: 'Run webcrack-powered JavaScript deobfuscation with bundle unpacking support.',
|
|
142
142
|
inputSchema: {
|
|
143
143
|
type: 'object',
|
|
144
144
|
properties: {
|
|
@@ -157,6 +157,75 @@ export const coreTools = [
|
|
|
157
157
|
description: 'Enable aggressive deobfuscation strategy',
|
|
158
158
|
default: false,
|
|
159
159
|
},
|
|
160
|
+
unpack: {
|
|
161
|
+
type: 'boolean',
|
|
162
|
+
description: 'Use webcrack to unpack webpack/browserify bundles when possible',
|
|
163
|
+
default: true,
|
|
164
|
+
},
|
|
165
|
+
unminify: {
|
|
166
|
+
type: 'boolean',
|
|
167
|
+
description: 'Use webcrack to reformat and unminify code before post-processing',
|
|
168
|
+
default: true,
|
|
169
|
+
},
|
|
170
|
+
jsx: {
|
|
171
|
+
type: 'boolean',
|
|
172
|
+
description: 'Ask webcrack to decompile React.createElement trees back to JSX when supported',
|
|
173
|
+
default: true,
|
|
174
|
+
},
|
|
175
|
+
mangle: {
|
|
176
|
+
type: 'boolean',
|
|
177
|
+
description: 'Rename obfuscated identifiers using webcrack mangle pass',
|
|
178
|
+
default: false,
|
|
179
|
+
},
|
|
180
|
+
outputDir: {
|
|
181
|
+
type: 'string',
|
|
182
|
+
description: 'Optional directory where webcrack should save the deobfuscated code and extracted bundle',
|
|
183
|
+
},
|
|
184
|
+
forceOutput: {
|
|
185
|
+
type: 'boolean',
|
|
186
|
+
description: 'Remove outputDir before saving webcrack artifacts',
|
|
187
|
+
default: false,
|
|
188
|
+
},
|
|
189
|
+
includeModuleCode: {
|
|
190
|
+
type: 'boolean',
|
|
191
|
+
description: 'Include unpacked module source in bundle output when returning bundle details',
|
|
192
|
+
default: false,
|
|
193
|
+
},
|
|
194
|
+
maxBundleModules: {
|
|
195
|
+
type: 'number',
|
|
196
|
+
description: 'Maximum number of bundle modules to return in the response',
|
|
197
|
+
default: 100,
|
|
198
|
+
},
|
|
199
|
+
mappings: {
|
|
200
|
+
type: 'array',
|
|
201
|
+
description: 'Optional remapping rules applied to unpacked bundle module paths. Each rule can match against module code or current path.',
|
|
202
|
+
items: {
|
|
203
|
+
type: 'object',
|
|
204
|
+
properties: {
|
|
205
|
+
path: {
|
|
206
|
+
type: 'string',
|
|
207
|
+
description: 'New module path to assign when the rule matches',
|
|
208
|
+
},
|
|
209
|
+
pattern: {
|
|
210
|
+
type: 'string',
|
|
211
|
+
description: 'Text or regex used to match module code/path',
|
|
212
|
+
},
|
|
213
|
+
matchType: {
|
|
214
|
+
type: 'string',
|
|
215
|
+
enum: ['includes', 'regex', 'exact'],
|
|
216
|
+
description: 'How to interpret pattern',
|
|
217
|
+
default: 'includes',
|
|
218
|
+
},
|
|
219
|
+
target: {
|
|
220
|
+
type: 'string',
|
|
221
|
+
enum: ['code', 'path'],
|
|
222
|
+
description: 'Whether to match against module source code or the current module path',
|
|
223
|
+
default: 'code',
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
required: ['path', 'pattern'],
|
|
227
|
+
},
|
|
228
|
+
},
|
|
160
229
|
},
|
|
161
230
|
required: ['code'],
|
|
162
231
|
},
|
|
@@ -258,7 +327,7 @@ export const coreTools = [
|
|
|
258
327
|
},
|
|
259
328
|
{
|
|
260
329
|
name: 'advanced_deobfuscate',
|
|
261
|
-
description: 'Run advanced deobfuscation with
|
|
330
|
+
description: 'Run advanced deobfuscation with webcrack backend (deprecated legacy flags ignored).',
|
|
262
331
|
inputSchema: {
|
|
263
332
|
type: 'object',
|
|
264
333
|
properties: {
|
|
@@ -286,6 +355,158 @@ export const coreTools = [
|
|
|
286
355
|
description: 'Operation timeout in milliseconds',
|
|
287
356
|
default: 60000,
|
|
288
357
|
},
|
|
358
|
+
unpack: {
|
|
359
|
+
type: 'boolean',
|
|
360
|
+
description: 'Use webcrack to unpack webpack/browserify bundles before advanced cleanup',
|
|
361
|
+
default: true,
|
|
362
|
+
},
|
|
363
|
+
unminify: {
|
|
364
|
+
type: 'boolean',
|
|
365
|
+
description: 'Use webcrack unminify pass before VM and AST-oriented cleanup',
|
|
366
|
+
default: true,
|
|
367
|
+
},
|
|
368
|
+
jsx: {
|
|
369
|
+
type: 'boolean',
|
|
370
|
+
description: 'Allow webcrack to decompile React.createElement back to JSX when supported',
|
|
371
|
+
default: true,
|
|
372
|
+
},
|
|
373
|
+
mangle: {
|
|
374
|
+
type: 'boolean',
|
|
375
|
+
description: 'Rename obfuscated identifiers during the webcrack phase',
|
|
376
|
+
default: false,
|
|
377
|
+
},
|
|
378
|
+
outputDir: {
|
|
379
|
+
type: 'string',
|
|
380
|
+
description: 'Optional directory where webcrack should save the deobfuscated code and extracted bundle',
|
|
381
|
+
},
|
|
382
|
+
forceOutput: {
|
|
383
|
+
type: 'boolean',
|
|
384
|
+
description: 'Remove outputDir before saving webcrack artifacts',
|
|
385
|
+
default: false,
|
|
386
|
+
},
|
|
387
|
+
includeModuleCode: {
|
|
388
|
+
type: 'boolean',
|
|
389
|
+
description: 'Include unpacked module source in bundle output when returning bundle details',
|
|
390
|
+
default: false,
|
|
391
|
+
},
|
|
392
|
+
maxBundleModules: {
|
|
393
|
+
type: 'number',
|
|
394
|
+
description: 'Maximum number of bundle modules to return in the response',
|
|
395
|
+
default: 100,
|
|
396
|
+
},
|
|
397
|
+
mappings: {
|
|
398
|
+
type: 'array',
|
|
399
|
+
description: 'Optional remapping rules applied to unpacked bundle module paths. Each rule can match against module code or current path.',
|
|
400
|
+
items: {
|
|
401
|
+
type: 'object',
|
|
402
|
+
properties: {
|
|
403
|
+
path: {
|
|
404
|
+
type: 'string',
|
|
405
|
+
description: 'New module path to assign when the rule matches',
|
|
406
|
+
},
|
|
407
|
+
pattern: {
|
|
408
|
+
type: 'string',
|
|
409
|
+
description: 'Text or regex used to match module code/path',
|
|
410
|
+
},
|
|
411
|
+
matchType: {
|
|
412
|
+
type: 'string',
|
|
413
|
+
enum: ['includes', 'regex', 'exact'],
|
|
414
|
+
description: 'How to interpret pattern',
|
|
415
|
+
default: 'includes',
|
|
416
|
+
},
|
|
417
|
+
target: {
|
|
418
|
+
type: 'string',
|
|
419
|
+
enum: ['code', 'path'],
|
|
420
|
+
description: 'Whether to match against module source code or the current module path',
|
|
421
|
+
default: 'code',
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
required: ['path', 'pattern'],
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
required: ['code'],
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: 'webcrack_unpack',
|
|
433
|
+
description: 'Run webcrack bundle unpacking directly and return extracted module graph details.',
|
|
434
|
+
inputSchema: {
|
|
435
|
+
type: 'object',
|
|
436
|
+
properties: {
|
|
437
|
+
code: {
|
|
438
|
+
type: 'string',
|
|
439
|
+
description: 'Bundled or obfuscated JavaScript source',
|
|
440
|
+
},
|
|
441
|
+
unpack: {
|
|
442
|
+
type: 'boolean',
|
|
443
|
+
description: 'Extract modules from the bundle when supported',
|
|
444
|
+
default: true,
|
|
445
|
+
},
|
|
446
|
+
unminify: {
|
|
447
|
+
type: 'boolean',
|
|
448
|
+
description: 'Unminify the code before extracting bundle modules',
|
|
449
|
+
default: true,
|
|
450
|
+
},
|
|
451
|
+
jsx: {
|
|
452
|
+
type: 'boolean',
|
|
453
|
+
description: 'Decompile React.createElement trees back to JSX when supported',
|
|
454
|
+
default: true,
|
|
455
|
+
},
|
|
456
|
+
mangle: {
|
|
457
|
+
type: 'boolean',
|
|
458
|
+
description: 'Rename obfuscated identifiers during the webcrack pass',
|
|
459
|
+
default: false,
|
|
460
|
+
},
|
|
461
|
+
outputDir: {
|
|
462
|
+
type: 'string',
|
|
463
|
+
description: 'Optional directory where webcrack should save the extracted bundle files',
|
|
464
|
+
},
|
|
465
|
+
forceOutput: {
|
|
466
|
+
type: 'boolean',
|
|
467
|
+
description: 'Remove outputDir before saving webcrack artifacts',
|
|
468
|
+
default: false,
|
|
469
|
+
},
|
|
470
|
+
includeModuleCode: {
|
|
471
|
+
type: 'boolean',
|
|
472
|
+
description: 'Include unpacked module source in bundle output',
|
|
473
|
+
default: false,
|
|
474
|
+
},
|
|
475
|
+
maxBundleModules: {
|
|
476
|
+
type: 'number',
|
|
477
|
+
description: 'Maximum number of bundle modules to return in the response',
|
|
478
|
+
default: 100,
|
|
479
|
+
},
|
|
480
|
+
mappings: {
|
|
481
|
+
type: 'array',
|
|
482
|
+
description: 'Optional remapping rules applied to unpacked bundle module paths. Each rule can match against module code or current path.',
|
|
483
|
+
items: {
|
|
484
|
+
type: 'object',
|
|
485
|
+
properties: {
|
|
486
|
+
path: {
|
|
487
|
+
type: 'string',
|
|
488
|
+
description: 'New module path to assign when the rule matches',
|
|
489
|
+
},
|
|
490
|
+
pattern: {
|
|
491
|
+
type: 'string',
|
|
492
|
+
description: 'Text or regex used to match module code/path',
|
|
493
|
+
},
|
|
494
|
+
matchType: {
|
|
495
|
+
type: 'string',
|
|
496
|
+
enum: ['includes', 'regex', 'exact'],
|
|
497
|
+
description: 'How to interpret pattern',
|
|
498
|
+
default: 'includes',
|
|
499
|
+
},
|
|
500
|
+
target: {
|
|
501
|
+
type: 'string',
|
|
502
|
+
enum: ['code', 'path'],
|
|
503
|
+
description: 'Whether to match against module source code or the current module path',
|
|
504
|
+
default: 'code',
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
required: ['path', 'pattern'],
|
|
508
|
+
},
|
|
509
|
+
},
|
|
289
510
|
},
|
|
290
511
|
required: ['code'],
|
|
291
512
|
},
|
|
@@ -3,7 +3,6 @@ import { CodeCollector } from '../../domains/shared/modules.js';
|
|
|
3
3
|
import { ScriptManager } from '../../domains/shared/modules.js';
|
|
4
4
|
import { Deobfuscator } from '../../domains/shared/modules.js';
|
|
5
5
|
import { AdvancedDeobfuscator } from '../../domains/shared/modules.js';
|
|
6
|
-
import { ASTOptimizer } from '../../domains/shared/modules.js';
|
|
7
6
|
import { ObfuscationDetector } from '../../domains/shared/modules.js';
|
|
8
7
|
import { CodeAnalyzer } from '../../domains/shared/modules.js';
|
|
9
8
|
import { CryptoDetector } from '../../domains/shared/modules.js';
|
|
@@ -13,7 +12,6 @@ interface CoreAnalysisHandlerDeps {
|
|
|
13
12
|
scriptManager: ScriptManager;
|
|
14
13
|
deobfuscator: Deobfuscator;
|
|
15
14
|
advancedDeobfuscator: AdvancedDeobfuscator;
|
|
16
|
-
astOptimizer: ASTOptimizer;
|
|
17
15
|
obfuscationDetector: ObfuscationDetector;
|
|
18
16
|
analyzer: CodeAnalyzer;
|
|
19
17
|
cryptoDetector: CryptoDetector;
|
|
@@ -24,13 +22,13 @@ export declare class CoreAnalysisHandlers {
|
|
|
24
22
|
private readonly scriptManager;
|
|
25
23
|
private readonly deobfuscator;
|
|
26
24
|
private readonly advancedDeobfuscator;
|
|
27
|
-
private readonly astOptimizer;
|
|
28
25
|
private readonly obfuscationDetector;
|
|
29
26
|
private readonly analyzer;
|
|
30
27
|
private readonly cryptoDetector;
|
|
31
28
|
private readonly hookManager;
|
|
32
29
|
constructor(deps: CoreAnalysisHandlerDeps);
|
|
33
30
|
private requireCodeArg;
|
|
31
|
+
private extractWebcrackArgs;
|
|
34
32
|
handleCollectCode(args: ToolArgs): Promise<ToolResponse>;
|
|
35
33
|
handleSearchInScripts(args: ToolArgs): Promise<ToolResponse>;
|
|
36
34
|
handleExtractFunctionTree(args: ToolArgs): Promise<ToolResponse>;
|
|
@@ -40,6 +38,7 @@ export declare class CoreAnalysisHandlers {
|
|
|
40
38
|
handleManageHooks(args: ToolArgs): Promise<ToolResponse>;
|
|
41
39
|
handleDetectObfuscation(args: ToolArgs): Promise<ToolResponse>;
|
|
42
40
|
handleAdvancedDeobfuscate(args: ToolArgs): Promise<ToolResponse>;
|
|
41
|
+
handleWebcrackUnpack(args: ToolArgs): Promise<ToolResponse>;
|
|
43
42
|
handleWebpackEnumerate(args: ToolArgs): Promise<ToolResponse>;
|
|
44
43
|
handleSourceMapExtract(args: ToolArgs): Promise<ToolResponse>;
|
|
45
44
|
handleClearCollectedData(): Promise<ToolResponse>;
|