Tools for Attacking Maginot Licenses
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)
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
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:
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:
2. Java Class Files at a Glance
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.
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];
}
The JVM class file specification offers six predefined types of
attribute_info structures:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
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:
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.
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];
}
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
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:
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
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:
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
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:
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:
The first thing to do is to "unjar" HotJavaBean.jar in a suitable location
with the command:
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
Contents
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:
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:
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:
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
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.
Tools for Attacking Maginot Licenses
3. Of Finjan's Trousers and Developers' Empty Pockets
These products are good examples for several reasons:
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.
4. WingDis, Take Dat
5. Mr. Bean Beans Java
"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.
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.
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.
6. Timeout for JTimer
{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.
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
7. No Easy Answers