AST反混淆练习题(一)
第1题:变量名替换(入门)
混淆代码
javascript
//混淆代码
var _0x1a2b = "Hello";
var _0x3c4d = "World";
var _0x5e6f = _0x1a2b + " " + _0x3c4d;
function _0x7g8h(_0x9i0j) {
return _0x9i0j.toUpperCase();
}
console.log(_0x7g8h(_0x5e6f));
期望还原
javascript
//期望还原
var greeting = "Hello";
var subject = "World";
var message = greeting + " " + subject;
function toUpperCase(text) {
return text.toUpperCase();
}
console.log(toUpperCase(message));
提示:
核心思路:建立一个映射表,把所有 _0x 开头的变量名替换为有意义的名称。
步骤:
定义映射表 nameMap ,键是旧名,值是新名
遍历 Identifier 节点
如果 path.node.name 在映射表中,替换为新名
答案:
javascript
// 1. 导入必要的模块
const fs = require('fs'); // 文件系统模块(本题未使用)
const parser = require('@babel/parser'); // 将JS代码解析为AST
const traverse = require('@babel/traverse').default; // 遍历AST节点
const t = require('@babel/types'); // AST节点类型工具
const generator = require('@babel/generator').default; // 将AST还原为代码
let jsCode = `
var _0x1a2b = "Hello";
var _0x3c4d = "World";
var _0x5e6f = _0x1a2b + " " + _0x3c4d;
function _0x7g8h(_0x9i0j) {
return _0x9i0j.toUpperCase();
}
console.log(_0x7g8h(_0x5e6f));
`
const ast = parser.parse(jsCode,{
sourceType: 'module'
});
let nameMap = {
'_0x1a2b':'greeting ',
'_0x3c4d':'subject',
'_0x7g8h':'toUpperCase',
'_0x9i0j':'text',
'_0x5e6f':'message'
};// 保存变量名和变量值的映射关系
const visitor = {
Identifier (path){
let name = path.node.name;
console.log(`混淆代码:${name}===>还原后:${nameMap[name]}`)
if(nameMap[name]){ // 如果存在映射关系,则替换变量名
path.node.name = nameMap[name]
}
}
}// 遍历AST节点
traverse(ast, visitor);// 执行遍历
let code = generator(ast).code;
console.log(code);
第2题:十六进制字符串解码
混淆代码
javascript
var msg = "\x48\x65\x6c\x6c\x6f \x57\x6f\x72\x6c\x64";
console.log(msg);
期望还原
javascript
var msg = "Hello World";
console.log(msg);
提示:
核心思路:十六进制字符串在AST中存储时, node.value 已经是解码后的值,但 node.extra.raw 还保留着原始的十六进制形式。我们需要让generator输出普通字符串。
关键知识点:
StringLiteral 节点有两个重要属性:
node.value :解码后的字符串值(如 "Hello World" )
node.extra.raw :原始字符串形式(如 "\x48\x65\x6c\x6c\x6f \x57\x6f\x72\x6c\x64" )
解决方法:
删除 node.extra 对象,这样generator就会使用 node.value 输出普通字符串
答案:
javascript
let jsCode = `
var msg = "\x48\x65\x6c\x6c\x6f \x57\x6f\x72\x6c\x64";
console.log(msg);
`
const ast = parser.parse(jsCode,{
sourceType: 'module'
});
const visitor = {
StringLiteral(path) {
// 删除 extra 对象,让generator输出普通字符串
delete path.node.extra;
}
};
traverse(ast, visitor);// 执行遍历
let code = generator(ast).code;
console.log(code);
第3题:字符串数组混淆(基础)
混淆代码
javascript
var _0x1234 = ['\x48\x65\x6c\x6c\x6f', '\x57\x6f\x72\x6c\x64', '\x4c\x6f\x67\x69\x6e'];
function _0x5678(_0x9abc) {
_0x9abc = _0x9abc - 0x0;
return _0x1234[_0x9abc];
}
var username = _0x5678('0x0');
var action = _0x5678('0x2');
console.log(username + ' ' + action);
期望还原
javascript
var username = "Hello";
var action = "Login";
console.log(username + ' ' + action);
提示
- 提取数组内容
遍历 VariableDeclarator ,找到 _0x1234
提取数组中的字符串到 stringArray - 替换函数调用
遍历 CallExpression ,找到 _0x5678('0x0') 这样的调用
解析参数 '0x0' → 0 ,取 stringArray[0]
用 t.stringLiteral() 创建新节点替换 - .删除无用代码
删除数组定义 _0x1234
删除函数定义 _0x5678
答案:
javascript
let jsCode = `
var _0x1234 = ['\x48\x65\x6c\x6c\x6f', '\x57\x6f\x72\x6c\x64', '\x4c\x6f\x67\x69\x6e'];
function _0x5678(_0x9abc) {
_0x9abc = _0x9abc - 0x0;
return _0x1234[_0x9abc];
}
var username = _0x5678('0x0');
var action = _0x5678('0x2');
console.log(username + ' ' + action);
`
const ast = parser.parse(jsCode,{
sourceType: 'module'
});
let stringArray = [];
const visitor = {
VariableDeclarator(path) {
if (path.node.id.name === '_0x1234') { // 找到_0x1234变量
stringArray = path.node.init.elements.map(item => item.value);// 提取字符串数组
path.remove();//提取完之后删除_0x1234变量
}
},
CallExpression(path) {
if(path.node.callee.name && path.node.callee.name === '_0x5678'){
const index = parseInt(path.node.arguments[0].value)
const stringvalue = stringArray[index]
// console.log(`数组中第${index}个元素是${stringvalue}`)
path.replaceWith(t.stringLiteral(stringvalue))
}
},
FunctionDeclaration(path) {
if(path.node.id.name === '_0x5678'){
path.remove();//删除_0x5678函数声明
}
}
};
traverse(ast, visitor);// 执行遍历
let code = generator(ast).code;
console.log(code);
第4题:简单控制流平坦化
混淆代码
javascript
function calculate(a, b) {
var _0x1 = 0;
var _0x2 = 0;
while (true) {
switch (_0x1) {
case 0:
_0x2 = a + b;
_0x1 = 1;
break;
case 1:
_0x2 = _0x2 * 2;
_0x1 = 2;
break;
case 2:
return _0x2;
}
}
}
期望还原
javascript
function calculate(a, b) {
var _0x2 = 0;
_0x2 = a + b;
_0x2 = _0x2 * 2;
return _0x2;
}
提示
步骤1:找到 while(true) 结构
遍历 WhileStatement
检查 path.node.test.value === true
如果不是 while(true) ,直接 return 跳过
步骤2:获取 switch 语句
while 的循环体是 BlockStatement (代码块)
BlockStatement 的 body 是语句数组
switch 是数组的第0个元素: path.node.body.body[0]
检查它是否是 SwitchStatement 类型
步骤3:判断是否状态赋值
判断条件:
stmt.type === 'ExpressionStatement' (表达式语句)
stmt.expression.type === 'AssignmentExpression' (赋值表达式)
stmt.expression.left.name === '_0x1' (赋值给状态变量)
步骤4:提取有效代码
创建一个空数组 statements = []
遍历 switchStmt.cases (所有case)
对每个 caseNode ,遍历 caseNode.consequent (case里的语句)
对每个语句:
如果是 BreakStatement ,跳过( return )
如果是状态赋值,跳过(调用你的辅助函数判断)
其他语句,推入 statements 数组
步骤5:替换整个 while
使用 path.replaceWithMultiple(statements)
这样就把 while-switch 替换成了顺序执行的代码
预期提取过程:
case 0:
_0x2 = a + b; ✅ 保留
_0x1 = 1; ❌ 跳过(状态赋值)
break; ❌ 跳过(break)
case 1:
_0x2 = _0x2 * 2; ✅ 保留
_0x1 = 2; ❌ 跳过(状态赋值)
break; ❌ 跳过(break)
case 2:
return _0x2; ✅ 保留
最终 statements 数组包含3个语句,替换掉整个 while
答案
javascript
let jsCode = `
function calculate(a, b) {
var _0x1 = 0;
var _0x2 = 0;
while (true) {
switch (_0x1) {
case 0:
_0x2 = a + b;
_0x1 = 1;
break;
case 1:
_0x2 = _0x2 * 2;
_0x1 = 2;
break;
case 2:
return _0x2;
}
}
}
`
const ast = parser.parse(jsCode, {
sourceType: 'module'
});
const visitor = {
WhileStatement(path) {
// 检查循环条件是否为true,如果不是则直接返回
if (path.node.test.value !== true) return;
// 检查循环体是否为switch语句,如果不是则直接返回
let switchStatement = path.node.body.body[0];
if (switchStatement.type !== 'SwitchStatement') return;
// 获取switch语句的所有case语句
let statements = [];
// 遍历case语句,获取所有非break语句和状态赋值语句
switchStatement.cases.forEach(caseNode => {
caseNode.consequent.forEach(statement => {
if (statement.type === 'BreakStatement') return;
//跳过状态赋值语句
if (statement.type === 'ExpressionStatement' && statement.expression.type === 'AssignmentExpression' &&
statement.expression.left.name === '_0x1') return;//
statements.push(statement);
// console.log(statements)
})
}
)
// 替换循环体为所有非break语句和状态赋值语句
path.replaceWithMultiple(statements);
}
};
traverse(ast, visitor);
let code = generator(ast).code;
console.log(code);
第5题:do-while 控制流平坦化
混淆代码
javascript
function processData(data) {
var _0x1 = 0;
var _0x2 = [];
var _0x3 = data.length;
var _0x4 = 0;
do {
switch (_0x1) {
case 0:
if (_0x4 < _0x3) {
_0x1 = 1;
} else {
_0x1 = 3;
}
break;
case 1:
_0x2.push(data[_0x4]);
_0x1 = 2;
break;
case 2:
_0x4++;
_0x1 = 0;
break;
case 3:
return _0x2;
}
} while (true);
}
期望还原
javascript
function processData(data) {
var _0x2 = [];
var _0x3 = data.length;
var _0x4 = 0;
while (_0x4 < _0x3) {
_0x2.push(data[_0x4]);
_0x4++;
}
return _0x2;
}
提示
提示:
这道题和第4题类似,但有3个关键区别:
区别1:循环类型不同
第4题是 while(true) + switch
本题是 do-while(true) + switch
访问者要改成 DoWhileStatement
区别2:有循环逻辑(不是顺序执行)
第4题的 case 是 0→1→2→结束(顺序执行)
本题的 case 是 0→1→2→0→1→2...(循环执行)
case 0 是条件判断,决定继续循环还是结束
需要识别出循环模式,保留 while ,只去掉 switch
区别3:需要提取循环条件
从 case 0 中提取条件: _0x4 < _0x3
用这个条件构建新的 while 循环
答案
javascript
let jsCode = `
function processData(data) {
var _0x1 = 0;
var _0x2 = [];
var _0x3 = data.length;
var _0x4 = 0;
do {
switch (_0x1) {
case 0:
if (_0x4 < _0x3) {
_0x1 = 1;
} else {
_0x1 = 3;
}
break;
case 1:
_0x2.push(data[_0x4]);
_0x1 = 2;
break;
case 2:
_0x4++;
_0x1 = 0;
break;
case 3:
return _0x2;
}
} while (true);
}
`
const ast = parser.parse(jsCode, {
sourceType: 'module'
});
const visitor = {
DoWhileStatement(path) {
// 1. 检查是否是 do-while(true)
if (path.node.test.value !== true) return;
// 2. 获取 switch 语句
let switchStmt = path.node.body.body[0];
if (switchStmt.type !== 'SwitchStatement') return;
let loopCondition = null;
let loopBody = [];
let returnStmt = null;
// 3. 遍历所有 case,自动识别类型
switchStmt.cases.forEach(caseNode => {
// 提取有效语句(去掉 break 和状态赋值)
let validStmts = caseNode.consequent.filter(stmt => {
if (stmt.type === 'BreakStatement') return false;
if (isStateAssignment(stmt)) return false;
return true;
});
// 判断 case 类型
if (isConditionCase(caseNode)) {
// 条件判断 case:提取 if 条件
let ifStmt = caseNode.consequent.find(s => s.type === 'IfStatement');
loopCondition = ifStmt.test;
}
else if (isReturnCase(caseNode)) {
// 返回 case
returnStmt = validStmts[0];
}
else {
// 循环体代码
loopBody.push(...validStmts);
}
});
// 4. 替换为 while + return
if (loopCondition && returnStmt) {
path.replaceWithMultiple([
t.whileStatement(loopCondition, t.blockStatement(loopBody)),
returnStmt
]);
}
},
// 删除无用的状态变量
VariableDeclarator(path) {
if (path.node.id.name === '_0x1') {
path.remove();
}
}
};
// 辅助函数:判断是否是状态赋值
function isStateAssignment(stmt) {
return stmt.type === 'ExpressionStatement' &&
stmt.expression.type === 'AssignmentExpression' &&
stmt.expression.left.name === '_0x1';
}
// 辅助函数:判断是否是条件判断 case
function isConditionCase(caseNode) {
return caseNode.consequent.some(s => s.type === 'IfStatement');
}
// 辅助函数:判断是否是返回 case
function isReturnCase(caseNode) {
return caseNode.consequent.some(s => s.type === 'ReturnStatement');
}
traverse(ast, visitor);
let code = generator(ast).code;
console.log(code);
第6题:乱序 Switch 控制流平坦化
混淆代码
javascript
function calculate(a, b, c) {
var _0x1 = 3;
var _0x2 = 0;
while (true) {
switch (_0x1) {
case 0:
_0x2 = _0x2 / 2;
_0x1 = 5;
break;
case 1:
return _0x2;
case 2:
_0x2 = a + b;
_0x1 = 4;
break;
case 3:
_0x2 = c * 10;
_0x1 = 2;
break;
case 4:
_0x2 = _0x2 - c;
_0x1 = 0;
break;
case 5:
_0x2 = _0x2 + 100;
_0x1 = 1;
break;
}
}
}
期望还原
javascript
let jsCode = `
function calculate(a, b, c) {
var _0x1 = 3;
var _0x2 = 0;
while (true) {
switch (_0x1) {
case 0:
_0x2 = _0x2 / 2;
_0x1 = 5;
break;
case 1:
return _0x2;
case 2:
_0x2 = a + b;
_0x1 = 4;
break;
case 3:
_0x2 = c * 10;
_0x1 = 2;
break;
case 4:
_0x2 = _0x2 - c;
_0x1 = 0;
break;
case 5:
_0x2 = _0x2 + 100;
_0x1 = 1;
break;
}
}
}
`
const ast = parser.parse(jsCode, {
sourceType: 'module'
});
let startMap = {};
let caseMap = {};
const visitor = {
//找到变量声明,记录到字典中
VariableDeclarator(path){
let varname = path.node.id.name;
let initValue = path.node.init?.value;
if(typeof initValue === 'number'){
startMap[varname] = initValue;
if(varname === '_0x1'){
path.remove();
}
//startMap -->{ _0x1: 3, _0x2: 0 }
}
},
//找到switch语句,找到开始的case变量
WhileStatement(path){
let caseMap = {};//case和变量的映射关系 - 使用对象
let startCase = null;//开始的case变量,初始值是null 代码解析后 应该是3
if(path.node.test.value !== true) return;//只处理while(true)的switch语句
//找到switch语句的case变量
let startVar = path.node.body.body[0].discriminant.name;
//判断case变量是否在字典中
if(startMap[startVar]){
startCase = startMap[startVar]; // 3
}
// console.log('startVar:', startVar, 'startCase:', startCase);
//下面需要处理case的执行顺序
let switchStmt = path.node.body.body[0].cases
//遍历case语句
switchStmt.forEach(caseStmt =>{
//找到_0x1的赋值,以及当前的case值
let caseNum = caseStmt.test.value;
let statements = [];
let nextCase = null;
let hasReturn = false;
//找到case语句的执行语句
caseStmt.consequent.forEach(stmt =>{
if(stmt.type ==='BreakStatement')return ; //跳过break语句
if(stmt.type ==='ExpressionStatement' &&
stmt.expression.type === 'AssignmentExpression' &&
stmt.expression.left.name === startVar ){//找到_0x1的赋值语句
nextCase = stmt.expression.right.value;//找到当前case的下一个case值
return;
}
if(stmt.type ==='ReturnStatement'){//找到return语句
hasReturn = true;//标记当前case有return语句
statements.push(stmt);//将return语句添加到执行语句中
return;
}
statements.push(stmt);//将其他语句添加到执行语句中
})
//将case值和执行语句添加到结果中
caseMap[caseNum] = {
statements,
nextCase,
hasReturn
}
});
// console.log('caseMap:', caseMap);
//按顺序集case的执行语句
// 2. 按顺序收集代码
let newStatements = [];//按顺序收集的执行语句
let current = startCase;//当前的case值
let visited = new Set();//记录已经访问过的case值
while(current !== null && !visited.has(current)) {//如果当前的case值不为null,并且没有访问过当前的case值
visited.add(current);//添加当前的case值到访问过的case值中
let info = caseMap[current]; // 获取当前case的信息
if(!info) break;//如果当前case没有信息,则跳出循环
newStatements.push(...info.statements);//将当前case的执行语句添加到新语句中
if(info.hasReturn) break;//如果当前case有return语句,则跳出循环
current = info.nextCase;//将当前的case值更新为下一个case值
}
// console.log('newStatements:', newStatements);
path.replaceWithMultiple(newStatements);//用新语句替换switch语句
}
}
traverse(ast, visitor);
let code = generator(ast).code;
console.log(code);
第7题
混淆代码
javascript
function complexCalc(a, b, c, d) {
var _s = 0;
var _r = 0;
while (true) {
switch (_s) {
case 0:
_r = a * b;
_s = 2;
break;
case 1:
if (_r > 100) {
_s = 3;
} else {
_s = 5;
}
break;
case 2:
_r = _r + c;
_s = 1;
break;
case 3:
_r = _r * 2;
_s = 4;
break;
case 4:
_r = _r - d;
_s = 6;
break;
case 5:
_r = _r + 50;
_s = 6;
break;
case 6:
return _r;
}
}
}
期望还原
javascript
function complexCalc(a, b, c, d) {
var _r = 0;
_r = a * b;
_r = _r + c;
if (_r > 100) {
_r = _r * 2;
_r = _r - d;
} else {
_r = _r + 50;
}
return _r;
}
提示
由于包含条件判断,还原后的代码会包含 if-else 结构
答案
javacript
let jsCode = `
function complexCalc(a, b, c, d) {
var _s = 0;
var _r = 0;
while (true) {
switch (_s) {
case 0:
_r = a * b;
_s = 2;
break;
case 1:
if (_r > 100) {
_s = 3;
} else {
_s = 5;
}
break;
case 2:
_r = _r + c;
_s = 1;
break;
case 3:
_r = _r * 2;
_s = 4;
break;
case 4:
_r = _r - d;
_s = 6;
break;
case 5:
_r = _r + 50;
_s = 6;
break;
case 6:
return _r;
}
}
}
`;
const ast = parser.parse(jsCode);
let startMap = {};
let startVar = null;
// 辅助函数:从 if-else 中提取分支的 nextCase
function extractBranches(ifStmt, stateVar) {
let branches = {};
// 处理 if 分支
if (ifStmt.consequent.type === 'BlockStatement') {
ifStmt.consequent.body.forEach(stmt => {
if (stmt.type === 'ExpressionStatement' &&
stmt.expression.type === 'AssignmentExpression' &&
stmt.expression.left.name === stateVar) {
branches.if = stmt.expression.right.value;
}
});
}
// 处理 else 分支
if (ifStmt.alternate && ifStmt.alternate.type === 'BlockStatement') {
ifStmt.alternate.body.forEach(stmt => {
if (stmt.type === 'ExpressionStatement' &&
stmt.expression.type === 'AssignmentExpression' &&
stmt.expression.left.name === stateVar) {
branches.else = stmt.expression.right.value;
}
});
}
return branches;
}
// 辅助函数:清理 if-else 中的状态赋值
function cleanIfStatement(ifStmt, stateVar) {
// 创建新的 if 语句,去掉状态赋值
let newIf = t.ifStatement(
ifStmt.test,
t.blockStatement([]),
ifStmt.alternate ? t.blockStatement([]) : null
);
// 复制 if 分支的非状态赋值语句
if (ifStmt.consequent.type === 'BlockStatement') {
newIf.consequent.body = ifStmt.consequent.body.filter(stmt => {
if (stmt.type === 'ExpressionStatement' &&
stmt.expression.type === 'AssignmentExpression' &&
stmt.expression.left.name === stateVar) {
return false;
}
return true;
});
}
// 复制 else 分支的非状态赋值语句
if (ifStmt.alternate && ifStmt.alternate.type === 'BlockStatement') {
newIf.alternate.body = ifStmt.alternate.body.filter(stmt => {
if (stmt.type === 'ExpressionStatement' &&
stmt.expression.type === 'AssignmentExpression' &&
stmt.expression.left.name === stateVar) {
return false;
}
return true;
});
}
return newIf;
}
const visitor = {
VariableDeclarator(path) {
let varname = path.node.id.name;
let initValue = path.node.init?.value;
if (typeof initValue === 'number') {
startMap[varname] = initValue;
}
},
WhileStatement(path) {
if (path.node.test.value !== true) return;
let switchStmt = path.node.body.body[0];
if (switchStmt.type !== 'SwitchStatement') return;
startVar = switchStmt.discriminant.name;
let entryCase = startMap[startVar];
// 构建 caseMap
let caseMap = new Map();
switchStmt.cases.forEach(caseNode => {
let caseNum = caseNode.test.value;
let statements = [];
let nextCase = null;
let hasReturn = false;
let hasBranch = false; // 是否有条件分支
let branches = {}; // 分支信息
caseNode.consequent.forEach(stmt => {
if (stmt.type === 'BreakStatement') return;
// 简单状态赋值
if (stmt.type === 'ExpressionStatement' &&
stmt.expression.type === 'AssignmentExpression' &&
stmt.expression.left.name === startVar) {
nextCase = stmt.expression.right.value;
return;
}
// 条件分支(if-else)
if (stmt.type === 'IfStatement') {
hasBranch = true;
branches = extractBranches(stmt, startVar);
// 清理 if-else 中的状态赋值,保留条件判断
let cleanIf = cleanIfStatement(stmt, startVar);
statements.push(cleanIf);
return;
}
if (stmt.type === 'ReturnStatement') {
hasReturn = true;
}
statements.push(stmt);
});
caseMap.set(caseNum, {
statements,
nextCase,
hasReturn,
hasBranch,
branches
});
});
// 递归展开所有路径
function expandCase(caseNum, visited, result) {
if (visited.has(caseNum)) return;
visited.add(caseNum);
let info = caseMap.get(caseNum);
if (!info) return;
// 添加当前 case 的语句
result.push(...info.statements);
if (info.hasReturn) return;
// 如果有条件分支,递归展开两个分支
if (info.hasBranch) {
// 创建 if-else 结构,包含展开的分支
let lastStmt = result[result.length - 1];
if (lastStmt.type === 'IfStatement') {
// 展开 if 分支
let ifBody = [];
if (info.branches.if !== undefined) {
let ifVisited = new Set(visited);
expandCase(info.branches.if, ifVisited, ifBody);
}
lastStmt.consequent = t.blockStatement(ifBody);
// 展开 else 分支
let elseBody = [];
if (info.branches.else !== undefined) {
let elseVisited = new Set(visited);
expandCase(info.branches.else, elseVisited, elseBody);
}
if (lastStmt.alternate) {
lastStmt.alternate = t.blockStatement(elseBody);
}
}
} else if (info.nextCase !== null) {
expandCase(info.nextCase, visited, result);
}
}
// 从入口开始展开
let newStatements = [];
expandCase(entryCase, new Set(), newStatements);
// 替换 while
path.replaceWithMultiple(newStatements);
}
};
traverse(ast, visitor);
// 清理未使用的变量声明
const cleanVisitor = {
VariableDeclarator(path) {
let varname = path.node.id.name;
if (varname === startVar) {
path.remove(); // 删除状态变量 _s
}
}
};
traverse(ast, cleanVisitor);
console.log(generator(ast, { compact: false }).code);