209 lines
7.8 KiB
JavaScript
209 lines
7.8 KiB
JavaScript
/**
|
|
* 完整反混淆脚本 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);
|