JBET - Java Binary Enhancement Tool

Andrew Reisse
NAI Labs
jbet@nailabs.com

The latest version can be found at opensource.nailabs.com/jbet/index.html.

 This software was developed under DARPA/SPAWAR contract
 N66001-00-C-8602 "SPMA" and DARPA/AFRL contract 
 F30602-00-C-0183 "Survivable Server" as part of the
 DARPA OASIS research program.

 See COPYING for license details.

Description:
 The Java Binary Enhancement Tool (JBET) is a general Java program analysis
 and manipulation tool. Existing class files can be disassembled, reassembled,
 or edited programmatically through the JBET API. JBET can also be used to
 create new Java class files from scratch. JBET uses a convenient internal
 representation of all the contents of Java binary (.class) files, allowing the
 user to edit the classes easily, in a structured manner.

Requirements:
 Sun JDK 1.4.1. (Other JDK vendor versions may work, but only this one
 has been tested). Java 1.4 API is required, but not heavily. To run Jbet with
 JDK 1.2 or 1.3, one should only have to remove use of the StackTraceElement
 class and related functions. The version of the JDK used to run Jbet does
 not affect the versions of class files that can be operated on.
 perl5 is required to run the tests.

Building:
1. Install jdk 1.4.1.
2. Set CLASSPATH to jdk1.4.1/jre/lib/rt.jar
3. cd src; make
4. If that didn't work, examine the makefile.
   java or javac may not be in the path.
5. To build a jar file that can be used with "java -jar jbet.jar",
   run "make jar".
6. If you have perl installed, run the tests with "make test".
   Optionally, run "make regen; make test".
   (See below for what the test suite does)

Installation:
Make a symbolic link from jbet3/bin/jbet to somewhere in your path.

Usage:
JBET uses the JNI format for class names, and JNI type and method
descriptors. For a summary of this syntax, use 'jbet help syntax'.
Sun's JVM specification may also be helpful. 

To look at a class disassembly, use 'jbet print'. Try disassembling
a class you have source for, and was built with debug info (-g):
'jbet -P <classpath> print <classname>'. Sun's JVM specification has
an instruction reference.

Here is a sample output:
$ jbet -j print java/lang/String.charAt
public charAt (I)C
	Attributes: Code
	Code Attributes: LineNumberTable
	Code Length: 33
	MaxStack/MaxLocals: 3/2
	0	459	iload_1
	1	459	iflt 12
	4	459	iload_1
	5	459	aload_0
	6	459	getfield java/lang/String . count : I
	9	459	if_icmplt 21
	12	460	new java/lang/StringIndexOutOfBoundsException
	15	460	dup
	16	460	iload_1
	17	460	invokespecial \
     java/lang/StringIndexOutOfBoundsException . <init> : (I)V
	20	460	athrow
	21	462	aload_0
	22	462	getfield java/lang/String . value : [C
	25	462	iload_1
	26	462	aload_0
	27	462	getfield java/lang/String . offset : I
	30	462	iadd
	31	462	caload
	32	462	ireturn

Each line has: instruction pointer, line number (if class has debug info), and
instruction. If there is debug info, variable name annotations may be present
on instructions that use local variables (aload, iinc, etc.). The source file
name that the line numbers are for is associated with the whole class; that
info can be viewed with 'jbet printclass', or 'jbet print <class>'. 

To edit instructions, use assemble and disassemble. 
jbet -P <classpath> disassemble <classname> > out.

Here is a sample disassembly output:
$ jbet -j disassemble java/lang/String.charAt
.method public charAt (I)C {
  .maxstack 3    # .maxstack specifies the stack limit
  .maxlocals 2   # .maxlocals specifies the local variable (register) limit
  .line 459      # .line tells what source line the next instructions were from
   iload_1
   iflt 1        # jump instructions use a text label for the target
   iload_1
   aload_0
   getfield java/lang/String . count : I
   if_icmplt 2
  1:             # definition of label 1
  .line 460
   new java/lang/StringIndexOutOfBoundsException
   dup
   iload_1
   invokespecial java/lang/StringIndexOutOfBoundsException . <init> : (I)V
   athrow
  2:
  .line 462
   aload_0
   getfield java/lang/String . value : [C
   iload_1
   aload_0
   getfield java/lang/String . offset : I
   iadd
   caload
   ireturn
}

To assemble an edited disassembly back into a class file, use
jbet -j assemble '%(include <filename>)'
The output is written to the output directory (-o <dir>), or the
current directory if none is specified. Note that in order to assemble,
an entire-class disassembly must be present in that file.

Using the Graph representation
Code transformations may be easier to program using the DAG representation
of Java code. 'jbet listdags' can be used to examine the DAG representation of
a method. Note that because it is a tree, some instruction nodes may be listed
multiple times; this does not mean that the calculation was performed more than
once. The output of listdags cannot be edited and reassembled.

The output of listdags uses different formatting than print, disassemble, or
other code display commands.
&1:4       The local variable 4, used in block 1. 
#B3        Block 3 as a jump target.
&N1        A temporary created by 'new'.
&1:4.f     The field 'f' from &1:4.
&1:4[5]    An array access at index 5 of array &1:4
(# &1:4)   Length of array &1.4
(L 1)      Conversion of '1' to long

Control flow leaving each basic block is specified on the 'exit' line. 
Variables read by the block are listed in 'inputs'. Variables stored to
are listed in 'outputs' (along with the stored value). Instructions with
side effects (such as new, invokevirtual, and putfield) are specified by
'se'.

Caution:
Most of the supplied transforms will not work on methods that are close to the
hardcoded Java limits on method size (65535 instructions, 65535 local
variables, 65535 stack) and will result in write errors or corrupt class files.

The code generator is very basic and generates horrible output (large and
underperforming).

Test Suite:
The included test suite (which can be run with 'make test', or the script
jbtest in the tests/ directory) tests various parts of the Jbet core. The
support for the graph representation (including code generator), the
data-flow analyzer, and the verifier are tested. If you add a new
code generator, it may be helpful to make your code generator the one used
by jbet/cmd/regencode.java and run the test suite.  Note that some tests
are expected to fail (this is because of unimplemented features in the
current code generator). The summary of running the tests will list the
number of unexpected failures and successes. Running 'make regen; make test'
further tests the code generator and other graph representation support by
creating a graph representation of Jbet, using the (simple) code generator
to write a new copy of Jbet, and then use that copy of Jbet to run the tests.
The version of jbet produced by 'make regen' will be much slower than the
original version, so is only useful for testing. After testing run
'make new' to rebuild jbet.
