
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!