const fs = require('fs');
const types = require("@babel/types");
const parser = require("@babel/parser");
const {log} = require('console');
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;


//js混淆代码读取
process.argv.length > 2 ? encodeFile = process.argv[2] : encodeFile = "./main_ok.js";
process.argv.length > 3 ? decodeFile = process.argv[3] : decodeFile = "./decodeResult22.js";

//将源代码解析为AST
let sourceCode = fs.readFileSync(encodeFile, {encoding: "utf-8"});

let ast = parser.parse(sourceCode, {sourceType: 'script'});


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


function getRandomName(length) {

    let initArr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
    let puzzleArr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
    let ranInx = Math.floor(Math.random() * initArr.length);
    let randomName = initArr[ranInx];

    for (var i = 1; i < length; i++) {
        ranInx = Math.floor(Math.random() * puzzleArr.length);
        randomName += puzzleArr[ranInx];
    }

    return randomName;
}


let allNewNames = new Map();  //定义一个全局变量，保存需要处理的函数

function getUnusedIdentifier() {//获取未被使用的名称,返回 Identifier 类型。
    do {
        var newName = "$C_" + getRandomName(3);

    } while (allNewNames.has(newName))

    allNewNames.set(newName, 1);

    let UnusedIdentifier = types.Identifier(newName);

    return UnusedIdentifier;
}


const renameFunc =
    {
        FunctionDeclaration(path) {
            let {node, parentPath} = path;
            let {id, params, body} = node;
            let name = id.name;

            if (params.length != 2 ||
                !types.isVariableDeclaration(body.body[0]) || !types.isReturnStatement(body.body[1])) {
                return;
            }

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

            if (!binding || !binding.constant) {
                return;
            }
            let newNameId = getUnusedIdentifier();
            // console.log('rename',name,newNameId)
            // console.log(generator(path.parent).code)
            for (let referPath of binding.referencePaths) {
                referPath.replaceWith(newNameId);
            }

            let newName = newNameId.name;
            allNewNames.set(newName, name)
            path.node.id.name = newName;
        }
    }

// traverse(ast, renameFunc);

allNewNames.clear();





function getBinding(scope, name, func) {
    let binding = scope.getBinding(name);
    // console.log(`scope.get(${name}).length = ${binding.references}`)
    if (binding.references > 0) {
        for (const referencePath of binding.referencePaths) {
            func(referencePath)
        }
    }

}

var globalFuncs = {}

function restoreValue(inName, scope, funcName) {
    let binding3 = scope.getBinding(inName);
    if (!binding3) return;
    console.log(funcName, inName, binding3.references)
    for (const inReferencePath of binding3.referencePaths) {
        console.log(generator(inReferencePath.parent).code, inReferencePath.parent.type)
        if (types.isCallExpression(inReferencePath.parent)) {
            let {arguments} = inReferencePath.parent
            if (arguments.length !== 1) continue
            let res = globalFuncs[funcName](arguments[0].value)
            // console.log(funcName,inName,arguments[0].value,res)
            inReferencePath.parentPath.replaceWith(types.valueToNode(res))
        } else if (types.isVariableDeclarator(inReferencePath.parent)) {
            // 如果是继续引用 直接再找
            let inName2 = inReferencePath.parent.id.name;
            restoreValue(inName2, inReferencePath.parentPath.scope, funcName)

        }
    }
}

for (let i = 0; i < 1; i++) {
    traverse(ast, {
        FunctionDeclaration(path) {

            // 先找
            //  function ip() {
            //   var t = ["NjR0QllZTQ", "bind", "reload", "NjR0QltQTw", "NjR0QV1dTw", "pxInit", "uid", "pxvid", "length", "72630ywVgnM", "platform", "vid", "trigger", "NjR0QVxRSA", "random", "67349mopHGY", "NjR0Ql9cQg", "306744MLTLsI", "NjR0QV9fTw", "_pxvid", "getTime", "xhrFailure", "527700doOldY", "_pxRootUrl", "_pxmvid", "top", "NjR0QVNQQg", "xhrResponse", "NjR0QVhbSg", "NjR0QV5QSg", "getItem", "_pxVid", "removeItem", "one", "NjR0QllfTg", "_asyncInit", "NjQ", "3060155IbqKSZ", "subscribe", "21026255lNKhiL", "self", "588762aejAEn", "xhrSuccess"];
            //   return (ip = function () {
            //     return t;
            //   })();
            // }
            //  这样的函数 解密内容的
            let {id, body, params} = path.node;
            let bodys = body.body;
            if (bodys.length !== 2 || params.length !== 0) return;
            let varDeclar = bodys[0];
            if (!types.isVariableDeclaration(varDeclar)) return;
            if (varDeclar.declarations.length !== 1 || !types.isArrayExpression(varDeclar.declarations[0].init)) return;
            if (!types.isReturnStatement(bodys[1])) return;
            let name = id.name;
            let code = generator(path.node).code;

            // if (name !== 'nv') return;
            let binding = path.parentPath.scope.getBinding(name);
            if (!binding)return;
            console.log(`scope.get(${name}).length = ${binding.references}`)
            if (binding.references > 0) {
                let code1, code2, funcName;
                for (const referencePath of binding.referencePaths) {
                    // console.log(code2);
                    // console.log('111',referencePath.parentPath.parent.type)
                    if (types.isUnaryExpression(referencePath.parentPath.parent)) {
                        code1 = generator(referencePath.parentPath.parent).code;
                        // console.groupCollapsed(['改变顺序', code1])
                    } else if (types.isVariableDeclarator(referencePath.parentPath.parent)) {
                        code2 = generator(referencePath.parentPath.parentPath.parentPath.parentPath.parent).code;
                        funcName = referencePath.parentPath.parentPath.parentPath.parentPath.parent.id.name
                        // console.groupCollapsed(['使用', code2])
                    }
                }

                let resCode = code + ';;;\n' + code1 + ';;;\n' + code2;
                resCode += `
                globalFuncs['${funcName}'] = ${funcName} 
                `
                // console.groupCollapsed([funcName, resCode])

                if (!globalFuncs[funcName]) {
                    eval(resCode)

                }
                // console.log(eval(`${funcName}(195)`))
                let binding2 = path.parentPath.scope.getBinding(funcName);
                // 找到Tp 的引用地方

                for (const referencePath of binding2.referencePaths) {
                    console.log('referencePath', generator(referencePath.parent).code)

                    // if (types.isBlockStatement(referencePath.parentPath.parentPath.parent) ) {
                    if (!types.isUnaryExpression(referencePath.parentPath.parentPath.parentPath.parentPath.parentPath.parentPath.parent)) {
                        // 找到引用点  排除掉 修改变量顺序的引用
                        console.log(generator(referencePath.parent).code, referencePath.parent.type)
                        if (types.isCallExpression(referencePath.parent)) {
                            let {arguments} = referencePath.parent
                            if (arguments.length !== 1) continue
                            let res = globalFuncs[funcName](arguments[0].value)
                            // console.log(funcName,inName,arguments[0].value,res)
                            referencePath.parentPath.replaceWith(types.valueToNode(res))
                        } else {
                            if (!referencePath.parent.id) continue;
                            // 继续找引用
                            let inName = referencePath.parent.id.name;
                            let binding3 = referencePath.scope.getBinding(inName);
                            if (!binding3) continue;
                            console.log('find', funcName, inName, binding3.references)
                            for (const inReferencePath of binding3.referencePaths) {
                                console.log('code ', generator(inReferencePath.parent).code, inReferencePath.parent.type)
                                if (types.isCallExpression(inReferencePath.parent)) {
                                    let {arguments} = inReferencePath.parent
                                    if (arguments.length !== 1) continue
                                    let res = globalFuncs[funcName](arguments[0].value)
                                    console.log('444', funcName, inName, arguments[0].value, res)
                                    if (res === 'getTime') {

                                        let {code} = generator(ast, opts = {jsescOption: {"minimal": true}});
                                        fs.writeFile('./tmp.js', code, (err) => {
                                        });
                                    }
                                    console.log('before', res, generator(inReferencePath.parent).code)
                                    inReferencePath.parentPath.replaceWith(types.valueToNode(res))
                                    console.log('afterr', res, generator(inReferencePath.parent).code)
                                    inReferencePath.skip()
                                } else if (types.isVariableDeclarator(inReferencePath.parent)) {
                                    // 如果是继续引用 直接再找
                                    let inName2 = inReferencePath.parent.id.name;

                                    restoreValue(inName2, inReferencePath.parentPath.scope, funcName)
                                }
                            }

                        }


                    }

                }
            }


            // console.log(name, code);

        }
    })
}


// 字符串还原铭文
var o = {};
var a = function () {
    try {
        if (atob && "test" === atob("dGVzdA==")) {
            return atob;
        }
    } catch (t) {
    }

    function t(t) {
        this["message"] = t;
    }

    t["prototype"] = new Error();
    t["prototype"]["name"] = "InvalidCharacterError";
    return function (e) {
        var n = String(e)["replace"](/[=]+$/, "");
        if (n["length"] % 4 == 1) {
            throw new t("'atob' failed: The string to be decoded is not correctly encoded.");
        }
        for (var r, a, o = 0, i = 0, c = ""; a = n["charAt"](i++); ~a && (r = o % 4 ? 64 * r + a : a, o++ % 4) ? c += String["fromCharCode"](255 & r >> (-2 * o & 6)) : 0) {
            a = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="["indexOf"](a);
        }
        return c;
    };
}();

function i(n) {
    // if (n === 'AD9SCUkOXg')debugger;
    if (o[n]) {
        u = o[n];
    } else {
        for (var c = a(n), u = "", f = 0; f < c["length"]; ++f) {
            var s = "Pgc8p6l"["charCodeAt"](f % 7);
            u += String["fromCharCode"](s ^ c["charCodeAt"](f));
        }
        o[n] = u;
    }
    if (u === 'PX12573')debugger;
    return u;
}


// traverse(ast, {
//     FunctionDeclaration(path) {
//         let {id, body} = path.node;
//         if (id.name !== 'J') return;
//         let binding = path.parentPath.scope.getBinding(id.name)
//         console.log(id.name, binding.references)
//         for (const referencePath of binding.referencePaths) {
//             // console.log(generator(referencePath.parent).code, referencePath.parent.type)
//             if (types.isVariableDeclarator(referencePath.parent)) {
//                 let idName = referencePath.parent.id.name;
//                 getBinding(referencePath.scope, idName, function (p) {
//                     if (types.isCallExpression(p.parent)) {
//                         let arguments = p.parent.arguments;
//                         if (arguments.length !== 1) return
//                         try {
//                             let res = J(arguments[0].value);
//                             if (res === 'PX12571') debugger;
//                             p.parentPath.replaceWith(types.valueToNode((res)))
//                         } catch (e) {
//                             // console.error(generator(p.parent).code)
//
//                         }
//                     }
//
//                 })
//
//             } else if (types.isCallExpression(referencePath.parent)) {
//                 let arguments = referencePath.parent.arguments;
//                 if (arguments.length !== 1) continue
//                 try {
//                     let res = U(arguments[0].value);
//                     referencePath.parentPath.replaceWith(types.valueToNode((res)))
//                 } catch (e) {
//                     // console.error(generator(p.parent).code)
//
//                 }
//             }
//
//         }
//     }
// })

// U

function U(t) {
    return f(J) === "function" ? J(t) : function (t) {
      var e;
      var n;
      var r;
      var a;
      var o = [];
      var i = 0;
      var c = t["length"];
      try {
        if (/[^+/=0-9A-Za-z]/["test"](t) || /=/["test"](t) && (/=[^=]/["test"](t) || /={3}/["test"](t))) {
          return null;
        }
        for (c % 4 > 0 && (c = (t += v["Array"](4 - c % 4 + 1)["join"]("="))["length"]); i < c;) {
          n = [];
          for (a = i; i < a + 4;) {
            n["push"]("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="["indexOf"](t["charAt"](i++)));
          }
          r = [((e = (n[0] << 18) + (n[1] << 12) + ((63 & n[2]) << 6) + (63 & n[3])) & 255 << 16) >> 16, 64 === n[2] ? -1 : (65280 & e) >> 8, 64 === n[3] ? -1 : 255 & e];
          for (a = 0; a < 3; ++a) {
            (r[a] >= 0 || 0 === a) && o["push"](String["fromCharCode"](r[a]));
          }
        }
        return o["join"]("");
      } catch (t) {
        return null;
      }
    }(t);
  }

   var J = function () {
    try {
      return atob;
    } catch (t) {}
  }();
function f(t) {
    f = "function" == typeof Symbol && "symbol" == typeof Symbol["iterator"] ? function (t) {
      return typeof t;
    } : function (t) {
      return t && "function" == typeof Symbol && t["constructor"] === Symbol && t !== Symbol["prototype"] ? "symbol" : typeof t;
    };
    return f(t);
  }
traverse(ast, {
    FunctionDeclaration(path) {
        let {id, body} = path.node;
        if (id.name !== 'i') return;
        let binding = path.parentPath.scope.getBinding(id.name)
        console.log(id.name, binding.references)
        for (const referencePath of binding.referencePaths) {
            // console.log(generator(referencePath.parent).code, referencePath.parent.type)
            if (types.isVariableDeclarator(referencePath.parent)) {
                let idName = referencePath.parent.id.name;
                getBinding(referencePath.scope, idName, function (p) {
                    if (types.isCallExpression(p.parent)) {
                        let arguments = p.parent.arguments;
                        if (arguments.length !== 1) return
                        try {
                            let res = i(arguments[0].value);
                            if (res === 'PX12573') debugger;
                            p.parentPath.replaceWith(types.valueToNode((res)))
                        } catch (e) {
                            // console.error(generator(p.parent).code)

                        }
                    }

                })

            } else if (types.isCallExpression(referencePath.parent)) {
                let arguments = referencePath.parent.arguments;
                if (arguments.length !== 1) continue
                try {

                    let res = U(arguments[0].value);
                    referencePath.parentPath.replaceWith(types.valueToNode((res)))
                } catch (e) {
                    // console.error(generator(p.parent).code)

                }
            }

        }
    }
})
//end
console.timeEnd("处理完毕，耗时");



let {code} = generator(ast, opts = {jsescOption: {"minimal": true}});

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