Code Annotations

Code Annotations are JavaScript comments that can be added to JS code. They are used to change Jscrambler’s behaviour when protecting an Application. Some transformations can cause performance issues or obfuscate code in an undesired way. Code Annotations can be used to counteract these situations.

The existing directives are:

  • enable - enables transformations and transformation aliases
  • define - defines a transformation alias with custom options
  • disable - disables transformations, transformation targets, and transformations aliases
  • order - enables transformations and transformation aliases in a specific order (allows repetition)
  • target - enables a subset of transformations available for a transformation target

To demonstrate how Code Annotations work, we’ve prepared code snippets, focusing on each directive.

Enable

Analysing getSecret function, assuming there is a string with sensitive information such as a secret:

function getSecret() {
    return 'This is the secret.';
}

It would make sense to encode specifically this string with sensitive information, which can be done by adding the enable directive locally.

// @jscrambler enable stringEncoding
function getSecret() {
    return 'This is the secret.';
}

This will enable String Encoding on the string.

function getSecret() {
    return '\u0054\u0068\u0069\u0073\u0020\u0069\u0073\u0020\u0074\u0068\u0065\u0020\u0073\u0065\u0063\u0072\u0065\u0074\u002e';
}

More than one transformation is allowed in an enable, and the protection in this case can become stronger through String Concealing:

// @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);
}

If there are other strings with sensitive information, it may be 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.';
}

This can be achieved through global enable, affecting the whole file:

// @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);
}

Both of the displayed secrets have been transformed by String Concealing and String Encoding.

Disable

Assume there is a file which has global String Encoding, and it has 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';

String Encoding could be disabled inside the loop, as it would affect the performance of the code, but other strings outside of this loop should be transformed. This is a reason to make use of 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';

Jscrambler will act so that the strings inside the loop won’t be affected by the transformations, but everything else will be affected.

As only the String Encoding is disabled, 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';

To disable all transformations make use of //@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;
}

Note that the strings in var a and var b aren’t affected by any transformation. var c was affected by these transformations though, by inserting a new jscrambler enable annotation above it.

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;
}

To affect var d=2; with Number to String the addition of another code annotation directly above it is needed:

// @jscrambler enable stringEncoding, numberToString
var c = 'But I wan\'t to be affected';
// @jscrambler enable stringEncoding, numberToString
var d = 2;

This will 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

Define can be used for transformations that include options, such as Self Defending, Char To Ternary Operator, and Control Flow Flattening.

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';

As an example is the Char to Ternary Operator transformation, and its option tern, the minimum number of ternary operators, set with 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';

Other numbers can be used such as 2 and 3:

//@jscrambler define charToTernaryOperator {tern: [2,3]} as iR1
//@jscrambler enable iR1
var a = 'o';
var b = 'o';

Which produces:

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 global is needed 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, with global, iR1 can be reused in define.

//@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 be similar to:

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 by defining an alias for a block it is possible to enable it in that block and in any of its 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 Char To Ternary Operator.

Order

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 Number To String, followed by String Encoding, while numStr will first be affected by String Encoding, then Number To String. Note that numStr was only affected by Number To String, since at the time String Encoding 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. To set a specific order for the whole JavaScript source code using the global modifier is acceptable.

// @jscrambler global order numberToString, stringEncoding
var strNum = 123;
var numStr = 456;

Will become:

    var strNum = +'\u0031\u0032\u0033';
    var numStr = '\u0034\u0035\u0036' - 0;

Repeating transformations is allowed but, the number of repetitions for a transformation is limited to 3 times. Something such as:

// @jscrambler order dotToBracketNotation, numberToString, stringEncoding, numberToString, stringEncoding
map.moveTo(0, 0);

Is transformed to:

map['\u006d\u006f\u0076\u0065\u0054\u006f'](+'\u0030', '\u0030' - +'\u0030');

By repeating Number To String and String Encoding, the numeric arguments produced by the first stringEncoding were transformed and all strings were encoded twice.

There are some transformations that can only be used once per node. These are:

Order can also be inherited so by defining a specific order for a block, any inner statement and block will inherit 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 alway be performed before A, B, and C. Transformations are also merged when repeated:

// @jscrambler order A, B, C
function foo() {
 // @jscrambler order A, A
 function baz() {}
 // @jscrambler order B
 function qux() {}
}

Will be merged to act the in 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, since the local code annotation has priority over the global annotations, and the global B is merged with the local B.

Target

For the following function:

function foo() {
    var reallySuperMegaHugeBigLongVar = 'target me!!';
}

Assume there is a need to target the strings and identifiers in the function but no knowledge of what transformations should be applied. Adding a target strings, identifiers as we below:

//@jscrambler target strings, identifiers
function foo() {
    var reallySuperMegaHugeBigLongVar = 'target me!!';
}

Applying a protection, will produce a code where both the string and identifiers are affected. This is due to Jscrambler targeting 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 is easy to use especially when not knowing what transformations are available, but knowing what needs to protect.

All these Code Annotations can be tested at https://app.jscrambler.com/. Simply create a new app, insert the code snippets and run a protection without selecting any transformations.

The resulting code will have the protections applied according to the used code annotations.

Follow the links for further details: