/** * 反混淆并美化原版 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();