欢迎光临
我们一直在努力

node与python同时使用ast还原2021春节番外篇的JSFuck

@TOC

题目地址:【2021春节】解题领红包活动开始喽,解出就送论坛币!

出js后,全部都是形如下面的符号,代码完全没有可读性

([]+[])[([][[]]+[])[+!![]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!![]+!![]]+(![]+[])[+!!

尝试直接使用node中的babel库进行还原

在这里插入图片描述

这里提示超过了最大调用栈了,那么这种情况先想办法简化一点点代码,再使用babel库,可能就不会出现这个报错,那么python中比较方便修改最大的调用栈,那么就用python进行初步还原

在python中要分析js语法就要用到【slimit】这个库,之前曾经有另外一篇文章介绍过
利用AST对抗某网站的javascript抽取型混淆

python部分的代码如下


from slimit.parser import Parser
from slimit.visitors.nodevisitor import ASTVisitor
from slimit import ast
import sys
sys.setrecursionlimit(100000)

class UnaryOp_Visitor1(ASTVisitor):
    # 自定义访问者,重写节点访问逻辑
    def visit_UnaryOp(self, node):
        child = node.children()[0]
        if isinstance(child, ast.UnaryOp):
            self.visit(child)
        else:
            for key in [i for i in filter(lambda n: not n.startswith("__") and not n == 'to_ecma' and not n == 'children', child.__dir__())]:
                if type(getattr(child, key)) != str and type(getattr(child, key)) != bool:
                    self.visit(getattr(child, key))

        if node.op == '!' and isinstance(node.children()[0], ast.UnaryOp) and node.children()[0].op == '!' and isinstance(node.children()[0].children()[0], ast.Array):
            node.value = https://www.52pojie.cn/ast.Boolean('false')
        elif node.op == '+' and isinstance(node.children()[0], ast.UnaryOp) and node.children()[0].op == '!' and node.children()[0].children()[0].value =https://www.52pojie.cn/='false':
            node.value = https://www.52pojie.cn/ast.Boolean('true')

def main():
    with open('2021春节.js', 'r', encoding='utf-8') as f:
        script = f.read()
    tree: ast.Program = Parser().parse(script)
    UnaryOp_Visitor1().visit(tree)
    script = tree.to_ecma()
    print(script)
    with open('decrypt.js', 'w', encoding='utf-8') as f:
        f.write(script)

if __name__ == '__main__':
    main()

上面的主要作用是
把  !![]             还原为     !false
把  +!flase      还原为     +true

此时得到的代码形如

[+true] + (!false + [][(![] + [])[+[]] + (![] + [])[!false + !false]

然后就给node中babel库继续还原,主要是对一元表达式,二元表达式,成员表达式进行还原。其中需要注意的是代码中用到了atob和btoa这两个函数,所以需要先补了这两个函数。

js代码如下

const parser = require("@babel/parser");
const template = require("@babel/template").default;
const traverse = require("@babel/traverse").default;
const types = require("@babel/types");
const generator = require("@babel/generator");
const fs = require("fs");

function wtofile(path, flags, code) {
    var fd = fs.openSync(path,flags);
    fs.writeSync(fd, code);
    fs.closeSync(fd);
}

function dtofile(path) {
    fs.unlinkSync(path);
}

var file_path = '2021春节.js';
var jscode = fs.readFileSync(file_path, {
    encoding: "utf-8"
});

let ast = parser.parse(jscode);

global.Buffer = global.Buffer || require('buffer').Buffer;

if (typeof btoa === 'undefined') {
    global.btoa = function (str) {
        return new Buffer(str+"").toString('base64');
    };
}

if (typeof atob === 'undefined') {
    global.atob = function (b64Encoded) {
        return new Buffer(b64Encoded, 'base64').toString();
    };
}

traverse(ast, {
    UnaryExpression: {
        exit: [
            function (path) {
                if(path.node.operator === '!' && path.node.argument.type === 'BooleanLiteral' && path.node.argument.value =https://www.52pojie.cn/== false){'!' && path.node.argument.type === 'ArrayExpression' && path.node.argument.elements.length === 0){
                    path.replaceWith(types.booleanLiteral(false))
                }else if(path.node.operator === '+' && path.node.argument.type === 'BooleanLiteral' && path.node.argument.value =https://www.52pojie.cn/== true){'+' && path.node.argument.type === 'ArrayExpression' && path.node.argument.elements.length === 0){
                    path.replaceWith(types.numericLiteral(0))
                }else if(path.node.operator === '+' && path.node.argument.type === 'ArrayExpression' && path.node.argument.elements.length === 1 && path.node.argument.elements[0].type === 'BooleanLiteral'){
                    path.replaceWith(types.identifier('NaN'))
                }else{
                    path.replaceWith(types.numericLiteral(eval(path.toString())))
                }
            }
        ]
    },
    BinaryExpression: {
        exit: [
            function (path) {
                if(path.node.operator === '+' && path.node.left.type === 'BooleanLiteral'){
                    if(path.node.right.type === 'BooleanLiteral'){
                        path.replaceWith(types.numericLiteral(eval(path.toString())))
                    }else if(path.node.right.type === 'ArrayExpression'){
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }else if(path.node.right.type === 'MemberExpression' && path.node.right.object.type === 'ArrayExpression' && path.node.right.property.type === 'StringLiteral' && path.node.right.property.value =https://www.52pojie.cn/=='flat'){
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }
                }else if(path.node.operator === '+' && path.node.left.type === 'NumericLiteral'){
                    if(path.node.right.type === 'BooleanLiteral'){
                        path.replaceWith(types.numericLiteral(eval(path.toString())))
                    }else if(path.node.right.type === 'ArrayExpression'){
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }else if(path.node.right.type === 'StringLiteral'){
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }else{
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }
                }else if(path.node.operator === '+' && path.node.left.type === 'StringLiteral' && path.node.right.type === 'StringLiteral'){
                    path.replaceWith(types.stringLiteral(eval(path.toString())))
                }else if(path.node.operator === '+' && path.node.left.type === 'StringLiteral' && path.node.right.type === 'NumericLiteral'){
                    path.replaceWith(types.stringLiteral(eval(path.toString())))
                }else if(path.node.operator === '+' && path.node.left.type === 'StringLiteral' && path.node.right.type === 'BooleanLiteral'){
                    path.replaceWith(types.stringLiteral(eval(path.toString())))
                }else if(path.node.operator === '+' && path.node.left.type === 'Identifier'){
                    path.replaceWith(types.stringLiteral(eval(path.toString())))
                }else if(path.node.operator === '+' && path.node.left.type === 'ArrayExpression'){
                    if(path.node.right.type === 'ArrayExpression'){
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }else if(path.node.left.elements.length === 1){
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }else if(path.node.right.type === 'MemberExpression' && path.node.right.object.type === 'ArrayExpression' && path.node.right.property.type === 'StringLiteral' && path.node.right.property.value =https://www.52pojie.cn/=='flat'){
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }else{
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }
                }else if(path.node.operator === '+' && path.node.right.type === 'ArrayExpression'){
                    if(path.node.left.type === 'CallExpression' && path.node.left.callee.type === 'MemberExpression'){
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }
                }else if(path.node.operator === '+' && path.node.left.type === 'StringLiteral' && path.node.right.type === 'Identifier'){
                    path.replaceWith(types.stringLiteral(eval(path.toString())))
                }else if(path.node.operator === '+' && path.node.left.type === 'StringLiteral' && path.node.right.type === 'MemberExpression'){
                    path.replaceWith(types.stringLiteral(eval(path.toString())))
                }else{
                    path.replaceWith(types.stringLiteral(eval(path.toString())))
                }
            }
        ]
    },
    MemberExpression: {
        exit: [
            function (path) {
                if(path.node.object.type === 'ArrayExpression' && path.node.property.type === 'ArrayExpression'){
                    path.replaceWith(types.identifier('undefined'))
                }else if(path.node.object.type === 'StringLiteral' && path.node.property.type === 'NumericLiteral'){
                    path.replaceWith(types.stringLiteral(eval(path.toString())))
                }else if(path.node.object.type === 'StringLiteral' && path.node.property.type === 'StringLiteral'){
                    if(!isNaN(parseInt(path.node.property.value))){
                        path.replaceWith(types.stringLiteral(eval(path.toString())))
                    }
                }
            }
        ]
    },
    CallExpression: {
        exit: [
            function (path) {
                if(path.node.callee.type === 'CallExpression' && path.node.callee.arguments.length === 1 && path.node.callee.arguments[0].type === 'StringLiteral' && path.node.callee.arguments[0].value =https://www.52pojie.cn/=='return statusbar'){
                    path.parentPath.replaceWith(types.stringLiteral("[object BarProp]"))
                }else if(path.node.callee.type === 'MemberExpression' && path.node.callee.object.type === 'NumericLiteral'){
                    path.replaceWith(types.stringLiteral(eval(path.toString())))
                }
            }
        ]
    }
});

let code = generator.default(ast, {
    compact: false
}).code;
wtofile('decrypt.js', 'w', code);

还原后可以得到最终容易阅读的代码

在这里插入图片描述

接下来就可以做进一步需要的分析了。

赞(0) 打赏
未经允许不得转载:哈哈网 » node与python同时使用ast还原2021春节番外篇的JSFuck

评论 抢沙发

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏