Although KlassMaster is best known for its obfuscation capabilities, there are several extra features in the currently available version of KlassMaster, which are intended to help a Java programmer be able to better maintain both the original and obfuscated versions of the program. Possibly one of the most prominent functions beyond obfuscation which Zelix utilizes as a marketing point, is a trimming function to help remove unused functions, fields, and classes from a given project. Such a tool is useful in order to minimize the size of a given project. Further features include a disassembler in which the Java bytecode can be examined, both before and after a trim or obfuscation procedure; and a stack trace translator, which translates the error reports submitted by users into ones that can be used by programmers. To make the trimming and obfuscating processes both easier on the programmer and repeatable, KlassMaster includes a ZKM Script Helper, which is used to automate the obfuscating and trimming procedures on a project.
A similar procedure is used to obtain a licensed version of Zelix KlassMaster. The only notable differences are that the e-mail may contain additional information, including the license key and the invoice. The restrictions on the usage of free e-mail services appears to be slightly relaxed as well, since such e-mail addresses are allowed only for non-credit card purchases.
In order to be able to run the program, Java 5 is required on the host machine. Actual installation of the demonstration version is as simple as decompressing the downloaded ZIP or TAR.GZ archive and placing the contents into their own directory. Normal execution of the program can be accomplished by the command 'java -Xmx256m -jar ZKM.jar'. If a user chooses to add ZKM.jar to the classpath, KlassMaster may instead be executed by the command 'java -Xmx256m com.zelix.ZKM'.
For the more advanced KlassMaster user, there is a more advanced means of calling upon the Trim or Obfuscate functions, without making use of the graphical interface. This is accomplished through the ZKM scripting language, whose primary benefit is to be able to provide uniformity of operations from one project to the next, without needing to reset all of the program settings each time. The scripts are plain text files, containing various settings to be used for the obfuscating or trimming processes. Such a script can be generated by hand, or there is a wizard to produce such a script, from within the graphical interface. Once such a script is saved, it can be executed on a particular project by executing the command 'java -jar ZKM.jar <scriptFileName>'.
Regardless of whether KlassMaster's features are invoked through the GUI or through the scripting language, there are a number of settings in each which users can set, in order to control the extent to which each operates. For the purposes of trimming a program, the first setting is for the Trim Exclusions. This list of items specifies to KlassMaster the items which should not be pruned during the Trim operation. One general example that is almost always included in this list is the 'main' method entry point in the program. Once the exclusions are set, KlassMaster then asks for the types of information to be trimmed from the bytecode, such as information about the source file, or regarding deprecated functions. Once these two settings are completed, KlassMaster is able to invoke the Trim operation on the specified program.
A similar procedure is used for the Obfuscating process. Again, the first information that is needed by KlassMaster is a list of exclusions of functions, classes, or other such artifacts in the program which should not be obfuscated. Once this is completed, the next setting controls how the Obfuscate function operates. These settings include whether to produce a change log, the degree of control flow obfuscation, and the degree to which methods are renamed. Once this procedure is completed, the user need only save the final version of the project to a new location in order to make the Trimmed or Obfuscated version of the program a tangible product on the computer.
KlassMaster has several ways in which code can be obfuscated. Possibly the most trivial is by name obfuscation, in which descriptive names of methods or classes are replaced with less meaningful ones. Although the name obfuscation mechanism is not described to any extent among the documentation, it can most likely be likened to the Refactoring operation in various development environments. One additional note here, though, is that name obfuscation allows for what the documentation describes as "incremental obfuscating;" that is, as the program is modified, the new parts of the program may be obfuscated to match the naming of the preexisting parts. Such information is likely written into the changelog file, to ensure such consistency across many obfuscation operations.
There are two additional functions which, like the name obfuscator, have limited details beyond the information given in the documentation: the control flow obfuscator, and the trimming engine. However, despite this, one can still make some educated guesses as to how these might work. In the features listing, the control flow obfuscator is described as taking various control constructs in a program, and adjusting their bytecode in such a way as that they do not have direct counterparts in the Java sourcode. One means that this can be accomplished is to have a predetermined pattern in which to obfuscate 'if', 'while', 'for', and other constructs. One point which is visible in the obfuscated code, however, is a surprising increase in the number of labels in the program. It is likely the use of these labels and the required organization to utilize so many at once, that confuses most Java decompilers.
Additionally, the mechanism used for trimming the unused methods, fields, or classes within a Java program can also be theorized about. In order for a program to distinguish unused program data from used data, KlassMaster would likely need to develop a graph of program structures. The entries or vertices in this graph are classes, methods, or fields which exist in the program, and edges in the graph indicate that a method, class, or field was used. Once such a graph is constructed, the vertices which are not connected in any way to the main portion of the graph could be disposed of. This certainly allows for the trimming of a Java program, though it is unclear if this, like the other algorithms mentioned for name obfuscation or control flow obfuscation, is similar to the algorithm used within KlassMaster itself.
Along the same lines of simplistic obfuscation operations is the string obfuscation mechanism. This operation takes any literal string within the program and obfuscates its contents so that a decompiler could not read the string as cleartext. One important point with string obfuscating which distinguishes it from the previous functions, however, is that in order to maintain the same functionality as the original program, the strings eventually need to be deciphered. This allows for a unique look at how KlassMaster performs this operation, by examining the string decoding function placed within an output program. The first major operation performed by KlassMaster is to identify the string literals within the program, and associate an internal variable with each. In this way, there can be a separation between the string which is specified in the bytecode of the program (which is located in the 'constant pool') with one that the program modifies or decodes at runtime. Once this is done, the string can be obfuscated, and the obfuscated version is placed within the 'constant pool' of the Java bytecode. Each character in the string undergoes an XOR operation with any one of a finite set of constants. In this way, the strings within the program are converted from cleartext into what otherwise appears as unremarkable sequences of bytes. All that is required, then, is to place a decoding operation in the output program, which both undoes the string obfuscation, and occurs before any actual executed program instruction. An example of the code segment which is placed into a program to decrypt a string is given here.
Building upon this previous example are more complex problems, stemming from the exclusions list for the obfuscate operation. There are even cases given in the Frequently Asked Questions, in which programs using the Java Reflections API fail to process properly under the default settings. In a situation like this, further entries would need to be added to the exclusion list explicitly in order to be processed properly. So in this sense, although the default settings for obfuscation are sufficient in many cases, by no means is it fully comprehensive. If a problem develops, it likely means that some setting must be adjusted, and it is the programmer's, not KlassMaster's, responsibility to correct this.
A further interesting point can be made in regard to the string obfuscation function, and the fact that the algorithm used to decode strings is placed in the obfuscated output programs. Since the strings are deciphered within the program, and specifically within the 'static' block, this does leave programs using the string obfuscation open to attack; in particular, such programs are susceptible to live code analysis. For example, by running an obfuscated Java program through a debugger such as 'jdb', one can read out the contents of any strings easily.
Since the central focus of KlassMaster is to obfuscate Java bytecode such that it is not easily decompiled by a decompiler, this aspect of KlassMaster was indeed tested. From these decompilation tests, one could see that some aspects of the program were still plainly visible even after using strong settings for the obfuscation. This is to be expected, since not all aspects of a program could necessarily be hidden. Fortunately, however, the obfuscator did accomplish its task in producing confusion from the decompiled bytecode. The decompiler used in this test appears to only interpret certain sequences of instructions; sequences outside of its normal pattern cause bizarre problems to develop. Such problems include disassembled Java instructions among the decompiled code, entire portions of the code missing, and in one case, the decompiler crashed altogether. Although this was only one Java decompiler being used as a representative in this test, it can demonstrate that KlassMaster can indeed produce bytecode which requires much more care and time to understand what operations are occurring.
Although a lot can be learned about Zelix KlassMaster through the evaluation version, there were several limitations which prevented a complete and thorough analysis of its performance on real-world projects. The one with the most repercussions indicates that the evaluation version only obfuscates two methods per class. Although this might be sufficient to see how well a methid might be obfuscated in theory, it does not allow an actual program, which can easily consist of more than two methods, to be tested. Further, because of this restriction, it is nearly impossible to tell with any accuracy whether an obfuscated program suffers from considerable performance issues in comparison to the non-obfuscated version.
Overall, Zelix KlassMaster is a very powerful Java program manipulator. Although the breadth of KlassMaster's functions only extends to trimming functions and obfuscation, these goals are accomplished. In particular, obfuscated code is notably more difficult to understand and decipher, as compared to the non-obfuscated original. Despite these upsides, there are several pitfalls to the program which can easily confuse beginning users of KlassMaster or otherwise leave them trying to figure out how to correct a problem; this is particularly true in trying to figure out and grasp the exclusion mechanism used in both the Trim and Obfuscate functions.
The main GUI interface |
The dialog that indicates what exclusions apply to a program. |
This dialog controls what kinds of obfuscation occur, plus controls for several other settings. |