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