目录
环境搭建
安装环境
npm install @babel/parser @babel/traverse @babel/generator @babel/types
ast转换的代码框架
const fs = require('fs'); const vm = require('node:vm'); const parser = require('@babel/parser'); const traverse = require('@babel/traverse').default; const t = require('@babel/types'); const generator = require('@babel/generator').default; // 读取混淆代码文件 const code = fs.readFileSync('obfuscated.js', 'utf-8'); // 解析为AST const ast = parser.parse(code); // TODO 修改AST的逻辑将在这里编写 // 生成新代码 const output = generator(ast); fs.writeFileSync('clean.js', output.code);
代码:修改AST的逻辑
重命名函数名
案例
function _0x1a2b(s) { return atob(s); } console.log(_0x1a2b("SGVsbG8=")); // 输出 "Hello"
结果
function decryptString(s) { return atob(s); } console.log(_0x1a2b("SGVsbG8=")); // 输出 "Hello"
代码实现:
//重命名加密函数 FunctionDeclaration(path) { if (path.node.id.name==='_0x1a2b') { path.node.id.name='decryptString'; } }
重命名变量并修改变量值
案例
function _0x12ab() { const _0x3cde = ["\\x48\\x65\\x6c\\x6c\\x6f", "\\x77\\x6f\\x72\\x6c\\x64"]; return _0x3cde[0] + " " + _0x3cde[1]; }
结果
function _0x12ab() { const words = ["Hello", "world"]; return words[0] + " " + words[1]; }
代码实现:
//重命名变量并解密字符串 //变量声明节点 VariableDeclarator(path) { if (path.node.id.name === '_0x3cde') { path.node.id.name = 'words'; // 修改变量名 // 遍历elements数组 path.node.init.elements = path.node.init.elements.map(element => { // 解密十六进制字符串 const decoded = element.value.replace(/\\x([0-9a-fA-F]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)) ); // 关键 return { type: 'StringLiteral', value: decoded }; }); } }, //对象成员访问节点 MemberExpression(path) { if(path.node.object.name==='_0x3cde'){ path.node.object.name='words'; } }
函数调用替换
案例
function _0x1a2b(s) { return atob(s); } console.log(_0x1a2b("SGVsbG8=")); // 输出 "Hello"
结果
function _0x1a2b(s) { return atob(s); } console.log("Hello"); // 输出 "Hello"
代码实现:
CallExpression(path) { if (path.node.callee.name==='_0x1a2b'&&path.node.arguments[0].type==='StringLiteral') { //取出参数 const encryptedStr=path.node.arguments[0].value; // 对参数进行解密 const decryptedStr=atob(encryptedStr); // 把原来的参数调用_0x1a2b("SGVsbG8="),替换为decryptedStr,即对atob(encryptedStr) path.replaceWith({ type:'StringLiteral', value:decryptedStr }); } }
控制流扁平化还原
案例
function _0x1234() { const _0x5678 = [2, 0, 1]; while (true) { switch (_0x5678.shift()) { case 0: console.log("world"); continue; case 1: console.log("!"); continue; case 2: console.log("Hello"); continue; } break; } }
结果
function _0x1234() { console.log("Hello"); console.log("world"); console.log("!"); }
- AST转换逻辑
· 识别switch-case结构:找到SwitchStatement节点
· 提取case顺序:通过_0x5678数组的初始值确定执行顺序(本例顺序为2→0→1)
· 重建代码顺序:按顺序合并case块中的语句,删除switch和while结构 代码实现:
FunctionDeclaration(path) { // 1. 定位控制流数组声明 const controlFlowDecl=path.node.body.body.find(n=> (t.isVariableDeclaration(n)&& n.declarations[0].id.name==='_0x5678') ); if (!controlFlowDecl) return; // 2. 提取控制流顺序 [2, 0, 1] const controlFlowArray = controlFlowDecl.declarations[0].init.elements .map(e=>e.value); // 3. 删除控制流数组声明 path.node.body.body=path.node.body.body.filter(n=> n!==controlFlowDecl); // 4. 提取switch语句 const switchStmt = path.node.body.body .find(n => t.isWhileStatement(n)).body.body .find(n => t.isSwitchStatement(n));; // 5. 删除while语句 path.node.body.body=path.node.body.body.filter(n=>!t.isWhileStatement(n)); // 6. 建立case值到语句的映射 const caseMap=new Map(); switchStmt.cases.forEach(n=>{ caseMap.set(n.test.value,n.consequent); }); // 7. 按控制流顺序重组语句 const orderedStatements=[]; for(const caseVal of controlFlowArray){ const stmts=caseMap.get(caseVal) .filter(n=>!t.isContinueStatement(n));// 移除continue orderedStatements.push(...stmts); } // 8. 插入到函数体头部(保留其他语句) path.node.body.body.unshift(...orderedStatements); }
删除未使用的变量
案例
// 原始代码 const unusedVar = "test"; // 无任何地方使用 const activeVar = "data"; console.log(activeVar);
结果
const activeVar = "data"; console.log(activeVar);
代码实现:
VariableDeclarator(path){ const binding = path.scope.getBinding(path.node.id.name); // 检测变量是否未被引用 if (!binding || binding.references === 0) { // 删除整个 VariableDeclaration(需判断是否最后一个声明) const parent = path.parent; if (parent.declarations.length === 1) { // 情况1:整个 VariableDeclaration 只有一个声明 eg: const a = 1; path.parentPath.remove();// 删除父节点(即整个声明语句) } else { // 情况2:声明语句中有多个变量 eg: let a = 1, b = 2; path.remove();// 只删除当前 VariableDeclarator } } }
对象属性简化
案例
const _0xabc = { "xYz": function(s) { return s.toUpperCase(); } }; console.log(_0xabc["xYz"]("hello")); // 输出 "HELLO"
结果
const _0xabc = { upper: function (s) { return s.toUpperCase(); } }; console.log(utils.upper("hello")); // 输出 "HELLO"
AST转换逻辑
- 识别对象属性:找到ObjectProperty节点中的动态键(如"xYz")
- 重命名属性和调用方式:将_0xabc["xYz"]改为utils.upper
代码实现:
ObjectProperty(path) { // 重命名键名 if (path.node.key.value === "xYz") { path.node.key = t.identifier("upper"); // 改为标识符形式 } }, MemberExpression(path) { // 转换动态属性访问为静态 if ( t.isIdentifier(path.node.object, { name: "_0xabc" }) && t.isStringLiteral(path.node.property, { value: "xYz" }) ) { path.node.object.name = "utils"; path.node.property = t.identifier("upper"); path.node.computed = false; // 改为.访问方式 } }
条件表达式优化
案例
const isVIP = !![]; // 混淆写法:!![] → true console.log(isVIP ? "VIP User" : "Guest");
结果
const isVIP = true; // 混淆写法:!![] → true console.log("VIP User");
- AST转换逻辑
· 计算常量表达式:在AST遍历阶段预计算!![]的值
· 删除无效分支:根据计算结果删除false分支 代码实现:
VariableDeclarator(path) { //!![] → true if (t.isUnaryExpression(path.node.init,{ operator: "!" })){ if (t.isUnaryExpression(path.node.init.argument,{ operator: "!" })){ // 3. 检测最内层是否为空数组 [] const arrayExpr = path.node.init.argument.argument; if(t.isArrayExpression(arrayExpr) && arrayExpr.elements.length === 0){ path.get('init').replaceWith(t.booleanLiteral(true)); } } } }, ConditionalExpression(path) { // 步骤1:检查 test 是否为变量引用(如 isVIP) if (!t.isIdentifier(path.node.test)) return; const varName = path.node.test.name; // 获取变量名 'isVIP' // 步骤2:从作用域中获取变量绑定信息 const binding = path.scope.getBinding(varName); // 步骤3:检查绑定是否存在且是常量 if (!binding || !binding.constant) return; // 步骤4:找到变量的声明节点 const declaration = binding.path.node; // 步骤5:提取变量的初始值 if (t.isVariableDeclarator(declaration)){ const initValue = declaration.init.value; // true console.log(`变量 ${varName} 的值为: ${initValue}`); // 步骤6(可选):根据值直接替换条件表达式 path.replaceWith( initValue ? path.node.consequent : path.node.alternate ); } }
表达式还原
案例
var e=840; e= e - (-0x2 * 0x7d6 + -0xd1 * 0x14 + 0x210e);
结果
var e = 840; e = e - 270;
代码实现:
// 静态计算表达式值 function evaluateExpression(node) { if (t.isLiteral(node)) { return node.value; } if (t.isUnaryExpression(node)) { const argValue = evaluateExpression(node.argument); if (typeof argValue === 'number') { return node.operator === '-' ? -argValue : argValue; } } if (t.isBinaryExpression(node)) { const left = evaluateExpression(node.left); const right = evaluateExpression(node.right); if (typeof left === 'number' && typeof right === 'number') { switch (node.operator) { case '+': return left + right; case '-': return left - right; case '*': return left * right; case '/': return left / right; // 可扩展其他运算符 } } } return undefined; // 无法静态计算 }
BinaryExpression(path) { const result = evaluateExpression(path.node); if (typeof result === 'number') { // 替换为计算结果的字面量 path.replaceWith(t.numericLiteral(result)); } }
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。