1、ProGuard简介
Java的字节码一般是非常容易反编译的。为了很好的保护Java源代码,我们往往会对编译好的class文件进行混淆处理。ProGuard的主要作用就是混淆。当然它还能对字节码进行缩减体积、优化等,但那些对于我们来说都算是次要的功能。
引用ProGuard官方的一段话来介绍就是:
2、Android Eclipse开发环境与ProGuard
在Android 2.3以前,混淆Android代码只能手动添加proguard来实现代码混淆,非常不方便.而2.3以后,Google已经将这个工具加入到了SDK的工具集里.具体路径:SDK\tools\proguard.当创建一个新的Android工程时,在工程目录的根路径下,会出现一个proguard的配置文件proguard.cfg.也就是说,我们可以通过简单的配置,在我们的elipse工程中直接使用ProGuard混淆Android工程.
具体混淆的步骤非常简单.首先,我们需要在工程描述文件default.properties中,添加一句话,启用ProGuard.如下所示:
java代码:
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-19
android.library.reference.1=../../../../wordspace2/appcompat_v7
这样,Proguard就可以使用了.当我们正常通过Android Tools导出Application Package时,Proguard就会自动启用,优化混淆你的代码.
导出成功后,你可以反编译看看混淆的效果,一些类名、方法名和变量名等,都变成了一些无意义的字母或者数字,证明混淆成功!
这是一般情况,即无第三方Jar包,如果存在第三方Jar包,那么打开proguard.cfg文件进行编辑,在其中加入以下代码:
1. -libraryjars %lib_jar_path%
有几个Jar包,便添加几次,如在项目的libs目录下有a.jar,b.jar,c.jar三个Jar包:
1. -libraryjars libs/a.jar
2. -libraryjars libs/b.jar
3. -libraryjars libs/c.jar
如此,通过android tools导出APK即可。
此外,还有些特殊情况,会令导出发生异常,视具体异常情况而定,修改proguard.cfg文件。
比如出现了以下异常:
1. Warning: com.google.android.maps.MapView: can't find referenced class com.android.mkstubs.stubber.MethodStubber
2. Warning: com.google.android.maps.MapView$1: can't find referenced class com.android.mkstubs.stubber.MethodStubber
即:
1. Warning: %class_full_name%: can't find referenced class %class_full_name%
这种异常情况,需要在proguard.cfg文件中,添加以下代码:
1. -dontwarn %class_full_name%
即可,便以上面例子而言,应当如下:
1. -dontwarn com.google.android.maps.*
等等,此类情况修改proguard.cfg文件即可,还有种特殊情况,需要对引入的Jar包进行修改,如下:
1. Warning: library class android.content.res.XmlResourceParser extends or implements program class org.xmlpull.v1.XmlPullParser
2. Warning: library class android.view.LayoutInflater depends on program class org.xmlpull.v1.XmlPullParser
这是因为引用的Jar包中含有xmlpull类库,Android系统的类库中已经包含了xmlpull,这样混淆出现了冲突,解决办法是把它里面已存在的和系统库冲突的类去掉,就可以了,产生冲突的类可见控制台输出。
另外对不想混淆的类/方法/变量,可以使用-keep指定,具体参考下文proguard.cfg文件写法。
3、proguard.cfg配置
稍微深入想一下混淆后的结果,你就会发现,如果一些提供给外部的类、方法、变量等名字被改变了,那么程序本身的功能就无法正常实现.那么Proguard如何知道哪些东西是可以改名,而哪些是不能改变的呢?
这个是靠proguard.cfg文件来进行配置的.Android工程中默认自动生成的proguard.cfg已经针对Android的一般情况进行了配置,我们打开这个配置文件.内容大概如下:
java代码:
1. -optimizationpasses 5
2. -dontusemixedcaseclassnames
3. -dontskipnonpubliclibraryclasses
4. -dontpreverify
5. -verbose
6. -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
7. -keep public class * extends android.app.Activity
8. -keep public class * extends android.app.Application
9. -keep public class * extends android.app.Service
10. -keep public class * extends android.content.BroadcastReceiver
11. -keep public class * extends android.content.ContentProvider
12. -keep public class * extends android.app.backup.BackupAgentHelper
13. -keep public class * extends android.preference.Preference
14. -keep public class com.android.vending.licensing.ILicensingService
15. -keepclasseswithmembernames class * {
16. native <methods>;
17. }
18. -keepclasseswithmembernames class * {
19. public <init>(android.content.Context, android.util.AttributeSet);
20. }
21. -keepclasseswithmembernames class * {
22. public <init>(android.content.Context, android.util.AttributeSet, int);
23. }
24. -keepclassmembers enum * {
25. public static **[] values();
26. public static ** valueOf(java.lang.String);
27. }
28. -keep class * implements android.os.Parcelable {
29. public static final android.os.Parcelable$Creator *;
30. }
从脚本中可以看到,混淆中保留了继承自Activity、Service、Application、BroadcastReceiver、ContentProvider等基本组件以及com.android.vending.licensing.ILicensingService,并保留了所有的Native变量名及类名,所有类中部分以设定了固定参数格式的构造函数,枚举等等。(详细信息请参考<proguard_path>\examples中的例子及注释。)
接下来,按照google帮助文档里说的
To enable ProGuard so that it runs as part of an Ant or Eclipse build, set the proguard.config property in the <project_root>/default.properties file. The path can be an absolute path or a path relative to the project's root.
所以我们修改default.properties file
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-19
android.library.reference.1=../../../../wordspace2/appcompat_v7
当然,我们使用的sdk版本比较低也没问题,只需将proguard.cfg文件拷贝到我们工程的根目录下,同样在default.properties文件中加上proguard.config=proguard.cfg即可。
最后签名发布我们的apk,再反编译试试看,除了proguard.cfg配置文件中保持的那些类名变量名没变之外,其他的全部都是abc之类的名字了,增加了反编译后的阅读难度,更有利于保护我们的代码。
4、*无需eclipse无需ant,直接在android源码中混淆编译(尚未验证)
在需要混淆的工程目录下(package/apps/下的工程)添加proguard.flags文件,该文件即为网络传说中的proguard.cfg,只是命名不一样而已,然后再Android.mk中添加如下两句:
LOCAL_PROGUARD_ENABLED := full
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
上面的full 也可以是custom,如果不写这句,那还得添加如下一句:
TARGET_BUILD_VARIANT := user或者TARGET_BUILD_VARIANT := userdebug
这样后在工程目录下执行mm便可以看到在out目录下生成了形如proguard.classes.jar的东东,这就说明已在编译中启动了proguard,但反编译一看,并未出现网络云说的abcd替代符号,其实代码并未真正混淆。
android在编译时默认关闭了混淆选项,有去研究build/core目录的同鞋会发现这里也有个proguard.flags文件,其实在proguard的过程中,编译器会调用包括本地目录下和系统定义了的多个proguard.flags文件,而在这个文件中混淆的选项被禁止了,故而编译出来的apk仍未混淆。因此将如下句子注释掉便可实现真正的混淆编译:
# Don't obfuscate. We only need dead code striping.
-dontobfuscate(将该句加个#号注释掉)
好奇的同鞋还可以继续看看,为什么TARGET_BUILD_VARIANT := user和LOCAL_PROGUARD_ENABLED := full二选一即可,详见build/core/package.mk:
LOCAL_PROGUARD_ENABLED:=$(strip $(LOCAL_PROGUARD_ENABLED))
ifndef LOCAL_PROGUARD_ENABLED
ifneq ($(filter user userdebug, $(TARGET_BUILD_VARIANT)),)
# turn on Proguard by default for user & userdebug build
LOCAL_PROGUARD_ENABLED :=full
endif
endif
ifeq ($(LOCAL_PROGUARD_ENABLED),disabled)
# the package explicitly request to disable proguard.
LOCAL_PROGUARD_ENABLED :=
endif
proguard_options_file :=
ifneq ($(LOCAL_PROGUARD_ENABLED),custom)
ifneq ($(all_resources),)
proguard_options_file:= $(package_expected_intermediates_COMMON)/proguard_options
endif # all_resources
endif # !custom
LOCAL_PROGUARD_FLAGS := $(addprefix -include ,$(proguard_options_file)) $(LOCAL_PROGUARD_FLAGS)
5、做混淆过程中出现的一些问题
1、项目在做混淆时,抛出Failed to run proguard: Error executing Proguard.
linux ubuntu开发环境下:
项目在做混淆时,在eclipse里运行程序,抛出异常如下:Failed to run proguard: Error executing Proguard. Please check Proguard is present at /home/wangwei/soft_dev/android-sdk-linux_86/tools/proguard/bin/proguard.sh
Error executing Proguard. Please check Proguard is present at /home/wangwei/soft_dev/android-sdk-linux_86/tools/proguard/bin/proguard.sh
大致意思是说:请检查Proguard文件是存在于/home/wangwei/soft_dev/android-sdk-linux_86/tools/proguard/bin/proguard.sh路径底下。
我仔细检查了,Proguard文件路径没错。那么是什么原因呢?
我考虑是不是proguard.sh文件是不是没有执行权限?(ubuntu环境下的文件通常需要修改执行权限)按照这个思路,我把proguard文件的权限改成权限可读写,允许以程序执行文件。再次运行程序时,就顺利过关了。
2、我在自己的工程中导入了一个自带的包,dataconnection_helper_v15.jar
在proguard中这样配置的-libraryjars/dataconnection_helper_v15.jar
但是打包时却总是显示:
[2011-07-25 10:36:24 - KVPioneer] proguard.ParseException: Unknown option '-libraryjars/dataconnection_helper_v15.jar' in line 7 of file 'G:\android\KVPioneer\proguard.cfg',
[2011-07-25 10:36:24 - KVPioneer] included from argument number 1
[2011-07-25 10:36:24 - KVPioneer] at proguard.ConfigurationParser.parse(ConfigurationParser.java:170)
[2011-07-25 10:36:24 - KVPioneer] at proguard.ProGuard.main(ProGuard.java:491)
这是怎么回事?跪求、、、
-optimizationpasses 5 |
管用,屏蔽了警告,打包成功!