ProGuard的实例

来源:互联网 发布:echarts源码分析 编辑:程序博客网 时间:2024/06/07 21:07

文章原文:https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html#manual/examples.html


你可以在ProGuard的分布的例子目录中找到一些配置文件的示例.

一个典型的应用

要收缩,优化和混淆一个简单的JAVA应用程序,你通常会创建一个配置文件就像myconfig.pro一样,可以使用

bin/proguard @myconfig.pro

配置文件指定输入,输出和程序的入口:

-injars       myapplication.jar-outjars      myapplication_out.jar-libraryjars  <java.home>/lib/rt.jar-printmapping myapplication.map-keep public class mypackage.MyMain {    public static void main(java.lang.String[]);}

注意使用 <java.home> 系统属性. ProGuard 在解析问件时会自动替换它.

该 -keep 选项指定要保存应用程序的入口点. 在这种情况下,访问修饰符pubic 和static并不真正需要, 因为我们知道先验该指定类和方法具有适当的访问标志.它仅仅是看起来熟悉这种方法.

需要注意的是所有类型的名称被完全指定: mypackage.MyMain and java.lang.String[].

我们写了一个混淆的映射文件-printmapping,对以后需要混淆的堆栈或扩展增量混淆.

我们可以通过其他的选项进一步完善结果:

-optimizationpasses 3-overloadaggressively-repackageclasses ''-allowaccessmodification
这些选项不是必须的,他们通过执行多达3次的优化过程并积极混淆类成员和报名(package names),从输出的jar包里去掉一些额外的字节

一般情况下,你可能需要处理: 本地方法(native methods), 回调方法(callback methods), 枚举(enumerations), 序列化(serializable classes), bean类(bean classes),注释(annotations), and 资源文件(resource files).


一个典型的小程序

这些选项收缩,优化,混淆的小程序 mypackage.MyApplet:

-injars      in.jar-outjars     out.jar-libraryjars <java.home>/lib/rt.jar-keep public class mypackage.MyApplet

典型的小应用程序的方法将被自动保留,因为mypackage.MyApplet是库rt.jar里面Applet类的延伸.

如果使用,你应该添加处理选项: native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files.


一个典型的MIDlet

这些选项收缩,优化,混淆和预校验 the midlet mypackage.MyMIDlet:

-injars      in.jar-outjars     out.jar-libraryjars /usr/local/java/wtk2.5.2/lib/midpapi20.jar-libraryjars /usr/local/java/wtk2.5.2/lib/cldcapi11.jar-overloadaggressively-repackageclasses ''-allowaccessmodification-microedition-keep public class mypackage.MyMIDlet

注意我们现在的目标是 Java Micro Edition run-time environment of midpapi20.jar and cldcapi11.jar,不是 Java 标准的运行环境 rt.jar. 你可以挑选合适的JME环境通过挑选合适包。

典型的MIDlet的方法将会被自动保留,因为mypackage.MyMIDlet是库midpapi20.jar里面MIDlet类的延伸。

该 -microedition 选项确保了JME对类文件进行预校验, producing compact StackMap attributes.不再需要运行一个外部预校验。

如果你在一个不区分大小的平台上使用外部预校验工具时要小心.因为如果该工具解压你的处理jar的话,那么你就应该使用 ProGuard的 -dontusemixedcaseclassnames 选项.

如果适用,你应该添加处理本地方方法和资源文件的选项。

注意,你仍然需要适应相应的jad文件中MIDlet的jar文件的大小;ProGuard不会为你做这些。

一个典型的JAVA CARD 小应用程序。

这些选项收缩,优化和混淆 Java Card 小程序的 mypackage.MyApplet:
-injars      in.jar-outjars     out.jar-libraryjars /usr/local/java/javacard2.2.2/lib/api.jar-dontwarn    java.lang.Class-overloadaggressively-repackageclasses ''-allowaccessmodification-keep public class mypackage.MyApplet

这个的配置跟MIDlet非常相似, 所不同的是,现在针对JAVA CARD的运行环境. 这个环境没有java.lang.Class,所以我们告诉ProGuard不需要担心它。

一个典型的xlet

这些选项收缩,优化和混淆 xlet 的 mypackage.MyXlet:
-injars      in.jar-outjars     out.jar-libraryjars /usr/local/java/jtv1.1/javatv.jar-libraryjars /usr/local/java/cdc1.1/lib/cdc.jar-libraryjars /usr/local/java/cdc1.1/lib/btclasses.zip-overloadaggressively-repackageclasses ''-allowaccessmodification-keep public class mypackage.MyXlet

配置跟MIDlet非常相似,所不同的是,现在针对Java TV API的CDC的运行环境。


一个android activity的例子

这些选项收缩,优化和混淆android 活动的mypackage.MyActivity:
-injars      bin/classes-outjars     bin/classes-processed.jar-libraryjars /usr/local/java/android-sdk/platforms/android-9/android.jar-dontpreverify-repackageclasses ''-allowaccessmodification-optimizations !code/simplification/arithmetic-keep public class mypackage.MyActivity

我们针对androidd的运行时间,并保持activity作为切入点。预校验对DEX编译器和Dalvik虚拟机是无关紧要的,所以我们可以用 -dontpreverify 选项将其关闭.

该 -optimizations 选项禁用了一些Dalvik1.0和1.5无法处理的算法. 注意,Delvik虚拟机也不能处理 aggressive overloading (静态属性).

如果使用, 你应该添加 native methods, callback methods, enumerations, annotations, and resource files的处理。

一个完整的android 的应用

这些选项收缩,优化和混淆了所有的 activities, services, broadcast receivers, and content providers 编译的类和外部的库:
-injars      bin/classes-injars      libs-outjars     bin/classes-processed.jar-libraryjars /usr/local/java/android-sdk/platforms/android-9/android.jar-dontpreverify-repackageclasses ''-allowaccessmodification-optimizations !code/simplification/arithmetic-keepattributes *Annotation*-keep public class * extends android.app.Activity-keep public class * extends android.app.Application-keep public class * extends android.app.Service-keep public class * extends android.content.BroadcastReceiver-keep public class * extends android.content.ContentProvider-keep public class * extends android.view.View {    public <init>(android.content.Context);    public <init>(android.content.Context, android.util.AttributeSet);    public <init>(android.content.Context, android.util.AttributeSet, int);    public void set*(...);}-keepclasseswithmembers class * {    public <init>(android.content.Context, android.util.AttributeSet);}-keepclasseswithmembers class * {    public <init>(android.content.Context, android.util.AttributeSet, int);}-keepclassmembers class * implements android.os.Parcelable {    static android.os.Parcelable$Creator CREATOR;}-keepclassmembers class **.R$* {    public static <fields>;}

最重要的是,我们保持了所有可以可以通过应用程序的androidManifest.xml文件中应用的基础类。如果你的清单文件中包含其他类和方法,你可能需要指定这些为好。

我们保持注释,因为它们可能被自定义的RemoteViews使用。

我们保留所有自定义View和其他有典型构造器的类,因为它们可能会在xml布局文件中引用。

我们同样需要保留实现Parcelable接口里有static的,因为他们通过introspection(反射?)被访问。

最后,我们保持引用内部类生成的R类的static属性。以防你的代码通过反射访问这些属性。注意,编译器已经内联了这些基本属性,所以ProGuard可以删除所有这样的类(因为类没有被引用,所以不需要)。

如果你使用了Google APIs,你最好还是指定那些,例如:

-libraryjars /usr/local/android-sdk/add-ons/google_apis-7_r01/libs/maps.jar

如果你使用了google的可选许可验证库(optional License Verification Library),你可以混淆它的和你的代码。你必须保持他的ILicensingService接口的正常工作。

-keep public interface com.android.vending.licensing.ILicensingService

如果你使用的是android的兼容性库,你应该添加以下行,让ProGuard知道库引用的一些类不是在所有版本的API上都是可用的:

-dontwarn android.support.**

如果适用,你应用添加对native methods, callback methods, enumerations, and resource files的处理。 你可能还需要添加选项用于产生有用的堆栈踪迹。你可以找到一个完整的示例在example/android.pro的GroGuard里。

 Android SDK (2.3及以上版本)的构建过程在默认情况下已经集成了ProGuard。你只需要启动它(发布版本)加入proguard.config=proguard.cfg到文件build.properties。如有问题,你可能要检查自动生成的文件proguard.cfg包含上面所讨论的设置。生成的Ant构建文件已经为你设置了输入和输出文件。

想了解更多信息,可以查看android sdk的官方开发指南。

一个典型的库

这些选项收缩,优化和混淆了整个库,保持所有public和proteced的类和类成员,本地方法名和序列化代码。库的处理版本仍然可以使用,例如,基于它的公开API开发代码。
-injars       in.jar-outjars      out.jar-libraryjars  <java.home>/lib/rt.jar-printmapping out.map-keepparameternames-renamesourcefileattribute SourceFile-keepattributes Exceptions,InnerClasses,Signature,Deprecated,                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod-keep public class * {    public protected *;}-keepclassmembernames class * {    java.lang.Class class$(java.lang.String);    java.lang.Class class$(java.lang.String, boolean);}-keepclasseswithmembernames class * {    native <methods>;}-keepclassmembers enum * {    public static **[] values();    public static ** valueOf(java.lang.String);}-keepclassmembers class * implements java.io.Serializable {    static final long serialVersionUID;    private static final java.io.ObjectStreamField[] serialPersistentFields;    private void writeObject(java.io.ObjectOutputStream);    private void readObject(java.io.ObjectInputStream);    java.lang.Object writeReplace();    java.lang.Object readResolve();}

这种配置应该保留我们曾经想要访问的库的一切。只有当

该 -keepclassmembernames选项对类$方法不是严格必须的。这些方法是在JDK1.2及以上由javac编译器和jikes的编译器分别插入来实现.class的构造器. ProGuard 会自动检测他们,处理他们,即使他们的名字已经被混淆了。但是,其他的混淆器会根据他们的原始名字。这可能有助于保护它们。在这种情况下,这些其他的混淆器会对使用过的库进一步混淆。

“异常”属性必须被保留下来,所以编译器知道哪些异常的方法可能会抛出。

该“内部类”属性(更准确的说,它源名称的一部分)也必须保留下来,否则,对于任何可以从库外引用的内部类。javac编译器将无法找到他们。

当用jdk5.0或更高版本完成时,“signature”属性也要求能够访问一般的类型。

该 -keepparameternames 选项保持参数名称中"LocalVariableTable" and "LocalVariableTypeTable" 公开库方法的属性。有些IDE提供这些名字给正在使用这个库的开发者。

最后,我们要保留“Depreated”的属性,让属性产生有用的堆栈踪迹。

我们还得增加一些选项来处理 native methods, enumerations, serializable classes, and annotations 类,这些都有例子来讨论。


在输入jar里面所有可能的应用

这些选项在in.jar里收缩,优化和混淆所有的公开应用:
-injars      in.jar-outjars     out.jar-libraryjars <java.home>/lib/rt.jar-printseeds-keepclasseswithmembers public class * {    public static void main(java.lang.String[]);}

注意使用 -keepclasseswithmembers. 我们不希望保留所有的类, 我们只是保留所有有main方法的类和这些方法(main).

该选项 -printseeds选项打印出哪些类恰好被保留,因此,我们肯定知道我们得到了我们想要的

如果适用, 你应该处理 native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files 这些类。

在输入jar里所有可能的applet

这些选项在in.jar里收缩,优化和混淆所有公开的applet:
-injars      in.jar-outjars     out.jar-libraryjars <java.home>/lib/rt.jar-printseeds-keep public class * extends java.applet.Applet

我们只是保留所有继承Applet的类

同样, -printseeds 打印出哪些恰好被保留的applet。

如果适用, 你应该处理 native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files 这些类。

在输入jar里所有可能的midlet

这些选项在in.jar里收缩,优化和混淆所有公开的midlet:
-injars      in.jar-outjars     out.jar-libraryjars /usr/local/java/wtk2.5.2/lib/midpapi20.jar-libraryjars /usr/local/java/wtk2.5.2/lib/cldcapi11.jar-overloadaggressively-repackageclasses ''-allowaccessmodification-microedition-printseeds-keep public class * extends javax.microedition.midlet.MIDlet

我们只是保留所有继承MIDlet的类

该-microedition选项确保类文件是Java Micro Edition要进行预校验的,产生 简洁的StackMap属性。它不再需要运行一个外部的预校验。

如果你在一个不区分大小写的文件系统上进行外部平台上预校验时,要小心, 像 Windows. 因为这个工具解压你的处理jar,你应该使用 ProGuard's -dontusemixedcaseclassnames 选项.

该 -printseeds 打印那些恰好被保留的midlets.

如果适用, 你应该处理 native methods and resource files 这些类选项.

请注意,你仍然必须适应相应的jad文件中的MIDlet的jar文件大小;ProGuard不会为你做这些。


在输入jars里所有可能的Java Card applets

这些选项在in.jar里收缩,优化和混淆所有公开的midlet Java Card applets:
-injars      in.jar-outjars     out.jar-libraryjars /usr/local/java/javacard2.2.2/lib/api.jar-dontwarn    java.lang.Class-overloadaggressively-repackageclasses ''-allowaccessmodification-printseeds-keep public class * implements javacard.framework.Applet

我们只是保留所有继承Applet的类

该 -printseeds 打印那些恰好被保留的Applet.

在输入jar里所有可能的xlets

这些选项在in.jar里收缩,优化和混淆所有公开的 xlets:
-injars      in.jar-outjars     out.jar-libraryjars /usr/local/java/jtv1.1/javatv.jar-libraryjars /usr/local/java/cdc1.1/lib/cdc.jar-libraryjars /usr/local/java/cdc1.1/lib/btclasses.zip-overloadaggressively-repackageclasses ''-allowaccessmodification-printseeds-keep public class * implements javax.tv.xlet.Xlet

我们只是保留所有继承Xlet的类

该 -printseeds 打印那些恰好被保留的Xlet.

在输入jar里所有可能的servlet

这些选项在in.jar里收缩,优化和混淆所有公开的servlets :
-injars      in.jar-outjars     out.jar-libraryjars <java.home>/lib/rt.jar-libraryjars /usr/local/java/servlet/servlet.jar-printseeds-keep public class * implements javax.servlet.Servlet

就像保留所有xlet一样保留所有servlet。该servletAPI不是标准的运行时JAR的一部分,所以我们将其指定为库。不要忘记使用合适的路径名。

我们保留所有实现Servlet借口的类。我们适应implements这个关键词,因为它看起来在这方面很相似,但它相当于继承,尽可能与 ProGuard的有关

该 -printseeds 打印那些恰好被保留的servlet.

如果适用, 你应该处理  native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files 类选项。

Scala applications with the Scala runtime

这些选项在in.jar里收缩,优化和混淆所有公开的Scala 应用 :
-injars      in.jar-injars      /usr/local/java/scala-2.9.1/lib/scala-library.jar-outjars     out.jar-libraryjars <java.home>/lib/rt.jar-dontwarn scala.**-keepclasseswithmembers public class * {    public static void main(java.lang.String[]);}-keep class * implements org.xml.sax.EntityResolver-keepclassmembers class * {    ** MODULE$;}-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinPool {    long eventCount;    int  workerCounts;    int  runControl;    scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode syncStack;    scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode spareStack;}-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinWorkerThread {    int base;    int sp;    int runState;}-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinTask {    int status;}-keepclassmembernames class scala.concurrent.forkjoin.LinkedTransferQueue {    scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference head;    scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference tail;    scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference cleanMe;}

作为处理的应用,配置是基本相同的。因为Scala被编译成普通的java字节码。但是,这个例子也可以处理Scala运行库。经过处理的jar比原始的要小,比原始的代码要小(用于Scala代码示例)。

该 -dontwarn 选项告诉ProGuard在Scala运行时的一些假象(至少Scala2.9.1)。请注意,该选项应始终小心使用。( tells ProGuard not to complain about some artefacts in the Scala runtime, the way it is compiled by the scalac compiler (at least in Scala 2.9.1 and older). Note that this option should always be used with care.)

附加 -keep选项确保一些反射访问的类和属性不会被删除和重命名。

如果适用, 你应该处理 native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files 这些类的选项。

处理本地方法(processing native methods)

如果你的应用, applet, servlet, library, 等, 包含本地方法的,你想要保留他们的名字和他们的类名,以便你仍然可以链接本地酷。下面的选项将会使用:
-keepclasseswithmembernames class * {    native <methods>;}

注意使用 -keepclasseswithmembernames。我们不是想保留所有的类和所有的本地方法;我们只是想保留有关的在混淆里。

ProGuard不会看你的本地代码,所以不会自动保存本地代码调用的类或者类成员。这些入口,你必须明确指出。回调方法在下面作为一个典型的例子讨论。

处理回调方法(processing callback methods)

如果你的应用程序,applet,servlet,library等,包含有外部代码(native代码,scripts等)的回调方法,你想要保留它们或许还有它们的类。他们只是你代码的入口就像应用程序的main方法。如果你不想保留其他的 -keep选项。那么像下面选项保留回调类和方法:
-keep class mypackage.MyCallbackClass {    void myCallbackMethod(java.lang.String);}

这将会在那些被删除或者重命名里保留指定的类和方法。

处理枚举类

如果,你的应用程序,applet,serlet,library等,包含有枚举类,你必须保留一些特殊的方法。枚举在Java5中引入的。枚举类在java中有特殊的结构。值得注意的是,这些类包含一些运行时通过反射访问的静态方法(是不是很重要?反射是新一代的自修改代码)。你必须名称指定这些,以确保他们不会被移除或混淆:
-keepclassmembers enum * {    public static **[] values();    public static ** valueOf(java.lang.String);}

处理序列化(serializable)的类

更复杂的应用,applet,servlets,libraries等,可能包含序列化的类。这取决于它们的使用方式,它们可能需要特别注意

  • 通常序列化是简单的传送数据,不会长期存储。 类被收缩,混淆后应该继续起作用听过一下附加选项:
    -keepclassmembers class * implements java.io.Serializable {    private static final java.io.ObjectStreamField[] serialPersistentFields;    private void writeObject(java.io.ObjectOutputStream);    private void readObject(java.io.ObjectInputStream);    java.lang.Object writeReplace();    java.lang.Object readResolve();}

    该 -keepclassmembers 选项确保了任何的序列化方法被保留。通过使用这个选项而不是基本的-keep选项,我们不会强迫保留所有的序列化类,只是保留了那些实际使用的。

  • 有时,序列化的数据存储,然后在一个新的版本的序列化的类读取回来。小心处理这些类保持兼容未处理的版本和以后的版本。在这种情况下,相关的类极有可能有serialVersionUID的属性,下面的选项足以保证以后的兼容性。
    -keepnames class * implements java.io.Serializable-keepclassmembers class * implements java.io.Serializable {    static final long serialVersionUID;    private static final java.io.ObjectStreamField[] serialPersistentFields;    !static !transient <fields>;    private void writeObject(java.io.ObjectOutputStream);    private void readObject(java.io.ObjectInputStream);    java.lang.Object writeReplace();    java.lang.Object readResolve();}

    serialVersionUID and serialPersistentFields 线会确保这些属性被保留,如果它们存在。该<属性>线保留所有的非static,非transieant属性用它们原来的名字。序列化和反序列化通过反射来处理后就会发现一致的名称。

  • 偶尔,序列化数据必须保持兼容,但是 所涉及的类缺少serialVersionUID属性。我认为原来的代码将难以维持,由于序列化的UID是从序列化类的功能列表来计算的。轻微的改变类就会改变改变序列化UID的计算。在Sun的Java对象序列化的流唯一标识符(Stream Unique Identifiers)的部分,功能列表是被指定的。下面指令至少能保证部分与原类的兼容性:
    -keepnames class * implements java.io.Serializable-keepclassmembers class * implements java.io.Serializable {    static final long serialVersionUID;    private static final java.io.ObjectStreamField[] serialPersistentFields;    !static !transient <fields>;    !private <fields>;    !private <methods>;    private void writeObject(java.io.ObjectOutputStream);    private void readObject(java.io.ObjectInputStream);    java.lang.Object writeReplace();    java.lang.Object readResolve();}

    新的选项强制保留所有的元素参与运算。此外, 用户必须手动指定所有序列化类的接口 (就像 "-keep interface MyInterface"),因为计算UID时候,这些名字也可以使用。次优的替代方法是简单的保持所有的借口用"-keep interface *".

注意上述方案可能保存更多的类和类成员,有些是不必要。例如大量的类实现了序列化的借口,但只有一小部分实际上永远被序列化。了解你的应用,并调整配置往往会产生更紧凑的结果。

处理bean类(Processing bean classes)

如果你的应用程序,applet,servlet,library等广泛使用反射来找你的bean类或getter,setter方法,那么配置可能变得非常痛苦。 确保bean类的名字或者setter,getter的名字不会改变就会非常重要,例如:
-keep public class mypackage.MyBean {    public void setMyProperty(int);    public int getMyProperty();}-keep public class mypackage.MyBeanEditor

如果有太多的元素需要明确列出,类名字和方法通配符可能就会非常有用。这个例子包含了mybeans包里所有类里所有可能的setters,getters:

-keep class mybeans.** {    void set*(***);    void set*(int, ***);    boolean is*();     boolean is*(int);    *** get*();    *** get*(int);}

该'***'通配符匹配任何类型(原始的或非原始的,数组或非数组)。带有'int'参数的方法匹配列出的属性。

处理注解(Processing annotations)

如果你的应用程序,applet,servlet,library等使用了注解,你可能想在处理输出的时候保留它们。 注解是对代码的执行没有直接影响的属性的表示。不过,它们的值可以通过反射来取回,而且允许开发人员相应的调整执行行为。默认情况下,ProGuard对待注解属性是随意的,它们会在混淆的时候被删除,如果需要,你必须明确指出,就像这样:
-keepattributes *Annotation*

为了简便起见,我们指定一个通配符属性名称,它将匹配RuntimeVisibleAnnotationsRuntimeInvisibleAnnotationsRuntimeVisibleParameterAnnotations,RuntimeInvisibleParameterAnnotations, and AnnotationDefault。根据处理的代码的作用,可以改进该选项,例如不保留运行时不可见的注释(它只在编译的时候使用)。

一些代码可以进一步利用发射弄清除匿名内部类封装的方法。在这种情况下,相应的属性必须被保留:

-keepattributes EnclosingMethod

处理数据库驱动程序(Processing database drivers)

数据库驱动程序实现了驱动程序借口。由于它们经常是动态创建,你可能想保留那些正在处理作为入口的实现类。

-keep class * implements java.sql.Driver

该选项还摆脱了ProGuard的使出有关(java.sql.Driver)的Class.forName的构造器,如果你在代码里实例化驱动程序(而不是必须实现自己的任何驱动程序)。

处理componentUI类(Processing componentUI calsses)

Swing UI看起来和感觉都是通过componentUI类来实现的。出于某些原因,在createUI里都包含一个静态方法,Swing API会使用反射来调用它。 你应该始终保留这个方法作为入口,就像这样:
-keep class * extends javax.swing.plaf.ComponentUI {    public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent);}

这个方法也保留自己的类名。

处理RMI代码(Processing RMI code)

据了解,处理RMI最简单的方法是处理ProGuard的代码,然后再调用RMIC工具。如果这些行不通,你可能需要尝试下面方式:
-keepattributes Exceptions-keep interface * extends java.rmi.Remote {    <methods>;}-keep class * implements java.rmi.Remote {    <init>(java.rmi.activation.ActivationID, java.rmi.MarshalledObject);}

第一个-keep选项让你保留所有的远程接口和它们的方法。第二个保留所有实现类中有RMI特殊构造器的,如果有的话。

第二个异常必须被保留,因为RMI处理代码会通过反射来检查方法签名是否兼容。

处理资源文件(Processing resource files)

如果你的应用程序,applet,servlet,library等,包含有资源文件,当应用程序被混淆时,它需要适配它们的名字以及它们的内容。下面的选项可以自动实现这一目标:
-adaptresourcefilenames    **.properties,**.gif,**.jpg-adaptresourcefilecontents **.properties,META-INF/MANIFEST.MF

在这种情况下,-adaptresourcefilenames选项重命名属性文件和图片文件在处理输出的基础上(如果有的话)。该-adaptresourcefilecontents选项会在属性文件和清单文件中找这些名字,并通过混淆的名称来替换这些名称(如果有的话)。你可能回想适配过滤器,来满足你的应用。

处理清单文件(Processing manifest files)

如上一节的说明,清单文件可以被看作普通的资源文件。ProGuard可以在文件里适配混淆类的名字,但不会做任何其他的改动。如果你想改变其他的,你需要使用外部工具。例如,如果清单文件包含签名信息,你需要在处理后再次给jar包签名。

如果你打算合并几个输入jars到一个输出jar里,你需要选择一个,通常通过过滤器指定:

-injars  in1.jar-injars  in2.jar(!META-INF/MANIFEST.MF)-injars  in3.jar(!META-INF/MANIFEST.MF)-outjars out.jar

该过滤器将会复制第一个jar里的清单文件并忽略第二个和第三个输入jar里的清单文件。需要中的的是ProGuard将会保留jar里文件的顺序不做改变;清单文件不一定放到第一个。

产生有用的混淆的堆栈踪迹(Producing useful obfuscated stack traces)

这些选项让混淆应用或库产生仍然可以被破译的堆栈踪迹
-printmapping out.map-renamesourcefileattribute SourceFile-keepattributes SourceFile,LineNumberTable

我们保留所有的资源文件属性,但是我们可以通过字符串“SourceFile”替换它们的值。我们可以使用任何字符串。这个字符串已经存在于所有的类文件里,因此它不占用任何额外的空间。如果你正在使用J++,你想要保留“SourceDir”的属性。

我们还需要保留所有方法的表的行号。

当这两个属性都存在,在Java运行时将打印出异常的堆栈踪迹时将会有行号的信息。

该信息仅在我们映射混淆名称返回到原始名称的时候有用,因此我们需要保存映射文件out.map。该信息可用于通过恢复工具来恢复原始堆栈踪迹。

混淆包名(Obfuscating package names)

包名可以用各种方式混淆,来使其混淆和简洁。例如,考虑下面的类:
mycompany.myapplication.MyMainmycompany.myapplication.Foomycompany.myapplication.Barmycompany.myapplication.extra.FirstExtramycompany.myapplication.extra.SecondExtramycompany.util.FirstUtilmycompany.util.SecondUtil

让我们假设类名mycompany.myapplication.MyMain是被配置保留的主应用程序类。其他所有的类名都可以被混淆。

默认情况下,包里包含不能重命名的类的话,包名也不能重命名,这样包名就被保留了。混淆后的类名称就像这样:

mycompany.myapplication.MyMainmycompany.myapplication.amycompany.myapplication.bmycompany.myapplication.a.amycompany.myapplication.a.bmycompany.a.amycompany.a.b

-flattenpackagehierarchy 选项进一步混淆包名,通过扁平化混淆包的包层次:

-flattenpackagehierarchy 'myobfuscated'

混淆后的类名如下所示:

mycompany.myapplication.MyMainmycompany.myapplication.amycompany.myapplication.bmyobfuscated.a.amyobfuscated.a.bmyobfuscated.b.amyobfuscated.b.b

另外,-repackageclasses选项混淆整个包,由混淆的类组成的一个单一的包。

-repackageclasses 'myobfuscated'
这时,混淆后的类名如下:
mycompany.myapplication.MyMainmycompany.myapplication.amycompany.myapplication.bmyobfuscated.amyobfuscated.bmyobfuscated.cmyobfuscated.d

另外指定的 -allowaccessmodification 选项允许类和类成员的访问权限被放大,给重新打包混淆类提供机会:

-repackageclasses 'myobfuscated'-allowaccessmodification
这时,混淆后的类名如下:
mycompany.myapplication.MyMainmyobfuscated.amyobfuscated.bmyobfuscated.cmyobfuscated.dmyobfuscated.emyobfuscated.f

指定的目标包永远是根目录的包。例如:

-repackageclasses ''-allowaccessmodification
混淆类名后,最短的名字:
mycompany.myapplication.MyMainabcdef

注意,不是所有级别的包名称的混淆都是代码可以接受的。值得注意的是,你可能不的不考虑将你的应用程序包含不得不配置的资源文件。

输出存档重组(Restructuring the output archives)

在一个简单的应用程序里,所有的输出类和资源文件都合并成一个单一的jar。例如
-injars  classes-injars  in1.jar-injars  in2.jar-injars  in3.jar-outjars out.jar

这个配置把一个类文件夹和3个jar包合并到一个单一的输出jar里out.jar。

如果你想保留输入Jar的结构(或 wars, ears, zips, or directories),你可以指定一个输出的目录 (或 a war, an ear, or a zip). 例如:

-injars  in1.jar-injars  in2.jar-injars  in3.jar-outjars out

输入jar就会在目录中重建出来,用原来的名字。

你也可以将其归档到更高级别的档案。例如

-injars  in1.jar-injars  in2.jar-injars  in3.jar-outjars out.war

其他的方法,你可以配合更高层次档案成简单的档案。

-injars  in.war-outjars out.jar

该配置将所有jars里面的in.war处理内容(加上in.war任何其他内容)放到out.jar里。

如果你想拼合输入的jar(和/或 wars, ears, zips, or directories)输出的jars (和/或 wars, ears, zips, or directories), 你可以组合-injars和-outjars选项。例如:

-injars base_in1.jar-injars base_in2.jar-injars base_in3.jar-outjars base_out.jar-injars  extra_in.jar-outjars extra_out.jar

该配置将所有的base_in的*.jar处理放到base_out.jar里,把extra_in.jar处理结果放到extra_out.jar里。需要注意的是,额外的空格只是为了显示更清晰。

这个分组,归档和扁平化和可以任意组合。 ProGuard 总是试图用一个合理的方式打包输出文档,尽量按要求重建输入项。

过滤输入和输出(Filtering the input and the output)

如果你想更好的控制,你可以添加过滤器,控制输入和输出,过滤输出的zips,ears,wars,jars,和/或普通文件。例如,你想从输入jar忽略某些文件:
-injars  in.jar(!images/**)-outjars out.jar

这个配置移除了图片目录和其子目录的所有文件。

这种过滤器可以方便的避免有关输出时候的重复文件。例如,只保留第一个输入jar里面的清单文件:

-injars  in1.jar-injars  in2.jar(!META-INF/MANIFEST.MF)-injars  in3.jar(!META-INF/MANIFEST.MF)-outjars out.jar

另一个有用的应用程序是通过ProGuard忽略大量不相关的运行时库jar的类来加速处理。

-libraryjars <java.home>/lib/rt.jar(java/**,javax/**)

过滤器使ProGuard过滤com.sun.**类,事实上,这不影响普通应用的处理。

另外,也可以在过滤jar(wars,ears,zips)本身,它们的名字,例如:

-injars  in(**/acme_*.jar;)-outjars out.jar

注意过滤器的分号,在它前面的过滤器适用于jar包的名i在。在这种情况下,只有 acme_*.jar包是从它的目录和子目录读的。 过滤器对war的名字,ear的名字和zip的名字可以添加额外的前缀。所有类型的过滤器可以组合。它们是有交互的。

另一方面,你还可以过滤输出,以控制哪些内容去哪里。例如:

-injars  in.jar-outjars code_out.jar(**.class)-outjars resources_out.jar

该配置分割处理输出,把**.class文件发送到code_out.jar里,把其他的所有文件发送到resources_out.jar里。

再次声明,过滤器是可以任意组合的,尤其是当输入和输出结合的时候。

一次处理多个应用程序(Processing multiple application at once)

你可以一次处理多个相关或无关的应用程序(或applets,midlets,..),为了节省时间和精力。 ProGuard的输入和输出处理提供了各种方法来保留输出更好的结构化。

最简单的方法是指定你的输入Jar(和/或wars,ears,zips,和directories)和一个输出目录。ProGuard将会在这个目录里用原来的jar名字重建输入jar。

-injars  application1.jar-injars  application2.jar-injars  application3.jar-outjars processed_applications

处理后,该目录processed_application将包含一个处理的应用程序的jar的版本,具有它的名字。

增量混淆(Incremental obfuscation)

在已经处理的应用程序,例如GroGuard本身,你仍然可以逐步添加其他部分的代码依赖于它,例如: ProGuard GUI:
-injars       proguardgui.jar-outjars      proguardgui_out.jar-injars       proguard.jar-outjars      proguard_out.jar-libraryjars  <java.home>/lib/rt.jar-applymapping proguard.map-keep public class proguard.gui.ProGuardGUI {    public static void main(java.lang.String[]);}

我们正在阅读的这2个未处理的jar作为输入。它们的处理内容将转到相应的输出jar。该-applymapping选项,确保代码的ProGuard的部分得到先前产生混淆的映射。最终的应用程序将包括混淆的ProGuard的jar和附加混淆的GUI的jar。

在这个例子中所添加的代码是明确的;它不会影响原有的代码。该proguard_out.jar与初始化步骤产生的是相同的。如果你预想增加更多复杂的代码来扩展你的代码,你应该在原来处理步骤指定选项--useuniqueclassmembernames, -dontshrink, 和-dontoptimize。这些选项确保混淆jar将始终保持可以,没有改变。然后,你可以知道基础的jar作为库jar:

-injars       proguardgui.jar-outjars      proguardgui_out.jar-libraryjars  proguard.jar-libraryjars  <java.home>/lib/rt.jar-applymapping proguard.map-keep public class proguard.gui.ProGuardGUI {    public static void main(java.lang.String[]);}

Java Micro Edition的预校验类文件(Preverifying class files for Java Micro Edition)

即使,你对收缩,优化,混淆你的midlets不感兴趣,如MIDlet所示的例子, 你仍然可以使用Java Micro Edition的ProGuard预校验的类文件。ProGuard产生比传统的外部预校验器稍微更简洁的结果。

-injars      in.jar-outjars     out.jar-libraryjars /usr/local/java/wtk2.5.2/lib/midpapi20.jar-libraryjars /usr/local/java/wtk2.5.2/lib/cldcapi11.jar-dontshrink-dontoptimize-dontobfuscate-microedition

我们不处理输入,只是确保目标的类文件被Java Micro Edition的-microedition选项进行预校验。需要注意的是,我们不需要任何-keep选项来指定入;所有类文件只是预校验。

Java6升级类文件(Upgrading class files to Java6)

下面的选项是Java6升级类文件,通过更新它们的内部版本号和预校验它们。然后,java6可以更有效的对类文件进行加载。
-injars      in.jar-outjars     out.jar-libraryjars <java.home>/lib/rt.jar-dontshrink-dontoptimize-dontobfuscate-target 1.6

我们不处理输入,只需要通过-target选项重定向类文件。它们将被java6自动预校验。需要注意的是,我们不需要任何-keep选项来指定入口;所有的类文件只是更新和预校验。

找到死的代码(Finding dead code) 

这些选项会列出在mypackage.MyuApplication应用里未使用的类,属性,和方法。
-injars      in.jar-libraryjars <java.home>/lib/rt.jar-dontoptimize-dontobfuscate-dontpreverify-printusage-keep public class mypackage.MyApplication {    public static void main(java.lang.String[]);}

我们不指定输出的jar,只是打印出一些结果。我们通过跳过其他处理步骤节省了一些时间。

Java编译器内联原始的常量和字符串常量(static final 属性的)。因此ProGuard通过分析列出那些类文件没有使用的属性,即使它们在源文件中使用。我们可以添加一个-keepclassmembers选项,保留这些字段,以免它们被列出:

-keepclassmembers class * {    static final %                *;    static final java.lang.String *;}

打印出类文件的内部结构(Printing out the internal structure of class files)

这些选项打印出输入jar里所有类文件的内部结构:

-injars in.jar-dontshrink-dontoptimize-dontobfuscate-dontpreverify-dump

注意我们不需要指定java运行的jar,因为我们不处理输入jar。

使用注释来配置ProGuard(Using annotations to configure ProGuard)

传统ProGuard配置允许保持代码和收缩,优化,混淆的配置的完全分离。 然而,也可以定义特定的注解,注释来配置处理代码。

你可以在目录中找到例子/注解/lib中ProGuard的分布一组预定义的注解。注释类在annotations.jar定义。 在相应的ProGuard的配置(或meta-configuration,如果你喜欢)中指定annotations.pro。使用这些文件,你可以注释你的代码。例如,java源文件Application.java可以注释如下:

@KeepApplicationpublic class Application {  ....}

应用程序的ProGuard的配置文件可以借助于这些注解来简化:

-injars      in.jar-outjars     out.jar-libraryjars <java.home>/lib/rt.jar-include lib/annotations.pro

注释实际上是在替换应用程序依赖的 -keep选项。你可能仍然希望那个添加传统的-keep选项处理native methods, enumerations, serializable classes, and annotations。

目录的例子/注解中包含许多的例子,说明了一些可能性。


0 0