Android的Java混淆

来源:互联网 发布:淘宝店铺什么是c店 编辑:程序博客网 时间:2024/05/17 18:44

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
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-dontwarn
-dontskipnonpubliclibraryclassmembers
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
在proguard.cfg中改成这样试试

管用,屏蔽了警告,打包成功

0 0