Skip to content

Instantly share code, notes, and snippets.

@vp777
Last active November 8, 2020 23:03
Show Gist options
  • Save vp777/6a1b04045af111a279f34eefcfb815f8 to your computer and use it in GitHub Desktop.
Save vp777/6a1b04045af111a279f34eefcfb815f8 to your computer and use it in GitHub Desktop.
babeljs plugin that unravels a number of common JS obfuscation patterns
/*
babeljs plugin that unravels a number of common obfuscation patterns (originally found in F5 JS obfuscation library).
tip: for better results you can feed the output of the below plugin to minify
usage: set the plugins in your .babelrc to the path of this script
What it does, it gets as input the following obfuscated code:
function toBeGone(s, u, m){
return Array.from(arguments).reduce((a,b)=>a+b, 0);
}
let arg=2;
let a=toBeGone(arg,6);
let b="\x41B\u0043";
let c=0x100;
let d1=(1>9,fff,3,0o20);
let d2=(1>9,fff(),3,0o20);
let e1=(1+Math.random())?"it's truthy":"it's falsy";
let e2=parseInt(256*Math.random(), 10);
and turns it into this:
function toBeGone(s, u, m) {
return arguments.reduce((a, b) => a + b, 0);
}
let arg = 2;
let a = 8;
let b = "ABC";
let c = 256;
let d1 = 16;
let d2 = (1 > 9, fff(), 3);
let e1 = "it's truthy";
let e2 = parseInt(256 * Math.random(), 10);
*/
const FUNCTION_WHITELIST = {
toBeGone(s, u, m) {
return Array.from(arguments).reduce((a, b) => a + b, 0);
}
}
/*
var arg=35;
func(arg);
arg++;
If you know that the above pattern is followed for FUNCTION_WHITELIST then you can set the below parameter to true.
*/
const FALLBACK_ASSIGNMENT_VALUE = false;
//Replace Math.random with 0.5 when it has a parent a control flow statement.
const SUBSTITUTE_MATH_RANDOM = true;
module.exports = function ({ types: t }) {
return {
visitor: {
"StringLiteral|NumericLiteral": function (path) {
if (path.node.hasOwnProperty("extra")) {
delete path.node["extra"];
}
},
CallExpression(path) {
const { node } = path;
const callee = node.callee;
const func = FUNCTION_WHITELIST[callee.name];
if (t.isIdentifier(callee) && func) {
fargs = [];
for (arg of path.get("arguments")) {
const res = arg.evaluate();
if (res.confident) fargs.push(res.value);
else if (FALLBACK_ASSIGNMENT_VALUE) {
try {
fargs.push(res.deopt.node.init.value)
} catch (err) {
return;
}
} else return;
}
path.replaceWith(t.valueToNode(func.apply(null, fargs)));
}
},
ConditionalExpression: {
exit(path) {
const res = path.get("test").evaluate();
if (res.confident) {
path.replaceWith(
res.value ? path.node.consequent : path.node.alternate
);
}
}
},
BinaryExpression: {
enter(path) {
if (!SUBSTITUTE_MATH_RANDOM) return;
if (!["ConditionalExpression", "IfStatement", "LogicalExpression"].some(type => path.parentPath.isNodeType(type))) return;
const { node } = path;
let currentBranch, currentCallee;
for (currentDirection of ["left", "right"]) {
currentBranch = path.get(currentDirection);
if (t.isCallExpression(currentBranch) && t.isMemberExpression(currentCallee = currentBranch.node.callee)) {
const object = currentCallee.object;
const property = currentCallee.property;
if (object.name === "Math" && property.name === "random") {
currentBranch.replaceWith(t.numericLiteral(0.5));
}
}
}
}
},
SequenceExpression: {
exit(path) {
const lastExpression = path.node.expressions.pop();
let stateChangerExpr = false;
path.traverse({
"AssignmentExpression|UpdateExpression|CallExpression": function (path) {
stateChangerExpr = true;
path.stop()
}
});
if (stateChangerExpr) return;
path.replaceWith(lastExpression);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment