September 30, 2014

Jscrambler transformations: what you can expect from Rename Local

By Filipe Silva | 4 min read
minify

If you don’t know our renaming source code transformations (or any renaming transformations for that matter), they basically replace identifier names with meaningless ones. There’s a reason why many organizations set coding conventions around naming variables and function names: they are really helpful in understanding more quickly what a program does. So it seems intuitive that by destroying any meaning those identifiers hold, you’ll get a much harder to read and confusing code. So this is good if you seek obfuscation.

You’ll also be happy to know that, since the verbosity of the identifiers names is unnecessary for the JavaScript engine to run your program, renaming them won’t have any impact on the code functionality. Even if you are not seeking to protect the code, renaming can be useful because it makes identifiers as small as possible, thus contributing to the minification of the code.

In this article we’ll address our Rename Local source code transformations. The Rename All and other source code transformations that create or change names are not covered here. Rename Local only targets identifiers belonging to variables, functions or objects that (we know for sure that) are declared locally. This means that identifiers that are accessible publicly or that we can not find the declaration to which it belongs to, are not renamed.

By working this way, Rename Local is the safest choice when you want to replace names of your source code without worrying about other JavaScripts where yours might depend on or depend to.

Rename Local figures out what it should rename automatically

In figure 1, you can see a simple example where only local declarations and their calls have been renamed, and public or native names have not been touched.

(function(global) {
    var logMessage = function(message) {
        global.console.log(message)
    }
    logMessage("Rename Local example");
})(window)

Figure 1a - Source code

(function(b) {
    var c = function(a) {
        b.console.log(a);
    };
    c("Rename Local example");
})(window);

Figure 1b - After Rename Local

Jscrambler does not maintain a (black) list of names like window, console, log, that it should not rename. And mostly because it doesn’t need to. For each local declaration, Jscrambler seeks their respective calls on the reachable local scope(s). If it doesn’t find any, then it doesn’t touch it.

Rename Local reuses names as much as possible

Another subtle, but interesting detail about Jscrambler renaming is that it tries, whenever possible, to reuse names. That benefits both the protection and minification purpose. For the former, it generates additional confusion because you see names repeated in different parts of the code. For the latter is good because it reduces the number of characters needed for names, thus reducing the overall size of the code. As you can see in Figure 2, the parameter names a and b are reused on both functions.

function drawCanvas(width, height) {
    // ...
}

function move(toPosition, effect) {
    // ...
}

Figure 2a - Source code

function c(a, b) {
    // ...
}

function d(a, b) {
    // ...
}

Figure 2b - After Rename Local

Rename Local picks up identifiers inside evaluated code strings

Rename Local also targets names inside string arguments that are evaluated by functions like eval, setTimeout and setInterval. Otherwise, the code could break due to unmatched references.

(function() {
    var three = 3;

    var doSomethingAmazing = function(condition) {
        var res;
        if (condition) {
            res = three;
        } else {
            res = three * 10;
        }
        console.log(res);
    }

    var doTheSameWithEval = function(condition) {
        var res;
        eval("if ( condition ) { res = three; } else { res = three*10; }");
        console.log(res);
    }

    doSomethingAmazing(true); // prints 3
    doTheSameWithEval(false); // prints 30

})()

Figure 3a - Source Code

(function() {
    var b = 3;
    var d = function(c) {
        var a;
        if (c) {
            a = b;
        } else {
            a = b * 10;
        }
        console.log(a);
    };
    var e = function(c) {
        var a;
        eval("if(c){a=b;}else{a=b*10;}");
        console.log(a);
    };
    d(true); // prints 3
    e(false); // prints 30
})();

Figure 3b - After Rename Local

Yet, there are limits to what you can do statically

However, there are cases where it can fail, mostly when it can’t figure out statically the link between declarations and their respective calls. In Figure 4 you can see a case where using Rename Local breaks the code by renaming hello variable to b.

(function() {
    var hello = 'Hello world!';
    var foo = 'hello';
    return eval(foo);
})();

// returns "Hello world!"

Figure 4a - Source Code

(function() {
    var b = 'Hello world!';
    var a = 'hello';
    return eval(a);
})();

// throws ReferenceError: hello is not defined

Figure 4b - After Rename Local

With a little help from my friends

In the previous example (Figure 4b) you can fix it by adding the hello variable to the Exceptions List. The result would be:

(function() {
    var hello = 'Hello world!';
    var a = 'hello';
    return eval(a);
})();

// returns "Hello world!"

Figure 5 - After Rename Local + Exception List (hello)

Sometimes other transformations may help too

It’s important to understand that other transformations may impact if ultimately Rename Local breaks the code or not. For instance, if we would have used Constant Propagation transformation, it would be applied before renaming. It would replace variable foo by the string ‘hello', and ‘hello' by ‘Hello world'. Rename Local would then come into action, but it basically wouldn’t have anything to work with (to rename), as you can see in Figure 6.

(function() {
    var hello = 'Hello world!';
    return eval('hello');
})();

Figure 6a - After Constant Propagation (1)

(function() {
    return eval('Hello world!');
})();

Figure 6b - After Constant Propagation (2)

Conclusions

In conclusion, Rename Local was developed to be safe and to work out of the box as much as it is statically possible with JavaScript. On the few occasions where Jscrambler is not matching all the occurrences of a name, you may add that name to the Exceptions List and that identifier name won’t be touched. Problem solved!

Author
Filipe SilvaSoftware Engineer currently working as a Technical Product Manager leading Software Engineering and R&D teams.
View All Posts

Subscribe to our weekly newsletter

Learn more about new security threats and technologies.

Projeto Co-Financiado por (Mais info)Norte 2020, Portugal 2020, FEDR