自学内容网 自学内容网

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)
    }
}

合并表达式方式二

这种方式会尝试计算替换所有能计算出结果的表达式;

  1. path.evaluate() 是计算表达式的值,会返回两个值,当第一个值为true的时候,第二个值就是计算的结果

  2. path.replaceInline() 用一个或多个节点替换另一个节点,在进行节点的替换时,必须先构造节点才可以替换;path.replaceWith()用一个节点替换另一个节点;path.replaceWithMultiple()用多个节点替换另一个节点;

  3. 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博客


原文地址:https://blog.csdn.net/qq_41741971/article/details/143699427

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!