不知道为什么要还原 ti,也不知道还原了 ti 有啥用。

aHR0cHM6Ly93d3cudGkuY29tLmNuL3JBUy0yL29Kai92cVItL2YzZ0EvdVJYbC9oWU41d1Z3NC9JQjRWUDBzRUJ3L0FGL1VkT1NrdEd5OEI=

# 源码分析

将代码放至 AST explorer 中解析。

根据解析的结果可以发现几个明显的特征:

// 于函数体内部的赋值表达式生成、非常规运算。
tv();
function tv(){
	SQ = +!+[]
};
// 类 OB 的运算混淆
var xB = function (qB, BB) {
    return qB >> BB;
}
// 隐藏 case 节点值的控制流平坦化及包含 BlockStatement 的 case 节点保护。
case sq:{
    
};
// 控制流调用交叉
function add(val1){
  while(val1>0){
    switch (val1) {
      case 1:
        console.log('this is 1 step');
        add(2);
        val1 = -1;
        break;
      case 2:
        return console.log('inner function end');
      case 3:
        val1 = 1;
        console.log('this is 3 step');
        break;
    }
  }
};
add(3);
// 对象还原
ff.gF(":\v+U4", g5)
// 函数调用
j5.call(this, p0);

在对其代码有大致的了解后,可以将能还原的先还原。

# 代码还原

# 函数体还原
function Xv() {
        xP = ['gc'];
};
---------------------------
// 还原后
xp = ['gc'];    
---------------------------
// 还原插件
const FuncReduce = {
    FunctionDeclaration(path){
        const {id,params,body} = path.node;
        if(params.length !== 0 || ReturnIdentify(body.body) || body.body.length !==1 ) return;
        let binding = path.scope.getBinding(id.name);
        if(!binding || !binding.referenced) return;;
        let references = binding.referencePaths;
        for(let reference of references){
            if(types.isCallExpression(reference.parent)){              if(!types.isExpressionStatement(reference.parentPath.parentPath)) return;
                reference.parentPath.parentPath.replaceInline(body.body);
            }
        };
        path.remove();
    }
};

插件撰写思路:

  1. 遍历函数声明式,只针对无形参,无返回值的函数声明式。
  2. 根据其函数名,找到其调用位置,需保证调用位置的结构为 "x ();" 的形式。
  3. 节点替换。
# 非常规运算
SQ = +!+[], jQ = +!+[] + !+[] + !+[], OQ = [+!+[]] + [+[]] - +!+[], KQ = +[], JQ = [+!+[]] + [+[]] - +!+[] - +!+[], CQ = +!+[] + !+[] + !+[] + !+[] + !+[] + !+[] + !+[], RQ = +!+[] + !+[] + !+[] + !+[] + !+[] + !+[], DQ = +!+[] + !+[] + !+[] + !+[] + !+[], ZQ = !+[] + !+[], WQ = [+!+[]] + [+[]] - [], dQ = !+[] + !+[] + !+[] + !+[];
--------------------------------------
// 还原后:
SQ = 1, jQ = 3, OQ = 9, KQ = 0, JQ = 8, CQ = 7, RQ = 6, DQ = 5, ZQ = 2, WQ = 10, dQ = 4;
--------------------------------------
// 插件:
// 能计算的计算,不能计算的回填。
const UnaryCaculator = {
    BinaryExpression(path){
        const {value,confident} = path.evaluate();
        if(!value || !confident) return;
        path.replaceWith(types.valueToNode(value));
    },
       UnaryExpression(path){
        const {operator} = path.node;
        if(operator !== '+') return;
        const {value,confident} = path.evaluate();
        if(!value && !confident) return;
        path.replaceWith(types.valueToNode(value));
    }
};
# 全局变量回填
SQ = 1, jQ = 3, OQ = 9, KQ = 0, JQ = 8, CQ = 7, RQ = 6, DQ = 5, ZQ = 2, WQ = 10, dQ = 4;
bq = jQ + ZQ * WQ + RQ * WQ * WQ + WQ * WQ * WQ,
-------------------------------
// 还原后
bq = 3 + 2 * 10 + 6 * 10 * 10 + 10 * 10 * 10
-------------------------------
const AssignReplace = {
    AssignmentExpression(path){
        const {left,right,operator} = path.node;
        let var_name = left.name;
        if(!types.isLiteral(right) || !types.isIdentifier(left) || operator!=='=') return;
        eval(path.toString());
        path.scope.traverse(path.scope.block,{
            Identifier(_path){
                if(_path.node.name !== var_name || _path.node === left) return;
                if(types.isBinaryExpression(_path.parent)){
                    _path.replaceWith(types.valueToNode(eval(var_name)));
                }else if(types.isSwitchCase(_path.parent)){
                    _path.replaceWith(types.valueToNode(eval(var_name)));
                }
            }
        });
        path.remove();
    }
};

此时还需要将还原后的基础节点进行一个计算。

但是,单纯这么写有问题,假如碰到了反复调用这种情况,那么就会出现一点问题

a=1,b=2,c=3,d=4;
function add(){
    m=a+b;
    n=m+c;
    x=n+d;
}
----------------------
a = 1, b = 2, c = 3, d = 4;
function add() {
  m = 1 + 2;
  n = 1 + 2 + 3;
  x = 1 + 2 + 3 + 4;
}
// 需要先对其进行一个替换,这样在进行上面一步就不会出现问题。
----------------------
const varExchange = {
    AssignmentExpression(path){
        const {left,right} = path.node;
        let var_name = left.name;
        path.scope.traverse(path.scope.block,{
            Identifier(_path){
                if(_path.node.name !== var_name) return;
                if(_path.node === left) return;
                // console.log(path.toString(),'->',_path.parentPath.toString());
                _path.replaceWith(right);
            }
        })
    }
};
# 控制流还原

经过更深层次的观察,我们发现,很多变量的声明位置都不在一开始的声明语句中,而是处于控制流中,同时在 case 节点处,包含了 Blockstatement (把 scope 禁用了),这样我们就首先需要进行一个 Blockstatement 的还原。

case 1:
{
    XXX;
}
-------------------
case 1:
  	XXX;
--------------------
// 去掉控制流中的 block
const BlockReplace = {
    BlockStatement(path){
        const {body} = path.node;
        if(!types.isSwitchCase(path.parent)) return;
        for(i of body.reverse()){
            path.insertAfter(i);
        };
        path.remove();
    },
    EmptyStatement(path){
        path.remove();
    }
};
traverse(ast_code, BlockReplace);

接下来还需要优化一下,然后将赋值语句替换进去,这里先还原部分。

const ReferenceReplace = {
    AssignmentExpression(path){
        const {left,right,operator} = path.node;
        let var_name = left.name;
        if(!types.isLiteral(right) || !types.isIdentifier(left) || operator!=='=') return;
        let binding = path.scope.getBinding(var_name);
        if(!binding) return;
        if(types.isForStatement(path.parent)||types.isForStatement(path.parentPath.parent)) return;
        let references = binding.referencePaths;
        if(IsSwitchVar(references)) return;
        for(let reference of references){
            reference.replaceWith(right);
            reference.scope.crawl();
        }
    }
};

在此之后,大部分数已经被还原了,接下来就需要进一步替换函数表达式的还原。

const FuncReplace = {
VariableDeclarator(path){
        const {id,init} = path.node;
        if(!types.isFunctionExpression(init)) return;
        if(init.body.body.length !== 1) return;
        if(!types.isBinaryExpression(init.body.body[0].argument)) return;
        let func_name = id.name;
        let binding = path.scope.getBinding(func_name);
        let operator = init.body.body[0].argument.operator;
        binding.scope.traverse(binding.scope.block,{
            CallExpression(_path){
                const {callee,arguments} = _path.node;
                if(callee.name !== func_name) return;
                _path.replaceWith(types.binaryExpression(operator,arguments[0],arguments[1]))
            }
        });
        path.remove();
    }
};
const FuncReplace2 = {
    VariableDeclarator(path){
        const {id,init} = path.node;
        if(!types.isFunctionExpression(init)) return;
        if(init.body.body.length !== 1) return;
        if(!types.isUnaryExpression(init.body.body[0].argument)) return;
        let func_name = id.name;
        let binding = path.scope.getBinding(func_name);
        let operator = init.body.body[0].argument.operator;
        binding.scope.traverse(binding.scope.block,{
            CallExpression(_path){
                const {callee,arguments} = _path.node;
                if(callee.name !== func_name) return;
                _path.replaceWith(types.unaryExpression(operator,arguments[0]))
            }
        });
        path.remove();
    }
};

现在,我们可以看到,大部分逻辑都很清楚了,但是其中还包含了一个解密对象,同时,根据实际情况可以看到,这个解密对象肯定是隐藏在控制流中赋值的,那么接下来就需要还原控制流,由于该处采用了 call、apply、this 这些混合调用对象,需要枚举所有情况撰写插件。

ok,接下来直接解控制流。

// 可判断当前状态,同时这里的替换应该是当前 case 节点中的所有部分
function get_case(func_name,caseNode,currentpath){
     const {test, consequent} = caseNode;
     let current = [];
     for(let i of consequent){
         if(!types.isBreakStatement(i)){
               let {expression} = i;
               //case 值修改
               if(types.isAssignmentExpression(expression)){
                   if(expression.left.name === func_name) {
                       eval(generator(expression).code);
                       continue;
                   }
               };
               current.push(i);
               // console.log(generator(i).code);
               if(types.isReturnStatement(expression)) return current;
         };
         if(types.isBreakStatement(i)){continue};
     };
     return current
};
function choose_case(func_name, all_case_nodes){
    let case_value = eval(func_name);
    for(let i of all_case_nodes){
        if(i.test.value == case_value){
            return i;
        }
    };
};
// 这里传入调用节点 path
function GetCurrentValue(path){
    const {callee,arguments} = path.node;
    if(types.isMemberExpression(path.node.callee)){
        if(path.node.callee.property.name === 'call'){
            if(types.isThisExpression(path.node.arguments[0])){
                return path.node.arguments[1];
            }else{
                return path.node.arguments[0]
            };
        }else if(path.node.callee.property.name ==='apply'){
            if(types.isThisExpression(path.node.arguments[0])){
                return path.node.arguments[1].elements[0]
            }else{
                return path.node.arguments[0].elements[0]
            }
        };
    }
    if(types.isIdentifier(callee)){
        return arguments[0]
    }
}
function ValueFileter(x,value){
    let currentbody = [];
    for(let i of x){
        if(i == value){continue}
        currentbody.push(i);
    };
    return currentbody
}
// 通过遍历替换值,这里每次替换会重新赋值,在这里基本上以第一个为基础节点,然后把所有的参数 eval 进内存
const SwitchReplace = {
    VariableDeclarator(path){
        const {id,init} = path.node;
        if(!types.isFunctionExpression(init)) return;
        let binding = path.scope.getBinding(id.name);
        let references = binding.referencePaths;
        if(path.toString().indexOf('while') < 0) return;
        let tempdict = {};
        binding.scope.traverse(binding.scope.block,{
            CallExpression(_path_){
                if(types.isIdentifier(_path_.node.callee)){
                    if(_path_.node.callee.name!==id.name) return;
                }else if(types.isMemberExpression(_path_.node.callee)){
                    if(_path_.node.callee.object.name!==id.name) return;
                }
                let now_value = GetCurrentValue(_path_);
                if(!types.isNumericLiteral(now_value)) return;
                eval(init.params[0].name+'='+generator(now_value).code);
                let currentbody = [];
                path.scope.traverse(path.scope.block,{
                    WhileStatement(_path) {
                        const testpath = _path.get('test');
                        let func_name = testpath.node.left.name;
                        if(func_name !== init.params[0].name) return;
                        let cases = _path.node.body.body[0].cases;
                        while(eval(testpath.toString())) {
                            let result = get_case(func_name, choose_case(func_name, cases), _path);
                            for(let j of result){
                                currentbody.push(j);
                            };
                            if(types.isReturnStatement(currentbody[currentbody.length-1])) break;
                        };
                        tempdict[id.name+'_'+now_value.value] = types.functionExpression(types.identifier(id.name+'_'+now_value.value),[init.params[1]],types.blockStatement(currentbody));
                        // 去掉节点
                        if(types.isMemberExpression(_path_.node.callee)){
                            _path_.node.callee.object.name = id.name+'_'+now_value.value;
                            if(_path_.node.callee.property.name === 'apply' ){
                                if(types.isThisExpression(_path_.node.arguments[0])){
                                    _path_.node.arguments[1].elements = ValueFileter(_path_.node.arguments[1].elements,now_value);
                                }else{
                                    _path_.node.arguments[0].elements = ValueFileter(_path_.node.arguments[0].elements,now_value);
                                }
                            }else{
                                _path_.node.arguments = ValueFileter(_path_.node.arguments,now_value);
                            }
                        }else{
                            _path_.node.callee.name = id.name+'_'+now_value.value;
                            _path_.node.arguments = ValueFileter(_path_.node.arguments,now_value);
                        }
                    }
                });
            }
        });
        for(let i in tempdict){
            path.parentPath.insertBefore(tempdict[i]);
        };
        path.remove();
    },
};

因为函数调用这一部分,都是采用的第一个形参作为控制流的变量,因此,我们这里采用将形参取出来,用于对控制流进行节点合并,然后单独生成一个对应这个形参包含的流程的控制流,接着把这个封装成一个函数 (命名包含形参的值为区分),接着把函数调用处的变量名称改为与函数对应的形式,然后将实参结构调整,最后再将大控制流删除。

(function(){
    .....
    function j5_32(vl){
       j5_24([]);
        j5_57.call(this, [j5_22([])]);
        qH = j5_7([]);
        lH = j5_39([]);
        j5_9([]);
        j5_14([j5_5([])]);
        ......
    }
    ......
    return j5_32.call(this);
})();
--------------------------------------
这里我们可以看到,整个的流程其实是从j5_32进去,然后调用其中的很多控制流啥的,同时注意到,这里面的参数为字面量,因此可以进一步还原。

接下来就对类似这种 j5_24 ([]) 的进行一个还原。

const FuncChange = {
    FunctionDeclaration(path){
        const {id,params,body} = path.node;
        if(id === null) return;
        let func_name = id.name;
        let binding = path.scope.getBinding(id.name);
        let references = binding.referencePaths;
        for(let reference of references){
            if(!types.isCallExpression(reference.parent)) return;
            if(types.isArrayExpression(reference.parent.arguments[0]) && reference.parent.arguments[0].elements.length===0){
                if(reference.parent.arguments[0].elements.length===0){
                    if(!types.isVariableDeclarator(reference.parentPath.parent)){
                    if(body.body.length>1){
                        for(let i of body.body){
                            reference.parentPath.parentPath.insertBefore(i);
                        };
                        reference.parentPath.parentPath.remove();
                    }else if(types.isReturnStatement(body.body[0])){
                            reference.parentPath.replaceWith(body.body[0].argument);
                    }else{
                        reference.parentPath.replaceWith(body.body[0]);
                    }
                }
                }
            }
        };
        // path.remove();
    }
};

这时候,我们就发现上述流程变成了这种形式

image-20220220192508585

跟进去 j5_57 这个函数,以及 j5_14 这个函数。

function j5_57(vl) {
    var RW = vl[0];
    for (var Dg = 0; Dg < RW.length; Dg = Dg + 1) {
      ff[RW[Dg]] = function () {
        var CW = RW[Dg];
        return function (JW, OW) {
          var WW = j5_52([JW, OW]);
          ff[CW] = function () {
            return WW;
          };
          return WW;
        };
      }();
    }
  }
  
  function j5_14(vl) {
    var Rg = vl[0];
    for (var Dg = Rg[cH[U5]] - 1; Dg >= 0; --Dg) {
      ff[Rg[Dg]] = function () {
        var Cg = Rg[Dg];
        return function (Jg, Og, Wg) {
          var Pg = HB_7([Jg, Og, Wg, qH]);
          ff[Cg] = function () {
            return Pg;
          };
          return Pg;
        };
      }();
    }
  }

我们可以看到。这里就是对 ff 对象赋予方法的地方,本质上是通过传入的数组,对其采取一个 for 循环遍历,然后生成一个函数。

在控制台打印 j5_57 这个里面 for 循环生成的函数:

Sh === function (JW, OW) {
          var WW = j5_52([JW, OW]);
          ff[CW] = function () {
            return WW;
          };
          return WW;
        }
//Sh 指代数组中对应索引的参数,这里的 CW 指代的就是 Sh。
// 修改一下
ff['Sh'] = function (JW,OW){
    var WW = j5_52([JW, OW]);
    ff['Sh'] = function () {
            return WW;
    };
      return WW;
    }

接着观察源代码,发现其存在大量的 ff.Sh ('a',b) 类似这种形式的调用。也就是说传入了一个字面量和一个变量,跟栈进行观察,可以发现这个这个变量其实是在 j5_24 ([]) 这个流程中生成的,批量替换一次即可。同时,发现了这里的异常点,就是在初始化完成后,传入一次形参,然后这个函数的结果就会一直为该形参,后续在更改任何参数都不会改变其值。

接下来看 j5_52 流程中在干啥。

function j5_52(vl) {
    var MP = vl[0];
    var zP = vl[1];
    var LP = "\\K2[Ja:r=jVn{L!";
    var GP = [] + [];
    for (var Dg = 0; Dg < MP.length; ++Dg) {
      var EP = (Dg + zP) % LP.length;
      var IP = MP.charCodeAt(Dg) ^ LP.charCodeAt(EP);
      GP += HB_42([IP]);
    }
    return GP;
  }
// 首先传入了一个数组,包含两个参数,分别赋值给 MP,zP, 然后通过对 MP 的长度进行遍历,接着对索引以及第二个参数进行一个求和,在除以 LP 的长度,接着对 MP 以及 LP 返回一个 Unicode 编码,最后将该参数传入 HB_42 这个流程与开头的 GP 加和。
function HB_42(dB) {
    var F5 = dB[0];
    if (F5 <= 65535) {
      return String.fromCharCode(F5);
    } else {
      F5 -= 65536;
      return String.fromCharCode((F5 >> sB) + 55296, F5 % 1024 + 56320);
    }
  }
//HB_42 中是对传入的阐述进行了一个判断,将 Unicode 编码转化为了字符。

接着看 j5_14 这个流程与 j5_57 有什么不同,由于该流程中包含大量的其他部分的调用,因此,先对字面量进行一个简单的还原。

const ZvarRepalce = {
    AssignmentExpression(path){
        const {left,operator,right} = path.node;
        let ass_name = left.name;
        if(operator !== '=') return;
        if(!types.isExpressionStatement(path.parent) || !types.isIdentifier(left)) return;
        if(types.isNumericLiteral(right)){
            if(!path.parentPath.parentPath.parent.id || path.parentPath.parentPath.parent.id.name !== 'j5_32') return;
            let binding = path.scope.getBinding(ass_name);
            if(!binding) return;
            let references = binding.referencePaths;
            for(let reference of references){
                reference.replaceWith(right);
            };
        }else if(types.isArrayExpression(right)){
            if(right.elements.length === 0) return;
            if(types.isAssignmentExpression(right.elements[0])) return;
            let binding = path.scope.getBinding(ass_name);
            if(!binding) return;
            let references = binding.referencePaths;
            if(!binding.referenced) return;
            console.log(path.toString());
            for(let reference of references){
                if(types.isMemberExpression(reference.parent) && types.isNumericLiteral(reference.parent.property)) {
                    reference.parentPath.replaceWith(right.elements[reference.parent.property.value]);
                }else{
                    reference.replaceWith(right);
                }
            }
        };
        // path.remove();
    }
};

接着看流程:

function j5_14(vl) {
    var Rg = vl[0];
	// 与上述流程类似
    for (var Dg = Rg["length"] - 1; Dg >= 0; --Dg) {
      ff[Rg[Dg]] = function () {
        var Cg = Rg[Dg];
        return function (Jg, Og, Wg) {
          var Pg = HB_7([Jg, Og, Wg, qH]);
          ff[Cg] = function () {
            return Pg;
          };
          return Pg;
        };
      }();
    }
  }
function HB_7(dB) {
    var kl = dB[0];
    var QH = dB[1];
    var kH = dB[2];
    var xH = dB[3];
    var BH = "";
    nH = kl;
	// 只有当第三个参数大于 0 才进入流程
    while (kH > 0) {
       // 对第二个参数进行判断,若符合进入 117 流程
      if (QH["constructor"] !== wf["Array"] && QH >= xH["length"]) {
        if (xH == [25, -15, 26, -47, 12, -9, -2, 1, 2, -5, -43, [16], -12, -3, -6, -9, 0, 13, -14, 15, -29, 18, -5, 16, -12, 21, -21, 8, 3, -36, 29, 4, -2, 1, 12, -21, 5, 5, 10, 5, -9, -11, -13, -4, 3, 21, -38, 33, -19, 19, -15, 1, -43, 37, 4, -5, -2, -17, 37, -9, -11, 18, -5, -14, -1, 2, -1, [7], 0, 6, 14, -38, 34, -11, 17, -9, -7, 2, 11, -55, [0], 9, -1, 6, -33, 22, 17, -21, [13], -54, -11, 0, -11, 9, -3, -4, 9, 6, -27, -1, 19, -58, 1, -11, 9, 27, 6, -18, 5, -35, 49, -17, 9, 6, -13, 8, 2, 7, -17, -34, [19], -13, 51, 14, -12, -38, -18, -3, 9, -2, 11, -45, 30, 15, -45, 26, 12, -8, 11, -15, 3, 15, -1, -3, -6, 20, -21, [10], 3, 9, 1, 1, -40, 35, -14, 3, 1, -3, 19, -14, -16, -41, [8], -84, 52, 13, 8, -73, 44, 25, 3, 3, 0, -24, 17, 19, -4, 17, -9, -1, 5, 11, -12, 18, -8, -8, 9, 6, -39, 34, -11, 1, 1, [18], 8, -4, 11, -7, -6, -6, 2, -16, -3, -9, -1, -2, 3, -14, 3, 3, 15, -19, 1, 7, 8, -19, -13, 21, 4, -8, 14, 1, -19, 12, 3, -2, 12, -8, 13, -17, 10, -26, 11, 11, -25, 33, -2, 20, 15, 4, 1, 1, 6, -11, 6, -12, -3, 14, -10, 10, -32, 29, -11, 0, -2, 5, -32, 25, 18, -11, -5, 5, -30, 15, 7, -11, 1, -3, 14, 20, -15, -16, 30, -17, 2, 2, 5, 0, -9, 13, -42, 29, 4, -2, 1, 12, -28, 11, 11, -2, 15, -33, 34, -5, 0, -1, 3, 2, -15, -1, -31, 51, -4, -15, 9, 5, -10, 6, -1, 5, 19, -1, 0, -5, -1, -1, [7], 4, -1, 0, 17, -2, -6, -33, 26, 20, 2, 6, -2, -14, -24, 34, 7, -17, -33, [16], -11, 13, -7, -7, -1, -12, 18, -8, -6, -1, 27, -9, -17, 9, -10, 11, 3, 13, -8, -18, -3, 5, 5, 33, -2, -9, 5, -7, 2, 11, -40, 2, -17, 13, 21, 0, 13, -47, [0], 24, 9, -2, 3, -89, 81, -14, 9, 5, -4, 3, -19, 5, -3, 19, -19, -63, 24, -24, -14, 15, -50, 48, -17, 21, -17, -18, 22, -2, 7, -13, 11, -7, 15, -19, 7, -40, 33, 11, 4, -12, 15, -13, -4, 19, -15, -32, 39, -7, 8, -8, 9, 6, -3, -4, -3, 11, 12, -3, 3, 3, -46, 33, 11, 4, -12, 11, -14, -13, 20, 10, -3, 5, 0, -8, -7, -15, 11, 11, -11, -1, -4, 44, 0, -4, -30, 19, 12, 4, -16, 14, 1, [13], 3, -57, 30, 13, 0, 1, 5, -84, 67, -2, 11, 0, -76, 65, -65, 67, 9, -11, 18, 0, -83, 65, 18, -83, 65, -65, 70, 15, -7, -11, [6], -8, 4, 0, 3, 9, -14, 6, 9, 6, -43, 33, [18], 9, -3, 7, -14, 13, -14, 17, -14, -12, 15, 11, -17, -35, 34, 45, -11, 11, -1, -5, -73, 23, -5, -18, 34, 45, -3, -8, 9, -1, 6, 15, 0, -11, 9, -10, -33, [19], 1, -2, -9, 5, -7, 2, -17, 13, 21, 0, 13, -47, [0], 19, -12, 3, -8, -20, 31, 4, 1, -48, [16], [8], -70, 42, -11, -1, -4, [13], 4, -1, 1, -6, 20, -21, [10], 4, 1, 2]) {
          BH += j5_117([nH]);
        }
        return BH;
      }
      if (QH["constructor"] === wf["Array"]) {
        var HH = [[44, -1, 0, -9, -2, 17, -11, 6, -1], [], [], [], [], [], [17, -11, 6, -1], [-6, 5, 6, -15, 13, -14, 11, 8, -9], [28, -6, 15, -3, 4, -4, -9, 14], [], [11, 8, -9], [], [], [12, 0, -4], [], [], [29, 19, -19], [], [-5, 0, 1, 9], [37, 1, 3, -8]][xH[QH[0]][0]];
        var gH = HB_7([nH, QH[1], kH, HH]);
        BH += gH;
        QH = QH[0];
        kH -= j5_728([gH]);
      } else if (xH[QH]["constructor"] === wf["Array"]) {
        var HH = [[44, -1, 0, -9, -2, 17, -11, 6, -1], [], [], [], [], [], [17, -11, 6, -1], [-6, 5, 6, -15, 13, -14, 11, 8, -9], [28, -6, 15, -3, 4, -4, -9, 14], [], [11, 8, -9], [], [], [12, 0, -4], [], [], [29, 19, -19], [], [-5, 0, 1, 9], [37, 1, 3, -8]][xH[QH][0]];
        var gH = HB_7([nH, 0, kH, HH]);
        BH += gH;
        kH -= j5_728([gH]);
      } else {
        BH += j5_117([nH]);
        nH += xH[QH];
        --kH;
      }
      ++QH;
    }
    return BH;
  }

进入 117 流程发现,其实也还是将其转化为一个 unicode 编码的形式,本质上这两个过程都是将传入的参数进行编码解码操作。

试着把这些全抠出来。

// 环境加载
ff = {};
window = global;
wf = window;
function j5_57(vl) {
    var RW = vl[0];
    for (var Dg = 0; Dg < RW.length; Dg = Dg + 1) {
      ff[RW[Dg]] = function () {
        var CW = RW[Dg];
        return function (JW, OW) {
          var WW = j5_52([JW, OW]);
          ff[CW] = function () {
            return WW;
          };
          return WW;
        };
      }();
    }
  }
function j5_52(vl) {
    var MP = vl[0];
    var zP = vl[1];
    var LP = "\\K2[Ja:r=jVn{L!";
    var GP = "";
    for (var Dg = 0; Dg < MP.length; ++Dg) {
      var EP = (Dg + zP) % LP.length;
      var IP = MP.charCodeAt(Dg) ^ LP.charCodeAt(EP);
      GP += HB_42([IP]);
    }
    return GP;
  }
function HB_42(dB) {
    var F5 = dB[0];
    if (F5 <= 65535) {
      return String.fromCharCode(F5);
    } else {
      F5 -= 65536;
      return String.fromCharCode((F5 >> 10) + 55296, F5 % 1024 + 56320);
    }
  }
function j5_14(vl) {
    var Rg = vl[0];
    for (var Dg = Rg["length"] - 1; Dg >= 0; --Dg) {
      ff[Rg[Dg]] = function () {
        var Cg = Rg[Dg];
        return function (Jg, Og, Wg) {
          var Pg = HB_7([Jg, Og, Wg, [25, -15, 26, -47, 12, -9, -2, 1, 2, -5, -43, [16], -12, -3, -6, -9, 0, 13, -14, 15, -29, 18, -5, 16, -12, 21, -21, 8, 3, -36, 29, 4, -2, 1, 12, -21, 5, 5, 10, 5, -9, -11, -13, -4, 3, 21, -38, 33, -19, 19, -15, 1, -43, 37, 4, -5, -2, -17, 37, -9, -11, 18, -5, -14, -1, 2, -1, [7], 0, 6, 14, -38, 34, -11, 17, -9, -7, 2, 11, -55, [0], 9, -1, 6, -33, 22, 17, -21, [13], -54, -11, 0, -11, 9, -3, -4, 9, 6, -27, -1, 19, -58, 1, -11, 9, 27, 6, -18, 5, -35, 49, -17, 9, 6, -13, 8, 2, 7, -17, -34, [19], -13, 51, 14, -12, -38, -18, -3, 9, -2, 11, -45, 30, 15, -45, 26, 12, -8, 11, -15, 3, 15, -1, -3, -6, 20, -21, [10], 3, 9, 1, 1, -40, 35, -14, 3, 1, -3, 19, -14, -16, -41, [8], -84, 52, 13, 8, -73, 44, 25, 3, 3, 0, -24, 17, 19, -4, 17, -9, -1, 5, 11, -12, 18, -8, -8, 9, 6, -39, 34, -11, 1, 1, [18], 8, -4, 11, -7, -6, -6, 2, -16, -3, -9, -1, -2, 3, -14, 3, 3, 15, -19, 1, 7, 8, -19, -13, 21, 4, -8, 14, 1, -19, 12, 3, -2, 12, -8, 13, -17, 10, -26, 11, 11, -25, 33, -2, 20, 15, 4, 1, 1, 6, -11, 6, -12, -3, 14, -10, 10, -32, 29, -11, 0, -2, 5, -32, 25, 18, -11, -5, 5, -30, 15, 7, -11, 1, -3, 14, 20, -15, -16, 30, -17, 2, 2, 5, 0, -9, 13, -42, 29, 4, -2, 1, 12, -28, 11, 11, -2, 15, -33, 34, -5, 0, -1, 3, 2, -15, -1, -31, 51, -4, -15, 9, 5, -10, 6, -1, 5, 19, -1, 0, -5, -1, -1, [7], 4, -1, 0, 17, -2, -6, -33, 26, 20, 2, 6, -2, -14, -24, 34, 7, -17, -33, [16], -11, 13, -7, -7, -1, -12, 18, -8, -6, -1, 27, -9, -17, 9, -10, 11, 3, 13, -8, -18, -3, 5, 5, 33, -2, -9, 5, -7, 2, 11, -40, 2, -17, 13, 21, 0, 13, -47, [0], 24, 9, -2, 3, -89, 81, -14, 9, 5, -4, 3, -19, 5, -3, 19, -19, -63, 24, -24, -14, 15, -50, 48, -17, 21, -17, -18, 22, -2, 7, -13, 11, -7, 15, -19, 7, -40, 33, 11, 4, -12, 15, -13, -4, 19, -15, -32, 39, -7, 8, -8, 9, 6, -3, -4, -3, 11, 12, -3, 3, 3, -46, 33, 11, 4, -12, 11, -14, -13, 20, 10, -3, 5, 0, -8, -7, -15, 11, 11, -11, -1, -4, 44, 0, -4, -30, 19, 12, 4, -16, 14, 1, [13], 3, -57, 30, 13, 0, 1, 5, -84, 67, -2, 11, 0, -76, 65, -65, 67, 9, -11, 18, 0, -83, 65, 18, -83, 65, -65, 70, 15, -7, -11, [6], -8, 4, 0, 3, 9, -14, 6, 9, 6, -43, 33, [18], 9, -3, 7, -14, 13, -14, 17, -14, -12, 15, 11, -17, -35, 34, 45, -11, 11, -1, -5, -73, 23, -5, -18, 34, 45, -3, -8, 9, -1, 6, 15, 0, -11, 9, -10, -33, [19], 1, -2, -9, 5, -7, 2, -17, 13, 21, 0, 13, -47, [0], 19, -12, 3, -8, -20, 31, 4, 1, -48, [16], [8], -70, 42, -11, -1, -4, [13], 4, -1, 1, -6, 20, -21, [10], 4, 1, 2]]);
          ff[Cg] = function () {
            return Pg;
          };
          return Pg;
        };
      }();
    }
  }
function j5_117(vl) {
    var DP = vl[0];
    if (DP <= 65535) {
      return wf["String"]["fromCharCode"](DP);
    } else {
      DP -= 65536;
      return wf["String"]["fromCharCode"]["apply"](null, [(DP >> 10) + 55296, DP % 1024 + 56320]);
    }
  }
function j5_728(vl) {
    var RP = vl[0];
    var CP = 0;
    for (var JP = 0; JP < RP.length; ++JP) {
      var OP = RP.charCodeAt(JP);
      if (OP < 55296 || OP > 56319) CP = CP + 1;
    }
    return CP;
  }
function HB_7(dB) {
    var kl = dB[0];
    var QH = dB[1];
    var kH = dB[2];
    var xH = dB[3];
    var BH = "";
    nH = kl;
    while (kH > 0) {
      if (QH["constructor"] !== wf["Array"] && QH >= xH["length"]) {
        if (xH == [25, -15, 26, -47, 12, -9, -2, 1, 2, -5, -43, [16], -12, -3, -6, -9, 0, 13, -14, 15, -29, 18, -5, 16, -12, 21, -21, 8, 3, -36, 29, 4, -2, 1, 12, -21, 5, 5, 10, 5, -9, -11, -13, -4, 3, 21, -38, 33, -19, 19, -15, 1, -43, 37, 4, -5, -2, -17, 37, -9, -11, 18, -5, -14, -1, 2, -1, [7], 0, 6, 14, -38, 34, -11, 17, -9, -7, 2, 11, -55, [0], 9, -1, 6, -33, 22, 17, -21, [13], -54, -11, 0, -11, 9, -3, -4, 9, 6, -27, -1, 19, -58, 1, -11, 9, 27, 6, -18, 5, -35, 49, -17, 9, 6, -13, 8, 2, 7, -17, -34, [19], -13, 51, 14, -12, -38, -18, -3, 9, -2, 11, -45, 30, 15, -45, 26, 12, -8, 11, -15, 3, 15, -1, -3, -6, 20, -21, [10], 3, 9, 1, 1, -40, 35, -14, 3, 1, -3, 19, -14, -16, -41, [8], -84, 52, 13, 8, -73, 44, 25, 3, 3, 0, -24, 17, 19, -4, 17, -9, -1, 5, 11, -12, 18, -8, -8, 9, 6, -39, 34, -11, 1, 1, [18], 8, -4, 11, -7, -6, -6, 2, -16, -3, -9, -1, -2, 3, -14, 3, 3, 15, -19, 1, 7, 8, -19, -13, 21, 4, -8, 14, 1, -19, 12, 3, -2, 12, -8, 13, -17, 10, -26, 11, 11, -25, 33, -2, 20, 15, 4, 1, 1, 6, -11, 6, -12, -3, 14, -10, 10, -32, 29, -11, 0, -2, 5, -32, 25, 18, -11, -5, 5, -30, 15, 7, -11, 1, -3, 14, 20, -15, -16, 30, -17, 2, 2, 5, 0, -9, 13, -42, 29, 4, -2, 1, 12, -28, 11, 11, -2, 15, -33, 34, -5, 0, -1, 3, 2, -15, -1, -31, 51, -4, -15, 9, 5, -10, 6, -1, 5, 19, -1, 0, -5, -1, -1, [7], 4, -1, 0, 17, -2, -6, -33, 26, 20, 2, 6, -2, -14, -24, 34, 7, -17, -33, [16], -11, 13, -7, -7, -1, -12, 18, -8, -6, -1, 27, -9, -17, 9, -10, 11, 3, 13, -8, -18, -3, 5, 5, 33, -2, -9, 5, -7, 2, 11, -40, 2, -17, 13, 21, 0, 13, -47, [0], 24, 9, -2, 3, -89, 81, -14, 9, 5, -4, 3, -19, 5, -3, 19, -19, -63, 24, -24, -14, 15, -50, 48, -17, 21, -17, -18, 22, -2, 7, -13, 11, -7, 15, -19, 7, -40, 33, 11, 4, -12, 15, -13, -4, 19, -15, -32, 39, -7, 8, -8, 9, 6, -3, -4, -3, 11, 12, -3, 3, 3, -46, 33, 11, 4, -12, 11, -14, -13, 20, 10, -3, 5, 0, -8, -7, -15, 11, 11, -11, -1, -4, 44, 0, -4, -30, 19, 12, 4, -16, 14, 1, [13], 3, -57, 30, 13, 0, 1, 5, -84, 67, -2, 11, 0, -76, 65, -65, 67, 9, -11, 18, 0, -83, 65, 18, -83, 65, -65, 70, 15, -7, -11, [6], -8, 4, 0, 3, 9, -14, 6, 9, 6, -43, 33, [18], 9, -3, 7, -14, 13, -14, 17, -14, -12, 15, 11, -17, -35, 34, 45, -11, 11, -1, -5, -73, 23, -5, -18, 34, 45, -3, -8, 9, -1, 6, 15, 0, -11, 9, -10, -33, [19], 1, -2, -9, 5, -7, 2, -17, 13, 21, 0, 13, -47, [0], 19, -12, 3, -8, -20, 31, 4, 1, -48, [16], [8], -70, 42, -11, -1, -4, [13], 4, -1, 1, -6, 20, -21, [10], 4, 1, 2]) {
          BH += j5_117([nH]);
        }
        return BH;
      }
      if (QH["constructor"] === wf["Array"]) {
        var HH = [[44, -1, 0, -9, -2, 17, -11, 6, -1], [], [], [], [], [], [17, -11, 6, -1], [-6, 5, 6, -15, 13, -14, 11, 8, -9], [28, -6, 15, -3, 4, -4, -9, 14], [], [11, 8, -9], [], [], [12, 0, -4], [], [], [29, 19, -19], [], [-5, 0, 1, 9], [37, 1, 3, -8]][xH[QH[0]][0]];
        var gH = HB_7([nH, QH[1], kH, HH]);
        BH += gH;
        QH = QH[0];
        kH -= j5_728([gH]);
      } else if (xH[QH]["constructor"] === wf["Array"]) {
        var HH = [[44, -1, 0, -9, -2, 17, -11, 6, -1], [], [], [], [], [], [17, -11, 6, -1], [-6, 5, 6, -15, 13, -14, 11, 8, -9], [28, -6, 15, -3, 4, -4, -9, 14], [], [11, 8, -9], [], [], [12, 0, -4], [], [], [29, 19, -19], [], [-5, 0, 1, 9], [37, 1, 3, -8]][xH[QH][0]];
        var gH = HB_7([nH, 0, kH, HH]);
        BH += gH;
        kH -= j5_728([gH]);
      } else {
        BH += j5_117([nH]);
        nH += xH[QH];
        --kH;
      }
      ++QH;
    }
    return BH;
  }
j5_57.call(this, [["cF", "Tk", "lG", "Sh", "KX", "SX", "q2", "CG", "Xm", "nk", "Ih", "Zh", "dh", "Zk", "SG", "lk", "s2", "xh", "Bh", "Gh", "nh", "kh", "ch", "th", "qh", "lh", "Hh", "Mh", "Qh", "gh", "jh", "rh", "bh", "Yh", "Eh", "zh", "ph", "Th", "Ck", "dk", "Dk", "W2", "P2", "Dh", "Lh", "gN", "pG", "Wh", "rm", "qF", "B2", "gm", "Pk", "IX", "EX", "WG", "w2", "SF", "bF", "MN", "Sm", "DG", "IN", "rX", "hk", "RF", "Tm", "bm", "TX", "Oh", "Bk", "kF", "pN", "mk", "Xk", "U2", "zX", "Qm", "TN", "zk", "jX", "NX", "bN", "EN", "Nh", "wX", "l9", "Yk", "RG", "KN", "qG", "zN", "V9", "LN", "mX", "gX", "Hm", "Lm", "qm", "SN", "YN", "ZG", "rG", "IG", "Ok", "Jk", "xX", "ZX", "Ph", "Ah", "VN", "fG", "F9", "FN", "Vk", "Rh", "BG", "fN", "c2", "vN", "Nm", "jm", "Wk", "H9", "lX", "wQ", "tN", "TG", "tm", "PN", "pF", "dm", "Kk", "Ch", "rN", "nG", "vF", "jF", "K2", "I2", "JG", "Gm", "z2", "L2", "xF", "cG", "OX", "dF", "k2", "HF", "dG", "xQ", "MG", "LG", "A2", "UG", "XG", "Z2", "M2", "VX", "YX", "v9", "K9", "h9", "tX", "Zm", "MF", "vG", "VF", "Z9", "QX", "BF", "xN", "fX", "QG", "g2", "WN", "Km", "dN", "mN", "XN", "c9", "PG", "Mm", "wm", "G9", "Ym", "pm", "TF", "gG", "KG", "P9", "bG", "lm", "CX", "UX", "N2", "OF", "Fk", "B9", "q9", "dX", "ZN", "l2", "C9", "Ek", "Em", "A9", "JX", "HX", "UN", "DN", "FX", "X9", "ck", "R2", "x9", "d2", "D2", "Ik", "Sk", "hN", "nm", "E2", "Cm", "Bm", "Jm", "p9", "BN", "Lk", "tG", "AN", "Om", "qX", "t9", "NF", "O2", "EF", "J2", "WF", "OG", "GN", "Rm", "nF", "E9", "HN", "zG", "NN", "xG", "J9", "HG", "w9", "x2", "Gk", "O9", "N9", "r9", "j9", "G2", "lQ", "HQ", "lF", "PF", "gk", "Rk", "gF", "jk", "L9", "LF", "sF", "wG", "Wm", "pX", "Q2", "JN", "CN", "lN", "X2", "m2", "mm", "qQ", "cQ", "BQ", "Pm", "rQ", "kk", "sm", "vm", "TQ", "Vm", "pQ", "cX", "MX", "H2", "PX", "S2", "W9", "AG", "RN", "CF", "sG", "AF", "kX", "QN", "jN", "cN", "wk", "zQ", "hQ", "LQ", "QQ", "tQ", "NQ", "mQ", "GQ", "kQ", "bk", "fk", "Uk", "EG", "BX", "v2", "V2", "rF", "AX", "mG", "Am", "sN", "T9", "hm", "nQ", "WX", "EQ", "rk", "Hk", "kG", "C2", "tF", "R9", "YF", "hG", "XX", "Y9", "DX", "wh", "D9", "vX", "fh", "Uh", "kN", "h2", "s9", "j2", "wN", "nX", "m9", "vk", "YG", "Mk", "Fh", "r2", "Xh", "z9", "Ak", "DF", "t2", "ZF", "xm", "nN", "jG", "ON", "qN", "JF", "zF", "Kh", "Im", "F2", "GG", "Dm", "mh", "cm", "g9", "n9", "Q9", "d9", "S9", "GX", "zm", "Um", "b9", "f2", "NG", "fm", "f9", "U9", "QF", "hX", "XQ", "FQ", "fQ", "UQ", "n2", "GF", "RX", "gQ", "IF", "sX", "KF", "tk", "Qk", "MQ", "I9", "km", "bQ", "M9", "hh", "YQ", "VG", "p2", "Fm", "k9", "LX", "pk", "T2", "Y2", "Nk", "bX", "b2", "sh", "xk", "qk", "vh", "Vh", "Jh"]]);
j5_14([["QU", "cU", "xU", "qU", "BU", "nU", "HU", "If", "TU", "Kf", "MU", "zU", "WU", "kU", "zf", "nf", "fU", "Af", "DU", "bU", "xf", "lU", "LU", "jU", "OU", "gU", "Bf", "Rf", "vf", "KU", "Pf", "SU", "lf", "rf", "Zf", "wF", "mU", "wU", "sf", "CU", "RU", "Yf", "tU", "bf", "pU", "Tf", "Vf", "mF", "Wf", "Of", "GU", "dU", "XF", "YU", "ZU", "Sf", "FU", "hU", "Lf", "hF", "Jf", "EU", "UF", "PU", "fF", "vU", "gf", "FF", "Mf", "sU", "IU", "JU", "Cf", "AU", "df", "Ef", "Hf", "qf", "Df", "UU", "pf", "XU", "jf", "rU", "NU", "VU"]]);

这样就把其全部抠出来了。

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

Mr2 WeChat Pay

WeChat Pay