Android 编程下的代码混淆

来源:互联网 发布:不看广告的软件 编辑:程序博客网 时间:2024/06/06 03:21

   

http://www.cnblogs.com/sunzn/archive/2013/03/06/2946952.html

点击打开链接

http://www.cnblogs.com/sunzn/archive/2013/03/06/2946952.html

http://www.cnblogs.com/sunzn/archive/2013/03/06/2946952.html

http://www.cnblogs.com/sunzn/archive/2013/03/06/2946952.html



Android 编程下的代码混淆

什么是代码混淆

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                    # 保持哪些类不被混淆-keep public class * extends android.app.backup.BackupAgentHelper               # 保持哪些类不被混淆-keep public class * extends android.preference.Preference                      # 保持哪些类不被混淆-keep public class com.android.vending.licensing.ILicensingService              # 保持哪些类不被混淆-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 的版本不同有 2 中不同的代码混淆方式,以上的 proguard.cfg 参数详解中所涉及到的信息是在较低版本 SDK 下的混淆脚本,事实上在高版本的 SDK 下混淆的原理和参数也与低版本的相差无几,只是在不同 SDK 版本的环境下引入混淆脚本的方式有所不同。具体方法如下:

  • 低版本 SDK 下,项目中同时包含 proguard.cfg 和 project.properties 文件,则只需在 project.properties 文件末尾添加 proguard.config=proguard.cfg 再将项目 Export即可。
  • 高版本 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-16
复制代码

以上的配置信息即是 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 *;#}
复制代码


















http://www.eoeandroid.com/thread-316591-1-1.html

http://www.eoeandroid.com/thread-316591-1-1.html

http://www.eoeandroid.com/thread-316591-1-1.html

http://www.eoeandroid.com/thread-316591-1-1.html

http://www.eoeandroid.com/thread-316591-1-1.html

本帖最后由 469874851 于 2013-12-6 17:26 编辑

之前写过关于打包混淆的一个帖子《android项目混淆打包注意事项》,它介绍的是对第三方jar包进行混淆,基本上可以解决混淆问题了。还不知道的,可以去看看,地址在这:http://my.eoe.cn/smile_it/archive/3820.html

现在的应用或多或少的都引用开源的项目作为外部library,如果对大部分开源项目还不是很了解的话,大家可以去我这篇博客去看看,它介绍了很多实用的开源项目和框架--《开源Club》:http://my.eoe.cn/smile_it/archive/17918.html

因此,这一篇文章主要是介绍在应用程序里引用了外部第三方库作为liarary时的混淆方式,也是由于最近一个项目引用了5个开源的library库,在上线之前专门实践了怎么对其进行混淆。


好了,废话不说,步入正题:

1、在project.properties文件中,你需要做的就是指定混淆规则,还有指明默认的第三方库的路径

proguard.config=proguard.cfg
target=android-14
android.library.reference.1=..\\引入的外部library的工程名

2、如果你对你引入的第三方库不是很了解或者官方未对其混淆给出具体的实例的话,建议还是直接屏蔽,即不对其进行混淆就行。
在proguard.cfg文件内 主要做的是就是

> -libraryjars ..\\第三方库的工程名--------引入的第三方库
> -dontwarn  包名.**  --------告诉ProGuard不要警告找不到包名.**这个包里面的类的相关引用
> -keep class 包名.** { *;}      --------对这个包里面的所有类和所有方法不进行混淆

示例:

由于我的项目中还有其他的jar包,故我贴出我的

1、project.properties
![图片说明文字][1]


  [1]:

 "QQ图片20131206132740.jpg"



2、proguard.cfg:

-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-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

#这是友盟社会化组件和统计提供的混淆代码--end
-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 *;
}

-dontwarn com.umeng.**

-dontwarn org.apache.commons.**

-dontwarn com.tencent.weibo.sdk.**

-keepattributes *Annotation*

-keep class com.umeng*.** {*; }

-keep public class [your_pkg].R$*{
    public static final int *;
}
-keep class com.tencent.open.TDialog$*

-keep class com.tencent.open.TDialog$* {*;}

-keep class com.tencent.open.PKDialog

-keep class com.tencent.open.PKDialog {*;}

-keep class com.tencent.open.PKDialog$*

-keep class com.tencent.open.PKDialog$* {*;}

-keep class com.tencent.mm.sdk.openapi.WXMediaMessage {*;}

-keep class com.tencent.mm.sdk.openapi.** implements com.tencent.mm.sdk.openapi.WXMediaMessage$IMediaObject {*;}

#这是友盟社会化组件和统计提供的混淆代码 end


-libraryjars ..\\第三方库的工程名
-dontwarn  包名.**
-keep class 包名.** { *;}

注:若还有其他的类库,可在这按照上面的格式继续添加,默认都是对其不混淆


如果有什么不明白的,可以给我留言哦!





















http://www.eoeandroid.com/thread-315434-1-1.html

http://www.eoeandroid.com/thread-315434-1-1.html

http://www.eoeandroid.com/thread-315434-1-1.html

http://www.eoeandroid.com/thread-315434-1-1.html



1.设置混淆

在工程下找到project.properties文件

在文件中加入proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt这个是系统的

也可以用自己的混淆文件(这样就可以配置一些自己的东西),去sdk.dir}/tools/proguard/ 下复制proguard-android.txt文件到本地工程中

然后设置成proguard.config=proguard-android.txt

project.properties文件:

[java] view plaincopy


  • #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt  
  • proguard.config=proguard-android.txt  
  •   
  • # Project target.  
  • target=android-17  
  • -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 *;

    }






    =====================================常见异常===================================

    参考:http://blog.csdn.net/vrix/article/details/7100841


    加入第三方jar包之后常出现的几个异常:

    proguard returned with error code 1.See console

    情况1:

    Proguard returned with error code 1. See console

    Error: C:/Documents (系统找不到指定文件)

    后来发现是因为将整个工程放到了桌面上,而桌面的目录是C:/Documents and Settings/Administrator/桌面,在这里面有空格,而proguard进行发编译的时候是不允许有空格的

    如果换了正确路径还不好用的话,直接删除proguard就好了

    注意:SDK和程序路径最好不要有空格符

    情况2:

    Proguard returned with error code 1. See console

    异常:

    java.lang.ArrayIndexOutOfBoundsException

    解决办法:将proguard.cfg中的"-dontpreverify"改成“-dontoptimize”

    参考文章:http://groups.google.com/group/android-developers/browse_thread/thread/eca3b0f5ce6ad00f


    我把项目中生成的proguard文件夹(此时文件夹是空的)删掉,然后再重新运行项目,就OK 了。

    情况3:

    [2011-10-21 13:22:32 - ZMKSMarket_Build_v1.0] Proguard returned with error code 1. See console


    [2011-10-21 13:22:32 - ZMKSMarket_Build_v1.0] java.io.IOException: Can't read [proguard.ClassPathEntry@106082] (No such file or directory)

    [2011-10-21 13:22:32 - ZMKSMarket_Build_v1.0]

    at proguard.InputReader.readInput(InputReader.java:230)

    [2011-10-21 13:22:32 - ZMKSMarket_Build_v1.0]

    at proguard.InputReader.readInput(InputReader.java:200)

    [2011-10-21 13:22:32 - ZMKSMarket_Build_v1.0]

    at proguard.InputReader.readInput(InputReader.java:178)

    [2011-10-21 13:22:32 - ZMKSMarket_Build_v1.0]

    at proguard.InputReader.execute(InputReader.java:100)

    [2011-10-21 13:22:32 - ZMKSMarket_Build_v1.0]

    at proguard.ProGuard.readInput(ProGuard.java:195)

    [2011-10-21 13:22:32 - ZMKSMarket_Build_v1.0]

    at proguard.ProGuard.execute(ProGuard.java:78)

    [2011-10-21 13:22:32 - ZMKSMarket_Build_v1.0]

    at proguard.ProGuard.main(ProGuard.java:499)

    抛出这样的异常的原因是第三方jar的引用路径不对,没有找到这个需要忽略混淆的jar包。




    ========================官方文档翻译========================================


    原文
    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报告,请保存这些文件的一个拷贝。对于为什么要保存这些文件的重要性的更多信息,请查看程序发布调试注意事项。




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

    • 一个仅引用于AndroidManifest.xml文件的类。
    • 一个通过JNI调用的方法。
    • 动态引用的属性和方法。


    缺省的proguard.cfg文件试图覆盖普通的情况,但是你可能碰到类似ClassNotFoundException的异常,这个异常出现在当你的程序去访问一个被混淆器移除了的类的时候。
    你可以在proguard.cfg文件中添加-keep这一行来修复这些错误。例如:

    -keep publicclass<MyClass>


    -Keep设置有很多可选项和注意地方,所以为了获得更多关于配置信息,强烈推荐你阅读混淆器用户手册。特别有用的有Keep选项综述举例部分。在混淆器手册问题解决方案部分,介绍了代码在混淆过程中你可能碰到的其他常见问题。
    解码混淆过的堆栈跟踪信息(Decoding Obfuscated Stack Traces)
    当混淆代码并输出了一个堆栈调试信息时,这些方法名字是混淆过的,虽然可以进行调试,但是调试变得困难。幸运的是,每当混淆器运行时候,它都会输出到文件<project_root>/bin/proguard/mapping.txt中,该文件包含了从原始类,方法和属性名字到混淆后名字的映射。
    Windows系统中retrace.bat脚本命令或者Linux和Mac 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文件由你决定。例如,你可以根据版本和构建号来重命名它们,或者连同你的源代码进行版本控制。

0 0