This document explains how to use the yGuard Java obfuscation software together with Ant. yGuard is a product of yWorks GmbH, creator of the outstanding Java™ graph visualization framework yFiles and other fine products.
Important: The syntax described here is deprecated.
Documentation for the current Ant syntax of the successor yGuard releases is
available here.
<obfuscate>
obfuscate
Elementproperty
Elementsexternalclasses
Elementinoutpair
Elementsexpose
Elementclass
Elementsmethod
Elementsfield
Elementslinenumbertable
Elementssourcefile
Elementsattribute
Elementsadjust
Elementsmap
Element
In order to make use of yGuard's obfuscate
task you must have Ant properly installed and configured
to run it. After downloading
and extracting the jar file
(yguard.jar
), place it in a path near to your build script. You may use
absolute paths, but in the following example, we expect the jar file to lie
in the same directory as your build file.
In order the get the Ant task running you should insert a couple of lines in
your build script (build.xml
):
<taskdef name="obfuscate" classname="com.yworks.yguard.ObfuscatorTask" classpath="yguard.jar"/> <target name="obfuscate"> <obfuscate> <!-- modify obfuscate element attributes and insert your statements here --> </obfuscate> </target>
Alternatively, you can place the taskdef
element inside the
target that uses the obfuscate task:
<target name="obfuscate"> <taskdef name="obfuscate" classname="com.yworks.yguard.ObfuscatorTask" classpath="yguard.jar"/> <obfuscate> <!-- modify obfuscate element attributes and insert your statements here --> </obfuscate> </target>
For a complete build.xml
file have a look at the
examples section.
<obfuscate>
The obfuscation process can completely be configured inside your Ant script. The following DTD should be used in it (please note that this is for information purposes only, i.e. you do not have to include the following lines anywhere):
<!ELEMENT obfuscate (inoutpair+,externalclasses?, property*,patch?,expose?,map?,adjust*)> <!ATTLIST obfuscate mainclass CDATA #IMPLIED logfile CDATA #IMPLIED conservemanifest CDATA #IMPLIED replaceClassNameStrings CDATA #IMPLIED > <!ELEMENT inoutpair EMPTY> <!ATTLIST inoutpair in CDATA #REQUIRED out CDATA #REQUIRED > <!ELEMENT externalclasses ANY> <!-- the externalclasses element is used just like ant's classpath element. See the Ant documentation for further details--> <!ELEMENT property EMPTY> <!ATTLIST property name CDATA #REQUIRED value CDATA #REQUIRED > <!ELEMENT patch (class)*> <!ELEMENT expose (class|method|field|sourcefile|attribute|linenumbertable)*> <!ATTLIST expose linenumbertable CDATA #IMPLIED localvariabletable CDATA #IMPLIED sourcefile CDATA #IMPLIED localvariabletypetable CDATA #IMPLIED runtimevisibleannotations CDATA #IMPLIED runtimevisibletypeannotations CDATA #IMPLIED runtimeinvisibleannotations CDATA #IMPLIED runtimeinvisibletypeannotations CDATA #IMPLIED > <!ELEMENT class (patternset)*> <!ATTLIST class classes CDATA #IMPLIED methods CDATA #IMPLIED fields CDATA #IMPLIED name CDATA #REQUIRED map CDATA #IMPLIED > <!ELEMENT method (patternset)*> <!ATTLIST method class CDATA #IMPLIED name CDATA #REQUIRED map CDATA #IMPLIED > <!ELEMENT field (patternset)*> <!ATTLIST field class CDATA #IMPLIED name CDATA #REQUIRED map CDATA #IMPLIED > <!ELEMENT sourcefile (property*,patternset*)> <!ELEMENT linenumbertable (property*,patternset*)> <!ELEMENT attribute (patternset*)> <!ELEMENT adjust ANY> <!-- the adjust element is used just like ant's fileset element. See the Ant documentation for further details--> <!ATTLIST adjust replaceName CDATA #IMPLIED replaceContent CDATA #IMPLIED replacePath CDATA #IMPLIED > <!ELEMENT map (class|method|field|package)*> <!ELEMENT package EMPTY> <!ATTLIST package name CDATA #REQUIRED map CDATA #REQUIRED >
Attention users of IDEs that "support" the creation of Ant files (e.g. IDEA's IntelliJ): Your IDE may indicate some errors inside your ANT file when you use yGuard specific elements. This is because the IDE does not know about the DTD used by yGuard. However this is not a real problem, since the Ant file should nevertheless work as expected.
The basic idea is, that all elements will be obfuscated by this task.
You have to specify all classes, methods, fields, and attributes that should be exposed,
i.e. that will not be obfuscated but kept in the API. There are different use cases, where you
sometimes want to expose or simply just have to expose some elements.
See the General Hints and Complete Examples sections
at the end of this text for more information.
Exposing elements can be achieved by using both
the expose element and the mainclass attribute of the obfuscate
element.
obfuscate
ElementThe following attributes can be specified in the obfuscate
element.
mainclass
can be used as a shortcut to specify the mainclass
of your application. Both the class name and the main method will not be
obfuscated. Alternatively you may want to consider to expose the main method only. If your jar contains
a Main-Class
attribute yGuard will automatically adjust the value to the obfuscated name.
logfile
determines the name of the logfile that is generated
during the obfuscation process. The logfile contains information about the
mappings the obfuscator generates as well as any warnings.
conservemanifest
is a boolean attribute (valid values: true
/false
)
that determines whether the manifest file of the jars should be left untouched
by the obfuscator. If set to false
(the default), the manifest
will be modified to reflect the new message digests.
replaceClassNameStrings
is a boolean attribute (valid values: true
/false
)
that determines whether yGuard should try to replace hardcoded Strings, which
are used in conjunction with the MyClass.class
construct.
If set to false
(the default), those Strings will be left
untouched and code of the form MyClass.class
will break if
MyClass gets obfuscated by name. If set to true
yGuard will try
to workaround this problem by replacing the hardcoded String with the
appropriate obfuscated name. However this will only work if the unobfuscated
class file has been generated with the usual compilers ('javac', 'jikes' and
'bjc') or compilers, that produce similar bytecode. This can also have the
side-effect of modifying too many Strings, e.g if you have code that looks like
System.out.println("com.mycompany.MyClass");
, it might get
replaced, if MyClass.class
resides in the very same class with
something like System.out.println("com.A.OoO");
. It will most
likely fail if the class has been previously obfuscated by another obfuscation
tool or a different compiler has been used for compilation. Anyway it is
always worth it to give it a try, if you want to have 'full obfuscation'.
inoutpair
Elements
At least one inoutpair
element has to be specified in order
to run the obfuscation task. This elements specifies the jar files, that
contain the class files, that should be obfuscated and the name of the
jar file, that will be created during the obfuscation process.
Note that only class files will be used for obfuscation, other files
(resources, ...) will be simply copied from the source jar to the target
jar.
This element has two mandatory attributes:
in
attribute specifies an exisiting jar file, which
contains the unobfuscated .class files.
out
attribute specifies a name for a jar file, which
will be created and used to put the results of the obfuscation process.
externalclasses
Element
If the jar to be obfuscated depends on external classes or libraries, this
element can be used to specify classpaths to these entities.
These libraries will neither be modified nor obfuscated. Use the
inoutpair
element for this purpose!
See the examples later in this document for an example of when to use this
element.
The elements attributes and child elements can be seen on the Ant documentation
page about using path elements.
property
Elements
property
elements can be used to give hints to the obfuscation
engine. Depending on the exact version of yGuard, the task may use these hints
to control the process of obfuscation.
This element has two mandatory attributes:
name
attribute specifies a key which may be interpreted
by the obfuscation task.
value
attribute specifies the corresponding value of the
property.
As of yGuard version 1.5, the following attributes are supported:
error-checking
attribute can be used to tell yGuard
to bail out if it detects any problems. Currently this property can
be set to the following value:
pedantic
will make the obfuscation run fail, i.e. the
target which uses the obfuscate
element will fail, if
yGuard detects any problems.
naming-scheme
attribute can be used to tell yGuard to use
a different naming scheme during the obfuscation. Currently this property can
be set to one of the following values:
small
will produce very short names, i.e. the resulting jar
file will be as small as possible.
best
will produce names, that are very likely to be
misunderstood by decompilers and disassemblers. Using this naming-scheme
it is even impossible on most filesystems to successfully unjar or unzip the
resulting jar file (Windows, Standard Unix, Standard Linux, MacOS).
However this scheme takes up a lot of space and the resulting jar is likely to
become large (typically roughly double the size).
mix
is a mixture of both the other two values, which leads
to reasonable small but still hard to decompile jar files.
language-conformity
attribute can be used to advise
yGuard to produce names, that should be decompilable by most decompilers. On
the other hand, yGuard can produce class files that should be executable and
verifiable by all of todays virtual machines, but produces absolutely nonsense
names when decompiled (Ever tried to compile 'int class = false.this
super(String$super.init if);
' ?!)
Currently this property can
be set to one of the following values:
compatible
will produce names, that are ok for (most) decompilers,
java, jar and manifest files and can be unzipped to most filesystems.
legal
will produce names, that are ok for (some) decompilers,
java, jar and manifest files.
illegal
will produce names, that will crash some tools but
usually not the jvm, but JBuilder7 in many occasions for example.
obfuscation-prefix
attribute can be used to instruct yGuard
to prefix packages, that are fully obfuscated with a given package prefix, e.g.
com.mycompany.obf
.
expose-attributes
attribute can be used to give yGuard
a list of attributes yGuard should expose in addition to the standard attributes.
By default yGuard removes unneeded attributes like "Deprecated" from methods. The value
can be a comma separated list of attributes as defined in
Section 4.7 of the VM Specification of the .class File Format. E.g. in order to keep
the "Deprecated" attribute one can add the following property:
<property name="expose-attributes" value="Deprecated"/>
expose
Element
This element is a child of the obfuscate
element. It can be used to
specify elements, that are exposed to the outside world. These can be
classes, methods, fields, and attributes.
The ObfuscatorTask can be used to remove a lot of unnecessery information
from class files, that are to be distributed and should not be decompiled.
There are a number of different boolean attributes one can specify to control what information
should be exposed (i.e. not be obfuscated or removed) by this task.
This will affect all class files.
For a better control of which attributes should
be exposed or removed from what classes use the attribute
Element.
sourcefile
determines whether the name of the original
source code file should be included in the obfuscated versions of the
classes. Default is 'false
', i.e. this information will be removed.
linenumbertable
determines whether the line number table,
that contains a mapping from each opcode in the class file to the line number
in the original source code file
should be included in the obfuscated versions of the
classes. Default is 'false
', i.e. this information will be removed.
localvariabletable
determines whether the local variable table,
that contains a mapping from each local variable in the class file
to the name that has been used
in the original source code file
should be included in the obfuscated versions of the
classes. Default is 'false
', i.e. this information will be removed.
localvariabletypetable
determines whether the local variable type table,
that contains a mapping from each local variable in the class file
to the name and its generic type signature that has been used
in the original source code file
should be included in the obfuscated versions of the
classes. Default is 'false
', i.e. this information will be removed.
class
Element
The class
element can be used to expose classes to the public, i.e. to exclude their names
and/or their methods and fiels from the obfuscation process, so that they remain unchanged.
There are two attributes, which determine what to do with the methods and fields of the specified class:
methods
determines what methods will be exposed, depending
on their visibilityfields
determines what methods will be exposed, depending
on their visibility
The methods
, fields
and classes
(see the following paragraphs) determine what elements get exposed during
obfuscation. The following table lists the possible values for all of these
attributes and shows which elements will be exposed. A '*' denotes, that
elements, which have the given visibility will be exposed, for the specified
attribute value. A '-' denotes, that the obfuscator tries to obfuscate these
elements.
Value/Visibility | public |
protected |
friendly |
private |
---|---|---|---|---|
none |
- | - | - | - |
public |
* | - | - | - |
protected |
* | * | - | - |
friendly |
* | * | * | - |
private |
* | * | * | * |
There are three possible ways of specifying classes.
<class name="mypackage.MyClass"/>
<class> <patternset> <include name="com.mycompany.**.*Bean"/> <exclude name="com.mycompany.secretpackage.*"/> <exclude name="com.mycompany.myapp.SecretBean"/> </patternset> </class>This will expose all classes which reside in the package subtree of com.mycompany and whose name ends with Bean except for those, that reside in the com.mycompany.secretpackage package and the single SecretBean in com.mycompany.myapp .
<class> <patternset> <include name="com.mycompany.myapp.MainClass"/> <include name="org.w3c.sax?."/> <exclude name="org.w3c.sax?.**.*$$*"/> </patternset> </class>This will expose the MainClass class and all classes, which reside in packages like org.w3c.sax1, org.w3c.sax2, org.w3c.saxb except for inner classes. '$' is used as a separator between outer class names and inner class names. Since Ant uses '$' as an escape character, you have to use two consecutive '
$
's ('$$
') if you want to pass
one as an argument to the task.
public
, protected
, package friendly
or private
(inner classes). This can be achieved by
additionally specifying the classes
attribute in the class
element.
<class classes="protected"> <patternset> <include name="com.mycompany.myapi."/> </patternset> </class>This will expose all class names, that are either
public
or
protected
and
which reside in one of the subpackages of com.mycompany.myapi
(note the
abbreviation: the trailing dot behaves like the trailing '/
' in the usual
patternset
, i.e. it could be rewritten as com.mycompany.myapi.**.*
)
<class classes="protected" methods="protected" fields="protected"> <patternset> <include name="**.*"/> </patternset> </class>This example shows the very common use case of exposing a complete public API. There is an abbreviation for this use case: you can omit the
patternset
element, since in the case where the
classes
attribute is specified and there is no
patternset
child
element used, the task will automatically apply this rule. In this example
all classes will be exposed, that are either public or protected.
Their methods and fields will be exposed as long as they are declared
public
or protected
. If a class is package friendly
or private
(inner classes), neither itself
nor its methods or fields will be exposed.
The last example shows how to expose the public methods of certain classes only, but neither field names nor the class names themselves.
<class classes="none" methods="public" fields="none"> <patternset> <include name="com.mycompany.myapi."/> </patternset> </class>
method
Element
Using the method
element you can specify methods by signature
which should be exposed, i.e. left unobfuscated.
This element has two attributes:
class
attribute specifies the class which contains the
method. Use the normal java syntax, i.e. the fully qualified name.
This attribute can be omitted, if the patternset element is used as
a child element, in which case the all classes matching the patternset
will be searched and their corresponding methods will be exposed.
name
attribute specifies the method to expose. Use the
complete signature using fully qualified class names and the return type!
Some examples:
<method class="com.mycompany.myapp.MyClass" name="void main(java.lang.String[])"/> <method class="com.mycompany.myapp.MyClass" name="int foo(double[][], java.lang.Object)"/> <method name="void writeObject(java.io.ObjectOutputStream)"> <patternset> <include name="com.mycompany.myapp.data.*"/> </patternset> </method> <method name="void readObject(java.io.ObjectInputStream)"> <patternset> <include name="com.mycompany.myapp.data.*"/> </patternset> </method>
This will expose the main method of the MyClass class and the foo method. Additionally all readObject and writeObject methods (used for Serialization) will be exposed in all classes of the com.mycompany.myapp.data package. Note that you have to specify the return argument's type, even if it is void and that you have to use the fully qualified name for all classes, even those, that are in the java.lang package.
field
Element
Using the field
element you can specify fields by name
which should be exposed, i.e. left unobfuscated.
This element has two attributes:
class
attribute specifies the class which contains the
field. Use the simple name of the field.
This attribute can be omitted, if the patternset element is used as
a child element, in which case the all classes matching the patternset
will be searched and their corresponding methods will be exposed.
name
attribute specifies the field to expose. Use the name
of the field only, do not include its type!
Some examples:
<field class="com.mycompany.myapp.MyClass" name="field"/> <field name="serialVersionUID"> <patternset> <include name="com.mycompany.myapp.data.*"/> </patternset> </field>
This will expose the field named 'field' of the MyClass class. Additionally all the serialVersionUID fields (used for Serialization) will be exposed in all classes of the com.mycompany.myapp.data package.
attribute
Element
Using the attribute
element you can specify for which classes the
which attributes should be exposed, i.e. left unobfuscated.
This element has only one attribute:
name
attribute specifies the class which contains the
field. Use the simple name of the field.
This attribute can be omitted, if the patternset element is used as
a child element, in which case the all classes matching the patternset
will be searched and their corresponding methods will be exposed.
name
attribute specifies the field to expose. Use the name
of the field only, do not include its type!
An example:
<attribute name="SourceFile, LineNumberTable, LocalVariableTable"> <patternset> <include name="com.mycompany.mylibrary.**"/> </patternset> </attribute>
This will expose the attributes named "SourceFile
", "LineNumberTable
",
and "LocalVariableTable
" effectively enabling debugging information for all classes in the
com.mycompany.mylibaray
package and subpackages.
sourcefile
Element
Using the sourcefile
element you can specify for which classes the
sourcefile attribute (used in stacktraces, e.g.) should be exposed, i.e. left unobfuscated.
Although roughly the same can be achieved using the attribute
element,
this element has no attributes but allows for nested property elements that can be used to hint the obfuscation
engine what to do with the source file names.
Some examples:
<sourcefile> <patternset> <include name="com.mycompany.myapp.**"/> </patternset> </sourcefile>
This will expose source file names of all the classes in the com.mycompany.myapp packages and subpackages.
Note that this will prevent proper obfuscation, since the name of the source file is normally strongly coupled
with the unobfuscated name of the class (e.g. "MySecretAlgorithm.java
".
The following example shows how to overcome this.
<sourcefile> <property name="mapping" value="y"/> <patternset> <include name="com.mycompany.myapp.**"/> </patternset> </sourcefile>
This will map all of the source file attributes in the packages below com.mycompany.myapp
to "y
",
which is small and generally a nice letter.
Note that as of yGuard 1.5 the property "mapping
" is the only property currently being supported in the
sourcefile
element.
linenumbertable
Element
Using the linenumbertable
element you can specify for which classes the
linenumbertalbe code attribute (used in stacktraces, e.g.) should be exposed, i.e. left unobfuscated.
Although roughly the same can be achieved using the attribute
element,
this element has no attributes but allows for nested property elements that can be used to hint the obfuscation
engine what to do with the line number tables.
Some examples:
<linenumbertable> <patternset> <include name="com.mycompany.myapp.**"/> </patternset> </linenumbertable>
This will expose the line numbers of all the classes in the com.mycompany.myapp packages and subpackages.
Note that in order to see the line numbers in stacktraces, the sourcefile
attribute has to be exposed for those files, too, since otherwise the JDK will display "Unknown source.
"
for the stack elements.
<linenumbertable> <property name="mapping-scheme" value="scramble"/> <property name="scrambling-salt" value="1234"/> <patternset id="CompanyPatternSet"> <include name="com.mycompany.myapp.**"/> </patternset> </linenumbertable> <sourcefile> <property name="mapping" value="y"/> <patternset refid="CompanyPatternSet"/> </sourcefile>
This will expose scrambled line numbers for all classes found in and below the com.mycompany.myapp packages.
The scrambling algorithm will use the given "salt" value to use a predefined scrambling scheme.
In order to see the scrambled line numbers, a sourcefile
element is used
on the same patternset, which is referenced by its previously declared reference id, to rename the source files to
"y
".
The mapping-scheme
property can be used with the following two values as of yGuard 1.5:
scramble
: this will use a non-trivial algorithm to scramble the line numbers in the existing file.
The algorithm implemented uses for each class a different scrambling scheme. The optional scrambling-salt
property can be used to provide an integer value that will be used to "salt" the algorithm's random seed for the scrambling.
The size of the (uncompressed) .class file will not change using this mapping scheme.
squeeze
: this will use a simple algorithm that virtually puts all of a methods code into the first line of
code of the method. It will appear as if each method had been written in a single line of code.
The advantage of this scheme is drastically reduced size requirements and thus smaller .class files, while at the same time
it will be possible to unambiguously determine the exact method from a stacktrace.
adjust
Element
Using the adjust
element one can specify resource files whose names and/or
contents should be adjusted to reflect the obfuscated class names.
This element has three attributes:
replaceName
attribute specifies whether or not the names of
the specified resources should be adjusted. By default this value is set to false
.
replaceContent
attribute specifies whether or not the contents of
resource files should be adjusted. By default this value is set to false
.
replacePath
attribute specifies whether or not the paths to the
resource files should be adjusted. By default this value is set to true
.
Some examples:
<!-- adjust the names of all java property files --> <adjust replaceName="true"> <include name="**/*.properties"/> </adjust> <!-- adjust the classnames specified within a single XML file --> <adjust file="plugins.xml" replaceContent="true" /> <!-- suppress the adjustment of the resource path com/mycompany/myapp/resource. --> <!-- the package com.mycompany.myapp still gets obfuscated. --> <adjust replacePath="false"> <include name="com/mycompany/myapp/resource/*"/> </adjust>
map
ElementThe map
element is an immediate optional child of the
obfuscate
element. It can be used to specify the mapping
for the obfuscation process directly. This is an advanced topic.
The element can have for different kinds of child elements.
package
element can be used to specify the obfuscated name for the
given package. Note that only the last path element, i.e. the innermost subpackage
name will be affected.
class
element can be used to specify the obfuscated name for the
given class. Note that only the class name, not its package path will be modified.
method
element can be used to specify the obfuscated name for the
given method in a class.
field
element can be used to specify the obfuscated name for the
given field in a class.
All of these elements use the name
attribute to specify the specific
element. The method
and field
element need the
class
attribute in order to function properly. Neither wildcards
nor nested patternset
elements are allowed.
Use the map
attribute to specify the new name (subpackage, classname,
methodname and fieldname respectively).
Some examples:
<map> <package name="com" map="etc"/> <package name="com.mycompany" map="nocompany"/> <package name="com.mycompany.myapp" map="asdf"/> <class name="com.mycompany.myapp.MainApp" map="foo"/> <method class="com.mycompany.myapp.MainApp" name="void main(java.lang.String[])" map="bar"/> <field class="com.mycompany.myapp.MainApp" name="field" map="a"/> </map>
In this example the package structure 'com.mycompany.myapp' will be obfuscated to 'etc.nocompany.asdf'. The MainApp class will be called 'foo' and its main method will be remapped to 'bar' (and can therefor not be executed from commandline anymore). The field called 'field' will be renamed to 'a'.
The true power of the map
element lies in its use together
with the patch
element, which itself is a child element
of the obfuscate
top level element.
Using the patch
element one can generate jars, that can be used
to serve as patches for versions of an application that have already been
deployed in obfuscated form.
During the main obfuscation run, yGuard produces an xml-logfile, in which
the mapping between the unobfuscated and obfuscated names is contained.
The patch
element is used to declare a set of classes, that need
to be patched. During the obfuscation, yGuard will include those files
in the obfuscated jars only, that are declared inside this element.
For example:
<patch> <class name="com.mycompany.myapp.MainClass"/> <class> <patternset> <include name="com.mycompany.myapp.bugs.*"/> </patternset> </class> </patch> <map logfile="yguardlog.xml"/>
This will only include the MainClass class and all classes that belong to the
bugs package in a patch jar.
In order to work with the previously delivered obfuscated version, it is
important to use the map
element to specify the mapping of the elements
from the previous run. This can most conveniently be achieved by
specifying the log file from the corresponding run in the map's logfile attribute.
There will be some examples given, that represent common use cases.
Following are the contents of a complete build.xml
file.
Just copy the following lines to a new document named build.xml
and put the file into your project's root directory.
<?xml version="1.0" encoding="UTF-8"?> <!-- file build.xml in your project root directory --> <!-- ANT build script for yfiles --> <!-- The java based ANT tool is available from --> <!-- http://jakarta.apache.org/ant --> <!-- This file demonstrates the use of the yGuard byte --> <!-- code obfuscator from yWorks Gmbh --> <!-- yGuard can be downloaded from --> <!--- http://www.yworks.com/products/yguard --> <project name="project" default="obfuscate" basedir="."> <!-- edit the following lines to your needs --> <target name="init"> <property name="project_name" value="DemoProject"/> <property name="srcDir" value="."/> <property name="classDir" value="classes"/> <property name="jar" value="${project_name}.jar"/> <property name="obfjar" value="${project_name}_obf.jar"/> <property name="obfuscationlog" value="${project_name}_obflog.xml.gz"/> <property name="mainclass" value="com.mycompany.myapp.Main"/> <mkdir dir="${classDir}" /> </target> <!-- obfuscate --> <target depends="jar" name="obfuscate"> <taskdef name="obfuscate" classname="com.yworks.yguard.ObfuscatorTask" classpath="yguard.jar"/> <!-- the following can be adjusted to your needs --> <obfuscate mainclass="${mainclass}" logfile="${obfuscationlog}" replaceclassnamestrings="true"> <property name="error-checking" value="pedantic"/> <inoutpair in="${jar}" out="${obfjar}"/> <expose> <class classes="protected" methods="protected" fields="protected"> <patternset> <include name="com.mycompany.publicapi.**.*"/> <exclude name="com.mycompany.publicapi.private.*"/> </patternset> </class> </expose> </obfuscate> </target> <!-- compile --> <target name="compile" depends="init"> <javac srcdir="${srcDir}" includes="com/mycompany/**/*.java" destdir="${classDir}"> </javac> </target> <!-- create .jar --> <target name="jar" depends="compile"> <jar jarfile="${jar}" basedir="${classDir}" includes="com/mycompany/**"> <fileset dir="${srcDir}"> <include name="com/mycompany/resources/*.properties"/> </fileset> </jar> </target> <!-- run project --> <target name="run" depends="obfuscate"> <java classname="${mainclass}" fork="true"> <classpath> <pathelement location="${obfjar}"/> </classpath> </java> </target> <!-- removes all that has been built --> <target name="clean" depends="init"> <delete dir="${classDir}" includeEmptyDirs="true" /> </target> </project> <!-- end file build.xml -->
An alternative obfuscate
section could look like this:
<obfuscate mainclass="com.mycompany.myapp.Main" replaceclassnamestrings="true" logfile="log.xml.gz"> <inoutpair in="classes.jar" out="classes_obf.jar"/> <inoutpair in="utils.jar" out="utils_obf.jar"/> <!-- don't let the obfuscator remove the "Deprecated" --> <!-- attributes from the .class file entries --> <expose> <class classes="protected" methods="protected" fields="protected"/> <attribute name="Deprecated"/> </expose> </obfuscate>
This case is especially useful when you want to provide and expose a public API.
All the classes, methods and fields, that can be seen in a javadoc generated
API will be exposed. Package friendly and private classes, methods and fields
will be obfuscated whenever possible.
This example also displays the use of the "attribute" element. In this
case it prevents the obfuscator from removing the "Deprecated" flag from
the entities in the .class files.
<obfuscate mainclass="com.mycompany.myapp.Main" logfile="log.xml" replaceclassnamestrings="true"> <inoutpair in="demo.jar" out="demo_obf.jar"/> <property name="language-conformity" value="illegal"/> <property name="naming-scheme" value="mix"/> <expose> <!-- needed for reflection --> <class name="com.mycompany.myapp.data.DataObject" methods="public" fields="none"/> <!-- needed for reflection (name only) --> <class name="com.mycompany.myapp.data.InnerDataObject"/> <!-- needed for serialization --> <method name="void writeObject(java.io.ObjectOutputStream)"> <patternset id="datapatternset"> <include name="com.mycompany.myapp.data.*"/> </patternset> </method> <method name="void readObject(java.io.ObjectInputStream)"> <patternset refid="datapatternset"/> </method> <field name="serialVersionUID"> <patternset refid="datapatternset"/> </field> </expose> </obfuscate>
<obfuscate mainclass="com.mycompany.myapp.Main" logfile="log.xml" replaceclassnamestrings="true"> <inoutpair in="mydemo.jar" out="mydemo_obf.jar"/> <property name="error-checking" value="pedantic"/> <externalclasses> <pathelement location="lib/external.jar"/> <pathelement location="lib/additional/classes/"/> </externalclasses> <expose> <class classes="public"/> </expose> </obfuscate>
This example demonstrates full method and field obfuscation for a program, that
has external dependencies. The dependencies are specified in the
externalclasses>
element using standard Ant path specification
mechanisms. Classes residing in lib/external.jar
and underneath
the lib/additional/classes/
directory (note the trailing slash),
will be used to resolve external dependencies during the obfuscation run.
This is necessary if external classes want to access obfuscated classes
directly using an externally defined interface or superclass. yGuard
automatically detects externally declared methods and prevents obfuscation of
these items. As a result, the obfuscated jar can be used together with
unmodified versions of external libraries without causing any problems.
This example also demonstrates the use of the error-checking
property. In this case the Ant target fails if any problem is detected during
the obfuscation run.
.properties
Files and Other Resource Files<obfuscate mainclass="com.mycompany.myapp.Main" logfile="log.xml" replaceclassnamestrings="true"> <inoutpair in="myapp.jar" out="myapp_obf.jar"/> <adjust replaceContent="true"> <!-- plain-text class names in the config files will --> <!-- be replaced with the obfuscated name versions --> <include name="**/*.config"/> <include name="com/mycompany/myapp/init/Main.properties"/> </adjust> <adjust replacePath="false"> <!-- keep the complete path to the resources, (gifs...) even if --> <!-- package com.mycompany.myapp gets obfuscated by name --> <include name="com/mycompany/myapp/resources/*"/> </adjust> <adjust replaceName="true"> <!-- Replace the .properties files' names with the obfuscated --> <!-- versions if the corresponding .class files get obfuscated --> <include name="**/*.properties"/> </adjust> </obfuscate>
This example, too, demonstrates full method and field obfuscation for a program, that
uses .properties files and other resources files.
Some configuration files are used that contain fully qualified classnames
for plugins that are going to be obfuscated. Therefor yGuard is instructed to
automatically replace the plain-text entries in those files with the obfuscated
name versions.
Additionally some resources are hardcoded into the classes (image locations
and html files, e.g.). yGuard gets instructed not to move these resource files
even if they reside in a package structure that is obfuscated.
Since the property files have been created with the same name as the classes
that make use of them and they are being loaded using this.getClass().getName()
,
yGuard is configured to rename the .properties files according to the obfuscated
names of the corresponding .class files.
<obfuscate mainclass="org.myorg.myapp.Main" logfile="log.xml.gz" replaceclassnamestrings="true"> <inoutpair in="myapp.jar" out="myapp_obf.jar"/> <inoutpair in="lib/thirdpartylib.jar" out="lib/thirdpartylib_obf.jar"/> <property name="error-checking" value="pedantic"/> <externalclasses> <pathelement location="lib/external.jar"/> </externalclasses> <expose> <!-- Tell the obfuscator to only adjust my classes --> <!-- to work with the obfuscated 3rd party library --> <!-- but leave them virtually unmodified otherwise --> <!-- The libconnector package however will be --> <!-- obfuscated as much as possible --> <class classes="private" methods="private" fields="private"> <patternset id="myopenapp"> <include name="org.myorg.myapp.**"/> <exclude name="org.myorg.myapp.mylibconnector.**"/> </patternset> </class> <!-- Keep all of the attributes for debugging, e.g. --> <attribute name="Deprecated, SourceFile, LineNumberTable, LocalVariableTable> <patternset refid="myopenapp"/> </attribute> </expose> </obfuscate>
This example demonstrates almost no method, class, and field obfuscation for a program, that
has external dependencies and additionally depends on a third party library jar
which has to be obfuscated before deployment. Only those parts that actually interface with
the third party jar in the mylibconnector
package are being obfuscated.
Nothing in the third party library jar will be
exposed in the final application, everything will be obfuscated and the code
in the open application that makes use of the third party jar will be adjusted.
Note that the public part of the application will still be debuggable since all of the
crucial attributes will be exposed for the open application part.
The dependencies are specified in the
externalclasses>
element using standard Ant path specification
mechanisms. Classes residing in lib/external.jar
will be used to resolve external dependencies during the obfuscation run.
This is not strictly necessary in this case since the public API will be fully exposed,
i.e. no methods which have been declared by interfaces or super class in external
classes will be renamed.
yGuard provides a simple tool that makes it easy for the obfuscating party to deobfuscate
stacktraces which have been obfuscated using yGuard. During the obfuscation yGuard
produces an XML logfile which can automatically be gzipped for convenient storage.
You should always keep those logfiles in order to be able to deobfuscate fully qualified
classnames or methods or fields for debugging purposes, for example.
In order to run the yGuard deobfuscation tool do the following:
Console> java -jar yguard.jar mylogfile.xml.gz
A tiny GUI will popup that will enable you to easily deobfuscate stacktraces and
fully qualified classnames as well as provide a convenient way to browse the mapping
generated by yGuard.
In the main window a tree view displays the package, class, and classmember hierarchy using the unobfuscated
names. For each entry that has been obfuscated
(classes, packages, methods, and fields that have not been obfuscated at all may not always be shown in the tree)
the corresponding mapped/obfuscated name is displayed.
The two buttons at the top of the window allow to change the sorting of the items in the tree structure, so that
they are either sorted according to their names before or after the obfuscation. Items will always be sorted by type first:
packages, classes, innerclasses, methods, and fields. Small icons provide a convenient way to quickly find the corresponding items.
The lower part of the window contains an editable text area that can be used to enter text or paste stacktraces in. Pressing the button at the bottom of the window labelled "Deobfuscate" will trigger the deobfuscation of the contents in the text area. The tool will try to identify fully qualified class names (separated by dots) and use the mapping information to reconstruct the original names. If the tool identifies a stack trace element, it will try to deobfuscate scrambled line numbers, too, if they have been scrambled during the obfuscation process.
There are a couple of things you should be aware of, when obfuscating software. You can find a lot of information on http://www.Retrologic.com. This is the site which hosts 'Retroguard'. The yGuard library has been derived from Retroguard. The most important facts are described here briefly:
MyApplication.class
will break in the
if MyApplication will be obfuscated by name
and the obfuscation switch replaceClassNameStrings
is not set or
set to a value of
false
. See obfuscate
for
more information about that switch.
Class.forName(className)
will not work unless you use the
obfuscated name string in your variable or the String is a local constant and
replaceClassNameStrings
is set to true
.
If you experience any problems or think you have found a bug feel free to send an email to yguard@yworks.com but please make sure you have read the documentation thoroughly before. We will do our best and try to answer your questions.