Skip to content

Instantly share code, notes, and snippets.

@msm-code
Created April 25, 2023 00:08
Show Gist options
  • Save msm-code/c0e401753ee5a8857ebe291265777bc1 to your computer and use it in GitHub Desktop.
Save msm-code/c0e401753ee5a8857ebe291265777bc1 to your computer and use it in GitHub Desktop.
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generate = require("@babel/generator").default;
const beautify = require("js-beautify");
const { readFileSync, writeFile } = require("fs");
const vm = require("vm");
const decryptFuncCtx = vm.createContext();
const constantFold = {
"BinaryExpression|UnaryExpression"(path) {
const { node } = path;
if (
t.isUnaryExpression(node) &&
(node.operator == "-" || node.operator == "void")
)
return;
let { confident, value } = path.evaluate();
if (!confident || value == Infinity || value == -Infinity) return;
let actualVal = t.valueToNode(value);
path.replaceWith(actualVal);
},
};
const deobfuscateEncodedStringVisitor = {
StringLiteral(path) {
if (path.node.extra) delete path.node.extra;
},
};
const splitDeclarators = {
VariableDeclaration(path) {
if (path.node.kind === 'var' || path.node.kind === 'let' || path.node.kind === 'const') {
const declarators = path.node.declarations;
if (declarators.length > 1) {
declarators.forEach(declarator => {
const declaration = t.variableDeclaration(path.node.kind, [declarator]);
path.insertBefore(declaration);
});
path.remove();
}
}
}
};
function maybeAddToScope(path, funcName, vm, ctx) {
var bind = path.scope.getBinding(funcName);
if (bind) {
vm.runInContext(generate(bind.path.node).code, ctx);
}
}
const callAndApplyToDirectCall = {
CallExpression(path) {
const node = path.node;
const callee = node.callee;
if (callee.type == "MemberExpression") {
if (callee.property.name == "call") {
const args = path.node.arguments.slice(1);
const newT = t.callExpression(t.identifier(callee.object.name), args);
path.replaceWith(newT);
}
else if (callee.property.name == "apply") {
const args = path.node.arguments.slice(1);
const newT = t.callExpression(t.identifier(callee.object.name), args[0].elements);
path.replaceWith(newT);
}
}
},
};
const deobfuscateEncryptedStringsVisitor = {
CallExpression(path) {
const node = path.node;
const toReplace = ["pzgbK9", "WbNnBx3", "_NCrEQ", "jqgmog", "HHFFUl", "CLlOtS", "MCcmnR", "Wk5pGK3", "X4Stmgr"];
if (toReplace.includes(node.callee.name)) {
var bind = path.scope.getBinding(node.callee.name);
console.log(node.callee.name, bind.path.type);
if (["VariableDeclarator", "FunctionDeclaration"].includes(bind.path.type)) {
const decryptFuncCode = generate(bind.path.node).code;
const expressionCode = generate(node).code;
try {
const decryptFuncCtx = vm.createContext();
vm.runInContext(readFileSync("./context.js", "utf8"), decryptFuncCtx)
vm.runInContext(decryptFuncCode, decryptFuncCtx);
maybeAddToScope(path, "MCcmnR", vm, decryptFuncCtx);
const value = vm.runInContext(expressionCode, decryptFuncCtx);
path.replaceWith(t.valueToNode(value));
} catch {
}
}
}
},
};
const deobfuscateStringConcatVisitor = {
BinaryExpression(path) {
let { confident, value } = path.evaluate(); // Evaluate the binary expression
if (!confident) return; // Skip if not confident
if (typeof value == "string") {
path.replaceWith(t.stringLiteral(value)); // Substitute the simplified value
}
},
};
const bracketToDotVisitor = {
MemberExpression(path) {
const validIdentifierRegex = /^[a-zA-Z0-9_]+$/;
let { object, property, computed } = path.node;
if (!computed) return; // Verify computed property is false
if (!t.isStringLiteral(property)) return; // Verify property is a string literal
if (!validIdentifierRegex.test(property.value)) return; // Verify that the property being accessed is a valid identifier
// If conditions pass:
// Replace the node with a new one
path.replaceWith(
t.MemberExpression(object, t.identifier(property.value), false)
);
},
};
const replaceStringConstants = {
Identifier(path) {
const { node } = path;
const { name } = node;
// Check if the identifier is a constant variable
// if (node.name != "IOTEST") {
// return;
// }
// Get the value of the constant variable
const binding = path.scope.getBinding(name);
if (!binding ||
binding.path.node.type != "VariableDeclarator"
) {
return;
}
let constVal = undefined;
if (binding.path.node.init && binding.path.node.init.type == "StringLiteral") {
constVal = binding.path.node.init.value;
} else if (binding.constantViolations.length == 1) {
if (binding.constantViolations[0].node.right) {
// console.log("x", binding.constantViolations)
// console.log("x", binding.constantViolations[0].node.right.value)
constVal = binding.constantViolations[0].node.right.value;
}
}
if (constVal === undefined) {
return;
}
const alloweds = ["LogicalExpression", "MemberExpression", "BinaryExpression", "UnaryExpression", "ObjectProperty"]
if (!alloweds.includes(path.parent.type)) {
return;
}
console.log("replacing", node.name, "with", constVal);
path.replaceWith(t.valueToNode(constVal));
}
};
const reparseAst = (ast) => {
let x = generate(ast, { comments: false }).code;
return parser.parse(x);
};
function deobfuscate(source) {
var ast = parser.parse(source);
traverse(ast, constantFold);
traverse(ast, deobfuscateEncodedStringVisitor);
ast = reparseAst(ast);
traverse(ast, splitDeclarators);
ast = reparseAst(ast);
traverse(ast, callAndApplyToDirectCall );
ast = reparseAst(ast);
traverse(ast, deobfuscateEncryptedStringsVisitor);
ast = reparseAst(ast);
traverse(ast, deobfuscateEncryptedStringsVisitor);
ast = reparseAst(ast);
traverse(ast, replaceStringConstants);
traverse(ast, deobfuscateStringConcatVisitor);
traverse(ast, bracketToDotVisitor);
let deobfCode = generate(ast, { comments: false }).code;
deobfCode = require("@babel/core").transformSync(deobfCode, {
plugins: ["babel-plugin-remove-unused-vars"]
}).code;
deobfCode = beautify(deobfCode, {
indent_size: 2,
space_in_empty_paren: true,
});
writeCodeToFile(deobfCode);
}
function writeCodeToFile(code) {
let outputPath = "output.js";
writeFile(outputPath, code, (err) => {
if (err) {
console.log("Error writing file", err);
} else {
console.log(`Wrote file to ${outputPath}`);
}
});
}
deobfuscate(readFileSync("./b.js", "utf8"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment