Jscrambler 101 — Code Annotations
May 28th, 2024 | By Jscrambler | 8 min read
Last updated on May 28th 2024.
Welcome back to Jscrambler 101! A collection of tutorials on how to use Jscrambler to protect your JavaScript. These tutorials cover Jscrambler version 8.3.
Introduction
Last time, on Jscrambler 101 — First Use, we talked about using our web app, applying transformations, creating templates, and learning how to export transformations as a JSON file, among other tips.
This time, we’re going to talk about Code Annotations.
What are Code Annotations?
Code Annotations are JavaScript comments you can add to your code. They are used to change Jscrambler’s behavior when protecting your app.
Sometimes, the transformations that you apply to protect your code can cause performance issues or obfuscate your code in an undesired way. Code Annotations can be used to counteract these situations.
Streamline This Process with Jscrambler Profiling
Using the Profiling feature, you can let Jscrambler do most of the heavy lifting towards minimizing eventual performance impact. Then, you can still tweak the final result using code annotations.
The existing Code Annotations directives are:
enable — enables transformations and transformation aliases
define — defines a transformation alias with custom options
disable — disables transformations, transformation targets, and transformation aliases
order — enables transformations and transformation aliases in a specific order (allows repetition)
target — enables all transformations available for a transformation target
To show you how Code Annotations work, we’re going to use several code snippets. Each snippet will focus on a certain directive.
Enable
Starting with the getSecret function, let's assume we have a string with sensitive information such as a secret:
function getSecret() {
return 'This is the secret.';
}
We want to specifically encode this string which has sensitive information, so we add the enable directive locally.
// @jscrambler enable stringEncoding
function getSecret() {
return 'This is the secret.';
}
This will enable the String Encoding transformation on our string.
function getSecret() {
return '\u0054\u0068\u0069\u0073\u0020\u0069\u0073\u0020\u0074\u0068\u0065\u0020\u0073\u0065\u0063\u0072\u0065\u0074\u002e';
}
It still looks a bit too easy to decode, so we’re going to add String Concealing as well:
// @jscrambler enable stringEncoding, stringConcealing
function getSecret() {
return 'This is the secret.';
}
This will become:
var uPIi = {
//string concealing and encoding
};
//...
function getSecret() {
return uPIi.o(0);
}
Let’s say you’ve added other strings with sensitive information and found it best to simply encode and conceal every string in the file.
function getSecret() {
return 'This is the secret.';
}
//…
function getAnotherSecret() {
return 'This is another secret.';
}
We can use global enable to affect the whole file, like this:
// @jscrambler global enable stringEncoding, stringConcealing
function getSecret() {
return 'This is the secret.';
}
//…
function getAnotherSecret() {
return 'This is another secret.';
}
This will be transformed into:
var pLZi = {
//string concealing and encoding
};
//...
function getSecret() {
return pLZi.n(1);
}
function getAnotherSecret() {
return pLZi.n(0);
}
Now, both displayed secrets have been transformed by String Concealing and String Encoding.
Disable
Imagine you’re using a file that has a global String Encoding, and you have a code block that does operations on a string.
// @jscrambler global enable stringEncoding, numberToString
var a = 'some string';
var b = 1;
//...
for (i = 0; i < 1000; i++) {
//something happens with strings
var s = 'disable string encoding here';
//...
}
var t = 'should be transformed';
Say you don’t want String Encoding to transform the strings inside the loop — as it would affect code performance — but you want all the other strings outside of this loop to be transformed. You can use the disable annotation.
// @jscrambler global enable stringEncoding, numberToString
var a = 'some string';
var b = 1;
//...
// @jscrambler disable stringEncoding
for (i = 0; i < 1000; i++) {
//something happens with strings
var s = 'disable string transformations here';
//...
}
var t = 'should be transformed';
Now Jscrambler will act so that the strings inside the loop won’t be affected by the transformations, but everything else will be affected.
As we only have the String Encoding disabled, you’ll notice that Number to String still works on the for loop.
var a = '\u0073\u006f\u006d\u0065\u0020\u0073\u0074\u0072\u0069\u006e\u0067';
var b = "1" + 0;
//...
for (i = "0" * 1; i < "1000" - 0; i++) {
var s = 'disable string transformations here';
}
var t = '\x73\x68\x6f\x75\x6c\x64\x20\x62\x65\x20\x74\x72\x61\x6e\x73\x66\x6f\x72\x6d\x65\x64';
If you don’t want transformations to occur at all in a code block, use //@jscrambler disable *
// @jscrambler global enable stringEncoding, numberToString
//...
// @jscrambler disable *
function foo() {
var a = 'I won\'t be affected';
function bar() {
var b = 'I won\'t be affected either';
}
// @jscrambler global enable stringEncoding, numberToString
var c = 'But I wan\'t to be affected';
var d = 2;
}
You will notice that the strings in var a and var b aren’t affected by any transformation. We wanted var c to be affected by these transformations though, so we had to insert a new Jscrambler to enable annotation.
function foo() {
var a = 'I won\'t be affected';
function bar() {
var b = 'I won\'t be affected either';
}
var c = '\x42\x75\x74\x20\x49\x20\x77\x61\x6e\x27\x74\x20\x74\x6f\x20\x62\x65\x20\x61\x66\x66\x65\x63\x74\x65\x64';
var d = 2;
}
If we want var d=2; to be affected by Number to String though, we’ll have to add another code annotation directly above it, as so:
// @jscrambler enable stringEncoding, numberToString
var c = 'But I wan\'t to be affected';
// @jscrambler enable stringEncoding, numberToString
var d = 2;
This would become:
var c = '\x42\x75\x74\x20\x49\x20\x77\x61\x6e\x27\x74\x20\x74\x6f\x20\x62\x65\x20\x61\x66\x66\x65\x63\x74\x65\x64';
var d = "2" * 1;
Define can be used for transformations that include options, such as Self-Defending, CharToTernaryOperator, and ControlFlowFlattening.
Define works by using both define and enable.
//@jscrambler define charToTernaryOperator {tern: [0,1]} as iR1
//@jscrambler enable iR1
var a = 'o';
var b = 'o';
Here, we’re using the Char to Ternary Operator transformation and setting its option tern, which is the minimum number of ternary operators between 0 and 1. This includes 0 and 1 as the minimum number of ternary operators and generates a random output.
var a = (730.08, 860.34) < 4.43 ? 0x13c4 : 'o';
var b='o';
We could use other numbers such as 2 and 3, so that:
//@jscrambler define charToTernaryOperator {tern: [2,3]} as iR1
//@jscrambler enable iR1
var a = 'o';
var b = 'o';
Would become:
var a = (5523, 8821) < (634, 4753) ? 108.94 < (230.23, 2050) ? 2880 <= 9190 ? 610.76 : (4.35e+2, 521.21) : (false, 6.46e+2) : 'o';
var b='o';
Define is local and will only affect the code block below it — so remember to use global when you want to affect more than one location. For example:
//@jscrambler define charToTernaryOperator {tern: [0,1]} as iR1
//@jscrambler enable iR1
var a= 'o';
//@jscrambler enable iR1
var b= 'o';
Won’t affect var b. But, if we use global, iR1 can be reused in var b.
//@jscrambler global define charToTernaryOperator {tern: [0,1]} as iR1
//@jscrambler enable iR1
var a= 'o';
//@jscrambler enable iR1
var b= 'o';
The transformed code will look like this:
var a = (730.08, 860.34) < 4.43 ? 0x13c4 : 'o';
var b = (273.32, 907.6) === (1500, 5782) ? (9.92e+2, false) : 'o';
Aliases — as all other code annotations — are inherited from upper blocks. So, if you define an alias for a block, you can enable it not only in that block but also in any of the inner statements and blocks without using global.
// @jscrambler define charToTernaryOperator {tern: [0,1]} as iR1
function foo() {
function bar() {
// @jscrambler enable iR1
function baz() {}
}
}
In this case, only baz() will be affected by the charToTernaryOperator.
Order allows transformations to be executed in a set order. As an example:
// @jscrambler order numberToString, stringEncoding
var strNum= 123;
// @jscrambler order stringEncoding, numberToString
var numStr= 456;
The strNum variable will first be affected by numberToString, followed by stringEncoding, while numStr will first be affected by stringEncoding, then numberToString.
You’ll notice that numStr was only affected by numberToString since at the time stringEncoding was performed, there wasn’t any string to act on.
var strNum = +'\x31\x32\x33';
var numStr = "456" | 0;
Order is best used to handle situations such as these — where the order in which the transformations are applied severely affects the outcome.
Imagine you want to set a specific order for the whole JavaScript source code. To do this, you can use the global modifier.
// @jscrambler global order numberToString, stringEncoding
var strNum = 123;
var numStr = 456;
Will become:
var strNum = +'\u0031\u0032\u0033';
var numStr = '\u0034\u0035\u0036' - 0;
You can also repeat transformations, but the same transformation can only be repeated up to 3 times. Something like:
// @jscrambler order dotToBracketNotation, numberToString, stringEncoding, numberToString, stringEncoding
map.moveTo(0, 0);
It is transformed to:
map['\u006d\u006f\u0076\u0065\u0054\u006f'](+'\u0030', '\u0030' - +'\u0030');
By repeating numberToString and stringEncoding, the numeric arguments produced by the first stringEncoding were transformed and all strings were encoded twice.
Some transformations can only be used once per node. These are:
The order can also be inherited — so if you define a specific order for a block, any inner statement and block will inherit it and combine it with any directive defined at that point. This means that:
// @jscrambler order A, B, C
function foo() {
// @jscrambler order D, E
function bar() {}
}
Will be applied in the following order:
// [A, B, C]
function foo() {
// [D, E, A, B, C]
function bar() {}
}
Code annotations prioritize local order, so D and E will always be performed before A, B, and C.
Transformations are also merged if they are repeated such as:
// @jscrambler order A, B, C
function foo() {
// @jscrambler order A, A
function baz() {}
// @jscrambler order B
function qux() {}
}
This will be merged to act in the following manner:
// [A, B, C]
function foo() {
// [A, A, B, C]
function baz() {}
// [B, A, C]
function qux() {}
}
For the baz function, transformation A is executed twice, as it was defined locally that way.
For the qux function, B is executed before A and C, because the local code annotation has priority over the global annotations and the global B ends up being merged with the local B.
Now, say you have the following function.
function foo() {
var reallySuperMegaHugeBigLongVar = 'target me!!';
}
You want to target the strings and identifiers in the function but aren’t sure what transformations you should apply. If you were to add a target strings, and identifiers as we do below:
//@jscrambler target strings, identifiers
function foo() {
var reallySuperMegaHugeBigLongVar = 'target me!!';
}
Then run a protection, you’ll notice that both the string and identifiers are affected. This happens because Jscrambler will target strings and identifiers, applying transformations accordingly.
var aWua = {
//string transformations
};
//...
function foo() {
var i = aWua["f"](0);
}
The available targets are:
booleans
controlFlow
functions
identifiers
numbers
objects
predicates
regularExpressions
statements
strings
variables
Target is equivalent to a global enable of a set of transformations, so it will affect all functions in a file. It’s easy to use — especially if you don’t know what transformations are available but know what you want to protect.
You can test all these Code Annotations on your Dashboard. Simply create a new app, insert the code snippets, and run protection without selecting any transformations.
You’ll see that the resulting code has the protections applied according to the used code annotations. Since we didn’t apply powerful sets of transformations, you can easily see how each code annotation affected each snippet.
Conclusion
You should now be able to use Code Annotations to change Jscrambler’s behavior when protecting specific code blocks.
Remember to check our Help Center for further information and examples on our transformations, code annotations, and API. Also, don’t forget to use the global modifier, when you want to affect the whole source file.
Feel free to proceed to one of our other 101 Tutorials:
Enjoy your testing and start protecting your Applications ASAP! If you have any additional questions, feel free to contact us.
Jscrambler
The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.
View All ArticlesMust read next
Jscrambler 101 — First Use
Welcome to our 101 tutorials on how to use Jscrambler to protect your JavaScript. In this tutorial, we will teach you how to make your first protection.
September 3, 2024 | By Jscrambler | 8 min read
Jscrambler 101 — Self Defending
Welcome back to our 101 tutorials on how to use Jscrambler to protect your JavaScript. This time, we’re going to talk about Self Defending.
November 14, 2023 | By Jscrambler | 3 min read