/** * 移除死代码分支 * 处理 if ("xxx" === "xxx") 和 if ("xxx" !== "yyy") 等模式 */ const fs = require('fs'); const babel = require('@babel/core'); const traverse = require('@babel/traverse').default; const generate = require('@babel/generator').default; const t = require('@babel/types'); const inputPath = 'D:/temp/破解/cursorpro-0.4.5/deobfuscated_full/extension/out/webview/provider.js'; const outputPath = 'D:/temp/破解/cursorpro-0.4.5/deobfuscated_full/extension/out/webview/provider.js'; console.log('╔════════════════════════════════════════════════════╗'); console.log('║ 死代码移除工具 ║'); console.log('╚════════════════════════════════════════════════════╝'); const code = fs.readFileSync(inputPath, 'utf8'); console.log(`读取文件: ${(code.length / 1024).toFixed(2)} KB`); console.log('\n[1] 解析 AST...'); let ast; try { ast = babel.parseSync(code, { sourceType: 'script', plugins: [] }); console.log('AST 解析成功'); } catch (e) { console.error('AST 解析失败:', e.message); process.exit(1); } console.log('\n[2] 移除死代码分支...'); let removedBranches = 0; // 评估常量条件 function evaluateCondition(node) { // "xxx" === "xxx" -> true if (t.isBinaryExpression(node, { operator: '===' }) || t.isBinaryExpression(node, { operator: '==' })) { if (t.isStringLiteral(node.left) && t.isStringLiteral(node.right)) { return node.left.value === node.right.value; } } // "xxx" !== "yyy" -> true if different if (t.isBinaryExpression(node, { operator: '!==' }) || t.isBinaryExpression(node, { operator: '!=' })) { if (t.isStringLiteral(node.left) && t.isStringLiteral(node.right)) { return node.left.value !== node.right.value; } } return null; } traverse(ast, { IfStatement(path) { const result = evaluateCondition(path.node.test); if (result === true) { // 条件永远为 true,保留 consequent,移除 if 结构 if (t.isBlockStatement(path.node.consequent)) { path.replaceWithMultiple(path.node.consequent.body); } else { path.replaceWith(path.node.consequent); } removedBranches++; } else if (result === false) { // 条件永远为 false,保留 alternate 或移除整个 if if (path.node.alternate) { if (t.isBlockStatement(path.node.alternate)) { path.replaceWithMultiple(path.node.alternate.body); } else { path.replaceWith(path.node.alternate); } } else { path.remove(); } removedBranches++; } }, ConditionalExpression(path) { const result = evaluateCondition(path.node.test); if (result === true) { path.replaceWith(path.node.consequent); removedBranches++; } else if (result === false) { path.replaceWith(path.node.alternate); removedBranches++; } } }); console.log(`移除了 ${removedBranches} 个死代码分支`); console.log('\n[3] 简化剩余控制流对象...'); let simplifiedProps = 0; // 再次处理剩余的控制流对象 const objectMappings = new Map(); traverse(ast, { VariableDeclarator(path) { if (!t.isIdentifier(path.node.id)) return; if (!t.isObjectExpression(path.node.init)) return; const varName = path.node.id.name; const props = {}; for (const prop of path.node.init.properties) { if (!t.isObjectProperty(prop)) continue; let key; if (t.isStringLiteral(prop.key)) { key = prop.key.value; } else if (t.isIdentifier(prop.key)) { key = prop.key.name; } else { continue; } if (t.isFunctionExpression(prop.value)) { const func = prop.value; if (func.body.body.length === 1 && t.isReturnStatement(func.body.body[0])) { const ret = func.body.body[0].argument; if (t.isBinaryExpression(ret) && func.params.length === 2) { const [p1, p2] = func.params; if (t.isIdentifier(ret.left) && t.isIdentifier(ret.right) && ret.left.name === p1.name && ret.right.name === p2.name) { props[key] = { type: 'binary', operator: ret.operator }; } } else if (t.isLogicalExpression(ret) && func.params.length === 2) { const [p1, p2] = func.params; if (t.isIdentifier(ret.left) && t.isIdentifier(ret.right) && ret.left.name === p1.name && ret.right.name === p2.name) { props[key] = { type: 'logical', operator: ret.operator }; } } else if (t.isCallExpression(ret)) { if (t.isIdentifier(ret.callee) && ret.callee.name === func.params[0]?.name) { props[key] = { type: 'call', argCount: ret.arguments.length }; } } } } else if (t.isStringLiteral(prop.value)) { props[key] = { type: 'string', value: prop.value.value }; } } if (Object.keys(props).length > 0) { objectMappings.set(varName, props); } } }); traverse(ast, { CallExpression(path) { const callee = path.node.callee; if (!t.isMemberExpression(callee)) return; if (!t.isIdentifier(callee.object)) return; const objName = callee.object.name; const mapping = objectMappings.get(objName); if (!mapping) return; let propName; if (t.isStringLiteral(callee.property)) { propName = callee.property.value; } else if (t.isIdentifier(callee.property) && !callee.computed) { propName = callee.property.name; } else { return; } const propInfo = mapping[propName]; if (!propInfo) return; const args = path.node.arguments; if (propInfo.type === 'binary' && args.length === 2) { path.replaceWith(t.binaryExpression(propInfo.operator, args[0], args[1])); simplifiedProps++; } else if (propInfo.type === 'logical' && args.length === 2) { path.replaceWith(t.logicalExpression(propInfo.operator, args[0], args[1])); simplifiedProps++; } else if (propInfo.type === 'call' && args.length >= 1) { const fn = args[0]; const fnArgs = args.slice(1); path.replaceWith(t.callExpression(fn, fnArgs)); simplifiedProps++; } }, MemberExpression(path) { if (path.parent && t.isCallExpression(path.parent) && path.parent.callee === path.node) { return; } if (!t.isIdentifier(path.node.object)) return; const objName = path.node.object.name; const mapping = objectMappings.get(objName); if (!mapping) return; let propName; if (t.isStringLiteral(path.node.property)) { propName = path.node.property.value; } else if (t.isIdentifier(path.node.property) && !path.node.computed) { propName = path.node.property.name; } else { return; } const propInfo = mapping[propName]; if (!propInfo) return; if (propInfo.type === 'string') { path.replaceWith(t.stringLiteral(propInfo.value)); simplifiedProps++; } } }); console.log(`简化了 ${simplifiedProps} 处属性访问`); console.log('\n[4] 生成代码...'); const output = generate(ast, { comments: false, compact: false, concise: false }, code); // 后处理 let finalCode = output.code; finalCode = finalCode.replace(/\["([a-zA-Z_$][a-zA-Z0-9_$]*)"\]/g, '.$1'); finalCode = finalCode.replace(/'(enumerable|value|get|writable|configurable)':/g, '$1:'); console.log('\n[5] 保存文件...'); fs.writeFileSync(outputPath, finalCode); console.log(`保存到: ${outputPath}`); console.log(`新文件大小: ${(finalCode.length / 1024).toFixed(2)} KB`); // 统计 const deadCodeRemaining = (finalCode.match(/"[A-Za-z]{5}"\s*===\s*"[A-Za-z]{5}"/g) || []).length; console.log(`\n剩余死代码条件: ${deadCodeRemaining}`); console.log('\n✅ 完成!');