350 lines
12 KiB
JavaScript
350 lines
12 KiB
JavaScript
/**
|
||
* 完整反混淆脚本 v3 - 更强大的字符串解码
|
||
* 策略:先执行完整的解码器设置,再遍历所有可能的调用参数
|
||
*/
|
||
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 setupFullDecoder(code) {
|
||
// 查找字符串数组函数名
|
||
const arrayFuncMatch = code.match(/function\s+(_0x[a-f0-9]+)\s*\(\)\s*\{/);
|
||
if (!arrayFuncMatch) {
|
||
console.log(' 未找到字符串数组函数');
|
||
return null;
|
||
}
|
||
const arrayFuncName = arrayFuncMatch[1];
|
||
|
||
// 查找解码函数 - 更宽松的匹配
|
||
const decoderPatterns = [
|
||
/function\s+(_0x[a-f0-9]+)\s*\(\s*_0x[a-f0-9]+\s*,\s*_0x[a-f0-9]+\s*\)\s*\{\s*const\s+_0x[a-f0-9]+\s*=\s*_0x[a-f0-9]+\s*\(\s*\)\s*;/,
|
||
/function\s+(_0x[a-f0-9]+)\s*\(\s*_0x[a-f0-9]+\s*,\s*_0x[a-f0-9]+\s*\)\s*\{/
|
||
];
|
||
|
||
let decoderFuncName = null;
|
||
for (const pattern of decoderPatterns) {
|
||
const match = code.match(pattern);
|
||
if (match) {
|
||
decoderFuncName = match[1];
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!decoderFuncName) {
|
||
console.log(' 未找到解码函数');
|
||
return null;
|
||
}
|
||
|
||
console.log(` 字符串数组函数: ${arrayFuncName}`);
|
||
console.log(` 解码函数: ${decoderFuncName}`);
|
||
|
||
// 提取从开头到解码函数结束的所有代码
|
||
// 找到解码函数的结束位置
|
||
const decoderStart = code.indexOf(`function ${decoderFuncName}(`);
|
||
if (decoderStart === -1) {
|
||
console.log(' 未找到解码函数定义');
|
||
return null;
|
||
}
|
||
|
||
// 找到解码函数的结束
|
||
let braceCount = 0;
|
||
let decoderEnd = decoderStart;
|
||
let 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;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 提取从文件开头到解码函数结束的所有代码
|
||
// 这包括 vip 变量、IIFE、数组函数、解码函数
|
||
const setupCode = code.substring(0, decoderEnd);
|
||
|
||
// 创建沙盒并执行
|
||
const sandbox = {
|
||
console: { log: () => {}, warn: () => {}, error: () => {} },
|
||
parseInt: parseInt,
|
||
String: String,
|
||
decodeURIComponent: decodeURIComponent
|
||
};
|
||
|
||
try {
|
||
vm.runInNewContext(setupCode, sandbox);
|
||
|
||
// 检查解码函数是否可用
|
||
const testResult = vm.runInNewContext(`typeof ${decoderFuncName}`, sandbox);
|
||
if (testResult !== 'function') {
|
||
console.log(` 解码函数不可用: ${testResult}`);
|
||
return null;
|
||
}
|
||
|
||
console.log(` 解码器设置成功`);
|
||
return { sandbox, decoderFuncName, arrayFuncName, setupCode };
|
||
} catch (e) {
|
||
console.log(` 沙盒执行失败: ${e.message}`);
|
||
// 尝试更激进的方法 - 执行整个文件的前半部分
|
||
return setupAggressiveDecoder(code, decoderFuncName, arrayFuncName);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更激进的解码器设置 - 执行更多代码
|
||
*/
|
||
function setupAggressiveDecoder(code, decoderFuncName, arrayFuncName) {
|
||
console.log(' 尝试激进模式...');
|
||
|
||
// 找到第一个 exports 或 Object.defineProperty 之前的所有代码
|
||
const exportIndex = Math.min(
|
||
code.indexOf('Object.defineProperty(exports') !== -1 ? code.indexOf('Object.defineProperty(exports') : Infinity,
|
||
code.indexOf('exports.') !== -1 ? code.indexOf('exports.') : Infinity
|
||
);
|
||
|
||
if (exportIndex === Infinity) {
|
||
console.log(' 未找到 exports 边界');
|
||
return null;
|
||
}
|
||
|
||
const setupCode = code.substring(0, exportIndex);
|
||
|
||
const sandbox = {
|
||
console: { log: () => {}, warn: () => {}, error: () => {} },
|
||
parseInt: parseInt,
|
||
String: String,
|
||
decodeURIComponent: decodeURIComponent,
|
||
this: {},
|
||
Object: Object
|
||
};
|
||
|
||
try {
|
||
vm.runInNewContext(setupCode, sandbox);
|
||
|
||
const testResult = vm.runInNewContext(`typeof ${decoderFuncName}`, sandbox);
|
||
if (testResult === 'function') {
|
||
console.log(` 激进模式成功`);
|
||
return { sandbox, decoderFuncName, arrayFuncName, setupCode };
|
||
}
|
||
} catch (e) {
|
||
console.log(` 激进模式失败: ${e.message}`);
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 收集并解码所有字符串调用
|
||
*/
|
||
function decodeAllStrings(code, decoder) {
|
||
if (!decoder) return { code, decodedCount: 0 };
|
||
|
||
const { sandbox, decoderFuncName } = decoder;
|
||
|
||
// 收集所有解码函数别名
|
||
const aliasPattern = new RegExp(`const\\s+(_0x[a-f0-9]+)\\s*=\\s*${decoderFuncName}\\s*;`, 'g');
|
||
const aliases = [decoderFuncName];
|
||
let match;
|
||
while ((match = aliasPattern.exec(code)) !== null) {
|
||
aliases.push(match[1]);
|
||
}
|
||
console.log(` 解码函数别名: ${aliases.join(', ')}`);
|
||
|
||
// 构建所有可能的调用模式
|
||
const decodedMap = new Map();
|
||
let totalFound = 0;
|
||
|
||
for (const alias of aliases) {
|
||
// 匹配多种调用格式
|
||
const patterns = [
|
||
new RegExp(`${alias}\\s*\\(\\s*(0x[a-f0-9]+)\\s*,\\s*'([^']*)'\\s*\\)`, 'g'),
|
||
new RegExp(`${alias}\\s*\\(\\s*(0x[a-f0-9]+)\\s*,\\s*"([^"]*)"\\s*\\)`, 'g'),
|
||
];
|
||
|
||
for (const pattern of patterns) {
|
||
let callMatch;
|
||
while ((callMatch = pattern.exec(code)) !== null) {
|
||
const fullMatch = callMatch[0];
|
||
const num = callMatch[1];
|
||
const key = callMatch[2];
|
||
|
||
if (!decodedMap.has(fullMatch)) {
|
||
try {
|
||
const decoded = vm.runInNewContext(
|
||
`${decoderFuncName}(${num},'${key.replace(/'/g, "\\'")}')`,
|
||
sandbox
|
||
);
|
||
if (typeof decoded === 'string') {
|
||
decodedMap.set(fullMatch, decoded);
|
||
totalFound++;
|
||
}
|
||
} catch (e) {
|
||
// 解码失败,跳过
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log(` 找到 ${totalFound} 个字符串调用`);
|
||
|
||
// 替换所有解码后的字符串
|
||
let decodedCount = 0;
|
||
for (const [original, decoded] of decodedMap) {
|
||
// 选择合适的引号
|
||
let replacement;
|
||
if (decoded.includes('\n') || decoded.includes('\r')) {
|
||
replacement = '`' + decoded.replace(/`/g, '\\`').replace(/\$/g, '\\$') + '`';
|
||
} else if (decoded.includes("'") && !decoded.includes('"')) {
|
||
replacement = JSON.stringify(decoded);
|
||
} else {
|
||
replacement = `'${decoded.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
||
}
|
||
|
||
// 全局替换
|
||
const escapedOriginal = original.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||
const regex = new RegExp(escapedOriginal, 'g');
|
||
const before = code.length;
|
||
code = code.replace(regex, replacement);
|
||
if (code.length !== before) {
|
||
decodedCount++;
|
||
}
|
||
}
|
||
|
||
console.log(` 替换了 ${decodedCount} 个不同的字符串`);
|
||
|
||
// 移除解码器代码(可选,让代码更干净)
|
||
// 注释掉解码器相关代码而不是删除
|
||
|
||
return { code, decodedCount };
|
||
}
|
||
|
||
/**
|
||
* 清理混淆的变量引用
|
||
*/
|
||
function cleanObfuscatedReferences(code) {
|
||
// 将 obj['property'] 转换为 obj.property
|
||
code = code.replace(/\['([a-zA-Z_$][a-zA-Z0-9_$]*)'\]/g, '.$1');
|
||
|
||
// 简化 !![] 为 true
|
||
code = code.replace(/!!\[\]/g, 'true');
|
||
|
||
// 简化 ![] 为 false
|
||
code = code.replace(/!\[\]/g, 'false');
|
||
|
||
return code;
|
||
}
|
||
|
||
/**
|
||
* AST 清洗
|
||
*/
|
||
function astClean(code, filename) {
|
||
try {
|
||
console.log(` [AST] 处理...`);
|
||
return deobfuscateIO(code);
|
||
} catch (e) {
|
||
console.log(` [AST] 失败: ${e.message}`);
|
||
return code;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理单个文件
|
||
*/
|
||
async function processFile(inputPath, outputPath, filename) {
|
||
console.log(`\n${'='.repeat(50)}`);
|
||
console.log(`处理: ${filename}`);
|
||
console.log('='.repeat(50));
|
||
|
||
let code = fs.readFileSync(inputPath, 'utf8');
|
||
console.log(`原始大小: ${(code.length / 1024).toFixed(2)} KB`);
|
||
|
||
// Step 1: 设置解码器
|
||
console.log('\nStep 1: 设置解码器...');
|
||
const decoder = setupFullDecoder(code);
|
||
|
||
// Step 2: 解码所有字符串
|
||
console.log('\nStep 2: 解码字符串...');
|
||
const decodeResult = decodeAllStrings(code, decoder);
|
||
code = decodeResult.code;
|
||
|
||
// Step 3: 清理混淆引用
|
||
console.log('\nStep 3: 清理混淆引用...');
|
||
code = cleanObfuscatedReferences(code);
|
||
|
||
// Step 4: AST 清洗
|
||
console.log('\nStep 4: AST 清洗...');
|
||
code = astClean(code, filename);
|
||
|
||
// Step 5: 再次清理(AST 可能引入新的混淆)
|
||
console.log('\nStep 5: 二次清理...');
|
||
code = cleanObfuscatedReferences(code);
|
||
|
||
// Step 6: 美化
|
||
console.log('\nStep 6: 美化代码...');
|
||
code = beautify(code, {
|
||
indent_size: 4,
|
||
preserve_newlines: true,
|
||
max_preserve_newlines: 2,
|
||
space_in_empty_paren: true,
|
||
jslint_happy: false,
|
||
brace_style: 'collapse'
|
||
});
|
||
|
||
console.log(`\n处理后大小: ${(code.length / 1024).toFixed(2)} KB`);
|
||
|
||
// 保存
|
||
ensureDir(path.dirname(outputPath));
|
||
fs.writeFileSync(outputPath, code);
|
||
console.log(`已保存: ${outputPath}`);
|
||
}
|
||
|
||
// 主函数
|
||
async function main() {
|
||
console.log('╔════════════════════════════════════════════════════╗');
|
||
console.log('║ 完整反混淆脚本 v3 ║');
|
||
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' },
|
||
{ input: 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/api/client.js', output: 'api/client.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('╚════════════════════════════════════════════════════╝');
|
||
console.log(`\n输出目录: ${outputDir}`);
|
||
}
|
||
|
||
main().catch(console.error);
|