@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
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import * as parser from '@babel/parser';
|
|
2
|
-
import traverse from '@babel/traverse';
|
|
3
|
-
import generate from '@babel/generator';
|
|
4
|
-
import * as t from '@babel/types';
|
|
5
1
|
import crypto from 'crypto';
|
|
6
2
|
import { logger } from '../../utils/logger.js';
|
|
7
3
|
import { DEOBF_LLM_MAX_TOKENS } from '../../constants.js';
|
|
8
4
|
import { generateDeobfuscationPrompt } from '../../services/prompts/deobfuscation.js';
|
|
9
5
|
import { calculateReadabilityScore as calculateReadabilityScoreUtil, detectObfuscationType as detectObfuscationTypeUtil, } from '../deobfuscator/Deobfuscator.utils.js';
|
|
6
|
+
import { runWebcrack } from '../deobfuscator/webcrack.js';
|
|
10
7
|
export class Deobfuscator {
|
|
11
8
|
llm;
|
|
12
|
-
stringArrays = new Map();
|
|
13
9
|
resultCache = new Map();
|
|
14
10
|
maxCacheSize = 100;
|
|
15
11
|
constructor(llm) {
|
|
@@ -17,9 +13,17 @@ export class Deobfuscator {
|
|
|
17
13
|
}
|
|
18
14
|
generateCacheKey(options) {
|
|
19
15
|
const key = JSON.stringify({
|
|
20
|
-
code: options.code.substring(0,
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
code: options.code.substring(0, 2000),
|
|
17
|
+
forceOutput: options.forceOutput,
|
|
18
|
+
includeModuleCode: options.includeModuleCode,
|
|
19
|
+
jsx: options.jsx,
|
|
20
|
+
llm: options.llm,
|
|
21
|
+
mangle: options.mangle ?? options.renameVariables,
|
|
22
|
+
mappings: options.mappings,
|
|
23
|
+
maxBundleModules: options.maxBundleModules,
|
|
24
|
+
outputDir: options.outputDir,
|
|
25
|
+
unpack: options.unpack,
|
|
26
|
+
unminify: options.unminify,
|
|
23
27
|
});
|
|
24
28
|
return crypto.createHash('md5').update(key).digest('hex');
|
|
25
29
|
}
|
|
@@ -30,427 +34,144 @@ export class Deobfuscator {
|
|
|
30
34
|
logger.debug('Deobfuscation result from cache');
|
|
31
35
|
return cached;
|
|
32
36
|
}
|
|
33
|
-
logger.info('Starting deobfuscation...');
|
|
37
|
+
logger.info('Starting webcrack deobfuscation...');
|
|
34
38
|
const startTime = Date.now();
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
const obfuscationType = this.detectObfuscationType(options.code);
|
|
40
|
+
const warnings = [];
|
|
41
|
+
if (options.aggressive !== undefined) {
|
|
42
|
+
warnings.push('aggressive is deprecated and ignored; webcrack is now the only deobfuscation engine.');
|
|
43
|
+
}
|
|
44
|
+
if (options.preserveLogic !== undefined) {
|
|
45
|
+
warnings.push('preserveLogic is deprecated and ignored.');
|
|
46
|
+
}
|
|
47
|
+
if (options.inlineFunctions !== undefined) {
|
|
48
|
+
warnings.push('inlineFunctions is deprecated and ignored.');
|
|
49
|
+
}
|
|
50
|
+
const webcrackResult = await runWebcrack(options.code, {
|
|
51
|
+
unpack: options.unpack,
|
|
52
|
+
unminify: options.unminify,
|
|
53
|
+
jsx: options.jsx,
|
|
54
|
+
mangle: options.mangle ?? options.renameVariables,
|
|
55
|
+
mappings: options.mappings,
|
|
56
|
+
includeModuleCode: options.includeModuleCode,
|
|
57
|
+
maxBundleModules: options.maxBundleModules,
|
|
58
|
+
outputDir: options.outputDir,
|
|
59
|
+
forceOutput: options.forceOutput,
|
|
60
|
+
});
|
|
61
|
+
if (!webcrackResult.applied) {
|
|
62
|
+
const reason = webcrackResult.reason ?? 'webcrack did not return a result';
|
|
63
|
+
logger.error(`webcrack deobfuscation failed: ${reason}`);
|
|
64
|
+
throw new Error(reason);
|
|
65
|
+
}
|
|
66
|
+
let analysis = this.buildAnalysis(webcrackResult, obfuscationType);
|
|
67
|
+
if (this.llm && options.llm) {
|
|
68
|
+
const llmResult = await this.llmAnalysis(webcrackResult.code);
|
|
69
|
+
if (llmResult) {
|
|
70
|
+
analysis = llmResult;
|
|
50
71
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
}
|
|
73
|
+
const transformations = [
|
|
74
|
+
{
|
|
75
|
+
type: 'webcrack',
|
|
76
|
+
description: `Ran webcrack (unminify=${webcrackResult.optionsUsed.unminify}, unpack=${webcrackResult.optionsUsed.unpack}, jsx=${webcrackResult.optionsUsed.jsx}, mangle=${webcrackResult.optionsUsed.mangle})`,
|
|
77
|
+
success: true,
|
|
78
|
+
},
|
|
79
|
+
...(webcrackResult.bundle
|
|
80
|
+
? [
|
|
81
|
+
{
|
|
82
|
+
type: 'webcrack-unpack',
|
|
83
|
+
description: `Recovered ${webcrackResult.bundle.moduleCount} bundled modules`,
|
|
84
|
+
success: true,
|
|
85
|
+
},
|
|
86
|
+
]
|
|
87
|
+
: []),
|
|
88
|
+
...(webcrackResult.savedTo
|
|
89
|
+
? [
|
|
90
|
+
{
|
|
91
|
+
type: 'webcrack-save',
|
|
92
|
+
description: `Saved webcrack artifacts to ${webcrackResult.savedTo}`,
|
|
93
|
+
success: true,
|
|
94
|
+
},
|
|
95
|
+
]
|
|
96
|
+
: []),
|
|
97
|
+
...(this.llm && options.llm
|
|
98
|
+
? [
|
|
99
|
+
{
|
|
57
100
|
type: 'llm-analysis',
|
|
58
|
-
description: 'AI-assisted
|
|
101
|
+
description: 'AI-assisted analysis completed after webcrack deobfuscation',
|
|
59
102
|
success: true,
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
103
|
+
},
|
|
104
|
+
]
|
|
105
|
+
: []),
|
|
106
|
+
];
|
|
107
|
+
const readabilityScore = this.calculateReadabilityScore(webcrackResult.code);
|
|
108
|
+
const confidence = this.calculateConfidence(webcrackResult, readabilityScore);
|
|
109
|
+
const duration = Date.now() - startTime;
|
|
110
|
+
logger.success(`webcrack deobfuscation completed in ${duration}ms (confidence: ${(confidence * 100).toFixed(1)}%)`);
|
|
111
|
+
const result = {
|
|
112
|
+
code: webcrackResult.code,
|
|
113
|
+
readabilityScore,
|
|
114
|
+
confidence,
|
|
115
|
+
obfuscationType,
|
|
116
|
+
transformations,
|
|
117
|
+
analysis,
|
|
118
|
+
bundle: webcrackResult.bundle,
|
|
119
|
+
savedTo: webcrackResult.savedTo,
|
|
120
|
+
savedArtifacts: webcrackResult.savedArtifacts,
|
|
121
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
122
|
+
engine: 'webcrack',
|
|
123
|
+
webcrackApplied: true,
|
|
124
|
+
};
|
|
125
|
+
if (this.resultCache.size >= this.maxCacheSize) {
|
|
126
|
+
const firstKey = this.resultCache.keys().next().value;
|
|
127
|
+
if (firstKey) {
|
|
128
|
+
this.resultCache.delete(firstKey);
|
|
80
129
|
}
|
|
81
|
-
this.resultCache.set(cacheKey, result);
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
logger.error('Deobfuscation failed', error);
|
|
86
|
-
throw error;
|
|
87
130
|
}
|
|
131
|
+
this.resultCache.set(cacheKey, result);
|
|
132
|
+
return result;
|
|
88
133
|
}
|
|
89
134
|
detectObfuscationType(code) {
|
|
90
135
|
return detectObfuscationTypeUtil(code);
|
|
91
136
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (t.isNumericLiteral(path.node.left) && t.isNumericLiteral(path.node.right)) {
|
|
101
|
-
const left = path.node.left.value;
|
|
102
|
-
const right = path.node.right.value;
|
|
103
|
-
let result;
|
|
104
|
-
switch (path.node.operator) {
|
|
105
|
-
case '+':
|
|
106
|
-
result = left + right;
|
|
107
|
-
break;
|
|
108
|
-
case '-':
|
|
109
|
-
result = left - right;
|
|
110
|
-
break;
|
|
111
|
-
case '*':
|
|
112
|
-
result = left * right;
|
|
113
|
-
break;
|
|
114
|
-
case '/':
|
|
115
|
-
result = left / right;
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
if (result !== undefined) {
|
|
119
|
-
path.replaceWith(t.numericLiteral(result));
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
IfStatement(path) {
|
|
124
|
-
if (t.isBooleanLiteral(path.node.test)) {
|
|
125
|
-
if (path.node.test.value) {
|
|
126
|
-
path.replaceWith(path.node.consequent);
|
|
127
|
-
}
|
|
128
|
-
else if (path.node.alternate) {
|
|
129
|
-
path.replaceWith(path.node.alternate);
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
path.remove();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
const output = generate(ast, {
|
|
138
|
-
comments: true,
|
|
139
|
-
compact: false,
|
|
140
|
-
});
|
|
141
|
-
transformations.push({
|
|
142
|
-
type: 'basic-ast-transform',
|
|
143
|
-
description: 'Applied constant folding and dead code elimination',
|
|
144
|
-
success: true,
|
|
145
|
-
});
|
|
146
|
-
return output.code;
|
|
137
|
+
calculateReadabilityScore(code) {
|
|
138
|
+
return calculateReadabilityScoreUtil(code);
|
|
139
|
+
}
|
|
140
|
+
calculateConfidence(webcrackResult, readabilityScore) {
|
|
141
|
+
let confidence = 0.7;
|
|
142
|
+
confidence += readabilityScore / 500;
|
|
143
|
+
if (webcrackResult.bundle) {
|
|
144
|
+
confidence += 0.1;
|
|
147
145
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
transformations.push({
|
|
151
|
-
type: 'basic-ast-transform',
|
|
152
|
-
description: 'Failed to apply AST transformations',
|
|
153
|
-
success: false,
|
|
154
|
-
});
|
|
155
|
-
return code;
|
|
146
|
+
if (webcrackResult.savedTo) {
|
|
147
|
+
confidence += 0.05;
|
|
156
148
|
}
|
|
149
|
+
return Math.min(confidence, 0.99);
|
|
157
150
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
plugins: ['jsx', 'typescript'],
|
|
163
|
-
});
|
|
164
|
-
let decoded = 0;
|
|
165
|
-
traverse(ast, {
|
|
166
|
-
StringLiteral(path) {
|
|
167
|
-
const value = path.node.value;
|
|
168
|
-
if (value.includes('\\x')) {
|
|
169
|
-
try {
|
|
170
|
-
const decodedValue = value.replace(/\\x([0-9A-Fa-f]{2})/g, (_, hex) => {
|
|
171
|
-
return String.fromCharCode(parseInt(hex, 16));
|
|
172
|
-
});
|
|
173
|
-
path.node.value = decodedValue;
|
|
174
|
-
decoded++;
|
|
175
|
-
}
|
|
176
|
-
catch { }
|
|
177
|
-
}
|
|
178
|
-
if (value.includes('\\u')) {
|
|
179
|
-
try {
|
|
180
|
-
const decodedValue = value.replace(/\\u([0-9A-Fa-f]{4})/g, (_, hex) => {
|
|
181
|
-
return String.fromCharCode(parseInt(hex, 16));
|
|
182
|
-
});
|
|
183
|
-
path.node.value = decodedValue;
|
|
184
|
-
decoded++;
|
|
185
|
-
}
|
|
186
|
-
catch { }
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
});
|
|
190
|
-
if (decoded > 0) {
|
|
191
|
-
const output = generate(ast, { comments: true, compact: false });
|
|
192
|
-
transformations.push({
|
|
193
|
-
type: 'string-decode',
|
|
194
|
-
description: `Decoded ${decoded} strings`,
|
|
195
|
-
success: true,
|
|
196
|
-
});
|
|
197
|
-
return output.code;
|
|
198
|
-
}
|
|
199
|
-
return code;
|
|
151
|
+
buildAnalysis(webcrackResult, obfuscationType) {
|
|
152
|
+
const parts = [`webcrack completed deobfuscation for detected types: ${obfuscationType.join(', ')}.`];
|
|
153
|
+
if (webcrackResult.bundle) {
|
|
154
|
+
parts.push(`Recovered a ${webcrackResult.bundle.type} bundle with ${webcrackResult.bundle.moduleCount} modules.`);
|
|
200
155
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
transformations.push({
|
|
204
|
-
type: 'string-decode',
|
|
205
|
-
description: 'Failed to decode strings',
|
|
206
|
-
success: false,
|
|
207
|
-
});
|
|
208
|
-
return code;
|
|
156
|
+
if (webcrackResult.savedTo) {
|
|
157
|
+
parts.push(`Artifacts saved to ${webcrackResult.savedTo}.`);
|
|
209
158
|
}
|
|
159
|
+
return parts.join(' ');
|
|
210
160
|
}
|
|
211
161
|
async llmAnalysis(code) {
|
|
212
162
|
if (!this.llm)
|
|
213
163
|
return null;
|
|
214
164
|
try {
|
|
215
165
|
const messages = generateDeobfuscationPrompt(code);
|
|
216
|
-
const response = await this.llm.chat(messages, {
|
|
166
|
+
const response = await this.llm.chat(messages, {
|
|
167
|
+
temperature: 0.3,
|
|
168
|
+
maxTokens: DEOBF_LLM_MAX_TOKENS,
|
|
169
|
+
});
|
|
217
170
|
return response.content;
|
|
218
171
|
}
|
|
219
172
|
catch (error) {
|
|
220
|
-
logger.warn('LLM analysis failed', error);
|
|
173
|
+
logger.warn('LLM analysis failed after webcrack deobfuscation', error);
|
|
221
174
|
return null;
|
|
222
175
|
}
|
|
223
176
|
}
|
|
224
|
-
calculateConfidence(transformations, readabilityScore) {
|
|
225
|
-
const successCount = transformations.filter((t) => t.success).length;
|
|
226
|
-
const totalCount = transformations.length || 1;
|
|
227
|
-
const transformConfidence = successCount / totalCount;
|
|
228
|
-
const readabilityConfidence = readabilityScore / 100;
|
|
229
|
-
const confidence = transformConfidence * 0.6 + readabilityConfidence * 0.4;
|
|
230
|
-
return Math.min(Math.max(confidence, 0), 1);
|
|
231
|
-
}
|
|
232
|
-
async extractStringArrays(code, transformations) {
|
|
233
|
-
try {
|
|
234
|
-
const ast = parser.parse(code, {
|
|
235
|
-
sourceType: 'module',
|
|
236
|
-
plugins: ['jsx', 'typescript'],
|
|
237
|
-
});
|
|
238
|
-
let extracted = 0;
|
|
239
|
-
traverse(ast, {
|
|
240
|
-
VariableDeclarator: (path) => {
|
|
241
|
-
if (t.isIdentifier(path.node.id) &&
|
|
242
|
-
path.node.id.name.startsWith('_0x') &&
|
|
243
|
-
t.isArrayExpression(path.node.init)) {
|
|
244
|
-
const arrayName = path.node.id.name;
|
|
245
|
-
const strings = [];
|
|
246
|
-
path.node.init.elements.forEach((element) => {
|
|
247
|
-
if (t.isStringLiteral(element)) {
|
|
248
|
-
strings.push(element.value);
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
if (strings.length > 0) {
|
|
252
|
-
this.stringArrays.set(arrayName, strings);
|
|
253
|
-
extracted++;
|
|
254
|
-
logger.debug(`Extracted string array: ${arrayName} (${strings.length} strings)`);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
},
|
|
258
|
-
});
|
|
259
|
-
if (extracted > 0) {
|
|
260
|
-
transformations.push({
|
|
261
|
-
type: 'extract-string-arrays',
|
|
262
|
-
description: `Extracted ${extracted} string arrays`,
|
|
263
|
-
success: true,
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
return code;
|
|
267
|
-
}
|
|
268
|
-
catch (error) {
|
|
269
|
-
logger.warn('String array extraction failed', error);
|
|
270
|
-
transformations.push({
|
|
271
|
-
type: 'extract-string-arrays',
|
|
272
|
-
description: 'Failed to extract string arrays',
|
|
273
|
-
success: false,
|
|
274
|
-
});
|
|
275
|
-
return code;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
async decryptArrays(code, transformations) {
|
|
279
|
-
if (this.stringArrays.size === 0) {
|
|
280
|
-
return code;
|
|
281
|
-
}
|
|
282
|
-
const self = this;
|
|
283
|
-
try {
|
|
284
|
-
const ast = parser.parse(code, {
|
|
285
|
-
sourceType: 'module',
|
|
286
|
-
plugins: ['jsx', 'typescript'],
|
|
287
|
-
});
|
|
288
|
-
let replaced = 0;
|
|
289
|
-
traverse(ast, {
|
|
290
|
-
MemberExpression(path) {
|
|
291
|
-
if (t.isIdentifier(path.node.object) &&
|
|
292
|
-
t.isNumericLiteral(path.node.property) &&
|
|
293
|
-
path.node.object.name.startsWith('_0x')) {
|
|
294
|
-
const arrayName = path.node.object.name;
|
|
295
|
-
const index = path.node.property.value;
|
|
296
|
-
const stringArray = self.stringArrays.get(arrayName);
|
|
297
|
-
if (stringArray && index >= 0 && index < stringArray.length) {
|
|
298
|
-
const value = stringArray[index];
|
|
299
|
-
if (value !== undefined) {
|
|
300
|
-
path.replaceWith(t.stringLiteral(value));
|
|
301
|
-
replaced++;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
},
|
|
306
|
-
});
|
|
307
|
-
if (replaced > 0) {
|
|
308
|
-
const output = generate(ast, { comments: true, compact: false });
|
|
309
|
-
transformations.push({
|
|
310
|
-
type: 'decrypt-arrays',
|
|
311
|
-
description: `Replaced ${replaced} array references`,
|
|
312
|
-
success: true,
|
|
313
|
-
});
|
|
314
|
-
return output.code;
|
|
315
|
-
}
|
|
316
|
-
return code;
|
|
317
|
-
}
|
|
318
|
-
catch (error) {
|
|
319
|
-
logger.warn('Array decryption failed', error);
|
|
320
|
-
transformations.push({
|
|
321
|
-
type: 'decrypt-arrays',
|
|
322
|
-
description: 'Failed to decrypt arrays',
|
|
323
|
-
success: false,
|
|
324
|
-
});
|
|
325
|
-
return code;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
async unflattenControlFlow(code, transformations) {
|
|
329
|
-
try {
|
|
330
|
-
const ast = parser.parse(code, {
|
|
331
|
-
sourceType: 'module',
|
|
332
|
-
plugins: ['jsx', 'typescript'],
|
|
333
|
-
});
|
|
334
|
-
let unflattened = 0;
|
|
335
|
-
traverse(ast, {
|
|
336
|
-
WhileStatement(path) {
|
|
337
|
-
if (t.isSwitchStatement(path.node.body) ||
|
|
338
|
-
(t.isBlockStatement(path.node.body) &&
|
|
339
|
-
path.node.body.body.length === 1 &&
|
|
340
|
-
t.isSwitchStatement(path.node.body.body[0]))) {
|
|
341
|
-
logger.debug('Found control flow flattening pattern');
|
|
342
|
-
unflattened++;
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
});
|
|
346
|
-
if (unflattened > 0) {
|
|
347
|
-
transformations.push({
|
|
348
|
-
type: 'unflatten-control-flow',
|
|
349
|
-
description: `Unflattened ${unflattened} control flow patterns`,
|
|
350
|
-
success: true,
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
return code;
|
|
354
|
-
}
|
|
355
|
-
catch (error) {
|
|
356
|
-
logger.warn('Control flow unflattening failed', error);
|
|
357
|
-
transformations.push({
|
|
358
|
-
type: 'unflatten-control-flow',
|
|
359
|
-
description: 'Failed to unflatten control flow',
|
|
360
|
-
success: false,
|
|
361
|
-
});
|
|
362
|
-
return code;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
async simplifyExpressions(code, transformations) {
|
|
366
|
-
try {
|
|
367
|
-
const ast = parser.parse(code, {
|
|
368
|
-
sourceType: 'module',
|
|
369
|
-
plugins: ['jsx', 'typescript'],
|
|
370
|
-
});
|
|
371
|
-
let simplified = 0;
|
|
372
|
-
traverse(ast, {
|
|
373
|
-
UnaryExpression(path) {
|
|
374
|
-
if (path.node.operator === '!' &&
|
|
375
|
-
t.isUnaryExpression(path.node.argument) &&
|
|
376
|
-
path.node.argument.operator === '!') {
|
|
377
|
-
path.replaceWith(path.node.argument.argument);
|
|
378
|
-
simplified++;
|
|
379
|
-
}
|
|
380
|
-
else if (path.node.operator === 'void' &&
|
|
381
|
-
t.isNumericLiteral(path.node.argument, { value: 0 })) {
|
|
382
|
-
path.replaceWith(t.identifier('undefined'));
|
|
383
|
-
simplified++;
|
|
384
|
-
}
|
|
385
|
-
},
|
|
386
|
-
});
|
|
387
|
-
if (simplified > 0) {
|
|
388
|
-
const output = generate(ast, { comments: true, compact: false });
|
|
389
|
-
transformations.push({
|
|
390
|
-
type: 'simplify-expressions',
|
|
391
|
-
description: `Simplified ${simplified} expressions`,
|
|
392
|
-
success: true,
|
|
393
|
-
});
|
|
394
|
-
return output.code;
|
|
395
|
-
}
|
|
396
|
-
return code;
|
|
397
|
-
}
|
|
398
|
-
catch (error) {
|
|
399
|
-
logger.warn('Expression simplification failed', error);
|
|
400
|
-
transformations.push({
|
|
401
|
-
type: 'simplify-expressions',
|
|
402
|
-
description: 'Failed to simplify expressions',
|
|
403
|
-
success: false,
|
|
404
|
-
});
|
|
405
|
-
return code;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
async renameVariables(code, transformations) {
|
|
409
|
-
try {
|
|
410
|
-
const ast = parser.parse(code, {
|
|
411
|
-
sourceType: 'module',
|
|
412
|
-
plugins: ['jsx', 'typescript'],
|
|
413
|
-
});
|
|
414
|
-
let renamed = 0;
|
|
415
|
-
const renameMap = new Map();
|
|
416
|
-
traverse(ast, {
|
|
417
|
-
VariableDeclarator(path) {
|
|
418
|
-
if (t.isIdentifier(path.node.id) && path.node.id.name.startsWith('_0x')) {
|
|
419
|
-
const oldName = path.node.id.name;
|
|
420
|
-
const newName = `var_${renamed}`;
|
|
421
|
-
renameMap.set(oldName, newName);
|
|
422
|
-
path.node.id.name = newName;
|
|
423
|
-
renamed++;
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
|
-
Identifier(path) {
|
|
427
|
-
if (renameMap.has(path.node.name)) {
|
|
428
|
-
path.node.name = renameMap.get(path.node.name);
|
|
429
|
-
}
|
|
430
|
-
},
|
|
431
|
-
});
|
|
432
|
-
if (renamed > 0) {
|
|
433
|
-
const output = generate(ast, { comments: true, compact: false });
|
|
434
|
-
transformations.push({
|
|
435
|
-
type: 'rename-variables',
|
|
436
|
-
description: `Renamed ${renamed} variables`,
|
|
437
|
-
success: true,
|
|
438
|
-
});
|
|
439
|
-
return output.code;
|
|
440
|
-
}
|
|
441
|
-
return code;
|
|
442
|
-
}
|
|
443
|
-
catch (error) {
|
|
444
|
-
logger.warn('Variable renaming failed', error);
|
|
445
|
-
transformations.push({
|
|
446
|
-
type: 'rename-variables',
|
|
447
|
-
description: 'Failed to rename variables',
|
|
448
|
-
success: false,
|
|
449
|
-
});
|
|
450
|
-
return code;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
calculateReadabilityScore(code) {
|
|
454
|
-
return calculateReadabilityScoreUtil(code);
|
|
455
|
-
}
|
|
456
177
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DeobfuscateBundleSummary, DeobfuscateOptions, DeobfuscateSavedArtifact } from '../../types/deobfuscator.js';
|
|
2
|
+
type WebcrackInvocationOptions = Pick<DeobfuscateOptions, 'forceOutput' | 'includeModuleCode' | 'jsx' | 'mangle' | 'mappings' | 'maxBundleModules' | 'outputDir' | 'unminify' | 'unpack'>;
|
|
3
|
+
export interface WebcrackExecutionResult {
|
|
4
|
+
applied: boolean;
|
|
5
|
+
code: string;
|
|
6
|
+
bundle?: DeobfuscateBundleSummary;
|
|
7
|
+
savedTo?: string;
|
|
8
|
+
savedArtifacts?: DeobfuscateSavedArtifact[];
|
|
9
|
+
optionsUsed: Required<Pick<DeobfuscateOptions, 'jsx' | 'mangle' | 'unminify' | 'unpack'>>;
|
|
10
|
+
reason?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function runWebcrack(code: string, options: WebcrackInvocationOptions): Promise<WebcrackExecutionResult>;
|
|
13
|
+
export {};
|