Jshrink 2.43 User Manual

Copyright 2022 Eastridge Technology www.e-t.com

Introduction

New Features

Installation

Operation

File Open

File Save As

Class Tree Checkbox

Decompile

File Script Open

File Script Save

User Interface Options

Log unused and renamed symbols

Finalize code for runtime use only

Rename classes

Eliminate unused fields and methods

Rename fields and methods

Classpath

Preserve fields and methods if native method in class

Preserve serializable fields and methods inherited from Classpath

Create manifest Main-Class

Keep classes whose names match strings if forName used

Keep classes whose names match strings unconditionally

Rename classes whose names match strings

Keep methods whose names match strings if getMethod used

String encryption

J2ME MIDP Support

Using Jshrink with Ant

Command Line Operation

-classpath path;path...

-classStringMatch

-cp path;path...

-delete path/file.ext

-forNameRename

-keep package.classname

-keepInterface package.classname

-keepJava

-keepLineNumbers

-keepMember package.classname.member

-keepPublic

-keepProtected

-keepPackage

-keepPrivate

-l

-license “key”

-manifestMain

-noFinalize

-noForName

-noGetMethod

-noNativePreserve

-noRenameClasses

-noRenamePackage

-noRenamePath

-noRenamePrivate

-noRenameProtected

-noRenamePublic

-o outfile

-overwrite

-script filename

-serialInherit

-stringEncrypt

-stringEncrypt2

Frequently Asked Questions

Revision History

 

Introduction

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.

Version 2.0 Features

Jshrink 2.0 introduces the following new features:

Renaming (or elimination) of public fields and methods (in addition to protected, package, and private).

Elimination of unused classes.

Class renaming.

Semantic recycling of fixed names.

Increased control over obfuscation options.

An enhanced Graphics User Interface (GUI) as well as a GUI-less command line interface.

Integrated decompiler.

Improved J2ME MIDP integration.

Automatic handling of Class.forName() classes.

Script file automation.

String encryption.

Installation

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.

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.

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.

Operation

The following figure shows the Jshrink user interface.

 

File Open

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.

File Save As

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.

Class Tree Checkbox

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.

Decompile

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.

File Save Jar As Exe

File Script Open

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.

File Script Save

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.

User Interface Options

Each option in the Jshrink user interface Options pane is described below. Default values may be overridden with the command line options described in the Command Line Operation section.

Log unused and renamed symbols

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.

Finalize code for runtime use only

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).

Rename classes

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.

Eliminate unused fields and methods

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.

Rename fields and methods

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.

Classpath

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.

Example: C:\PROGRAM FILES\JAVA\J2RE1.4.0\lib\rt.jar;thirdparty.jar

Preserve fields and methods if native method in class

Select this to preserve all members in a class that also contains native methods. This is advisable if native methods access class members and you do not want to preserve such members individually.

Preserve serializable fields and methods inherited from Classpath

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.

Create manifest Main-Class

Select this to make the output jar executable. If a ‘main’ method is found, a file called META-INF/MANIFEST.MF will be created containing:

            Manifest-Version: 1.0

            Main-Class: mypack/myclass

If META-INF/MANIFEST.MF already exists in the class tree and is checkmarked it will not be overwritten and Main-Class will not be added; uncheck it to force it to be overwritten.

Keep classes whose names match strings if forName used

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() and  this.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.

Keep classes whose names match strings unconditionally

This option forces inclusion of class whose names match string constants even if Class.forName() calls are not present. See Keep classes whose names match strings if forName.

Rename classes whose names match strings

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”.

Keep methods whose names match strings if getMethod used

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.

String encryption

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.

Jshrink string encryptions requires that the Java compiler option '-target 1.6' be used on input files. This is because changes to the Java class file after 1.6 prevent string encryption in many cases due to 'invokedynamic' internals.

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. Note that named string constants such as 'final static String STRING="string";' are always eliminated by Jshrink unused symbol detection; if you manually retain such symbols then they will not be encrypted.

J2ME MIDP Support

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 usually  a benefit.

Using Jshrink with Ant

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.

build.xml:

<project name="Compile and Jshrink" default="dist" basedir=".">

    <description>

        Compile and Jshrink

    </description>

  <!-- set global properties for this build -->

  <property name="src" location="../src"/>

  <property name="build" location="build"/>

  <target name="compile"

        description="compile the source " >

    <!-- Compile the java code from ${src} into ${build} -->

    <javac srcdir="${src}" destdir="${build}"/>

  </target>

  <target name="dist" depends="compile"

        description="generate the distribution" >

                        <!-- note: do not output jar file in ant's current directory -->

                        <!-- ant will open it and then jshrink cannot overwrite it -->

               <java classname="jshrink.Jshrink">

                         <arg line="${build} -manifestMain -o ../foo.jar"/>

                         <classpath>

                           <pathelement location="c:/jshrink/jshrink.jar"/>

                         </classpath>

               </java>

  </target>

</project>

 

Command Line Operation

Jshrink arguments may be supplied from the command line in addition to the user interface.

java –jar jshrink.jar [option…] [input filespec…] [option…] [-o output filespec]

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 ‘.’).

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 path;path...

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 (;).

Example: –classpath C:\PROGRAM FILES\JAVA\J2RE1.4.0\lib\rt.jar;thirdparty.jar

-classStringMatch

This option is equivalent to Keep classes whose names match strings unconditionally.

-cp path;path...

This option adds new paths to the front of -classpath. The new entries are searched before existing ones.

-delete path/file.ext

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.

Example: java –jar jshrink.jar foo.jar –delete mypack/foo.gif –delete mypack/myclass.class -delete unusedpack/

-forNameRename

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.

-keep package.classname

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.

Examples:

            java –jar jshrink.jar mypack\myclass.class –keep mypack.myclass

            java –jar jshrink.jar mypack\myclass.class –keep my?

 

-keepInterface package.classname

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.

Examples:

            java –jar jshrink.jar myjar.jar –keepInterface com.mycompany.packagex.ClassA

            java –jar jshrink.jar myjar.jar –keepInterface com.mycompany.?

-keepJava

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.

-keepLineNumbers

This option causes Java debug (-g) line number tables to be retained.

-keepMember package.classname.member

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.

Examples:

            -keepMember mypack.myclass.field

            -keepMember mypack.myclass.method()             

            -keepMember mypack.myclass.method(I)V         

-keepPublic

Do not delete unused public members.

-keepProtected

Do not delete unused protected members.

-keepPackage

Do not delete unused package members.

-keepPrivate

Do not delete unused private members.

-l

Log (lower case L). Output verbose information about unused and renamed symbols and size statistics to standard output. Renamed symbols are presented in alphabetic order for ease in interpreting obfuscated application tracebacks. Jshrink output can be captured with standard redirection, e.g. “java –jar jshrink.jar myapp > myapp.log”

-license “key”

Normally Jshrink stores its license key in the preference file jshrink.ini, which is stored in the user’s home directory. You may also supply the license key directly with this option.

Example: -license “03/15/2002 Bogus Inc. TMLJHETRXTJXPSQT”

-manifestMain

Create META-INF/MANFEST.MF Main-Class as described under Create manifest Main-Class.

-noFinalize

Do not finalize code for run-time use only. Use this if the class files will be used in further Java compilations, such as in a redistributable library. See ‘Finalize code for runtime use only’.

-noForName

Do not include forName string matches as described in Keep classes whose names match strings if forName used.

-noGetMethod

Do not include getMethod string matches as described in the User Interface Options section.

-noNativePreserve

If there is a native method in the class then do not automatically preserve all class members.

-noRenameClasses

Do not rename classes.

-noRenamePackage

Do not rename package members.

-noRenamePath

Do not rename packages when renaming classes.

-noRenamePrivate

Do not rename private members.

-noRenameProtected

Do not rename protected members.

-noRenamePublic

Do not rename public members.

-o outfile

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.

-overwrite

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.

-script filename

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.

Example script file:

            input.jar

            -keepInterface mypack.myclass

            -o output.jar

 

-serialInherit

See discussion under Preserve serializable fields and methods inherited from Classpath.

-stringEncrypt

Encrypts strings in class files. See discussion under String encryption.

-stringEncrypt2

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.

Frequently Asked Questions

Q: Does Jshrink support JDK 1.5?

A: Yes, Jshrink supports all versions of Sun’s Java Development Kit (JDK).

Q: Does Jshrink work with inner classes?

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.

Q: Why is symbol renaming necessary? Doesn't compilation get rid of symbolic references?

A: Java compilation does reduce local variables to numeric offsets, but leaves member names intact so that .class files can be linked together at run time.

Q: Jshrink runs out of memory on my large application. What should I do?

A: Use: java -Xmx256m –jar jshrink.jar to increase Java’s maximum memory allocation to 256 Mbytes (more may be required).  The default value in recent Java versions has been -Xmx64m.

Q: Some field and method names were not changed. Why?

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’.

 

Revision History

Jshrink 2.0 was released on April 5, 2002.

Jshrink 1.0 was released on May 17, 1997.

 

[end]