Jshrink extracts
the minimal set of Java class files for an application, removes unused code and
data, obfuscates symbolic names, finalizes code for optimized execution, and
stores the results in a Java archive .jar file.

Jshrink typically reduces program size by 30-40%. Jshrink
obfuscated code is much harder to comprehend when decompiled, a claim that can
be readily verified using Jshrink’s built-in Java decompiler. What at first
glance seems to be meaningful names in Jshrink obfuscated code are often reused
system names, a Jshrink obfuscation technique called semantic recycling.

Jshrink is distributed in a file called jshrink.jar and
requires Java JDK /JRE 1.2 (or later) runtime to execute. To run Jshrink use
the following command:

java –jar
jshrink.jar

You may also double
click jshrink.jar to start Jshrink.

For convenience Jshrink
is also distributed in the form of a Windows executable file called
jshrink.exe. Running jshrink.exe is equivalent to double clicking jshrink.jar.
Jshrink.exe may be used as a drag and drop target.

Jshrink comes in
two versions: evaluation and production.

The evaluation version of Jshrink does not include the command
line or script interface used for automating obfuscation builds. It also does
not include the save jar as exe feature.

The production version of Jshrink requires a license key in
order to run. License keys may be acquired at http://www.e-t.com/jshrink.html.
License keys and the production version of Jshrink are delivered by email. When
your license key arrives copy and paste it into the ‘License’ pane of the
Jshrink user interface. Paste in the entire key (example: 03/15/2002 Foobaz
Inc. TMLJHETRXTJXPSQT). Jshrink stores the license key in the preference file
jshrink.ini which is stored in the current user’s home directory. The license
key may also be specified with the -license command line option.

Jshrink is protected by copyright law and international
copyright treaty. Each license entitles the licensee to run a single copy of
Jshrink at a time.

To use Jshrink, Java class files and related data files are
loaded into the class tree in the left pane. Files are loaded with the ‘File…
Open…’ menu command (equivalent to the open folder icon on the toolbar).

Multiple files can be selected at once with the File Open
dialog. On Windows this is done by holding down the Ctrl key when clicking on
the file. The easiest way to load files is to load an entire directory or jar
file. Note that all files in the directory or jar file are loaded except files
ending in .java; this can be overridden with the –keepJava command line option.

To add files to the class tree without clearing it first use
the ‘File… Merge…’ menu command. Files may also be added by drag and drop into
the class tree pane.

The ‘File… Save As…’ command reduces, obfuscates, and saves
files in the shrink set to a .jar or .zip file or to a directory tree (the disk
icon on the toolbar is equivalent to Save). Once the file is saved the class
tree is transformed to show the renamed classes and members. Classes and
members that were unused are removed from the display.

Save’s default operation is to output every file in the
class tree; Jshrink can also save a subset of the class tree using the class
tree checkbox feature.

Each class file in the class tree pane has a checkbox to its
left. When you select the box an ‘X’ appears in it. This mark forces inclusion
of the class file in the output and prevents it from being renamed. If no class
files are checkmarked and a ‘main’ method is not found then Jshrink acts as if
all class files were checkmarked: all class files are included in the output
and no class files are renamed. Otherwise Jshrink will include in the output
only those files that are checkmarked or that are referenced by checkmarked
files.

Each field and method also has a checkbox. Marking a field
or method forces its inclusion and prevents it from being renamed. This also
forces inclusion of the containing class, but does not keep the class itself
from being renamed.

Clicking on the class checkbox again changes the ‘X’ to a
capital ‘I’ for ‘Interface’. This option not only preserves the class name but
also preserves all public and protected fields and methods within the class.
This is useful for preserving a library interface.

Jshrink automatically preserves fields and methods that are
used by the Java runtime environment. For instance, if a class is derived from
java.awt.Component, Jshrink does not alter the method ‘void paint(Graphics g)’
because that method is called from outside the files in the class tree. The
‘Classpath’ option controls which Java environment is used for this purpose.

Clicking on a class, field, or method name will display
decompiled code in the Decompile pane. Jshrink
will only decompile its own output if it was produced by a matching production
license key, preventing others from decompiling your code with Jshrink.

The ‘File… Save Jar As Exe…’ command creates a Windows
executable .exe file from any existing executable Jar file. An executable Jar
file is one that contains a valid Main-Class manifest (see Create manifest
Main-Class option).The .exe file
will lanch the Jar file provided that it can find a Java Run Time Environment
on the target machine. The .exe file will open a console window if the Jar file
writes to System.out or System.err. The .exe file may be used as a drag and
drop target.A memory setting of
–Xmx256m is used (Java default is –Xmx64m).

You can customize the output .exe files by modifying its
resources with a Windows resource editor such as Microsoft Developer
Studio.Resources that can be changed
are the program icons (16x16 and 32x32) and the command used to invoke Java
(string 1, the default is ‘java -Xmx256m). To use a colocated Java VM set
string 1 to a relative path specification such as 'jre\bin\java ...'.

The ‘File… Open script…’ command opens a Jshrink script file
and executes all commands within it except for –o (output). This is useful for
checking a script file before using it in command line operation.

The ‘File… Save script…’ command saves all user initiated
operations (input files, option settings, output file) in a script. Jshrink can
then be run with the “-script filename” option to automate the operations.The script contains absolute paths; these
may be manually edited to be relative to a path of your choosing.

Output information about unused and renamed symbols as well
as individual class size statistics to the Log pane. Renamed symbols are
presented in alphabetic order for ease in interpreting obfuscated application
tracebacks. The Log pane may be saved to a file with cut and paste.

Select this option if the class files will not be referenced
in further Java compilations. This option removes ‘throws’ information, marks
all non-overridden methods as ‘final’ so they can be potentially inlined at
runtime, and may not preserve compiler generated members with ‘$’ in their
names used by inner classes and Remote Method Invocation (RMI).

Select this to rename class files to names like ‘A’, ‘B’,
etc. Class files with checked boxes in the class tree pane or –keep command
line option are never renamed . Classes potentially used by Class.forName() are
by default not renamed; see ‘Include forName and getMethod string matches’
below.

If the class has a package name it is also renamed, provided
that all classes in the package can be renamed and no data files are included
in the package directory.

Select each access class (public, protected, package,
private) to be eligible for removal of unused fields and methods. All class
files to be affected by these settings must be loaded for this to work
properly. For instance, if package level is selected then all class files in
each obfuscated package must be loaded, otherwise Jshrink cannot decide what is
unused.

Select each access class (public, protected, package,
private) to be eligible for renaming of fields and methods. All class files to
be affected by these settings must be loaded for this to work properly. For
instance, if package level is selected then all class files in each obfuscated
package must be loaded, otherwise Jshrink cannot rename all references
uniformly.

The classpath specifies the Java run time and any other
object libraries inherited by the classes being shrunk.Any class file that is used as a superclass
that is not itself being shrunk must be on the classpath. Classpath files are
read only and are not included in the Jshrink output. You should use the latest
Java run time version available for the platform you are targeting; for
standard Java this would be the latest JDK from Sun. By default this field is
set to the runtime system active when Jshrink is started. Paths are separated
with a semicolon.

Jshrink preserves serializable fields (those not declared
‘transient’) of classes declared serializable. In addition, if a serializable
class does not contain a serialVersionUID, then the class name and all
non-private methods are also preserved since altering them would affect the
default class signature and cause serialization to fail. Select this option to
also preserve members if an ancestor class in the Classpath is serializable.
For instance, java.awt.Component is serializable; selecting this option would
preserve all members of classes that inherit from Component so that the
descendants would serialize correctly. The default is not to do this since it
defeats obfuscation and the user often does not intend to serialize such
classes.

Select this if your application uses Class.forName() calls
and you do not want to manually preserve the class names used by checking each
class box. This option causes Jshrink to preserve all classes whose names match
string constants if a forName call is detected. For instance, if a class is
loaded with forName(“com.bogus.Widget”) and com.bogus.Widget is in the shrink
set, then Jshrink will include com.bogus.Widget in the output and will not
rename it. This will occur even if the string “com.bogus.Widget” is not used
directly in the forName call.

You may not be aware that the Java compiler is generating
calls to forName which in turn will cause classes to not be renamed. If a class
uses ‘assert’ then the JDK 1.4 Java compiler generates a forName call which
prevents the class from being renamed. Use of class.getName() andthis.class.getResource() also generates a
forName call (note: getClass().getResource() does not generate a forName call
and generates less code than using this.class.getResource()). Use the Rename
classes whose names match strings option to rename classes that use
forName.

This option allows strings and classes found by ‘Keep
classes whose names match strings’ to be renamed to obfuscated names. This
should be used with care, since if a class is named “Dog” and is renamed to
“I”, then all occurrences of the string “Dog” are also changed to “I”. This is
less likely to be a problem if packages are used, where strings such as
“com.bogus.Widget” would be renamed to a string like “I.I”.

Select this if your application uses Class.getMethod() calls
and you do not want to manually preserve the member names used. This option
causes Jshrink to include all methods names that match string constants when
getMethod calls are encountered. For instance, if a method is referenced with
getMethod(“widget”) and a method named “widget” is in the shrink set, then
Jshrink will include widget in the output and will not rename it. This will
occur even if “widget” is not used directly in the getMethod call.

The use of string literals such as
“invalid license key” can reveal the purpose of code when the Java class file
is decompiled. Jshrink string encryption replaces literal strings with static
method calls to a Jshrink supplied class which returns interned strings read
from an encrypted source. Repeated uses of a string literal are most always
cached to avoid producing transient strings that must be garbage
collected.Note that this technique
does impose some overhead and is not foolproof to attack with a debugger or a
determined analysis.

String encryption does not occur
for string with character codes greater than 255; for strings longer than 254
characters; or in methods whose code size exceeds 32,767 bytes. String
encryption only works with a single Jshrink output jar file; if you are using
multiple Jshrink'ed jars only one can have string encryption applied.

With its emphasis on reducing class file size Jshrink is a
good choice for Java 2 Micro Edition (J2ME/MIDP/CLDC) applications.

Set Jshrink’s Classpath to the location of the J2ME classes
(example: c:\wtk104\lib\midpapi.zip). For best results checkmark (-keep) the
main Midlet class file.

Jshrink can be run on J2ME class files before or after the
‘preverify’ step (if string encryption is used the Jshrink should be used
before a final preverify step). When saving a .jar file a check is made for a
.jad file with the same name; if found the .jad file will be automatically
updated with the .jar file size. Jshrink can thus be run on final J2ME
applications without modifying the build process.

CLDC 1.0 differs from standard Java and later versions of
CLDC in that string literals are not interned and String.intern() is not
implemented. The Jshrink command line parameter -stringEncrypt2 should be used
for CLDC 1.0 compatibility.

String encryption may invalidate class file preverification
since it might increase the maximum calling stack depth.In this case it is advisable to reverify the
class files.Note that string
encryption removes strings from code space and adds them to data space which is
usuallya benefit.

Below is an example using Ant and Jshrink command line
operation to compile and obfuscate directory ../src to an output file called
../foo.jar.The Ant script is stored in
a file called build.xml; when Ant is run in the directory containing build.xml
it will execute it.

Note: it is best not to write the Jshrink output jar file
into Ant's execution directory (basedir) or else Jshrink will not be able to
overwrite it the second time the script is executed (presumably Ant opens
existing jar files which prevents other programs from deleting them). In the
example below foo.jar is written to "../" which is the parent
directory of the current execution directory.

Input filespec can
be a .jar or .zip archive, a directory name (including ‘.’, the current
directory), or an individual file. If a directory name is specified then all
files in the directory are loaded, including subdirectories. However, files
ending with .java will not be included unless the –keepJava command line switch
is specified before the input filespec.

If the –o option is present then Jshrink is run in batch
mode without a user interface. The output filespec can be a jar or zip file or
a directory path (including ‘.’).

If you are using jshrink.exe instead of jshrink.jar then do
the following to enable output to the console when –o is used:

java –jar
jshrink.exe … -o out.jar

Options are order independent except for –keep and -keepJava.
The default value for each option is the same as the initial user interface
value. Each option is described below.

Classpath specifies the paths containing superclass files
used by the class files being shrunk; classpath files are read only and are not
included in the Jshrink output. The classpath should specify the Java run time
and any object libraries inherited by the classes being shrunk. You can use –cp
to add to the default –classpath without resetting its contents. Paths are
separated with a semicolon (;).

Do not include file in output. Path/file refers to a file
loaded in a previous filespec. The path/class specification is case sensitive
and uses jar format path specification forward slashes (/). If just a path is
specified then all files beginning with the path are excluded from output.

If Class.forName() is encountered, this option allows
classes and matching strings to be renamed. See the discussion under Include
forName and getMethod string matches and Rename forName class and string
matches.

Force output of class and do not change its name. The class
must refer to a class file that has already been loaded in an input filespec
(the input filespec must appear before the –keep option). If no –keep class is
specified and no 'main' method is found then all class files are kept by
default and class renaming will not occur. The package.classname specification
is case sensitive. The file extension '.class' is assumed and should not be
added to the classname. If the argument ends with '?' then all matching classes
are kept.

Like –keep, but additionally preserves all public and protected
fields and methods of the class. This is useful for preserving a library
interface.-noFinalize is also applied
to package.classname under the assumption that the interface will be used in
further compiles. The -keepInterface option is equivalent to the user interface
operation of clicking on a class checkbox until capital I appears. If the
argument ends with '?' then all matching classes are marked as -keepInterface.

Include files ending with .java in output. Use this to force
inclusion of Java source files. This option must be specified before any
filespec to which it applies. The purpose of this option is to guard against
inadvertant release of source files.

This option forces inclusion of a field or method and prevents
it from being renamed. This also forces inclusion of the containing class, but
does not keep the class itself from being renamed. Add parentheses () to the
name if it is a method. A more detailed low level signature of the form
displayed by "javap -s -p package.classname" can be supplied if the
method name is overloaded. The class must refer to a class file that has
already been loaded in an input filespec, .i.e. the input filespec must appear
before the –keepMember option.Note
that wildcards are not supported.

Save output to outfile. If outfile has extension .jar or
.zip then the files are saved to an archive; otherwise outfile is interpreted
as a directory tree base in which to write the files. Use ‘-o .’ to write files
to the current working directory. The output path of a class is determined by
the -o output path plus its internal package name (example: java.lang becomes
path ./java/lang/...) If the -o option is present then Jshrink saves and exits
without bringing up its user interface.

This option forces all class files to be overwritten in
place. It automatically sets -noRenameClasses. If the -overwrite option is
present then Jshrink saves and exits without bringing up its user interface.
Note that –overwrite will not delete unused classes or other files in the
–overwrite path.

This option opens the specified file and reads Jshrink
filespecs and options from it as if they were specified on the command line.
Each line of the script file should contain a single filespec or option with
argument, if any. Do not add double quotes around filenames containing spaces;
filenames are delimited by the end of line. Blank lines and lines beginning
with ‘#’ (comment) are ignored. Script files can be produced automatically with
the Jshrink File Save Script command.

The Jshrink command line parameter -stringEncrypt2 should be
used for CLDC 1.0 compatibility.CLDC
1.0 differs from standard Java and later versions of CLDC in that string
literals are not interned and String.intern() is not implemented.
-stringEncrypt2 does not intern. If -stringEncrypt2 is used, ("a" ==
"a") is false, but ("a".equals("a")) is true, as
always.

A: Yes, Jshrink works with inner classes. As with all class
files, use –noFinalize if you will compile other files using the shrunk files.

Q: Does Jshrink work with RMI?

A: Yes, Jshrink works with Remote Method Invocation (RMI).
It is not necessary to shrink the server side code with the client side code.
If you do shrink the server side then you need to mark (-keep) the server
implementation as well as its stub and skeleton files.

Q: Does Jshrink work with servlets?

A: Yes, Jshrink works with servlets. Don’t forget to add the
javax.servlet classes to Classpath if they are not already there.

A: Method names like Component.paint() cannot be changed
because the Java run time system calls them by name. Use of native methods
preserves all members in the same class unless –noNativePreserve is specified.
In a serializable class only transient and static fields can be deleted or
renamed. Also, Jshrink reuses names it cannot change, so what at first glance
looks like a meaningful name may be a name recycled by Jshrink.

Q: Not all of my classes were renamed. Why?

A: If you do not checkmark at least one class in the GUI or
specify ‘–keep’ of at least one class on the command line then no classes are
renamed. Classes potentially used in Class.forName() are also not renamed. Note
that Java may insert forName calls that you are not aware of, such as with use
of the assert keyword or the .class field. You can use the –forNameRename
option to rename such classes.

Q: I am using JBuilder and Jshrink does not eliminate
‘static final int’ variables. Why not?

A: JBuilder outputs assignment statements for ‘static final
int’ variables; javac does not. Since the variables are used Jshrink does not
eliminate them.

Q: I want to use Jshrink 2.0 with a redistributable
library as I did with Jshrink 1.0. How do I do it now?

A: For Jshrink 1.0
compatibility specify the following options: -noFinalize –noRenameClasses
–keepPublic –keepProtected –norenamePublic –norenameProtected. Do not specify
an entry point with checkmarks or –keep. If saving to a directory rather than a
jar file, use –overwrite. If the library entry points are confined to a few
modules you might want to use -keepInterface instead.

Q: I obfuscated
my application and now Java cannot find my java.util.PropertyResourceBundle.getBundle
resource class files. What should I do?

A: There are two kinds of resource bundle files: text files
and class files. Resource text files require no special attention as they are
automatically retained by Jshrink. Resource class files need to be checkmarked
(command line option –keep) or they will be eliminated. Resource class files
should not be renamed or the automatic resource localization feature of Java
will not work.

Q: Does Jshrink
work with serializable classes?

A: Jshrink preserves non-transient fields by default; no
user intervention is required. In addition, if no serialVersionUID is present,
Jshrink also preserves serializable class names and non-private methods. If
serializability is inherited from outside the shrink set this does not occur;
see discussion under ‘Preserve serializable fields and methods inherited from
Classpath’.