Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save patmigliaccio/3ff42251a1e4487be6408cd9b1112f9d to your computer and use it in GitHub Desktop.
Save patmigliaccio/3ff42251a1e4487be6408cd9b1112f9d to your computer and use it in GitHub Desktop.
patmigliaccio.com/client-side-security 7/27/17
/**
* Reverses basic obfuscation techniques used by the JavaScript Obfuscator.
*
* Reference: https://javascriptobfuscator.com/
*
* @param {string} data String representation of a JavaScript file
* @returns {string}
*/
function reverseObfuscation(data) {
return replaceArrayReferences(replaceHexArrayValues(replaceHexVariables(data)));
}
/**
* Replaces all hex values with `_0x` prefix, 4 characters following,
* and an optional `x#` value to letter(s) representation.
*
* e.g. `_0xcd03` -> `a` or `_0xea02x8` -> `ab`
*
* @param {string} data JavaScript file text
* @returns
*/
function replaceHexVariables(data) {
// Match all hex values
let matches = data.match(/_0x.{4}(x[0-9a-z]+)?/g);
// Filters out only unique hex values.
// Reference: https://stackoverflow.com/a/9229821/5199198
var detected = {};
matches
.filter(value => detected.hasOwnProperty(value) ? false : (detected[value] = true))
.forEach((hexValue, i) => {
// Replaces hex value with character
data = data.replace(new RegExp(hexValue, "g"), numberToLetters(i));
});
return data;
}
/**
* Converts hex values `\x??` found in arrays to their ASCII equivalent.
*
* e.g. `\x63` -> `c`
*
* @param {string} data JavaScript file text
* @returns {string}
*/
function replaceHexArrayValues(data) {
return data = data.replace(/\\x.{2}/g, value => String.fromCharCode(parseInt(value.substr(2), 16)));
}
/**
* Replaces array reference values with the property name stored in an array.
*
* e.g. `e[a[6]]()` -> `e['trim']()`
*
* @param {string} data JavaScript file text
*/
function replaceArrayReferences(data) {
let arrays = {};
// Extracts basic array declarations created by the obfuscator into the `arrays` object.
const arraySyntaxPattern = /var\s(.+)\s=\s(\[.+\]);/g;
data.replace(arraySyntaxPattern, (match, arrayName, arrayString) => {
// Be wary of the use of `eval` here
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
arrays[arrayName] = eval(arrayString);
});
// Replaces array references with the corresponding value from the `arrays` object.
const arrayReferenceSyntaxPattern = /([a-z]+)\[([0-9]+)\]/g;
data = data.replace(arrayReferenceSyntaxPattern, (match, arrayName, indexValue) => {
return Array.isArray(arrays[arrayName]) && arrays[arrayName][indexValue] !== undefined ? `'${arrays[arrayName][indexValue]}'` : match;
});
return data;
}
/**
* Converts integers to multi-character strings.
*
* e.g. `0` -> a or `26` -> `aa`
*
* Reference: https://stackoverflow.com/a/32007970/5199198
*
* @param {number} i Position of value
* @returns {string}
*/
function numberToLetters(i) {
return (i >= 26 ? numberToLetters((i / 26 >> 0) - 1) : '') + 'abcdefghijklmnopqrstuvwxyz'[i % 26 >> 0];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment