备份: 完整开发状态(含反混淆脚本和临时文件)
This commit is contained in:
290
direct_decode.js
Normal file
290
direct_decode.js
Normal file
@@ -0,0 +1,290 @@
|
||||
/**
|
||||
* 直接解码脚本 - 使用 RC4 + 非标准 Base64 算法
|
||||
* 不依赖沙盒执行,直接实现解码逻辑
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const baseDir = 'D:/temp/破解/cursorpro-0.4.5';
|
||||
|
||||
// 非标准 Base64 字母表 (小写在前)
|
||||
const BASE64_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';
|
||||
|
||||
/**
|
||||
* 非标准 Base64 解码
|
||||
*/
|
||||
function base64Decode(str) {
|
||||
let result = '';
|
||||
let buffer = 0;
|
||||
let bufferLen = 0;
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str[i];
|
||||
const charIndex = BASE64_CHARS.indexOf(char);
|
||||
if (charIndex === -1) continue;
|
||||
if (charIndex === 64) continue; // '=' padding
|
||||
|
||||
buffer = (buffer << 6) | charIndex;
|
||||
bufferLen += 6;
|
||||
|
||||
if (bufferLen >= 8) {
|
||||
bufferLen -= 8;
|
||||
result += String.fromCharCode((buffer >> bufferLen) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* RC4 解密
|
||||
*/
|
||||
function rc4Decrypt(data, key) {
|
||||
// 初始化 S-box
|
||||
const s = [];
|
||||
for (let i = 0; i < 256; i++) {
|
||||
s[i] = i;
|
||||
}
|
||||
|
||||
let j = 0;
|
||||
for (let i = 0; i < 256; i++) {
|
||||
j = (j + s[i] + key.charCodeAt(i % key.length)) % 256;
|
||||
[s[i], s[j]] = [s[j], s[i]];
|
||||
}
|
||||
|
||||
// 加密/解密
|
||||
let result = '';
|
||||
let i = 0;
|
||||
j = 0;
|
||||
|
||||
for (let k = 0; k < data.length; k++) {
|
||||
i = (i + 1) % 256;
|
||||
j = (j + s[i]) % 256;
|
||||
[s[i], s[j]] = [s[j], s[i]];
|
||||
result += String.fromCharCode(data.charCodeAt(k) ^ s[(s[i] + s[j]) % 256]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码单个字符串
|
||||
*/
|
||||
function decodeString(encodedStr, key) {
|
||||
try {
|
||||
const base64Decoded = base64Decode(encodedStr);
|
||||
const decrypted = rc4Decrypt(base64Decoded, key);
|
||||
return decrypted;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取字符串数组
|
||||
*/
|
||||
function extractStringArray(code) {
|
||||
// 查找字符串数组 - 这是一个大的嵌套数组
|
||||
const arrayMatch = code.match(/return\s*\[([^\]]*(?:\]\.concat\(function\s*\(\)\s*\{\s*return\s*\[[^\]]*\][^\}]*\}\(\)\))*[^\]]*)\]/s);
|
||||
if (!arrayMatch) return null;
|
||||
|
||||
// 提取所有字符串
|
||||
const strMatch = code.match(/return\s*\['([^']*)'(?:,'([^']*)')*\]/g);
|
||||
if (!strMatch) return null;
|
||||
|
||||
const strings = [];
|
||||
const allStrings = code.match(/'[^']*'/g) || [];
|
||||
|
||||
// 找到字符串数组的开始位置
|
||||
const arrayStart = code.indexOf("const _0x") || code.indexOf("function _0x");
|
||||
const arrayEnd = code.indexOf("Object.defineProperty(exports");
|
||||
|
||||
if (arrayStart !== -1 && arrayEnd !== -1) {
|
||||
const arraySection = code.substring(arrayStart, arrayEnd);
|
||||
// 提取这个区域内所有带引号的字符串
|
||||
const stringMatches = arraySection.match(/'([^'\\]*(\\.[^'\\]*)*)'/g) || [];
|
||||
|
||||
for (const match of stringMatches) {
|
||||
const str = match.slice(1, -1); // 移除引号
|
||||
if (str.length > 3 && /^[a-zA-Z0-9+/=]+$/.test(str)) {
|
||||
strings.push(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` 提取了 ${strings.length} 个候选编码字符串`);
|
||||
return strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为文件生成解码映射
|
||||
*/
|
||||
function generateDecodeMapForFile(code, filename) {
|
||||
console.log(`\n处理: ${filename}`);
|
||||
console.log('='.repeat(50));
|
||||
|
||||
// 找到所有解码函数调用
|
||||
const callPattern = /(_0x[a-f0-9]+)\s*\(\s*(0x[a-f0-9]+)\s*,\s*'([^']*)'\s*\)/g;
|
||||
const calls = new Map();
|
||||
let match;
|
||||
|
||||
while ((match = callPattern.exec(code)) !== null) {
|
||||
const fullMatch = match[0];
|
||||
const funcName = match[1];
|
||||
const numHex = match[2];
|
||||
const key = match[3];
|
||||
|
||||
calls.set(fullMatch, { funcName, numHex, key, num: parseInt(numHex, 16) });
|
||||
}
|
||||
|
||||
console.log(` 找到 ${calls.size} 个唯一调用`);
|
||||
|
||||
// 提取字符串数组
|
||||
// 找到类似这样的模式:
|
||||
// return [vip, 'encodedStr1', 'encodedStr2', ...]
|
||||
const stringArrayMatch = code.match(/return\s*\[\s*vip\s*,\s*('[^']*'(?:\s*,\s*'[^']*')*)\s*\]/);
|
||||
let stringArray = ['cursor']; // vip = 'cursor'
|
||||
|
||||
if (stringArrayMatch) {
|
||||
const stringsSection = stringArrayMatch[1];
|
||||
const strings = stringsSection.match(/'([^']*)'/g);
|
||||
if (strings) {
|
||||
for (const s of strings) {
|
||||
stringArray.push(s.slice(1, -1));
|
||||
}
|
||||
}
|
||||
console.log(` 提取了 ${stringArray.length} 个数组元素`);
|
||||
} else {
|
||||
// 尝试另一种模式 - concat 嵌套数组
|
||||
const concatPattern = /return\s*\[([^\]]*)\]\.concat/g;
|
||||
const allArrays = [];
|
||||
let cm;
|
||||
while ((cm = concatPattern.exec(code)) !== null) {
|
||||
const content = cm[1];
|
||||
const strings = content.match(/'([^']*)'/g);
|
||||
if (strings) {
|
||||
for (const s of strings) {
|
||||
allArrays.push(s.slice(1, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allArrays.length > 0) {
|
||||
stringArray = ['cursor', ...allArrays];
|
||||
console.log(` 通过 concat 提取了 ${stringArray.length} 个数组元素`);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果无法提取数组,返回空
|
||||
if (stringArray.length < 10) {
|
||||
console.log(' 无法提取足够的字符串数组');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 尝试解码
|
||||
const decodeMap = {};
|
||||
let successCount = 0;
|
||||
|
||||
for (const [fullMatch, info] of calls) {
|
||||
const { num, key } = info;
|
||||
|
||||
// 计算实际索引(减去偏移量)
|
||||
// 不同文件的偏移量不同,需要尝试
|
||||
for (let offset = 0; offset <= 0x200; offset += 0x10) {
|
||||
const adjustedIndex = num - offset;
|
||||
if (adjustedIndex >= 0 && adjustedIndex < stringArray.length) {
|
||||
const encodedStr = stringArray[adjustedIndex];
|
||||
const decoded = decodeString(encodedStr, key);
|
||||
if (decoded && decoded.length > 0 && /^[\x20-\x7E\u4e00-\u9fff]+$/.test(decoded)) {
|
||||
decodeMap[fullMatch] = decoded;
|
||||
successCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` 成功解码: ${successCount}/${calls.size}`);
|
||||
|
||||
return decodeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用已有的解码器脚本为每个文件生成映射
|
||||
*/
|
||||
async function generateWithExistingDecoder(inputPath, outputPath, filename) {
|
||||
console.log(`\n处理: ${filename}`);
|
||||
console.log('='.repeat(50));
|
||||
|
||||
if (fs.existsSync(outputPath)) {
|
||||
const existing = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
|
||||
console.log(` 已存在: ${Object.keys(existing).length} 条映射`);
|
||||
return existing;
|
||||
}
|
||||
|
||||
// 读取代码
|
||||
const code = fs.readFileSync(inputPath, 'utf8');
|
||||
|
||||
// 查找解码器函数名
|
||||
const decoderMatch = code.match(/function\s+(_0x[a-f0-9]+)\s*\(\s*_0x[a-f0-9]+\s*,\s*_0x[a-f0-9]+\s*\)\s*\{/);
|
||||
if (!decoderMatch) {
|
||||
console.log(' 未找到解码函数');
|
||||
return null;
|
||||
}
|
||||
const decoderFuncName = decoderMatch[1];
|
||||
console.log(` 解码函数: ${decoderFuncName}`);
|
||||
|
||||
// 收集所有调用
|
||||
const callPattern = new RegExp(
|
||||
`(_0x[a-f0-9]+)\\s*\\(\\s*(0x[a-f0-9]+)\\s*,\\s*'([^']*)'\\s*\\)`,
|
||||
'g'
|
||||
);
|
||||
|
||||
const calls = new Set();
|
||||
let match;
|
||||
while ((match = callPattern.exec(code)) !== null) {
|
||||
calls.add(match[0]);
|
||||
}
|
||||
|
||||
console.log(` 找到 ${calls.size} 个调用`);
|
||||
|
||||
// 对于没有映射的文件,我们需要用不同的方法
|
||||
// 使用之前成功的解码器代码片段
|
||||
|
||||
return { size: calls.size, decoder: decoderFuncName };
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main() {
|
||||
console.log('╔════════════════════════════════════════════════════╗');
|
||||
console.log('║ 直接解码映射生成器 ║');
|
||||
console.log('║ 使用 RC4 + 非标准 Base64 ║');
|
||||
console.log('╚════════════════════════════════════════════════════╝');
|
||||
|
||||
const files = [
|
||||
{ input: '原版本/extension/out/utils/account.js', output: 'account_decoded_map.json' },
|
||||
{ input: '原版本/extension/out/utils/sqlite.js', output: 'sqlite_decoded_map.json' },
|
||||
{ input: '原版本/extension/out/api/client.js', output: 'client_decoded_map.json' }
|
||||
];
|
||||
|
||||
for (const file of files) {
|
||||
const inputPath = path.join(baseDir, file.input);
|
||||
const outputPath = path.join(baseDir, file.output);
|
||||
|
||||
if (!fs.existsSync(inputPath)) {
|
||||
console.log(`\n跳过: ${file.input} (不存在)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const code = fs.readFileSync(inputPath, 'utf8');
|
||||
const decodeMap = generateDecodeMapForFile(code, file.input);
|
||||
|
||||
if (decodeMap && Object.keys(decodeMap).length > 0) {
|
||||
fs.writeFileSync(outputPath, JSON.stringify(decodeMap, null, 2));
|
||||
console.log(` 已保存: ${outputPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n完成!');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in New Issue
Block a user