代码混淆

来源:互联网 发布:英国哪个城市最美 知乎 编辑:程序博客网 时间:2024/05/16 12:14

什么是代码混淆

  • Java 是一种跨平台的、解释型语言,Java 源代码编译成中间”字节码”存储于 class 文件中。由于跨平台的需要,Java
    字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法,这些符号带有许多语义信息,很容易被反编译成 Java源代码。为了防止这种现象,我们可以使用 Java 混淆器对 Java 字节码进行混淆。

  • 混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序的真正语义。被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译,也将难以阅读。同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。

  • 混淆器的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。由于以上介绍的缩短变量和函数名以及丢失部分信息的原因, 编译后 jar 文件体积大约能减少25% ,这对当前费用较贵的无线网络传输是有一定意义的。

混淆文件 proguard.cfg 参数详解

-optimizationpasses 5                                                           # 压缩级别-dontusemixedcaseclassnames                                                     # 不使用大小写混合-dontskipnonpubliclibraryclasses                                                # 混淆第三方jar-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                    # 保持哪些类不被混淆-keepclasseswithmembernames class * {                                           # 保持native 方法不被混淆    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 * {                                                      # 保持枚举 enum 类不被混淆    public static **[] values();    public static ** valueOf(java.lang.String);}-keep class * implements android.os.Parcelable {                                # 保持 Parcelable 不被混淆  public static final android.os.Parcelable$Creator *;}-keep class MyClass;                                                            # 保持自己定义的类不被混淆

代码混淆的方法

  • 高版本 SDK 下,项目中同时包含 proguard-project.txt 和 project.properties 文件,这时需要在 proguard-project.txt 文件中进行如下信息的配置,然后再将项目 Export 即可。下面以真实的文件进行演示说明。
# 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.txtproguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt# Project target.target=android-18

以上的配置信息即是 project.properties 文件中内容,蓝色文字为我们在代码混淆过程中需要添加的配置信息,其中:sdk.dir 为你在当前机器上 SDK 的安装路径。如果想保留某个包下的文件不被混淆,可以在 proguard-project.txt文件中加入保留对应包名的语句即可。

# To enable ProGuard in your project, edit project.properties# to define the proguard.config property as described in that file.## Add project specific ProGuard rules here.# By default, the flags in this file are appended to flags specified# in ${sdk.dir}/tools/proguard/proguard-android.txt# You can edit the include path and order by changing the ProGuard# include property in project.properties.## For more details, see#   http://developer.android.com/guide/developing/tools/proguard.html# Add any project specific keep options here:-dontwarn com.cnki.android.cnkireader.** -keep class com.cnki.android.cnkireader.** { *; }# If your project uses WebView with JS, uncomment the following# and specify the fully qualified class name to the JavaScript interface# class:#-keepclassmembers class fqcn.of.javascript.interface.for.webview {#   public *;#}复制代码-optimizationpasses 5# 混淆时不会产生形形色色的类名 -dontusemixedcaseclassnames# 指定不去忽略非公共的库类-dontskipnonpubliclibraryclasses# 不预校验# -dontpreverify# 预校验-dontoptimize # 这1句是屏蔽警告-ignorewarnings -verbose# 优化-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*# 这1句是导入第三方的类库,防止混淆时候读取包内容出错#-libraryjars libs/youjar.jar # 去掉警告-dontwarn -dontskipnonpubliclibraryclassmembers# 不进行混淆保持原样-keep public class * extends android.app.Activity-keep public class * extends android.app.Application-keep public class * extends android.app.Fragment -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# 过滤R文件的混淆:-keep class **.R$* {*;}# 过滤第三方包的混淆:其中packagename为第三方包的包名# -keep class packagename.** {*;}-keep class com.hisilicon.android.hibdinfo.** {*;}-keep class com.huawei.iptv.stb.dlna.mymedia.dto.** {*;}-keep class com.huawei.iptv.stb.dlna.mymedia.facade.** {*;}-keep class com.huawei.mymediaprifacade.** {*;}-keep class com.hisilicon.android.mediaplayer.** {*;}-keep class com.nostra13.universalimageloader.** {*;}-keep class com.huawei.android.airsharing.** {*;}# 所有方法不进行混淆-keep public abstract interface com.huawei.android.airsharing.listener{public protected <methods>;}-keep public abstract interface com.huawei.android.airsharing.api{public protected <methods>;}# 对该方法不进行混淆# -keep public class com.asqw.android{# public void Start(java.lang.String); # }# 保护指定的类和类的成员的名称,如果所有指定的类成员出席(在压缩步骤之后)#-keepclasseswithmembernames class * {#    native <methods>;#}# 保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在#-keepclasseswithmembernames class * {#    public <init>(android.content.Context, android.util.AttributeSet);#}-keepclasseswithmembernames 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 *;}#-libraryjars libs/android-support-v4.jar#-dontwarn android.support.v4.**{*;}# If your project uses WebView with JS, uncomment the following# and specify the fully qualified class name to the JavaScript interface# class:-keepclassmembers class fqcn.of.javascript.interface.for.webview {   public *;}#不混淆第三方包中的指定内容-keep class android-support-v4.**{*;}-keep public class * extends android.support.v4.**-keep class android.view.**{*;}ProGuard是一个免费的java类文件压缩,优化,混淆器.它探测并删除没有使用的类,字段,方法和属性.它删除没有用的说明并使用字节码得到最大优化.它使用无意义的名字来重命名类,字段和方法.ProGuard的使用是为了:1.创建紧凑的代码文档是为了更快的网络传输,快速装载和更小的内存占用.2.创建的程序和程序库很难使用反向工程.3.所以它能删除来自源文件中的没有调用的代码4.充分利用java6的快速加载的优点来提前检测和返回java6中存在的类文件.参数:-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} 设置源文件中给定的字符串常量因为我们开发的是webwork+spring+hibernate的架构的项目,所有需要很详细的配置。(经过n次失败后总结)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 *;}keep选项[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname    [extends|implements [@annotationtype] classname][{    [@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> |                                                                      (fieldtype fieldname);    [@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> |                                                                                           <init>(argumenttype,...) |                                                                                           classname(argumenttype,...) |                                                                                           (returntype methodname(argumenttype,...));    [@annotationtype] [[!]public|private|protected|static ... ] *;    ...}]

什么是ProGuard工具?

ProGuard是android提供的一个免费的工具,它能够移除工程中一些没用的代码,或者使用语义上隐晦的名称来重命名代码中的类、字段和函数等,达到压缩、优化和混淆代码的功能。具体来说,使用ProGuard工具,可以达到下面两个目的:

删除了源文件中没有调用的那部分代码,最大化的精简了字节码文件,使得最终生成的apk文件更小。
使用语义混淆的命名替换了代码中的类、字段和函数等,使得其他人无法反编译获取源代码,起到对代码的保护作用。
我看网上有不少人根据ProGuard工具的作用,直接称呼其为“混淆代码工具”,本文也暂时用这个词简称。

更多的理解,可以参考ProGuard工具的官方文档地址:http://developer.android.com/tools/help/proguard.html

ProGuard工具的集成与使用环境

其实,ProGuard工具是已经集成到我们android系统中的,所以不需要用户手动的去集成。但是有一点需要注意,仅在程序处于Release模式时ProGuard才有效,反之在Debug模式是不能通过ProGuard来混淆代码的。

根据ProGuard的具体使用环境,我分在Eclipse工具和android源码两种编译环境浅谈ProGuard的使用方法

Eclipse环境中ProGuard的使用

以我电脑的android4.0环境为例,当我们在Eclipse中新建一个项目,或者导入一个已存在项目(保证当前项目没有语法错误)后,在工程的根目录,会自动生成两个ProGuard的混淆文件:proguard-project.txt和project.properties(在老版本的ADT中,只会生成一个叫proguard.cfg的文件)。我们先看下文件project.properties :# 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-18看后面一段注释:To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home) ,意指要让ProGuard 来压缩和混淆代码,把这句注释去掉即可!所以,我们只要把下面一句注释取消即可,如下所示:# 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我们仔细的看下这部分代码:这个地方是通过设置proguard.config属性来指定混淆代码的配置文件为本机SDK目录下面的proguard-android.txt文件,以及制定混淆的个性化配置文件为当前工程(eclipse下)根目录下面的proguard-project.txt文件 ,而后面这个文件,恰是我们刚才看到的原本在根目录下自动生成的另外一个文件!其实打开了这个地方,我们就已经可以混淆代码了,不过这里要注意:不能试图通过运行eclipse中的Run as 和 Debug as 菜单来生成混淆代码,必须通过如下图所示的方法将apk导出才行,当然你可以选择“签名”或者“不签名”:这样一步操作后,算是代码混淆完成了。那么怎么才能检验我们真的混淆了代码了呢?首先,我们能够看到在工程的根目录新生产了一个文件夹proguard,里面有四个文件,其内容如下:dump.txt : 描述了apk中所有类 文件中内部的结构体。( Describes the internal structure of all the class files in the .apk filemapping.txt : 列出了原始的类、方法和名称与混淆代码见得映射。( Lists the mapping between the original and obfuscated class, method, and field names. )seeds.txt : 列出了没有混淆的类和方法。( Lists the classes and members that are not obfuscated )usage.txt : 列出apk中删除的代码。( Lists the code that was stripped from the .apk )同时使用反编译软件对新生成的apk反编译后会发现,里面的类名、方法和变量等,都变成了简单的a、b、c、d等毫无含义的字母,这样就达到了混淆的目的:但在实际使用过程中,我们会发现当前apk中的有些方法和类,是要供外部使用的,而此时混淆了名称,外部调用就会报错了,那么怎么解决这个问题?此时就要用到我们刚才提到的混淆的个性化配置文件proguard-project.txt,在其中去配置不需要混淆的类、方法和变量等。关于混淆文件的具体配置方法,请看下面的最后一个标题会有详述。Android源码环境中ProGuard使用在Google发布的android源码中,面对那么多代码和文件目录,此时该如何混淆代码与配置混淆文件呢?android中默认是将代码混淆ProGuard关闭的,在alps/build/core/proguard.flags中有下面一句,意指将默认不混淆,不需要代码删除,我们将这一句注释起来,就起到代码混淆编译的作用。# Don't obfuscate. We only need dead code striping.-dontobfuscate可以说,这句是android工程中代码混淆的总开关,然而,注释了上面的代码后,整个工程就已经是代码混淆了吗?不是的,这里还要关注一个文件alps/build/core/package.mk,在这个文件中有这么一段:ifndef LOCAL_PROGUARD_ENABLEDifneq ($(filter user userdebug, $(TARGET_BUILD_VARIANT)),)    # turn on Proguard by default for user & userdebug build    #LOCAL_PROGUARD_ENABLED :=fullendifendif切记:当我们需要对整个工程进行代码混淆的时候,就把此处的 #LOCAL_PROGUARD_ENABLED :=full注释去掉,成为有效的宏即可。如果不想对整个工程代码混淆,而只是相对某个模块混淆的话,就先不要动这里的代码。接着建议将真个工程new一遍,之后就可以针对具体的apk文件进行混淆文件的设置和定制了。下面以alps/packages/apps/Music为例说说该如何对特定模块做到混淆代码:在Music目录下,我们看到一个平时不太关注,但今天一定很在意的文件名:proguard.flags ,对了,这个文件就是Music的混淆配置文件,可以打开看看(有些地方没有这个文件,用户可以自己手动新建一下,最好名称也叫proguard.flags,android下默认都是这个名字)。当然,设置了配置文件还是不够的,还需要在同目录的Android.mk中如下设置如下两句:LOCAL_PROGUARD_ENABLED  := fullLOCAL_PROGUARD_FLAG_FILES := proguard.flags只有这样才能让混淆代码有效,并且将混淆配置文件关联起来。(有些模块没有这两句句,就自己手动加上)反之,如果用户已经在alps/build/core/package.mk打开了全工程混淆编译的控制点后,又在针对某个模块时不想混淆编译怎么办?这就简单了,将模块下的Android.mk中设置为**LOCAL_PROGUARD_ENABLED := disabled**即可。这样,我们通过mm编译后的代码生成的apk,或者new真个工程后生成的烧机代码,都是已经添加相应配置的混淆代码了。反编译后,除过proguard.flags中定制的不需要混淆的代码外,其他都是被混淆了,如图所示是android中Music模块的混淆后反编译结果:混淆文件的配置在实际使用过程中,我们会发现当前apk中的有些方法和类,是要供外部使用的,而此时混淆了名称,外部调用就会报错了,那么怎么解决这个问题?此时就需要我们配置混淆的个性化文件proguard-project.txt(eclipse环境中)或者proguard.flags(android源码环境),在其中去配置不需要混淆的类、方法和变量等。关于混淆文件的具体配置方法,大家可以去搜索下,我这里提供一段网上有人共享的配置代码,这个配置代码保留了工程文件中的Activity、Application、Service、BroadcastReceiver、ContentProvider、BackupAgentHelper、Preference和ILicensingService的子类,并保留了所有的Native变量名及类名,所有类中部分以设定了固定参数格式的构造函数,枚举等等,用来防止外部调用出错,大家可以借鉴下,以后来配置自己的文件:-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 -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 *;}
0 0
原创粉丝点击