js逆向-ast混淆还原入门基础练一练

基础练一练,不喜勿喷。

jscode对应visitor,jscode1对应visitor1,以此类推。

 

/*
* 安装 npm install @babel/core
* */

// 将JS源码转换成语法树
const parser = require("@babel/parser");
// 为parser提供模板引擎
const template = require("@babel/template").default;
// 遍历AST
const traverse = require("@babel/traverse").default;
// 操作节点,比如判断节点类型,生成新的节点等
const t = require("@babel/types");
// 将语法树转换为源代码
const generator = require("@babel/generator").default;
// 操作文件
const fs = require("fs");

//https://astexplorer.net/
/*
type: 表示当前路径的类型;
start:表示当前节点的起始位置;
end: 表示当前节点的结束位置;
loc: 表示当前节点所在源代码中的起始和结束的行号和列号。
declarations :声明
kind:表示定义的类型


*/


const visitor = {
    VariableDeclarator(path) {
        // 当前路径所对应的源代码,使用toString方法
        console.log(path.toString());// a = 123   b = 'ccc'
        //获取path的上一级路径,使用parentPath
        let parent = path.parentPath;
        console.log(parent.node.kind) //var let
        //获取path的子路径,使用 get方法,得到id节点
        let id = path.get('id');
        console.log(id.node.name); //a b
        //获取当前路径的节点
        const node = path.node;
        console.log(node.init.value);//123 ccc
        //得到init节点
        let init = path.get('init');
        //判断node下的init节点类型
        if (t.isNumericLiteral(node.init)) {
            id.replaceWith(t.Identifier('A'));
            console.log(id.node.name); //A
            init.replaceWith(t.NumericLiteral(4));
            console.log(node.init.value);//4
        }
        //直接拿init节点来判断类型
        if (init.isStringLiteral()) {
            id.replaceWith(t.Identifier('B'));
            console.log(id.node.name); //B
            init.replaceWith({type: "StringLiteral", value: 'qqq'});
            console.log(init.node.value);//qqq
            // 删除init节点
            // init.remove()
            // delete path.node.init;
        }

    }
};
//替换path,单路径可以使用replaceWith方法,多路径则使用replaceWithMultiple方法
//init.replaceWith 和 path.replaceWith区别:前者只是替换当前遍历节点里的init节点,后者是把当前遍历节点整个替换

const visitor1 = {
    CallExpression(path) {
        console.log(path.toString());           //aaa(1, 2)

        const node = path.node;
        // 获取CallExpression节点结构
        // console.log(JSON.stringify(node,null,'\t'));
        console.log(node.callee.name);          //aaa
        console.log(node.arguments[0].value);   //1
        console.log(node.arguments[1].value);   //2

        let callee = path.get('callee');
        // console.log(callee.toString());         //aaa
        console.log(callee.node.name)           //aaa
    }
};

const visitor2 = {
    'StringLiteral|NumericLiteral'(path) {
        // let value = path.node.value
        // //此行效果等于上行
        // // let {value} = path.node
        // console.log(value)
        //之所以会以十六进制显示,完全是 raw这个节点造成的,那就删除
        delete path.node.extra
    }
};
const visitor3 = {
    MemberExpression(path) {
        // const node = path.node;
        // let property = path.get('property')
        // if (t.isStringLiteral(node.property)) {
        //     let value = node.property.value;
        //     console.log(value)
        //     //原为true,改后的效果把[]变为.
        //     node.computed = false
        //     //如果写成path.replaceWith是将整个MemberExpression节点换为value,节点类型也变为Identifier,例:window.btoa变为btoa
        //     //我们仅需要替换property节点
        //     property.replaceWith(t.Identifier(value))
        //     }
        //效果同上
        let property = path.get('property')
        if (property.isStringLiteral()) {
            let value = property.node.value;
            console.log(value)
            path.node.computed = false
            property.replaceWith(t.Identifier(value))
        }
    }
};
const visitor4 = {
    VariableDeclaration(path) {
        const operator = '='; //赋值语句的运算符是 =
        const left = t.Identifier('b'); //新建赋值语句左边的节点;
        const right = t.NumericLiteral(456);//新建赋值语句右边的节点;

        const new_assign = t.AssignmentExpression(operator, left, right) //构造一个赋值语句节点
        const new_express = t.ExpressionStatement(new_assign); //再构造成表达式语句
        path.insertBefore(new_express);//在变量声明语句前面插入
        const new_vdtor = t.VariableDeclarator(left, right)
        const new_vdtion = t.VariableDeclaration('let', [new_vdtor])
        path.insertAfter(new_vdtion); //在变量声明语句后面插入

        var str = path.toString()
        const new_memberexp = t.MemberExpression(t.Identifier('console'), t.Identifier('log'),)
        const new_callexp = t.CallExpression(new_memberexp, [t.StringLiteral(str)])
        path.insertAfter(new_callexp); //在变量声明语句后面插入
        path.stop()   //不停止会无限循环下去
    }


}
const visitor5 = {
    SwitchCase(path) {
        const node = path.node;
        let consequent = path.get('consequent');//数组类型
        //长度
        // console.log(consequent.length)
        for (var i = 0; i < consequent.length; i++) {
            if (t.isContinueStatement(consequent[i])) continue
            const conqt = consequent[i].toString();
            const new_memberexp = t.MemberExpression(t.Identifier('console'), t.Identifier('log'),);
            const new_callexp = t.CallExpression(new_memberexp, [t.StringLiteral(conqt)]);
            consequent[i].insertBefore(new_callexp); //在变量声明语句后面插入
            // 可删除原来的节点
            // t.isReturnStatement(consequent[i]) ?  consequent[i].node.argument = null : consequent[i].remove()
            consequent[i].stop();  //不停止会无限循环下去
        }
        //运行得到的代码得到混淆前的代码
    }

}
const visitor6 = {
    NumericLiteral(path) {
        // const node = path.node;
        // const value = node.value
        //等效上2行
        const value = path.toString()
        const first = 0 - Math.floor(Math.random() * 10000000 + 10000000);
        const second = value ^ first;

        const binexp = t.BinaryExpression('^', t.NumericLiteral(first), t.NumericLiteral(second))
        path.replaceWith(binexp)
        path.stop()
    }
}

const visitor7 = {
    //默认是enter,先遍历顶层,修改为exit,则是先遍历底层,看visitor7_1
    'BinaryExpression|UnaryExpression|ConditionalExpression'(path) {
        // const value = path.evaluate().value;
        const {value} = path.evaluate();
        // console.log(path.toString(),'   ',value)
        switch (typeof value) {
            case 'boolean':
                path.replaceWith(t.BooleanLiteral(value))
                break;
            case 'string':
                path.replaceWith(t.StringLiteral(value))
                break;
            case 'number':
                path.replaceWith(t.NumericLiteral(value))
                break;
            default:
                break;
        }

    }

}
// visitor7另一种写法
const visitor7_1 = function (path) {
    // const value = path.evaluate().value;
    const {value} = path.evaluate();
    console.log(path.toString(), '   ', value)
    // switch (typeof value) {
    //     case 'boolean':
    //         path.replaceWith(t.BooleanLiteral(value))
    //         break;
    //     case 'string':
    //         path.replaceWith(t.StringLiteral(value))
    //         break;
    //     case 'number':
    //         path.replaceWith(t.NumericLiteral(value))
    //         break;
    //     default:
    //         break;
    // }
}
const visitor8 = {
    VariableDeclarator(path) {
        //此法为替换原init,
        console.log(path.toString())
        const init = path.get('init')           //a = 123 | 456
        const left = init.node.left.value       //123
        const right = init.node.right.value     //456
        //重新构造一个函数
        const p_left = t.Identifier('s'), p_right = t.Identifier('h')  //设定2个形参
        const params = [p_left, p_right]                  //数组类型
        const retsta = [t.ReturnStatement(t.BinaryExpression('|', p_left, p_right))]//数组类型
        const body = t.BlockStatement(retsta,)          //第二参数默认[]
        const callee = t.functionExpression(null, params, body,)
        const arguments = [t.NumericLiteral(left), t.NumericLiteral(right)]      //数组类型
        const new_callexp = t.callExpression(callee, arguments)
        init.replaceWith(new_callexp)

        // 还可以直接在原init上修改
        // const init = path.get('init')           //a = 123 | 456
        // const left = init.node.left.value       //123
        // const right = init.node.right.value     //456
        //
        // const p_left = t.Identifier('s'), p_right = t.Identifier('h')  //设定2个形参
        // const params = [p_left, p_right]                  //数组类型
        // const retsta = [t.ReturnStatement(t.BinaryExpression('|', p_left, p_right))]//数组类型
        // const body = t.BlockStatement(retsta,)
        // init.node.type = "CallExpression";
        // init.node.arguments = [t.NumericLiteral(left), t.NumericLiteral(right)];
        // init.node.callee = t.functionExpression(null, params, body,)

    }
}
const visitor9 = {
    StringLiteral(path) {
        delete path.node.extra;
        // 发现英文的Unicode形式被还原了,但是中文的没有被还原
        // 需要给generator(ast,)添加参数{jsescOption:{"minimal":true}}
    }

}
const visitor10 = {
    VariableDeclarator(path) {
        const init = path.get('init');
        if (init.isFunctionExpression()) {
            const {init, id} = path.node;
            const name = id.name;

            const params = init.params;
            if (params.length !== 2) return;
            let first_arg = params[0].name;
            let second_arg = params[1].name;

            const body = init.body;
            if (!body.body || body.body.length !== 1) return;

            let return_body = body.body[0];
            let argument = return_body.argument;
            if (!t.isReturnStatement(return_body) ||
                !t.isBinaryExpression(argument)) return;
            let {left, right, operator} = argument;
            if (!t.isIdentifier(left, {name: first_arg}) ||
                !t.isIdentifier(right, {name: second_arg})) return;

            let scope = path.scope;
            console.log(scope)
            traverse(scope.block, {
                CallExpression: function(_path){
                    let _node = _path.node;
                    let args = _node.arguments;
                    if (args.length === 2 && t.isIdentifier(_node.callee, {name: name}))
                        _path.replaceWith(t.BinaryExpression(operator, args[0], args[1]))
                },
            });
        }
    }
};

const visitor11 = {}
const visitor12 = {}

var jscode = `var a = 123;let b='ccc';`
var jscode1 = `function aaa(a,b){}aaa(1,2);`
var jscode2 = `h[$b('\x30\x78\x37\x31', '\x31\x21\x58\x47') + '\x4a\x61'] = function (Q, R) {return Q + R;}
             var num = 0x30+0x31;
             var $a = ['\x77\x71\x78\x70\x55\x41\x3d\x3d']
             `
var jscode3 = `
            if (!j) {
              g['setCookie'](['*'], 'counter', 0x1);
            } else if (j) {
              i = g['getCookie'](null, 'counter');
            } else {
              g['removeCookie']();
            }
            (function (_0x1a61bd) {})()
            ['constructor']("while (true) {}")["apply"]("counter");
            `
var jscode4 = `var a = 123;`
var jscode5 = `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();`
var jscode6 = `var value = 12345678;`
var jscode7 = `var a=!![],
               b='hello '+'world '+'!',
               c=2+3*50,
               d=Math.abs(-200)%19,
               e=true ? 123:456;`
var jscode8 = `var a = 123 | 456;`
var jscode9 = `var test ='\u4f60\u597d\u4e16\u754c\u0054\u0068\u0069\u0073\u0020\u0069\u0073\u0020\u0061\u0020\u0074\u0065\u0073\u0074\u0021'`
var jscode10 = `var Xor = function (p,q){
                  return p ^ q;
                }
                let a = Xor(111,222);`
var jscode11 = ``
var jscode12 = ``


let ast = parser.parse(jscode10);  //修改此处
// console.log(JSON.stringify(ast,null,'\t'))
// traverse(ast,visitor)        //var a = 123;let b='ccc';
// traverse(ast,visitor1)       //aaa(1,2);
// traverse(ast, visitor2)          //将\x30\x78\x37\x31转变为可识别字符串,十六进制数值还原成十进制
// traverse(ast, visitor3)          //将a["length"]转变为a.length
// traverse(ast, visitor4)          //插入节点
// traverse(ast, visitor5)             //SwitchCase插入节点,平坦化还原
// traverse(ast, visitor6)             //把12345678拆成两个数的异或操作
// traverse(ast, visitor7)            //合并Literal类型的计算表达式
// traverse(ast, {                     //visitor7 的另一种写法
//     'BinaryExpression|UnaryExpression|ConditionalExpression': {
//         enter: [visitor7_1]
//         //exit: [visitor7_1]
//     }
// })
// traverse(ast, visitor8)   //将 BinaryExpression 类型转换为 CallExpression 类型
// traverse(ast, visitor9)  //将unicode码转为可识别的字符
traverse(ast, visitor10)    //CallExpression  -> BinaryExpression
// traverse(ast, visitor11)
// traverse(ast, visitor12)

let {code} = generator(ast,); //第9需要添加参数{jsescOption:{"minimal":true}}
console.log(code)
// fs.writeFile('xxxx.js', code, (err)=>{});

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值