I deobfuscated PerimeterX’s bot detection JavaScript using the shift-refactor library.
- Input file (
perimeterx.js
): Original, Mirror. -
Deobfuscator Code (
perimeterx_deobfuscator.js
):const { RefactorSession } = require('shift-refactor'); const { parseScript } = require('shift-parser'); const Shift = require('shift-ast'); const fileContents = require('fs').readFileSync('./perimeterx.js', 'utf8'); const tree = parseScript(fileContents); const refactor = new RefactorSession(tree); /* String deobfuscation functions, copied from perimeterx.js. */ var lf = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var vf = /[^+\/=0-9A-Za-z]/; function atob(a) { return new Buffer(a, 'base64').toString('binary'); }; function btoa(b) { return new Buffer(b).toString('base64'); }; function ot(t) { return it(t) } function it(t) { var n = [], e = void 0, r = void 0, o = void 0, i = 0, a = void 0, c = t.length; if (vf.test(t) || /=/.test(t) && (/=[^=]/.test(t) || /={3}/.test(t))) return null; for (c % 4 > 0 && (t += Array(4 - c % 4 + 1).join("="), c = t.length); i < c;) { for (r = [], a = i; i < a + 4;) r.push(lf.indexOf(t.charAt(i++))); for (e = (r[0] << 18) + (r[1] << 12) + ((63 & r[2]) << 6) + (63 & r[3]), o = [(e & 255 << 16) >> 16, 64 === r[2] ? -1 : (65280 & e) >> 8, 64 === r[3] ? -1 : 255 & e], a = 0; a < 3; ++a)(o[a] >= 0 || 0 === a) && n.push(String.fromCharCode(o[a])) } return n.join("") } function f(f) { for (var n = atob(f), r = n.charCodeAt(0), t = "", i = 1; i < n.length; ++i) t += String.fromCharCode(r ^ n.charCodeAt(i)); return t; } function n(r) { var t = f; return (n = "function" == typeof Symbol && typeof Symbol.iterator === t("+omDl5iVlg") ? function (f) { return typeof f; } : function (n) { var r = f; return n && "function" == typeof Symbol && n.constructor === Symbol && n !== Symbol.prototype ? r("3q2ns7yxsg") : typeof n; })(r); } function r(f, n) { return (r = Object.setPrototypeOf || function (f, n) { return f.__proto__ = n, f; })(f, n); } function t(f, n, i) { return (t = function () { if ("undefined" == typeof Reflect || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if ("function" == typeof Proxy) return true; try { return Date.prototype.toString.call(Reflect.construct(Date, [], function () {})), true; } catch (f) { return false; } }() ? Reflect.construct : function (f, n, t) { var i = [null]; i.push.apply(i, n); var c = new (Function.bind.apply(f, i)); return t && r(c, t.prototype), c; }).apply(null, arguments); } function i(n, r) { return function (f) { if (Array.isArray(f)) return f; }(n) || function (n, r) { var t = f, i = [], c = true, e = false, a = void 0; try { for (var o, u = n[Symbol.iterator](); !(c = (o = u.next()).done) && (i.push(o.value), !r || i.length !== r); c = true) ; } catch (f) { e = true, a = f; } finally { try { c || null == u[t("HG55aGlucg")] || u[t("VyUyIyIlOQ")](); } finally { if (e) throw a; } } return i; }(n, r) || function () { throw new TypeError(f("XhcwKD8yNzp+PyoqOzMuKn4qMX46Oy0qLCs9KissO34wMTBzNyo7LD88Mjt+NzAtKj8wPTs")); }(); } function c(n) { return function (f) { if (Array.isArray(f)) { for (var n = 0, r = new Array(f.length); n < f.length; n++) r[n] = f[n]; return r; } }(n) || function (n) { var r = f; if (Symbol.iterator in Object(n) || Object.prototype.toString.call(n) === r("aDMHCgINCxxIKRoPHQUNBhwbNQ")) return Array.from(n); }(n) || function () { throw new TypeError(f("oOnO1sHMycSAwdTUxc3Q1IDUz4DT0NLFwcSAzs/OjcnUxdLBwszFgMnO09TBzsPF")); }(); } /* End copied string deobfuscation functions */ /* Replace ot calls with real values */ refactor.replaceRecursive( `CallExpression[callee.type="IdentifierExpression"][callee.name="ot"][arguments.length=1][arguments.0.type="LiteralStringExpression"]`, node => { return new Shift.LiteralStringExpression({ value: ot(node.arguments[0].value) }) } ); console.log("/* Replaced ot calls with real values */") /* Replace f calls with real values */ refactor.replaceRecursive( `CallExpression[callee.type="IdentifierExpression"][callee.name="f"][arguments.length=1][arguments.0.type="LiteralStringExpression"]`, node => { return new Shift.LiteralStringExpression({ value: f(node.arguments[0].value) }) } ); console.log("/* Replaced f calls with real values */") /* Replace r calls with real values */ refactor.replaceRecursive( `CallExpression[callee.type="IdentifierExpression"][callee.name="r"][arguments.length=1][arguments.0.type="LiteralStringExpression"]`, node => { return new Shift.LiteralStringExpression({ value: f(node.arguments[0].value) }) } ); console.log("/* Replaced r calls with real values */") /* Replace e calls with real values */ refactor.replaceRecursive( `CallExpression[callee.type="IdentifierExpression"][callee.name="e"][arguments.length=1][arguments.0.type="LiteralStringExpression"]`, node => { return new Shift.LiteralStringExpression({ value: f(node.arguments[0].value) }) } ); console.log("/* Replaced e calls with real values */") /* Replace f referenced calls with real values */ refactor.replace( `CallExpression[callee.type="IdentifierExpression"][arguments.length=1][arguments.0.type="LiteralStringExpression"]`, node => { var functionName = node.callee.name; var query = `VariableDeclarator[init.type="IdentifierExpression"][init.name="f"][binding.name="` + functionName + `"]`; if (refactor.query(query).length > 0) { return new Shift.LiteralStringExpression({ value: f(node.arguments[0].value) }) } else { return node; } } ); console.log("/* Replaced f referenced calls with real values */") /* Replace r referenced calls with real values */ refactor.replace( `CallExpression[callee.type="IdentifierExpression"][arguments.length=1][arguments.0.type="LiteralStringExpression"]`, node => { var functionName = node.callee.name; var query = `VariableDeclarator[init.type="IdentifierExpression"][init.name="r"][binding.name="` + functionName + `"]`; if (refactor.query(query).length > 0) { return new Shift.LiteralStringExpression({ value: f(node.arguments[0].value) }) } else { return node; } } ); console.log("/* Replaced r referenced calls with real values */") refactor.expandBoolean(); refactor.normalizeIdentifiers(); console.log("/* This file is the result of running `node perimeterx_deobfuscator.js > perimeterx_deobfuscated.js` */"); console.log(refactor.print());
- Deobfuscated result: http://ix.io/2t5R