Zelix KlassMaster - Java Obfuscator

Zelix KlassMaster™ Java Obfuscator and Obfuscation FAQ

Version 9.0

  1. How do I get started with Zelix KlassMaster™?
  2. Why wont Zelix KlassMaster™ run?
  3. Does Zelix KlassMaster™ support Java 8 and Java 9?
  4. What is the difference between the evaluation and licensed versions of Zelix KlassMaster?
  5. Will Zelix KlassMaster™ run in Windows, Mac OS X, UNIX or using the Microsoft SDK?
  6. Does Zelix KlassMaster™ support JEE™, Spring, Hibernate™, Java ME™ and Google Android™?
  7. Why don't all names change when I obfuscate?
  8. Wont string encryption slow down my code and make it bigger?
  9. When I change a method name in one class, why do some methods in other classes also get renamed?
  10. When changing a method name, why do I get the message "New name clashes with a method in Class name" when the class mentioned doesn't seem to be related to the class that defines the method?
  11. After being obfuscated why does my code get the runtime errors "ClassFormatError", "VerifyError", "NoClassDefFoundError" or "Can't find class Classname"?
  12. Can Zelix KlassMaster™ automatically handle Java Reflection API calls?
  13. When I open some classes why do I get the "Class Open Warnings" window?
  14. When I open some classes why do I get the "Class Open Errors" window?
  15. What options should I use when obfuscating my classes?
  16. What can I do if I get an OutOfMemoryError or "Insufficient Memory to open classes" error?
  17. Why does Zelix KlassMaster™ run so slowly?
  18. How do I obfuscate classes with native methods?
  19. I used the Zelix KlassMaster™ ZKM Script Helper to create a ZKM Script file but when I run it I get warnings. Why?
  20. Can Zelix KlassMaster™ obfuscate JSPs?
  21. Wont flow obfuscation slow down my code and make it bigger?
  22. Can I tell Zelix KlassMaster™ not to flow obfuscate certain methods?
  23. Why aren't all my methods flow obfuscated?
  24. What is the difference between "normal", "aggressive" and "flow obfuscate" String encryption?
  25. After obfuscation, why don't my classes work properly?
  26. What does the message "WARNING: changeLogIn fileName: Method oldMethodName in class className1 is mapped to new name newMethodName1 but the corresponding method in class className2 is mapped to new name newMethodName2. Using mapping of newMethodName2 for both methods" mean?
  27. How does the Input Change Log functionality interact with the name exclusion functionality?
  28. If I exclude a class from being obfuscated will the class's methods still be obfuscated?
  29. Can I edit a change log and then use it as an input change log so that I can predetermine new class, field or method names?
  30. I have a number of applets in the default package that I obfuscate separately. How do I stop Zelix KlassMaster™ from giving them the same name (e.g."a.class")?
  31. What is the easiest way of excluding non-static, non-transient fields from being renamed in serializable classes?
  32. My application will consist of a client jar file and server jar file which share some common classes. How do I obfuscate it?
  33. Does Zelix KlassMaster™ support incremental obfuscation?
  34. Can Zelix KlassMaster™ be called from a build tool such as Apache Ant?
  35. Can I access environment variables from within a ZKM Script?
  36. Our application uses Java Serialization. How does this impact obfuscation?
  37. Does Zelix KlassMaster™ support ResourceBundles as used in Localization and Internationalization?
  38. Why do I get a ClassCastException when I try to obfuscate bytecode compiled with Java 5 (i.e. JDK 1.5)?
  39. How do I refer to inner classes in ZKM Script statements?
  40. I am getting "Proguard returned with error code 1". How do I find out what is wrong?
  41. What settings do I use for Android™ Apps?
  42. What are default exclusions?
  43. Wont the Reference Obfuscation function slow down my code and make it bigger?

Answers

Q0. How do I get started with Zelix KlassMaster™?

It is recommended that you initially use the GUI Build Helper tool to get a feeling for the processing sequence and then to create your first ZKM Script. It is also highly recommended that you at least read the basic exclusions part of the ZKM Script Exclusions Tutorial before modifying your first ZKM Scripts.

See the Getting Started page of the documentation for more detail.

Q1. Why wont Zelix KlassMaster™ run?

There could be a number of problems.
  1. You cannot run Zelix KlassMaster™ using a Java 7 or earier JVM. You need Java 8 (JDK 1.8 or better).
  2. Zelix KlassMaster™ may not run on your operating system. See the answer to Q4.
If you are not getting a useful error message, try running with the -verify and -verbose switches of the JVM.

Q2. Does Zelix KlassMaster™ support Java 8 and Java 9?

Yes. Although, Zelix KlassMaster™ requires only Java 8 to run, it can handle Java 9 bytecode including modules.

Of course Zelix KlassMaster™ will also open and process Java 1.1 through to Java 8 bytecode. All you need to do is set the Zelix KlassMaster™ classpath to point to the appropriate bootstrap classes. For Java 2 through to Java 8 that is the corresponding rt.jar. For Java 9 it is the corresponding lib\jrt-fs.jar file system.

Q3. What is the difference between the evaluation and licensed versions of Zelix KlassMaster?

The major differences between the evaluation version and the commercial version are as follows:
  • The evaluation version will flow obfuscate only one or two methods in each class.
  • The evaluation version is time limited to thirty days.

Q4. Will Zelix KlassMaster™ run in Windows, Mac OS X, UNIX or using the Microsoft SDK?

Zelix KlassMaster™ is written entirely in Java 8 and technically should run on any platform that supports a Java 8 Virtual Machine. However, differences in the file systems and GUIs can cause problems. Also, Zelix KlassMaster's flow obfuscation technology can expose bugs in some Just In Time compilers. It has been tested on
  • Windows 10™ 64bit using
    • Oracle build 1.8.0_152-ea-b05 Java HotSpot(TM) 64-Bit Server VM (build 25.152-b05, mixed mode)
    • Oracle build 9+177 Java HotSpot(TM) 64-Bit Server VM (build 9+177, mixed mode)
  • Windows 8.1™ Pro 64bit using
    • Oracle build 1.8.0_152-ea-b05 Java HotSpot(TM) 64-Bit Server VM (build 25.152-b05, mixed mode)
    • Oracle build 9+177 Java HotSpot(TM) 64-Bit Server VM (build 9+177, mixed mode)
  • Linux (Xubuntu 16.04) using
    • Oracle build 1.8.0_152-ea-b05 Java HotSpot(TM) 64-Bit Server VM (build 25.152-b05, mixed mode)
    • Oracle build 9+176 Java HotSpot(TM) 64-Bit Server VM (build 9+176, mixed mode)
    • IBM build pxa6480sr4fp7-20170627_02(SR4 FP7) IBM J9 VM (build 2.8, JRE 1.8.0 Linux amd64-64 Compressed References 20170616_352529 (JIT enabled, AOT enabled)
  • MacOS Sierra™ 10.12.5 using
    • Oracle build 1.8.0_131-b11 Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
    • Oracle build 1.8.0_152-ea-b05 Java HotSpot(TM) 64-Bit Server VM (build 25.152-b05, mixed mode)
    • Oracle build 9+176 Java HotSpot(TM) 64-Bit Server VM (build 9+176, mixed mode)

Does Zelix KlassMaster™ support JEE™, Spring, Hibernate™, Java ME™ and Google Android™?

Yes. Zelix KlassMaster™ supports JEE. Its default exclusions and its SmartSave™ technology provide automatic support for servlets, the EJB 1 specification and the EJB 3 specification to the extent that you have used annotations rather than XML. If your application is an EJB 2 application that uses advanced features such as container managed persistence then we recommend that, for all classes directly related to the EJBs, you exclude
  • public class names and their containing package names,
  • public field names,
  • public method names.
If your EJB related classes are in specific packages then it should be quite easy to specify these exclusions in ZKM Script. Flow obfuscation and String encryption should cause no JEE problems.

Yes. Zelix KlassMaster™ supports Spring and Hibernate™ by automatically updating the XML to reflect any obfuscated names. However, there are some minor qualifiactions.

Yes. Zelix KlassMaster™ supports Java ME. There is a plugin for the Sun Java ME Wireless Toolkit. If you are not using the Sun Java ME Wireless Toolkit then see the Java ME obfuscation tutorial for more detail on the steps invloved.

Yes. Zelix KlassMaster™ supports Google Android™ by obfuscating the Java class files before they are converted into an APK file. Depending on your build environment you may need to use of Zelix KlassMaster™'s ProGuard compatability.

Q6. Why don't all names change when I obfuscate?

There are a number of reasons why all class, field or method names don't change when you obfuscate.
  • The name is public or protected and your obfuscate options say that public or protected names shouldn't be changed.
  • The name of a package scoped method is overridden to be protected or public in a subclass. If your obfuscate options say that protected or public method names shouldn't be changed then the overridden method name can't be changed without breaking the polymorphic link.
  • The name of a method overrides a method in a superclass or implements a method in an interface but the superclass or interface hasn't been opened inside Zelix KlassMaster. In such cases the method name cannot be changed without breaking the polymorphic link.

Q7. Wont string encryption slow down my code and make it bigger?

Encrypted strings are decrypted at runtime using code that has been added to your classes. This means that the classes will run slightly slower and will be bigger. How much slower and bigger depends on how many String literals there are in your bytecode and the option selected. Typically the performance impact is insignificant. The bytecode size increase if all Strings are encrypted is typically in the range of 10% to 30%. The enhanced setting will result in the biggest increase in bytecode size but it will also provide the best protection. It is recommended that you measure the impact on your classes.

Q8. When I change a method name in one class, why do some methods in other classes also get renamed?

When you change a method name, Zelix KlassMaster™ automatically changes all method names that are polymorphically linked to that method. So overridden methods in superclasses and overriding methods in subclasses must also be renamed. If the method is an implementation of an interface method then the method in the interface and the corresponding methods in all classes that implement that interface must also be renamed. So the change can ripple across to different branches of the class hierarchy.

Q9. When changing a method name, why do I get the message "New name clashes with a method in Class name" when the class mentioned doesn't seem to be related to the class that defines the method?

The answer to Q8 explains how a method name change can ripple across to different branches of the class hierarchy. So Zelix KlassMaster™ must check that the new name wont clash with existing names in these other classes. A name clash occurs if the proposed new signature
  • already exists in a class
  • already exists in a superclass or subclass so that a new polymorphic relationship would be created.

Q10. After being obfuscated why does my code get the runtime errors "ClassFormatError", "VerifyError", "NoClassDefFoundError" or "Can't find class Classname"?

This will happen if you obfuscate your bytecode using Java illegal identifiers and you have run you code with verification switched on. Verification is on:
  • in JDK 1.1.7 or less, for applications run with the -verify switch
  • in Java 2 or better by default unless you use the -Xverify:none switch
  • is always switched on for Applets.
You cannot use illegal identifiers if your classes must run after being verified.

Alternatively, you will get such messages even when not runing with verification if you have renamed your highest level package qualifiers so that class names are Java illegal when fully qualified. For example a class named com.1.0 will cause no problems when running without verification but a class named 1.1.0 will cause problems.

Q11. Can Zelix KlassMaster™ automatically handle Java Reflection API calls?

The short answer is yes. Zelix KlassMaster™'s AutoReflection™ functionality can automatically handle Java Reflection API calls. However, it is highly recommended that you also explicitly specify the subset of classes, fields and methods that are accessed by unresolved Reflection API calls by using the accessedByReflection and/or accessedByReflectionExclude statements.

If you choose not to use the AutoReflection™ functionality, Zelix KlassMaster™ can still automatically handle many simple 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 such resolved Reflection API calls by updating the String literal that holds the class, field or method name to reflect the corresponding obfuscated name.

Regardless of whether or not you use the the AutoReflection™ functionality, 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...".

Zelix KlassMaster™ may still be able to automatically process classes that appear in the Class Warnings list (even if not you don't use the AutoReflection™ functionality). For example, calls to Class.getName() are often made purely for debugging purposes. Also, in certain situations, Zelix KlassMaster™ can change the contents of a string containing a class name to match the new class name. In any case, the user must look at the way the call is used to determine if the class can be reliably processed automatically.

The bottom line is that if a particular Reflection API call is not handled automatically by Zelix KlassMaster™ then the package, class, field or method that is accessed by name by that call must be explicitly excluded from being renamed.

Q12. When I open some classes why do I get the "Class Open Warnings" window?

When Zelix KlassMaster™ opens a class that it might not be able to trim or obfuscate reliably it reports the fact using the "Class Open Warnings" window. You may get the Class Open Warnings window when an opened class contains a Java Reflection API call to a method like one of the following:
  • Class.forName(String)
  • Class.getName()
  • Class.getField(String)
  • Class.getMethod(String)
  • Class.getDeclaredField(String)
  • Class.getDeclaredMethod(String)
  • Field.getName()
  • Method.getName()
  • Constructor.getName()
  • ClassLoader.loadClass(String,boolean)
  • ClassLoader.defineClass()
  • ClassLoader.findSystemClass(String)
  • ClassLoader.findLoadedClass(String)
  • RMIClassLoader.loadClass(String)
  • RMIClassLoader.loadClass(URL, String)
  • LoaderHandler.loadClass(String)
  • LoaderHandler.loadClass(URL, String)
  • EventSetDescriptor(Class, String, Class, String)
  • EventSetDescriptor(Class, String, Class, String[], String, String)
  • EventSetDescriptor(Class, String, Class, String[], String, String, String)
Note that the Class Open Warnings window lists
  • the class in which the method call appears
  • the signature of the method in which the method call appears
  • the signature of the method being called.
It does not name the class, field or method that is being accessed by name. Please see the answer to Q11 for detail on what to do if you get "Class Open Warnings".

Q13. When I open some classes why do I get the "Class Open Errors" window?

Some obfuscators add corrupt information to bytecode files in the hope of confusing or crashing decompilers. Zelix KlassMaster™ detects this corrupt information and reports the fact using the Class Open Errors window? Corrupt Local Variable Table and Inner Class information is automatically removed.

Zelix KlassMaster™ will also report the existence of duplicate classes if the classes
  • appear in the same archive or
  • are not absolutely identical.

Q14. What options should I use when obfuscating my classes?

Inner classes

Generally, you don't need to keep the inner class information so you select "false" in the "Keep inner class information" list. This will reduce the size of your bytecode. Typically, JVMs make no use of inner class information at runtime.

However, compilers do make use of inner class information at compile time. So, if your classes form a library which third party classes will be compiled against and if some of your inner classes are exposed to the user then you will need to select either "true" or "if name not obfuscated" from the "Keep inner class information" list. Selecting "true" will preserve all inner class information.

Generics

Similarly, you generally don't need to keep the generics information of your classes so you select "false" in the "Keep generics information" list. Typically, JVMs make no use of generics information at runtime and removing the generics information will reduce the size of your bytecode.

However, compilers do make use of generics information at compile time. So, if your classes form a library which third party classes will be compiled against then you should select "true" from the "Keep generics information" list.

String encryption

The "encrypt string literals" option is generally safe. However, use of the "aggressive", "flowObfuscate" or "enhanced" settings can, in certain unusual circumstances, result in the contents of public static final String fields of some interfaces not being accessible to classes outside of the set that you have obfuscated. If this happens, you will get a warning message in the Zelix KlassMaster™ log. Also, note that String encryption does make your bytecode larger and slightly slower (see the answer to Q7).

Flow obfuscation

If you are obfuscating an applet note that there are known bugs in the JITs of the early browsers that can be exposed by flow obfuscation. If you flow obfuscate using the "light" option then the risk of JIT problems will be greatly reduced. Nonetheless, if you flow obfuscate your applet then test it thoroughly.

Application types

If your classes make up a self-contained application then it should be safe to change all package, class, field and method names other than those of the public static main(String[]) method and the class that contains it. You can achieve this by selecting the "Self contained application or applet" application type and by selecting your entry paint class in the "Don't change main class name" list.

If you are obfuscating a set of classes that will be used by third parties as a non-extensible class library then you should
  • not obfuscate public class names
  • not obfuscate public field or method names
You can achieve this is the GUI by selecting the "Non-extensible class library" application type.

If you are obfuscating a set of classes that will be used by third parties as an extensible framework then you should
  • not obfuscate public class names
  • not obfuscate public or protected field or method names
You can achieve this is the GUI by selecting the "Extensible framework" application type.

In either case you should
  • limit String encryption by either
    • using the "normal" setting or
    • using the "aggressive", "flowObfuscate" or "enhanced" settings but excluding all interfaces from String encryption using the ZKM Script stringEncryptionExclude statement. (e.g. "stringEncryptionExclude interface *.*;")
  • consider choosing the line numbers "scramble" option

If your classes form a complex EJB 2 application or (an EJB 3 application where you have used XML rather than annotations) then the easiest solution is to exclude all public classes, fields and methods that are directly related to the EJBs. Otherwise it should be safe to use light flow obfuscation and String encryption.

Line numbers

If the size of your bytecode is critical then select the "delete" line numbers option. However, if you are obfuscating for a beta release or if the size of your code isn't critical then you can select the "scramble" line numbers option. The code will be larger but you will find crashes much easier to track down.

Local variables

By default, Zelix KlassMaster™ deletes all local variable information. This information is primary used for debugging purposes and is not essential at runtime. However, the localVariables parameter of the ZKM Script obfuscate statement allows you to keep all or some of the local variable information or to obfuscate that information.

When using ZKM Script, you generally should use the "delete" local variable setting (which is the default). This is particularly the case if the size of your bytecode is important. However, if you are obfuscating a class library and need the method signatures to be previewable in IDEs then you should use the "keepVisibleMethodParameters" or "keepVisibleMethodParametersIfNotObfuscated" settings. (If you are using Java 8 then see the "Method parameters" section below.) See the documentation for the obfuscate functionality for more detail.

You can use the "obfuscate" setting to slightly complicate any attempts at decompilation but this will result in increased bytecode size when compared to the use of the "delete" setting.

Generally it is recommended that you never use the "keep" setting outside of your development environment.

Method parameters

Java 8 introduced the optional MethodParameters attribute which stores the names of your method parameters and some access flag information. These attributes are created only if you specify the -parameters compiler option.

When using ZKM Script, you generally should use the "delete" local variable setting (which is the default). This is particularly the case if the size of your bytecode is important. However, if you are obfuscating a class library and need the method signatures to be previewable in IDEs then you should use the "keepVisible" or "keepVisibleIfNotObfuscated" settings. See the documentation for the obfuscate statement for more detail.

Legal identifiers

It is highly recommended that you always use the "legal identifiers" option. This option is now available only in ZKM Script and only for the purposes of backwards compatibility. If you are obfuscating an application that doesn't have to run with verification switched on then you can get extra protection with this option but they will cause problems with modern JVMs.

The bottom line

Please note that whatever options you choose it remains your responsibility to fully test or retest your classes after they have been processed by Zelix KlassMaster™.

Q15. What can I do if I get an OutOfMemoryError or "Insufficient Memory to open classes" error?

Zelix KlassMaster™ holds a lot of information in memory for each class that it opens. So, the more classes you open and the more complicated those classes are the more memory you need. If Zelix KlassMaster™ tells you that there was insufficient memory to open your classes or if it crashes with an OutOfMemoryError then you can do one of the following:
  1. Give Java more memory to work with by using the -Xmx??m option where ?? represents the number of megabytes. (For example, the option -Xmx512m gives Java 512MB of heap space.)
  2. When you are running Zelix KlassMaster™ via Ant then you need to give more memory to Ant itself. An easy way of doing this is by setting the "ANT_OPTS" environment variable.
    e.g. set ANT_OPTS=-Xmx512m
  3. Use the "Tools/Garbage Collect" menu option or the gc ZKM Script statement after opening your classes and before obfuscating.
  4. Install more physical memory.
It is not recommended that you make use of virtual memory (by giving Java more memory to work with than is physically installed.) Performance will be unreasonably degraded as memory is swapped to and from the disk.

Q16. Why does Zelix KlassMaster™ run so slowly?

There are a number of reasons Zelix KlassMaster™ may run slowly.
  1. Your computer may have insufficient real memory installed. If your computer has to use virtual memory performance will be very poor. You will notice that the hard disk is being very heavily utilized.
  2. You are obfuscating while viewing a very large class or method. This results in reduced performance due to the overhead of updating the class or method details in the View Panel.

Q17. How do I obfuscate classes with native methods?

Zelix KlassMaster's default exclusions will automatically handle any native methods by ensuring that
  1. the names of the native methods themselves and
  2. the fully qualified names of the classes that contain them
will not be changed.

However, Zelix KlassMaster™ has no way of knowing if your native code accesses your Java fields or methods. If your native code does access any of your Java fields or methods then you will need to explicitly exclude the accessed member names and the fully qualified names of the classes that contain them from obfuscation.

Q18. I used the Zelix KlassMaster™ ZKM Script Helper to create a ZKM Script file but when I run it I get warnings. Why?

The current version of the ZKM Script Helper does not validate all of your choices to ensure they are not contradictory. For example, it will allow you to exclude final abstract methods even though such methods cannot exist. The parser will give you a warning in this case.

Q19. Can Zelix KlassMaster™ obfuscate JSPs?

Zelix KlassMaster™ cannot obfuscate JSP (Java Server Page) source but it can obfuscate the servlet that is generated from a JSP. However, problems can arise if a JSP servlet is automatically regenerated at runtime replacing the class produced by Zelix KlassMaster. If this can happen in your environment then you should specify your JSP servlet classes as "fixed" by using the ZKM Script fixedClasses statement. For example you could say:

fixedClasses *.* implements javax.servlet.jsp.JspPage;

Q20. Wont flow obfuscation slow down my code and make it bigger?

Generally, Zelix KlassMaster's flow obfuscation will slightly increase the size of your bytecode and it will reduce its performance. It is a trade off between the degree of protection against decompilation and bytecode size and speed. The table below shows the approximate bytecode size increase in a compressed JAR file that could be expected for typical applications. Note that the size increase will vary from application to application and you should measure the impact on your bytecode.
flow obfuscation
option used
approximate
size increase
light 2%
normal 3%
aggressive 7%
extraAggressive 10%

The performance impact of flow obfuscation varies depending upon
  • the bytecode that is being obfuscated,
  • the JVM used to execute the bytecode,
  • the flow obfuscation option used.
When the HotSpot™ 1.8.0 JVMs are used, the performance impact for general purpose applications is insignificant. However, the performance impact on extremely CPU intensive applications will be greater. Also, the the impact when other JVMs (e.g IBM) using JIT compiler technology will be greater. Again, it is recommended that you measure the impact on your classes. If you experience an unacceptable performance impact then you should
  • use "light" flow obfuscation,
  • use the ZKM Script obfuscateFlowExclude statement to exclude performance critical methods from flow obfuscation.

Q21. Can I tell Zelix KlassMaster™ not to flow obfuscate certain methods?

You can use the ZKM Script "obfuscateFlowExclude" statement to specify methods that you don't want flow obfuscated. This functionality is not yet available in the GUI interface. (Note that the name exclusion functionality has no impact at all on flow obfuscation.)

Q22. Why aren't all my methods flow obfuscated?

Zelix KlassMaster™ only attempts to obfuscate the flow of methods that already have some control flow complexity. So methods without if, switch, while or for constructs will not be flow obfuscated. Also there are small percentage of methods with some control flow complexity that Zelix KlassMaster™ cannot yet flow obfuscate. Finally, note that the evaluation version will only flow obfuscate one or two methods in each opened class.

Q23. What is the difference between "normal", "aggressive" and "flow obfuscate" String encryption?

"Normal" String encryption encrypts all String literals that are accessed by methods in the defining class. This can mean that the values of some final String fields were not obfuscated. For example, String constants in interfaces would typically not be encrypted.

"Aggressive" String encryption uses a more sophisticated approach that can normally encrypt Strings even in interfaces.

The "flow obfuscate" option is the same as the "aggressive" option but causes the runtime decryption instructions, that is inserted into your bytecode, to be flow obfuscated. This makes the decrypt instructions harder to decompile but it could cause problems with some JITs. Note that the limitations mentioned above for the "aggressive" option also apply for the "flow obfuscate" option. Note also that the String Encryption "flow obfuscate" option is quite independent of the general Flow Obfuscation functionality. You can choose to have one without the other.

Q24. After obfuscation, why don't my classes work properly?

If you get any of the runtime errors "ClassFormatError", "VerifyError", "NoClassDefFoundError" or "Can't find class Classname" see the answer to Q10.

Another reasons for errors such as "NoClassDefFoundError", "Can't find class Classname" or "NoSuchMethodError" are:
  • You didn't open all your classes inside Zelix KlassMaster™ before obfuscating
  • You application
    • uses the Java™ Reflection API and
    • Zelix KlassMaster™ has reported that there are some Reflection API calls in your bytecode that it may not be able to automatically handle and
    • you didn't exclude the names of the classes, fields or methods that are accessed by those Reflection API calls.
    Please see the answer to Q12 for more detail.

Finally, if you used Flow Obfuscation and your classes are causing the VM to crash (typically with a memory access error) or if you are getting an unexpected NullPointerException, you may be running foul of a bug in the JIT. To test this, run your application with the JIT switched off. If the problems occur only when the JIT is being used you should:
  • Upgrade to a more recent JIT. Of course it isn't always practical to ask your users to upgrade their JITs. JITs that are known to cause bugs with flow obfuscated bytecode are:
    • Microsoft VMs earlier than 5.00.3167
    • Symantec JITs earlier than 3.00.072b.
    • some of the IBM 1.3 JVMs
  • If you obfuscated with "aggressive" flow obfuscation, try the "normal" setting.
  • If you obfuscated with "normal" flow obfuscation, try the "light" setting. The "light" setting should eliminate most problems.
  • If you can identify the flow obfuscated method that is triggering the JIT bug then you can use the ZKM Script obfuscateFlowExclude statement to exclude that method from flow obfuscation.
  • As a last resort you can switch off flow obfuscation altogether by using the "none" option.

Q25. What does the message "WARNING: changeLogIn fileName: Method oldMethodName in class className1 is mapped to new name newMethodName1 but the corresponding method in class className2 is mapped to new name newMethodName2. Using mapping of newMethodName2 for both methods" mean?

The warning message indicates that the method name mappings in the Input Change Log are inconsistent for the opened classes. This can happen if
  • you have directly edited the change log file and make some mistakes or
  • your application has changed so that there are now polymorphic relationships between two or more methods that where previously unrelated.
In either case, Zelix KlassMaster™ handles the situation automatically to ensure the obfuscated bytecode is OK.

Q26. How does the Input Change Log functionality interact with the name exclusion functionality?

If you are using the GUI or if you specified an input change log using the changeLogFileIn parameter of the ZKM Script obfuscate statement then Zelix KlassMaster™ will always try to rename packages, classes, fields and methods to the names specified by the input change log. All the other statements such as the exclude and obfuscate statements remain fully active but generally they will only have impact on the names of packages, classes, fields and methods that do not already appear in the input change log.

So, for example, if the input change log clashes with the exclusions that have been set (or haven't been set) then warnings will be generated but the input change log will be given priority. The only exceptions are where:
  • the input change log contains inconsistent or conflicting information. For example if it
    • contains duplicate new package, class, field or method names
    • contains fully qualified new class names that conflict with the corresponding new package name
  • the input change log contains information that cannot be validly applied to the opened classes. For example if it
    • would incorrectly make or break a polymorphic relationship between methods.
However, if you specified an input change log using the looseChangeLogFileIn parameter of the ZKM Script obfuscate statement then your exclusions will take precedence over the change log mappings. However, this could mean that the newly obfuscated classes will not be fully consistent with your previous release.

Q27. If I exclude a class from being obfuscated will the class's methods still be obfuscated?

Yes. If you specify a class in the exclude statement or using the GUI's Obfuscate Advanced Name Exclusion Options Dialog, you are excluding only the class name from obfuscation. Excluding a class name from obfuscation has no effect
  1. on the obfuscation of that class's field and method names or
  2. on the flow obfuscation or String encryption of any of its methods. To exclude methods from flow obfuscation you must use the ZKM Script obfuscateFlowExclude statement. To exclude String literals from encryption you must use the ZKM Script stringEncryptionExclude statement.

Q28. Can I edit a change log and then use it as an input change log so that I can predetermine new class, field or method names?

Yes but be careful. If you are using a change log that was generated when you obfuscated a release of your application or applet then make sure you edit a copy. You may still need the original to interpret stack dumps from the released bytecode.

Also, be careful when you choose you predetermined names. It's a complicated business - especially if you are predetermining method names. Zelix KlassMaster™ will perform a large number of cross checks on your input change log and on your opened classes but it may not check for every possible conflict. If Zelix KlassMaster™ gives you warnings then read them carefully.

Note that Zelix KlassMaster's ZKM Script language allows you to specify more than one input change log. So you could specify an small, edited input change log that specifies only your changes followed by the full, original input change log. This can be a safer approach.

Q29. I have a number of applets in the default package that I obfuscate separately. How do I stop Zelix KlassMaster™ from giving them the same name (e.g."a.class")?

It causes problems when different applets (or different classes used by different applets) are given the same name. Browsers and proxy servers can get confused. The recommended solution is to put your applets in different packages that are unique to your organisation and to exclude those package names from obfuscation. If you then obfuscate the package as a whole, then each class will be given a unique name within the name space of the package.

However, it is recognized that, for various reasons, not everyone wants to do this. The best alternative way of handling this situation is to use Zelix KlassMaster's Input Change Log functionality. Perform a preliminary obfuscation with all of your applet classes opened inside Zelix KlassMaster. Don't bother saving the obfuscated classes. All you want is the "cross applet" change log with each class being given a unique name. Now obfuscate each of the individual applets using the "cross applet" change log as an input change log so that each class again gets its unique name. Retain the "cross applet" change log for use as an input change log in any subsequent releases of your applets. You only need save the individual applet change logs if you have used line number scrambling.

Q30. What is the easiest way of excluding non-static, non-transient fields from being renamed in serializable classes?

Using the ZKM Script interface you can accomplish this easily with a statement like:

exclude *.* implements java.io.Serializable !static !transient *;

Using the GUI interface's Advanced Exclusion Options dialog, you can select "!static" and "!transient" from the drop down lists.

Q31. My application will consist of a client jar file and server jar file which share some common classes. How do I obfuscate it?

Obfuscate your application as a whole in one pass. Zelix KlassMaster™ version 3 or better automatically handles issues relating to obfuscating multiple JAR files.

If you will sometimes need to release just a changed client or server jar file then you should use Zelix KlassMaster's Input Change Log functionality to ensure consistent renaming across your releases. The steps are
  1. Initially obfuscate your client and server jar files together in one pass generating an overall change log.
  2. If you make changes to only one of the jar files then obfuscate both your client and server jar files together in one pass once more using the change log generated in the step above as an input change log. Release just the changed jar.
  3. Use the change log generated in the second step as the new overall change log.

Q32. Does Zelix KlassMaster™ support incremental obfuscation?

Yes. Zelix KlassMaster's Input Change Log functionality ensures
  1. consistent package, class, field and method renaming and
  2. consistent flow obfuscation
across releases. It is generally recommended that you always open ALL the classes of the application inside Zelix KlassMaster. This gives Zelix KlassMaster™ the opportunity to ensure that all references are consistent. This is the case even if you are planning to distribute just the changed class subset in the form of some kind of patch.

Zelix KlassMaster™ does provide some limited ability to obfuscate a subset of your application provided the mappings for the full application appears in an input change log. To do this you should set the allClassesOpened parameter of the obfuscate statement to false and set the classpath so that the unobfuscated versions of all the unopened classes of the application can been located.

However, if you are using the Trim function then you must still open your application as a whole.

Q33. Can Zelix KlassMaster™ be called from a build tool such as Apache Ant?

Yes. Zelix KlassMaster™ exposes a simple, generic Java API for use by build tools. It also provides an Ant task called ZKMTask. See the online documentation for a full description of the API and for extra detail on the Ant task definition.

Q34. Can I access environment variables from within a ZKM Script?

To access an enviroment variable such as PATH from within your ZKM script you should firstly add the environment variable to the System properties when you start Zelix KlassMaster. Use this syntax for Windows:

java -D"PATH=%PATH%" -jar ZKM.jar

and this syntax for Unix

java -DPATH=$PATH -jar ZKM.jar

If you running Zelix KlassMaster™ through Apache Ant then you can either add the environment variable to the Ant project properties or use the Zelix KlassMaster™ Ant task (ZKMTask) to add the environment variable to the System propeties. (See the online documentation for more detail.)

Then access the variable in your ZKM Script by using the %...% delimiters. For example %PATH%. The Zelix KlassMaster™ preprocessor will perform the substitution.

Q35. Our application uses Java Serialization. How does this impact obfuscation?

If you never have to deserialize a non-obfuscated class with a obfuscated class then Serialization presents no problems. Zelix KlassMaster™ will automatically handle the necessary exclusions using its defaults and its Input Change Log functionality can be used to ensure consistent renaming across releases. However, if you need to deserialize already existing (ie. unobfuscated) serialized instances using your obfuscated classes then your obfuscated classes need to be compatible for Serialization purposes with your unobfuscated classes.

To achieve this compatibility you need to use the ZKM Script existingSerializedClasses statement to specify the classes that have existing serialized instances. For each class matched by the statement, Zelix KlassMaster™ will
  • add name exclusions for
    • the fully qualified name of the class
    • the name of every field contained or inherited by the class
    • the fully qualified class name of the type of each non-primitive field contained or inherited by the class
  • add a serialVersionUID field to the class (if it doesn't already exist) set to a value based upon the non-obfuscated class definition.

Q36. Does Zelix KlassMaster™ support ResourceBundles as used in Localization and Internationalization?

The issue here is whether Zelix KlassMaster's obfuscation of class names will effect ResourceBundle.getBundle(String, Locale) calls.

PropertyResourceBundles

In the case of PropertyResourceBundles, because they are backed by properties files and don't involve Java class files, they are generally unaffected by obfuscation. For example, a properties file with the file name "pack1/pack2/dir3/MyProperties.properties" will be moved to be "a/a/dir3/MyProperties.properties" if the packages "pack1.pack2" are obfuscated to be "a.a".

However, if a property file backing a PropertyResourceBundle
  1. Has a name containing an unqualified class name (e.g. "pack1.pack2.MyClass0.properties") and
  2. The name of the class is used to get the name of the resource bundle (e.g. "ResourceBundle.getBundle(getClass().getName())")
then the code will be broken if the unqualified class name is obfuscated. This because the properties file will be moved to reflect obfuscated package names but it will not be renamed to reflect an obfuscated unqualified class name. In such a case you must explicitly exclude the unqualified class name from being obfuscated.

ListResourceBundles

On the other hand, ListResourceBundles are backed by class files. If you create your ListResourceBundles by specifying the fully qualified base class name in a String literal and if the base class is within a package then Zelix KlassMaster™ will automatically change the value of the String literal to match the new obfuscated base class name. So the following code would be automatically handled (as far as the base class is concerned).

ResourceBundle myBundle = ResourceBundle.getBundle("package1.Class1", myLocale);

However, some additional work would still have to be done to handle the subsidiary ListResourceBundle classes such as
  • package1.Class1_de,
  • package1.Class1_de_DE,
  • package1.Class1_fr and
  • package1.Class1_fr_CH
Basically, a class suffix exclusion statement must be used for each language code, country code and locale variant combination to ensure that the subsidiary classes are renamed in synchronization with the base class with the suffixes being retained. An example ZKM Script exclusion statement would be:

exclude *.<link>_de and
        *.<link>_de_DE and
        *.<link>_fr and
        *.<link>_fr_CH;
If your organization uses a standard set of localizations, you could consider adding such an exclusion to your defaultExclude.txt file so that it is automatically applied.

Otherwise, if your ListResourceBundle class names are not stored as fully qualified names in String literals or if the classes are not within packages then you would have to explicitly exclude the base class and subsidiary class names from being obfuscated.

Another issue with ListResourceBundles is that they must be explicitly from being trimmed if you use the trim function. This can be done as follows.

trimExclude *.* extends java.util.ListResourceBundle

Q37. Why do I get a ClassCastException when I try to obfuscate bytecode compiled with Java 5 (i.e. JDK 1.5)?

A ClassCastException can occur if you obfuscate bytecode compiled with the JDK 1.5 or better with Zelix KlassMaster™ 4.2 or earlier. It is due to the fact that the JDK 1.5 changed the JVM specification for the loading of constants in a fundamental way.

Zelix KlassMaster™ (version 4.3 and better) has been enhanced to support bytecode created with the JDK 1.5. However, if you compile with the JDK 1.5 using the "-source 1.4 -target 1.4" parameters then you may still be able to use earlier versions of Zelix KlassMaster™ without further difficulties.

Q38. How do I refer to inner classes in ZKM Script statements?

Inner classes are compiled to be top level classes. If you have an inner class Inner within the class com.mycompany.Outer then that inner class Outer.Inner will be compiled to the class file com/mycompany/Outer$Inner.class. At the level of the bytecode, the name of that inner class will be com.mycompany.Outer$Inner. Note the presence of the '$' character in the unqualified class name.

When you refer to inner classes in ZKM Script statements, you need to use the bytecode level name. So you would use the name com.mycompany.Outer$Inner and not com.mycompany.Outer.Inner.

Because all inner classes will contain the '$' character in their unqualified class name, you could exclude all inner classes with the following ZKM Script exclude statement. However, the statement would also match any non-inner classes that have been given a name including an embedded '$' character.

exclude *.*$*;

Q39. I am getting "Proguard returned with error code 1". How do I find out what is wrong?

You can get this error when you use Zelix KlassMaster™ to take the place of the ProGuard obfuscator in the Google Android Eclipse plugin. You should first look in the ZKM_PG_log.txt file. This is a special log file which Zelix KlassMaster™ uses to write messages relating to the translation of a ProGuard configuration file.

The ZKM_PG_log.txt file will also give you the location of the Zelix KlassMaster log file which is named ZKM_log.txt by default. You should look in the Zelix KlassMaster log file for any warnings or errors. See the ProGuard Configuration File Translation Tutorial Troubleshooting section for more detail.

Q40. What settings do I use for Android™ Apps?

Below is an example ZKM Script for the obfuscation of Java classes before they are compiled into an APK. It should be adequate for most Android™ Apps. Obviously it would have to be modified for your environment.
classpath   "/usr/local/java/android-sdk/platforms/android-9/android.jar"
            ;

open        "bin/classes\*"
            "libs\*"
            ;

trimExclude public *.^*^ extends android.app.Activity and 
            public *.^*^ extends android.app.Application and 
            public *.^*^ extends android.app.Service and 
            public *.^*^ extends android.content.BroadcastReceiver and 
            public *.^*^ extends android.content.ContentProvider and 
            public *.^*^ extends android.view.View public <init>(android.content.Context) and 
            public *.^*^ extends android.view.View public <init>(android.content.Context,android.util.AttributeSet) and 
            public *.^*^ extends android.view.View public <init>(android.content.Context,android.util.AttributeSet,int) and 
            public *.^*^ extends android.view.View public set*(*) and 
            *.^*^ containing {public <init>(android.content.Context,android.util.AttributeSet)} public <init>(android.content.Context,android.util.AttributeSet) and 
            *.^*^ containing {public <init>(android.content.Context,android.util.AttributeSet,int)} public <init>(android.content.Context,android.util.AttributeSet,int) and 
            *.* extends android.content.Context public *(android.view.View) and 
            *.* extends android.content.Context public *(android.view.MenuItem) and 
            *.* implements android.os.Parcelable static android.os.Parcelable$Creator CREATOR and 
            *.R$* public static * and 
            *.* @android.webkit.JavascriptInterface *(*)
            ;

trim        deleteDeprecatedAttributes=true
            deleteSourceFileAttributes=true
            deleteAnnotationAttributes=false
            deleteExceptionAttributes=true
            //deleteUnknownAttributes=true
            ;

exclude     public *.^*^ extends android.app.Activity and 
            public *.^*^ extends android.app.Application and 
            public *.^*^ extends android.app.Service and 
            public *.^*^ extends android.content.BroadcastReceiver and 
            public *.^*^ extends android.content.ContentProvider and 
            public *.^*^ extends android.view.View public <init>(android.content.Context) and 
            public *.^*^ extends android.view.View public <init>(android.content.Context,android.util.AttributeSet) and 
            public *.^*^ extends android.view.View public <init>(android.content.Context,android.util.AttributeSet,int) and 
            public *.^*^ extends android.view.View public set*(*) and 
            *.^*^ containing {public <init>(android.content.Context,android.util.AttributeSet)} public <init>(android.content.Context,android.util.AttributeSet) and 
            *.^*^ containing {public <init>(android.content.Context,android.util.AttributeSet,int)} public <init>(android.content.Context,android.util.AttributeSet,int) and 
            *.* extends android.content.Context public *(android.view.View) and 
            *.* extends android.content.Context public *(android.view.MenuItem) and 
            *.* implements android.os.Parcelable static android.os.Parcelable$Creator CREATOR and 
            *.R$* public static * and 
            *.* @android.webkit.JavascriptInterface *(*)
            ;

obfuscate   keepGenericsInfo=false
            keepInnerClassInfo=false
            obfuscateFlow=none //can change from none to light, normal or aggressive
            encryptStringLiterals=none //can change from none to normal, aggressive, flowObfuscate or enhanced
            exceptionObfuscation=none //can change from none to light or heavy
            autoReflectionHandling=none //can change from none to normal
            randomize=false //can change from false to true
            collapsePackagesWithDefault=""
            preverify=false //don't need to preverify if only used for Android
            mixedCaseClassNames=ifInArchive
            keepBalancedLocks=true //ensures compatability with ART verifier
            ;

saveAll     archiveCompression=all
            "bin/classes-processed.jar"
            ;

Q41. What are default exclusions?

By default, Zelix KlassMaster™ will exclude certain classes, fields and methods from being trimmed by the Trim function or renamed by the Name Obfuscation function. However, you may override these defaults with your own.

In the case of the Trim function, Zelix KlassMaster™ looks for a file in the current user directory named "defaultTrimExclude.txt". If it finds the file then it will use its contents as the default trim exclusions. You can tell Zelix KlassMaster™ to use a different file for default trim exclusions by using the -dte command line option.

In the case of Name Obfuscation, Zelix KlassMaster™ looks for a file in the current user directory named "defaultExclude.txt". If it finds the file then it will use its contents as the default exclusions. You can tell Zelix KlassMaster™ to use a different file for default exclusions by using the -de command line option.

If the default exclusion file cannot be found then Zelix KlassMaster™ applies its predefined internal default exclusions. If the defaultExclude.txt file exists but is is empty then there there will be no default exclusions. The same applies for the default trim exclusion file.

Q42. Wont the Reference Obfuscation function slow down my code and make it bigger?

Yes it will. Although you could obfuscate ALL of the field and method references in your application, it would have a dramatic and unacceptable impact on your application's performance. Reflection API calls are much slower than direct references.

The Reference Obfuscation functionality is intended to be used sparingly to obscure references in and to sensitive parts of your application. It can make decompiled code quite incomprehensible. It can also obscure key API calls which otherwise would allow a hacker to "zero in" on a particular part of your bytecode.

When you use the Reference Obfuscation functionality to obscure sensitive parts of your application you should be careful to also obscure a significant number non-sensitive parts. Otherwise you will be drawing attention to the sensitive parts which would be counter productive. However, you should be careful not to unnecessarily obfuscate references in performance sensitive parts of your code such as within deeply nested loops.