python-js逆向之AST语法树初学(一)

这两天看了什么是控制流平坦化,又研究了下ast语法数,如何把混淆乱序后的代码恢复之前的样子。

知识点:1.js包为 recast

2.Recast除了parse/print/builder以外,

还有三项主要功能: run: 通过命令行读取js文件,并转化成ast以供处理。

tnt(recast.types.namedTypes): 通过assert()和check(),可以验证ast对象的类型。

visit: 遍历ast树,获取有效的AST对象并进行更改。

 

下面举例一个案例:

通过 https://obfuscator.io/ 将原代码混淆乱序后输出,复制粘贴到饭碗里,再格式化得到:

function test() {
    var _0x238e3e = {
        'puzUT': '4|3|2|1|0', 'Rgtsl': function (_0x3a9ad8, _0x312b1a) {
            return _0x3a9ad8 < _0x312b1a;
        }, 'HFHYB': function (_0x13ea88, _0x2533df) {
            return _0x13ea88 + _0x2533df;
        }
    };
    var _0x388255 = _0x238e3e['puzUT']['split']('|');
    var _0xfbda52 = 0x0;
    while (!![]) {
        switch (_0x388255[_0xfbda52++]) {
            case'0':
                return _0x2ce383;
            case'1':
                for (var _0x1fa16b = 0x0; _0x238e3e['Rgtsl'](_0x1fa16b, _0x3c5b0c['length']); _0x1fa16b++) {
                    _0x2ce383['push'](String['fromCharCode'](_0x3c5b0c[_0x1fa16b]));
                }
                continue;
            case'2':
                var _0x2ce383 = [];
                continue;
            case'3':
                _0x3c5b0c[0x0] = _0x238e3e['HFHYB'](_0x3c5b0c[0x0], 0x1);
                continue;
            case'4':
                var _0x3c5b0c = [0x67, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64];
                continue;
        }
        break;
    }
}
var s = test();
console['log'](s['join'](''));

在将上方代码粘贴到 https://astexplorer.net/,可以看到ast语法树

 

下面就是代码环节,如何将乱序后的代码有序输出。

将混淆乱序后的代码命名为demo.js,再命名一个read.js写入以下代码:

const recast = require("recast");
const TNT = recast.types.namedTypes;
const {
    expressionStatement,
    memberExpression,
    identifier: id,
    callExpression,
    stringLiteral        //字符串
} = recast.types.builders;
recast.run(function (ast, printSource) {
    recast.visit(ast, {
        visitSwitchCase(node) {
            var codes = "";
            // console.log(node.value.consequent.length)    // 打印case:下的有几条语句  1 2 2 2 2
            for (var i = 0; i < node.value.consequent.length; i++) {
                // console.log(node.value.consequent[i])  // SwitchCase节点
                // 检测语句是否有continue,即检测节点中是否有 ContinueStatement
                if (TNT.ContinueStatement.check(node.value.consequent[i])) {
                    continue
                }
                aaa = recast.print(node.value.consequent[i]).code   // 将ast组装成能执行的代码
                codes += aaa ;   // 这句貌似没啥用??
                // console.log(codes)   //打印每条语句
                // 等效于上面1行
                // printSource(node.value.consequent[i]);console.log('\n');  // 输出AST对象对应的源码
            }
            // printSource(stringLiteral(codes));console.log('\n');
            exp = expressionStatement(callExpression(memberExpression(id('console'), id('1og')), [stringLiteral(codes)]));
            // console.log(recast.print(exp).code)   //打印 console.1og(".......");
            node.value.consequent.unshift(exp);   // 向数组的开头添加一个或更多元素 consequent是一个数组,相当于在原代码中增加exp
            return false
        }
    });
    // console.log(recast.print(ast).code)
    printSource(ast)
});

命令行执行 node read demo.js

输出得到并命名为demo-2.js:

function test() {
    var _0x238e3e = {
        'puzUT': '4|3|2|1|0', 'Rgtsl': function (_0x3a9ad8, _0x312b1a) {
            return _0x3a9ad8 < _0x312b1a;
        }, 'HFHYB': function (_0x13ea88, _0x2533df) {
            return _0x13ea88 + _0x2533df;
        }
    };
    var _0x388255 = _0x238e3e['puzUT']['split']('|');
    var _0xfbda52 = 0x0;
    while (!![]) {
        switch (_0x388255[_0xfbda52++]) {
            case '0':
                console.log("return _0x2ce383;");
                return _0x2ce383;
            case '1':
                console.log(
                    "for (var _0x1fa16b = 0x0; _0x238e3e['Rgtsl'](_0x1fa16b, _0x3c5b0c['length']); _0x1fa16b++) {\r\n_0x2ce383['push'](String['fromCharCode'](_0x3c5b0c[_0x1fa16b]));}");
                for (var _0x1fa16b = 0x0; _0x238e3e['Rgtsl'](_0x1fa16b, _0x3c5b0c['length']); _0x1fa16b++) {
                    _0x2ce383['push'](String['fromCharCode'](_0x3c5b0c[_0x1fa16b]));
                }
                continue;
            case '2':
                console.log("var _0x2ce383 = [];");
                var _0x2ce383 = [];
                continue;
            case '3':
                console.log("_0x3c5b0c[0x0] = _0x238e3e['HFHYB'](_0x3c5b0c[0x0], 0x1);");
                _0x3c5b0c[0x0] = _0x238e3e['HFHYB'](_0x3c5b0c[0x0], 0x1);
                continue;
            case '4':
                console.log(
                    "var _0x3c5b0c = [0x67, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64];");
                var _0x3c5b0c = [0x67, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64];
                continue;
        }
        break;
    }
}

var s = test();
console['log'](s['join'](''));

运行demo-2.js得到:

var _0x3c5b0c = [0x67, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64];
_0x3c5b0c[0x0] = _0x238e3e['HFHYB'](_0x3c5b0c[0x0], 0x1);
var _0x2ce383 = [];
for (var _0x1fa16b = 0x0; _0x238e3e['Rgtsl'](_0x1fa16b, _0x3c5b0c['length']); _0x1fa16b++) {
    _0x2ce383['push'](String['fromCharCode'](_0x3c5b0c[_0x1fa16b]));
}
return _0x2ce383;

再将demo-2.js修改为:

function test() {
    var _0x238e3e = {
        'puzUT': '4|3|2|1|0', 'Rgtsl': function (_0x3a9ad8, _0x312b1a) {
            return _0x3a9ad8 < _0x312b1a;
        }, 'HFHYB': function (_0x13ea88, _0x2533df) {
            return _0x13ea88 + _0x2533df;
        }
    };
    var _0x3c5b0c = [0x67, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64];
    _0x3c5b0c[0x0] = _0x238e3e['HFHYB'](_0x3c5b0c[0x0], 0x1);
    var _0x2ce383 = [];
    for (var _0x1fa16b = 0x0; _0x238e3e['Rgtsl'](_0x1fa16b, _0x3c5b0c['length']); _0x1fa16b++) {
        _0x2ce383['push'](String['fromCharCode'](_0x3c5b0c[_0x1fa16b]));
    }
    return _0x2ce383;


}
var s = test();
console['log'](s['join'](''));

输出得到 hello world,相比混淆乱序的更方便调试。

 

法二:

直接在demo.js中修改代码,无需read.js。demo.js代码如下:

const sss = `function test() {
    var _0x238e3e = {
        'puzUT': '4|3|2|1|0', 'Rgtsl': function (_0x3a9ad8, _0x312b1a) {
            return _0x3a9ad8 < _0x312b1a;
        }, 'HFHYB': function (_0x13ea88, _0x2533df) {
            return _0x13ea88 + _0x2533df;
        }
    };
    var _0x388255 = _0x238e3e['puzUT']['split']('|');
    var _0xfbda52 = 0x0;
    while (!![]) {
        switch (_0x388255[_0xfbda52++]) {
            case'0':
                return _0x2ce383;
            case'1':
                for (var _0x1fa16b = 0x0; _0x238e3e['Rgtsl'](_0x1fa16b, _0x3c5b0c['length']); _0x1fa16b++) {
                    _0x2ce383['push'](String['fromCharCode'](_0x3c5b0c[_0x1fa16b]));
                }
                continue;
            case'2':
                var _0x2ce383 = [];
                continue;
            case'3':
                _0x3c5b0c[0x0] = _0x238e3e['HFHYB'](_0x3c5b0c[0x0], 0x1);
                continue;
            case'4':
                var _0x3c5b0c = [0x67, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64];
                continue;
        }
        break;
    }
}
var s = test();
console['log'](s['join'](''));`

const recast = require("recast");
// // 用 parse 解析成 ast树
const ast = recast.parse(sss)
const TNT = recast.types.namedTypes;
const {
    expressionStatement,
    memberExpression,
    identifier: id,
    callExpression,
    stringLiteral        //字符串
} = recast.types.builders;

recast.visit(ast, {
    visitSwitchCase(node) {
        var codes = "";
        // console.log(node.value.consequent.length)    // 打印case:下的有几条语句  1 2 2 2 2
        for (var i = 0; i < node.value.consequent.length; i++) {
            // console.log(node.value.consequent[i])  // SwitchCase节点
            // 检测语句是否有continue,即检测节点中是否有 ContinueStatement
            if (TNT.ContinueStatement.check(node.value.consequent[i])) {
                continue
            }
            aaa = recast.print(node.value.consequent[i]).code   // 将ast组装成能执行的代码
            codes += aaa;   // 这句貌似没啥用??
            // console.log(codes)   //打印每条语句
            // 等效于上面1行
            // printSource(node.value.consequent[i]);console.log('\n');  // 输出AST对象对应的源码
        }
        // printSource(stringLiteral(codes));console.log('\n');   //"......"
        exp = expressionStatement(callExpression(memberExpression(id('console'), id('1og')), [stringLiteral(codes)]));
        // console.log(recast.print(exp).code)   //打印 console.1og(".......");
        node.value.consequent.unshift(exp);   // 向数组的开头添加一个或更多元素,consequent是一个数组,相当于在原代码中增加exp语句
        return false
    }
});
console.log(recast.print(ast).code)

运行得到:也就是demo-2.js的内容,再运行,就得到有序的代码。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值