Files
cursornew2026/run_codex_deobf.js

231 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 反混淆并美化原版 extension/out 下的核心文件,输出到 codexfanbianyi/extension/out。
* 策略:动态执行字符串数组 + 解码函数,解出所有形如 _0x****(0x123,'key') 的调用并替换为明文,再用 js-beautify 排版。
*/
const fs = require('fs');
const path = require('path');
const vm = require('vm');
const beautify = require('js-beautify').js;
const baseDir = __dirname;
const inputBase = path.join(baseDir, '原版本', 'extension', 'out');
const outputBase = path.join(baseDir, 'codexfanbianyi', 'extension', 'out');
const targets = [
'webview/provider.js',
'extension.js',
'utils/account.js',
'utils/sqlite.js',
'api/client.js'
];
const mapPaths = {
'webview/provider.js': path.join(baseDir, 'provider_decoded_map.json'),
'extension.js': path.join(baseDir, 'extension_decoded_map.json'),
'utils/account.js': path.join(baseDir, 'account_decoded_map.json'),
'utils/sqlite.js': path.join(baseDir, 'sqlite_decoded_map.json'),
'api/client.js': path.join(baseDir, 'client_decoded_map.json')
};
function ensureDir(dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
function extractFunctionWithBody(code, nameRegex) {
const match = nameRegex.exec(code);
if (!match) return null;
const start = match.index;
const braceStart = code.indexOf('{', start);
if (braceStart === -1) return null;
let depth = 0;
for (let i = braceStart; i < code.length; i++) {
const ch = code[i];
if (ch === '{') depth++;
else if (ch === '}') {
depth--;
if (depth === 0) {
return {
name: match[1],
code: code.slice(start, i + 1),
end: i + 1
};
}
}
}
return null;
}
function buildSandbox() {
const sandbox = {
console: { log: () => {}, warn: () => {}, error: () => {} },
vip: 'cursor',
Buffer,
atob: (str) => Buffer.from(str, 'base64').toString('binary'),
btoa: (str) => Buffer.from(str, 'binary').toString('base64'),
decodeURIComponent,
encodeURIComponent,
parseInt,
String,
setTimeout,
clearTimeout,
setInterval,
clearInterval,
exports: {},
module: { exports: {} },
require: () => ({})
};
vm.createContext(sandbox);
return sandbox;
}
function buildDecoder(code) {
const arrayFunc = extractFunctionWithBody(code, /function\s+(_0x[a-f0-9]+)\s*\(\s*\)/i);
const decoderFunc = extractFunctionWithBody(code, /function\s+(_0x[a-f0-9]+)\s*\(\s*_0x[a-f0-9]+\s*,\s*_0x[a-f0-9]+\s*\)/i);
if (!arrayFunc || !decoderFunc) {
return null;
}
const sandbox = buildSandbox();
try {
// 1) 先只注入字符串数组函数 + 解码函数(避免执行到 exports/require 等顶层逻辑)
vm.runInContext(`${arrayFunc.code}\n${decoderFunc.code}`, sandbox, { timeout: 4000 });
// 2) 执行“数组旋转/IIFE”等启动代码通常位于文件开头exports 之前)
const stopMarkers = [
'var __createBinding',
'Object.defineProperty(exports',
'exports.',
'module.exports'
];
let bootstrapEnd = code.length;
for (const marker of stopMarkers) {
const idx = code.indexOf(marker);
if (idx !== -1 && idx < bootstrapEnd) bootstrapEnd = idx;
}
const bootstrapCode = code.slice(0, bootstrapEnd);
vm.runInContext(bootstrapCode, sandbox, { timeout: 4000 });
const decoder = sandbox[decoderFunc.name];
if (typeof decoder !== 'function') {
console.warn(` 解码器 ${decoderFunc.name} 不是函数`);
return null;
}
return decoder.bind(sandbox);
} catch (e) {
console.warn(` 解码器初始化失败: ${e.message}`);
return null;
}
}
function literalFor(decoded) {
if (decoded == null) return 'undefined';
if (typeof decoded !== 'string') return JSON.stringify(decoded);
if (decoded.includes('\n')) {
return '`' + decoded.replace(/`/g, '\\`') + '`';
}
return JSON.stringify(decoded);
}
function replaceCalls(code, decoder) {
const callPattern = /_0x[a-f0-9]+\s*\(\s*(0x[a-f0-9]+|\d+)\s*,\s*['"]([^'"]+)['"]\s*\)/gi;
const replacements = new Map();
let match;
while ((match = callPattern.exec(code)) !== null) {
const full = match[0];
if (replacements.has(full)) continue;
const index = match[1].startsWith('0x') ? parseInt(match[1], 16) : parseInt(match[1], 10);
const key = match[2];
try {
const decoded = decoder(index, key);
if (typeof decoded === 'string') {
replacements.set(full, literalFor(decoded));
}
} catch {
/* ignore individual decode errors */
}
}
let replacedCount = 0;
for (const [call, literal] of replacements) {
const newCode = code.split(call).join(literal);
if (newCode !== code) {
replacedCount++;
code = newCode;
}
}
return { code, replacedCount, found: replacements.size };
}
function replaceWithMap(code, relPath) {
const mapPath = mapPaths[relPath];
if (!mapPath || !fs.existsSync(mapPath)) {
return { code, replacedCount: 0, entries: 0 };
}
const decodeMap = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
let replacedCount = 0;
for (const [pattern, decoded] of Object.entries(decodeMap)) {
const literal = literalFor(decoded);
const newCode = code.split(pattern).join(literal);
if (newCode !== code) {
replacedCount++;
code = newCode;
}
}
return { code, replacedCount, entries: Object.keys(decodeMap).length };
}
function cleanDecoderBlocks(code) {
// 保留原始函数,避免因匹配不准破坏结构
return code;
}
function processFile(relPath) {
const inputPath = path.join(inputBase, relPath);
const outputPath = path.join(outputBase, relPath);
if (!fs.existsSync(inputPath)) {
console.warn(`跳过缺失文件: ${relPath}`);
return;
}
let code = fs.readFileSync(inputPath, 'utf8');
console.log(`\n==== 处理 ${relPath} ====`);
const mapResult = replaceWithMap(code, relPath);
if (mapResult.entries > 0) {
console.log(` 预置映射 ${mapResult.entries} 条,替换 ${mapResult.replacedCount}`);
code = mapResult.code;
}
const decoder = buildDecoder(code);
if (!decoder) {
console.warn(' 未找到可用的解码器,直接美化原始代码');
} else {
const result = replaceCalls(code, decoder);
console.log(` 找到 ${result.found} 个唯一调用,成功替换 ${result.replacedCount}`);
code = result.code;
}
code = cleanDecoderBlocks(code);
code = beautify(code, { indent_size: 4, max_preserve_newlines: 2, end_with_newline: true });
ensureDir(path.dirname(outputPath));
fs.writeFileSync(outputPath, code, 'utf8');
console.log(` 输出: ${outputPath}`);
}
function main() {
targets.forEach(processFile);
console.log('\n全部处理完成。');
}
main();