reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
const { clone } = require("../shared");
|
||||
const { runPatternPass } = require("./shared-pattern-pass");
|
||||
const { createSandbox, evaluateExpression, evaluateNodes, foldWrapperObjects, traverse, t } = require("./pattern-utils");
|
||||
|
||||
function getOpcodeTestInfo(testPath) {
|
||||
if (!testPath.isBinaryExpression()) {
|
||||
return null;
|
||||
}
|
||||
if (!["===", "=="].includes(testPath.node.operator)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (testPath.get("left").isNumericLiteral() && testPath.get("right").isIdentifier()) {
|
||||
return {
|
||||
discriminant: clone(testPath.get("right").node),
|
||||
discriminantName: testPath.get("right").node.name,
|
||||
caseValue: clone(testPath.get("left").node)
|
||||
};
|
||||
}
|
||||
|
||||
if (testPath.get("right").isNumericLiteral() && testPath.get("left").isIdentifier()) {
|
||||
return {
|
||||
discriminant: clone(testPath.get("left").node),
|
||||
discriminantName: testPath.get("left").node.name,
|
||||
caseValue: clone(testPath.get("right").node)
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function blockify(node) {
|
||||
if (!node) {
|
||||
return [];
|
||||
}
|
||||
if (t.isBlockStatement(node)) {
|
||||
return node.body.map((stmt) => clone(stmt));
|
||||
}
|
||||
return [clone(node)];
|
||||
}
|
||||
|
||||
function withTrailingBreak(statements) {
|
||||
if (statements.length === 0) {
|
||||
return [t.breakStatement()];
|
||||
}
|
||||
const last = statements[statements.length - 1];
|
||||
if (t.isBreakStatement(last) || t.isReturnStatement(last) || t.isThrowStatement(last) || t.isContinueStatement(last)) {
|
||||
return statements;
|
||||
}
|
||||
return [...statements, t.breakStatement()];
|
||||
}
|
||||
|
||||
function collectOpcodeCasesFromAlternate(node, discriminantName, cases) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (t.isIfStatement(node)) {
|
||||
const info = getOpcodeTestInfo({
|
||||
isBinaryExpression: () => t.isBinaryExpression(node.test),
|
||||
node: node.test,
|
||||
get(key) {
|
||||
if (key === "left") {
|
||||
return {
|
||||
isNumericLiteral: () => t.isNumericLiteral(node.test.left),
|
||||
isIdentifier: () => t.isIdentifier(node.test.left),
|
||||
node: node.test.left
|
||||
};
|
||||
}
|
||||
return {
|
||||
isNumericLiteral: () => t.isNumericLiteral(node.test.right),
|
||||
isIdentifier: () => t.isIdentifier(node.test.right),
|
||||
node: node.test.right
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
if (info && info.discriminantName === discriminantName) {
|
||||
cases.push(t.switchCase(info.caseValue, withTrailingBreak(blockify(node.consequent))));
|
||||
collectOpcodeCasesFromAlternate(node.alternate, discriminantName, cases);
|
||||
return;
|
||||
}
|
||||
|
||||
cases.push(t.switchCase(null, withTrailingBreak(blockify(node))));
|
||||
return;
|
||||
}
|
||||
|
||||
if (t.isBlockStatement(node)) {
|
||||
node.body.forEach((stmt) => {
|
||||
if (t.isIfStatement(stmt)) {
|
||||
const info = getOpcodeTestInfo({
|
||||
isBinaryExpression: () => t.isBinaryExpression(stmt.test),
|
||||
node: stmt.test,
|
||||
get(key) {
|
||||
if (key === "left") {
|
||||
return {
|
||||
isNumericLiteral: () => t.isNumericLiteral(stmt.test.left),
|
||||
isIdentifier: () => t.isIdentifier(stmt.test.left),
|
||||
node: stmt.test.left
|
||||
};
|
||||
}
|
||||
return {
|
||||
isNumericLiteral: () => t.isNumericLiteral(stmt.test.right),
|
||||
isIdentifier: () => t.isIdentifier(stmt.test.right),
|
||||
node: stmt.test.right
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
if (info && info.discriminantName === discriminantName) {
|
||||
cases.push(t.switchCase(info.caseValue, withTrailingBreak(blockify(stmt.consequent))));
|
||||
collectOpcodeCasesFromAlternate(stmt.alternate, discriminantName, cases);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cases.push(t.switchCase(null, withTrailingBreak([clone(stmt)])));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
cases.push(t.switchCase(null, withTrailingBreak([clone(node)])));
|
||||
}
|
||||
|
||||
function convertOpcodeIfChains(ast) {
|
||||
let changed = false;
|
||||
|
||||
traverse(ast, {
|
||||
IfStatement(path) {
|
||||
const info = getOpcodeTestInfo(path.get("test"));
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cases = [
|
||||
t.switchCase(info.caseValue, withTrailingBreak(blockify(path.node.consequent)))
|
||||
];
|
||||
collectOpcodeCasesFromAlternate(path.node.alternate, info.discriminantName, cases);
|
||||
path.replaceWith(t.switchStatement(info.discriminant, cases));
|
||||
path.skip();
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
function stripPseudoForLoops(ast) {
|
||||
let changed = false;
|
||||
|
||||
traverse(ast, {
|
||||
ForStatement(path) {
|
||||
const testPath = path.get("test");
|
||||
if (
|
||||
!testPath.isBinaryExpression() ||
|
||||
path.node.update !== null ||
|
||||
!(
|
||||
testPath.get("left").isNumericLiteral() ||
|
||||
testPath.get("right").isNumericLiteral()
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.node.init) {
|
||||
path.replaceWith(clone(path.node.init));
|
||||
} else {
|
||||
path.remove();
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
function xiaohongshuWrapperPass(ast) {
|
||||
let changed = false;
|
||||
const body = ast.program.body;
|
||||
|
||||
if (
|
||||
body.length >= 3 &&
|
||||
body[1].type === "VariableDeclaration" &&
|
||||
body[1].declarations.length > 0 &&
|
||||
t.isIdentifier(body[1].declarations[0].id)
|
||||
) {
|
||||
const sandbox = createSandbox();
|
||||
const decoderName = body[1].declarations[0].id.name;
|
||||
|
||||
if (evaluateNodes(body.slice(0, 3), sandbox) && typeof sandbox[decoderName] === "function") {
|
||||
ast.program.body = body.slice(3).map((node) => clone(node));
|
||||
changed = true;
|
||||
|
||||
const aliasNames = new Set([decoderName]);
|
||||
let progress = false;
|
||||
do {
|
||||
progress = false;
|
||||
|
||||
traverse(ast, {
|
||||
VariableDeclarator(path) {
|
||||
if (!path.get("id").isIdentifier() || !path.get("init").isIdentifier()) {
|
||||
return;
|
||||
}
|
||||
if (aliasNames.has(path.node.init.name) && !aliasNames.has(path.node.id.name)) {
|
||||
aliasNames.add(path.node.id.name);
|
||||
progress = true;
|
||||
}
|
||||
},
|
||||
CallExpression(path) {
|
||||
if (!path.get("callee").isIdentifier() || !aliasNames.has(path.node.callee.name) || path.node.arguments.length !== 1) {
|
||||
return;
|
||||
}
|
||||
const result = evaluateExpression(`${decoderName}(${path.get("arguments.0").toString()})`, sandbox);
|
||||
if (!result.ok) {
|
||||
return;
|
||||
}
|
||||
path.replaceWith(t.valueToNode(result.value));
|
||||
progress = true;
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
} while (progress);
|
||||
|
||||
traverse(ast, {
|
||||
VariableDeclarator(path) {
|
||||
if (!path.get("id").isIdentifier() || !path.get("init").isIdentifier()) {
|
||||
return;
|
||||
}
|
||||
if (aliasNames.has(path.node.id.name) && aliasNames.has(path.node.init.name)) {
|
||||
path.remove();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let localChanged = false;
|
||||
do {
|
||||
({ changed: localChanged } = foldWrapperObjects(ast));
|
||||
if (localChanged) {
|
||||
changed = true;
|
||||
}
|
||||
} while (localChanged);
|
||||
|
||||
if (convertOpcodeIfChains(ast)) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (stripPseudoForLoops(ast)) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return { ast, changed };
|
||||
}
|
||||
|
||||
runPatternPass(xiaohongshuWrapperPass);
|
||||
Reference in New Issue
Block a user