DOCUMENTATION
1 Configuration file structure
1.1 Input tag
1.2 Classpath tag
1.3 Keep-names tag
1.3.1 class tag
1.3.2 field tag
1.3.3 method tag
1.4 Watermark tag
1.5 Expiry tag
1.6 Property tag
1.6.1 General properties
1.6.1.1 log-file
1.6.1.2 random-seed
1.6.2 String encryption properties
1.6.2.1 string-encryption
1.6.2.2 string-encryption-type
1.6.2.3 string-encryption-version
1.6.2.4 string-encryption-ignored-strings
1.6.3 Control flow obfuscation properties
1.6.3.1 control-flow-obfuscation
1.6.3.2 extensive-flow-obfuscation
1.6.4 Renaming properties
1.6.4.1 default-package
1.6.4.2 force-default-package
1.6.4.3 packages-naming
1.6.4.4 classes-naming
1.6.4.5 methods-naming
1.6.4.6 fields-naming
1.6.4.7 classes-naming-prefix
1.6.4.8 methods-naming-prefix
1.6.4.9 fields-naming-prefix
1.6.4.10 local-variables-naming
1.6.4.11 skip-renaming
1.6.4.12 update-resource-names
1.6.4.13 update-resource-contents
1.6.5 Other properties
1.6.5.1 line-numbers
1.6.5.2 generics
1.6.5.3 inner-classes
1.6.5.4 throws-clause
1.6.5.5 member-reorder
1.6.5.6 finalize
1.6.5.7 version-marker
1.6.5.8 synthetize-methods
1.6.5.9 synthetize-fields
1.6.5.10 set-methods-to-public
1.6.5.11 set-fields-to-public
1.6.5.12 remove-toString
1.6.5.13 remove-calls
1.6.5.14 remove-annotations
1.6.5.15 output-jar-compression-level
1.6.5.16 output-jar-duplicate-name-entries
1.6.6 Incremental obfuscation properties
1.6.6.1 incremental-obfuscation
1.6.6.2 unique-renaming
1.7 Ignore-classes tag
2 Annotations
3 Android obfuscation
3.1 Using Android Studio
3.2 Using Ant
4 Eclipse IDE plugin
Allatori configuration file is an XML file with the following structure:
<config> <input basedir="input-jars" single-jar="application.jar"> <jar in="app.jar" out="app-obf.jar"/> <jar in="input/*.jar" out="output/*.jar"/> <dir in="in-dir" out="out-dir"/> </input> <classpath basedir="library-jars"><!-- Adding library.jar to the classpath --><jar name="library.jar"/><!-- Adding all jars in the lib directory to the classpath --><jar name="lib/*.jar"/><!-- Adding all jars in the lib2 directory and its subdirectories to the classpath --><jar name="lib2/**/*.jar"/> </classpath> <keep-names> <class template="class SomeClass"/> <class template="class * instanceof java.io.Serializable"/> <class template="class com.package.*"/> <class access="protected+"> <field access="protected+"/> <method access="protected+"/> </class> <class template="class com.company.abc.*"> <field template="public int *"/> <method template="public get*(*)"/> <method template="public set*(*)"/> </class> </keep-names> <watermark key="secure-key-to-extract-watermark" value="Customer: John Smith"/> <expiry date="2017/01/01" string="EXPIRED!"/><!-- Configuration properties, all properties are optional --><!-- General properties, we recommend to use these two properties --><property name="log-file" value="renaming-log.xml"/> <property name="random-seed" value="type anything here"/><!-- String encryption --><property name="string-encryption" value="enable"/> <property name="string-encryption-type" value="fast"/> <property name="string-encryption-version" value="v4"/> <property name="string-encryption-ignored-strings" value="patterns.txt"/><!-- Control flow obfuscation --><property name="control-flow-obfuscation" value="enable"/> <property name="extensive-flow-obfuscation" value="normal"/><!-- Renaming --><property name="default-package" value="com.package"/> <property name="force-default-package" value="enable"/> <property name="packages-naming" value="abc"/> <property name="classes-naming" value="compact"/> <property name="methods-naming" value="compact"/> <property name="fields-naming" value="compact"/> <property name="local-variables-naming" value="optimize"/> <property name="update-resource-names" value="enable"/> <property name="update-resource-contents" value="enable"/><!-- Other --><property name="line-numbers" value="obfuscate"/> <property name="generics" value="remove"/> <property name="inner-classes" value="remove"/> <property name="throws-clause" value="remove"/> <property name="member-reorder" value="random"/> <property name="finalize" value="disable"/> <property name="version-marker" value="anyValidIdentifierName"/> <property name="synthetize-methods" value="all"/> <property name="synthetize-fields" value="all"/> <property name="set-methods-to-public" value="all"/> <property name="set-fields-to-public" value="all"/> <property name="remove-toString" value="enable"/> <property name="remove-calls" value="com.package.Logger.debug"/> <property name="output-jar-compression-level" value="9"/><!-- Incremental obfuscation --><property name="incremental-obfuscation" value="input-renaming-log.xml"/> </config>
Note 1. All relative paths are resolved against configuration file location.
Note 2. If you run Allatori from Ant, then you can reference properties defined in the Ant build file using standard Ant syntax: ${PropertyName}.
Note 3. System properties and environment variables could be referenced using ${System.getProperty(property.name)} and ${System.getenv(VARIABLE_NAME)} respectively.
The input tag is used to set jar (war, ear) files that should be obfuscated. It should contain at least one nested jar or dir tag to set input and output files.
The input tag has two optional attributes:
Attribute | Value |
---|---|
basedir | Optional. Relative paths to jar files will be resolved against the specified directory. By default, relative paths are resolved against configuration file location. |
single-jar | Optional. Allatori will create an additional output jar file with all obfuscated classes. |
The nested jar tag has two required attributes:
Attribute | Value |
---|---|
in | Required. The name of the jar file to obfuscate. |
out | Required. The name of the output jar file. It can be the same as in, in such case the jar will be overwritten with its obfuscated version. |
The nested dir tag has two required attributes:
Attribute | Value |
---|---|
in | Required. The name of the directory with class files to obfuscate. |
out | Required. The name of the output directory for obfuscated class files. The output directory will not be emptied before writing new files to avoid accidental removal of data in the case of typo in the config. You should empty the output directory before running Allatori (i.e. using Ant's delete task). |
Example:
<input basedir="input-jars" single-jar="application.jar"> <jar in="app.jar" out="app-obf.jar"/> <jar in="input/*.jar" out="output/*.jar"/> <dir in="in-dir" out="out-dir"/> </input>
The classpath tag is used to set the classpath for the obfuscated application. It contains nested jar tags with the names of jar files. It is not necessary to reference all library jars needed by your application, but missing classpath elements may result in a weaker obfuscation. Allatori will warn you about all missing classes during the obfuscation process.
The classpath tag has one optional attribute:
Attribute | Value |
---|---|
basedir | Optional. Relative paths to jar files will be resolved against the specified directory. By default, relative paths are resolved against configuration file location. |
The nested jar tag has one required attribute:
Attribute | Value |
---|---|
name | Required. The name of jar file to add to the classpath. Wildcard syntax is allowed: "*" matches any characters in the file name; "**" recurses into subdirectories. |
Example:
<classpath basedir="library-jars"><!-- Adding library.jar to the classpath --><jar name="library.jar"/><!-- Adding all jars in the lib directory to the classpath --><jar name="lib/*.jar"/><!-- Adding all jars in the lib2 directory and its subdirectories to the classpath --><jar name="lib2/**/*.jar"/> </classpath>
The keep-names tag is used to set names of classes, methods and fields that should not be renamed during the obfuscation process. If the obfuscated application is a library, then you should keep all public API. For stand-alone applications, you should keep at least the main class's name. You should also keep names of classes and methods which are used via reflection.
You can use annotations for more accurate control of renamed elements. Annotations override configuration file settings.
The keep-names tag contains any number of the following nested tags:
field tag to specify fields that should not be renamed;
method tag to specify methods that should not be renamed;
class tag to specify classes that should not be renamed. Can, in turn, contain nested field and method tags.
These nested tags set rules for matching names of classes, fields and methods. Matched names will not be renamed. All these tags can have either access or template attribute.
The access attribute matches elements by access levels and can have the following values:
Value | Description |
---|---|
private | Matches classes, fields or methods with private access. |
private+ | Matches classes, fields or methods with private or wider access. |
package | Matches classes, fields or methods with package access. |
package+ | Matches classes, fields or methods with package or wider access. |
protected | Matches classes, fields or methods with protected access. |
protected+ | Matches classes, fields or methods with protected or wider access. |
public | Matches classes, fields or methods with public access. |
The template attribute is similar to Java language syntax. It has different formats for class, field and method tags.
The class tag is used to match classes. It has the following attributes:
Attribute | Value |
---|---|
access | Required*. Sets matching rule. Possible values are above. |
template | Required*. Sets matching rule. Its format is described below. |
ignore | Optional. If set to "true" or "yes", then matched classes will be renamed, but nested method and field tags will be processed as usual. It allows to keep names of some fields and methods without keeping the name of the class. If set to "keep-if-members-match", then at least one of the nested method or field tags should also match to keep class name. |
stop | Optional. If set to "true" or "yes", then Allatori will stop applying any further rules to the matched classes. |
template attribute of the class tag has the following format:
[@annotation] [modifiers] (class | interface) classname [extends classname] [implements classname] [instanceof classname]
The '*' symbol in class or type name matches any number of characters. If name starts with 'regex:', then it is treated like a standard regular expression.
Examples:
Value | Description |
---|---|
class * | Matches all classes and interfaces. |
interface * | Matches all interfaces. |
public class * | Matches all public classes and interfaces. |
protected+ class * | Matches all protected and public classes and interfaces. |
class *abc* | Matches all classes containing "abc" in their fully qualified name. |
class com.abc.* | Matches all classes in the com.abc package and its subpackages. |
class *.abc.* | Matches all classes in all "abc" packages and their subpackages. |
class * extends java.util.Enumeration | Matches all classes extending java.util.Enumeration. |
class * extends *.Enumeration | Matches all classes extending Enumeration. |
class * instanceof java.io.Serializable | Matches all classes that are instances of java.io.Serializable. |
class * implements *.MouseListener | Matches all classes implementing MouseListener. |
@java.lang.Deprecated class * | Matches all deprecated classes. |
class regex:com.package.(foo|bar).* | Matches all classes in com.package.foo and com.package.bar packages and their subpackages. |
class regex:com\.package\.(foo|bar)\..* | More precise version of the above rule. Dots should be escaped, as unescaped dot now means any character. |
The field tag is used to match fields. If field tag is nested in the class tag, then it is applied only to classes matched by the parent class tag. If parent tag is keep-names, then it is applied to all classes. The field tag has the following attributes:
Attribute | Value |
---|---|
access | Required*. Sets matching rule. Possible values are above. |
template | Required*. Sets matching rule. Its format is described below. |
template attribute of the field tag has the following format:
[@annotation] [modifiers] [type] fieldname [instanceof classname]
The '*' symbol in field or type name matches any number of characters. If name starts with 'regex:', then it is treated like a standard regular expression.
Examples:
Value | Description |
---|---|
* | Matches all fields. |
private * | Matches all private fields. |
private+ * | Matches all fields. |
protected+ * | Matches all protected and public fields. |
static * | Matches all static fields. |
public static * | Matches all public static fields. |
public int * | Matches all public integer fields. |
java.lang.String * | Matches all String fields. |
java.lang.* * | Matches all fields with type in java.lang package. |
abc* | Matches all fields which names start with "abc". |
private abc* | Matches all private fields which names start with "abc". |
* instanceof java.io.Serializable | Matches all serializable fields. |
@java.lang.Deprecated * | Matches all deprecated fields. |
regex:(a|b).* | Matches all fields which names start with "a" or "b". |
The method tag is used to match methods. If method tag is nested in the class tag, then it is applied only to classes matched by the parent class tag. If parent tag is keep-names, then it is applied to all classes. The method tag has the following attributes:
Attribute | Value |
---|---|
access | Required*. Sets matching rule. Possible values are above. |
template | Required*. Sets matching rule. Its format is described below. |
parameters | Optional. If set to "keep", then names of method's parameters will not be changed. Useful for public API methods. |
template attribute of the method tag has the following format:
[@annotation] [modifiers] [type] methodname(arguments)
The '*' symbol in method or type name matches any number of characters. The '*' symbol in arguments matches any single argument. Use "**" to match any number of arguments. If name starts with 'regex:', then it is treated like a standard regular expression.
Examples:
Value | Description |
---|---|
*(**) | Matches all methods. |
private *(**) | Matches all private methods. |
private+ *(**) | Matches all methods. |
protected+ *(**) | Matches all protected and public methods. |
private+ *(*) | Matches all methods with exactly one argument. |
private+ *(*,*) | Matches all methods with exactly two arguments. |
private+ *(java.lang.String) | Matches all methods with exactly one argument of String type. |
private+ *(java.lang.String,**) | Matches all methods with String as the first argument. |
private+ *(java.lang.*) | Matches all methods with exactly one argument which type is in java.lang package. |
public get*(**) | Matches all public methods which names start with "get". |
public *abc*(**) | Matches all public methods which names contain "abc". |
private+ int *(**) | Matches all methods with int return type. |
@java.lang.Deprecated *(**) | Matches all deprecated methods. |
public regex:(g|s)et.*(**) | Matches all public getter and setter methods. |
Example:
<keep-names><!-- Stops applying further rules to classes in the com.company.abc package, therefore all classes, methods and fields in this package will be renamed --><class template="class com.company.abc.*" stop="true"/><!-- Further rules instruct Allatori not to rename matched elements --><!-- Matches classes with the name "Main" in any package --><class template="class *.Main"/><!-- Matches classes with the name ending with "Bean" --><class template="class *Bean"><!-- Matches all fields --><field access="private+"/><!-- Matches public integer fields --><field template="public int *"/><!-- Matches all static fields --><field template="static *"/><!-- Matches protected and public String fields --><field template="protected+ java.lang.String *"/><!-- Matches all methods --><method template="private+ *(**)"/><!-- Matches all getter methods --><method template="private+ get*(**)"/><!-- Matches all methods with String argument, parameter names of these methods will not be changed --><method template="private+ *(java.lang.String)" parameters="keep"/> </class><!-- Matches serialization members --><class template="class * instanceof java.io.Serializable"> <field template="static final long serialVersionUID"/> <method template="void writeObject(java.io.ObjectOutputStream)"/> <method template="void readObject(java.io.ObjectInputStream)"/> <method template="java.lang.Object writeReplace()"/> <method template="java.lang.Object readResolve()"/> </class><!-- Matches applets --><class template="class * instanceof java.applet.Applet"/><!-- Matches servlets --><class template="class * instanceof javax.servlet.Servlet"/><!-- Matches midlets --><class template="class * instanceof javax.microedition.midlet.MIDlet"/> </keep-names>
The watermark tag is used to set key and value for watermarking. The tag has two attributes:
Attribute | Value |
---|---|
key | Required. The key used to embed a watermark into the application using steganography techniques. |
value | Required for adding watermark. Any string that will be embedded into the application jars. It can be copyright, customer name, company name or any other information that uniquely identifies the build. A watermark can be used to identify owners of the software or track the origin of a pirated copy. |
Example:
<watermark key="secure-key-to-extract-watermark" value="Customer: John Smith"/>
Watermark can also be added without obfuscation to already obfuscated or non-obfuscated jars. Complete examples of adding and extracting watermarks can be found in the tutorial that comes with Allatori distribution.
The expiry tag is used to set expiry date to your application. Expiry date checks are inserted into many methods, not just main method, therefore cannot be easily removed. This feature can be used to obfuscate libraries that do not even have main method. The tag has two required attributes:
Attribute | Value |
---|---|
date | Required. Expiry date in the yyyy/mm/dd format. |
string | Required. Any string message for the exception thrown if the application is run after the specified expiry date. |
Example:
<expiry date="2017/01/01" string="EXPIRED!"/>
Complete example of using expiry date can be found in the tutorial that comes with Allatori distribution.
The property tag is used to set different obfuscation properties. The tag has two required attributes - name and value:
<property name="property-name" value="property-value"/>
Value | Description |
---|---|
filename | Allatori will write obfuscation log to the specified file. If the property is not set, then log file is not created. Relative paths are resolved against configuration file location. |
The log file is used to restore original stack trace from the obfuscated one. It keeps obfuscated-to-original mapping of names and line numbers.
Examples:
<property name="log-file" value="log.xml"/> <property name="log-file" value="logs/file.xml"/>
Stack trace utility takes log file and restores original names:
java -cp allatori.jar com.allatori.StackTrace2 log.xml input.txt output.txt
Value | Description |
---|---|
any string | String to initialize random number generator. |
The default value is current time in milliseconds.
By default, if you run Allatori twice on the same input jars, it will produce different output jars. Renamed classes, fields and methods will have different names, fields and methods will be reordered differently, etc. This is done to make analyzing two versions of the obfuscated application extremely difficult. If you need the same output jars over several consecutive runs of Allatori, then you need to set the random seed. We recommend changing the random seed for different public releases of your application.
Example:
<property name="random-seed" value="any text here"/>
Value | Description |
---|---|
enable | (default) All string literals that can be safely changed with the encrypted value will be encrypted. Allatori will add a method to decrypt strings on run-time. |
disable | String encryption is disabled. |
maximum | All string literals will be encrypted. See limitations below. |
maximum-with-warnings | All string literals will be encrypted. Every string comparison using == operator will produce a warning, and you'll be able to replace these comparisons with equals() calls. |
Sometimes, strings are compared this way:
String myString = "Hello"; ... public boolean test() { return myString == "Hello"; }
Although it is a bad practice to compare strings using == operator instead of equals method, the method in the example above will return true, because JVM caches String objects to reuse them within the same class. However, after string encryption the method will look something like this:
public boolean test() { return myString == new String("Hello"); // "Hello" string isn't encrypted to make this example more clear }
This version of the method will return false, because compared objects are different.
If you set string-encryption property to enable, then Allatori will not encrypt strings which are compared using == operator, and your application will work correctly.
If you are always using equals method to compare strings, then set string-encryption property to maximum.
Example:
<property name="string-encryption" value="enable"/>
String encryption can be enabled/disabled in the specified classes using annotations or apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<!-- Disabling string encryption for classes in com.abc package --><property name="string-encryption" value="disable" apply2class="class com.abc.*"/><!-- Enabling string encryption for all other classes --><property name="string-encryption" value="enable"/>
Value | Description |
---|---|
fast | (default) Allatori will use very fast string encryption algorithm. |
strong | Allatori will use strong and tricky string encryption algorithm. It is, however, slower. |
custom (package.EncryptClassName.encryptMethodName, package.DecryptClassName.decryptMethodName) |
Allatori will use specified string encryption/decryption methods. See below for more information. |
String encryption should be enabled for this property to take effect.
Example:
<property name="string-encryption-type" value="strong"/>
String encryption type can be applied to the specified classes using annotations or apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<!-- Setting strong string encryption type for classes in com.abc package --><property name="string-encryption-type" value="strong" apply2class="class com.abc.*"/><!-- Setting fast string encryption type for all other classes --><property name="string-encryption-type" value="fast"/>
Custom string encryption.
Usage samples could be found in the tutorial/step15-custom-string-encryption folder.
Encryption method is needed during obfuscation only, it is not needed in the runtime of your application.
It could be located in a separate jar file not included in your distribution. Decryption method is needed in the runtime and could be put in any class of your application.
You can combine custom string encryption with Allatori string encryption
<property name="string-encryption-type" value="custom(package.EncryptClassName.encryptMethodName, package.DecryptClassName.decryptMethodName)" apply2class="class com.some.package.*"/>
and use multiple custom string encryption methods
<property name="string-encryption-type" value="custom(EncryptClassName1.encryptMethodName1, DecryptClassName1.decryptMethodName1)" apply2class="class com.some.package.*"/> <property name="string-encryption-type" value="custom(EncryptClassName2.encryptMethodName2, DecryptClassName2.decryptMethodName2)" apply2class="class com.some.other.package.*"/><!-- Methods for classes not matched by the rules above --><property name="string-encryption-type" value="custom(EncryptClassName3.encryptMethodName3, DecryptClassName3.decryptMethodName3)"/>
As custom string encryption feature replaces strings in the runtime, it could be used for internationalization instead of encryption. Encryption method would be called for all strings during the obfuscation process (if string encryption is set to maximum), so you can use it to log all strings. And decryption method would replace strings with their internationalized versions in the runtime.
Value | Description |
---|---|
v4 | (default) New string encryption algorithm will be used. |
v3 | Allatori will use string encryption algorithm from the 3.X version. |
String encryption should be enabled for this property to take effect.
It doesn't mean that v3 algorithm is outdated. We'll make changes to both v3 and v4 algorithms from time to time to keep them fresh. This property was introduced as the key ideas of v3 and v4 algorithms are very different.
Example:
<property name="string-encryption-version" value="v3"/>
Value | Description |
---|---|
filename | Text file containing string literals which should be excluded from obfuscation. These strings will not be encrypted. |
The specified text file contains string templates. Each line is a new template, '*' matches any number of any characters. If template line starts with "regex:", then it is processed as a regular expression:
Copyright* All Rights Reserved* *CompanyName* regex:\d+
Example:
<property name="string-encryption-ignored-strings" value="patterns.txt"/>
Value | Description |
---|---|
enable | (default) Allatori will alter the code of the methods. It will not change the application behaviour at run-time, but will make the decompilation process much harder. Generally, control flow obfuscation also makes application smaller and faster. |
disable | Control flow obfuscation is disabled. |
Example:
<property name="control-flow-obfuscation" value="enable"/>
Control flow obfuscation can be enabled/disabled in the specified classes using annotations or apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<!-- Disabling control flow obfuscation for classes in com.abc package --><property name="control-flow-obfuscation" value="disable" apply2class="class com.abc.*"/><!-- Enabling control flow obfuscation for all other classes --><property name="control-flow-obfuscation" value="enable"/>
Value | Description |
---|---|
normal | (default) Allatori will use control flow obfuscation techniques, which make the obfuscated application a bit bigger and slower. However, Allatori will minimize the number of such code transformations. |
disable | Extensive control flow obfuscation is disabled. |
maximum | Allatori will fully use control flow obfuscation techniques, which make the obfuscated application a bit bigger and slower. |
Control flow obfuscation should be enabled for this property to take effect.
Example:
<property name="extensive-flow-obfuscation" value="maximum"/>
Extensive control flow obfuscation can be applied to the specified classes using annotations or apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<!-- Using "maximum" value for classes in com.abc package --><property name="extensive-flow-obfuscation" value="maximum" apply2class="class com.abc.*"/><!-- Using "normal" value for all other classes --><property name="extensive-flow-obfuscation" value="normal"/>
Value | Description |
---|---|
package name | Full package name, existing or new. |
If all classes in some package are renamed, then Allatori will move them to the default package. To move absolutely all renamed classes to the default package, you should enable the force-default-package property. The usage of "" as the default package will reduce the size of the resulting jar.
Examples:
<property name="default-package" value=""/> <property name="default-package" value="com.company.product"/>
Value | Description |
---|---|
disable | (default) Only classes from packages where all classes are renamed will be moved to the default package. |
enable | Absolutely all renamed classes will be moved to the default package. |
Default package should be set for this property to take effect.
Example:
<property name="force-default-package" value="enable"/>
Value | Description |
---|---|
abc | (default) Packages will be renamed to 'a', 'b', 'c', 'd', ..., 'aa', 'ab', etc. Names will have lower-case letters only. |
ABC | Packages will be renamed to 'A', 'B', 'C', 'D', ..., 'AA', 'AB', etc. Names will have upper-case letters only. |
123 | Packages will be renamed to '1', '2', '3', ..., '00', '01', etc. |
keep | Packages will keep their original names. |
custom(filename.txt) | Names will be constructed using the provided text file. Each line of the file represents a single name element. If the file has two lines '0' and '1', then the generated names would be '0', '1', '00', '01', '10', '11', '000', etc. |
Example:
<property name="packages-naming" value="abc"/>
Value | Description |
---|---|
compact | (default) Allatori will use single-character names as much as possible, thus making the resulting jar smaller. Classes may have mixed-case names differing only in case (a.class and A.class). Jar files allow mixed-case file names, while Windows file system does not, so it will be tricky to unzip some classes (a.class will overwrite A.class on Windows when unzipped). The jar file with mixed-case file names will work fine on all platforms including Windows. |
iii | All names will have the same length and differ in case only - iiii, iiiI, iiIi, etc. The resulting jar file will be bigger comparing to other renaming options. |
abc | Classes will be renamed to 'a', 'b', 'c', 'd', ..., 'aa', 'ab', etc. Names will have lower-case letters only. |
ABC | Classes will be renamed to 'A', 'B', 'C', 'D', ..., 'AA', 'AB', etc. Names will have upper-case letters only. |
123 | Classes will be renamed to '1', '2', '3', ..., '00', '01', etc. |
windows | Allatori will use names prohibited on Windows ('con', 'prn', 'aux', 'nul', etc.) as class names. It is ok to have con.class in the jar, but this class cannot be unzipped on Windows. Classes may also have mixed-case names differing only in case. The jar file with such file names will work fine on all platforms including Windows. This option makes the resulting jar file bigger comparing to compact or abc naming. |
custom(filename.txt) | Names will be constructed using the provided text file. Each line of the file represents a single name element. If the file has two lines '0' and '1', then the generated names would be '0', '1', '00', '01', '10', '11', '000', etc. |
unique | All renamed classes will have unique names. There will be no matching class names in different packages. Can be combined with other classes naming options. |
keep-$-sign | Renamed classes will retain Java inner class naming notation, i.e. classes Foo and Foo$Bar will be renamed to a and a$b. By default, Allatori will rename Foo and Foo$Bar to a and b. Can be combined with other classes naming options. |
Example:
<property name="classes-naming" value="abc"/>
Value | Description |
---|---|
compact | (default) Allatori will use single-character names as much as possible, thus making the resulting jar smaller. |
iii | All names will have the same length and differ in case only - iiii, iiiI, iiIi, etc. The resulting jar file will be bigger comparing to other renaming options. |
abc | Methods will be renamed to 'a', 'b', 'c', 'd', ..., 'aa', 'ab', etc. |
ABC | Methods will be renamed to 'A', 'B', 'C', 'D', ..., 'AA', 'AB', etc. |
123 | Methods will be renamed to '1', '2', '3', ..., '00', '01', etc. |
keywords | Allatori will use Java reserved keywords ('if', 'for', 'int', etc.) as method names. Such naming is legal in class file format, but can confuse many decompilers. However, this makes the resulting jar file bigger comparing to compact naming. |
real | Usually, some methods are not renamed according to configuration rules. Allatori will take these methods's names and give them to renamed methods, making the difference between new and original names unclear. Can be combined with other methods naming options (if it runs out of names, then the second option will be used). |
custom(filename.txt) | Names will be constructed using the provided text file. Each line of the file represents a single name element. If the file has two lines '0' and '1', then the generated names would be '0', '1', '00', '01', '10', '11', '000', etc. |
unique | Shortcut for unique-renaming property. Can be combined with other methods naming options. If any two methods have the same name and signature, then these methods will be renamed to the same new name. If any two methods have different name/signature, then these methods will have different names after renaming. It ensures consistency during subsequent incremental obfuscation runs. |
Example:
<property name="methods-naming" value="keywords"/>
Value | Description |
---|---|
compact | (default) Allatori will use single-character names as much as possible, thus making the resulting jar smaller. |
iii | All names will have the same length and differ in case only - iiii, iiiI, iiIi, etc. The resulting jar file will be bigger comparing to other renaming options. |
abc | Fields will be renamed to 'a', 'b', 'c', 'd', ..., 'aa', 'ab', etc. |
ABC | Fields will be renamed to 'A', 'B', 'C', 'D', ..., 'AA', 'AB', etc. |
123 | Fields will be renamed to '1', '2', '3', ..., '00', '01', etc. |
keywords | Allatori will use Java reserved keywords ('if', 'for', 'int', etc.) as field names. Such naming is legal in class file format, but can confuse many decompilers. However, this makes the resulting jar file bigger comparing to compact naming. |
real | Usually, some fields are not renamed according to configuration rules. Allatori will take these fields's names and give them to renamed fields, making the difference between new and original names unclear. Can be combined with other fields naming options (if it runs out of names, then the second option will be used). |
custom(filename.txt) | Names will be constructed using the provided text file. Each line of the file represents a single name element. If the file has two lines '0' and '1', then the generated names would be '0', '1', '00', '01', '10', '11', '000', etc. |
unique | All renamed fields will have unique names. Can be combined with other fields naming options. |
Example:
<property name="fields-naming" value="keywords"/>
Value | Description |
---|---|
any string | The specified string will be used as a name prefix for all renamed classes. |
Example:
<property name="classes-naming-prefix" value="c_"/>
Possible usage is specifying MainClass$ as a prefix:
<property name="classes-naming-prefix" value="MainClass$"/>
Some decompilers will see renamed classes as inner classes of MainClass.
Value | Description |
---|---|
any string | The specified string will be used as a name prefix for all renamed methods. |
Example:
<property name="methods-naming-prefix" value="m_"/>
Value | Description |
---|---|
any string | The specified string will be used as a name prefix for all renamed fields. |
Example:
<property name="fields-naming-prefix" value="f_"/>
Value | Description |
---|---|
optimize | (default) Allatori performs optimizations to reduce the total number of local variables in a method. The remaining local variables will have the same name (single-name renaming option). This is the default and recommended option. |
single-name | Almost all local variables will have the same name. It is allowed by the Java virtual machine but can confuse a lot of decompilers. |
abc | Local variables will be renamed to unique names 'a', 'b', 'c', 'd', etc. |
remove | The original local variable names will be removed. It can reduce the size of the resulting jar. |
keep-parameters | Parameter names will be kept unchanged, all other local variables will be renamed. It is useful for methods that are part of the public API. There is also an option to keep parameters only in the specified methods using method tag in the keep-names section. |
keep | All local variable names will be kept unchanged. This option is not recommended. |
Example:
<property name="local-variables-naming" value="single-name"/>
The default single name for single-name and optimize renaming options is 'a'. You can change it using one of the following lines:
<property name="local-variables-naming" value="optimize:ANY_OTHER_NAME"/> <property name="local-variables-naming" value="optimize:int"/> <property name="local-variables-naming" value="single-name:4"/>
Value | Description |
---|---|
disable | (default) Allatori performs renaming of classes, methods and fields according to keep-names rules. |
enable | All classes, methods and fields will not be renamed. Local variables naming is controlled separately by local-variables-naming property. String encryption, flow obfuscation, etc. will be applied normally according to the settings in the configuration file. |
Example:
<property name="skip-renaming" value="enable"/>
Value | Description |
---|---|
disable | (default) Resource file names will not be changed. |
enable | Resource files will be renamed to reflect changes in class names. If a resource file name is based on a class name, and that class is renamed, then the resource file will also be renamed. |
Example:
<property name="update-resource-names" value="enable"/>
Value | Description |
---|---|
disable | (default) Resource file contents will not be changed. |
enable | Resource contents will be updated to reflect changes in class names. |
enable:ENCODING | Resource contents will be updated to reflect changes in class names using the specified encoding. The default encoding is UTF-8. |
Example:
<property name="update-resource-contents" value="enable"/> <property name="update-resource-contents" value="enable:UTF-8"/>
The property can be applied to the specified files using apply2file attribute:
<property name="update-resource-contents" value="enable" apply2file="*.xml"/>
Value | Description |
---|---|
obfuscate | (default) Debug information is obfuscated and cannot be used without further transformation. Allatori has a special utility, which allows reconstructing the original stack trace with the help of an obfuscated one.
Reported stack trace will look like this: java.lang.NullPointerException at com.company.c.a(m:61) at com.company.b.b(w:94) at com.company.b.a(w:83) at com.company.a.a(n:75) After transforming it with Allatori Stack Trace Utility, stack trace will look like this: java.lang.NullPointerException at com.company.Util.createTestException(Util.java:38) at com.company.TraceTest.testNullObject(TraceTest.java:53) at com.company.TraceTest.allTraceTests(TraceTest.java:14) at com.company.Main.runTest(Main.java:27) Using this option makes the resulting jar smaller. |
remove | This option can be used, when the size of your application matters really much.
Reported stack trace will look like this: java.lang.NullPointerException at com.company.c.a(Unknown Source) at com.company.b.b(Unknown Source) at com.company.b.a(Unknown Source) at com.company.a.a(Unknown Source) |
keep | Leaves debug information without modifications.
This option can be helpful for internal testing of your application. In other cases it's better to choose other options.
Reported stack trace will look like this: java.lang.NullPointerException at com.company.c.a(Util.java:38) at com.company.b.b(TraceTest.java:53) at com.company.b.a(TraceTest.java:14) at com.company.a.a(Main.java:27) |
Example:
<property name="line-numbers" value="obfuscate"/>
Value | Description |
---|---|
keep | (default) If you are using reflection to determine generic types or need to compile other classes using the obfuscated jar as a library, then you need to keep generic type signatures. |
remove | Generic types information will be removed, i.e. a vector of strings Vector<String> will be seen as a generic Vector. It doesn't affect the performance, makes jar smaller, and is the recommended option. |
Example:
<property name="generics" value="remove"/>
This property can be set for the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<!-- Keeping generic types information in com.abc package's classes --><property name="generics" value="keep" apply2class="class com.abc.*"/><!-- Removing generic types information in all other classes --><property name="generics" value="remove"/>
Value | Description |
---|---|
keep | (default) Java compiler adds informational attributes with inner classes names. These attributes will be kept in obfuscated classes. |
remove | Informational attributes will be removed, class hierarchy will be harder to restore. It doesn't affect the performance, makes jar smaller, and is the recommended option. |
Example:
<property name="inner-classes" value="remove"/>
This property can be set for the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<!-- Keeping inner classes information in com.abc package's classes --><property name="inner-classes" value="keep" apply2class="class com.abc.*"/><!-- Removing inner classes information in all other classes --><property name="inner-classes" value="remove"/>
Value | Description |
---|---|
keep | (default) Keep methods' throws declarations. |
remove | JVM allows throwing exceptions without declaring them via throws keyword. The idea is similar to Lombok's sneaky throws, but used for obfuscation purposes. |
Example:
<property name="throws-clause" value="remove"/>
This property can be set for the specified classes/methods using apply2class and/or apply2method attributes. The apply2class attribute has the same format as template attribute of the class tag. The apply2method attribute has the same format as template attribute of the method tag. Here is an example:
<!-- Removing throws in all private methods --><property name="throws-clause" value="remove" apply2method="private *(**)"/><!-- Removing throws in com.abc package's classes --><property name="throws-clause" value="remove" apply2class="class com.abc.*"/>
Value | Description |
---|---|
enable | (default) Usually, developers place related methods and fields one after the other in the source file. This sequence is kept after the compilation process. Allatori will shuffle fields and methods. |
random | The same as enable. |
alphabetical | Fields and methods will be put in alphabetical order. |
reverse-alphabetical | Fields and methods will be put in reverse alphabetical order. |
disable | Member reordering is disabled. |
Example:
<property name="member-reorder" value="random"/>
This property can be set for the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<!-- Reordering members in com.abc package's classes --><property name="member-reorder" value="random" apply2class="class com.abc.*"/><!-- No reordering in all other classes --><property name="member-reorder" value="disable"/>
Value | Description |
---|---|
disable | (default) Class finalizing is disabled. |
enable | Classes with no subclasses (leaf classes) will be declared final. This feature should be used only for obfuscating stand-alone applications. It can make your application run faster. |
Example:
<property name="finalize" value="enable"/>
Value | Description |
---|---|
valid Java identifier name | Allatori will use the given identifier as a name for some renamed methods and fields. This will mark the obfuscated class files. You can use it to mark the demo version of your product. For instance, Allatori demo version is marked with "ALLATORI_DEMO" string. Note that demo version of Allatori marks obfuscated jars and adds "ALLATORI_DEMO_" to whatever you use as a value of this property. |
Example:
<property name="version-marker" value="THIS_IS_DEMO_VERSION"/>
Value | Description |
---|---|
private | (default) All private methods will be marked as synthetic. |
all | All methods will be marked as synthetic. |
package | All package visible methods will be marked as synthetic. |
protected | All protected methods will be marked as synthetic. |
public | All public methods will be marked as synthetic. |
disable | Allatori will not mark methods as synthetic. |
Some decompilers do not output synthetic methods.
Example:
<property name="synthetize-methods" value="all"/>
The property can be used more than once:
<property name="synthetize-methods" value="private"/> <property name="synthetize-methods" value="package"/> <property name="synthetize-methods" value="protected"/>
The property can be applied to the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<property name="synthetize-methods" value="all" apply2class="class com.abc.*"/> <property name="synthetize-methods" value="private"/>
Value | Description |
---|---|
disable | (default) Allatori will not mark fields as synthetic. |
all | All fields will be marked as synthetic. |
private | All private fields will be marked as synthetic. |
package | All package visible fields will be marked as synthetic. |
protected | All protected fields will be marked as synthetic. |
public | All public fields will be marked as synthetic. |
Some decompilers do not output synthetic fields.
Example:
<property name="synthetize-fields" value="all"/>
The property can be used more than once:
<property name="synthetize-fields" value="private"/> <property name="synthetize-fields" value="package"/> <property name="synthetize-fields" value="protected"/>
The property can be applied to the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<property name="synthetize-fields" value="all" apply2class="class com.abc.*"/> <property name="synthetize-fields" value="private"/>
Value | Description |
---|---|
disable | (default) Allatori will not mark methods as public. |
all | All methods will be marked as public. Marking private methods as public could break the functionality of your application. |
private | All private methods will be marked as public. Marking private methods as public could break the functionality of your application. |
package | All package visible methods will be marked as public. |
protected | All protected methods will be marked as public. |
Example:
<property name="set-methods-to-public" value="all"/>
The property can be used more than once:
<property name="set-methods-to-public" value="package"/> <property name="set-methods-to-public" value="protected"/>
The property can be applied to the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<property name="set-methods-to-public" value="all" apply2class="class com.abc.*"/> <property name="set-methods-to-public" value="protected"/>
Value | Description |
---|---|
disable | (default) Allatori will not mark fields as public. |
all | All fields will be marked as public. |
private | All private fields will be marked as public. |
package | All package visible fields will be marked as public. |
protected | All protected fields will be marked as public. |
Example:
<property name="set-fields-to-public" value="all"/>
The property can be used more than once:
<property name="set-fields-to-public" value="package"/> <property name="set-fields-to-public" value="protected"/>
The property can be applied to the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<property name="set-fields-to-public" value="all" apply2class="class com.abc.*"/> <property name="set-fields-to-public" value="protected"/>
Value | Description |
---|---|
disable | (default) toString methods would not be removed. |
enable | Allatori will remove toString() method in obfuscated classes. toString method can reveal some information about the class and is often used for debug only, therefore it can be removed. |
Example:
<property name="remove-toString" value="enable"/>
The property can be applied to the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<property name="remove-toString" value="enable" apply2class="class com.abc.*"/> <property name="remove-toString" value="enable" apply2class="class com.xyz.*"/>
Value | Description |
---|---|
ClassName.methodName | Allatori will remove calls to the specified method. Could be used to remove debug logging calls.
ClassName and methodName can contain * to match several classes/methods.
If method has a return value, then the return value is replaced with null (0, false) and Allatori prints out a warning during the obfuscation. |
Examples:
<property name="remove-calls" value="android.util.Log.d"/> <property name="remove-calls" value="android.util.Log.*"/> // if a call to methodOne() is removed, then the line int i = methodOne(); // would be changed to int i = 0; // if a call to methodTwo() is removed, then the line Object o = methodTwo(); // would be changed to Object o = null;
The property can be applied to the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<!-- Removing Logger.debug calls from classes in com.abc package --><property name="remove-calls" value="com.package.Logger.debug" apply2class="class com.abc.*"/>
Value | Description |
---|---|
AnnotationClassName | Allatori will remove AnnotationClassName annotations. Could be used to remove unnecessary/debug annotations. AnnotationClassName can contain * to match several classes. |
Examples:
<property name="remove-annotations" value="kotlin.Metadata"/> <property name="remove-annotations" value="com.package.annotations.*"/>
The property can be applied to the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<!-- Removing com.package.Annotation annotations from classes in com.abc package --><property name="remove-annotations" value="com.package.Annotation" apply2class="class com.abc.*"/>
Value | Description |
---|---|
0-9 | Sets compression level for output jar files. 0 is no compression, 9 is the highest level of compression. Direct mapping to ZipOutputStream.setLevel(int) |
Examples:
<property name="output-jar-compression-level" value="9"/>
Value | Description |
---|---|
remove | (default) If input jar contains several files with the same name in one folder, Allatori will put just one file in the output jar. |
keep | Allatori will write all duplicate name jar entries to the output jar. Zip(jar) file format allows having duplicate name entries, however standard Java API doesn't allow writing such entries.
Allatori needs access to java.util.zip.ZipOutputStream.names private field to overcome this, so JVM should be run with
--add-opens java.base/java.util.zip=ALL-UNNAMED option when running Allatori on Java 16+ or --illegal-access=permit option on older Java versions. |
Examples:
<property name="output-jar-duplicate-name-entries" value="keep"/>
Value | Description |
---|---|
name of the previously created log file | Sets the log file of the previous Allatori run. Relative paths are resolved against configuration file location.
Incremental obfuscation is used when you need to create a patch or add-on to your application. In such case you need guarantee, that new names of classes, methods and fields are consistent with the previously obfuscated version. Using log file generated during the previous run of Allatori as input when obfuscating the next release, makes these two releases fully compatible. So a patch or add-on can be seamlessly integrated into the previously deployed application. When using incremental obfuscation you should include in the obfuscation process all your classes even if only some of them are to be distributed. |
Example:
<property name="incremental-obfuscation" value="input-renaming-log.xml"/>
Value | Description |
---|---|
disable | (default) Unique renaming is disabled. | enable | If any two methods have the same name and signature, then these methods will be renamed to the same new name. If any two methods have different name/signature, then these methods will have different names after renaming. It ensures consistency during subsequent incremental obfuscation runs. |
Example:
<property name="unique-renaming" value="enable"/>
The property can be applied to the specified classes using apply2class attribute. The apply2class attribute has the same format as template attribute of the class tag. Here is an example:
<property name="unique-renaming" value="enable" apply2class="class com.abc.*"/>
The ignore-classes tag is used to completely exclude some classes from the obfuscation process.
These classes are copied "as is" with no changes to the output jar file.
Note that ignored classes will reference other classes by their original names. You cannot rename classes/methods referenced by ignored classes.
The tag contains nested class tags which are the same as class tags in the keep-names section.
Example:
<ignore-classes> <class template="class com.company.abc.*"/> <class template="class com.company.xyz.SomeClass"/> </ignore-classes>
Annotation classes are located in the allatori-annotations.jar. All Allatori annotations are removed during the obfuscation process, so you don't need this jar file in the runtime. Annotations are used for easier and more accurate configuration of the obfuscator.
Here all are available annotations located in the com.allatori.annotations package:
Annotation | Applicable to | Description |
---|---|---|
Rename | classes, methods and fields | Instructs Allatori to rename the annotated element. Overrides configuration file settings. |
DoNotRename | classes, methods and fields | Instructs Allatori not to rename the annotated element. Overrides configuration file settings. |
StringEncryption | classes | Possible values are:
StringEncryption.ENABLE StringEncryption.DISABLE StringEncryption.MAXIMUM StringEncryption.MAXIMUM_WITH_WARNINGS Overrides string-encryption property. Example: @StringEncryption(StringEncryption.ENABLE) |
StringEncryptionType | classes | Possible values are:
StringEncryptionType.FAST StringEncryptionType.STRONG Overrides string-encryption-type property. Example: @StringEncryptionType(StringEncryptionType.STRONG) |
ControlFlowObfuscation | classes and methods | Possible values are:
ControlFlowObfuscation.ENABLE ControlFlowObfuscation.DISABLE Overrides control-flow-obfuscation property. Example: @ControlFlowObfuscation(ControlFlowObfuscation.ENABLE) |
ExtensiveFlowObfuscation | classes and methods | Possible values are:
ExtensiveFlowObfuscation.DISABLE ExtensiveFlowObfuscation.NORMAL ExtensiveFlowObfuscation.MAXIMUM Overrides extensive-flow-obfuscation property. Example: @ExtensiveFlowObfuscation(ExtensiveFlowObfuscation.MAXIMUM) |
Allatori performs full-featured obfuscation of Android applications and can be easily integrated into the build process. We've created a typical configuration file for Android projects, making it very easy to obfuscate Android application. You can find the configuration file (allatori.xml) and sample build files (build.gradle and build.xml) in the tutorial that comes with Allatori distribution.
Here are three steps to set up Allatori for your Android Studio project:
1. Create allatori folder in your rootDir folder. Copy allatori.jar to the created folder;
2. Copy allatori.xml to your projectDir folder (where project's build.gradle is) from our tutorial;
3. Edit build.gradle:
android { ... // for applications applicationVariants.all { variant -> variant.javaCompileProvider.get().doLast { runAllatori(variant) } } // for libraries // libraryVariants.all { variant -> // variant.javaCompileProvider.get().doLast { // runAllatori(variant) // } // } } def runAllatori(variant) { copy { from "$projectDir/allatori.xml" into "$buildDir/intermediates/classes/" expand(classesRoot: variant.javaCompileProvider.get().destinationDir, kotlinRoot: "${buildDir}/tmp/kotlin-classes/${variant.name}", androidJar: "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar", classpathJars: variant.javaCompileProvider.get().classpath.getAsPath(), logFile: "allatori-log-${variant.name}.xml") rename('allatori.xml', "allatori-${variant.name}.xml") } new File("${variant.javaCompileProvider.get().destinationDir}-obfuscated").deleteDir() javaexec { main = 'com.allatori.Obfuscate' classpath = files("$rootDir/allatori/allatori.jar") args "$buildDir/intermediates/classes/allatori-${variant.name}.xml" } new File("${variant.javaCompileProvider.get().destinationDir}").deleteDir() new File("${variant.javaCompileProvider.get().destinationDir}-obfuscated").renameTo(new File("${variant.javaCompileProvider.get().destinationDir}")) // Kotlin support // new File("${buildDir}/tmp/kotlin-classes/${variant.name}").deleteDir() // new File("${buildDir}/tmp/kotlin-classes/${variant.name}-obfuscated").renameTo(new File("${buildDir}/tmp/kotlin-classes/${variant.name}")) }
Here are three steps to set up Allatori for your Android Ant project:
1. Create allatori folder in your project's folder. Copy allatori.jar to the created folder;
2. Copy allatori.xml to your project's folder from our tutorial;
3. Add the following target to your build.xml:
<target name="-obfuscate" unless="do.not.compile"> <taskdef name="allatori" classname="com.allatori.ant.ObfuscatorTask" classpath="allatori/allatori.jar"/> <delete dir="${out.classes.absolute.dir}-obfuscated"/> <allatori config="allatori.xml"/> <property name="out.dex.input.absolute.dir" value="${out.classes.absolute.dir}-obfuscated"/> </target>
You may need to perform further configuration for your project. Properties defined in the Ant build file can be referenced from the Allatori configuration file using standard Ant syntax: ${PropertyName}.
Allatori can be easily used with Eclipse IDE:
1. Copy allatori.jar to the eclipse/dropins folder;
2. Right-click your project's name in Eclipse, choose Configure -> Add Allatori Builder in the popup menu (see screenshot);
3. Clean rebuild the project, obfuscation process runs only on clean builds.
The default allatori.xml configuration file will be created in the project's root folder during the first run.
You can use ${eclipse-input} and ${eclipse-classpath} properties in the configuration file:
<input> ${eclipse-input} </input> <classpath> ${eclipse-classpath} </classpath>