/** * 完整反混淆脚本 v2 - 先解码字符串,再 AST 清洗 */ const fs = require('fs'); const path = require('path'); const vm = require('vm'); const deobfuscateIO = require('obfuscator-io-deobfuscator').deobfuscate; const beautify = require('js-beautify').js; const outputDir = 'D:/temp/破解/cursorpro-0.4.5/deobfuscated_full/extension/out'; function ensureDir(dir) { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } } /** * 提取并执行字符串解码器 */ function setupDecoder(code) { // 查找字符串数组函数名 (如 _0x4ff4, _0x2b0b 等) const arrayFuncMatch = code.match(/function (_0x[a-f0-9]+)\(\)\{/); if (!arrayFuncMatch) return null; const arrayFuncName = arrayFuncMatch[1]; // 查找解码函数名 (如 _0x56bd, _0xc90d 等) const decoderMatch = code.match(/function (_0x[a-f0-9]+)\(_0x[a-f0-9]+,_0x[a-f0-9]+\)\{const _0x[a-f0-9]+=_0x[a-f0-9]+\(\);/); if (!decoderMatch) return null; const decoderFuncName = decoderMatch[1]; console.log(` 字符串数组函数: ${arrayFuncName}`); console.log(` 解码函数: ${decoderFuncName}`); // 提取字符串数组函数 const arrayFuncStart = code.indexOf(`function ${arrayFuncName}(){`); let braceCount = 0, arrayFuncEnd = arrayFuncStart, foundStart = false; for (let i = arrayFuncStart; i < code.length; i++) { if (code[i] === '{') { braceCount++; foundStart = true; } else if (code[i] === '}') { braceCount--; if (foundStart && braceCount === 0) { arrayFuncEnd = i + 1; break; } } } const arrayFunc = code.substring(arrayFuncStart, arrayFuncEnd); // 提取解码函数 const decoderStart = code.indexOf(`function ${decoderFuncName}(`); braceCount = 0; let decoderEnd = decoderStart; foundStart = false; for (let i = decoderStart; i < code.length; i++) { if (code[i] === '{') { braceCount++; foundStart = true; } else if (code[i] === '}') { braceCount--; if (foundStart && braceCount === 0) { decoderEnd = i + 1; break; } } } const decoderFunc = code.substring(decoderStart, decoderEnd); // 查找 IIFE const iifeStart = code.indexOf(';(function(_0x', arrayFuncEnd); if (iifeStart === -1) return null; // 查找 IIFE 结束 const iifeEndPattern = `,${arrayFuncName})`; let iifeEnd = code.indexOf(iifeEndPattern, iifeStart); if (iifeEnd === -1) return null; iifeEnd = code.indexOf(';', iifeEnd) + 1; const iifeCode = code.substring(iifeStart, iifeEnd); // 在沙盒中执行 const sandbox = { vip: 'cursor' }; try { const setupCode = `${arrayFunc}\n${decoderFunc}\n${iifeCode}`; vm.runInNewContext(setupCode, sandbox); vm.runInNewContext(`globalThis.decoder = ${decoderFuncName};`, sandbox); return { sandbox, decoderFuncName, arrayFuncName, arrayFunc, decoderFunc, iifeCode }; } catch (e) { console.log(` 沙盒执行失败: ${e.message}`); return null; } } /** * 解码所有字符串调用 */ function decodeStrings(code, decoder) { if (!decoder) return code; const { sandbox, decoderFuncName } = decoder; // 查找所有别名 const aliasPattern = new RegExp(`const (_0x[a-f0-9]+)=${decoderFuncName};`, 'g'); const aliases = [decoderFuncName]; let match; while ((match = aliasPattern.exec(code)) !== null) { aliases.push(match[1]); } console.log(` 解码函数别名: ${aliases.join(', ')}`); // 解码所有调用 let decodedCount = 0; for (const alias of aliases) { const callPattern = new RegExp(`${alias}\\((0x[a-f0-9]+),'([^']+)'\\)`, 'g'); code = code.replace(callPattern, (fullMatch, num, key) => { try { const decoded = vm.runInNewContext(`decoder(${num},'${key}')`, sandbox); if (typeof decoded === 'string') { decodedCount++; // 根据内容选择引号 if (decoded.includes('\n') || decoded.length > 200) { return '`' + decoded.replace(/`/g, '\\`').replace(/\$/g, '\\$') + '`'; } else if (decoded.includes("'") && !decoded.includes('"')) { return JSON.stringify(decoded); } else { return `'${decoded.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`; } } } catch (e) {} return fullMatch; }); } console.log(` 解码了 ${decodedCount} 个字符串`); // 移除解码器代码 code = code.replace(decoder.arrayFunc, '/* [STRING ARRAY REMOVED] */'); code = code.replace(decoder.decoderFunc, '/* [DECODER REMOVED] */'); code = code.replace(decoder.iifeCode, '/* [IIFE REMOVED] */'); // 移除别名 for (const alias of aliases) { if (alias !== decoder.decoderFuncName) { code = code.replace(new RegExp(`const ${alias}=${decoder.decoderFuncName};`, 'g'), ''); } } return code; } /** * AST 清洗 */ function astClean(code, filename) { try { console.log(` [AST] 处理 ${filename}...`); return deobfuscateIO(code); } catch (e) { console.log(` [AST] 失败: ${e.message}`); return code; } } /** * 处理单个文件 */ async function processFile(inputPath, outputPath, filename) { console.log(`\n========== ${filename} ==========`); let code = fs.readFileSync(inputPath, 'utf8'); console.log(`原始大小: ${(code.length / 1024).toFixed(2)} KB`); // Step 1: 解码字符串 console.log('\nStep 1: 解码字符串...'); const decoder = setupDecoder(code); code = decodeStrings(code, decoder); // Step 2: AST 清洗 console.log('\nStep 2: AST 清洗...'); code = astClean(code, filename); // Step 3: 美化 console.log('\nStep 3: 美化...'); code = beautify(code, { indent_size: 4, preserve_newlines: true, max_preserve_newlines: 2 }); console.log(`处理后大小: ${(code.length / 1024).toFixed(2)} KB`); // 保存 ensureDir(path.dirname(outputPath)); fs.writeFileSync(outputPath, code); console.log(`✓ 已保存: ${outputPath}`); } // 主函数 async function main() { console.log('╔════════════════════════════════════════╗'); console.log('║ 完整反混淆脚本 v2 ║'); console.log('║ 字符串解码 + AST清洗 + 美化 ║'); console.log('╚════════════════════════════════════════╝\n'); const files = [ { input: 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/webview/provider.js', output: 'webview/provider.js' }, { input: 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/extension.js', output: 'extension.js' }, { input: 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/utils/account.js', output: 'utils/account.js' }, { input: 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/utils/sqlite.js', output: 'utils/sqlite.js' }, ]; for (const file of files) { if (fs.existsSync(file.input)) { await processFile(file.input, path.join(outputDir, file.output), file.output); } else { console.log(`\n跳过: ${file.input} (不存在)`); } } console.log('\n╔════════════════════════════════════════╗'); console.log('║ 完成! ║'); console.log('╚════════════════════════════════════════╝'); } main().catch(console.error);