01
前言
02
处理过程
part.js
function q() {
const x = ["return (function() ", "51rDvFsO", "280bIrfll", "crossOrigin", "debu", "5573704jgYESE", "526422EOMPDB", "19pdzydt", "70220etHPRV", "26502443qIuDbf", '{}.constructor("return this")( )', "type", "string", "172742zcyDzi", "tagName", "include", "link", "LINK", "same-origin", "test", "function *\( *\)", "apply", "init", "386856yRDrIu", "addedNodes", 'link[rel="modulepreload"]', "length", "input", "stateObject", "modulepreload", "relList", "createElement", "supports", "10594465MEmbDB", "5JjJNqT", "setInterval", "querySelectorAll", "referrerPolicy", "credentials", "gger", "anonymous", "integrity", "observe", "action", "use-credentials", "constructor", "omit", "\+\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)"];
return q = function() {
return x
},
q()
} (function(x, n) {
const e = z,
t = x();
for (;;) try {
if ( - parseInt(e(285)) / 1 * ( - parseInt(e(291)) / 2) + parseInt(e(279)) / 3 * (parseInt(e(286)) / 4) + -parseInt(e(264)) / 5 * (parseInt(e(284)) / 6) + -parseInt(e(263)) / 7 + -parseInt(e(283)) / 8 + -parseInt(e(253)) / 9 * (parseInt(e(280)) / 10) + parseInt(e(287)) / 11 === n) break;
t.push(t.shift())
} catch {
t.push(t.shift())
}
})(q, -256319 + -5 * -139997 + 7 * 57662),
function() {
const x = z;
let n;
try {
n = Function(x(278) + x(288) + ");")()
} catch {
n = window
}
n[x(265)](V, 9793 + -977 * 9)
} (),
function() {
const n = z,
e = function() {
let o = !0;
return function(c, i) {
const f = o ?
function() {
const b = z;
if (i) {
const s = i[b(251)](c, arguments);
return i = null,
s
}
}: function() {};
return o = !1,
f
}
} (),
t = document[n(261)](n(294))[n(260)];
if (t && t[n(262)] && t[n(262)](n(259))) return;
for (const o of document[n(266)](n(255))) a(o);
new MutationObserver(o => {
const c = n;
for (const i of o) if (i[c(289)] === "childList") for (const f of i[c(254)]) f[c(292)] === c(295) && f.rel === c(259) && a(f)
})[n(272)](document, {
childList: !0,
subtree: !0
});
function r(o) {
const c = n,
i = {};
return o[c(271)] && (i.integrity = o[c(271)]),
o[c(267)] && (i[c(267)] = o[c(267)]),
o[c(281)] === c(274) ? i.credentials = c(293) : o.crossOrigin === c(270) ? i[c(268)] = c(276) : i[c(268)] = c(296),
i
}
function a(o) {
if (function() {
e(this,
function() {
const i = z,
f = new RegExp(i(250)),
b = new RegExp(i(277), "i"),
s = V(i(252)); ! f[i(249)](s + "chain") || !b[i(249)](s + i(257)) ? s("0") : V()
})()
} (), o.ep) return;
o.ep = !0;
const c = r(o);
fetch(o.href, c)
}
} ();
function z(x, n) {
const e = q();
return z = function(t, r) {
return t = t - ( - 109 * -23 + -6806 + 4548),
e[t]
},
z(x, n)
}
function V(x) {
function n(e) {
const t = z;
if (typeof e === t(290)) return (function(r) {})[t(275)]("while (true) {}")[t(251)]("counter"); ("" + e / e)[t(256)] !== 3561 + 712 * -5 || e % (10 * 929 + 676 + 4973 * -2) === 8536 + -1 * 5 + -1 * 8531 ? (function() {
return ! 0
})[t(275)](t(282) + t(269)).call(t(273)) : (function() {
return ! 1
})[t(275)]("debu" + t(269))[t(251)](t(258)),
n(++e)
}
try {
if (x) return n;
n(599 * -15 + 8263 * 1 + 722)
} catch {}
}
function q() {
const x = ["return (function() ", "51rDvFsO", "280bIrfll", "crossOrigin", "debu", "5573704jgYESE", "526422EOMPDB", "19pdzydt", "70220etHPRV", "26502443qIuDbf", '{}.constructor("return this")( )', "type", "string", "172742zcyDzi", "tagName", "include", "link", "LINK", "same-origin", "test", "function *\( *\)", "apply", "init", "386856yRDrIu", "addedNodes", 'link[rel="modulepreload"]', "length", "input", "stateObject", "modulepreload", "relList", "createElement", "supports", "10594465MEmbDB", "5JjJNqT", "setInterval", "querySelectorAll", "referrerPolicy", "credentials", "gger", "anonymous", "integrity", "observe", "action", "use-credentials", "constructor", "omit", "\+\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)"];
return q = function() {
return x
},
q()
}
function z(x, n) {
const e = q();
return z = function(t, r) {
return t = t - ( - 109 * -23 + -6806 + 4548),
e[t]
},
z(x, n)
}
如果调用了q函数,q() return 的q(),实际上就是返回x数组;
function q() {
const x = ["return (function() ", "51rDvFsO", "280bIrfll", "crossOrigin", "debu", "5573704jgYESE", "526422EOMPDB", "19pdzydt", "70220etHPRV", "26502443qIuDbf", '{}.constructor("return this")( )', "type", "string", "172742zcyDzi", "tagName", "include", "link", "LINK", "same-origin", "test", "function *\( *\)", "apply", "init", "386856yRDrIu", "addedNodes", 'link[rel="modulepreload"]', "length", "input", "stateObject", "modulepreload", "relList", "createElement", "supports", "10594465MEmbDB", "5JjJNqT", "setInterval", "querySelectorAll", "referrerPolicy", "credentials", "gger", "anonymous", "integrity", "observe", "action", "use-credentials", "constructor", "omit", "\+\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)"];
return x
}
=>
function q() {
return ["return (function() ", "51rDvFsO", "280bIrfll", "crossOrigin", "debu", "5573704jgYESE", "526422EOMPDB", "19pdzydt", "70220etHPRV", "26502443qIuDbf", '{}.constructor("return this")( )', "type", "string", "172742zcyDzi", "tagName", "include", "link", "LINK", "same-origin", "test", "function *\( *\)", "apply", "init", "386856yRDrIu", "addedNodes", 'link[rel="modulepreload"]', "length", "input", "stateObject", "modulepreload", "relList", "createElement", "supports", "10594465MEmbDB", "5JjJNqT", "setInterval", "querySelectorAll", "referrerPolicy", "credentials", "gger", "anonymous", "integrity", "observe", "action", "use-credentials", "constructor", "omit", "\+\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)"];
}
调用z函数,其实调用的是
z = function(t, r) {
return t = t - ( - 109 * -23 + -6806 + 4548),
e[t]
}
AST explorer
这是一个在线网址,https://astexplorer.net/。可以将js代码根据选择的编译器转为json结构的ast代码
如下图,左侧是源代码,右侧可以选择Tree的视图或者Json视图:
移动鼠标到代码上,右侧就会相应得标红
babel
npm install @babel/core
npm install @babel/parser --save-dev
npm install @babel/traverse --save-dev
npm install @babel/generator --save-dev
npm install @babel/types
@babel/core 是 Babel 工具链的核心模块;接着是解析器,转换器,代码生成器,最后的types模块可以帮助判断node类型,构造节点。
化简q类函数
function q() {
const x = ["return (function() ", "51rDvFsO", "280bIrfll", "crossOrigin", "debu", "5573704jgYESE", "526422EOMPDB", "19pdzydt", "70220etHPRV", "26502443qIuDbf", '{}.constructor("return this")( )', "type", "string", "172742zcyDzi", "tagName", "include", "link", "LINK", "same-origin", "test", "function *\( *\)", "apply", "init", "386856yRDrIu", "addedNodes", 'link[rel="modulepreload"]', "length", "input", "stateObject", "modulepreload", "relList", "createElement", "supports", "10594465MEmbDB", "5JjJNqT", "setInterval", "querySelectorAll", "referrerPolicy", "credentials", "gger", "anonymous", "integrity", "observe", "action", "use-credentials", "constructor", "omit", "\+\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)"];
return q = function() {
return x
},
q()
}
=>
function q() {
return ["return (function() ", "51rDvFsO", "280bIrfll", "crossOrigin", "debu", "5573704jgYESE", "526422EOMPDB", "19pdzydt", "70220etHPRV", "26502443qIuDbf", '{}.constructor("return this")( )', "type", "string", "172742zcyDzi", "tagName", "include", "link", "LINK", "same-origin", "test", "function *\( *\)", "apply", "init", "386856yRDrIu", "addedNodes", 'link[rel="modulepreload"]', "length", "input", "stateObject", "modulepreload", "relList", "createElement", "supports", "10594465MEmbDB", "5JjJNqT", "setInterval", "querySelectorAll", "referrerPolicy", "credentials", "gger", "anonymous", "integrity", "observe", "action", "use-credentials", "constructor", "omit", "\+\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)"];
}
对比标黄的部分,很明显的可以看出来,q1的body只有一个ReturnStatement
我们直接把return的值改为 x数组,请看下面的代码
const returnArrayVisitor = {
FunctionDeclaration(path) {
var funcname = path.node.id.name;
if (funcname.length === 1 && path.node.body.body.length === 2) {
let body = path.node.body.body;
if (
types.isVariableDeclaration(body[0]) &&
types.isReturnStatement(body[1])
) {
let elements;
if (types.isArrayExpression(body[0].declarations[0].init)) {
elements = body[0].declarations[0].init.elements;
// 构造ArrayExpression节点作为返回值
const arrayExpression = types.arrayExpression(elements);
body[1] = types.returnStatement(arrayExpression);
body.splice(0, 1);
}
}
}
}
};
首先是FunctionDeclaration,意思是所有traverse所有FunctionDeclaration类型的节点。
接着是path和node,path即是路径,node就是节点。其实本身一个json数据,这里的.就是取出子json结构的数据。
// 判断是否符合q函数类型
var funcname = path.node.id.name;
if (funcname.length === 1 && path.node.body.body.length === 2)
// 进一步明确
if (types.isVariableDeclaration(body[0]) &&types.isReturnStatement(body[1])
//这里的话就是拿到数组,然后构造返回的节点
let elements;
if (types.isArrayExpression(body[0].declarations[0].init)) {
elements = body[0].declarations[0].init.elements;
// 构造ArrayExpression节点作为返回值
const arrayExpression = types.arrayExpression(elements);
body[1] = types.returnStatement(arrayExpression);
完整代码.js
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;
//将源代码解析为AST
var sourceCode = `
function q() {
const x = ["return (function() ", "51rDvFsO", "280bIrfll", "crossOrigin", "debu", "5573704jgYESE", "526422EOMPDB", "19pdzydt", "70220etHPRV", "26502443qIuDbf", '{}.constructor("return this")( )', "type", "string", "172742zcyDzi", "tagName", "include", "link", "LINK", "same-origin", "test", "function *\\( *\\)", "apply", "init", "386856yRDrIu", "addedNodes", 'link[rel="modulepreload"]', "length", "input", "stateObject", "modulepreload", "relList", "createElement", "supports", "10594465MEmbDB", "5JjJNqT", "setInterval", "querySelectorAll", "referrerPolicy", "credentials", "gger", "anonymous", "integrity", "observe", "action", "use-credentials", "constructor", "omit", "\\+\\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)"];
return q = function() {
return x
},
q()
}
`
let ast = parser.parse(sourceCode);
const returnArrayVisitor = {
FunctionDeclaration(path) {
var funcname = path.node.id.name;
if (funcname.length === 1 && path.node.body.body.length === 2) {
let body = path.node.body.body;
if (
types.isVariableDeclaration(body[0]) &&
types.isReturnStatement(body[1])
) {
let elements;
if (types.isArrayExpression(body[0].declarations[0].init)) {
elements = body[0].declarations[0].init.elements;
// 构造ArrayExpression节点作为返回值
const arrayExpression = types.arrayExpression(elements);
body[1] = types.returnStatement(arrayExpression);
body.splice(0, 1);
}
}
}
}
};
traverse(ast, returnArrayVisitor);
let decodeFile = "./encode_ok.js"
let { code } = generator(ast, opts = {
"compact": false,
"comments": false,
"jsescOption": { "minimal": true },
});
fs.writeFile(decodeFile, code, (err) => { });
function q() {
return ["return (function() ", "51rDvFsO", "280bIrfll", "crossOrigin", "debu", "5573704jgYESE", "526422EOMPDB", "19pdzydt", "70220etHPRV", "26502443qIuDbf", '{}.constructor("return this")( )', "type", "string", "172742zcyDzi", "tagName", "include", "link", "LINK", "same-origin", "test", "function *\( *\)", "apply", "init", "386856yRDrIu", "addedNodes", 'link[rel="modulepreload"]', "length", "input", "stateObject", "modulepreload", "relList", "createElement", "supports", "10594465MEmbDB", "5JjJNqT", "setInterval", "querySelectorAll", "referrerPolicy", "credentials", "gger", "anonymous", "integrity", "observe", "action", "use-credentials", "constructor", "omit", "\+\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)"];
}
化简z类函数
z函数
function z(x, n) {
const e = q();
return z = function(t, r) {
return t = t - ( - 109 * -23 + -6806 + 4548),
e[t]
},
z(x, n)
}
function z(x, n) {
const e = q();
return z = function(t, r) {
return t = t - ( - 109 * -23 + -6806 + 4548),
e[t]
},
z(x, n)
}
=>
function z(x, n) {
const e = q();
z = function (t, r) {
t = t - (-109 * -23 + -6806 + 4548);
return e[t];
};
return z(x, n);
}
=>
var z = function(t, r) {
const e = q();
t = t - ( - 109 * -23 + -6806 + 4548)
return e[t]
}
const sequenceVisitor =
{
SequenceExpression(path)
{
let {scope,parentPath,node} = path;
let expressions = node.expressions;
if (parentPath.isReturnStatement({"argument":node}))
{
let lastExpression = expressions.pop();
for (let expression of expressions)
{
parentPath.insertBefore(types.ExpressionStatement(expression=expression));
}
path.replaceInline(lastExpression);
}
else if (parentPath.isExpressionStatement({"expression":node}))
{
let body = [];
expressions.forEach(express=>{body.push(types.ExpressionStatement(express));});
path.replaceWithMultiple(body);
}
else
{
return;
}
scope.crawl();
}
}
再把这个函数放到解析网站上
可以看到body下有三个node,主要处理第二个。请看下面的插件
//给构造好的z = funcion加一个var。也就是 var z= function
statements.push(types.variableDeclaration('var', [variableDeclaration]));
const moveFunctionBodyVisitor = {
FunctionDeclaration(path) {
const { params, body } = path.node;
const statements = [];
if (body.body.length == 3) {
body.body.forEach((statement) => {
if (types.isVariableDeclaration(statement)) {
statements.push(types.variableDeclaration(statement.kind, statement.declarations));
} else if (types.isExpressionStatement(statement)) {
const { expression } = statement;
if (types.isAssignmentExpression(expression)) {
const { left, right } = expression;
if (types.isIdentifier(left)) {
const variableDeclaration = types.variableDeclarator(left, right);
variableDeclaration.init.body.body.unshift(statements[0]);
statements.push(types.variableDeclaration('var', [variableDeclaration]));
}
}
} else if (!types.isReturnStatement(statement)) {
statements.push(statement);
}
});
statements.shift()
path.replaceWithMultiple(statements);
}
},
};
traverse(ast, moveFunctionBodyVisitor)
完整代码.js
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;
//将源代码解析为AST
var sourceCode = `
function q() {
const x = ["return (function() ", "51rDvFsO", "280bIrfll", "crossOrigin", "debu", "5573704jgYESE", "526422EOMPDB", "19pdzydt", "70220etHPRV", "26502443qIuDbf", '{}.constructor("return this")( )', "type", "string", "172742zcyDzi", "tagName", "include", "link", "LINK", "same-origin", "test", "function *\\( *\\)", "apply", "init", "386856yRDrIu", "addedNodes", 'link[rel="modulepreload"]', "length", "input", "stateObject", "modulepreload", "relList", "createElement", "supports", "10594465MEmbDB", "5JjJNqT", "setInterval", "querySelectorAll", "referrerPolicy", "credentials", "gger", "anonymous", "integrity", "observe", "action", "use-credentials", "constructor", "omit", "\\+\\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)"];
return q = function() {
return x
},
q()
}
function z(x, n) {
const e = q();
return z = function(t, r) {
return t = t - ( - 109 * -23 + -6806 + 4548),
e[t]
},
z(x, n)
}
`
let ast = parser.parse(sourceCode);
const returnArrayVisitor = {
FunctionDeclaration(path) {
var funcname = path.node.id.name;
if (funcname.length === 1 && path.node.body.body.length === 2) {
let body = path.node.body.body;
if (
types.isVariableDeclaration(body[0]) &&
types.isReturnStatement(body[1])
) {
let elements;
if (types.isArrayExpression(body[0].declarations[0].init)) {
elements = body[0].declarations[0].init.elements;
// 构造ArrayExpression节点作为返回值
const arrayExpression = types.arrayExpression(elements);
body[1] = types.returnStatement(arrayExpression);
body.splice(0, 1);
}
}
}
}
};
traverse(ast, returnArrayVisitor);
const sequenceVisitor =
{
SequenceExpression(path)
{
let {scope,parentPath,node} = path;
let expressions = node.expressions;
if (parentPath.isReturnStatement({"argument":node}))
{
let lastExpression = expressions.pop();
for (let expression of expressions)
{
parentPath.insertBefore(types.ExpressionStatement(expression=expression));
}
path.replaceInline(lastExpression);
}
else if (parentPath.isExpressionStatement({"expression":node}))
{
let body = [];
expressions.forEach(express=>{body.push(types.ExpressionStatement(express));});
path.replaceWithMultiple(body);
}
else
{
return;
}
scope.crawl();
}
}
traverse(ast, sequenceVisitor);
const moveFunctionBodyVisitor = {
FunctionDeclaration(path) {
const { params, body } = path.node;
const statements = [];
if (body.body.length === 3) {
body.body.forEach((statement) => {
if (types.isVariableDeclaration(statement)) {
statements.push(types.variableDeclaration(statement.kind, statement.declarations));
} else if (types.isExpressionStatement(statement)) {
const { expression } = statement;
if (types.isAssignmentExpression(expression)) {
const { left, right } = expression;
if (types.isIdentifier(left)) {
const variableDeclaration = types.variableDeclarator(left, right);
variableDeclaration.init.body.body.unshift(statements[0]);
statements.push(types.variableDeclaration('var', [variableDeclaration]));
}
}
} else if (!types.isReturnStatement(statement)) {
statements.push(statement);
}
});
statements.shift()
path.replaceWithMultiple(statements);
}
},
};
traverse(ast,moveFunctionBodyVisitor);
let decodeFile = "./encode_ok.js"
let { code } = generator(ast, opts = {
"compact": false,
"comments": false,
"jsescOption": { "minimal": true },
});
fs.writeFile(decodeFile, code, (err) => { });
剩余处理
var Z = E;
var E = function (t, r) {
var e = N();
t = t - 481;
var a = e[t];
return a;
};
function N() {
return ["action", "string", "2331990Smsoio", "length", "function *\( *\)", "call", "input", "stateObject", "counter", "930bExSFt", "savePosition", "while (true) {}", "chain", "98601tspbnR", "setInterval", "constructor", "10EjKgiA", "\+\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)", "value", "test", "mergedFrom", "init", "debu", "prototype", "56CjCzAS", "677128zAClZZ", "previousPosition", "75022iPEXCA", "15202JaLHoO", "apply", "581502egQFhJ", "gger", "531924HtjIlh", "51xGTVPz", "serialize"];
}
(function () {
for (var e = E, t = N();;) {
try {
var r = parseInt(e(494)) / 1 + -parseInt(e(508)) / 2 * (-parseInt(e(514)) / 3) + -parseInt(e(513)) / 4 * (-parseInt(e(497)) / 5) + -parseInt(e(511)) / 6 + -parseInt(e(505)) / 7 * (parseInt(e(506)) / 8) + parseInt(e(483)) / 9 + -parseInt(e(490)) / 10 * (parseInt(e(509)) / 11);
if (r === 358789) {
break;
}
t["push"](t["shift"]());
} catch {
t["push"](t["shift"]());
}
}
})();
var d0 = function () {
var x = true;
return function (n, e) {
var t = x ? function () {
var r = E;
if (e) {
var a = e[r(510)](n, arguments);
e = null;
return a;
}
} : function () {};
x = false;
return t;
};
}();
(function () {
d0(this, function () {
var x = E;
var n = new RegExp(x(485));
var e = new RegExp(x(498), "i");
var t = U(x(502));
!n[x(500)](t + x(493)) || !e[x(500)](t + x(487)) ? t("0") : U();
})();
})();
(function () {
var x = E;
try {
t = Function('return (function() {}.constructor("return this")( ));')();
} catch {
t = window;
}
var e = n();
e[x(495)](U, 1000);
})();
function j(x, n) {
var e = E;
this["x"] = x["x"];
this["y"] = x["y"];
this[e(499)] = n || 2;
this[e(507)] = null;
this[e(501)] = null;
}
j[Z(504)][Z(491)] = function () {
var x = Z;
this[x(507)] = {
"x": this["x"],
"y": this["y"]
};
};
j["prototype"]["updatePosition"] = function (x) {
this["x"] = x["x"];
this["y"] = x["y"];
};
j[Z(504)][Z(515)] = function () {
var x = Z;
return {
"position": {
"x": this["x"],
"y": this["y"]
},
"value": this[x(499)]
};
};
function U(x) {
function n(e) {
var t = E;
if (typeof e === t(482)) {
return function (r) {}[t(496)](t(492))[t(510)](t(489));
}
("" + e / e)[t(484)] !== 1 || e % 20 === 0 ? function () {
return true;
}[t(496)](t(503) + t(512))[t(486)](t(481)) : function () {
return false;
}[t(496)](t(503) + t(512))[t(510)](t(488));
n(++e);
}
try {
if (x) {
return n;
}
n(0);
} catch {}
}
我们想法是,直接计算出函数调用的结果,然后替换,在webstorm里看了下引用(赋值)的地方。
我的选择是把要调用的函数放到main.js里,然后碰到调用函数名长度为1函数且实参是一个数字,就执行。
var E = function (t, r) {
var e = N();
t = t - 481;
var a = e[t];
return a;
};
function N() {
return ["action", "string", "2331990Smsoio", "length", "function *\( *\)", "call", "input", "stateObject", "counter", "930bExSFt", "savePosition", "while (true) {}", "chain", "98601tspbnR", "setInterval", "constructor", "10EjKgiA", "\+\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)", "value", "test", "mergedFrom", "init", "debu", "prototype", "56CjCzAS", "677128zAClZZ", "previousPosition", "75022iPEXCA", "15202JaLHoO", "apply", "581502egQFhJ", "gger", "531924HtjIlh", "51xGTVPz", "serialize"];
}
const callToLiteral=
{
CallExpression(path)
{
let {callee,arguments} = path.node;
if (!types.isIdentifier(callee) || arguments.length !== 1)
{
return;
}
let name = callee.name;
// if(['x','r','x','t'].includes(name) && types.isNumericLiteral(arguments[0])){
// let value = A(arguments[0].value);
// path.replaceWith(types.valueToNode(value));
// }
if(['r','s','t','i','x','l','n'].includes(name) && types.isNumericLiteral(arguments[0])){
let value = T(arguments[0].value);
path.replaceWith(types.valueToNode(value));
}
}
}
再次给一下完整代码
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;
//将源代码解析为AST
var sourceCode = `
var Z = E;
var E = function (t, r) {
var e = N();
t = t - 481;
var a = e[t];
return a;
};
function N() {
return ["action", "string", "2331990Smsoio", "length", "function *\\( *\\)", "call", "input", "stateObject", "counter", "930bExSFt", "savePosition", "while (true) {}", "chain", "98601tspbnR", "setInterval", "constructor", "10EjKgiA", "\\+\\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)", "value", "test", "mergedFrom", "init", "debu", "prototype", "56CjCzAS", "677128zAClZZ", "previousPosition", "75022iPEXCA", "15202JaLHoO", "apply", "581502egQFhJ", "gger", "531924HtjIlh", "51xGTVPz", "serialize"];
}
(function () {
for (var e = E, t = N();;) {
try {
var r = parseInt(e(494)) / 1 + -parseInt(e(508)) / 2 * (-parseInt(e(514)) / 3) + -parseInt(e(513)) / 4 * (-parseInt(e(497)) / 5) + -parseInt(e(511)) / 6 + -parseInt(e(505)) / 7 * (parseInt(e(506)) / 8) + parseInt(e(483)) / 9 + -parseInt(e(490)) / 10 * (parseInt(e(509)) / 11);
if (r === 358789) {
break;
}
t["push"](t["shift"]());
} catch {
t["push"](t["shift"]());
}
}
})();
var d0 = function () {
var x = true;
return function (n, e) {
var t = x ? function () {
var r = E;
if (e) {
var a = e[r(510)](n, arguments);
e = null;
return a;
}
} : function () {};
x = false;
return t;
};
}();
(function () {
d0(this, function () {
var x = E;
var n = new RegExp(x(485));
var e = new RegExp(x(498), "i");
var t = U(x(502));
!n[x(500)](t + x(493)) || !e[x(500)](t + x(487)) ? t("0") : U();
})();
})();
(function () {
var x = E;
try {
t = Function('return (function() {}.constructor("return this")( ));')();
} catch {
t = window;
}
var e = n();
e[x(495)](U, 1000);
})();
function j(x, n) {
var e = E;
this["x"] = x["x"];
this["y"] = x["y"];
this[e(499)] = n || 2;
this[e(507)] = null;
this[e(501)] = null;
}
j[Z(504)][Z(491)] = function () {
var x = Z;
this[x(507)] = {
"x": this["x"],
"y": this["y"]
};
};
j["prototype"]["updatePosition"] = function (x) {
this["x"] = x["x"];
this["y"] = x["y"];
};
j[Z(504)][Z(515)] = function () {
var x = Z;
return {
"position": {
"x": this["x"],
"y": this["y"]
},
"value": this[x(499)]
};
};
function U(x) {
function n(e) {
var t = E;
if (typeof e === t(482)) {
return function (r) {}[t(496)](t(492))[t(510)](t(489));
}
("" + e / e)[t(484)] !== 1 || e % 20 === 0 ? function () {
return true;
}[t(496)](t(503) + t(512))[t(486)](t(481)) : function () {
return false;
}[t(496)](t(503) + t(512))[t(510)](t(488));
n(++e);
}
try {
if (x) {
return n;
}
n(0);
} catch {}
}
`
let ast = parser.parse(sourceCode);
const returnArrayVisitor = {
FunctionDeclaration(path) {
var funcname = path.node.id.name;
if (funcname.length === 1 && path.node.body.body.length === 2) {
let body = path.node.body.body;
if (
types.isVariableDeclaration(body[0]) &&
types.isReturnStatement(body[1])
) {
let elements;
if (types.isArrayExpression(body[0].declarations[0].init)) {
elements = body[0].declarations[0].init.elements;
// 构造ArrayExpression节点作为返回值
const arrayExpression = types.arrayExpression(elements);
body[1] = types.returnStatement(arrayExpression);
body.splice(0, 1);
}
}
}
}
};
traverse(ast, returnArrayVisitor);
const sequenceVisitor =
{
SequenceExpression(path)
{
let {scope,parentPath,node} = path;
let expressions = node.expressions;
if (parentPath.isReturnStatement({"argument":node}))
{
let lastExpression = expressions.pop();
for (let expression of expressions)
{
parentPath.insertBefore(types.ExpressionStatement(expression=expression));
}
path.replaceInline(lastExpression);
}
else if (parentPath.isExpressionStatement({"expression":node}))
{
let body = [];
expressions.forEach(express=>{body.push(types.ExpressionStatement(express));});
path.replaceWithMultiple(body);
}
else
{
return;
}
scope.crawl();
}
}
traverse(ast, sequenceVisitor);
const moveFunctionBodyVisitor = {
FunctionDeclaration(path) {
const { params, body } = path.node;
const statements = [];
if (body.body.length === 3) {
body.body.forEach((statement) => {
if (types.isVariableDeclaration(statement)) {
statements.push(types.variableDeclaration(statement.kind, statement.declarations));
} else if (types.isExpressionStatement(statement)) {
const { expression } = statement;
if (types.isAssignmentExpression(expression)) {
const { left, right } = expression;
if (types.isIdentifier(left)) {
const variableDeclaration = types.variableDeclarator(left, right);
variableDeclaration.init.body.body.unshift(statements[0]);
statements.push(types.variableDeclaration('var', [variableDeclaration]));
}
}
} else if (!types.isReturnStatement(statement)) {
statements.push(statement);
}
});
statements.shift()
path.replaceWithMultiple(statements);
}
},
};
traverse(ast,moveFunctionBodyVisitor);
var E = function (t, r) {
var e = N();
t = t - 481;
var a = e[t];
return a;
};
function N() {
return ["action", "string", "2331990Smsoio", "length", "function *\( *\)", "call", "input", "stateObject", "counter", "930bExSFt", "savePosition", "while (true) {}", "chain", "98601tspbnR", "setInterval", "constructor", "10EjKgiA", "\+\+ *(?:[a-zA-Z_$][0-9a-zA-Z_$]*)", "value", "test", "mergedFrom", "init", "debu", "prototype", "56CjCzAS", "677128zAClZZ", "previousPosition", "75022iPEXCA", "15202JaLHoO", "apply", "581502egQFhJ", "gger", "531924HtjIlh", "51xGTVPz", "serialize"];
}
const callToLiteral=
{
CallExpression(path)
{
let {callee,arguments} = path.node;
if (!types.isIdentifier(callee) || arguments.length !== 1)
{
return;
}
let name = callee.name;
if(['r','s','t','i','x','l','e','Z'].includes(name) && types.isNumericLiteral(arguments[0])){
let value = E(arguments[0].value);
path.replaceWith(types.valueToNode(value));
}
}
}
traverse(ast,callToLiteral)
let decodeFile = "./encode_ok.js"
let { code } = generator(ast, opts = {
"compact": false,
"comments": false,
"jsescOption": { "minimal": true },
});
fs.writeFile(decodeFile, code, (err) => { });
03
总结
附件:
原文始发于微信公众号(山石网科安全技术研究院):从一道Web游戏题学习AST反混淆技术