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


//js混淆代码读取
process.argv.length > 2 ? encodeFile = process.argv[2] : encodeFile = "./main.js";  //默认的js文件
process.argv.length > 3 ? decodeFile = process.argv[3] : decodeFile = encodeFile.slice(0, encodeFile.length - 3) + "_ok.js";

//将源代码解析为AST
let sourceCode = fs.readFileSync(encodeFile, {encoding: "utf-8"});
let ast = parser.parse(sourceCode);
console.time("处理完毕，耗时");


const callToConditionalExpression =
    {
        CallExpression: {
            exit(path) {
                let {callee, arguments} = path.node;
                if (arguments.length != 1 || !types.isConditionalExpression(arguments[0])) {
                    return;
                }

                let {test, consequent, alternate} = arguments[0];

                let consequentCallNode = types.CallExpression(callee, [consequent]);
                let alternateCallNode = types.CallExpression(callee, [alternate]);

                let ConditionalNode = types.ConditionalExpression(test, consequentCallNode, alternateCallNode);

                path.replaceWith(ConditionalNode);
            }

        },
    }

traverse(ast, callToConditionalExpression);


let window = globalThis;
let decodeCode = "";
let funcName = "";

const getAtobSourceCode =
    {
        FunctionDeclaration(path) {
            let name = path.node.id.name;
            let sourceCode = path.toString();
            if (!sourceCode.includes("fromCharCode") || !sourceCode.includes("charCodeAt")) {
                return;
            }

            let allPrevSiblings = path.getAllPrevSiblings();
            if (allPrevSiblings.length < 1) {
                return;
            }

            if (!allPrevSiblings[0].isVariableDeclaration()) {
                return;
            }


            for (let prevSibling of allPrevSiblings.reverse())  //这里的reverse确保代码的执行流程一致。
            {
                decodeCode += prevSibling.toString();
            }

            decodeCode += sourceCode;
            eval(decodeCode);

            funcName = name;
            globalThis[funcName] = eval(funcName);


            if (true) {//使用块级作用域分离代码

                function calcCallExpression(name, path) {

                    let {scope, node} = path;

                    if (name != funcName) {
                        return;
                    }

                    let binding = undefined;
                    if (path.isVariableDeclarator()) {
                        binding = scope.getBinding(node.id.name);
                        if (!binding || binding.constantViolations.length > 1) {

                            return;
                        }

                        if (binding.constantViolations.length == 1 && binding.constantViolations[0] != path) {

                            return;
                        }
                    } else if (path.isAssignmentExpression() && path.get('left').isIdentifier()) {
                        binding = scope.getBinding(node.left.name);
                        if (!binding || binding.constantViolations.length != 1) {

                            return;
                        }
                    }

                    if (!binding) return;

                    for (let referPath of binding.referencePaths) {

                        let {parentPath, node} = referPath;

                        if (parentPath.isVariableDeclarator({"init": node}) || parentPath.isAssignmentExpression({"right": node})) {
                            calcCallExpression(name, parentPath);
                        }

                        if (!parentPath.isCallExpression({"callee": node})) {

                            continue;
                        }

                        let {arguments} = parentPath.node;

                        if (arguments.length != 1 || !types.isStringLiteral(arguments[0])) {

                            continue;
                        }
                        let value = globalThis[funcName](arguments[0].value);
                        console.log(parentPath.toString(), "--->", value);
                        parentPath.replaceWith(types.valueToNode(value));
                    }

                }

                let scope = path.parentPath.scope;
                let binding = scope.getBinding(name);

                for (let referPath of binding.referencePaths) {

                    let {parentPath, node} = referPath;

                    if (parentPath.isVariableDeclarator({"init": node}) || parentPath.isAssignmentExpression({"right": node})) {
                        calcCallExpression(name, parentPath);
                    }

                    if (!parentPath.isCallExpression({"callee": node})) {

                        continue;
                    }

                    let {arguments} = parentPath.node;

                    if (arguments.length != 1 || !types.isStringLiteral(arguments[0])) {

                        continue;
                    }
                    let value = globalThis[funcName](arguments[0].value);
                    console.log(parentPath.toString(), "--->", value);
                    parentPath.replaceWith(types.valueToNode(value));
                }

            }

            path.stop(); //遍历一次就停止，防止遍历到错误的函数

        },

    }


traverse(ast, getAtobSourceCode);

console.timeEnd("处理完毕，耗时");
let {code} = generator(ast, opts = {
    "compact": false,  // 是否压缩代码
    "comments": false,  // 是否保留注释
    "jsescOption": {"minimal": true},  //Unicode转义
});
ast = parser.parse(code);  //去除多余的 ()，可以将其屏蔽，看看效果。

console.time("处理完毕，耗时");

const ShowCallExpression = {
    CallExpression(path) {
        let {callee, arguments} = path.node;
        if (!types.isIdentifier(callee) || arguments.length != 2) {
            return;
        }
        console.log(path.toString());

    },
}

traverse(ast, ShowCallExpression);


function isExpressionConstant(PathOrNode) {

    let node = PathOrNode.node || PathOrNode;

    let BrowList = ['window', 'document', 'navigator', 'location', 'history', 'screen',];

    if (types.isLiteral(node) && node.value != null) {
        return true;
    }

    if (types.isIdentifier(node) && BrowList.includes(node.name)) {
        return true;
    }

    if (types.isIdentifier(node) && typeof globalThis[node.name] != "undefined") {
        return true;
    }

    if (types.isMemberExpression(node)) {
        let {object, property} = node;

        if (types.isIdentifier(object) && typeof globalThis[object.name] != "undefined") {
            let properName = types.isIdentifier(property) ? property.name : property.value;
            if (typeof globalThis[object.name][properName] != "undefined") {
                return true;
            }
        }

        if (types.isMemberExpression(object)) {
            return isExpressionConstant(object);
        }

    }

    if (types.isUnaryExpression(node) && ["+", "-", "!", "typeof", "~"].includes(node.operator)) {
        return isExpressionConstant(node.argument);
    }

    return false;
}

const restoreVarDeclarator =
    {
        VariableDeclarator(path) {
            let scope = path.scope;
            let {id, init} = path.node;

            if (!types.isIdentifier(id) || init == null || !isExpressionConstant(init)) {
                return;
            }

            const binding = scope.getBinding(id.name);

            if (!binding) return;

            let {constant, referencePaths, constantViolations} = binding;

            if (constantViolations.length > 1) {
                return;
            }

            if (constant || constantViolations[0] == path) {
                for (let referPath of referencePaths) {
                    referPath.replaceWith(init);
                }
            }
        },
    }

traverse(ast, restoreVarDeclarator);


const restoreAssignConstant =
    {//常量还原插件
        AssignmentExpression(path) {
            let {scope, node, parentPath} = path;

            let {left, operator, right} = node;

            if (!types.isIdentifier(left) || operator != "=" || !isExpressionConstant(right)) {
                return;
            }

            let binding = scope.getBinding(left.name);

            if (!binding || binding.constantViolations.length > 1) {//如果没有binding,或者赋值语句本身改变了它，因此这里判断只有一处改变。
                return;
            }
            if (binding.constantViolations.length !== 1) return
            if (!binding.constantViolations[0].node) return
            let {start} = binding.constantViolations[0].node;

            let referStart = start;

            for (let referPath of binding.referencePaths) {
                if (referPath.node.start < referStart) {
                    referStart = referPath.node.start;
                }
            }

            if (start > referStart) {//防止在更改前被引用
                return;
            }

            for (let referPath of binding.referencePaths) {
                referPath.replaceWith(right);
            }

            if (parentPath.isExpressionStatement() || parentPath.isSequenceExpression()) {
                path.remove();
            }
        },

    }

traverse(ast, restoreAssignConstant);

ast = parser.parse(generator(ast).code);  //去除多余的 ()，可以将其屏蔽，看看效果。

const constantFold = {
    "BinaryExpression|UnaryExpression|MemberExpression|CallExpression"(path) {
        if (path.isUnaryExpression({operator: "-"}) ||
            path.isUnaryExpression({operator: "void"})) {
            return;
        }
        const {confident, value} = path.evaluate();

        if (!confident)
            return;

        if (typeof value == 'number' && (!Number.isFinite(value))) {
            return;
        }

        path.replaceWith(types.valueToNode(value));
    },
}

traverse(ast, constantFold);


console.timeEnd("处理完毕，耗时");
code = generator(ast, opts = {
    "compact": false,  // 是否压缩代码
    "comments": false,  // 是否保留注释
    "jsescOption": {"minimal": true},  //Unicode转义
}).code


ast = parser.parse(code);  //去除多余的 ()，可以将其屏蔽，看看效果。

traverse(ast, {
    VariableDeclaration(path) {
        let {node, scope} = path;
        if (node.declarations.length == 0) {
            path.remove();
            return;
        }

        let declarations = node.declarations;
        let res = [];
        if (declarations.length === 1) return;
        for (let i = 0; i < declarations.length; i++) {
            let declaration = declarations[i];
            res.push(types.variableDeclaration('var', [
                declaration
            ]))
        }
        // let binding = scope.getBinding(node.name);
        console.log(generator(node).code)
        path.replaceWithMultiple(res);
        // path.skip()

    }
})

traverse(ast, {
    VariableDeclaration(path) {
        let {node, scope} = path;
        let declarations = node.declarations;
        if (declarations.length !== 1) return;
        let obj = declarations[0].init;
        if (!types.isObjectExpression(obj)) return;
        let name = declarations[0].id.name;
        // if (name !== 'e')return
        let properties = obj.properties;
        let binding = scope.getBinding(name);
        if(!binding)return;
        let referencePaths = binding.referencePaths;
        for (let referPath of referencePaths) {
            let {parentPath} = referPath;
            if (!parentPath.isMemberExpression()) continue;
            let {object, property} = parentPath.node;
            if (!types.isIdentifier(object) || !types.isIdentifier(property)) continue;
            let key = property.name;
            let propertyNode = properties.find(p => p.key.name === key);
            console.log(generator(referPath.parentPath.node).code)
            if(generator(referPath.parentPath.node).code === 't.A')debugger
            if (!propertyNode) continue;
            if (!types.isNumericLiteral(propertyNode.value))continue;
            let value = propertyNode.value;
            parentPath.replaceWith(value);
        }

    }
})
code = generator(ast, opts = {
    "compact": false,  // 是否压缩代码
    "comments": true,  // 是否保留注释
    "jsescOption": {"minimal": true},  //Unicode转义
}).code


fs.writeFile(decodeFile, code, (err) => {
});
