Files
reverse-skills-max/vendor-new/xbsReverseSkill/ast-deobfuscation/scripts/inline-dispatchers.js

131 lines
4.7 KiB
JavaScript

const { clone, parseArgs, parseFile, reparse, saveAst, t, traverse } = require("./shared");
function collectDispatcherValue(node, dispatcherMap) {
if (t.isStringLiteral(node) || t.isNumericLiteral(node) || t.isBooleanLiteral(node)) {
return { type: "literal", value: clone(node) };
}
if (t.isMemberExpression(node) && t.isIdentifier(node.object) && !node.computed && t.isIdentifier(node.property)) {
return { type: "member", objectName: node.object.name, key: node.property.name };
}
if (t.isFunctionExpression(node) && node.body.body.length === 1 && t.isReturnStatement(node.body.body[0])) {
const arg = node.body.body[0].argument;
if (t.isBinaryExpression(arg) && t.isIdentifier(arg.left) && t.isIdentifier(arg.right)) {
return { type: "binary", operator: arg.operator };
}
if (t.isLogicalExpression(arg) && t.isIdentifier(arg.left) && t.isIdentifier(arg.right)) {
return { type: "logical", operator: arg.operator };
}
if (t.isCallExpression(arg) && t.isIdentifier(arg.callee)) {
return { type: "call" };
}
if (t.isCallExpression(arg) && t.isMemberExpression(arg.callee) && t.isIdentifier(arg.callee.object) && !arg.callee.computed && t.isIdentifier(arg.callee.property)) {
const target = dispatcherMap[arg.callee.object.name] && dispatcherMap[arg.callee.object.name][arg.callee.property.name];
if (target && (target.type === "binary" || target.type === "logical" || target.type === "call")) {
return target;
}
}
}
return null;
}
function inlineDispatchers(ast) {
let changed = false;
const dispatcherMap = Object.create(null);
const removableEntries = [];
traverse(ast, {
VariableDeclarator(path) {
if (!path.get("id").isIdentifier() || !path.get("init").isObjectExpression()) {
return;
}
const name = path.node.id.name;
const collected = dispatcherMap[name] || Object.create(null);
let localChanged = false;
let supportedPropertyCount = 0;
let collectedPropertyCount = 0;
path.get("init.properties").forEach(propertyPath => {
if (!propertyPath.isObjectProperty()) {
return;
}
supportedPropertyCount += 1;
const keyNode = propertyPath.node.key;
const key = t.isIdentifier(keyNode) ? keyNode.name : keyNode.value;
const value = collectDispatcherValue(propertyPath.node.value, dispatcherMap);
if (value) {
collected[key] = value;
localChanged = true;
collectedPropertyCount += 1;
}
});
if (localChanged) {
dispatcherMap[name] = collected;
removableEntries.push({
path,
removable: supportedPropertyCount > 0 && supportedPropertyCount === collectedPropertyCount
});
}
}
});
traverse(ast, {
CallExpression(path) {
if (!path.get("callee").isMemberExpression()) {
return;
}
const objectName = path.node.callee.object.name;
const key = path.node.callee.computed ? path.node.callee.property.value : path.node.callee.property.name;
const entry = dispatcherMap[objectName] && dispatcherMap[objectName][key];
if (!entry) {
return;
}
const args = path.node.arguments.map(arg => clone(arg));
if (entry.type === "binary" && args.length >= 2) {
path.replaceWith(t.binaryExpression(entry.operator, args[0], args[1]));
changed = true;
} else if (entry.type === "logical" && args.length >= 2) {
path.replaceWith(t.logicalExpression(entry.operator, args[0], args[1]));
changed = true;
} else if (entry.type === "call" && args.length >= 1) {
path.replaceWith(t.callExpression(args[0], args.slice(1)));
changed = true;
}
},
MemberExpression(path) {
if (!path.get("object").isIdentifier()) {
return;
}
const objectName = path.node.object.name;
const key = path.node.computed ? path.node.property.value : path.node.property.name;
const entry = dispatcherMap[objectName] && dispatcherMap[objectName][key];
if (!entry) {
return;
}
if (entry.type === "literal") {
path.replaceWith(clone(entry.value));
changed = true;
}
}
});
if (changed) {
removableEntries.forEach(({ path, removable }) => {
if (removable && !path.removed) {
path.remove();
}
});
}
return { ast, changed };
}
const { inputPath, outputPath } = parseArgs();
let ast = parseFile(inputPath);
let changed = false;
do {
({ ast, changed } = inlineDispatchers(ast));
if (changed) {
ast = reparse(ast);
}
} while (changed);
saveAst(ast, outputPath);