两种免费的Java Obfuscator比较

本文对比了两种免费的Java代码混淆工具yguard和Proguard的特点和优缺点,包括混淆速度、开源情况、包名混淆支持、增量混淆等功能,并介绍了yguard的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  Java的bytecode很容易通过JAD等反编译工具搞出源代码, 目前最有效的保护方法是obfuscate类名和方法名。注意: 用obfuscate防盗版是根本不可能, 连汇编这种东西都能被破解掉, 破解java代码简直就是小菜。用obfuscate主要是为了保护源代码的知识产权, 别人无法用反编译的源代码做事情.

  本文从几个不同的方面比较了两种Free的Java Obfuscator的优缺点..

  两种Free的Java Obfuscator: yguard和Proguard.

  yguard:http://www.yworks.com/en/products_yguard_about.htm

  Proguard:http://proguard.sourceforge.net/

  下面列出两者的不同和优缺点

  1. 速度

  yguard的速度远远高于proguard, 主要区别在于proguard不管用没有用, 上来就扫描jre/lib/rt.jar和其他所有用到的library。这个回合yguard胜出

  2. 是否Opensource以及遵循协议

  yguard: ywork公司的产品,免费使用, 但不是Open source

  Proguard是GPL(这里GPL只是指Proguard本身, 对于obfuscate出来的jar没有限制), 可以从sourceforge.net下载源代码 但是作者并不希望别人参与,没有CVS,所以也无从知道作者的最新进展.

  这个回合proguard胜出

  3. 是否支持Package Name obfuscate

  yguard支持Package Name obfuscate

  Proguard不支持

  这其实是很重要的一个特性, 一个好的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,很容易猜出来是干什么的

  但是如果你obfuscate成:

  com.mycompany.a.a com.mycompany.a.b com.mycompany.a.c

  则大大增加了困难, 尤其大型software, 有几十个package的情况下, 会呈几何级数增加反编译的难度

  这个回合yguard胜出, 但是Proguard已经准备在4.0里推出这个特性

  4. 增量obfuscate

  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胜出

  5. 其它

  其他不太重要的特性:

  - Proguard可以用一篇文章中的词(比如莎士比亚的剧本)作为变量名字, 呵呵

  - Proguard可以删除所有没有用到的Class或者方法(叫做Shrink)

  - Yguard能够obfuscate资源名称, 比如Messages.properties->a.properties并且修改相应的ResourceBundle类

  - Yguard可以replace指定文本文件里的Class Name,比如修改eclipse plugin.xml里的Class名字

  我个人不太赞成这种做法

  总结

  对于小型java应用(只有一个package,一个jar文件), 两者区别很小, (但是小型的java应用值得obfuscate么?呵呵)

  目前因为没有obfuscate package name这个重要特性, 所以目前我偏向用yguard, 但是yguard没有incremental obufscate, 真伤脑筋, 现在就等Proguard4.0出来, 马上移植到Proguard4.0上去

This document explains how to use the yGuard Java obfuscation and shrinking software together with Ant. yGuard is a product of , creator of the outstanding JavaTM graph visualization framework and other fine .

Documentation for the deprecated Ant syntax of previous yGuard releases is still .

yGuard 2.1 Ant Task Documentation

Contents

Introduction

Installation

For a complete build.xml file have a look at the .

General Hints & Troubleshooting

There are a couple of things you should be aware of when obfuscating and shrinking software.
The weakest part of an application considering name obfuscation and code shrinking is code that uses reflection to dynamically load classes, invoke methods etc. Therefore, you have to be especially careful when using the yguard task on applications that rely on reflection. <!--You can find a lot of information on <a href="http://www.retrologic.com">http://www.Retrologic.com</a>. This is the site which hosts 'Retroguard'. The yGuard <i>library</i> has been derived from Retroguard. -->
The most important facts to keep in mind when using yGuard are described here briefly:

If you use the 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>

The obfuscation and shrinking process can be completely configured inside your Ant script. The yguard task and nested elements should be used according to the following DTD (please note that this is for information purposes only, i.e. you do not have to include the following lines anywhere):

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

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 yguard Element

The inoutpair Elements

At least one inoutpair element has to be specified in order to run the yguard task. This elements specifies the paths to the input and output jar files.

The externalclasses Element

The attribute Element

The shrink Element

The entrypointjar Element

The rename Element

The basic idea is, that all elements will be renamed by this task. Using the nested element, you have to specify all classes, methods, fields, and attributes that should be excluded from name obfuscation, i.e. that will not be renamed but kept in the API. There are different use cases, where you sometimes want to exclude or simply just have to exclude some elements from name obfuscation. See the section for explanation of some common use cases. If your code uses reflection, please read the section for information on this topic. Excluding elements can be achieved by using both the element and the mainclass attribute of the rename element.

Attributes

Attribute Description Required 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

Child Elements

The property Elements

property elements can be used to give hints to the name obfuscation engine. Depending on the exact version of yGuard, the task may use these hints to control the process of obfuscation.

The following properties are supported:

Name Description error-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: smallWill produce very short names, i.e. the resulting jar file will be as small as possible. bestWill 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). mixIs 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: compatibleWill produce names, that are ok for (most) decompilers, java, jar and manifest files and can be unzipped to most filesystems. legalWill produce names, that are ok for (some) decompilers, java, jar and manifest files. illegalWill 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 .

Child Elements

The property element has no child elements.

The keep Element

This element is a child of the or element. It can be used to specify elements that are excluded from the parent rename or shrink task. The excluded classes, methods and fields are defined using nested , and elements.
The elements given in the keep element are considered as code entrypoints. All code reachable from these entrypoints will be implicitly excluded from shrinking, too.

Attributes

The keep element provides a number of boolean attributes that determine whether debug information and annotations present in the input class files are to be retained in the output files. The default behavior is to remove all of this information.
Note that a more fine-grained control over which attributes to keep for which class files is possible using the element.

Attribute Description Required 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.

Common Child Elements

Child Elements only available in the task

The task allows for a special treatment of the linenumbertable and sourcefile attributes. This treatment can be specified in the following child elements:

The class Element

The class element can be used for excluding certain classes and/or their fields and methods from the renaming or shrinking process.
If no name, extends or implements attribute is given and the class element contains no nested patternset, a class element matches all class names.

The classes, methods and fields attributes tell the shrinking and renaming engines which classes, methods and fields to keep based on their visibility. The following table lists the possible values for all of these attributes and shows which elements will be excluded. A '*' denotes, that elements that have the given visibility will be excluded for the specified attribute value. A '-' denotes that the these elements will not be excluded from the process.


Child Elements

Explanation

There are three possible ways of specifying which classes will be excluded from the shrinking and obfuscation process:

One can specify a single java class using the fully qualified name in java syntax with the 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.

The last example shows how to keep 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

The field Element

The sourcefile Element

The linenumbertable Element

The linenumbertable element allows for a special treatment of the linenumbertable attribute by the .
Using nested property elements, the mapping of linenumbertable elements in obfuscated class files can be adjusted.

Child Elements

property patternset

Examples

  <linenumbertable>
    <patternset>
      <include name="com.mycompany.myapp.**"/>
    </patternset>
  </linenumbertable>

This will keep 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 attribute has to be retained 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 keep 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 element is used on the same patternset, which is referenced by its previously declared reference id, to rename the source files to "y".

The adjust Element

The map Element

Generating Patch Jars

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, put the file into your project's root directory and edit the file to suit your needs.

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

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 excluded from the shrinking and renaming tasks. Package friendly and private classes, methods and fields will be shrinked or obfuscated whenever possible.
This example also displays the use of the "attribute" element. In this case it prevents the yguard task from removing the "Deprecated" flag from the entities in the .class files.

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>

This example demonstrates the common use case of a demo program. The keep sections of both the shrink and rename elements contain code entities that will often have to be excluded from the shrinking and renaming process. These are the main code entrypoints (the main method), classes that are loaded per reflection and fields and methods needed for serialization. Note how the same patternsets can be reused using the id and refid attributes.

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>

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 renaming and shrinking of these items. As a result, the shrinked and obfuscated jar file 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

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

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

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>

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.

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>

This example demonstrates the usage of the new and attributes of the element. All Serializable classes are excluded from shrinking by using the attribute of the element. Additionally, all classes that extend the base class for menu items, org.myorg.myapp.MyMenuItem, are defined as entrypoints for the shrinking engine using the attribute of the element. The readObject and writeObject methods and the field serialVersionUID needed for serialization are excluded from name obfuscation as well.

Deobfuscating Stacktraces Etc.

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 e.g.
In order to run the yGuard deobfuscation tool do the following:

      Console> java -jar yguard.jar mylogfile.xml
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.

Problems and Bug Reports

<!-- EOF -->
===================================================================================
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值