Files
reverse-skills-max/vendor-new/xbsReverseSkill/ast-deobfuscation/scripts/patterns/tonghuashun-order-pass.js

190 lines
5.1 KiB
JavaScript

const { runPatternPass } = require("./shared-pattern-pass");
const {
createSandbox,
evaluateNodes,
foldWrapperObjects,
inlineStringMethodCalls,
seedSandboxFromAliases,
traverse,
t
} = require("./pattern-utils");
function getDecoderPreludeNodes(ast) {
const topLevelDeclarator = ast.program.body
.filter((node) => t.isVariableDeclaration(node))
.flatMap((node) => node.declarations)
.find((decl) =>
t.isIdentifier(decl.id) &&
(
t.isFunctionExpression(decl.init) ||
(t.isCallExpression(decl.init) && t.isFunctionExpression(decl.init.callee))
)
);
const rootFunction = !topLevelDeclarator ? null :
t.isFunctionExpression(topLevelDeclarator.init) ? topLevelDeclarator.init :
t.isCallExpression(topLevelDeclarator.init) && t.isFunctionExpression(topLevelDeclarator.init.callee) ? topLevelDeclarator.init.callee :
null;
if (!rootFunction || !t.isBlockStatement(rootFunction.body)) {
return [];
}
const nodes = [];
for (const stmt of rootFunction.body.body) {
if (t.isFunctionDeclaration(stmt) && stmt.id && (stmt.id.name === "t" || stmt.id.name === "u")) {
nodes.push(stmt);
continue;
}
if (t.isVariableDeclaration(stmt) && stmt.declarations.every((decl) => t.isIdentifier(decl.id) && decl.init == null)) {
nodes.push(stmt);
continue;
}
if (t.isExpressionStatement(stmt)) {
nodes.push(stmt);
break;
}
}
return nodes;
}
function tonghuashunOrderPass(ast) {
let changed = false;
let localChanged = false;
const sandbox = createSandbox();
const preludeNodes = getDecoderPreludeNodes(ast);
if (preludeNodes.length > 0 && evaluateNodes(preludeNodes, sandbox, 2000)) {
while (seedSandboxFromAliases(ast, sandbox)) {
changed = true;
}
}
const decoderAliasNames = collectDecoderAliasNames(ast, ["t"]);
do {
localChanged = false;
if (replaceDecoderAliasCalls(ast, sandbox, decoderAliasNames)) {
localChanged = true;
changed = true;
}
if (inlineStringMethodCalls(ast)) {
localChanged = true;
changed = true;
}
const foldResult = foldWrapperObjects(ast, {
ignoreDynamicUsage: true
});
if (foldResult.changed) {
localChanged = true;
changed = true;
}
if (inlineStringMethodCalls(ast)) {
localChanged = true;
changed = true;
}
} while (localChanged);
return { ast, changed };
}
function collectDecoderAliasNames(ast, seedNames) {
const aliasNames = new Set(seedNames);
let expanded = false;
do {
expanded = false;
traverse(ast, {
VariableDeclarator(path) {
if (!path.get("id").isIdentifier() || !path.get("init").isIdentifier()) {
return;
}
const sourceName = path.get("init").node.name;
const aliasName = path.get("id").node.name;
if (!aliasNames.has(sourceName) || aliasNames.has(aliasName)) {
return;
}
aliasNames.add(aliasName);
expanded = true;
},
AssignmentExpression(path) {
if (!path.get("left").isIdentifier() || !path.get("right").isIdentifier()) {
return;
}
const sourceName = path.get("right").node.name;
const aliasName = path.get("left").node.name;
if (!aliasNames.has(sourceName) || aliasNames.has(aliasName)) {
return;
}
aliasNames.add(aliasName);
expanded = true;
}
});
} while (expanded);
return aliasNames;
}
function nodeFromPrimitiveValue(value) {
if (typeof value === "undefined") {
return t.identifier("undefined");
}
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return t.valueToNode(value);
}
return null;
}
function replaceDecoderAliasCalls(ast, sandbox, aliasNames) {
if (typeof sandbox.t !== "function" || aliasNames.size === 0) {
return false;
}
let changed = false;
traverse(ast, {
CallExpression(path) {
const calleePath = path.get("callee");
let isDecoderAliasCall = false;
if (calleePath.isIdentifier()) {
isDecoderAliasCall = aliasNames.has(calleePath.node.name);
} else if (
calleePath.isAssignmentExpression({ operator: "=" }) &&
calleePath.get("left").isIdentifier() &&
calleePath.get("right").isIdentifier()
) {
isDecoderAliasCall = aliasNames.has(calleePath.get("right").node.name);
}
if (!isDecoderAliasCall) {
return;
}
if (path.node.arguments.length !== 1 || !path.get("arguments.0").isNumericLiteral()) {
return;
}
let value;
try {
value = sandbox.t(path.get("arguments.0").node.value);
} catch (error) {
return;
}
const literalNode = nodeFromPrimitiveValue(value);
if (!literalNode) {
return;
}
path.replaceWith(literalNode);
changed = true;
}
});
return changed;
}
runPatternPass(tonghuashunOrderPass);