android 混淆器(ProGuard)

来源:互联网 发布:python parse教程 编辑:程序博客网 时间:2024/05/22 12:30

前言

本章内容为开发者指南(Dev Guide)/Developing/Tools/ProGuard,本章内容为"混淆器",版本为Android3.0 r1,翻译来自:"Jim",欢迎与他交流:"litaojun@gmail.com",再次感谢"Jim" !期待你一起参与翻译Android的相关资料,联系我over140@gmail.com。

 

声明

  欢迎转载,但请保留文章原始出处:)

    JavaEye社区:http://www.iteye.com

Android中文翻译组:http://goo.gl/6vJQl

 

原文

         http://developer.android.com/guide/developing/tools/proguard.html

 

混淆器(ProGuard)

  在本文中(In this document)

    Enabling ProGuard

    Configuring ProGuard

    Decoding Obfuscated Stack Traces

      Debugging considerations for published applications

  参见

    ProGuard Manual »

    ProGuard ReTrace Manual »

混淆器通过删除从未用过的代码和使用晦涩名字重命名类、字段和方法,对代码进行压缩,优化和混淆。结果是一个比较小的.apk文件,该文件比较难进行逆向工程。因此,当你的应用程序对安全敏感(要求高),例如当你授权应用程序的时候,混淆器是一种重要的保护手段。

混淆器被集成在android 构建系统中,所以你不必手动调用它。同时混淆器仅在发布模式下进行构建应用程序的时候才会运行起来,所以在调试模式下构建程序时,你不必处理混淆代码。让混淆器运行起来是可选择的,但是推荐选上。

这个文档描述了怎样启用并配置混淆器,以及使用跟踪(retrace)工具对混淆的堆栈跟踪信息(stack traces)进行解码。

 

 

启用混淆器Enabling ProGuard

         当你新建了一个Android工程之后,一个proguard.cfg文件会在工程的根目录下自动创建。这个文件定义了混淆器是怎样优化和混淆你的代码的,所以懂得怎样根据你的需要来定制是非常重要的。缺省的配置文件仅覆盖到了通常情况,所以根据你的需求,很可能需要编辑它。接下来的内容是关于通过定制混淆器配置文件来对混淆器配置

         为了让启用混淆器作为Ant或者Eclipse构建过程中一部分,可以在<project_root>/default.properties文件中,设置proguard.config属性。路径可以是绝对路径或者工程根目录的相对路径。

         如果你让proguard.cfg文件在缺省位置(工程的根目录),你可以像这样指定位置:

proguard.config=proguard.cfg

         同样,你可以把该文件放到任意的位置,并指定它的绝对路径。

proguard.config=/path/to/proguard.cfg

         当你在发布模式下,或者通过运行ant release,或者通过使用Eclipse中的Export Wizard构建你的应用程序的时候,构建系统都会自动地去检查proguard.config属性是否被设置了。如果被设置了,混淆器在把所有东西打包成.apk文件之前,自动地对应用程序字节码进行混淆处理。而在调试模式中构建则不会调用混淆器,因为那样调试会更加繁重。

         运行混淆器之后输出的文件有:

         dump.txt

                   描述.apk包中所有class文件的内部结构。

         mapping.txt

                   列出了源代码与混淆后的类,方法和属性名字之间的映射。这个文件对于在构建之后得到的bug报告是有用的,因为它把混淆的堆栈跟踪信息反翻译为源代码中的类,方法和成员名字。更多信息,查看解码混淆过的堆栈跟踪信息

         seeds.txt

                   列出那些未混淆的类和成员。

         usage.txt

                   列出从.apk中剥离的代码。

         这些文件放在以下目录中:

<project_root>/bin/proguard 当你使用Ant

<project_root>/proguard 当你使用Eclipse

注意: 每次在发布模式下构建时,这些文件都会被最新的文件覆盖。所以每次发布程序时候,为了反混淆来自构建时产生的bug报告,请保存这些文件的一个拷贝。对于为什么要保存这些文件的重要性的更多信息,请查看程序发布调试注意事项。

 

 

混淆器配置(Configuring ProGuard)

         某些情况下,proguard.cfg文件的缺省配置可以满足需求了。但是,对于混淆器来说,大多数情况做出正确的分析是困难的,并且它或许会删除在它看来是无用的,但对于程序来说却确实需要的代码。一些例子如下:

一个仅引用于AndroidManifest.xml文件的类。

一个通过JNI调用的方法。

动态引用的属性和方法。

缺省的proguard.cfg文件试图覆盖普通的情况,但是你可能碰到类似ClassNotFoundException的异常,这个异常出现在当你的程序去访问一个被混淆器移除了的类的时候。

你可以在proguard.cfg文件中添加-keep这一行来修复这些错误。例如:

-keep public class <MyClass>

-Keep设置有很多可选项和注意地方,所以为了获得更多关于配置信息,强烈推荐你阅读混淆器用户手册。特别有用的有Keep选项综述举例部分。在混淆器手册问题解决方案部分,介绍了代码在混淆过程中你可能碰到的其他常见问题。

 

 

解码混淆过的堆栈跟踪信息(Decoding Obfuscated Stack Traces)

         当混淆代码并输出了一个堆栈调试信息时,这些方法名字是混淆过的,虽然可以进行调试,但是调试变得困难。幸运的是,每当混淆器运行时候,它都会输出到文件<project_root>/bin/proguard/mapping.txt中,该文件包含了从原始类,方法和属性名字到混淆后名字的映射。

         Windows系统中retrace.bat脚本命令或者LinuxMac OS X系统中retrace.sh脚本命令能把混淆后的堆栈调试信息转换为可以理解的文件。它被放在<sdk_root>/tools/proguard/目录下。运行retrace工具的命令语法是:

retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]

例如:

retrace.bat -verbose mapping.txt obfuscated_trace.txt

如果你没有为<stracktrace_file>指定值,那么retrace工具从标准输入读取。

 

已发布应用程序的调试注意事项(Debugging considerations for published applications)

         保存好每一个已发布给用户的程序的mapping.txt文件。通过保存发布构建版本的mapping.txt文件拷贝,确保当用户碰到bug,并把混淆后的堆栈调试跟踪信息提交给你时,你可以进行调试从而修复问题。程序的mapping.txt文件在每次发布构建时都会被覆盖,所以你一定要注意保存正确的版本。

         例如,假设你已经发布了一个应用程序并在继续在新的版本中开发添加新的功能。接着你马上启动混淆器并创建一个新的发布版本。该操作把mapping.txt文件覆盖了。一个用户提交了来自当前发布版本的bug报告,该报告包含了堆栈调试信息。你再也不能对用户的堆栈信息进行调试了,因为这个对应用户本机上版本的mapping.txt文件不存在了。其他覆盖mapping.txt文件的情况还有很多,所以对于每一个可能需要调试的版本,你都要确保有一份拷贝。

         如何保存mapping.txt文件由你决定。例如,你可以根据版本和构建号来重命名它们,或者连同你的源代码进行版本控制。

转自:http://www.iteye.com/topic/1012395

 

 

from:http://www.eoeandroid.com/thread-209210-1-1.html
proguard 原理
Java代码编译成二进制class 文件,这个class 文件也可以反编译成源代码 ,除了注释外,原来的code 基本都可以看到。为了防止重要code 被泄露,我们往往需要混淆(Obfuscation code , 也就是把方法,字段,包和类这些java 元素的名称改成无意义的名称,这样代码结构没有变化,还可以运行,但是想弄懂代码的架构却很难。 proguard 就是这样的混淆工具,它可以分析一组class 的结构,根据用户的配置,然后把这些class 文件的可以混淆java 元素名混淆掉。在分析class 的同时,他还有其他两个功能,删除无效代码(Shrinking 收缩),和代码进行优化 (Optimization Options)。
缺省情况下,proguard 会混淆所有代码,但是下面几种情况是不能改变java 元素的名称,否则就会这样就会导致程序出错。
一, 我们用到反射的地方。
二, 我们代码依赖于系统的接口,比如被系统代码调用的回调方法,这种情况最复杂。
三, 是我们的java 元素名称是在配置文件中配置好的。
所以使用proguard时,我们需要有个配置文件告诉proguard 那些java 元素是不能混淆的。

proguard 配置
最常用的配置选项
-dontwarn 缺省proguard 会检查每一个引用是否正确,但是第三方库里面往往有些不会用到的类,没有正确引用。如果不配置的话,系统就会报错。
-keep 指定的类和类成员被保留作为 入口 。
-keepclassmembers 指定的类成员被保留。
-keepclasseswithmembers 指定的类和类成员被保留,假如指定的类成员存在的话。

proguard 问题和风险
代码混淆后虽然有混淆优化的好处,但是它往往也会带来如下的几点问题
1,混淆错误,用到第三方库的时候,必须告诉 proguard 不要检查,否则proguard 会报错。
2,运行错误,当code 不能混淆的时候,我们必须要正确配置,否则程序会运行出错,这种情况问题最多。
3,调试苦难,出错了,错误堆栈是混淆后的代码 ,自己也看不懂。


为了防止混淆出问题,你需要熟悉你所有的code ,系统的架构 ,以及系统和你code的集成的接口,并细心分析。 同时你必须需要一轮全面的测试。 所以混淆也还是有一定风险的。 为了避免风险,你可以只是混淆部分关键的代码,但是这样你的混淆的效果也会有所降低。

常见的不能混淆的androidCode
Android 程序 ,下面这样代码混淆的时候要注意保留。
Android系统组件,系统组件有固定的方法被系统调用。
被Android Resource 文件引用到的。名字已经固定,也不能混淆,比如自定义的View 。
Android Parcelable ,需要使用android 序列化的。
其他Anroid 官方建议 不混淆的,如
android.app.backup.BackupAgentHelper
android.preference.Preference
com.android.vending.licensing.ILicensingService
Java序列化方法,系统序列化需要固定的方法。
枚举 ,系统需要处理枚举的固定方法。
本地方法,不能修改本地方法名
annotations 注释
数据库驱动
有些resource 文件
用到反射的地方


如何实施
现在的系统已经配置为混淆时候会保留
Android系统组件
自定义View
Android Parcelable
Android R 文件
Android Parcelable
枚举
各个开发人员必须检查自己的code 是否用到反射 ,和其他不能混淆的地方。告诉我来修改配置文件(已经保留的就不需要了)


目前系统部检查的第三方库为
-dontwarn android.support.**
-dontwarn com.tencent.**
-dontwarn org.dom4j.**
-dontwarn org.slf4j.**
-dontwarn org.http.mutipart.**
-dontwarn org.apache.**
-dontwarn org.apache.log4j.**
-dontwarn org.apache.commons.logging.**
-dontwarn org.apache.commons.codec.binary.**
-dontwarn weibo4android.**
proguard 参数
-include {filename}    从给定的文件中读取配置参数

-basedirectory {directoryname}    指定基础目录为以后相对的档案名称

-injars {class_path}    指定要处理的应用程序jar,war,ear和目录

-outjars {class_path}    指定处理完后要输出的jar,war,ear和目录的名称

-libraryjars {classpath}    指定要处理的应用程序jar,war,ear和目录所需要的程序库文件

-dontskipnonpubliclibraryclasses    指定不去忽略非公共的库类。

-dontskipnonpubliclibraryclassmembers    指定不去忽略包可见的库类的成员。


保留选项
-keep {Modifier} {class_specification}    保护指定的类文件和类的成员

-keepclassmembers {modifier} {class_specification}    保护指定类的成员,如果此类受到保护他们会保护的更好

-keepclasseswithmembers {class_specification}    保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在。

-keepnames {class_specification}    保护指定的类和类的成员的名称(如果他们不会压缩步骤中删除)

-keepclassmembernames {class_specification}    保护指定的类的成员的名称(如果他们不会压缩步骤中删除)

-keepclasseswithmembernames {class_specification}    保护指定的类和类的成员的名称,如果所有指定的类成员出席(在压缩步骤之后)

-printseeds {filename}    列出类和类的成员-keep选项的清单,标准输出到给定的文件

压缩
-dontshrink    不压缩输入的类文件

-printusage {filename}

-whyareyoukeeping {class_specification}     

优化
-dontoptimize    不优化输入的类文件

-assumenosideeffects {class_specification}    优化时假设指定的方法,没有任何副作用

-allowaccessmodification    优化时允许访问并修改有修饰符的类和类的成员

混淆
-dontobfuscate    不混淆输入的类文件

-printmapping {filename}

-applymapping {filename}    重用映射增加混淆

-obfuscationdictionary {filename}    使用给定文件中的关键字作为要混淆方法的名称

-overloadaggressively    混淆时应用侵入式重载

-useuniqueclassmembernames    确定统一的混淆类的成员名称来增加混淆

-flattenpackagehierarchy {package_name}    重新包装所有重命名的包并放在给定的单一包中

-repackageclass {package_name}    重新包装所有重命名的类文件中放在给定的单一包中

-dontusemixedcaseclassnames    混淆时不会产生形形色色的类名

-keepattributes {attribute_name,...}    保护给定的可选属性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.

-renamesourcefileattribute {string}    设置源文件中给定的字符串常量

解决export打包的报错
这个时候export提示“conversion to Dalvik format failed with error 1”错误,网上说法有好多种,最后我还是把proguard从4.4升级到4.8就解决了。官方地址是http://proguard.sourceforge.net。上面的配置文件参数可以在这里查阅。
升级办法很简单,就是把android sdk目录下的tool/proguard目录覆盖一下即可。

打包出来的程序如何调试
一旦打包出来,就不能用eclipse的logcat去看了,这里可以用android sdk中ddms.bat的tool来看,一用就发现和logcat其实还是一个东西,就是多了个设备的选择。

使用 gson 需要的配置
当Gson用到了泛型就会有报错,这个真给郁闷了半天,提示“Missing type parameter”。最后找到一个资料给了一个解决办法,参考:http://stackoverflow.com/questio ... sing-type-parameter。
另外我又用到了JsonObject,提交的Object里面的members居然被改成了a。所以上面给的东西还不够,还要加上
# 用到自己拼接的JsonObject
-keep class com.google.gson.JsonObject { *; }


个人建议减少这些依赖包混淆带来的麻烦,干脆都全部保留不混淆。例如
-keep class com.badlogic.** { *; }
-keep class * implements com.badlogic.gdx.utils.Json*
-keep class com.google.** { *; }

使用libgdx需要的配置
参考http://code.google.com/p/libgdx-users/wiki/Ant


验证打包效果
利用了apktool的反编译工具,把打包文件又解压了看了一下,如果包路径、类名、变量名、方法名这些变化和你期望一致,那就OK了。命令:


apktool.bat d xxx.apk destdir

配置实例
-injars  androidtest.jar【jar包所在地址】
-outjars  out【输出地址】
-libraryjars    'D:\android-sdk-windows\platforms\android-9\android.jar' 【引用的库的jar,用于解析injars所指定的jar类】

-optimizationpasses 5
-dontusemixedcaseclassnames 【混淆时不会产生形形色色的类名 】
-dontskipnonpubliclibraryclasses 【指定不去忽略非公共的库类。 】
-dontpreverify 【不预校验】
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 【优化】
-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.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keep public abstract interface com.asqw.android.Listener{
public protected <methods>;  【所有方法不进行混淆】
}
-keep public class com.asqw.android{
public void Start(java.lang.String); 【对该方法不进行混淆】
}
-keepclasseswithmembernames class * { 【保护指定的类和类的成员的名称,如果所有指定的类成员出席(在压缩步骤之后)】
native <methods>;
}
-keepclasseswithmembers class * { 【保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在。】
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity {【保护指定类的成员,如果此类受到保护他们会保护的更好 】
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {【保护指定的类文件和类的成员】
public static final android.os.Parcelable$Creator *;
}
//不混淆指定包下的类
-keep class com.aspire.**

 

Example:

    -injars <project>.jar

    -outjars <project>_out.jar

 

    -libraryjars <java.home>/lib/rt.jar

    -libraryjars <project.home>/webroot/WEB-INF/lib/webwork.jar

    .......

 

    # 保留实现Action接口类中的公有的,友好的,私有的属性 和 公有的,友好的方法。其它的全部压缩,优化,混淆。

    # 因为配置文件中的类名是一个完整的类名,如果经过处理后就有可能找不到这个类。

    # 属性是jsp页面所需要的,如果经过处理jsp页面就无法得到action中的数据。

    -keep public class * implements com.opensymphony.xwork.Action{

        public protected private <fields>;

        public protected <methods>;

    }

 

    # 保留实现了Serializable接口类中的公有的,友好的,私有的成员(属性和方法)

    # 这个配置主要是对应实体类的配置。

    -keep public class * implements java.io.Serializable{

        public protected private *;

    }