吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3542|回复: 20
收起左侧

[Web逆向] node与python同时使用ast还原2021春节番外篇的JSFuck

[复制链接]
漁滒 发表于 2021-12-29 23:21

@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 = 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 == 'false':
            node.value = 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 === false){
                    path.replaceWith(types.booleanLiteral(true))
                }else if(path.node.operator === '!' && 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 === true){
                    path.replaceWith(types.numericLiteral(1))
                }else if(path.node.operator === '+' && 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 === '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 === '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 === '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);

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

在这里插入图片描述

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

免费评分

参与人数 8吾爱币 +16 热心值 +8 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
蚯蚓翔龙 + 1 + 1 用心讨论,共获提升!
风辰熙 + 2 + 1 用心讨论,共获提升!
独行风云 + 1 + 1 用心讨论,共获提升!
Virgo178 + 1 + 1 用心讨论,共获提升!
涛之雨 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
为之奈何? + 1 + 1 我很赞同!
苏紫方璇 + 2 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

包子学渗透 发表于 2021-12-29 23:24
渔佬妙啊 发麻中
平淡最真 发表于 2021-12-30 00:09
Dearc 发表于 2021-12-30 01:14
lxd2000317 发表于 2021-12-30 09:01
学到了很厉害
KNOPSTEAM 发表于 2021-12-30 10:13
谢谢分享
wBo1 发表于 2021-12-30 10:23
学到了学到了,大佬太厉害了
xiong779 发表于 2021-12-30 10:32
学到了学到了,大佬太厉害了
liushuaijie123 发表于 2021-12-30 10:42
那么多现成的工具不用
huyifan311 发表于 2021-12-30 11:01
辛苦楼主的分享了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-3-29 18:33

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表