Obfuscation Example - RetroGuard Documentation


Prev   Contents   Next

We'll now take an example obfuscation process from beginning to end, starting with the internal default RetroGuard script, working through the various issues that can arise during obfuscation, and ending with a maximally obfuscated and compact jar.

Since RetroGuard is itself a Java application, we'll use it as the example codebase. We begin with the unobfuscated jar (in.jar), which is around 242kb in size.

Default Obfuscation

As recommended in the quick start section, we'll try the internal default script first:

java -cp retroguard.jar:ant.jar RetroGuard in.jar out.jar

Since no script file is specified as the third parameter to RetroGuard and no file named 'script.rgs' exists in the current directory, RetroGuard uses its internal default script.

Also, notice that ant.jar has been included in the Java classpath. The software in.jar (which is just RetroGuard itself) can be run under Apache Ant and so the Ant library must be accessible during obfuscation, as mentioned in access to libraries.

The result is out.jar which is 166kb as well as the retroguard.log file. We now examine retroguard.log and find the following lines:

# WARNING - Methods are called which may unavoidably break in obfuscated
# version at runtime. Please review your source code to ensure that these
# methods are not intended to act on classes within the obfuscated Jar file.
# Your class COM/rl/obf/Cl$ExtNameListUp uses '.class' or calls java/lang/Class.forName(Ljava/lang/String;)Ljava/lang/Class;
# Your class COM/rl/obf/Cl$ExtNameListUp calls the java/lang/Class method getDeclaredField(Ljava/lang/String;)Ljava/lang/reflect/Field;
# Your class COM/rl/obf/Cl$ExtNameListUp calls the java/lang/Class method getField(Ljava/lang/String;)Ljava/lang/reflect/Field;
# Your class COM/rl/obf/Cl uses '.class' or calls java/lang/Class.forName(Ljava/lang/String;)Ljava/lang/Class;

This is an example of RetroGuard discovering use of reflection methods. Since in.jar (which is RetroGuard here) uses reflection only to examine classes outside of its own jar, it is safe to ignore these warnings in this case.

[RetroGuard-v2.2.x only]

In future runs, we can suppress the warnings using the .nowarn <class> script entry.

If these reflection methods were used to reference classes, methods, or fields inside in.jar we would have to preserve those classes, methods, and fields from being obfuscated by listing them in the script file. Notice that it needs an understanding of how the obfuscated software will be used to correctly handle reflection. It is not even theoretically possible for RetroGuard to handle reflection automatically in all cases.

The internal default script has done a good job of obfuscating RetroGuard and reducing the application size. However, on examination of the log file, we see that the Apache Ant library interfaces have not been preserved. In general, it is necessary to preserve your software's library interfaces by listing them in the script file. There is no way for RetroGuard to decide automatically that those library entry points must be left unchanged.

Also, we see from the log that RetroGuard's internal default script is very conservative and has left several names unobfuscated that could safely have been changed. We will now construct an optimal script file specifically for this obfuscation process.

Optimal Obfuscation

[RetroGuard-v2.2.x only]

To begin, we could use RGdefault to output the internal default script:

java -cp retroguard.jar RGdefault > script.rgs

and then modify this as needed.

In this case, however, we'll build up the script.rgs file step by step. The simplest useful script for our example is:

.option Application
.class ** public method extends org/apache/tools/ant/Task
.nowarn COM/rl/obf/Cl*

which preserves the various application main methods and their classes, as well as the Apache Ant library methods. Note the use of the extends clause to limit the scope of the .class ** statement to classes derived from Ant Task.

[RetroGuard-v2.2.x only]

The .nowarn line suppresses warnings about the reflection methods.

This obfuscation run:

java -cp retroguard.jar:ant.jar RetroGuard in.jar out.jar script.rgs

results in an output jar 167kb in size.

[RetroGuard-v2.2.x only]

We now add the statement:

.option Repackage

to script.rgs which instructs RetroGuard to repackage all classes into the top level of the package hierarchy. This leads to a size reduction since the repackaged names are usually much shorter and they are referenced many times in each class. We now have an output jar 162kb in size.

[RetroGuard-v2.1.x and later]

Finally we add the statement:

.option Trim

which causes RetroGuard to trim any unused methods, fields, and even whole classes from the output. RetroGuard doesn't have much unused code; a few debugging statements. However, in an application which includes a large library but uses only a small part of that library the size reduction due to trimming would be considerable. Finally, then, we have an output jar 159kb in size, a 34% reduction from the original.

The size reduction seen during obfuscation depends very much on the detailed content and identifier names used in your software, and will vary greatly in other examples.

Null Obfuscation

Sometimes it can be useful to start with a null script; one that runs the obfuscator but preserves all class, method, and field names and keeps all class attributes. Here is such a script:

.class ** protected
.method;private ** *
.field;private ** *
.attribute SourceFile
.attribute LocalVariableTable
.attribute LineNumberTable
.attribute Deprecated
.attribute Signature
.attribute LocalVariableTypeTable
.attribute EnclosingMethod
.option Annotations

The various .attribute lines and .option Annotations preserve all class attributes. The .class ** protected statement preserves class names in all packages (the ** wildcard crosses package boundaries) and preserves public, package, and protected access methods and fields in all those classes. The private methods and fields, which are always safe to obfuscate, are preserved here using the .field and .method lines.

This obfuscation run strips the jar and its component classes apart, but renames nothing. From this starting point, obfuscation can be switched on statement-by-statement using the !class, !method, !field negation entries.


Prev   Contents   Next
 Copyright © 1998-2007 Retrologic Systems.
 All rights reserved.