备份: 完整开发状态(含反混淆脚本和临时文件)
This commit is contained in:
208
full_deobfuscate_v2.js
Normal file
208
full_deobfuscate_v2.js
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* 完整反混淆脚本 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);
|
||||
Reference in New Issue
Block a user