@jshookmcp/jshook 0.2.8 → 0.3.0

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.
Files changed (162) hide show
  1. package/README.md +36 -5
  2. package/README.zh.md +36 -5
  3. package/dist/{AntiCheatDetector-S8VRj-dD.mjs → AntiCheatDetector-CqGDXmfc.mjs} +160 -54
  4. package/dist/{CodeInjector-4Z3ngPoX.mjs → CodeInjector-BdjRfNx7.mjs} +5 -5
  5. package/dist/ConsoleMonitor-DykL3IAw.mjs +2269 -0
  6. package/dist/{DarwinAPI-B8hg_yhz.mjs → DarwinAPI-ETyy0xyo.mjs} +1 -1
  7. package/dist/DetailedDataManager-HT49OrvF.mjs +217 -0
  8. package/dist/EventBus-DFKvADm3.mjs +141 -0
  9. package/dist/EvidenceGraphBridge-318Oi0Lf.mjs +153 -0
  10. package/dist/{ExtensionManager-D5-bO9D8.mjs → ExtensionManager-BDMsY2Dz.mjs} +27 -13
  11. package/dist/{FingerprintManager-BVxFJL2-.mjs → FingerprintManager-BN4UQWnX.mjs} +1 -1
  12. package/dist/{HardwareBreakpoint-DK1yjWkV.mjs → HardwareBreakpoint-Cc2AFq1Y.mjs} +3 -3
  13. package/dist/{HeapAnalyzer-CEbo10xU.mjs → HeapAnalyzer-DruMgsgj.mjs} +21 -21
  14. package/dist/HookGeneratorBuilders.core.generators.storage-CTbB4Lcx.mjs +566 -0
  15. package/dist/InstrumentationSession-DLH0vd-z.mjs +244 -0
  16. package/dist/{MemoryController-DdtnBdD4.mjs → MemoryController-CMtviNW_.mjs} +3 -3
  17. package/dist/{MemoryScanSession-RMixN3bX.mjs → MemoryScanSession-ITgb_NMi.mjs} +81 -78
  18. package/dist/{MemoryScanner-QjK4ld0B.mjs → MemoryScanner-CiL7Z3ey.mjs} +50 -21
  19. package/dist/{NativeMemoryManager.impl-CB6gJ0NM.mjs → NativeMemoryManager.impl-D9Lkovvn.mjs} +20 -56
  20. package/dist/{NativeMemoryManager.utils-BML4q1ry.mjs → NativeMemoryManager.utils-BBlAixF5.mjs} +1 -1
  21. package/dist/{PEAnalyzer-CK0xe0Fs.mjs → PEAnalyzer-DMQ44gen.mjs} +16 -16
  22. package/dist/PageController-BPJNqqBN.mjs +431 -0
  23. package/dist/{PointerChainEngine-Cd73qu5b.mjs → PointerChainEngine-K7wN8Z-w.mjs} +10 -7
  24. package/dist/PrerequisiteError-TuyZIs6n.mjs +20 -0
  25. package/dist/ProcessRegistry-zGg12QbE.mjs +74 -0
  26. package/dist/ResponseBuilder-CJXWmWNw.mjs +143 -0
  27. package/dist/ReverseEvidenceGraph-C02-gXOh.mjs +269 -0
  28. package/dist/ScriptManager-ZuWD-0Jg.mjs +3003 -0
  29. package/dist/{Speedhack-CeF0XmEz.mjs → Speedhack-D-z0umeT.mjs} +2 -2
  30. package/dist/{StructureAnalyzer-D4GkMduU.mjs → StructureAnalyzer-Cav5AVSL.mjs} +9 -6
  31. package/dist/ToolCatalog-5OJdMiF0.mjs +582 -0
  32. package/dist/ToolError-jh9whhMd.mjs +15 -0
  33. package/dist/ToolProbe-DbCFGyrg.mjs +45 -0
  34. package/dist/ToolRegistry-B9krbTtI.mjs +180 -0
  35. package/dist/ToolRouter.policy-BGDAGyeH.mjs +344 -0
  36. package/dist/TraceRecorder-B41Z5XBj.mjs +1286 -0
  37. package/dist/{Win32API-Bc0QnQsN.mjs → Win32API-C2kjj0ze.mjs} +19 -13
  38. package/dist/{Win32Debug-DUHt9XUn.mjs → Win32Debug-CKrGOTpo.mjs} +3 -3
  39. package/dist/WorkflowEngine-DJ6M4opp.mjs +569 -0
  40. package/dist/analysis-BHeJW2Nb.mjs +1234 -0
  41. package/dist/antidebug-BRKeyt27.mjs +1081 -0
  42. package/dist/artifactRetention-CPXkUJXp.mjs +598 -0
  43. package/dist/artifacts-DkfosXH3.mjs +59 -0
  44. package/dist/authorization-schema-DRqyJMSk.mjs +31 -0
  45. package/dist/betterSqlite3-DLSBZodi.mjs +74 -0
  46. package/dist/binary-instrument--V3MAhJ4.mjs +971 -0
  47. package/dist/bind-helpers-ClV34xdn.mjs +42 -0
  48. package/dist/boringssl-inspector-Bo_LOLaS.mjs +180 -0
  49. package/dist/browser-Dx3_S2cG.mjs +4369 -0
  50. package/dist/capabilities-CcHlvWgK.mjs +33 -0
  51. package/dist/concurrency-Drev_Vz9.mjs +41 -0
  52. package/dist/{constants-CCvsN80K.mjs → constants-CDZLOoVv.mjs} +105 -48
  53. package/dist/coordination-DgItD9DL.mjs +259 -0
  54. package/dist/debugger-RS3RSAqs.mjs +1288 -0
  55. package/dist/definitions-BEoYofW5.mjs +47 -0
  56. package/dist/definitions-BRaefg3u.mjs +365 -0
  57. package/dist/definitions-BbkvZkiv.mjs +96 -0
  58. package/dist/definitions-BtWSHJ3o.mjs +17 -0
  59. package/dist/definitions-C1gCHO0i.mjs +43 -0
  60. package/dist/definitions-CDOg_b-l.mjs +138 -0
  61. package/dist/definitions-CVPD9hzZ.mjs +54 -0
  62. package/dist/definitions-Cea8Lgl7.mjs +94 -0
  63. package/dist/definitions-DAgIyjxM.mjs +10 -0
  64. package/dist/definitions-DJA27nsL.mjs +66 -0
  65. package/dist/definitions-DKPFU3LW.mjs +25 -0
  66. package/dist/definitions-DPRpZQ96.mjs +47 -0
  67. package/dist/definitions-DUE5gmdn.mjs +18 -0
  68. package/dist/definitions-DYVjOtxa.mjs +26 -0
  69. package/dist/definitions-DcYLVLCo.mjs +37 -0
  70. package/dist/definitions-Pp5LI2H4.mjs +27 -0
  71. package/dist/definitions-j9KdHVNR.mjs +14 -0
  72. package/dist/definitions-uzkjBwa7.mjs +258 -0
  73. package/dist/definitions-va-AnLuQ.mjs +28 -0
  74. package/dist/encoding-DJeqHmpd.mjs +1079 -0
  75. package/dist/evidence-graph-bridge-DcYizFk2.mjs +136 -0
  76. package/dist/{factory-CibqTNC8.mjs → factory-C90tBff6.mjs} +41 -56
  77. package/dist/flat-target-session-Dgax2Cy3.mjs +29 -0
  78. package/dist/graphql-CoHrhweh.mjs +1197 -0
  79. package/dist/handlers-4jmR0nMs.mjs +898 -0
  80. package/dist/handlers-BAHPxcch.mjs +789 -0
  81. package/dist/handlers-BOs9b907.mjs +2600 -0
  82. package/dist/handlers-BWXEy6ef.mjs +917 -0
  83. package/dist/handlers-Bndn6QvE.mjs +111 -0
  84. package/dist/handlers-BqC4bD4s.mjs +681 -0
  85. package/dist/handlers-BtYq60bM2.mjs +276 -0
  86. package/dist/handlers-BzgcB4iv.mjs +799 -0
  87. package/dist/handlers-CRyRWj2b.mjs +859 -0
  88. package/dist/handlers-CVv2H1uq.mjs +592 -0
  89. package/dist/handlers-Dl5a7JS4.mjs +572 -0
  90. package/dist/handlers-Dx2d7jt7.mjs +2537 -0
  91. package/dist/handlers-Dz9PYsCa.mjs +2805 -0
  92. package/dist/handlers-HujRKC3b.mjs +661 -0
  93. package/dist/handlers.impl-XWXkQfyi.mjs +807 -0
  94. package/dist/hooks-B1B8NRHL.mjs +898 -0
  95. package/dist/index.mjs +491 -259
  96. package/dist/{logger-BmWzC2lM.mjs → logger-Dh_xb7_2.mjs} +14 -6
  97. package/dist/maintenance-PRMkLVRW.mjs +835 -0
  98. package/dist/manifest-67Bok-Si.mjs +58 -0
  99. package/dist/manifest-6lNTMZAB2.mjs +87 -0
  100. package/dist/manifest-B2duEHiH.mjs +90 -0
  101. package/dist/manifest-B6EY9Vm8.mjs +57 -0
  102. package/dist/manifest-B6nKSbyY.mjs +95 -0
  103. package/dist/manifest-BL8AQNPF.mjs +106 -0
  104. package/dist/manifest-BSZvJJmV.mjs +47 -0
  105. package/dist/manifest-BU7qzUyX.mjs +418 -0
  106. package/dist/manifest-Bl62e8WK.mjs +49 -0
  107. package/dist/manifest-Bo5cXjdt.mjs +82 -0
  108. package/dist/manifest-BpS4gtUK.mjs +1347 -0
  109. package/dist/manifest-Bv65_e2W.mjs +101 -0
  110. package/dist/manifest-BytNIF4Z.mjs +117 -0
  111. package/dist/manifest-C-xtsjS3.mjs +81 -0
  112. package/dist/manifest-CDYl7OhA.mjs +66 -0
  113. package/dist/manifest-CRZ3xmkD.mjs +61 -0
  114. package/dist/manifest-CoW6u4Tp.mjs +132 -0
  115. package/dist/manifest-Cq5zN_8A.mjs +50 -0
  116. package/dist/manifest-D7YZM_2e.mjs +194 -0
  117. package/dist/manifest-DE_VrAeQ.mjs +314 -0
  118. package/dist/manifest-DGsXSCpT.mjs +39 -0
  119. package/dist/manifest-DJ2vfEuW.mjs +156 -0
  120. package/dist/manifest-DPXDYhEu.mjs +80 -0
  121. package/dist/manifest-Dd4fQb0a.mjs +322 -0
  122. package/dist/manifest-Deq6opGg.mjs +223 -0
  123. package/dist/manifest-DfJTafJK.mjs +37 -0
  124. package/dist/manifest-DgOdgN_j.mjs +50 -0
  125. package/dist/manifest-DlbMW4v4.mjs +47 -0
  126. package/dist/manifest-DmVfbH0w.mjs +374 -0
  127. package/dist/manifest-Dog6Ddjr.mjs +109 -0
  128. package/dist/manifest-DvgU5FWb.mjs +58 -0
  129. package/dist/manifest-HsfDBs7j.mjs +50 -0
  130. package/dist/manifest-I8oQHvCG.mjs +186 -0
  131. package/dist/manifest-NvH_a-av.mjs +786 -0
  132. package/dist/manifest-cEJU1v0Z.mjs +129 -0
  133. package/dist/manifest-wOl5XLB12.mjs +112 -0
  134. package/dist/modules-tZozf0LQ.mjs +10635 -0
  135. package/dist/mojo-ipc-DXNEXEqb.mjs +640 -0
  136. package/dist/network-CPVvwvFg.mjs +3852 -0
  137. package/dist/{artifacts-BbdOMET5.mjs → outputPaths-um7lCRY3.mjs} +219 -216
  138. package/dist/parse-args-B4cY5Vx5.mjs +39 -0
  139. package/dist/platform-CYeFoTWp.mjs +2161 -0
  140. package/dist/process-BTbgcVc6.mjs +1306 -0
  141. package/dist/proxy-r8YN6nP1.mjs +192 -0
  142. package/dist/registry-Bl8ZQW61.mjs +34 -0
  143. package/dist/response-CWhh2aLo.mjs +34 -0
  144. package/dist/server/plugin-api.mjs +2 -2
  145. package/dist/shared-state-board-BoZnSoj-.mjs +586 -0
  146. package/dist/sourcemap-BIDHUVXy.mjs +934 -0
  147. package/dist/ssrf-policy-Dsqd-DTX.mjs +166 -0
  148. package/dist/streaming-Dal6utPp.mjs +725 -0
  149. package/dist/tool-builder-BHJp32mV.mjs +186 -0
  150. package/dist/transform-DRVgGG90.mjs +1011 -0
  151. package/dist/types-Bx92KJfT.mjs +4 -0
  152. package/dist/wasm-BYx5UOeG.mjs +1044 -0
  153. package/dist/webcrack-Be0_FccV.mjs +747 -0
  154. package/dist/workflow-BpuKEtvn.mjs +725 -0
  155. package/package.json +82 -49
  156. package/dist/ExtensionManager-CPTJhHFg.mjs +0 -2
  157. package/dist/ToolCatalog-Bq4V2sbJ.mjs +0 -67201
  158. package/dist/{CacheAdapters-CzFNpD9a.mjs → CacheAdapters-jJFy20G-.mjs} +0 -0
  159. package/dist/{StealthVerifier-BzBCFiwx.mjs → StealthVerifier-BWmPgQsv.mjs} +0 -0
  160. package/dist/{VersionDetector-CNXcvD46.mjs → VersionDetector-K3V4vGsw.mjs} +0 -0
  161. package/dist/{formatAddress-ChCSIRWT.mjs → formatAddress-nnMvEohD.mjs} +0 -0
  162. package/dist/{types-BBjOqye-.mjs → types-DDBWs9UP.mjs} +1 -1
@@ -0,0 +1,747 @@
1
+ import { t as logger } from "./logger-Dh_xb7_2.mjs";
2
+ import { _t as JSVMP_MAX_ITERATIONS, fn as SANDBOX_EXEC_TIMEOUT_MS, gn as SANDBOX_TERMINATE_GRACE_MS, gt as JSVMP_DEOBFUSCATE_TIMEOUT_MS, hn as SANDBOX_STACK_SIZE_MB, mn as SANDBOX_MEMORY_LIMIT_MB } from "./constants-CDZLOoVv.mjs";
3
+ import { t as ProcessRegistry } from "./ProcessRegistry-zGg12QbE.mjs";
4
+ import { n as cpuLimit } from "./concurrency-Drev_Vz9.mjs";
5
+ import path from "node:path";
6
+ import { readdir, rm, stat } from "node:fs/promises";
7
+ import * as parser from "@babel/parser";
8
+ import traverse from "@babel/traverse";
9
+ import * as t from "@babel/types";
10
+ import generate from "@babel/generator";
11
+ import { Worker } from "node:worker_threads";
12
+ //#region src/modules/security/ExecutionSandbox.ts
13
+ /**
14
+ * ExecutionSandbox — Safe code execution for untrusted JavaScript.
15
+ *
16
+ * Uses worker_threads + vm module to isolate dynamic code execution
17
+ * (e.g., deobfuscation eval, packer unpacking).
18
+ *
19
+ * Security:
20
+ * - Runs in a separate worker thread (process isolation)
21
+ * - vm.createContext with empty global (no require, no fs, no net)
22
+ * - Timeout enforcement via worker termination
23
+ * - Memory limit via worker resourceLimits
24
+ */
25
+ const WORKER_SCRIPT = `
26
+ import { workerData, parentPort } from 'node:worker_threads';
27
+ import * as vm from 'node:vm';
28
+
29
+ const { code, timeoutMs } = workerData;
30
+
31
+ try {
32
+ // Create an isolated context with minimal globals
33
+ const sandbox = {
34
+ // Safe built-ins only
35
+ parseInt, parseFloat, isNaN, isFinite,
36
+ encodeURIComponent, decodeURIComponent,
37
+ encodeURI, decodeURI,
38
+ JSON: { parse: JSON.parse, stringify: JSON.stringify },
39
+ Math,
40
+ String, Number, Boolean, Array, Object, Map, Set,
41
+ Date, RegExp, Error, TypeError, RangeError,
42
+ Promise,
43
+ Symbol,
44
+ undefined,
45
+ NaN,
46
+ Infinity,
47
+ // Explicitly denied: require, process, __filename, __dirname, Buffer, setTimeout, setInterval, fetch
48
+ };
49
+
50
+ const context = vm.createContext(sandbox, {
51
+ name: 'jshook-sandbox',
52
+ codeGeneration: { strings: false, wasm: false },
53
+ });
54
+
55
+ const script = new vm.Script(code, {
56
+ filename: 'sandbox-eval.js',
57
+ timeout: timeoutMs,
58
+ });
59
+
60
+ const result = script.runInContext(context, { timeout: timeoutMs });
61
+ parentPort.postMessage({ ok: true, output: result });
62
+ } catch (err) {
63
+ parentPort.postMessage({
64
+ ok: false,
65
+ error: err.message || String(err),
66
+ timedOut: err.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT',
67
+ });
68
+ }
69
+ `;
70
+ var ExecutionSandbox = class {
71
+ /**
72
+ * Execute JavaScript in an isolated sandbox.
73
+ * Wrapped in cpuLimit for global concurrency control.
74
+ */
75
+ async execute(request) {
76
+ return cpuLimit(() => this.executeInternal(request));
77
+ }
78
+ executeInternal(request) {
79
+ const timeoutMs = request.timeoutMs ?? SANDBOX_EXEC_TIMEOUT_MS;
80
+ const memoryLimitMB = request.memoryLimitMB ?? SANDBOX_MEMORY_LIMIT_MB;
81
+ const startTime = Date.now();
82
+ return new Promise((resolve) => {
83
+ let settled = false;
84
+ const terminationTimeout = setTimeout(() => {
85
+ if (!settled) {
86
+ worker.terminate();
87
+ logger.warn(`[ExecutionSandbox] Worker terminated after ${timeoutMs + SANDBOX_TERMINATE_GRACE_MS}ms`);
88
+ finish({
89
+ ok: false,
90
+ error: "Execution timed out (worker terminated)",
91
+ timedOut: true
92
+ });
93
+ }
94
+ }, timeoutMs + SANDBOX_TERMINATE_GRACE_MS);
95
+ const workerOptions = {
96
+ eval: true,
97
+ workerData: {
98
+ code: request.code,
99
+ timeoutMs
100
+ },
101
+ resourceLimits: {
102
+ maxOldGenerationSizeMb: memoryLimitMB,
103
+ maxYoungGenerationSizeMb: Math.ceil(memoryLimitMB / 4),
104
+ stackSizeMb: SANDBOX_STACK_SIZE_MB
105
+ }
106
+ };
107
+ workerOptions.type = "module";
108
+ const worker = new Worker(WORKER_SCRIPT, workerOptions);
109
+ if (typeof worker.unref === "function") worker.unref();
110
+ ProcessRegistry.register(worker);
111
+ const finish = (result) => {
112
+ if (settled) return;
113
+ settled = true;
114
+ if (terminationTimeout) clearTimeout(terminationTimeout);
115
+ resolve({
116
+ ...result,
117
+ durationMs: Date.now() - startTime
118
+ });
119
+ };
120
+ worker.on("message", (msg) => {
121
+ finish({
122
+ ok: msg.ok,
123
+ output: msg.output,
124
+ error: msg.error,
125
+ timedOut: msg.timedOut || false
126
+ });
127
+ worker.terminate();
128
+ });
129
+ worker.on("error", (err) => {
130
+ finish({
131
+ ok: false,
132
+ error: `Worker error: ${err.message}`,
133
+ timedOut: false
134
+ });
135
+ });
136
+ worker.on("exit", (code) => {
137
+ if (!settled) finish({
138
+ ok: false,
139
+ error: `Worker exited unexpectedly with code ${code}`,
140
+ timedOut: false
141
+ });
142
+ });
143
+ });
144
+ }
145
+ };
146
+ //#endregion
147
+ //#region src/modules/deobfuscator/JSVMPDeobfuscator.restore.ts
148
+ async function restoreJSVMPCode(context, code, vmType, aggressive) {
149
+ const warnings = [];
150
+ const unresolvedParts = [];
151
+ if (vmType === "obfuscator.io") return restoreObfuscatorIO(context, code, aggressive, warnings, unresolvedParts);
152
+ if (vmType === "jsfuck") return restoreJSFuck(context, code, warnings);
153
+ if (vmType === "jjencode") return restoreJJEncode(context, code, warnings);
154
+ return restoreCustomVM(context, code, aggressive, warnings, unresolvedParts);
155
+ }
156
+ async function restoreObfuscatorIO(context, code, aggressive, warnings, unresolvedParts) {
157
+ let restored = code;
158
+ let confidence = .5;
159
+ try {
160
+ const stringArrayMatch = code.match(/var\s+(_0x[a-f0-9]+)\s*=\s*(\[.*?\]);/s);
161
+ if (stringArrayMatch) {
162
+ const arrayName = stringArrayMatch[1];
163
+ const arrayContent = stringArrayMatch[2];
164
+ logger.info(` : ${arrayName}`);
165
+ try {
166
+ const sandboxResult = await context.sandbox.execute({
167
+ code: `return ${arrayContent || "[]"};`,
168
+ timeoutMs: 3e3
169
+ });
170
+ const stringArray = sandboxResult.ok ? sandboxResult.output : void 0;
171
+ if (Array.isArray(stringArray)) {
172
+ logger.info(`String array detected, ${stringArray.length} strings found`);
173
+ const refPattern = new RegExp(`${arrayName}\\[(\\d+)\\]`, "g");
174
+ restored = restored.replace(refPattern, (_match, index) => {
175
+ const idx = parseInt(index, 10);
176
+ if (idx < stringArray.length) return JSON.stringify(stringArray[idx]);
177
+ return _match;
178
+ });
179
+ confidence += .2;
180
+ }
181
+ } catch (e) {
182
+ warnings.push(`: ${e}`);
183
+ unresolvedParts.push({
184
+ location: "String Array",
185
+ reason: "",
186
+ suggestion: ""
187
+ });
188
+ }
189
+ }
190
+ restored = restored.replace(/\(function\s*\(_0x[a-f0-9]+,\s*_0x[a-f0-9]+\)\s*\{[\s\S]*?\}\(_0x[a-f0-9]+,\s*0x[a-f0-9]+\)\);?/g, "");
191
+ if (aggressive) {
192
+ restored = restored.replace(/\(function\s*\(\)\s*\{([\s\S]*)\}\(\)\);?/g, "$1");
193
+ confidence += .1;
194
+ }
195
+ restored = restored.replace(/0x([0-9a-f]+)/gi, (_match, hex) => {
196
+ return String(parseInt(hex, 16));
197
+ });
198
+ restored = restored.replace(/;\s*;/g, ";");
199
+ restored = restored.replace(/\{\s*\}/g, "{}");
200
+ warnings.push("obfuscator.io detected, may need special handling");
201
+ return {
202
+ code: restored,
203
+ confidence: Math.min(confidence, 1),
204
+ warnings,
205
+ unresolvedParts: unresolvedParts.length > 0 ? unresolvedParts : void 0
206
+ };
207
+ } catch (error) {
208
+ warnings.push(`obfuscator.io: ${error}`);
209
+ return {
210
+ code,
211
+ confidence: .2,
212
+ warnings,
213
+ unresolvedParts
214
+ };
215
+ }
216
+ }
217
+ async function restoreJSFuck(context, code, warnings) {
218
+ try {
219
+ logger.info("JSFuck detected, attempting deobfuscation...");
220
+ try {
221
+ if (code.length > 1e5) {
222
+ warnings.push("JSFuck code detected, file too large to process directly.");
223
+ warnings.push("Consider using an online JSFuck decoder tool.");
224
+ return {
225
+ code,
226
+ confidence: .1,
227
+ warnings
228
+ };
229
+ }
230
+ const sandboxResult = await context.sandbox.execute({
231
+ code: `return ${code};`,
232
+ timeoutMs: 5e3
233
+ });
234
+ const result = sandboxResult.ok ? sandboxResult.output : void 0;
235
+ if (typeof result === "string") {
236
+ logger.info(" JSFuck");
237
+ return {
238
+ code: result,
239
+ confidence: .9,
240
+ warnings: ["JSFuck"]
241
+ };
242
+ }
243
+ warnings.push("JSFuck");
244
+ return {
245
+ code,
246
+ confidence: .2,
247
+ warnings
248
+ };
249
+ } catch (execError) {
250
+ warnings.push(`JSFuck: ${execError}`);
251
+ warnings.push("Consider using an online JSFuck decoder tool.");
252
+ return {
253
+ code,
254
+ confidence: .1,
255
+ warnings
256
+ };
257
+ }
258
+ } catch (error) {
259
+ warnings.push(`JSFuck: ${error}`);
260
+ return {
261
+ code,
262
+ confidence: .1,
263
+ warnings
264
+ };
265
+ }
266
+ }
267
+ async function restoreJJEncode(context, code, warnings) {
268
+ try {
269
+ logger.info("JJEncode detected, attempting deobfuscation...");
270
+ try {
271
+ const lines = code.split("\n").filter((line) => line.trim());
272
+ if ((lines.length > 0 ? lines[lines.length - 1] : "")?.includes("$$$$")) {
273
+ const sandboxResult = await context.sandbox.execute({
274
+ code: `${code}; return $$$$()`,
275
+ timeoutMs: 5e3
276
+ });
277
+ const result = sandboxResult.ok ? sandboxResult.output : void 0;
278
+ if (typeof result === "string") {
279
+ logger.info(" JJEncode");
280
+ return {
281
+ code: result,
282
+ confidence: .9,
283
+ warnings: ["JJEncode"]
284
+ };
285
+ }
286
+ }
287
+ const sandboxResult = await context.sandbox.execute({
288
+ code,
289
+ timeoutMs: 5e3
290
+ });
291
+ if (!sandboxResult.ok) logger.warn("JJEncode sandbox execution failed:", sandboxResult.error);
292
+ warnings.push("JJEncode deobfuscation may be incomplete");
293
+ warnings.push("Result may still contain JJEncode fragments");
294
+ return {
295
+ code,
296
+ confidence: .2,
297
+ warnings
298
+ };
299
+ } catch (execError) {
300
+ warnings.push(`JJEncode: ${execError}`);
301
+ warnings.push("Result may contain evaluation artifacts");
302
+ return {
303
+ code,
304
+ confidence: .1,
305
+ warnings
306
+ };
307
+ }
308
+ } catch (error) {
309
+ warnings.push(`JJEncode: ${error}`);
310
+ return {
311
+ code,
312
+ confidence: .1,
313
+ warnings
314
+ };
315
+ }
316
+ }
317
+ async function restoreCustomVM(_context, code, aggressive, warnings, unresolvedParts) {
318
+ warnings.push("AI-assisted deobfuscation removed, using fallback directly.");
319
+ return restoreCustomVMBasic(code, aggressive, warnings, unresolvedParts);
320
+ }
321
+ function restoreCustomVMBasic(code, aggressive, warnings, unresolvedParts) {
322
+ let restored = code;
323
+ let confidence = .3;
324
+ try {
325
+ restored = restored.replace(/if\s*\([^)]*\)\s*\{\s*\}/g, "");
326
+ restored = restored.replace(/!!\s*\(/g, "Boolean(");
327
+ restored = restored.replace(/""\s*\+\s*/g, "");
328
+ if (aggressive) {
329
+ restored = restored.replace(/debugger;?/g, "");
330
+ confidence += .1;
331
+ restored = restored.replace(/\?\s*([^:]+)\s*:\s*\1/g, "$1");
332
+ confidence += .05;
333
+ }
334
+ warnings.push("Analysis incomplete, partial results may be returned");
335
+ warnings.push("For better results, configure an LLM API key");
336
+ unresolvedParts.push({
337
+ location: "Custom VM",
338
+ reason: "VM",
339
+ suggestion: "VM protection detected, LLM-assisted analysis recommended"
340
+ });
341
+ return {
342
+ code: restored,
343
+ confidence,
344
+ warnings,
345
+ unresolvedParts: unresolvedParts.length > 0 ? unresolvedParts : void 0
346
+ };
347
+ } catch (error) {
348
+ warnings.push(`: ${error}`);
349
+ return {
350
+ code,
351
+ confidence: .1,
352
+ warnings,
353
+ unresolvedParts
354
+ };
355
+ }
356
+ }
357
+ //#endregion
358
+ //#region src/modules/deobfuscator/JSVMPDeobfuscator.ts
359
+ var JSVMPDeobfuscator = class {
360
+ sandbox = new ExecutionSandbox();
361
+ async deobfuscate(options) {
362
+ const startTime = Date.now();
363
+ const { code, aggressive = false, extractInstructions = false, timeout = JSVMP_DEOBFUSCATE_TIMEOUT_MS, maxIterations = JSVMP_MAX_ITERATIONS } = options;
364
+ logger.info(" JSVMP...");
365
+ try {
366
+ const vmFeatures = this.detectJSVMP(code);
367
+ if (!vmFeatures) {
368
+ logger.info("JSVMP");
369
+ return {
370
+ isJSVMP: false,
371
+ deobfuscatedCode: code,
372
+ confidence: 0,
373
+ warnings: ["JSVMP"]
374
+ };
375
+ }
376
+ logger.info(`JSVMP analysis complete, complexity: ${vmFeatures.complexity}`);
377
+ logger.info(` : ${vmFeatures.instructionCount}`);
378
+ const vmType = this.identifyVMType(code, vmFeatures);
379
+ logger.info(` : ${vmType}`);
380
+ let instructions;
381
+ if (extractInstructions) {
382
+ logger.info(" ...");
383
+ instructions = this.extractInstructions(code, vmFeatures);
384
+ logger.info(` ${instructions.length} `);
385
+ }
386
+ logger.info(" ...");
387
+ const deobfuscationResult = await this.restoreCode(code, vmFeatures, vmType, aggressive, timeout, maxIterations);
388
+ const processingTime = Date.now() - startTime;
389
+ const result = {
390
+ isJSVMP: true,
391
+ vmType,
392
+ vmFeatures,
393
+ instructions,
394
+ deobfuscatedCode: deobfuscationResult.code,
395
+ confidence: deobfuscationResult.confidence,
396
+ warnings: deobfuscationResult.warnings,
397
+ unresolvedParts: deobfuscationResult.unresolvedParts,
398
+ stats: {
399
+ originalSize: code.length,
400
+ deobfuscatedSize: deobfuscationResult.code.length,
401
+ reductionRate: 1 - deobfuscationResult.code.length / code.length,
402
+ processingTime
403
+ }
404
+ };
405
+ logger.info(`JSVMP deobfuscation complete in ${processingTime}ms`);
406
+ logger.info(` : ${(result.confidence * 100).toFixed(1)}%`);
407
+ return result;
408
+ } catch (error) {
409
+ logger.error("JSVMP", error);
410
+ return {
411
+ isJSVMP: false,
412
+ deobfuscatedCode: code,
413
+ confidence: 0,
414
+ warnings: [`: ${error}`]
415
+ };
416
+ }
417
+ }
418
+ detectJSVMP(code) {
419
+ try {
420
+ const ast = parser.parse(code, {
421
+ sourceType: "unambiguous",
422
+ plugins: ["jsx", "typescript"],
423
+ errorRecovery: true
424
+ });
425
+ let hasSwitch = false;
426
+ let hasInstructionArray = false;
427
+ let hasProgramCounter = false;
428
+ let instructionCount = 0;
429
+ let interpreterLocation = "";
430
+ let maxSwitchCases = 0;
431
+ let hasBytecodeArray = false;
432
+ let hasApplyCall = false;
433
+ let hasWhileLoop = false;
434
+ let bytecodePattern = false;
435
+ traverse(ast, {
436
+ SwitchStatement(path) {
437
+ const caseCount = path.node.cases.length;
438
+ if (caseCount > 10) {
439
+ hasSwitch = true;
440
+ if (caseCount > maxSwitchCases) {
441
+ maxSwitchCases = caseCount;
442
+ instructionCount = caseCount;
443
+ interpreterLocation = `Line ${path.node.loc?.start.line || 0}`;
444
+ }
445
+ }
446
+ },
447
+ ArrayExpression(path) {
448
+ if (path.node.elements.length > 50) hasInstructionArray = true;
449
+ },
450
+ UpdateExpression(path) {
451
+ if (path.node.operator === "++" || path.node.operator === "--") {
452
+ const arg = path.node.argument;
453
+ if (t.isIdentifier(arg) && arg.name.length <= 3) hasProgramCounter = true;
454
+ }
455
+ },
456
+ CallExpression(path) {
457
+ if (t.isIdentifier(path.node.callee, { name: "parseInt" }) && path.node.arguments.length >= 2) {
458
+ const firstArg = path.node.arguments[0];
459
+ if (t.isBinaryExpression(firstArg) && firstArg.operator === "+") {
460
+ bytecodePattern = true;
461
+ hasBytecodeArray = true;
462
+ }
463
+ }
464
+ if (t.isMemberExpression(path.node.callee) && t.isIdentifier(path.node.callee.property, { name: "apply" })) hasApplyCall = true;
465
+ },
466
+ WhileStatement(path) {
467
+ if (t.isBooleanLiteral(path.node.test, { value: true }) || t.isNumericLiteral(path.node.test, { value: 1 })) hasWhileLoop = true;
468
+ },
469
+ ForStatement(path) {
470
+ if (!path.node.test) hasWhileLoop = true;
471
+ }
472
+ });
473
+ if (hasSwitch && (hasInstructionArray || hasProgramCounter) && (hasApplyCall || hasWhileLoop || bytecodePattern)) {
474
+ const complexity = instructionCount > 100 ? "high" : instructionCount > 50 ? "medium" : "low";
475
+ logger.info(" JSVMP:");
476
+ logger.info(` - Switch: ${hasSwitch} (${maxSwitchCases} cases)`);
477
+ logger.info(` - : ${hasInstructionArray}`);
478
+ logger.info(` - : ${hasProgramCounter}`);
479
+ logger.info(` - : ${hasBytecodeArray}`);
480
+ logger.info(` - Apply: ${hasApplyCall}`);
481
+ logger.info(` - : ${hasWhileLoop}`);
482
+ logger.info(` - : ${bytecodePattern}`);
483
+ return {
484
+ instructionCount,
485
+ interpreterLocation,
486
+ complexity,
487
+ hasSwitch,
488
+ hasInstructionArray,
489
+ hasProgramCounter
490
+ };
491
+ }
492
+ return null;
493
+ } catch (error) {
494
+ logger.warn("JSVMP analysis failed", error);
495
+ return this.detectJSVMPWithRegex(code);
496
+ }
497
+ }
498
+ detectJSVMPWithRegex(code) {
499
+ const hasSwitch = (code.match(/switch\s*\(/g)?.length || 0) > 0;
500
+ const bytecodePattern = /parseInt\s*\(\s*["']?\s*\+\s*\w+\[/g.test(code);
501
+ const applyPattern = /\.apply\s*\(/g.test(code);
502
+ const whilePattern = /while\s*\(\s*(true|1)\s*\)/g.test(code);
503
+ if (hasSwitch && (bytecodePattern || applyPattern || whilePattern)) {
504
+ logger.info(" JSVMP");
505
+ return {
506
+ instructionCount: 0,
507
+ interpreterLocation: "Unknown",
508
+ complexity: "medium",
509
+ hasSwitch: true,
510
+ hasInstructionArray: bytecodePattern,
511
+ hasProgramCounter: applyPattern
512
+ };
513
+ }
514
+ return null;
515
+ }
516
+ identifyVMType(code, _features) {
517
+ if (code.includes("_0x") && code.includes("function(_0x")) return "obfuscator.io";
518
+ if (/^\s*\[\s*\]\s*\[\s*\(/.test(code)) return "jsfuck";
519
+ if (code.includes("$=~[];")) return "jjencode";
520
+ return "custom";
521
+ }
522
+ extractInstructions(code, features) {
523
+ const instructions = [];
524
+ try {
525
+ traverse(parser.parse(code, {
526
+ sourceType: "unambiguous",
527
+ plugins: ["jsx", "typescript"]
528
+ }), { SwitchStatement: (path) => {
529
+ if (path.node.cases.length === features.instructionCount) path.node.cases.forEach((caseNode, index) => {
530
+ const opcode = caseNode.test ? t.isNumericLiteral(caseNode.test) ? caseNode.test.value : t.isStringLiteral(caseNode.test) ? caseNode.test.value : index : index;
531
+ const type = this.inferInstructionType(caseNode);
532
+ instructions.push({
533
+ opcode,
534
+ name: `INST_${opcode}`,
535
+ type,
536
+ description: `Instruction ${opcode}`
537
+ });
538
+ });
539
+ } });
540
+ } catch (error) {
541
+ logger.warn("", error);
542
+ }
543
+ return instructions;
544
+ }
545
+ inferInstructionType(caseNode) {
546
+ const code = generate(caseNode).code;
547
+ const consequent = caseNode.consequent;
548
+ let hasAssignment = false;
549
+ let hasArrayAccess = false;
550
+ let hasFunctionCall = false;
551
+ let hasArithmetic = false;
552
+ let hasControlFlow = false;
553
+ for (const stmt of consequent) {
554
+ if (t.isExpressionStatement(stmt)) {
555
+ const expr = stmt.expression;
556
+ if (t.isAssignmentExpression(expr)) hasAssignment = true;
557
+ if (t.isMemberExpression(expr) && t.isNumericLiteral(expr.property)) hasArrayAccess = true;
558
+ if (t.isCallExpression(expr)) hasFunctionCall = true;
559
+ if (t.isBinaryExpression(expr)) {
560
+ if ([
561
+ "+",
562
+ "-",
563
+ "*",
564
+ "/",
565
+ "%",
566
+ "**"
567
+ ].includes(expr.operator)) hasArithmetic = true;
568
+ }
569
+ }
570
+ if (t.isIfStatement(stmt) || t.isWhileStatement(stmt) || t.isBreakStatement(stmt) || t.isContinueStatement(stmt) || t.isReturnStatement(stmt)) hasControlFlow = true;
571
+ }
572
+ if ((code.includes("push") || code.includes(".push(")) && (hasArrayAccess || code.includes("["))) return "load";
573
+ if (hasAssignment && !hasArithmetic && !hasFunctionCall) return "store";
574
+ if (hasArithmetic || code.match(/[+\-*/%]/)) return "arithmetic";
575
+ if (hasControlFlow || code.includes("break") || code.includes("continue")) return "control";
576
+ if (hasFunctionCall || code.includes(".apply(") || code.includes(".call(")) return "call";
577
+ return "unknown";
578
+ }
579
+ async restoreCode(code, _features, vmType, aggressive, _timeout, _maxIterations) {
580
+ this.restoreCustomVMBasic;
581
+ return restoreJSVMPCode({ sandbox: this.sandbox }, code, vmType, aggressive);
582
+ }
583
+ restoreCustomVMBasic(code, aggressive, warnings, unresolvedParts) {
584
+ return restoreCustomVMBasic(code, aggressive, warnings, unresolvedParts);
585
+ }
586
+ };
587
+ //#endregion
588
+ //#region src/modules/deobfuscator/webcrack.ts
589
+ const DEFAULT_OPTIONS = {
590
+ jsx: true,
591
+ mangle: false,
592
+ unminify: true,
593
+ unpack: true
594
+ };
595
+ const MAX_BUNDLE_MODULES = 100;
596
+ async function importOptionalModule(specifier) {
597
+ return await import(specifier);
598
+ }
599
+ function normalizeOptions(options) {
600
+ return {
601
+ jsx: options.jsx ?? DEFAULT_OPTIONS.jsx,
602
+ mangle: options.mangle ?? DEFAULT_OPTIONS.mangle,
603
+ unminify: options.unminify ?? DEFAULT_OPTIONS.unminify,
604
+ unpack: options.unpack ?? DEFAULT_OPTIONS.unpack
605
+ };
606
+ }
607
+ function isSupportedNodeVersion() {
608
+ const [majorPart = "0", minorPart = "0"] = process.versions.node.split(".");
609
+ const major = Number.parseInt(majorPart, 10);
610
+ const minor = Number.parseInt(minorPart, 10);
611
+ if (!Number.isFinite(major) || !Number.isFinite(minor)) return false;
612
+ if (major === 20) return minor >= 19;
613
+ if (major === 22) return minor >= 12;
614
+ return major > 22;
615
+ }
616
+ function matchesRule(module, rule) {
617
+ const target = rule.target === "path" ? module.path : module.code;
618
+ const matchType = rule.matchType ?? "includes";
619
+ if (matchType === "exact") return target === rule.pattern;
620
+ if (matchType === "regex") try {
621
+ return new RegExp(rule.pattern, "m").test(target);
622
+ } catch {
623
+ return false;
624
+ }
625
+ return target.includes(rule.pattern);
626
+ }
627
+ function applyBundleMappings(bundle, mappings) {
628
+ const remapped = /* @__PURE__ */ new Map();
629
+ if (!mappings || mappings.length === 0) return remapped;
630
+ for (const module of bundle.modules.values()) for (const rule of mappings) {
631
+ if (!rule.path || !rule.pattern) continue;
632
+ if (matchesRule(module, rule)) {
633
+ if (module.path !== rule.path) {
634
+ remapped.set(module.id, { fromPath: module.path });
635
+ module.path = rule.path;
636
+ }
637
+ break;
638
+ }
639
+ }
640
+ return remapped;
641
+ }
642
+ function summarizeBundle(bundle, options, remapped) {
643
+ const maxBundleModules = options.maxBundleModules ?? MAX_BUNDLE_MODULES;
644
+ const modules = Array.from(bundle.modules.values()).toSorted((left, right) => {
645
+ if (left.isEntry !== right.isEntry) return left.isEntry ? -1 : 1;
646
+ return left.path.localeCompare(right.path);
647
+ }).slice(0, maxBundleModules).map((module) => ({
648
+ id: module.id,
649
+ path: module.path,
650
+ isEntry: module.isEntry,
651
+ size: module.code.length,
652
+ code: options.includeModuleCode ? module.code : void 0,
653
+ mappedPathFrom: remapped.get(module.id)?.fromPath
654
+ }));
655
+ return {
656
+ type: bundle.type,
657
+ entryId: bundle.entryId,
658
+ moduleCount: bundle.modules.size,
659
+ truncated: bundle.modules.size > maxBundleModules,
660
+ mappingsApplied: remapped.size,
661
+ modules
662
+ };
663
+ }
664
+ async function collectSavedArtifacts(rootDir, currentDir = rootDir) {
665
+ const entries = await readdir(currentDir, { withFileTypes: true });
666
+ const artifacts = [];
667
+ for (const entry of entries) {
668
+ const fullPath = path.join(currentDir, entry.name);
669
+ if (entry.isDirectory()) {
670
+ artifacts.push(...await collectSavedArtifacts(rootDir, fullPath));
671
+ continue;
672
+ }
673
+ if (!entry.isFile()) continue;
674
+ const metadata = await stat(fullPath);
675
+ artifacts.push({
676
+ path: path.relative(rootDir, fullPath).replace(/\\/g, "/"),
677
+ size: metadata.size,
678
+ type: "file"
679
+ });
680
+ }
681
+ return artifacts.toSorted((left, right) => left.path.localeCompare(right.path));
682
+ }
683
+ async function runWebcrack(code, options) {
684
+ const optionsUsed = normalizeOptions(options);
685
+ if (!isSupportedNodeVersion()) {
686
+ const reason = `webcrack requires Node.js 20.19+ or 22.12+; current runtime is ${process.versions.node}`;
687
+ logger.warn(reason);
688
+ return {
689
+ applied: false,
690
+ code,
691
+ optionsUsed,
692
+ reason
693
+ };
694
+ }
695
+ try {
696
+ await import("isolated-vm");
697
+ } catch {
698
+ logger.warn("isolated-vm is unavailable (likely Node 24 incompatibility). Deobfuscation sandbox is disabled — do not process untrusted code.");
699
+ }
700
+ try {
701
+ const { webcrack } = await importOptionalModule("webcrack");
702
+ const result = await webcrack(code, {
703
+ jsx: optionsUsed.jsx,
704
+ unpack: optionsUsed.unpack,
705
+ deobfuscate: true,
706
+ unminify: optionsUsed.unminify,
707
+ mangle: optionsUsed.mangle
708
+ });
709
+ const remapped = result.bundle ? applyBundleMappings(result.bundle, options.mappings) : /* @__PURE__ */ new Map();
710
+ let savedTo;
711
+ let savedArtifacts;
712
+ if (typeof options.outputDir === "string" && options.outputDir.trim().length > 0) {
713
+ savedTo = path.resolve(options.outputDir);
714
+ const cwd = process.cwd();
715
+ const relFromCwd = path.relative(cwd, savedTo);
716
+ if (path.isAbsolute(relFromCwd) || relFromCwd.startsWith("..") || savedTo === "/" || savedTo === path.parse(savedTo).root) throw new Error(`outputDir must resolve to a path within the project root. Got: ${savedTo}`);
717
+ if (options.forceOutput) await rm(savedTo, {
718
+ recursive: true,
719
+ force: true
720
+ });
721
+ await result.save(savedTo);
722
+ savedArtifacts = await collectSavedArtifacts(savedTo);
723
+ }
724
+ return {
725
+ applied: true,
726
+ code: result.code,
727
+ bundle: result.bundle ? summarizeBundle(result.bundle, {
728
+ includeModuleCode: options.includeModuleCode,
729
+ maxBundleModules: options.maxBundleModules
730
+ }, remapped) : void 0,
731
+ savedTo,
732
+ savedArtifacts,
733
+ optionsUsed
734
+ };
735
+ } catch (error) {
736
+ const reason = error instanceof Error ? error.message : String(error);
737
+ logger.warn("webcrack execution failed, falling back to legacy pipeline", error);
738
+ return {
739
+ applied: false,
740
+ code,
741
+ optionsUsed,
742
+ reason
743
+ };
744
+ }
745
+ }
746
+ //#endregion
747
+ export { JSVMPDeobfuscator as n, runWebcrack as t };