备份: 完整开发状态(含反混淆脚本和临时文件)
This commit is contained in:
219
full_deobfuscate_v4.js
Normal file
219
full_deobfuscate_v4.js
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* 完整反混淆脚本 v4 - 使用预计算的解码映射
|
||||
* 策略:读取已有的 decoded_map.json 文件,直接替换字符串
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const deobfuscateIO = require('obfuscator-io-deobfuscator').deobfuscate;
|
||||
const beautify = require('js-beautify').js;
|
||||
|
||||
const baseDir = 'D:/temp/破解/cursorpro-0.4.5';
|
||||
const outputDir = path.join(baseDir, 'deobfuscated_full/extension/out');
|
||||
|
||||
function ensureDir(dir) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载解码映射
|
||||
*/
|
||||
function loadDecodeMap(mapPath) {
|
||||
if (!fs.existsSync(mapPath)) {
|
||||
console.log(` 映射文件不存在: ${mapPath}`);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const map = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
|
||||
console.log(` 加载了 ${Object.keys(map).length} 个解码映射`);
|
||||
return map;
|
||||
} catch (e) {
|
||||
console.log(` 加载映射失败: ${e.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用映射替换字符串
|
||||
*/
|
||||
function replaceWithMap(code, decodeMap) {
|
||||
if (!decodeMap) return { code, count: 0 };
|
||||
|
||||
let count = 0;
|
||||
for (const [pattern, decoded] of Object.entries(decodeMap)) {
|
||||
// 转义正则特殊字符
|
||||
const escapedPattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const regex = new RegExp(escapedPattern, 'g');
|
||||
|
||||
// 选择合适的引号
|
||||
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 before = code;
|
||||
code = code.replace(regex, replacement);
|
||||
if (code !== before) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` 替换了 ${count} 个字符串模式`);
|
||||
return { code, count };
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理混淆的引用
|
||||
*/
|
||||
function cleanObfuscatedReferences(code) {
|
||||
// 将 obj['property'] 转换为 obj.property
|
||||
code = code.replace(/\['([a-zA-Z_$][a-zA-Z0-9_$]*)'\]/g, '.$1');
|
||||
|
||||
// 简化布尔值
|
||||
code = code.replace(/!!\[\]/g, 'true');
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除解码器相关代码
|
||||
*/
|
||||
function removeDecoderCode(code) {
|
||||
// 移除字符串数组函数(大量字符串数组)
|
||||
code = code.replace(/function\s+_0x[a-f0-9]+\s*\(\)\s*\{\s*const\s+_0x[a-f0-9]+\s*=\s*function\s*\(\)\s*\{\s*return\s*\[[^\]]*\]\.concat\(function\s*\(\)\s*\{[\s\S]*?\}\(\)\)[\s\S]*?\};[\s\S]*?return\s+_0x[a-f0-9]+;\s*\}/g, '/* [STRING_ARRAY_REMOVED] */');
|
||||
|
||||
// 移除解码函数
|
||||
code = code.replace(/function\s+_0x[a-f0-9]+\s*\(\s*_0x[a-f0-9]+\s*,\s*_0x[a-f0-9]+\s*\)\s*\{[\s\S]*?return\s+_0x[a-f0-9]+\s*\(\s*_0x[a-f0-9]+\s*,\s*_0x[a-f0-9]+\s*\)\s*;\s*\}/g, '/* [DECODER_REMOVED] */');
|
||||
|
||||
// 移除 IIFE 初始化
|
||||
code = code.replace(/;\s*\(function\s*\(\s*_0x[a-f0-9]+[\s\S]*?\)\s*\)\s*\(\s*0x[a-f0-9]+\s*,\s*0x[a-f0-9]+\s*,\s*_0x[a-f0-9]+\s*,\s*0x[a-f0-9]+\s*\)\s*;/g, '/* [IIFE_REMOVED] */');
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个文件
|
||||
*/
|
||||
async function processFile(inputPath, outputPath, filename, mapPath) {
|
||||
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 decodeMap = loadDecodeMap(mapPath);
|
||||
|
||||
// Step 2: 替换字符串
|
||||
console.log('\nStep 2: 替换字符串...');
|
||||
const replaceResult = replaceWithMap(code, decodeMap);
|
||||
code = replaceResult.code;
|
||||
|
||||
// Step 3: 移除解码器代码
|
||||
console.log('\nStep 3: 移除解码器代码...');
|
||||
code = removeDecoderCode(code);
|
||||
|
||||
// Step 4: 清理混淆引用
|
||||
console.log('\nStep 4: 清理混淆引用...');
|
||||
code = cleanObfuscatedReferences(code);
|
||||
|
||||
// Step 5: AST 清洗
|
||||
console.log('\nStep 5: AST 清洗...');
|
||||
code = astClean(code, filename);
|
||||
|
||||
// Step 6: 再次清理
|
||||
console.log('\nStep 6: 二次清理...');
|
||||
code = cleanObfuscatedReferences(code);
|
||||
|
||||
// Step 7: 美化
|
||||
console.log('\nStep 7: 美化代码...');
|
||||
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('║ 完整反混淆脚本 v4 ║');
|
||||
console.log('║ 使用预计算解码映射 + AST清洗 + 美化 ║');
|
||||
console.log('╚════════════════════════════════════════════════════════╝\n');
|
||||
|
||||
// 首先为每个文件生成解码映射(如果不存在)
|
||||
const files = [
|
||||
{
|
||||
input: path.join(baseDir, '原版本/extension/out/webview/provider.js'),
|
||||
output: 'webview/provider.js',
|
||||
map: path.join(baseDir, 'provider_decoded_map.json')
|
||||
},
|
||||
{
|
||||
input: path.join(baseDir, '原版本/extension/out/extension.js'),
|
||||
output: 'extension.js',
|
||||
map: path.join(baseDir, 'extension_decoded_map.json')
|
||||
},
|
||||
{
|
||||
input: path.join(baseDir, '原版本/extension/out/utils/account.js'),
|
||||
output: 'utils/account.js',
|
||||
map: path.join(baseDir, 'account_decoded_map.json')
|
||||
},
|
||||
{
|
||||
input: path.join(baseDir, '原版本/extension/out/utils/sqlite.js'),
|
||||
output: 'utils/sqlite.js',
|
||||
map: path.join(baseDir, 'sqlite_decoded_map.json')
|
||||
},
|
||||
{
|
||||
input: path.join(baseDir, '原版本/extension/out/api/client.js'),
|
||||
output: 'api/client.js',
|
||||
map: path.join(baseDir, 'client_decoded_map.json')
|
||||
}
|
||||
];
|
||||
|
||||
for (const file of files) {
|
||||
if (fs.existsSync(file.input)) {
|
||||
await processFile(file.input, path.join(outputDir, file.output), file.output, file.map);
|
||||
} 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