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 JavaTM graph visualization framework yFiles and other fine products.

yGuard Ant Task Documentation

Contents

Installation

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:

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

DTD used for Ant <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)*>
  <!ATTLIST expose
      linenumbertable CDATA #IMPLIED
      localvariabletable CDATA #IMPLIED
      sourcefile 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 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 and fields that should be exposed, i.e. that will not be obfuscated. There are different use cases, where you sometimes want to expose or simply just have to expose some elements. See the general hints at the end of this text for more information. This can be achieved by using both the expose element and the mainclass attribute of the obfuscate element.

The obfuscate Element

The following attributes can be specified in the obfuscate element.

The 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:

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

The 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:

As of yGuard version 1.3, the following attributes are supported:

The 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 and fields.

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 three boolean attributes, one can specify to control what information should be exposed (i.e. not be obfuscated or removed) by this task.

The class Element

The class element can be used to expose classes, i.e. to exclude their names and/or their methods and fiels from the obfuscation process.

There are two attributes, which determine what to do with the methods and fields of the specified class:

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.

  1. One can specify a java class using the fully qualified name in java syntax with the name attribute. For example:
    <class name="mypackage.MyClass"/>
  2. One can specify multiple java classes using a modified version of a patternset. The patternset's includes and excludes element should use java syntax, but the usual wildcards are allowed. Some examples:
    
      <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.
  3. Finally one can specify classes depending on their visibility, i.e. depending whether they have been declared 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>
     

The 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:

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.

The 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:

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.

The 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:

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>

  
 

The map Element

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

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

Generating Patch Jars

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.

Complete Examples

There will be some examples given, that represent common use cases.

Example 1: Getting started with ANT and yGuard (for ANT newbies)

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"/>
        <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 -->
 

Example 2: A Public API

An alternative obfuscate section could look like this:

  <obfuscate mainclass="com.mycompany.myapp.Main" logfile="log.xml">
    <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          -->
    <property name="expose-attributes" value="Deprecated"/>
    <expose>
      <class classes="protected" 
                methods="protected" 
                fields="protected"/>
    </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 "expose-attributes" property. In this case it prevents the obfuscator from removing the "Deprecated" flag from the entities in the .class files.

Example 3: A Demo Program

  <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>
          <include name="com.mycompany.myapp.data.*"/>
        </patternset>
      </method>
      <method name="void readObject(java.io.ObjectInputStream)">
        <patternset>
          <include name="com.mycompany.myapp.data.*"/>
        </patternset>
      </method>
      <field name="serialVersionUID">
        <patternset>
          <include name="com.mycompany.myapp.data.*"/>
        </patternset>
      </field>
    </expose>
  </obfuscate>
 

Example 4: A Program Using an External Library

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

Example 5: A Program with .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.

General Hints

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:

Problems and Bug Reports

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.