code

Maximize JS

JavaScript source maps are really cool. They allow people to ship minified code while retaining the ability to map back to the original source code in development tools like browser debuggers. There are three pieces to a full source map experience when debugging in browsers:

  • The minified script, which the browser has already
  • The source map, which the browser can get from a comment at the end of the minified script
  • The original script, which the browser can get from the source map

The source map and original script are also retrieved from the web server. If you are missing either the source map or the original script, existing tools give up and only show the minified script. I had a theory that the source map contained enough data by itself to make a decent reproduction of the original script. Comments will be missing, whitespace won’t be the same, etc. However, the code would very readable, and details such as what libraries have been used should be evident.

I decided to test the theory by making Maximize. Maximize takes a URL to a script hosted on a website and outputs a deobfuscated, beautified version of the script, assuming the source map is available. For example, this is the beginning of the minified script on fontdragr.com:

"use strict";(function(e,n,t){function r(e){return String.fromCharCode(e)}function i(e){return e&&"number"==typeof e.length?"function"!=typeof e.hasOwnProperty&&"function"!=typeof e.constructor?!0:e instanceof an||Yt&&e instanceof Yt||"[object Object]"!==er.call(e)||"function"==typeof e.callee:!1}function o(e,n,t){var r;if(e)if(k(e))for(r in e)"prototype"!=r&&"length"!=r&&"name"!=r&&e.hasOwnProperty(r)&&n.call(t,e[r],r);

After maximizing, the maximized script starts to look like:

"use strict";
(function(window, document, undefined) {
    function fromCharCode(code) {
        return String.fromCharCode(code)
    }

    function isArrayLike(obj) {
        return obj && "number" == typeof obj.length ? "function" != typeof obj.hasOwnProperty && "function" != typeof obj.constructor ? !0 : obj instanceof JQLite || jQuery && obj instanceof jQuery || "[object Object]" !== toString.call(obj) || "function" == typeof obj.callee : !1
    }

    function forEach(obj, iterator, context) {
        var key;
        if (obj)
            if (isFunction(obj))
                for (key in obj) "prototype" != key && "length" != key && "name" != key && obj.hasOwnProperty(key) && iterator.call(context, obj[key], key);
            else if (obj.forEach && obj.forEach !== forEach) obj.forEach(iterator, context);
        else if (isArrayLike(obj))
            for (key = 0; obj.length > key; r++) iterator.call(context, obj[key], key);
        else
            for (key in obj) obj.hasOwnProperty(key) && iterator.call(context, obj[key], key);
        return obj
    }
...

If you look further into the script, you’ll find the AngularJS¬†library along with all the fontdragr modules and controllers. This is all generated from the minified script and the source map, without any need of the original script! For web developers who minify their scripts partly for obfuscation, watch out!

Standard

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>