本文从几个不同的方面比较了两种Free的Java Obfuscator的优缺点..
两种Free的Java Obfuscator: yguard和Proguard.
yguard:http://www.yworks.com/en/products_yguard_about.htm
Proguard:http://proguard.sourceforge.net/
yguard的速度远远高于proguard, 主要区别在于proguard不管用没有用, 上来就扫描jre/lib/rt.jar和其他所有用到的library。这个回合yguard胜出
yguard: ywork公司的产品,免费使用, 但不是Open source
yguard支持Package Name obfuscate
这其实是很重要的一个特性, 一个好的Java程序往往在一个package里只有10个以下Class,根据package Name很容易猜出各个Class是干什么的, 比如:
com.mycompany.license.a com.mycompany.license.b com.mycompany.license.c
虽然obfuscate了Class Name,但是因为这个license只有3个Class,很容易猜出来是干什么的
com.mycompany.a.a com.mycompany.a.b com.mycompany.a.c
则大大增加了困难, 尤其大型software, 有几十个package的情况下, 会呈几何级数增加反编译的难度
这个回合yguard胜出, 但是Proguard已经准备在4.0里推出这个特性
proguard支持增量obfuscate , yguard不支持.
也就是在obfuscate a.jar时, 记住所有的名字映射关系(比如MyUtil->a), 然后在obfuscate b.jar的时候, 使用那个映射关系(所有调用MyUtil改成调用a)
假设a.jar里面有一个MyUtil被b.jar里面的Main使用, 在没有Incremental Obfuscate的情况下, 你必须指定不obfuscate a.jar里的MyUtil.
没有增量obfuscate 坏处是很显然的: 第一obfuscate的配置复杂了, 第二,暴露了某些Class
- Proguard可以用一篇文章中的词(比如莎士比亚的剧本)作为变量名字, 呵呵
- Proguard可以删除所有没有用到的Class或者方法(叫做Shrink)
- Yguard能够obfuscate资源名称, 比如Messages.properties->a.properties并且修改相应的ResourceBundle类
- Yguard可以replace指定文本文件里的Class Name,比如修改eclipse plugin.xml里的Class名字
对于小型java应用(只有一个package,一个jar文件), 两者区别很小, (但是小型的java应用值得obfuscate么?呵呵)
Documentation for the deprecated Ant syntax of previous yGuard releases is still .
yGuard 2.1 Ant Task Documentation
Contents
Introduction
Elements can be excluded from the renaming process using a nested element. Both elements operate on the same set of input and output files specified in the containing element.
Installation
<target name="yguard"> <taskdef name="yguard" classname="com.yworks.yguard.YGuardTask" classpath="yguard.jar"/> <yguard> <!-- insert your yguard elements here --> </yguard> </target>
For a complete build.xml
file have a look at the .
General Hints & Troubleshooting
rename
task, code in the form of MyApplication.class
will break if MyApplication will be obfuscated by name and the obfuscation switch is set to false
. The shrink
task will currently recognize code in the form of MyApplication.class
only if the java files were compiled using an arbitrary version of the standard javac compiler (although the shrinking engine might recognize the .class
construct also if the classes were compiled using a compiler that generates similar bytecode). Automatic introspection and reflection will break in most cases, when you decide to obfuscate the corresponding methods and fields. If you use the shrink
task and your application uses reflection you should explicitly designate all entities loaded per reflection as code entrypoints using the element. If your application is broken after using the
shrink
task, consider using the attribute of the shrink
task to find out which additional entities you need to include in the element. Class.forName(className)
will not work when using the rename
task unless you use the obfuscated name string in your variable or the String is a local constant and is not set or set to true
. If you use the shrink
task, className
should be contained in the list of entrypoints using the element. The customized serialization mechanism will not work if you obfuscated or shrinked the writeObject and readObject methods as well as the serializationUID field. Simple bean introspection will not work, if you decide to obfuscate your public accessor methods, since it makes use of reflection. If you do not set the -Xmx
property for the Java virtual machine, the yguard
Ant task might fail due to a java.lang.OutOfMemoryError
.To solve this problem, set the
-Xmx
option in the ANT_OPTS
variable, e.g.:
bash> export ANT_OPTS="-Xmx512M" or cshell> setenv ANT_OPTS "-Xmx512M"
DTD used for Ant <yguard>
<!ELEMENT yguard (inoutpair+,externalclasses?,attribute*,(shrink|rename)+)> <!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 attribute (patternset)*> <!ELEMENT shrink (entrypointjar*,keep?)> <!ELEMENT rename (property*,patch?,adjust*,map?,keep?)> <!ELEMENT property EMPTY> <!ATTLIST property name CDATA #REQUIRED value CDATA #REQUIRED> <!ELEMENT patch (class)*> <!ELEMENT adjust (#PCDATA)> <!ATTLIST adjust replaceName CDATA #REQUIRED replaceContent CDATA #REQUIRED replacePath CDATA #REQUIRED> <!ELEMENT map (class|method|field|package)*> <!ELEMENT package (#PCDATA)> <!ATTLIST package name CDATA #REQUIRED map CDATA #REQUIRED> <!ELEMENT keep (class|method|field|sourcefile|linenumbertable)*> <!-- NOTE: the nested <sourcefile>,<linenumbertable> and <attribute> sections are only supported in the <rename> element. --> <!ATTLIST keep linenumbertable CDATA #IMPLIED localvariabletable CDATA #IMPLIED localvariabletypetable CDATA #IMPLIED runtimeinvisibleannotations CDATA #IMPLIED runtimeinvisibletypeannotations CDATA #IMPLIED runtimevisibleannotations CDATA #IMPLIED runtimevisibletypeannotations CDATA #IMPLIED sourcefile CDATA #IMPLIED> <!ELEMENT class (patternset)*> <!ATTLIST class classes CDATA #IMPLIED fields CDATA #IMPLIED map CDATA #IMPLIED methods CDATA #IMPLIED name CDATA #IMPLIED> <!-- NOTE: the map attribute is only supported if the <class> element is nested inside an <rename> element. --> <!ELEMENT method (patternset)*> <!ATTLIST method class CDATA #IMPLIED map CDATA #IMPLIED name CDATA #IMPLIED> <!-- NOTE: the map attribute is only supported if the <method> element is nested inside an <rename> element. --> <!ELEMENT field (patternset)*> <!ATTLIST field class CDATA #IMPLIED map CDATA #IMPLIED name CDATA #IMPLIED> <!-- NOTE: the field attribute is only supported if the <method> element is nested inside an <rename> element. -->
The yguard
Element
The inoutpair
Elements
in
specifies an exisiting jar file, which contains the unshrinked and unobfuscated .class files. Yes out
specifies a path to a jar file which will be created and used to put the results of the shrinking and obfuscation process. Yes resources
Will only be considered if the element contains a nested element.Determines how the shrinking engine handles all non-.class files.
Currently the following three resource policies are supported:
copy
: the default, simply copies all resource files to the output jar. auto
: copies only those resource files that reside in a directory that still contains one or more .class files after shrinking. none
: discards all resource files. No, defaults to copy
.
The inoutpair
element has no child elements.
The externalclasses
Element
The attribute
Element
name
A comma-separated list of attribute names that are to be retained in the shrinked and/or obfuscated class files. Yes
<attribute name="SourceFile, LineNumberTable, LocalVariableTable"> <patternset> <include name="com.mycompany.mylibrary.**"/> </patternset> </attribute>
The shrink
Element
logfile
Determines the name of the logfile that is generated during the shrinking process. The logfile contains information about the entrypoints the shrinking engine uses, the removed classes, methods and fields as well as any warnings. If the name ends with a ".gz", yGuard will automatically create a gzipped version of the file which potentially saves a lot of disc space. No, defaults to yshrinklog.xml
createStubs
Instead of removing methods completely, this attribute causes the shrink
task to insert a method stub that throws a java.lang.InternalError
if it is called. This attribute is very useful if the shrinking process causes your application to break and you are uncertain about which additional code entities you have to include in the element. Note that classes considered as completely obsolete by the shrinking engine are still removed completely - this attribute only affects obsolete methods of non-obsolete classes. No, defaults to false
The entrypointjar
Element
entrypointjar
element has no child elements.
<yguard> <inoutpair in="lib-in.jar" out="lib-out.jar" /> <shrink> <entrypointjar name="myApp.jar"/> </shrink> </yguard>
The rename
Element
mainclass
Can be used as a shortcut to specify the mainclass of your application. Both the class name and the main method will be excluded from name obfuscation. Alternatively you may want to consider to exclude the main method only. If your jar contains a Main-Class
attribute, the rename task will automatically adjust the value to the obfuscated name. No logfile
Determines the name of the logfile that is generated during the renaming process. The logfile contains information about the mappings the name obfuscator generates as well as any warnings. If the name ends with a ".gz", yGuard will automatically create a gzipped version of the file which potentially saves a lot of disc space. No, defaults to yguardlog.xml
conservemanifest
a boolean attribute (valid values: true
/false
) that determines whether the manifest file of the jars should be left untouched by the renaming engine. If set to false
, the manifest will be modified to reflect the new message digests. No, defaults to false. replaceClassNameStrings
a boolean attribute (valid values: true
/false
) that determines whether the renaming engine should try to replace hardcoded Strings, which are used in conjunction with the MyClass.class
construct. If set to false
, 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
(the default), 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'. No, defaults to true
The property
Elements
name
specifies a key which may be interpreted by the obfuscation task. Yes value
specifies the corresponding value of the property. Yes
The following properties are supported:
Name Descriptionerror-checking
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 rename
element will fail, if yGuard detects any problems. naming-scheme
Can be used to tell the renaming engine 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
Can be used to advise the renaming engine 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. overload-enabled
Determines whether the renaming engine tries to use the same names for methods with different signatures or whether it always generates unique method names. Setting this property to false
eases the analysis of stacktraces but reduces the obfuscation effect. obfuscation-prefix
Can be used to instruct the renaming engine to prefix packages, that are fully obfuscated with a given package prefix, e.g. com.mycompany.obf
. expose-attributes
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 . E.g. in order to keep the "Deprecated" attribute one can add the following property: <property name="expose-attributes" value="Deprecated"/>
Note that this affects all classes which will be obfuscated. For a better control of which attributes should be exposed in what classes use the .
property
element has no child elements.
The keep
Element
sourcefile
Determines whether the name of the original source code file should be included in the output class files. No, defaults to '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 output class files. No, defaults to '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 output class files. No, defaults to '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 output class files. No, defaults to 'false
', i.e. this information will be removed. runtimevisibleannotations
Determines whether annotations with the retention policy RetentionPolicy.RUNTIME
should be included in the output class files. No, defaults to 'false
', i.e. these annotations will be removed. runtimevisibleparameterannotations
Determines whether method paramater annotations with the retention policy RetentionPolicy.RUNTIME
should be included in the output class files. No, defaults to 'false
', i.e. these annotations will be removed. runtimeinvisibleannotations
Determines whether annotations with the retention policy RetentionPolicy.CLASS
should be included in the output class files. No, defaults to 'false
', i.e. these annotations will be removed. runtimeinvisibleparameterannotations
Determines whether method paramater annotations with the retention policy RetentionPolicy.CLASS
should be included in the output class files. No, defaults to 'false
', i.e. these annotations will be removed.
Child Elements only available in the task
The class
Element
public
protected
friendly
private
none
- - - - public
* - - - protected
* * - - friendly
* * * - private
* * * *
name
The name of the class to be kept. No classes
The visibility of the classes to be kept. No, defaults to none
methods
The visibility of the methods to be kept. No, defaults to none
fields
The visibility of the fields to be kept. No, defaults to none
extends
If no name
attribute is given, keeps all classes that equal or extend the class defined by the given fully qualified classname. See for an example usage of this attribute. No
implements
If no name
attribute is given, keeps all classes that equal or implement the class defined by the given fully qualified classname.See for an example usage of this attribute. No
name
attribute. For example:
<class name="mypackage.MyClass"/>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. 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 keep 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 excluding a complete public API from the shrinking and obfuscation process. 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.
<class classes="none" methods="public" fields="none"> <patternset> <include name="com.mycompany.myapi."/> </patternset> </class>
The method
Element
name
Specifies the method to keep. Use the complete signature using fully qualified class names and the return type! Yes class
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 all classes matching the patternset will be searched and their corresponding methods will be kept. No
<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>
The field
Element
name
specifies the field to keep. Use the name of the field only, do not include its type! Yes class
Specifies the class which contains the field. 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 fields will be kept. No
<field class="com.mycompany.myapp.MyClass" name="field"/> <field name="serialVersionUID"> <patternset> <include name="com.mycompany.myapp.data.*"/> </patternset> </field>
The sourcefile
Element
The following properties are supported:
Name Descriptionmapping
the value of this property determines the name all sourceFile attributes matched by the sourcefile
element are mapped to.
property
<!-- <pre class="programlisting"> <sourcefile> <patternset> <include name="com.mycompany.myapp.**" /> </patternset> </sourcefile> </pre> <p> 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. "<code>MySecretAlgorithm.java</code>". The following example shows how to overcome this. </p> -->
<sourcefile> <property name="mapping" value="y"/> <patternset> <include name="com.mycompany.myapp.**"/> </patternset> </sourcefile>
The linenumbertable
Element
The following properties are supported:
Name Descriptionmapping-scheme
can be used with the following two values:
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. scrambling-salt
can be used in conjunction with mapping-scheme
to provide an integer value that will be used to "salt" the algorithm's random seed for the scrambling.
property
patternset
<linenumbertable> <patternset> <include name="com.mycompany.myapp.**"/> </patternset> </linenumbertable>
<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>
The adjust
Element
replaceName
specifies whether or not the names of the specified resources should be adjusted. No, defaults to false
replaceContent
specifies whether or not the contents of resource files should be adjusted. No, defaults to false
replacePath
specifies whether or not the paths to the resource files should be adjusted. No, defaults to true
adjust
element can be used just like the standard Ant element.
<!-- adjust the names of all java property files in the jars --> <adjust replaceName="true"> <include name="**/*.properties"/> </adjust> <!-- adjust the classnames specified within a single XML file in the jar --> <adjust file="plugins.xml" replaceContent="true" /> <!-- suppress the adjustment of the resource path com/mycompany/myapp/resource in the jar. --> <!-- the package com.mycompany.myapp still gets obfuscated. --> <adjust replacePath="false"> <include name="com/mycompany/myapp/resource/*"/> </adjust>
The map
Element
package
class
method
field
<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>
Generating Patch Jars
yguard
element has no attributes.
<patch> <class name="com.mycompany.myapp.MainClass"/> <class> <patternset> <include name="com.mycompany.myapp.bugs.*"/> </patternset> </class> </patch> <map logfile="yguardlog.xml"/>
Complete Examples
There will be some examples given, that represent common use cases.
Example 1: Getting started with Ant and yGuard (for Ant newbies)
<?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="yguard" 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="renamelog" value="${project_name}_renamelog.xml"/> <property name="shrinklog" value="${project_name}_shrinklog.xml"/> <property name="mainclass" value="com.mycompany.myapp.Main"/> <mkdir dir="${classDir}" /> </target> <target depends="jar" name="yguard"> <taskdef name="yguard" classname="com.yworks.yguard.YGuardTask" classpath="yguard.jar"/> <!-- the following can be adjusted to your needs --> <yguard> <inoutpair in="${jar}" out="${obfjar}"/> <shrink logfile="${shrinklog}"> <keep> <class classes="protected" methods="protected" fields="protected"> <patternset> <include name="com.mycompany.publicapi.**.*"/> <exclude name="com.mycompany.publicapi.private.*"/> <include name="com.mycompany.menu.reflection.**.*"/> </patternset> </class> </keep> </shrink> <rename mainclass="${mainclass}" logfile="${renamelog}"> <property name="error-checking" value="pedantic"/> <keep> <class classes="protected" methods="protected" fields="protected"> <patternset> <include name="com.mycompany.publicapi.**.*"/> <exclude name="com.mycompany.publicapi.private.*"/> </patternset> </class> </keep> </rename> </yguard> </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="yguard"> <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 yguard
section could look like this:
<yguard> <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 --> <attribute name="Deprecated"/> <shrink logfile="shrinklog.xml"> <keep> <class classes="protected" methods="protected" fields="protected"/> </keep> </shrink> <rename mainclass="com.mycompany.myapp.Main" logfile="obflog.xml"> <keep> <class classes="protected" methods="protected" fields="protected"/> </keep> </rename> </yguard>
Example 3: A Demo Program
<yguard> <inoutpair in="demo.jar" out="demo_obf.jar"/> <shrink logfile="shrinklog.xml"> <keep> <!-- main method --> <method name="void main(String[])" class="com.mycompany.myapp.Main" /> <!-- 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> </keep> </shrink> <rename mainclass="com.mycompany.myapp.Main" logfile="renamelog.xml"> <property name="language-conformity" value="illegal"/> <property name="naming-scheme" value="mix"/> <keep> <class name="com.mycompany.myapp.data.DataObject" methods="public" fields="none"/> <class name="com.mycompany.myapp.data.InnerDataObject"/> <method name="void writeObject(java.io.ObjectOutputStream)"> <patternset refid="datapatternset" /> </method> <method name="void readObject(java.io.ObjectInputStream)"> <patternset refid="datapatternset"/> </method> <field name="serialVersionUID"> <patternset refid="datapatternset"/> </field> </keep> </rename> </yguard>
Example 4: A Program Using an External Library
<yguard> <inoutpair in="mydemo.jar" out="mydemo_obf.jar"/> <externalclasses> <pathelement location="lib/external.jar"/> <pathelement location="lib/additional/classes/"/> </externalclasses> <shrink logfile="shrinklog.xml"> <property name="error-checking" value="pedantic"/> <keep> <method name="void main(String[])" class="com.mycompany.myapp.Main" /> <class classes="public"/> </keep> </shrink> <rename mainclass="com.mycompany.myapp.Main" logfile="renamelog.xml"> <property name="error-checking" value="pedantic"/> <keep> <class classes="public"/> </keep> </rename> </yguard>
Example 5: A Program with .properties
Files and Other Resource Files
<yguard> <inoutpair in="myapp.jar" out="myapp_obf.jar"/> <shrink logfile="shrinklog.xml"> <keep> <!-- single entrypoint: main method --> <method name="void main(String[])" class="com.mycompany.myapp.Main" /> </keep> </shrink> <rename mainclass="com.mycompany.myapp.Main" logfile="renamelog.xml"> <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> </rename> </yguard>
Example 6: A Program Linked Against a Library That Needs to be Obfuscated
<yguard> <inoutpair in="myapp.jar" out="myapp_obf.jar"/> <inoutpair in="lib/thirdpartylib.jar" out="lib/thirdpartylib_obf.jar"/> <externalclasses> <pathelement location="lib/external.jar"/> </externalclasses> <!-- Keep all of the attributes for debugging, e.g. --> <attribute name="Deprecated, SourceFile, LineNumberTable, LocalVariableTable> <patternset refid="myopenapp"/> </attribute> <rename mainclass="org.myorg.myapp.Main" logfile="renamelog.xml"> <property name="error-checking" value="pedantic"/> <keep> <!-- 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> </rename> </yguard>
Example 7: Using the extends
and implements
attributes (Serializable exclusion)
<yguard> <inoutpair in="myapp.jar" out="myapp_obf.jar"/> <shrink> <keep> <!-- main method --> <method name="void main(java.lang.String[])" class="org.myorg.myapp.Main" /> <!-- serializable classes --> <class implements="java.io.Serializable" classes="private" methods="private" fields="private" /> <!-- menu items loaded per reflection --> <class extends="org.myorg.myapp.MyMenuItem" classes="friendly" methods="public" fields="public" /> </keep> </shrink> <rename mainclass="org.myorg.myapp.Main" logfile="renamelog.xml"> <keep> <method name="void readObject(java.io.ObjectInputStream)" /> <method name="void writeObject(java.io.ObjectOutputStream)" /> <field name="serialVersionUID" /> <class extends="org.myorg.myapp.MyMenuItem" classes="friendly" methods="public" fields="public" /> </keep> </rename> </yguard>
Deobfuscating Stacktraces Etc.
Console> java -jar yguard.jar mylogfile.xmlA 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.