不知道为什么要还原 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(); | |
} | |
}; |
插件撰写思路:
- 遍历函数声明式,只针对无形参,无返回值的函数声明式。
- 根据其函数名,找到其调用位置,需保证调用位置的结构为 "x ();" 的形式。
- 节点替换。
# 非常规运算
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(); | |
} | |
}; |
这时候,我们就发现上述流程变成了这种形式
跟进去 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"]]); |
这样就把其全部抠出来了。