@ai-jshook/mcp 0.1.1
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/.env.example +38 -0
- package/CLAUDE.md +170 -0
- package/README.md +564 -0
- package/bun.lock +1484 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/analyzer/AISummarizer.d.ts +41 -0
- package/dist/modules/analyzer/AISummarizer.d.ts.map +1 -0
- package/dist/modules/analyzer/AISummarizer.js +186 -0
- package/dist/modules/analyzer/AISummarizer.js.map +1 -0
- package/dist/modules/analyzer/CodeAnalyzer.d.ts +28 -0
- package/dist/modules/analyzer/CodeAnalyzer.d.ts.map +1 -0
- package/dist/modules/analyzer/CodeAnalyzer.js +1287 -0
- package/dist/modules/analyzer/CodeAnalyzer.js.map +1 -0
- package/dist/modules/analyzer/IntelligentAnalyzer.d.ts +114 -0
- package/dist/modules/analyzer/IntelligentAnalyzer.d.ts.map +1 -0
- package/dist/modules/analyzer/IntelligentAnalyzer.js +1176 -0
- package/dist/modules/analyzer/IntelligentAnalyzer.js.map +1 -0
- package/dist/modules/browser/BrowserModeManager.d.ts +31 -0
- package/dist/modules/browser/BrowserModeManager.d.ts.map +1 -0
- package/dist/modules/browser/BrowserModeManager.js +241 -0
- package/dist/modules/browser/BrowserModeManager.js.map +1 -0
- package/dist/modules/captcha/AICaptchaDetector.d.ts +32 -0
- package/dist/modules/captcha/AICaptchaDetector.d.ts.map +1 -0
- package/dist/modules/captcha/AICaptchaDetector.js +387 -0
- package/dist/modules/captcha/AICaptchaDetector.js.map +1 -0
- package/dist/modules/captcha/CaptchaDetector.d.ts +28 -0
- package/dist/modules/captcha/CaptchaDetector.d.ts.map +1 -0
- package/dist/modules/captcha/CaptchaDetector.js +513 -0
- package/dist/modules/captcha/CaptchaDetector.js.map +1 -0
- package/dist/modules/collector/CodeCache.d.ts +37 -0
- package/dist/modules/collector/CodeCache.d.ts.map +1 -0
- package/dist/modules/collector/CodeCache.js +188 -0
- package/dist/modules/collector/CodeCache.js.map +1 -0
- package/dist/modules/collector/CodeCollector.d.ts +107 -0
- package/dist/modules/collector/CodeCollector.d.ts.map +1 -0
- package/dist/modules/collector/CodeCollector.js +796 -0
- package/dist/modules/collector/CodeCollector.js.map +1 -0
- package/dist/modules/collector/CodeCompressor.d.ts +65 -0
- package/dist/modules/collector/CodeCompressor.d.ts.map +1 -0
- package/dist/modules/collector/CodeCompressor.js +245 -0
- package/dist/modules/collector/CodeCompressor.js.map +1 -0
- package/dist/modules/collector/DOMInspector.d.ts +51 -0
- package/dist/modules/collector/DOMInspector.d.ts.map +1 -0
- package/dist/modules/collector/DOMInspector.js +437 -0
- package/dist/modules/collector/DOMInspector.js.map +1 -0
- package/dist/modules/collector/PageController.d.ts +79 -0
- package/dist/modules/collector/PageController.d.ts.map +1 -0
- package/dist/modules/collector/PageController.js +287 -0
- package/dist/modules/collector/PageController.js.map +1 -0
- package/dist/modules/collector/SmartCodeCollector.d.ts +38 -0
- package/dist/modules/collector/SmartCodeCollector.d.ts.map +1 -0
- package/dist/modules/collector/SmartCodeCollector.js +208 -0
- package/dist/modules/collector/SmartCodeCollector.js.map +1 -0
- package/dist/modules/collector/StreamingCollector.d.ts +46 -0
- package/dist/modules/collector/StreamingCollector.d.ts.map +1 -0
- package/dist/modules/collector/StreamingCollector.js +127 -0
- package/dist/modules/collector/StreamingCollector.js.map +1 -0
- package/dist/modules/crypto/CryptoDetector.d.ts +22 -0
- package/dist/modules/crypto/CryptoDetector.d.ts.map +1 -0
- package/dist/modules/crypto/CryptoDetector.js +168 -0
- package/dist/modules/crypto/CryptoDetector.js.map +1 -0
- package/dist/modules/crypto/CryptoDetectorEnhanced.d.ts +31 -0
- package/dist/modules/crypto/CryptoDetectorEnhanced.d.ts.map +1 -0
- package/dist/modules/crypto/CryptoDetectorEnhanced.js +269 -0
- package/dist/modules/crypto/CryptoDetectorEnhanced.js.map +1 -0
- package/dist/modules/crypto/CryptoRules.d.ts +59 -0
- package/dist/modules/crypto/CryptoRules.d.ts.map +1 -0
- package/dist/modules/crypto/CryptoRules.js +234 -0
- package/dist/modules/crypto/CryptoRules.js.map +1 -0
- package/dist/modules/debugger/BlackboxManager.d.ts +14 -0
- package/dist/modules/debugger/BlackboxManager.d.ts.map +1 -0
- package/dist/modules/debugger/BlackboxManager.js +98 -0
- package/dist/modules/debugger/BlackboxManager.js.map +1 -0
- package/dist/modules/debugger/DebuggerManager.d.ts +138 -0
- package/dist/modules/debugger/DebuggerManager.d.ts.map +1 -0
- package/dist/modules/debugger/DebuggerManager.js +777 -0
- package/dist/modules/debugger/DebuggerManager.js.map +1 -0
- package/dist/modules/debugger/EventBreakpointManager.d.ts +30 -0
- package/dist/modules/debugger/EventBreakpointManager.d.ts.map +1 -0
- package/dist/modules/debugger/EventBreakpointManager.js +125 -0
- package/dist/modules/debugger/EventBreakpointManager.js.map +1 -0
- package/dist/modules/debugger/RuntimeInspector.d.ts +54 -0
- package/dist/modules/debugger/RuntimeInspector.d.ts.map +1 -0
- package/dist/modules/debugger/RuntimeInspector.js +277 -0
- package/dist/modules/debugger/RuntimeInspector.js.map +1 -0
- package/dist/modules/debugger/ScriptManager.d.ts +94 -0
- package/dist/modules/debugger/ScriptManager.d.ts.map +1 -0
- package/dist/modules/debugger/ScriptManager.js +433 -0
- package/dist/modules/debugger/ScriptManager.js.map +1 -0
- package/dist/modules/debugger/WatchExpressionManager.d.ts +52 -0
- package/dist/modules/debugger/WatchExpressionManager.d.ts.map +1 -0
- package/dist/modules/debugger/WatchExpressionManager.js +136 -0
- package/dist/modules/debugger/WatchExpressionManager.js.map +1 -0
- package/dist/modules/debugger/XHRBreakpointManager.d.ts +21 -0
- package/dist/modules/debugger/XHRBreakpointManager.d.ts.map +1 -0
- package/dist/modules/debugger/XHRBreakpointManager.js +81 -0
- package/dist/modules/debugger/XHRBreakpointManager.js.map +1 -0
- package/dist/modules/deobfuscator/ASTOptimizer.d.ts +12 -0
- package/dist/modules/deobfuscator/ASTOptimizer.d.ts.map +1 -0
- package/dist/modules/deobfuscator/ASTOptimizer.js +234 -0
- package/dist/modules/deobfuscator/ASTOptimizer.js.map +1 -0
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.d.ts +52 -0
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.d.ts.map +1 -0
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.js +985 -0
- package/dist/modules/deobfuscator/AdvancedDeobfuscator.js.map +1 -0
- package/dist/modules/deobfuscator/Deobfuscator.d.ts +23 -0
- package/dist/modules/deobfuscator/Deobfuscator.d.ts.map +1 -0
- package/dist/modules/deobfuscator/Deobfuscator.js +487 -0
- package/dist/modules/deobfuscator/Deobfuscator.js.map +1 -0
- package/dist/modules/deobfuscator/JSVMPDeobfuscator.d.ts +19 -0
- package/dist/modules/deobfuscator/JSVMPDeobfuscator.d.ts.map +1 -0
- package/dist/modules/deobfuscator/JSVMPDeobfuscator.js +594 -0
- package/dist/modules/deobfuscator/JSVMPDeobfuscator.js.map +1 -0
- package/dist/modules/deobfuscator/JScramberDeobfuscator.d.ts +28 -0
- package/dist/modules/deobfuscator/JScramberDeobfuscator.d.ts.map +1 -0
- package/dist/modules/deobfuscator/JScramberDeobfuscator.js +239 -0
- package/dist/modules/deobfuscator/JScramberDeobfuscator.js.map +1 -0
- package/dist/modules/deobfuscator/PackerDeobfuscator.d.ts +38 -0
- package/dist/modules/deobfuscator/PackerDeobfuscator.d.ts.map +1 -0
- package/dist/modules/deobfuscator/PackerDeobfuscator.js +191 -0
- package/dist/modules/deobfuscator/PackerDeobfuscator.js.map +1 -0
- package/dist/modules/detector/ObfuscationDetector.d.ts +35 -0
- package/dist/modules/detector/ObfuscationDetector.d.ts.map +1 -0
- package/dist/modules/detector/ObfuscationDetector.js +278 -0
- package/dist/modules/detector/ObfuscationDetector.js.map +1 -0
- package/dist/modules/emulator/AIEnvironmentAnalyzer.d.ts +32 -0
- package/dist/modules/emulator/AIEnvironmentAnalyzer.d.ts.map +1 -0
- package/dist/modules/emulator/AIEnvironmentAnalyzer.js +548 -0
- package/dist/modules/emulator/AIEnvironmentAnalyzer.js.map +1 -0
- package/dist/modules/emulator/BrowserAPIDatabase.d.ts +34 -0
- package/dist/modules/emulator/BrowserAPIDatabase.d.ts.map +1 -0
- package/dist/modules/emulator/BrowserAPIDatabase.js +326 -0
- package/dist/modules/emulator/BrowserAPIDatabase.js.map +1 -0
- package/dist/modules/emulator/BrowserEnvironmentRules.d.ts +47 -0
- package/dist/modules/emulator/BrowserEnvironmentRules.d.ts.map +1 -0
- package/dist/modules/emulator/BrowserEnvironmentRules.js +493 -0
- package/dist/modules/emulator/BrowserEnvironmentRules.js.map +1 -0
- package/dist/modules/emulator/EnvironmentEmulator.d.ts +27 -0
- package/dist/modules/emulator/EnvironmentEmulator.d.ts.map +1 -0
- package/dist/modules/emulator/EnvironmentEmulator.js +1113 -0
- package/dist/modules/emulator/EnvironmentEmulator.js.map +1 -0
- package/dist/modules/emulator/EnvironmentEmulatorEnhanced.d.ts +26 -0
- package/dist/modules/emulator/EnvironmentEmulatorEnhanced.d.ts.map +1 -0
- package/dist/modules/emulator/EnvironmentEmulatorEnhanced.js +493 -0
- package/dist/modules/emulator/EnvironmentEmulatorEnhanced.js.map +1 -0
- package/dist/modules/emulator/templates/chrome-env.d.ts +260 -0
- package/dist/modules/emulator/templates/chrome-env.d.ts.map +1 -0
- package/dist/modules/emulator/templates/chrome-env.js +253 -0
- package/dist/modules/emulator/templates/chrome-env.js.map +1 -0
- package/dist/modules/hook/AIHookGenerator.d.ts +53 -0
- package/dist/modules/hook/AIHookGenerator.d.ts.map +1 -0
- package/dist/modules/hook/AIHookGenerator.js +353 -0
- package/dist/modules/hook/AIHookGenerator.js.map +1 -0
- package/dist/modules/hook/HookManager.d.ts +67 -0
- package/dist/modules/hook/HookManager.d.ts.map +1 -0
- package/dist/modules/hook/HookManager.js +1225 -0
- package/dist/modules/hook/HookManager.js.map +1 -0
- package/dist/modules/monitor/ConsoleMonitor.d.ts +140 -0
- package/dist/modules/monitor/ConsoleMonitor.d.ts.map +1 -0
- package/dist/modules/monitor/ConsoleMonitor.js +834 -0
- package/dist/modules/monitor/ConsoleMonitor.js.map +1 -0
- package/dist/modules/monitor/PerformanceMonitor.d.ts +65 -0
- package/dist/modules/monitor/PerformanceMonitor.d.ts.map +1 -0
- package/dist/modules/monitor/PerformanceMonitor.js +175 -0
- package/dist/modules/monitor/PerformanceMonitor.js.map +1 -0
- package/dist/modules/stealth/StealthScripts2025.d.ts +17 -0
- package/dist/modules/stealth/StealthScripts2025.d.ts.map +1 -0
- package/dist/modules/stealth/StealthScripts2025.js +274 -0
- package/dist/modules/stealth/StealthScripts2025.js.map +1 -0
- package/dist/modules/symbolic/JSVMPSymbolicExecutor.d.ts +69 -0
- package/dist/modules/symbolic/JSVMPSymbolicExecutor.d.ts.map +1 -0
- package/dist/modules/symbolic/JSVMPSymbolicExecutor.js +232 -0
- package/dist/modules/symbolic/JSVMPSymbolicExecutor.js.map +1 -0
- package/dist/modules/symbolic/SymbolicExecutor.d.ts +69 -0
- package/dist/modules/symbolic/SymbolicExecutor.d.ts.map +1 -0
- package/dist/modules/symbolic/SymbolicExecutor.js +346 -0
- package/dist/modules/symbolic/SymbolicExecutor.js.map +1 -0
- package/dist/server/AIHookToolDefinitions.d.ts +3 -0
- package/dist/server/AIHookToolDefinitions.d.ts.map +1 -0
- package/dist/server/AIHookToolDefinitions.js +284 -0
- package/dist/server/AIHookToolDefinitions.js.map +1 -0
- package/dist/server/AIHookToolHandlers.d.ts +50 -0
- package/dist/server/AIHookToolHandlers.d.ts.map +1 -0
- package/dist/server/AIHookToolHandlers.js +311 -0
- package/dist/server/AIHookToolHandlers.js.map +1 -0
- package/dist/server/AdvancedToolDefinitions.d.ts +3 -0
- package/dist/server/AdvancedToolDefinitions.d.ts.map +1 -0
- package/dist/server/AdvancedToolDefinitions.js +218 -0
- package/dist/server/AdvancedToolDefinitions.js.map +1 -0
- package/dist/server/AdvancedToolHandlers.d.ts +85 -0
- package/dist/server/AdvancedToolHandlers.d.ts.map +1 -0
- package/dist/server/AdvancedToolHandlers.js +431 -0
- package/dist/server/AdvancedToolHandlers.js.map +1 -0
- package/dist/server/BrowserToolDefinitions.d.ts +3 -0
- package/dist/server/BrowserToolDefinitions.d.ts.map +1 -0
- package/dist/server/BrowserToolDefinitions.js +841 -0
- package/dist/server/BrowserToolDefinitions.js.map +1 -0
- package/dist/server/BrowserToolHandlers.d.ts +290 -0
- package/dist/server/BrowserToolHandlers.d.ts.map +1 -0
- package/dist/server/BrowserToolHandlers.js +784 -0
- package/dist/server/BrowserToolHandlers.js.map +1 -0
- package/dist/server/CacheToolDefinitions.d.ts +3 -0
- package/dist/server/CacheToolDefinitions.d.ts.map +1 -0
- package/dist/server/CacheToolDefinitions.js +166 -0
- package/dist/server/CacheToolDefinitions.js.map +1 -0
- package/dist/server/DebuggerToolDefinitions.d.ts +3 -0
- package/dist/server/DebuggerToolDefinitions.d.ts.map +1 -0
- package/dist/server/DebuggerToolDefinitions.js +600 -0
- package/dist/server/DebuggerToolDefinitions.js.map +1 -0
- package/dist/server/DebuggerToolHandlers.d.ts +230 -0
- package/dist/server/DebuggerToolHandlers.d.ts.map +1 -0
- package/dist/server/DebuggerToolHandlers.js +935 -0
- package/dist/server/DebuggerToolHandlers.js.map +1 -0
- package/dist/server/MCPServer.d.ts +55 -0
- package/dist/server/MCPServer.d.ts.map +1 -0
- package/dist/server/MCPServer.js +1344 -0
- package/dist/server/MCPServer.js.map +1 -0
- package/dist/server/TokenBudgetToolDefinitions.d.ts +3 -0
- package/dist/server/TokenBudgetToolDefinitions.d.ts.map +1 -0
- package/dist/server/TokenBudgetToolDefinitions.js +114 -0
- package/dist/server/TokenBudgetToolDefinitions.js.map +1 -0
- package/dist/services/LLMService.d.ts +41 -0
- package/dist/services/LLMService.d.ts.map +1 -0
- package/dist/services/LLMService.js +792 -0
- package/dist/services/LLMService.js.map +1 -0
- package/dist/types/index.d.ts +527 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/AdaptiveDataSerializer.d.ts +27 -0
- package/dist/utils/AdaptiveDataSerializer.d.ts.map +1 -0
- package/dist/utils/AdaptiveDataSerializer.js +215 -0
- package/dist/utils/AdaptiveDataSerializer.js.map +1 -0
- package/dist/utils/CacheAdapters.d.ts +30 -0
- package/dist/utils/CacheAdapters.d.ts.map +1 -0
- package/dist/utils/CacheAdapters.js +83 -0
- package/dist/utils/CacheAdapters.js.map +1 -0
- package/dist/utils/TokenBudgetManager.d.ts +52 -0
- package/dist/utils/TokenBudgetManager.d.ts.map +1 -0
- package/dist/utils/TokenBudgetManager.js +190 -0
- package/dist/utils/TokenBudgetManager.js.map +1 -0
- package/dist/utils/UnifiedCacheManager.d.ts +55 -0
- package/dist/utils/UnifiedCacheManager.d.ts.map +1 -0
- package/dist/utils/UnifiedCacheManager.js +207 -0
- package/dist/utils/UnifiedCacheManager.js.map +1 -0
- package/dist/utils/cache.d.ts +13 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +92 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/config.d.ts +7 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +93 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/detailedDataManager.d.ts +60 -0
- package/dist/utils/detailedDataManager.d.ts.map +1 -0
- package/dist/utils/detailedDataManager.js +204 -0
- package/dist/utils/detailedDataManager.js.map +1 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +47 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/parallel.d.ts +40 -0
- package/dist/utils/parallel.d.ts.map +1 -0
- package/dist/utils/parallel.js +148 -0
- package/dist/utils/parallel.js.map +1 -0
- package/package.json +94 -0
- package/server.json +39 -0
- package/tsconfig.dev.json +14 -0
|
@@ -0,0 +1,985 @@
|
|
|
1
|
+
import { logger } from '../../utils/logger.js';
|
|
2
|
+
import * as parser from '@babel/parser';
|
|
3
|
+
import traverse from '@babel/traverse';
|
|
4
|
+
import generate from '@babel/generator';
|
|
5
|
+
import * as t from '@babel/types';
|
|
6
|
+
export class AdvancedDeobfuscator {
|
|
7
|
+
llm;
|
|
8
|
+
constructor(llm) {
|
|
9
|
+
this.llm = llm;
|
|
10
|
+
}
|
|
11
|
+
async deobfuscate(options) {
|
|
12
|
+
logger.info('Starting advanced deobfuscation...');
|
|
13
|
+
const startTime = Date.now();
|
|
14
|
+
let code = options.code;
|
|
15
|
+
const detectedTechniques = [];
|
|
16
|
+
const warnings = [];
|
|
17
|
+
let vmDetected;
|
|
18
|
+
let astOptimized = false;
|
|
19
|
+
try {
|
|
20
|
+
code = this.normalizeCode(code);
|
|
21
|
+
if (this.detectInvisibleUnicode(code)) {
|
|
22
|
+
detectedTechniques.push('invisible-unicode');
|
|
23
|
+
logger.info('Detected: Invisible Unicode Obfuscation (2025)');
|
|
24
|
+
code = this.decodeInvisibleUnicode(code);
|
|
25
|
+
}
|
|
26
|
+
if (this.detectStringEncoding(code)) {
|
|
27
|
+
detectedTechniques.push('string-encoding');
|
|
28
|
+
logger.info('Detected: String Encoding');
|
|
29
|
+
code = this.decodeStrings(code);
|
|
30
|
+
}
|
|
31
|
+
const vmInfo = this.detectVMProtection(code);
|
|
32
|
+
if (vmInfo.detected) {
|
|
33
|
+
detectedTechniques.push('vm-protection');
|
|
34
|
+
logger.info(`Detected: VM Protection (${vmInfo.type})`);
|
|
35
|
+
vmDetected = {
|
|
36
|
+
type: vmInfo.type,
|
|
37
|
+
instructions: vmInfo.instructionCount,
|
|
38
|
+
deobfuscated: false,
|
|
39
|
+
};
|
|
40
|
+
if (options.aggressiveVM) {
|
|
41
|
+
const vmResult = await this.deobfuscateVM(code, vmInfo);
|
|
42
|
+
if (vmResult.success) {
|
|
43
|
+
code = vmResult.code;
|
|
44
|
+
vmDetected.deobfuscated = true;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
warnings.push('VM deobfuscation failed, code may be incomplete');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (this.detectControlFlowFlattening(code)) {
|
|
52
|
+
detectedTechniques.push('control-flow-flattening');
|
|
53
|
+
logger.info('Detected: Control Flow Flattening');
|
|
54
|
+
code = await this.unflattenControlFlow(code);
|
|
55
|
+
}
|
|
56
|
+
if (this.detectStringArrayRotation(code)) {
|
|
57
|
+
detectedTechniques.push('string-array-rotation');
|
|
58
|
+
logger.info('Detected: String Array Rotation');
|
|
59
|
+
code = this.derotateStringArray(code);
|
|
60
|
+
}
|
|
61
|
+
if (this.detectDeadCodeInjection(code)) {
|
|
62
|
+
detectedTechniques.push('dead-code-injection');
|
|
63
|
+
logger.info('Detected: Dead Code Injection');
|
|
64
|
+
code = this.removeDeadCode(code);
|
|
65
|
+
}
|
|
66
|
+
if (this.detectOpaquePredicates(code)) {
|
|
67
|
+
detectedTechniques.push('opaque-predicates');
|
|
68
|
+
logger.info('Detected: Opaque Predicates');
|
|
69
|
+
code = this.removeOpaquePredicates(code);
|
|
70
|
+
}
|
|
71
|
+
if (options.useASTOptimization !== false) {
|
|
72
|
+
logger.info('Applying AST optimizations...');
|
|
73
|
+
const optimized = this.applyASTOptimizations(code);
|
|
74
|
+
if (optimized !== code) {
|
|
75
|
+
code = optimized;
|
|
76
|
+
astOptimized = true;
|
|
77
|
+
detectedTechniques.push('ast-optimized');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (this.llm && detectedTechniques.length > 0) {
|
|
81
|
+
logger.info('Using LLM for final cleanup...');
|
|
82
|
+
const llmResult = await this.llmCleanup(code, detectedTechniques);
|
|
83
|
+
if (llmResult) {
|
|
84
|
+
code = llmResult;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const duration = Date.now() - startTime;
|
|
88
|
+
const confidence = this.calculateConfidence(detectedTechniques, warnings, code);
|
|
89
|
+
logger.success(`Advanced deobfuscation completed in ${duration}ms`);
|
|
90
|
+
return {
|
|
91
|
+
code,
|
|
92
|
+
detectedTechniques,
|
|
93
|
+
confidence,
|
|
94
|
+
warnings,
|
|
95
|
+
vmDetected,
|
|
96
|
+
astOptimized,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
logger.error('Advanced deobfuscation failed', error);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
detectInvisibleUnicode(code) {
|
|
105
|
+
const invisibleChars = [
|
|
106
|
+
'\u200B',
|
|
107
|
+
'\u200C',
|
|
108
|
+
'\u200D',
|
|
109
|
+
'\u2060',
|
|
110
|
+
'\uFEFF',
|
|
111
|
+
];
|
|
112
|
+
return invisibleChars.some(char => code.includes(char));
|
|
113
|
+
}
|
|
114
|
+
decodeInvisibleUnicode(code) {
|
|
115
|
+
logger.info('Decoding invisible unicode...');
|
|
116
|
+
const charToBit = {
|
|
117
|
+
'\u200B': '0',
|
|
118
|
+
'\u200C': '1',
|
|
119
|
+
'\u200D': '00',
|
|
120
|
+
'\u2060': '01',
|
|
121
|
+
'\uFEFF': '10',
|
|
122
|
+
};
|
|
123
|
+
let decoded = code;
|
|
124
|
+
const invisiblePattern = /[\u200B\u200C\u200D\u2060\uFEFF]+/g;
|
|
125
|
+
const matches = code.match(invisiblePattern);
|
|
126
|
+
if (matches) {
|
|
127
|
+
matches.forEach(match => {
|
|
128
|
+
let binary = '';
|
|
129
|
+
for (const char of match) {
|
|
130
|
+
binary += charToBit[char] || '';
|
|
131
|
+
}
|
|
132
|
+
if (binary.length % 8 === 0) {
|
|
133
|
+
let text = '';
|
|
134
|
+
for (let i = 0; i < binary.length; i += 8) {
|
|
135
|
+
const byte = binary.substring(i, i + 8);
|
|
136
|
+
text += String.fromCharCode(parseInt(byte, 2));
|
|
137
|
+
}
|
|
138
|
+
decoded = decoded.replace(match, text);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return decoded;
|
|
143
|
+
}
|
|
144
|
+
detectVMProtection(code) {
|
|
145
|
+
const vmPatterns = [
|
|
146
|
+
/while\s*\(\s*true\s*\)\s*\{[\s\S]*?switch\s*\(/i,
|
|
147
|
+
/var\s+\w+\s*=\s*\[\s*\d+(?:\s*,\s*\d+){10,}\s*\]/i,
|
|
148
|
+
/\w+\[pc\+\+\]/i,
|
|
149
|
+
/stack\.push|stack\.pop/i,
|
|
150
|
+
];
|
|
151
|
+
const matchCount = vmPatterns.filter(pattern => pattern.test(code)).length;
|
|
152
|
+
if (matchCount >= 2) {
|
|
153
|
+
return {
|
|
154
|
+
detected: true,
|
|
155
|
+
type: matchCount >= 3 ? 'custom-vm' : 'simple-vm',
|
|
156
|
+
instructionCount: this.countVMInstructions(code),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return { detected: false, type: 'none', instructionCount: 0 };
|
|
160
|
+
}
|
|
161
|
+
countVMInstructions(code) {
|
|
162
|
+
const match = code.match(/case\s+\d+:/g);
|
|
163
|
+
return match ? match.length : 0;
|
|
164
|
+
}
|
|
165
|
+
async deobfuscateVM(code, vmInfo) {
|
|
166
|
+
logger.warn('VM deobfuscation is experimental and may fail');
|
|
167
|
+
try {
|
|
168
|
+
const vmStructure = this.analyzeVMStructure(code);
|
|
169
|
+
if (vmStructure.hasInterpreter) {
|
|
170
|
+
logger.info(`Detected VM interpreter with ${vmStructure.instructionTypes.length} instruction types`);
|
|
171
|
+
}
|
|
172
|
+
const vmComponents = this.extractVMComponents(code);
|
|
173
|
+
if (this.llm) {
|
|
174
|
+
const prompt = this.buildVMDeobfuscationPrompt(code, vmInfo, vmStructure, vmComponents);
|
|
175
|
+
const response = await this.llm.chat([
|
|
176
|
+
{
|
|
177
|
+
role: 'system',
|
|
178
|
+
content: `# Role
|
|
179
|
+
You are a world-class expert in JavaScript VM deobfuscation and reverse engineering with expertise in:
|
|
180
|
+
- Virtual machine architecture and instruction set design
|
|
181
|
+
- Bytecode interpretation and JIT compilation
|
|
182
|
+
- Control flow reconstruction from VM instructions
|
|
183
|
+
- Stack-based and register-based VM analysis
|
|
184
|
+
- Obfuscation techniques used by TikTok, Shopee, and commercial protectors
|
|
185
|
+
|
|
186
|
+
# Task
|
|
187
|
+
Analyze VM-protected JavaScript code and reconstruct the original, readable JavaScript.
|
|
188
|
+
|
|
189
|
+
# Methodology
|
|
190
|
+
1. **Identify VM Components**: Locate instruction array, interpreter loop, stack/registers
|
|
191
|
+
2. **Decode Instructions**: Map VM opcodes to JavaScript operations
|
|
192
|
+
3. **Reconstruct Control Flow**: Convert VM jumps/branches to if/while/for
|
|
193
|
+
4. **Simplify**: Remove VM overhead and restore natural code structure
|
|
194
|
+
5. **Validate**: Ensure output is syntactically valid and functionally equivalent
|
|
195
|
+
|
|
196
|
+
# Critical Requirements
|
|
197
|
+
- Output ONLY valid, executable JavaScript (no markdown, no explanations)
|
|
198
|
+
- Preserve exact program logic and side effects
|
|
199
|
+
- Use meaningful variable names based on context
|
|
200
|
+
- Add brief comments for complex patterns
|
|
201
|
+
- Do NOT hallucinate or guess functionality
|
|
202
|
+
- If uncertain, preserve original code structure
|
|
203
|
+
|
|
204
|
+
# Output Format
|
|
205
|
+
Return clean JavaScript code without any wrapper or formatting.`
|
|
206
|
+
},
|
|
207
|
+
{ role: 'user', content: prompt },
|
|
208
|
+
], {
|
|
209
|
+
temperature: 0.05,
|
|
210
|
+
maxTokens: 4000,
|
|
211
|
+
});
|
|
212
|
+
const deobfuscatedCode = this.extractCodeFromLLMResponse(response.content);
|
|
213
|
+
if (this.isValidJavaScript(deobfuscatedCode)) {
|
|
214
|
+
logger.success('VM deobfuscation succeeded via LLM');
|
|
215
|
+
return {
|
|
216
|
+
success: true,
|
|
217
|
+
code: deobfuscatedCode,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
logger.warn('LLM output is not valid JavaScript, falling back to original');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const simplifiedCode = this.simplifyVMCode(code, vmComponents);
|
|
225
|
+
return {
|
|
226
|
+
success: simplifiedCode !== code,
|
|
227
|
+
code: simplifiedCode
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
logger.error('VM deobfuscation failed', error);
|
|
232
|
+
return { success: false, code };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
analyzeVMStructure(code) {
|
|
236
|
+
const structure = {
|
|
237
|
+
hasInterpreter: false,
|
|
238
|
+
instructionTypes: [],
|
|
239
|
+
hasStack: false,
|
|
240
|
+
hasRegisters: false,
|
|
241
|
+
};
|
|
242
|
+
if (/while\s*\(\s*true\s*\)|for\s*\(\s*;\s*;\s*\)/.test(code)) {
|
|
243
|
+
structure.hasInterpreter = true;
|
|
244
|
+
}
|
|
245
|
+
const switchMatches = code.match(/case\s+0x[0-9a-f]+:/gi);
|
|
246
|
+
if (switchMatches && switchMatches.length > 10) {
|
|
247
|
+
structure.hasInterpreter = true;
|
|
248
|
+
structure.instructionTypes = switchMatches.map(m => m.replace(/case\s+/i, '').replace(/:/, ''));
|
|
249
|
+
}
|
|
250
|
+
if (/\.push\(|\.pop\(/.test(code)) {
|
|
251
|
+
structure.hasStack = true;
|
|
252
|
+
}
|
|
253
|
+
if (/r\d+\s*=|reg\[\d+\]/.test(code)) {
|
|
254
|
+
structure.hasRegisters = true;
|
|
255
|
+
}
|
|
256
|
+
return structure;
|
|
257
|
+
}
|
|
258
|
+
extractVMComponents(code) {
|
|
259
|
+
const components = {};
|
|
260
|
+
try {
|
|
261
|
+
const ast = parser.parse(code, {
|
|
262
|
+
sourceType: 'module',
|
|
263
|
+
plugins: ['jsx', 'typescript'],
|
|
264
|
+
});
|
|
265
|
+
traverse(ast, {
|
|
266
|
+
VariableDeclarator(path) {
|
|
267
|
+
if (t.isArrayExpression(path.node.init)) {
|
|
268
|
+
const arrayLength = path.node.init.elements.length;
|
|
269
|
+
if (arrayLength > 50) {
|
|
270
|
+
const arrayName = t.isIdentifier(path.node.id) ? path.node.id.name : 'unknown';
|
|
271
|
+
const firstElement = path.node.init.elements[0];
|
|
272
|
+
if (t.isNumericLiteral(firstElement)) {
|
|
273
|
+
components.instructionArray = arrayName;
|
|
274
|
+
}
|
|
275
|
+
else if (t.isStringLiteral(firstElement)) {
|
|
276
|
+
components.dataArray = arrayName;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
FunctionDeclaration(path) {
|
|
282
|
+
let hasBigSwitch = false;
|
|
283
|
+
traverse(path.node, {
|
|
284
|
+
SwitchStatement(switchPath) {
|
|
285
|
+
if (switchPath.node.cases.length > 10) {
|
|
286
|
+
hasBigSwitch = true;
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
}, path.scope, path);
|
|
290
|
+
if (hasBigSwitch && t.isIdentifier(path.node.id)) {
|
|
291
|
+
components.interpreterFunction = path.node.id.name;
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
logger.debug('Failed to extract VM components:', error);
|
|
298
|
+
}
|
|
299
|
+
return components;
|
|
300
|
+
}
|
|
301
|
+
buildVMDeobfuscationPrompt(code, vmInfo, vmStructure, vmComponents) {
|
|
302
|
+
const codeSnippet = code.length > 6000 ? code.substring(0, 6000) + '\n\n// ... (code truncated)' : code;
|
|
303
|
+
return `# VM Deobfuscation Analysis
|
|
304
|
+
|
|
305
|
+
## VM Profile
|
|
306
|
+
- **Architecture**: ${vmInfo.type}
|
|
307
|
+
- **Instruction Count**: ${vmInfo.instructionCount}
|
|
308
|
+
- **Interpreter Loop**: ${vmStructure.hasInterpreter ? 'Detected' : 'Not detected'}
|
|
309
|
+
- **Stack Operations**: ${vmStructure.hasStack ? 'Present' : 'Absent'}
|
|
310
|
+
- **Register Usage**: ${vmStructure.hasRegisters ? 'Present' : 'Absent'}
|
|
311
|
+
- **Instruction Variety**: ${vmStructure.instructionTypes.length} distinct types
|
|
312
|
+
|
|
313
|
+
## Identified Components
|
|
314
|
+
${vmComponents.instructionArray ? `✓ Instruction Array: Found at ${vmComponents.instructionArray}` : '✗ Instruction Array: Not found'}
|
|
315
|
+
${vmComponents.dataArray ? `✓ Data Array: Found at ${vmComponents.dataArray}` : '✗ Data Array: Not found'}
|
|
316
|
+
${vmComponents.interpreterFunction ? `✓ Interpreter Function: Found at ${vmComponents.interpreterFunction}` : '✗ Interpreter Function: Not found'}
|
|
317
|
+
|
|
318
|
+
## VM-Protected Code
|
|
319
|
+
\`\`\`javascript
|
|
320
|
+
${codeSnippet}
|
|
321
|
+
\`\`\`
|
|
322
|
+
|
|
323
|
+
## Deobfuscation Instructions (Chain-of-Thought)
|
|
324
|
+
|
|
325
|
+
### Step 1: VM Structure Analysis
|
|
326
|
+
Examine the code to identify:
|
|
327
|
+
- Instruction array (usually a large array of numbers/strings)
|
|
328
|
+
- Interpreter loop (while/for loop processing instructions)
|
|
329
|
+
- Stack/register variables
|
|
330
|
+
- Opcode handlers (switch-case or if-else chains)
|
|
331
|
+
|
|
332
|
+
### Step 2: Instruction Decoding
|
|
333
|
+
For each instruction type, determine:
|
|
334
|
+
- What JavaScript operation it represents (e.g., opcode 0x01 = addition)
|
|
335
|
+
- How it manipulates the stack/registers
|
|
336
|
+
- What side effects it has (function calls, property access, etc.)
|
|
337
|
+
|
|
338
|
+
### Step 3: Control Flow Reconstruction
|
|
339
|
+
- Map VM jumps/branches to JavaScript if/while/for statements
|
|
340
|
+
- Identify function calls and returns
|
|
341
|
+
- Reconstruct try-catch blocks if present
|
|
342
|
+
|
|
343
|
+
### Step 4: Code Generation
|
|
344
|
+
- Replace VM instruction sequences with equivalent JavaScript
|
|
345
|
+
- Use meaningful variable names based on usage context
|
|
346
|
+
- Remove VM overhead (interpreter loop, stack management)
|
|
347
|
+
- Preserve all side effects and program behavior
|
|
348
|
+
|
|
349
|
+
### Step 5: Validation
|
|
350
|
+
- Ensure output is syntactically valid JavaScript
|
|
351
|
+
- Verify no functionality is lost
|
|
352
|
+
- Add comments for complex patterns
|
|
353
|
+
|
|
354
|
+
## Example Transformation (Few-shot Learning)
|
|
355
|
+
|
|
356
|
+
**VM Code (Before)**:
|
|
357
|
+
\`\`\`javascript
|
|
358
|
+
var vm = [0x01, 0x05, 0x02, 0x03, 0x10];
|
|
359
|
+
var stack = [];
|
|
360
|
+
for(var i=0; i<vm.length; i++) {
|
|
361
|
+
switch(vm[i]) {
|
|
362
|
+
case 0x01: stack.push(5); break;
|
|
363
|
+
case 0x02: stack.push(3); break;
|
|
364
|
+
case 0x10: var b=stack.pop(), a=stack.pop(); stack.push(a+b); break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
console.log(stack[0]);
|
|
368
|
+
\`\`\`
|
|
369
|
+
|
|
370
|
+
**Deobfuscated Code (After)**:
|
|
371
|
+
\`\`\`javascript
|
|
372
|
+
// VM instructions decoded: PUSH 5, PUSH 3, ADD
|
|
373
|
+
var result = 5 + 3;
|
|
374
|
+
console.log(result);
|
|
375
|
+
\`\`\`
|
|
376
|
+
|
|
377
|
+
## Critical Requirements
|
|
378
|
+
1. Output ONLY the deobfuscated JavaScript code
|
|
379
|
+
2. NO markdown code blocks, NO explanations, NO comments outside the code
|
|
380
|
+
3. Code must be syntactically valid and executable
|
|
381
|
+
4. Preserve exact program logic and side effects
|
|
382
|
+
5. If full deobfuscation is impossible, return the best partial result
|
|
383
|
+
|
|
384
|
+
## Output Format
|
|
385
|
+
Return clean JavaScript code starting immediately (no preamble).`;
|
|
386
|
+
}
|
|
387
|
+
extractCodeFromLLMResponse(response) {
|
|
388
|
+
let code = response.trim();
|
|
389
|
+
code = code.replace(/^```(?:javascript|js)?\s*\n/i, '');
|
|
390
|
+
code = code.replace(/\n```\s*$/i, '');
|
|
391
|
+
return code.trim();
|
|
392
|
+
}
|
|
393
|
+
isValidJavaScript(code) {
|
|
394
|
+
try {
|
|
395
|
+
parser.parse(code, {
|
|
396
|
+
sourceType: 'module',
|
|
397
|
+
plugins: ['jsx', 'typescript'],
|
|
398
|
+
});
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
simplifyVMCode(code, vmComponents) {
|
|
406
|
+
try {
|
|
407
|
+
let simplified = code;
|
|
408
|
+
if (vmComponents.interpreterFunction) {
|
|
409
|
+
const regex = new RegExp(`function\\s+${vmComponents.interpreterFunction}\\s*\\([^)]*\\)\\s*\\{[^}]*\\}`, 'g');
|
|
410
|
+
simplified = simplified.replace(regex, '// VM interpreter removed');
|
|
411
|
+
}
|
|
412
|
+
if (vmComponents.instructionArray) {
|
|
413
|
+
const regex = new RegExp(`var\\s+${vmComponents.instructionArray}\\s*=\\s*\\[[^\\]]*\\];`, 'g');
|
|
414
|
+
simplified = simplified.replace(regex, '// VM instruction array removed');
|
|
415
|
+
}
|
|
416
|
+
return simplified;
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
logger.debug('Failed to simplify VM code:', error);
|
|
420
|
+
return code;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
detectControlFlowFlattening(code) {
|
|
424
|
+
const pattern = /while\s*\(\s*!!\s*\[\s*\]\s*\)\s*\{[\s\S]*?switch\s*\(/i;
|
|
425
|
+
return pattern.test(code);
|
|
426
|
+
}
|
|
427
|
+
async unflattenControlFlow(code) {
|
|
428
|
+
logger.info('Unflattening control flow...');
|
|
429
|
+
if (this.llm) {
|
|
430
|
+
try {
|
|
431
|
+
const codeSnippet = code.length > 3000 ? code.substring(0, 3000) + '\n\n// ... (truncated)' : code;
|
|
432
|
+
const response = await this.llm.chat([
|
|
433
|
+
{
|
|
434
|
+
role: 'system',
|
|
435
|
+
content: `# Role
|
|
436
|
+
You are an expert in JavaScript control flow deobfuscation specializing in:
|
|
437
|
+
- Control flow flattening detection and removal
|
|
438
|
+
- Switch-case state machine analysis
|
|
439
|
+
- Dispatcher loop identification
|
|
440
|
+
- Control flow graph (CFG) reconstruction
|
|
441
|
+
|
|
442
|
+
# Task
|
|
443
|
+
Analyze control flow flattened JavaScript and reconstruct the original, natural control flow.
|
|
444
|
+
|
|
445
|
+
# Control Flow Flattening Pattern
|
|
446
|
+
Obfuscators replace normal if/while/for with a dispatcher loop:
|
|
447
|
+
\`\`\`javascript
|
|
448
|
+
// Flattened (obfuscated)
|
|
449
|
+
var state = '0';
|
|
450
|
+
while (true) {
|
|
451
|
+
switch (state) {
|
|
452
|
+
case '0': console.log('a'); state = '1'; break;
|
|
453
|
+
case '1': console.log('b'); state = '2'; break;
|
|
454
|
+
case '2': return;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Original (deobfuscated)
|
|
459
|
+
console.log('a');
|
|
460
|
+
console.log('b');
|
|
461
|
+
return;
|
|
462
|
+
\`\`\`
|
|
463
|
+
|
|
464
|
+
# Requirements
|
|
465
|
+
- Output ONLY valid JavaScript code
|
|
466
|
+
- Preserve exact program logic
|
|
467
|
+
- Remove dispatcher loops and state variables
|
|
468
|
+
- Restore natural if/while/for structures
|
|
469
|
+
- Use meaningful variable names`
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
role: 'user',
|
|
473
|
+
content: `# Control Flow Flattened Code
|
|
474
|
+
\`\`\`javascript
|
|
475
|
+
${codeSnippet}
|
|
476
|
+
\`\`\`
|
|
477
|
+
|
|
478
|
+
# Instructions
|
|
479
|
+
1. Identify the dispatcher loop (while/for with switch-case)
|
|
480
|
+
2. Trace state transitions to determine execution order
|
|
481
|
+
3. Reconstruct original control flow (if/while/for)
|
|
482
|
+
4. Remove state variables and dispatcher overhead
|
|
483
|
+
5. Return ONLY the deobfuscated code (no explanations)
|
|
484
|
+
|
|
485
|
+
Output the deobfuscated JavaScript code:`
|
|
486
|
+
},
|
|
487
|
+
], {
|
|
488
|
+
temperature: 0.1,
|
|
489
|
+
maxTokens: 3000,
|
|
490
|
+
});
|
|
491
|
+
return this.extractCodeFromLLMResponse(response.content);
|
|
492
|
+
}
|
|
493
|
+
catch (error) {
|
|
494
|
+
logger.warn('LLM control flow unflattening failed', error);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return code;
|
|
498
|
+
}
|
|
499
|
+
detectStringArrayRotation(code) {
|
|
500
|
+
return /\w+\s*=\s*\w+\s*\+\s*0x[0-9a-f]+/.test(code);
|
|
501
|
+
}
|
|
502
|
+
derotateStringArray(code) {
|
|
503
|
+
logger.info('Derotating string array...');
|
|
504
|
+
try {
|
|
505
|
+
const ast = parser.parse(code, {
|
|
506
|
+
sourceType: 'module',
|
|
507
|
+
plugins: ['jsx', 'typescript'],
|
|
508
|
+
});
|
|
509
|
+
let derotated = 0;
|
|
510
|
+
traverse(ast, {
|
|
511
|
+
CallExpression(path) {
|
|
512
|
+
if (!t.isFunctionExpression(path.node.callee) &&
|
|
513
|
+
!t.isArrowFunctionExpression(path.node.callee)) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const func = path.node.callee;
|
|
517
|
+
if (!t.isFunctionExpression(func) || !t.isBlockStatement(func.body)) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const hasWhileLoop = func.body.body.some(stmt => t.isWhileStatement(stmt));
|
|
521
|
+
const hasArrayRotation = JSON.stringify(func.body).includes('push') &&
|
|
522
|
+
JSON.stringify(func.body).includes('shift');
|
|
523
|
+
if (hasWhileLoop && hasArrayRotation) {
|
|
524
|
+
logger.debug('Found string array rotation IIFE');
|
|
525
|
+
path.remove();
|
|
526
|
+
derotated++;
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
if (derotated > 0) {
|
|
531
|
+
logger.info(`Removed ${derotated} string array rotation functions`);
|
|
532
|
+
return generate(ast, { comments: true, compact: false }).code;
|
|
533
|
+
}
|
|
534
|
+
return code;
|
|
535
|
+
}
|
|
536
|
+
catch (error) {
|
|
537
|
+
logger.error('Failed to derotate string array:', error);
|
|
538
|
+
return code;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
detectDeadCodeInjection(code) {
|
|
542
|
+
return /if\s*\(\s*false\s*\)|if\s*\(\s*!!\s*\[\s*\]\s*\)/.test(code);
|
|
543
|
+
}
|
|
544
|
+
removeDeadCode(code) {
|
|
545
|
+
logger.info('Removing dead code...');
|
|
546
|
+
try {
|
|
547
|
+
const ast = parser.parse(code, {
|
|
548
|
+
sourceType: 'module',
|
|
549
|
+
plugins: ['jsx', 'typescript'],
|
|
550
|
+
});
|
|
551
|
+
let removed = 0;
|
|
552
|
+
traverse(ast, {
|
|
553
|
+
IfStatement(path) {
|
|
554
|
+
const test = path.node.test;
|
|
555
|
+
if (t.isBooleanLiteral(test) && test.value === false) {
|
|
556
|
+
if (path.node.alternate) {
|
|
557
|
+
path.replaceWith(path.node.alternate);
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
path.remove();
|
|
561
|
+
}
|
|
562
|
+
removed++;
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (t.isBooleanLiteral(test) && test.value === true) {
|
|
566
|
+
path.replaceWith(path.node.consequent);
|
|
567
|
+
removed++;
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
if (t.isUnaryExpression(test) && test.operator === '!' &&
|
|
571
|
+
t.isUnaryExpression(test.argument) && test.argument.operator === '!' &&
|
|
572
|
+
t.isArrayExpression(test.argument.argument)) {
|
|
573
|
+
path.replaceWith(path.node.consequent);
|
|
574
|
+
removed++;
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
BlockStatement(path) {
|
|
579
|
+
const body = path.node.body;
|
|
580
|
+
let foundTerminator = false;
|
|
581
|
+
const newBody = [];
|
|
582
|
+
for (const stmt of body) {
|
|
583
|
+
if (foundTerminator) {
|
|
584
|
+
removed++;
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
newBody.push(stmt);
|
|
588
|
+
if (t.isReturnStatement(stmt) || t.isThrowStatement(stmt)) {
|
|
589
|
+
foundTerminator = true;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (newBody.length < body.length) {
|
|
593
|
+
path.node.body = newBody;
|
|
594
|
+
}
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
if (removed > 0) {
|
|
598
|
+
logger.info(`Removed ${removed} dead code blocks`);
|
|
599
|
+
return generate(ast, { comments: true, compact: false }).code;
|
|
600
|
+
}
|
|
601
|
+
return code;
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
logger.error('Failed to remove dead code:', error);
|
|
605
|
+
return code;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
detectOpaquePredicates(code) {
|
|
609
|
+
return /if\s*\(\s*\d+\s*[<>!=]+\s*\d+\s*\)/.test(code);
|
|
610
|
+
}
|
|
611
|
+
removeOpaquePredicates(code) {
|
|
612
|
+
logger.info('Removing opaque predicates...');
|
|
613
|
+
try {
|
|
614
|
+
const ast = parser.parse(code, {
|
|
615
|
+
sourceType: 'module',
|
|
616
|
+
plugins: ['jsx', 'typescript'],
|
|
617
|
+
});
|
|
618
|
+
let removed = 0;
|
|
619
|
+
traverse(ast, {
|
|
620
|
+
IfStatement(path) {
|
|
621
|
+
const test = path.node.test;
|
|
622
|
+
if (t.isBinaryExpression(test)) {
|
|
623
|
+
const left = test.left;
|
|
624
|
+
const right = test.right;
|
|
625
|
+
const operator = test.operator;
|
|
626
|
+
if (t.isNumericLiteral(left) && t.isNumericLiteral(right)) {
|
|
627
|
+
let result;
|
|
628
|
+
switch (operator) {
|
|
629
|
+
case '>':
|
|
630
|
+
result = left.value > right.value;
|
|
631
|
+
break;
|
|
632
|
+
case '<':
|
|
633
|
+
result = left.value < right.value;
|
|
634
|
+
break;
|
|
635
|
+
case '>=':
|
|
636
|
+
result = left.value >= right.value;
|
|
637
|
+
break;
|
|
638
|
+
case '<=':
|
|
639
|
+
result = left.value <= right.value;
|
|
640
|
+
break;
|
|
641
|
+
case '===':
|
|
642
|
+
case '==':
|
|
643
|
+
result = left.value === right.value;
|
|
644
|
+
break;
|
|
645
|
+
case '!==':
|
|
646
|
+
case '!=':
|
|
647
|
+
result = left.value !== right.value;
|
|
648
|
+
break;
|
|
649
|
+
}
|
|
650
|
+
if (result !== undefined) {
|
|
651
|
+
if (result) {
|
|
652
|
+
path.replaceWith(path.node.consequent);
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
if (path.node.alternate) {
|
|
656
|
+
path.replaceWith(path.node.alternate);
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
path.remove();
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
removed++;
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (t.isBinaryExpression(test) && (test.operator === '===' || test.operator === '==')) {
|
|
668
|
+
const left = test.left;
|
|
669
|
+
const right = test.right;
|
|
670
|
+
if (t.isBinaryExpression(left) && left.operator === '*' &&
|
|
671
|
+
t.isNumericLiteral(right) && right.value === 0) {
|
|
672
|
+
if ((t.isNumericLiteral(left.left) && left.left.value === 0) ||
|
|
673
|
+
(t.isNumericLiteral(left.right) && left.right.value === 0)) {
|
|
674
|
+
path.replaceWith(path.node.consequent);
|
|
675
|
+
removed++;
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
});
|
|
682
|
+
if (removed > 0) {
|
|
683
|
+
logger.info(`Removed ${removed} opaque predicates`);
|
|
684
|
+
return generate(ast, { comments: true, compact: false }).code;
|
|
685
|
+
}
|
|
686
|
+
return code;
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
logger.error('Failed to remove opaque predicates:', error);
|
|
690
|
+
return code;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
async llmCleanup(code, techniques) {
|
|
694
|
+
if (!this.llm)
|
|
695
|
+
return null;
|
|
696
|
+
try {
|
|
697
|
+
const codeSnippet = code.length > 3000 ? code.substring(0, 3000) + '\n\n// ... (code truncated)' : code;
|
|
698
|
+
const prompt = `# Code Cleanup Task
|
|
699
|
+
|
|
700
|
+
## Detected Obfuscation Techniques
|
|
701
|
+
${techniques.map(t => `- ${t}`).join('\n')}
|
|
702
|
+
|
|
703
|
+
## Deobfuscated Code (needs cleanup)
|
|
704
|
+
\`\`\`javascript
|
|
705
|
+
${codeSnippet}
|
|
706
|
+
\`\`\`
|
|
707
|
+
|
|
708
|
+
## Your Task
|
|
709
|
+
Clean up and improve this deobfuscated JavaScript code:
|
|
710
|
+
|
|
711
|
+
1. **Variable Naming**: Rename variables to meaningful names based on their usage
|
|
712
|
+
- Avoid generic names like 'a', 'b', 'temp'
|
|
713
|
+
- Use descriptive names like 'userConfig', 'apiEndpoint', 'responseData'
|
|
714
|
+
|
|
715
|
+
2. **Code Structure**: Improve readability
|
|
716
|
+
- Remove unnecessary parentheses and brackets
|
|
717
|
+
- Simplify complex expressions
|
|
718
|
+
- Extract magic numbers to named constants
|
|
719
|
+
|
|
720
|
+
3. **Comments**: Add brief comments for:
|
|
721
|
+
- Complex logic or algorithms
|
|
722
|
+
- Non-obvious functionality
|
|
723
|
+
- Important data structures
|
|
724
|
+
|
|
725
|
+
4. **Consistency**: Ensure consistent code style
|
|
726
|
+
- Use consistent indentation
|
|
727
|
+
- Follow JavaScript best practices
|
|
728
|
+
|
|
729
|
+
## Important Rules
|
|
730
|
+
- Preserve ALL original functionality
|
|
731
|
+
- Do NOT remove any functional code
|
|
732
|
+
- Do NOT change the program logic
|
|
733
|
+
- Output ONLY valid JavaScript code
|
|
734
|
+
- Do NOT add explanations outside the code
|
|
735
|
+
|
|
736
|
+
## Output Format
|
|
737
|
+
Return only the cleaned JavaScript code without markdown formatting.`;
|
|
738
|
+
const response = await this.llm.chat([
|
|
739
|
+
{
|
|
740
|
+
role: 'system',
|
|
741
|
+
content: `# Role
|
|
742
|
+
You are an expert JavaScript code reviewer and refactoring specialist with expertise in:
|
|
743
|
+
- Code readability and maintainability improvement
|
|
744
|
+
- Semantic variable naming based on usage context
|
|
745
|
+
- Code smell detection and refactoring
|
|
746
|
+
- JavaScript best practices (ES6+, clean code principles)
|
|
747
|
+
- Preserving exact program functionality during refactoring
|
|
748
|
+
|
|
749
|
+
# Task
|
|
750
|
+
Clean up and improve deobfuscated JavaScript code while preserving 100% of its functionality.
|
|
751
|
+
|
|
752
|
+
# Refactoring Principles
|
|
753
|
+
1. **Semantic Naming**: Infer variable purpose from usage patterns
|
|
754
|
+
- API calls → apiClient, fetchData, apiResponse
|
|
755
|
+
- DOM elements → userInput, submitButton, errorMessage
|
|
756
|
+
- Crypto operations → encryptedData, decryptionKey, hashValue
|
|
757
|
+
- Loops/counters → index, itemCount, currentPage
|
|
758
|
+
|
|
759
|
+
2. **Code Simplification**: Remove obfuscation artifacts
|
|
760
|
+
- Unnecessary IIFEs and closures
|
|
761
|
+
- Redundant variable assignments
|
|
762
|
+
- Complex ternary chains → if-else
|
|
763
|
+
- Magic numbers → named constants
|
|
764
|
+
|
|
765
|
+
3. **Structure Improvement**: Enhance readability
|
|
766
|
+
- Extract repeated code to functions
|
|
767
|
+
- Group related operations
|
|
768
|
+
- Consistent indentation and spacing
|
|
769
|
+
- Logical code organization
|
|
770
|
+
|
|
771
|
+
# Critical Constraints
|
|
772
|
+
- **NEVER** change program logic or behavior
|
|
773
|
+
- **NEVER** remove functional code (even if it looks redundant)
|
|
774
|
+
- **NEVER** add new functionality
|
|
775
|
+
- **ONLY** improve naming, structure, and readability
|
|
776
|
+
- Output must be syntactically valid JavaScript
|
|
777
|
+
- Preserve all side effects and edge cases
|
|
778
|
+
|
|
779
|
+
# Output Format
|
|
780
|
+
Return ONLY the cleaned JavaScript code (no markdown, no explanations).`
|
|
781
|
+
},
|
|
782
|
+
{ role: 'user', content: prompt },
|
|
783
|
+
], {
|
|
784
|
+
temperature: 0.15,
|
|
785
|
+
maxTokens: 3000,
|
|
786
|
+
});
|
|
787
|
+
const cleanedCode = this.extractCodeFromLLMResponse(response.content);
|
|
788
|
+
if (this.isValidJavaScript(cleanedCode)) {
|
|
789
|
+
logger.success('LLM cleanup succeeded');
|
|
790
|
+
return cleanedCode;
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
logger.warn('LLM cleanup produced invalid JavaScript');
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
catch (error) {
|
|
798
|
+
logger.warn('LLM cleanup failed', error);
|
|
799
|
+
return null;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
normalizeCode(code) {
|
|
803
|
+
code = code.replace(/\s+/g, ' ');
|
|
804
|
+
code = code.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
805
|
+
code = code.replace(/\/\/.*/g, '');
|
|
806
|
+
return code.trim();
|
|
807
|
+
}
|
|
808
|
+
detectStringEncoding(code) {
|
|
809
|
+
const patterns = [
|
|
810
|
+
/\\x[0-9a-f]{2}/i,
|
|
811
|
+
/\\u[0-9a-f]{4}/i,
|
|
812
|
+
/String\.fromCharCode/i,
|
|
813
|
+
/atob\(/i,
|
|
814
|
+
];
|
|
815
|
+
return patterns.some(p => p.test(code));
|
|
816
|
+
}
|
|
817
|
+
decodeStrings(code) {
|
|
818
|
+
logger.info('Decoding strings...');
|
|
819
|
+
try {
|
|
820
|
+
const ast = parser.parse(code, {
|
|
821
|
+
sourceType: 'module',
|
|
822
|
+
plugins: ['jsx', 'typescript'],
|
|
823
|
+
});
|
|
824
|
+
let decoded = 0;
|
|
825
|
+
traverse(ast, {
|
|
826
|
+
CallExpression(path) {
|
|
827
|
+
if (t.isMemberExpression(path.node.callee) &&
|
|
828
|
+
t.isIdentifier(path.node.callee.object, { name: 'String' }) &&
|
|
829
|
+
t.isIdentifier(path.node.callee.property, { name: 'fromCharCode' })) {
|
|
830
|
+
const allNumbers = path.node.arguments.every((arg) => t.isNumericLiteral(arg));
|
|
831
|
+
if (allNumbers) {
|
|
832
|
+
const charCodes = path.node.arguments.map((arg) => arg.value);
|
|
833
|
+
const decodedString = String.fromCharCode(...charCodes);
|
|
834
|
+
path.replaceWith(t.stringLiteral(decodedString));
|
|
835
|
+
decoded++;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
},
|
|
839
|
+
});
|
|
840
|
+
if (decoded > 0) {
|
|
841
|
+
logger.info(`Decoded ${decoded} string expressions`);
|
|
842
|
+
return generate(ast, { comments: false, compact: false }).code;
|
|
843
|
+
}
|
|
844
|
+
return code;
|
|
845
|
+
}
|
|
846
|
+
catch (error) {
|
|
847
|
+
logger.error('Failed to decode strings:', error);
|
|
848
|
+
return code;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
applyASTOptimizations(code) {
|
|
852
|
+
logger.info('Applying AST optimizations...');
|
|
853
|
+
try {
|
|
854
|
+
const ast = parser.parse(code, {
|
|
855
|
+
sourceType: 'module',
|
|
856
|
+
plugins: ['jsx', 'typescript'],
|
|
857
|
+
});
|
|
858
|
+
let optimized = 0;
|
|
859
|
+
traverse(ast, {
|
|
860
|
+
BinaryExpression(path) {
|
|
861
|
+
const { left, right, operator } = path.node;
|
|
862
|
+
if (t.isNumericLiteral(left) && t.isNumericLiteral(right)) {
|
|
863
|
+
let result;
|
|
864
|
+
switch (operator) {
|
|
865
|
+
case '+':
|
|
866
|
+
result = left.value + right.value;
|
|
867
|
+
break;
|
|
868
|
+
case '-':
|
|
869
|
+
result = left.value - right.value;
|
|
870
|
+
break;
|
|
871
|
+
case '*':
|
|
872
|
+
result = left.value * right.value;
|
|
873
|
+
break;
|
|
874
|
+
case '/':
|
|
875
|
+
result = left.value / right.value;
|
|
876
|
+
break;
|
|
877
|
+
case '%':
|
|
878
|
+
result = left.value % right.value;
|
|
879
|
+
break;
|
|
880
|
+
case '**':
|
|
881
|
+
result = Math.pow(left.value, right.value);
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
if (result !== undefined) {
|
|
885
|
+
path.replaceWith(t.numericLiteral(result));
|
|
886
|
+
optimized++;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
},
|
|
890
|
+
LogicalExpression(path) {
|
|
891
|
+
const { left, right, operator } = path.node;
|
|
892
|
+
if (operator === '&&' && t.isBooleanLiteral(left) && left.value === true) {
|
|
893
|
+
path.replaceWith(right);
|
|
894
|
+
optimized++;
|
|
895
|
+
}
|
|
896
|
+
if (operator === '||' && t.isBooleanLiteral(left) && left.value === false) {
|
|
897
|
+
path.replaceWith(right);
|
|
898
|
+
optimized++;
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
EmptyStatement(path) {
|
|
902
|
+
path.remove();
|
|
903
|
+
optimized++;
|
|
904
|
+
},
|
|
905
|
+
ConditionalExpression(path) {
|
|
906
|
+
const { test, consequent, alternate } = path.node;
|
|
907
|
+
if (t.isBooleanLiteral(test) && test.value === true) {
|
|
908
|
+
path.replaceWith(consequent);
|
|
909
|
+
optimized++;
|
|
910
|
+
}
|
|
911
|
+
if (t.isBooleanLiteral(test) && test.value === false) {
|
|
912
|
+
path.replaceWith(alternate);
|
|
913
|
+
optimized++;
|
|
914
|
+
}
|
|
915
|
+
},
|
|
916
|
+
});
|
|
917
|
+
if (optimized > 0) {
|
|
918
|
+
logger.info(`Applied ${optimized} AST optimizations`);
|
|
919
|
+
return generate(ast, { comments: true, compact: false }).code;
|
|
920
|
+
}
|
|
921
|
+
return code;
|
|
922
|
+
}
|
|
923
|
+
catch (error) {
|
|
924
|
+
logger.error('Failed to apply AST optimizations:', error);
|
|
925
|
+
return code;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
calculateConfidence(techniques, warnings, code) {
|
|
929
|
+
let confidence = 0.3;
|
|
930
|
+
const techniqueBonus = Math.min(techniques.length * 0.12, 0.5);
|
|
931
|
+
confidence += techniqueBonus;
|
|
932
|
+
const warningPenalty = warnings.length * 0.08;
|
|
933
|
+
confidence -= warningPenalty;
|
|
934
|
+
const highConfidenceTechniques = [
|
|
935
|
+
'invisible-unicode',
|
|
936
|
+
'string-array-rotation',
|
|
937
|
+
'dead-code-injection',
|
|
938
|
+
'opaque-predicates',
|
|
939
|
+
'string-encoding',
|
|
940
|
+
'ast-optimized',
|
|
941
|
+
];
|
|
942
|
+
const highConfidenceCount = techniques.filter(t => highConfidenceTechniques.some(ht => t.includes(ht))).length;
|
|
943
|
+
confidence += highConfidenceCount * 0.05;
|
|
944
|
+
if (techniques.some(t => t.includes('vm-protection'))) {
|
|
945
|
+
confidence -= 0.15;
|
|
946
|
+
}
|
|
947
|
+
if (techniques.some(t => t.includes('control-flow-flattening'))) {
|
|
948
|
+
confidence -= 0.05;
|
|
949
|
+
}
|
|
950
|
+
const complexity = this.estimateCodeComplexity(code);
|
|
951
|
+
if (complexity < 10) {
|
|
952
|
+
confidence += 0.1;
|
|
953
|
+
}
|
|
954
|
+
else if (complexity > 100) {
|
|
955
|
+
confidence -= 0.1;
|
|
956
|
+
}
|
|
957
|
+
return Math.max(0.1, Math.min(0.95, confidence));
|
|
958
|
+
}
|
|
959
|
+
estimateCodeComplexity(code) {
|
|
960
|
+
try {
|
|
961
|
+
const ast = parser.parse(code, {
|
|
962
|
+
sourceType: 'module',
|
|
963
|
+
plugins: ['jsx', 'typescript'],
|
|
964
|
+
});
|
|
965
|
+
let complexity = 0;
|
|
966
|
+
traverse(ast, {
|
|
967
|
+
FunctionDeclaration() { complexity += 2; },
|
|
968
|
+
FunctionExpression() { complexity += 2; },
|
|
969
|
+
ArrowFunctionExpression() { complexity += 2; },
|
|
970
|
+
IfStatement() { complexity += 1; },
|
|
971
|
+
SwitchStatement() { complexity += 2; },
|
|
972
|
+
ConditionalExpression() { complexity += 1; },
|
|
973
|
+
WhileStatement() { complexity += 2; },
|
|
974
|
+
ForStatement() { complexity += 2; },
|
|
975
|
+
DoWhileStatement() { complexity += 2; },
|
|
976
|
+
TryStatement() { complexity += 3; },
|
|
977
|
+
});
|
|
978
|
+
return complexity;
|
|
979
|
+
}
|
|
980
|
+
catch {
|
|
981
|
+
return 100;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
//# sourceMappingURL=AdvancedDeobfuscator.js.map
|