Android_ProGuard一览天下

第一位博主

前言

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

 

声明

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

    博客园:http://www.cnblogs.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这一行来修复这些错误。例如:

-keeppublicclass<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 文件由你决定。例如,你可以根据版本和构建号来重命名它们,或者连同你的源代码进行版本控制。
 
 

第二位博主

 

先说点题外的,前一秒我心想干点什么呢,想到写篇博文吧,最近写的频率有点低了。另外标题有点长了。

进正文。

前些日子几次遇到ProGuard的问题,想偷个懒,没好好RTFM,后来通读了一下ProGuard的Manual,有点收获,总结一下。

主要是读了Usage部分,http://proguard.sourceforge.net/#manual/usage.html

命令:java -jar proguard.jar options ... 或 java -jar proguard.jar @myconfig.pro(myconfig.pro是配置文件)。Android提供的ant脚本把这个包含了进去,ADT也相应做了处理,所以基本不会直接用到这个。proguard包含在android sdk的tools目录下。

options或者配置文件设定了proguard的参数,分为Input/Output Options,Input/Output Options,Shrinking Options,Optimization Options,Obfuscation Options,Preverification Options,General Options 这些可选的参数。

从配置选项中其实可以看出,proguard有几部分的功能,Shrinking,Optimization,Obfuscation,Preverification。

1. Shrinking

就是缩减代码,他的工作是把代码中没被引用或者依赖的类、类成员删掉。可以通过Keep参数来设定保存。我之前就遇到这么一个问题,这里简称为“寻找getSomething游戏”(这个问题几乎会贯穿全文),我写了这么一段代码,大意如下:

public class JavascriptInterface {
    void getSomething(String something) {}
}

webview.addJavascriptObject(new JavascriptInterface(), "jsi");
webview.loadUrl("javascript:window.jsi.getSomething("hello")");

就是通过js给java传个值。然后打包运行正常,再然后使用proguard处理打包,结果不正常,提示大意为object没有getSomething方法。

出了什么问题呢,因为android的proguard默认配置是开启Shrinking的,所以结果是getSomething(String)方法被删掉了,我通过反编译打出来的apk包也证实了这一点。

解决方法是,在配置文件中加入keep

-keep public class yourpackagename.JavascriptInterface

对于keep有几个类似的选项,下面的表格抄自:http://proguard.sourceforge.net/#manual/usage.html

Keep
From being removed or renamed From being renamed
Classes and class members -keep -keepnames
Class members only -keepclassmembers -keepclassmembernames
Classes and class members, if class members present -keepclasseswithmembers -keepclasseswithmembernames

我就不解释了。

2. Optimization

代码优化,说是bytecode层级的优化,具体怎么优化的我就不知道,而且Android默认配置也没开起这个。

3. Obfuscation

这个就是传说中的混淆了,可以通过-dontobfuscate关闭(关闭为什么还用proguard呢?)什么是混淆呢,就是把类和类成员 (包括变量和函数)的名字替换成相应的随机字符,大大增加别人解包破解你代码的难度。混淆过程生成mapping文件,记录每个类和成员被替换称什么随机字符了,也可以自己提供一些生成随机字符的规则,这里提供了很多选项,很有意思。

前面说道“寻找getSomething游戏”的例子,其实象刚才那样做并没有搞定这个问题,这里一个重要的概念是,单写一行keep并不能让proguard不做成员变量的混淆处理,而只是不被删掉。所以getSomething作为一个类成员方法依然会被混淆,变成了一个随机字符,比如a,那么这句代码:webview.loadUrl("javascript:window.jsi.getSomething("hello")"); 显然不再能正常使用了,因为它已经找不到getSomething了。

过程中我反复解包,由于认为keep就可以防止混淆,觉得无解,因我解包发现每次getSomething都变成了a,所以进行了一种很狗屎的方法的尝试,将webview.loadUrl("javascript:window.jsi.getSomething("hello")");换成webview.loadUrl("javascript:window.jsi.a("hello")");,把getSomething直接写成了a。。。但是由于我的实际JavascriptInterface类里面还有一些别的东西,有些东西也会被混淆成a,所以居然连狗屎运都没有。

后来我发现-useuniqueclassmembernames这个参数,顾名思义,可以让类成员使用唯一的名字,于是我给getSomething改成了后来混淆后的唯一的名字,终于在寻找getSomething的游戏中,找到了它,虽然它已经不叫getSomething了。。。

还是觉得这个方法太屎了,胜之不武。

关于keep有个复杂的语法,下面依然抄自ProGuard文档。

[@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 ... ] *;
    ...
}]

。。。

我之前看了这段就跳过了。后来耐心看下还是很容易看懂的,而且这个部分后面有个说明。

这些符号其实很常见了,“|” 表示或关系,“!”表示非,“[]”表示可选,“...”代表等等,黑色的部分是关键字。

之前-keep public class yourpackagename.JavascriptInterface 这样写,注意语法有个花括号,里面是用来说明成员变量是否keep的,可以写一个范围,比如写<fields>就是所有字段不被混淆,<methods>就是所有方法不被混淆,*就是所有都不被混淆。还可以单独指出哪个函数不被混淆,比如对于“寻找getSomething游戏”,可以这样写:

-keep public class yourpackagename.JavascriptInterface {
  void getSomething(java.lang.String)
}

这样,getSomething函数就不会被混淆了。这里一个值得注意的问题是,所有类都要写全称,就是包名.类名,String要写成java.lang.String,我最开始就只写了String,结果是还别混淆了,郁闷了很久。

好了,“寻找getSomething游戏”完胜了。。。它再也不会找不到getSomething了。

4. Preverification

预验证,在载入类之前的验证,《Android前向兼容的几个问题》里面说的大概是这个,Android的ProGuard配置也没有开启这个,我也不是很清楚,就不说了。

第三位博主

 

Android代码混淆导致内部类不可用

Android代码混淆导致内部类不可用    
  • 5.00 / 5                     5          
  • 1 / 5
  • 2 / 5
  • 3 / 5
  • 4 / 5
  • 5 / 5
1 vote, 5.00 avg. rating ( 91% score) 

webview下调用js的时候的一个内部类,代码混淆造成无法调用js

这里用的方法是保留内部类不被混淆

修改proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt

添加内容

-keep class com.zl.proguarddemo.MainActivity$Inner{

    public <fields>;

    public <methods>;

}

Inner为MainActivity的内部类(注意类路径名写全!!!)

如果内部类的方法想不被优化掉,必须在代码中调用一下,否则还是会被优化掉。

第四位博主

当前是有些工具比如apktool,dextojar等是可以对我们android安装包进行反编译,获得源码的。为了减少被别人破解,导致源码泄露,程序被别人盗取代码,等等。我们需要对代码进行混淆,android的sdk中为我们提供了ProGrard这个工具,可以对代码进行混淆(一般是用无意义的名字来重命名),以及去除没有使用到的代码,对程序进行优化和压缩,这样可以增加你想的难度。最近我做的项目,是我去配置的混淆配置,因此研究了一下,这里分享一下。

如何启用ProGuard

ant项目和eclipse项目启用方法

在项目的project.properties文件中添加一下代码      

proguard.config=proguard.cfg  //proguard.cfg为proguard的配置文件
proguard.config=/path/to/proguard.cfg //路径不在项目根目录时,填写实际路径

填写这句配置后,在release打包时就会按照我们的配置进行混淆,注意,在我们平时的debug时是不会进行混淆的。

Gradle项目(以及Android Studio)

在build.gradle中进行配置

android {
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),'some-other-rules.txt'
            //proguardFile 'some-other-rules.txt'  配置单个文件这样
        }
    }
}

如上面代码所示,我们可以使用runProguard true开启,并且对其配置混淆配置,可以配置多个文件或单个文件。

android的sdk中已经为我们提供了两个默认的配置文件,我们可以拿过来进行使用,proguard-android.txt和proguard-android-optimize.txt。

ProGuard配置

上面说到android为我们提供了两个默认的配置文件,在其中,我们可以看到他的一些语法。本节进行描述。

保留选项(配置不进行处理的内容)

-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    不混淆输入的类文件
-obfuscationdictionary {filename}    使用给定文件中的关键字作为要混淆方法的名称
-overloadaggressively    混淆时应用侵入式重载
-useuniqueclassmembernames    确定统一的混淆类的成员名称来增加混淆
-flattenpackagehierarchy {package_name}    重新包装所有重命名的包并放在给定的单一包中
-repackageclass {package_name}    重新包装所有重命名的类文件中放在给定的单一包中
-dontusemixedcaseclassnames    混淆时不会产生形形色色的类名
-keepattributes {attribute_name,...}    保护给定的可选属性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.
-renamesourcefileattribute {string}    设置源文件中给定的字符串常量 

后面的文件名,类名,或者包名等可以使用占位符代替
?表示一个字符
可以匹配多个字符,但是如果是一个类,不会匹配其前面的包名
*
可以匹配多个字符,会匹配前面的包名。

在android中在android Manifest文件中的activity,service,provider, receviter,等都不能进行混淆。一些在xml中配置的view也不能进行混淆,android提供的默认配置中都有。      

ProGuard的输出文件及用处

混淆之后,会给我们输出一些文件,在gradle方式下是在<project_dir>/build/proguard/目录下,ant是在<project_dir>/bin/proguard目录,eclipse构建在<project_dir>/proguard目录像。
分别有以下文件:
+ dump.txt 描述apk文件中所有类文件间的内部结构。
+ mapping.txt 列出了原始的类,方法,和字段名与混淆后代码之间的映射。
+ seeds.txt 列出了未被混淆的类和成员
+ usage.txt 列出了从apk中删除的代码

当我们发布的release版本的程序出现bug时,可以通过以上文件(特别时mapping.txt)文件找到错误原始的位置,进行bug修改。同时,可能一开始的proguard配置有错误,也可以通过错误日志,根据这些文件,找到哪些文件不应该混淆,从而修改proguard的配置。

注意:重新release编译后,这些文件会被覆盖,所以没吃发布程序,最好都保存一份配置文件。

调试Proguard混淆后的程序

上面说了输出的几个文件,我们在改bug时可以使用,通过mapping.txt,通过映射关系找到对应的类,方法,字段等。        

另外Proguard文件中包含retrace脚本,可以将一个被混淆过的堆栈跟踪信息还原成一个可读的信息,window下时retrace.bat,linux和mac是retrace.sh,在<sdk_root>/tools/proguard/文件夹下。语法为:    

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

例如:

retrace.bat -verbose mapping.txt obfuscated_trace.txt

如果你没有指定<stacktrace_file>,retrace工具会从标准输入读取。

一些常用包的Proguard配置

下面再写一些我在项目中使用到的一些第三方包需要单独配置的混淆配置

sharesdk混淆注意

-keep class android.net.http.SslError
-keep class android.webkit.**{*;}
-keep class cn.sharesdk.**{*;}
-keep class com.sina.**{*;}
-keep class m.framework.**{*;}

Gson混淆配置

-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.idea.fifaalarmclock.entity.***
-keep class com.google.gson.stream.** { *; }

Umeng sdk混淆配置

-keepclassmembers class * {
   public <init>(org.json.JSONObject);
}

-keep class com.umeng.**

-keep public class com.idea.fifaalarmclock.app.R$*{
    public static final int *;
}

-keep public class com.umeng.fb.ui.ThreadView {
}

-dontwarn com.umeng.**

-dontwarn org.apache.commons.**

-keep public class * extends com.umeng.**

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

关于配置方面,我写的不够详细,可以去看参考资料第二条,proguard官方文档。也欢迎大家交流使用遇到的问题和心得。

 

 

你可能感兴趣的:(Android_ProGuard一览天下)