AST反混淆
合并表达式方式一
这种方式不会计算替换含有变量的表达式
function evaluateAST(node) {
switch (node.type) {
case 'Identifier':
return false
case 'Literal': // 处理字面量节点
return node.value;
case 'NumericLiteral': // 处理字面量节点
return node.value;
case 'StringLiteral': // 处理字面量节点
return node.value;
case 'UnaryExpression':
return parseInt(node.operator+node.argument.value)
case 'BinaryExpression': // 处理二元运算符节点
const left = evaluateAST(node.left);
const right = evaluateAST(node.right);
if(!left || !right) {return '--continue--'}
switch (node.operator) {
case '+':
return left + right;
case '-':
return left - right;
case '*':
return left * right;
case '/':
return left / right;
case '%':
return left % right;
case '<':
return left < right;
case '>':
return left > right;
default:
throw new Error(`Unsupported operator: ${node.operator}`);
}
default:
throw new Error(`Unsupported node type: ${node.type}`);
}
}
function calc_be(path){
var paren_node = path.node
let result = evaluateAST(paren_node)
console.log(result)
if (result !== '--continue--'){
let type_
// 创建一个新的字面量节点替换掉原来的节点
if(typeof result === 'string'){
type_ = 'StringLiteral'
}else if(typeof result === 'number'){
type_ = 'NumericLiteral'
}else if(typeof result === 'boolean'){
type_ = 'BooleanLiteral'
}
const newNode = {
type: type_,
value: result,
raw: result.toString()
}
// 替换当前节点
path.replaceWith(newNode)
}
}
合并表达式方式二
这种方式会尝试计算替换所有能计算出结果的表达式;
path.evaluate() 是计算表达式的值,会返回两个值,当第一个值为true的时候,第二个值就是计算的结果
path.replaceInline() 用一个或多个节点替换另一个节点,在进行节点的替换时,必须先构造节点才可以替换;path.replaceWith()用一个节点替换另一个节点;path.replaceWithMultiple()用多个节点替换另一个节点;
types.valueToNode() 为创建构造节点,将字面量常量值转换为node节点
var merge_expression = {
"BinaryExpression|CallExpression|ConditionalExpression"(path){
const {confident, value} = path.evaluate();
confident && path.replaceInline(types.valueToNode(value))
}
}
解Unicode、Hex、合并表达式、平坦流
ast解混淆主代码
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
const fs = require("fs");
const types = require("@babel/types");
// 删除节点中的extra属性(二进制、Unicode等编码 -> utf-8)
function rm_unicode_hex(path) {
// console.log(path)
let node = path.node;
if (node.extra === undefined)
return;
delete node.extra;
}
// 计算二元计算
function calc_be(path){
const {confident, value} = path.evaluate();
console.log(confident, value)
confident && path.replaceInline(types.valueToNode(value))
}
// 控制流平坦化
function replace_WhileStatement(path) {
cur_node = path.node;
if(cur_node.body.body[0].type === "SwitchStatement"){
var swithStm = cur_node.body.body[0];
// 找到path节点的前兄弟节点,即 _0x1eb1f3所在的节点,然后获取 _0x1eb1f3数组
var arrNodePath = path.getAllPrevSiblings()[0]
var arrValue = arrNodePath.node.declarations[0].init.callee.object.value.split('|');
// SwitchCase节点遍历并临时存储到一个数组
var caseList = swithStm.cases;
var temp_arr_switch_case = [];
arrValue.map(targetIdx => {
var targetBody = caseList[targetIdx].consequent;
// 如果最后一个节点是,则删除ContinueStatement块(continue语句)
if (types.isContinueStatement(targetBody[targetBody.length - 1])){
targetBody.pop();
}
temp_arr_switch_case = temp_arr_switch_case.concat(targetBody)
});
// 多个节点替换一个节点的话使用replaceWithMultiple方法
path.replaceWithMultiple(temp_arr_switch_case);
}
}
// 需要解码的文件位置
let encode_file = "./decode_js/test.js"
// 解码后的文件位置
let decode_file = "./encode_js/test.js"
// 读取需要解码的js文件, 注意文件编码为utf-8格式
let jscode = fs.readFileSync(encode_file, {encoding: "utf-8"});
// 将js代码修转成AST语法树
let ast = parser.parse(jscode);
// AST结构修改逻辑
const visitor = {
StringLiteral: {
enter: [rm_unicode_hex]
},
NumericLiteral: {
enter: [rm_unicode_hex]
},
"BinaryExpression|CallExpression|ConditionalExpression": {
enter: [calc_be]
},
WhileStatement: {
enter: [replace_WhileStatement]
}
}
// 遍历语法树节点,调用修改函数
traverse(ast, visitor);
// 将ast转成js代码,{jsescOption: {"minimal": true}} unicode -> 中文
let {code} = generator(ast, opts = {jsescOption: {"minimal": true}});
// 将js代码保存到文件
fs.writeFile(decode_file, code, (err) => {});
需要解混淆的JS代码
function i(y) {
var O = '3|0|4|2|5|1|6'['split']('|')
, K = -0x2705 + -0x4 * 0x233 + -0x2fd1 * -0x1;
while (!![]) {
switch (O[K++]) {
case '0':
J['GJoEW'](y, ArrayBuffer) && (y = new Uint8Array(y));
continue;
case '1':
while (J['cRGWF'](o, d0)) {
d1 = y[o++],
z += String['fromCharCode'](d1);
}
continue;
case '2':
d0 = y['length'];
continue;
case '3':
var z, o, d0, d1;
continue;
case '4':
z = '';
continue;
case '5':
o = 0x151c + 0xb * 0x1eb + -0x2a35 * 0x1;
continue;
case '6':
return z;
}
break;
}
}
解混淆后的代码
function i(y) {
var O = "3|0|4|2|5|1|6"["split"]("|"),
K = 0;
var z, o, d0, d1;
J["GJoEW"](y, ArrayBuffer) && (y = new Uint8Array(y));
z = "";
d0 = y["length"];
o = 0;
while (J["cRGWF"](o, d0)) {
d1 = y[o++], z += String["fromCharCode"](d1);
}
return z;
}
参考博客:十一姐-CSDN博客