433 lines
13 KiB
JavaScript
433 lines
13 KiB
JavaScript
/**
|
|
* 完整反混淆 provider.js
|
|
* 使用 RC4 + 非标准 Base64 解码
|
|
*/
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// 非标准 Base64 字母表(小写在前)
|
|
const BASE64_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';
|
|
|
|
/**
|
|
* 非标准 Base64 解码
|
|
*/
|
|
function base64Decode(input) {
|
|
let output = '';
|
|
let buffer = 0;
|
|
let bitsCollected = 0;
|
|
|
|
for (let i = 0; i < input.length; i++) {
|
|
const char = input[i];
|
|
if (char === '=') break;
|
|
|
|
const value = BASE64_CHARS.indexOf(char);
|
|
if (value === -1) continue;
|
|
|
|
buffer = (buffer << 6) | value;
|
|
bitsCollected += 6;
|
|
|
|
if (bitsCollected >= 8) {
|
|
bitsCollected -= 8;
|
|
output += String.fromCharCode((buffer >> bitsCollected) & 0xFF);
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* RC4 解密
|
|
*/
|
|
function rc4Decrypt(data, key) {
|
|
const s = [];
|
|
let j = 0;
|
|
let result = '';
|
|
|
|
// 初始化 S-box
|
|
for (let i = 0; i < 256; i++) {
|
|
s[i] = i;
|
|
}
|
|
|
|
// 密钥调度
|
|
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 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]];
|
|
const keyByte = s[(s[i] + s[j]) % 256];
|
|
result += String.fromCharCode(data.charCodeAt(k) ^ keyByte);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 解码单个字符串
|
|
*/
|
|
function decodeString(encoded, key) {
|
|
try {
|
|
const base64Decoded = base64Decode(encoded);
|
|
const decrypted = rc4Decrypt(base64Decoded, key);
|
|
return decrypted;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 从代码中提取字符串数组
|
|
*/
|
|
function extractStringArray(code) {
|
|
// 找到字符串数组函数
|
|
const arrayMatch = code.match(/function\s+_0x[a-f0-9]+\(\)\s*\{\s*const\s+_0x[a-f0-9]+\s*=\s*\(function\(\)\s*\{\s*return\s*\[vip,([\s\S]*?)\]\.concat/);
|
|
|
|
if (!arrayMatch) {
|
|
console.log('未找到字符串数组,尝试其他模式...');
|
|
// 尝试直接匹配数组内容
|
|
const directMatch = code.match(/return\s*\[\s*vip\s*,\s*'([^']+)'/);
|
|
if (directMatch) {
|
|
console.log('找到数组起始');
|
|
}
|
|
return null;
|
|
}
|
|
|
|
console.log('找到字符串数组定义');
|
|
|
|
// 提取所有字符串
|
|
const arrayContent = arrayMatch[1];
|
|
const strings = ['cursor']; // vip = 'cursor'
|
|
|
|
// 匹配所有字符串
|
|
const stringRegex = /'([^'\\]*(?:\\.[^'\\]*)*)'/g;
|
|
let match;
|
|
while ((match = stringRegex.exec(arrayContent)) !== null) {
|
|
strings.push(match[1]);
|
|
}
|
|
|
|
console.log(`提取了 ${strings.length} 个字符串`);
|
|
return strings;
|
|
}
|
|
|
|
/**
|
|
* 动态执行提取字符串数组
|
|
*/
|
|
function extractStringArrayDynamic(code) {
|
|
console.log('使用动态方法提取字符串数组...');
|
|
|
|
// 提取 _0x4ff4 或类似函数
|
|
const funcMatch = code.match(/(function\s+(_0x[a-f0-9]+)\(\)\s*\{[\s\S]*?return\s+_0x[a-f0-9]+;\s*\})/);
|
|
|
|
if (!funcMatch) {
|
|
console.log('未找到字符串数组函数');
|
|
return null;
|
|
}
|
|
|
|
const funcName = funcMatch[2];
|
|
console.log(`找到函数: ${funcName}`);
|
|
|
|
// 提取完整的数组定义部分
|
|
// 找到 concat 链的结束
|
|
let depth = 0;
|
|
let start = code.indexOf('return[vip,');
|
|
if (start === -1) start = code.indexOf("return [vip,");
|
|
if (start === -1) {
|
|
console.log('未找到数组起始');
|
|
return null;
|
|
}
|
|
|
|
let end = start;
|
|
let inString = false;
|
|
let stringChar = '';
|
|
|
|
for (let i = start; i < code.length; i++) {
|
|
const char = code[i];
|
|
|
|
if (!inString) {
|
|
if (char === '"' || char === "'") {
|
|
inString = true;
|
|
stringChar = char;
|
|
} else if (char === '[') {
|
|
depth++;
|
|
} else if (char === ']') {
|
|
depth--;
|
|
if (depth === 0) {
|
|
// 检查后面是否还有 .concat
|
|
const after = code.slice(i + 1, i + 20);
|
|
if (!after.match(/^\s*\.\s*concat/)) {
|
|
end = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (char === stringChar && code[i-1] !== '\\') {
|
|
inString = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 构建可执行代码
|
|
const arrayCode = `
|
|
var vip = 'cursor';
|
|
${code.slice(start, end)};
|
|
`;
|
|
|
|
try {
|
|
const result = eval(arrayCode);
|
|
console.log(`动态提取了 ${result.length} 个字符串`);
|
|
return result;
|
|
} catch (e) {
|
|
console.log('动态执行失败:', e.message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 手动解析提取字符串数组
|
|
*/
|
|
function extractStringsManual(code) {
|
|
console.log('使用手动解析方法...');
|
|
|
|
const strings = ['cursor'];
|
|
|
|
// 找到所有在数组中的字符串字面量
|
|
// 模式: 'xxxxx' 在数组上下文中
|
|
const regex = /\[vip,([\s\S]*?)\]\.concat\(\(function\(\)/g;
|
|
let match;
|
|
|
|
while ((match = regex.exec(code)) !== null) {
|
|
const content = match[1];
|
|
const strRegex = /'([^'\\]*(?:\\.[^'\\]*)*)'/g;
|
|
let strMatch;
|
|
while ((strMatch = strRegex.exec(content)) !== null) {
|
|
strings.push(strMatch[1].replace(/\\'/g, "'"));
|
|
}
|
|
}
|
|
|
|
// 继续查找 concat 中的字符串
|
|
const concatRegex = /return\s*\[([\s\S]*?)\](?:\s*;\s*\}|\s*\.\s*concat)/g;
|
|
while ((match = concatRegex.exec(code)) !== null) {
|
|
const content = match[1];
|
|
if (content.includes('vip')) continue; // 跳过第一个
|
|
|
|
const strRegex = /'([^'\\]*(?:\\.[^'\\]*)*)'/g;
|
|
let strMatch;
|
|
while ((strMatch = strRegex.exec(content)) !== null) {
|
|
const str = strMatch[1].replace(/\\'/g, "'");
|
|
if (!strings.includes(str)) {
|
|
strings.push(str);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`手动提取了 ${strings.length} 个字符串`);
|
|
return strings;
|
|
}
|
|
|
|
/**
|
|
* 在沙盒中执行代码提取字符串数组
|
|
*/
|
|
function extractWithSandbox(code) {
|
|
console.log('尝试沙盒执行...');
|
|
|
|
// 找到字符串数组函数名
|
|
const funcNameMatch = code.match(/function\s+(_0x[a-f0-9]+)\(\)\s*\{\s*const\s+_0x[a-f0-9]+\s*=\s*\(function\(\)\s*\{/);
|
|
if (!funcNameMatch) {
|
|
console.log('未找到数组函数');
|
|
return null;
|
|
}
|
|
|
|
const funcName = funcNameMatch[1];
|
|
console.log(`数组函数名: ${funcName}`);
|
|
|
|
// 提取整个函数定义
|
|
const funcStart = code.indexOf(`function ${funcName}()`);
|
|
let braceCount = 0;
|
|
let funcEnd = funcStart;
|
|
let started = false;
|
|
|
|
for (let i = funcStart; i < code.length; i++) {
|
|
if (code[i] === '{') {
|
|
braceCount++;
|
|
started = true;
|
|
} else if (code[i] === '}') {
|
|
braceCount--;
|
|
if (started && braceCount === 0) {
|
|
funcEnd = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const funcCode = code.slice(funcStart, funcEnd);
|
|
|
|
// 执行并获取结果
|
|
const execCode = `
|
|
var vip = 'cursor';
|
|
${funcCode}
|
|
${funcName}();
|
|
`;
|
|
|
|
try {
|
|
const result = eval(execCode);
|
|
console.log(`沙盒执行成功,获取 ${result.length} 个字符串`);
|
|
return result;
|
|
} catch (e) {
|
|
console.log('沙盒执行失败:', e.message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 主函数
|
|
*/
|
|
async function main() {
|
|
console.log('╔════════════════════════════════════════════════════╗');
|
|
console.log('║ Provider.js 完整反混淆工具 ║');
|
|
console.log('╚════════════════════════════════════════════════════╝');
|
|
|
|
const inputPath = 'D:/temp/破解/cursorpro-0.4.5/原版本/extension/out/webview/provider.js';
|
|
const outputPath = 'D:/temp/破解/cursorpro-0.4.5/deobfuscated_full/extension/out/webview/provider.js';
|
|
|
|
// 读取文件
|
|
console.log('\n[1] 读取文件...');
|
|
let code = fs.readFileSync(inputPath, 'utf8');
|
|
console.log(`文件大小: ${(code.length / 1024).toFixed(2)} KB`);
|
|
|
|
// 提取字符串数组
|
|
console.log('\n[2] 提取字符串数组...');
|
|
let stringArray = extractWithSandbox(code);
|
|
|
|
if (!stringArray || stringArray.length < 100) {
|
|
console.log('沙盒方法失败,尝试手动解析...');
|
|
stringArray = extractStringsManual(code);
|
|
}
|
|
|
|
if (!stringArray || stringArray.length < 10) {
|
|
console.log('错误:无法提取字符串数组');
|
|
return;
|
|
}
|
|
|
|
console.log(`成功提取 ${stringArray.length} 个编码字符串`);
|
|
|
|
// 找到解码器函数名和密钥
|
|
console.log('\n[3] 分析解码器...');
|
|
const decoderMatch = code.match(/const\s+(_0x[a-f0-9]+)\s*=\s*(_0x[a-f0-9]+);/);
|
|
let decoderName = '_0x56bd';
|
|
let decoderAlias = '_0xa6d6ac';
|
|
|
|
if (decoderMatch) {
|
|
decoderAlias = decoderMatch[1];
|
|
decoderName = decoderMatch[2];
|
|
console.log(`解码器: ${decoderName}, 别名: ${decoderAlias}`);
|
|
}
|
|
|
|
// 构建解码映射
|
|
console.log('\n[4] 解码字符串...');
|
|
const decodeMap = new Map();
|
|
let decodedCount = 0;
|
|
|
|
// 找到所有解码器调用: _0x56bd(0x123, 'key') 或 _0xa6d6ac(0x123, 'key')
|
|
const callPattern = new RegExp(
|
|
`(?:${decoderName}|${decoderAlias})\\s*\\(\\s*(0x[a-f0-9]+)\\s*,\\s*'([^']*)'\\s*\\)`,
|
|
'gi'
|
|
);
|
|
|
|
const calls = new Set();
|
|
let callMatch;
|
|
while ((callMatch = callPattern.exec(code)) !== null) {
|
|
calls.add(JSON.stringify([callMatch[0], callMatch[1], callMatch[2]]));
|
|
}
|
|
|
|
console.log(`找到 ${calls.size} 个唯一的解码器调用`);
|
|
|
|
// 解码每个调用
|
|
const baseIndex = 0x107; // 从混淆代码中提取的基准索引
|
|
|
|
for (const callStr of calls) {
|
|
const [fullMatch, indexHex, key] = JSON.parse(callStr);
|
|
const index = parseInt(indexHex, 16) - baseIndex;
|
|
|
|
if (index >= 0 && index < stringArray.length) {
|
|
const encoded = stringArray[index];
|
|
const decoded = decodeString(encoded, key);
|
|
|
|
if (decoded && decoded.length > 0 && !decoded.includes('\x00')) {
|
|
decodeMap.set(fullMatch, decoded);
|
|
decodedCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`成功解码 ${decodedCount} 个字符串`);
|
|
|
|
// 替换代码中的调用
|
|
console.log('\n[5] 替换代码...');
|
|
let newCode = code;
|
|
let replaceCount = 0;
|
|
|
|
for (const [pattern, decoded] of decodeMap) {
|
|
// 转义特殊字符用于字符串
|
|
let replacement;
|
|
if (decoded.includes('\n') || decoded.includes('\r') || decoded.includes('`')) {
|
|
// 使用 JSON.stringify 处理复杂字符串
|
|
replacement = JSON.stringify(decoded);
|
|
} else if (decoded.includes("'") && !decoded.includes('"')) {
|
|
replacement = `"${decoded}"`;
|
|
} else {
|
|
replacement = `'${decoded.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
}
|
|
|
|
const before = newCode.length;
|
|
newCode = newCode.split(pattern).join(replacement);
|
|
if (newCode.length !== before) {
|
|
replaceCount++;
|
|
}
|
|
}
|
|
|
|
console.log(`替换了 ${replaceCount} 个模式`);
|
|
|
|
// 清理代码
|
|
console.log('\n[6] 清理代码...');
|
|
|
|
// 简化属性访问
|
|
newCode = newCode.replace(/\['([a-zA-Z_$][a-zA-Z0-9_$]*)'\]/g, '.$1');
|
|
|
|
// 替换布尔值
|
|
newCode = newCode.replace(/!!\[\]/g, 'true');
|
|
newCode = newCode.replace(/!\[\]/g, 'false');
|
|
|
|
// 保存
|
|
console.log('\n[7] 保存文件...');
|
|
const outputDir = path.dirname(outputPath);
|
|
if (!fs.existsSync(outputDir)) {
|
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
}
|
|
|
|
fs.writeFileSync(outputPath, newCode);
|
|
console.log(`保存到: ${outputPath}`);
|
|
console.log(`新文件大小: ${(newCode.length / 1024).toFixed(2)} KB`);
|
|
|
|
// 保存解码映射供调试
|
|
const mapPath = 'D:/temp/破解/cursorpro-0.4.5/provider_decode_map.json';
|
|
const mapObj = {};
|
|
for (const [k, v] of decodeMap) {
|
|
if (v.length < 500) { // 只保存较短的字符串
|
|
mapObj[k] = v;
|
|
}
|
|
}
|
|
fs.writeFileSync(mapPath, JSON.stringify(mapObj, null, 2));
|
|
console.log(`解码映射保存到: ${mapPath}`);
|
|
|
|
console.log('\n✅ 完成!');
|
|
}
|
|
|
|
main().catch(console.error);
|