AutoReflection Tutorial
This tutorial is divided into the following sections:
The Java Reflection API allows you to access classes, fields or methods by name. Examples of Reflection API calls are
- Class.forName(String),
- Class.getField(String),
- Class.getMethod(String),
- Class.getDeclaredField(String) and
- Class.getDeclaredMethod(String).
Of course, Name Obfuscation can change the names of classes, fields and methods. This means that Name Obfuscation can break Java Reflection API calls.
When Zelix KlassMaster opens your classes, it detects Java Reflection API calls that access classes, fields or methods and
it automatically looks for the source of the class, field or method name which is being passed to the Reflection API method.
If Zelix KlassMaster can find a String literal which contains the name of the class, field or method then it is said to have "resolved" that call.
Zelix KlassMaster automatically handles resolved Reflection API calls by updating the String literal that holds the class, field or method name to reflect the corresponding obfuscated name.
If Zelix KlassMaster cannot fully resolve a Reflection API call then it lists the unresolved call in the Class Open Warnings window if you are using the GUI or
in the Zelix KlassMaster log (which is named "ZKM_log.txt" by default) under the heading "API calls detected that may not be handled automatically...".
Prior to the introduction of the AutoReflection functionality, if you had unresolved Reflection API calls which were being broken by Name Obfuscation, then you
had to explicitly exclude from being renamed the name of the class, field or method being accessed by name.
The AutoReflection functionality allows the names of such classes, fields and methods to be obfuscated.
AutoReflection works by creating a lookup table which maps a hash of the original class, field or method names to the corresponding obfuscated name.
Your bytecode is then changed to make use of the lookup table so that any original name that would be used in an unresolved Reflection API call is replaced with the corresponding obfuscated name.
A key thing to note here is that only the names of classes, fields and methods that are actually accessed by unresolved Reflection API calls need to be in the lookup table.
However, because the Reflection API calls are unresolved, Zelix KlassMaster doesn't know which names to add to the lookup table.
So it is highly recommended that you tell Zelix KlassMaster which names to add to the looup table by using the ZKM Script accessedByReflection
and/or accessedByReflectionExclude statements.
The other thing to note is that a hash of the original, unobfuscated class, field or method names is stored in the lookup table within the obfuscated bytecode.
Provided the original class, field or method names are of a reasonable length (e.g. 11 characters or more) then the hash values are reasonably safe from brute force attack.
Zelix KlassMaster will report warnings if overly short names are being added to the lookup table.
Nonetheless, as is mentioned above, it is highly recommended that you tell Zelix KlassMaster which names to add to the lookup table so that it contains as few names as is possible.
In particular, you do not want any overly short names to be added to the lookup table if they are not actually being accessed by unresolved Reflection API calls.
If you leave any any overly short names in the lookup table unecessarily then you are needlessly providing a mechanism for reversing the name obfuscation of those names.
When you are using the Zelix KlassMaster GUI interface, you switch on AutoReflection by clicking the "auto reflection handling" box on the Obfuscate Options window.
However, there is no way of telling Zelix KlassMaster which names to add to the looup table using the GUI interface so this method is only recommended for initial familiarization purposes.
When you are using Zelix KlassMaster's ZKM Script interface, you switch on AutoReflection by using the autoReflectionHandling=normal setting on the obfuscate statement.
By default, the SHA-512 or SHA-1 hash algorithm will be used depending upon the Java version of your bytecode. If for some reason you need to specify a different algorithm then you can do so
by using the autoReflectionHash parameter of the obfuscate statement. For example you could say autoReflectionHash="MD5" .
Of course, the algorithm that you specify must be available in the runtime environment.
You can also optionally specify the package into which the AutoReflection lookup class should be placed.
You do this by using the autoReflectionPackage parameter of the obfuscate statement.
However, the specified package name must exist within the classes that have been opened for obfuscation.
As mentioned above, you can (and should) tell Zelix KlassMaster which names to add to the looup table by using the ZKM Script accessedByReflection
and/or accessedByReflectionExclude statements.
If neither statement precedes your obfuscate statement, then all class, field and method names which could be accesssed by the detected Reflection API calls will be added to the lookup table which is not recommended.
If only an accessedByReflection statement precedes your obfuscate statement,
then only the names of the classes, fields and methods that it specifies will be added to the lookup table.
If only an accessedByReflectionExclude statement precedes your obfuscate statement,
then the set of names added to the lookup table will be the set of all class, field and method names less the names of the classes, fields and methods that the statement specifies.
If an accessedByReflection statement followed by an accessedByReflectionExclude statement precedes your obfuscate statement,
then the set of names added to the lookup table will be the set of class, field and method names specified by the accessedByReflection statement
less the names of the classes, fields and methods specified by the accessedByReflectionExclude statement.
1. As mentioned above, AutoReflection puts a hash of the original name into a lookup table within the obfuscated bytecode. This hash can be reversed by a brute force attack but,
provided the original class, field or method names are of a reasonable length (e.g. 11 characters or more), then the hash values are reasonably secure.
In any case, if a class, field or method name is accessed by an unresolved Reflection API call then the alternative to AutoReflection is to leave that name totally unobfuscated.
2. The AutoReflection functionality has no effect on the Trim function. If you are using the Trim function then your ZKM Script trim statement should mirror your
accessedByReflection and/or accessedByReflectionExclude statements to exclude those classes, fields or methods that you know will be accessed via unresolved Reflection API calls.
3. There is a limit to the number of class, field and method names that can be added to the AutoReflection lookup table. The limit is high (about 25,000) but it is highly recommended that you use the
accessedByReflection and/or accessedByReflectionExclude statements to ensure that the table contains only those entries
that are actually required.
4. AutoReflection should only be applied once on a given set of classes. If you are applying a number of obfuscation steps on a given set of classes then the autoreflection should be applied in the last step which
involves the name obfuscation of any relevant classes, fields or methods.
5. AutoReflection is incompatible with the allClassesOpened=false setting of the obfuscate statement. This means that all the classes must be opened for obfuscation
if you are going to use AutoReflection. Zelix KlassMaster will report a fatal error if you specify both allClassesOpened=false and autoReflectionHandling=normal
6. AutoReflection will not work in Java 1.0.2 or JavaME environments because there is not the necessary support for hash algorithms.
If you find yourself needing to debug your AutoReflection exclusions when running a ZKM Script then you may find it helpful to run Zelix KlassMaster using the "-v" (ie. verbose) parameter.
This will result in a great deal of extra information being written to the log. This extra information will include
- which classes, fields or methods were directly matched by a particular accessedByReflection or accessedByReflectionExclude exclusion parameter,
- which Reflection API calls were "handled" by AutoReflection and
- which class, field or method names where included in the AutoReflection lookup table.
If you see class, field or method names being included in the AutoReflection lookup table and you know that those names would not be accessed by Reflection
then you can add or modify your accessedByReflection or accessedByReflectionExclude statements accordingly.
Note that AutoReflection does not support the Zelix KlassMaster's Method Parameter Changes functionality.
That is, if you access a method via the Reflection API and then change that method's parameters then AutoReflection will not handle the change.
|