[Image] Tools for Attacking Maginot Licenses The Maginot License: Failed Approaches to Licensing Java Software Over the Internet Copyright (c) 1997 Mark D. LaDue, Ph. D. The unscrupulous have the command of much of this kind of knowledge without our aid; and there is moral and commercial justice in placing on their guard those who might possibly suffer therefrom. - Charles Tomlinson - Rudimentary Treatise on the Construction of Locks (1853) Contents 1. Introduction 2. Java Class Files at a Glance 3. Of Finjan's Trousers and Developers' Empty Pockets 4. WingDis, Take Dat 5. Mr. Bean Beans Java 6. Timeout for JTimer 7. No Easy Answers 1. Introduction The Java Programming language is often hailed as a Financial Savior by software developers. In their rush to exploit the Internet for profit, they frequently fail to look at the darker side of the language. Here we offer an excursion into that darker side. Java presents unprecedented opportunities for those who wish to purloin intellectual property and bring software developers to ruin. It is well-known that the Class File Format allows easy disassembly and even decompilation of Java software. There are a number of free and commercial packages available that can do just that. On the defensive side, there exist several "obfuscation" tools that try to thwart decompilation, or at least render it more difficult. But code obfuscation is simply a new form of "security through obscurity." While it may make the decompilation of Java class files more diffcult to carry out and any resulting source code more difficult to comprehend, it does absolutely nothing to address the fundamental issues. The fact remains that the disassembly of class files cannot be thwarted. This is a direct consequence of the Java Class File Format. In particular, software can be readily reverse-engineered from a class file's code arrays and constant pool, no matter what obfuscation techniques are employed. Moreover, Java has handy methods for reading and writing primitive data types, including unsigned 1-, 2-, and 4-byte quantities, at arbitrary locations in files. Consequently, it is easy to read and manipulate Java class files for unwholesome purposes. While the truth is well-known, software developers continue to disregard the obvious and leave their intellectual property at risk. To illustrate the hazards, it will be instructive to examine the products of several companies that market their Java-based software over the Internet on a "try-before-you-buy" basis and attempt to have their software enforce the terms of a trial license. We refer to these abortive efforts as Maginot licenses because, like the French fortifications constructed between the World Wars, they are simple to detect and to skirt. In each case we will explicitly show how to subvert a Maginot license by wielding the javap utility along with a few simple tools to read and alter key class files. No advanced decompilers and disassemblers will be used here - indeed, that would be cheating and would give the impression that what has been done is somehow quite difficult. Some software developers are sure to be offended when they find themselves caught with their trousers down and pockets empty. Instead of squandering time and money on attorneys in paltry efforts to intimidate critics into silence, perhaps they would do better to devote their resources to solving their software's problems. Let it be said in advance that the author does not, and does not advocate that anyone, go around subverting software licenses in order to get something for nothing. To answer the ludicrous charge that the author is providing a tutorial for those who would do so, one can do no better than cite the words of a certain Charles Tomlinson, a nineteenth century locksmith obviously well-versed in human nature and the security issues of his day. Before taking aim at Maginot licenses, we will review some salient facts about the Java Class File Format. Contents 2. Java Class Files at a Glance When Java source code is compiled, the result is a class file, having a .class extension and containing platform-independent byte code in a very specific format. A class file should be regarded as a stream of 8-bit bytes, with 16-bit, 32-bit, and 64-bit quantities being constructed in big-endian order from two, four, and eight consecutive 8-bit bytes, respectively. The Java Virtual Machine (JVM) Specification represents a class file in a C-like structure notation as follows: ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count - 1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count]; } Here the notation un refers to an unsigned n-byte quantity. While this structure gives some idea of the nature of Java class files, it will be helpful to take a closer look at a few of the details. The 4-byte quantity magic has the value 0xCAFEBABE and identifies the class file as such. The 2-byte quantities minor_version and major_version specify which version of the Java compiler produced the class file. The constant_pool is a table of structures that represent an assortment of class, field, and method names as well as string and other constants used within the class file. The constant_pool_count specifies how many entries are present in the constant_pool, while each cp_info structure is one of eleven different types that may appear in the constant_pool. The 2-byte quantity access_flags is a mask of modifiers used to specify class and interface accessibility. An extended form of access_flags also occurs in the field_info and method_info structures, where it serves the same purpose. The 2-byte quantities this_class and super_class refer to the constant_pool entries containing precisely what their names indicate. The remainder of the class file consists of four tables, with one table each for interfaces, fields, methods, and attributes. Each table is preceded by a 2-byte quantity specifying the number of entries in that table, and each entry in a particular table is a structure of a type appropriate for that table. While each of these tables is an integral part of the class file, the methods table contains the byte code to be run in the JVM, and so a closer look at it is in order. The methods table of a Java class file contains methods_count entries, and each entry is a structure of type method_info, which has the following format: method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; } The JVM class file specification offers six predefined types of attribute_info structures: 1. Code 2. ConstantValue 3. Exceptions 4. LineNumberTable 5. LocalVariableTable 6. SourceFile The most important of these attribute_info structures is the Code attribute, which contains the JVM instructions for a single Java method and has the following format: Code_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; table_info exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count]; } The code array contains the bytes of code actually run by the JVM. Each byte of the code array is either a legal Java opcode, of which there are 201 at the present time, or an operand of an opcode. The code array, like the class file as a whole, is subject to a multitude of static and structural constraints, all of which must be checked by the Java Verifier. While the class file format greatly enhances Java's security by making the verification process much more tractable, it also raises some security concerns of its own. The well-defined format and level of detail present in class files make it a straightforward, though tedious, task to recover source code from them. The justly celebrated Mocha decompiler does precisely that. Using the Mocha decompiler, for example, it is an easy matter for one to decompile class files to source code and scour them for security weaknesses, and it is just as easy for a Java developer to decompile a business competitor's work and search for trade secrets. What can be read can often be rewritten, but one need not go to all of the effort of decompiling class files to source code, editing that source code, and re-compiling it to obtain hacked class files. The hacker who knows a bit of Java programming, the class file format, and Java opcodes can easily insert, delete, or otherwise alter code in class files, all without effect on the class files' verifiability. To insert some code, for instance, one need only append entries to the constant_pool, append the appropriate opcodes to a suitable method's code array, and use the goto instruction (167 or 0xa7) to jump to and from the inserted code. One has only to be careful and adjust the appropriate counts in the class file to maintain its verifiability. Changing the flow of control in a Java program is even easier. To do so one simply has to substitute one opcode for another at the proper point in a code array. This will turn out to be all that is needed to subvert many Maginot licenses. The class java.io.RandomAccessFile has handy methods for reading and writing Java primitive data types, including unsigned 1-, 2-, and 4-byte quantities, at arbitrary locations in files. With Java's power and ease of use, it takes a scant few hours to develop the knowledge and skills required for the task, and it is extremely simple to create tools to read and manipulate Java class files for evil purposes. Contents 3. Of Finjan's Trousers and Developers' Empty Pockets Finjan Software's SurfinShield is their self-hyped, yet grossly inadequate, security tool that pretends to protect one from hostile executable content. Since we have exposed its many failures elsewhere, we need not go into the sordid details of just how pitifully this software performs. While we have also previously described a method for skirting SurfinShield's Maginot license, it will be instructive to look at that method in some detail. We will see that Finjan's folly was to hard-code the starting date of the trial license into a very simple class file, and then the ease with which class files can be inspected and altered allows one to subvert their Maginot license. As the reader may be aware, Finjan and their attorneys were not very happy about being caught with trousers down and Duke exposed, but Mr. Tomlinson would surely have approved of our candid review. The evaluation version of SurfinShield that one downloads over the Internet has a 30 day evaluation license. We observed that when sfsinstall, SurfinShield's installation script, installed the software, it allowed the zip application to call attention to a certain Java class file, SFped.class. Unzipping SurfinShield.zip, we found SFped.class and examined it with Sun's javap utility. Looking at the output of javap, we noticed that the original installation date (March 15, 1997 - Beware the Ides of March) had been hard-coded into the class file, and it was easy to deduce the likely form of the SFped.java. From this we surmised that SurfinShield's licensing aparatus was most likely calculating the time elapsed from the installation date to the present. Thus by writing a simple new SFped class in which the ped field is set to one less than the current date and updating SurfinShield.zip, we were able to defeat Finjan's Maginot license. With the new SFped class installed, SurfinShield ran just as before, and its splash screen would always report that the evaluation license had 29 days before it expired. Though it might seem hard to believe, hard-coding licensing data and restrictions on software capabilities into class files is common among those who sell Java applications over the Internet on a try-before-you-buy basis. While few Java developers are as foolhardy as Finjan in their methods, nevertheless many are leaving themselves at risk. This seems to be yet another variation of security through obscurity - in this case the mistaken notion is that licensing tools will remain secure because no one will be interested in or capable of finding, figuring out, and subverting their code. To further illustrate the ease with which this can be done, we will consider two more software products as examples: 1. WingSoft Company's Java decompiler, WingDis 2.11; 2. Sun Microsystems' BDK BeanBox tool, HotJava HTML Component Version 1.1.1. These products are good examples for several reasons: 1. They are fairly desirable tools that work reasonably well; 2. They are offered over the Internet on a try-before-you-buy basis, and their cost, while not particularly outrageous, is significant enough to make them attractive to scoundrels; 3. Their licensing code is of the Maginot type. Thus they would be tempting targets for enterprising hackers or software pirates who might enjoy offering improved versions of the tools at greatly reduced costs. Once we show just how easily the licensing schemes of these products are defeated, we will examine a third product, JTimer 1.0, developed by the InetSoft Technology Corporation, which claims to handily solve the problem of Maginot licenses by using public-key cryptography. Unfortunately, it turns out that the proposed solution dramatically worsens the problem. We will show that any licensing scheme which incorporates JTimer can be readily subverted by changing a single, easily located byte in the "protected" application, and we will demonstrate our method on JTimer itself. Thus rather than a secure solution, JTimer turns out to be a handy tool for manufacturing Maginot licenses. Contents 4. WingDis, Take Dat The justly celebrated Mocha decompiler, developed by the late Hanpeter van Vliet, was the original tool for recovering source code from Java class files. Since its creator's untimely demise, a number of commercial decompilers have been developed and are now being marketed. Among the best of them is WingSoft Company's WingDis decompiler, which has gone through numerous revisions. Here we will discuss WingDis 2.11, but the same will most likely hold true of any later releases (at least until WingSoft reads this article). By registering with WingSoft, one is able to obtain a trial version of their decompiler. The trial version appears to have the same power and functionality as the real product, but with two restrictions: 1. It can only be used for five days from the date of download; running WingDis thereafter causes it to print the message "Sorry, the trial version has expired" and exit; 2. It cannot decompile any of its own classes; trying to do so causes WingDis to print the message "Sorry, WingDis is not allowed to decompile itself" and exit. In order to relax these restrictions, one needs to locate and alter the code that enforces them. The first step is readily accomplished with a Bourne shell script. When run from the decompiler's home directory, this shell script looks for the word "Sorry" in each class file and prints the names of any class files that it finds. Running it on version 2.11 yields a single class file, ./wingsoft/javadis/ClassReader.class, as the answer. Thus we should examine ClassReader.class in order to locate the restrictive code. This is easily done with a combination of Sun's javap utility and our own simple tool, Inspector.java. From the output of javap (abridged and annotated) we find that "Sorry" occurs in exactly 6 locations across 3 methods. Examining the instructions that invoke the error messages and cause WingDis to exit reveals that it suffices to change 6 branching opcodes to the goto instruction (167). The question now is which bytes in ClassReader.class to change, and the answer is provided by the output of Inspector. From the the javap output we know the methods and the offsets (given by the line numbers) within those methods for the bytes to be changed, and from the Inspector output we know precisely where in the class file the methods in question begin. Adding the offsets to the starting points tells us which bytes to change. For example, we want to change the instruction at offset 315 in Method void read_class(boolean), and this method begins at byte 12872. Thus we must change byte 12872 + 315 = 13187 in ClassReader.class to the goto instruction (167). Likewise, we find that the other bytes to change are located at positions 14412, 23342, 23364, 23423, and 23566. One way of making the desired changes is to employ a hex editor. Lacking that, another way of doing so is to employ the programs BtoI.java and ItoB.java to convert the class file to an array of integers, edit that array, and then convert it back to bytes. A third way is to write a simple Java application to make the changes. No matter what method one uses, when the resulting ClassReader.class is put in place of the original, the Maginot license of WingDis no longer expires, and WingDis is able to decompile itself. Thus we see once again that Java class files offer no protection against those who wish to filch intellectual property. Moreover, the code obfuscation that was present in ClassReader.class did absolutuely nothing to hinder its inspection and alteration. Java developers who give away full-powered demonstration versions of their software on a try-before-you-buy basis and depend on Java for self-defense are easy prey for hackers and software pirates. Contents 5. Mr. Bean Beans Java According to Sun's documentation, roughly 80% of their HotJava Browser's functionality can be provided by just four JavaBeans components. Sun believes that these JavaBeans will prove so useful to developers of sundry tools that they are willing to license them for a nominal fee, and they are offering them on a try-before-you-buy basis for 30 days. In this case, however, the licensing software is designed more to annoy and embarrass the developer into compliance than to enforce a fixed licensing period, and it works as follows. Assuming that Sun's Beans Development Kit (BDK), or some other compatible development environment, is set up, the developer downloads the HotJava HTML Component Version 1.1.1 and installs a pair of jar files, HotJavaBean.jar and TextBean.jar. When these jar files are loaded into the development environment, a set of 5 JavaBeans becomes available for use: * HotJavaBrowserBean; * HotJavaDocumentStack; * AuthenticatorBean; * HotJavaSystemState; and * TextBean. Since the HotJavaBrowserBean, also referred to as the HotJava HTML Component, provides the bulk of the functionality, it would necessarily be used in any browser application. However, any time the HotJavaBrowserBean is loaded, whether in the BeanBox or not, it pops up a window with the following reminder: "Notice: This is an evaluation copy of the HotJava Browser software. The evaluation license expires 30 days after initial installation. Please visit the JavaSoft web site at http://java.sun.com/products/hotjava for additional licensing information." It also calls System.err.println() to print the same message. So while the software itself does not try to enforce the time frame of the licensing agreement, no self-respecting developer would dare to incorporate the HotJavaBrowserBean into a commercial application as it stands, and the other terms of the license are effectively enforced. Nevertheless, an unscrupulous developer can easily disable the embarrassing warning messages and quietly make use of the HotJava HTML Component for profit. We will now show how this can be done. The first thing to do is to "unjar" HotJavaBean.jar in a suitable location with the command: jar xvf HotJavaBean.jar. Then running another Bourne shell script reveals a single class file, ./sunw/hotjava/bean/MainPanel.class, as the only one which can contain the warning message. So in this case we must examine the MainPanel class in order to discover how to disable the warnings. From the output of javap (slightly abridged and annotated) we see that we can prevent the warning window from being popped up by changing an iconst_1 opcode to iconst_0. This has the effect of calling the Frame.setVisible() method with an argument of false, thereby rendering the annoying frame invisble. We also see that the method message() calls System.err.println() to print the warning message, and we can disable this feature by changing an ifnonnull opcode to goto. From the output of Inspector we learn that the necessary bytes to be changed are bytes 1801 and 1981 of MainPanel.class. As before, we may use a hex editor or the programs BtoI.java and ItoB.java to make the desired changes. Alternatively, a simple Java application will do the job equally well. Once MainPanel.class has been altered, we have to produce a new jar file with the command jar cvmf MRBEAN.MF HotJavaBean.jar sunw lib hjResourceBundle.properties, where we have to use the manifest file MRBEAN.MF in order to make sure that the necessary classes are available as JavaBeans. When the new HotJavaBean.jar is loaded into a JavaBeans development environment, any browsers that are created with its HotJavaBrowserBean are silent about the terms of the licensing agreement, and so developers with limited budgets can jump on the Java bandwagon and start turning a profit at Sun's expense. Contents 6. Timeout for JTimer From our trio of examples it is apparent that the Maginot license is a serious and potentially fatal problem for Java developers who market their software over the Internet on a try-before-you-buy basis. The InetSoft Technology Corporation claims that the problem can be solved using public-key cryptography, and they offer a tool called JTimer as their solution. Among the features and benefits claimed for JTimer are the following, which we quote verbatim from JTimer's home page: * Secure timer based on public-key private-key encryption. * Lightweight with a single class to include in your application. No license server is needed. * Easy key and ticket management. * Simple API. Add true protection to your software in minutes! * Increased exposure to potential customers by allowing download of evaluation copies on Internet. * Protection againt piracy with highly secure electronic signature. The JTimer package consists of two Java classes, Admin and Timer. To use JTimer a Java developer first uses its Admin class to generate a public/private key pair and a vendor ID. The developer then feeds the vendor ID, the private key, and an expiration date to the Admin class in order to generate a time ticket. In order to make use of the JTimer package, the developer must include JTimer's Timer.class, the time ticket, and the public key along with the application. The application also must create an instance of the Timer class and call Timer's checkTicket() method, which returns a boolean, to check the expiration date of the license from the ticket and public key. The application must then take action based upon the value returned by checkTicket(). The idea sounds feasible at first, but then we awaken to the stark reality that these are Java applications and that the customer has the class files. In theory all a malevolent customer needs to do is to alter the application's byte code so that the checkTicket() method always returns the boolean value true. In general, it would often suffice to change a single byte in the application from a branching opcode to a goto in order to make it function as if the checkTicket() method always returns true. This would mean that an arbitrary time ticket would always be valid, so that the application's licensing software would be defeated. Thus any software licensing scheme that uses JTimer is fundamentally and fatally flawed. To see how the malevolent scheme would work, consider the following whimsical code snippet: {Determine the value of bull} if (!bull) {Do something useful} {Do other ineresting stuff} Here the variable bull is of type boolean. This would most likely generate the following byte code (as seen through the eyes of javap): {Determine the value of bull and push it onto the top of the stack} X ifne Y {Byte code for something useful} Y {Start of byte code for other interesting stuff} Changing the ifne instruction (154) to goto (167) makes the code function as if bull were always true. To see how it works in practice there can be no better example than JTimer itself. In the JTimer package is a folder named tea/set/timer. It contains JTimer's Admin and Timer classes, but it also contains files called ticket and pubkey. Each time the Admin program is run, in addition to performing its tasks, it prints out the following message: The evaluation period has expired Please purchase a copy or stop using the software So it appears that the Admin application has been "protected" with JTimer and that the time ticket has expired, with the consequence of an expired ticket being the warning message. We can use the Admin tool to verify the ticket provided with JTimer. The command: java tea.set.timer.Admin -verify ./tea/set/timer/ticket ./tea/set/timer/pubkey gets the result: The evaluation period has expired Please purchase a copy or stop using the software Verification successful Ticket expires at Sun Nov 23 23:53:12 CST 1997 That the Admin tool has indeed been protected by JTimer can be seen from the output of javap (abridged and annotated as usual). We also see that changing a single ifne instruction to goto makes the code function as if checkTicket() always returns the value true. As usual, we use the output of Inspector to calculate that the byte to be changed in this particular case is byte 4192 of Admin.class, and we give a simple Java application to do the job. When the altered Admin.class is put in place of the original, JTimer no longer prints the warning message, but otherwise it functions just as before. This shows that JTimer itself possesses a prototypical Maginot license and that it is little more than a tool for generating Maginot licenses. Contents 7. No Easy Answers From our first three examples we see that the Maginot license is a serious problem for Java developers who desire to sell their software over the Internet on a try-before-you-buy basis, and from the example of JTimer we see that this problem has no simple solution. Indeed, there may be no solution at all. Code obfuscation is yet another example of security through obscurity. Perhaps it can be helpful if the goal is to thwart specific decompilers, but it does nothing to thwart the disassembly of Java class files, which, as we have seen, is the most effective means of understanding and attacking Maginot licenses. Its primary effect is to lull Java developers into a false sense of security about their products. Code obfuscation does little more than obfuscate an important truth about Java - the class file format allows free and easy disassembly of classes by anyone who cares to inspect and tamper with them. Including encryption to protect Java applications is equally futile. As we have seen in the case of JTimer, whoever possesses a class file can easily fling open a trap door and break the system. A moment's thought reveals that distributing Java applications protected by encryption is much like selling safes with lock combinations engraved on their bottoms. While many people would never think to look on the bottom of a safe for its combination, that combination is there to be had by anyone who troubles himself to look. Considering these examples, Java developers are foolish to think that they can enforce licensing terms from within the software that they sell. Interactive demonstrations over the Web are a much better idea, as are distributing toy versions of tools to show some of their features. Of course such demonstrations can fail to make a convincing case for a product, but perhaps making fewer sales is better for developers than giving away products for nothing. On the other hand, the traditional concept of shareware might be the best solution of all for Java developers. That would allow them to focus more on developing better products and supporting users who pay a modest licensing fee instead of wasting time dreaming up further Maginot licensing schemes. Contents Tools for Attacking Maginot Licenses [Image]