Android混淆工具——Proguard实践
ElizabethLi
8年前
<p>最近使用了一个非常高效和方便的混淆工具—— Proguard ,使用了这个工具混淆打包后,apk体积显著的减少了,而且反编译难度也加大了,所以写个博客记录一下这个混淆的过程。</p> <p>说说什么是 Proguard 吧。</p> <h3>Proguard 介绍</h3> <p>官网 的介绍是: ProGuard 是一个免费的 Java 类文件缩小,优化,混淆和预验证的工具。它检测和删除未使用的类,字段,方法和属性;优化字节码并删除未使用的指令;它使用短的无意义的名称重命名剩余的类,字段和方法。所得到的应用程序和库更小,更快,并且更好地针对逆向工程进行优化。</p> <p>而且 Proguard 已经集成在 Android studio 构建系统里了,可以通过简单的代码来实现构建apk的时候进行混淆打包。</p> <h3>Proguard 使用</h3> <p>首先,我们需要在项目里的 build.gradle 文件里配置 Proguard 。</p> <pre> <code class="language-java">buildTypes { release { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { shrinkResources true minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }</code></pre> <p>可以看见,分别在 release 和 debug 两种构建类型配置了 Proguard ,在 debug 配置是因为你可以直接编译测试你混淆的效果,是否有影响正常功能的使用。</p> <p>shrinkResources 是去除无效的资源文件,压缩资源。</p> <p>minifyEnabled 是开启混淆。</p> <p>这是默认的 Proguard 配置, proguard-rules.pro 是需要在你的项目里创建的文件,层级跟 build.gradle 文件一样的,当然你可以随意改他的文件名,只不过需要在配置代码里面跟着修改,其实这个文件就是 Proguard 的自定义配置文件, <strong>没有这个文件你构建是会报错的</strong> 。</p> <pre> <code class="language-java">proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'</code></pre> <h3>Proguard 基本语法</h3> <p>-keep 不混淆类和成员或者被重命名</p> <p>-keepnames 防止类和成员被重命名</p> <p>-keepclassmembers 不混淆成员被移除或者被重命名</p> <p>-keepnames 防止成员被重命名</p> <p>-keepclasseswithmembers 不混淆拥有该成员的类和成员或者被重命名</p> <p>-keepclasseswithmembernames 防止拥有该成员的类和成员被重命名</p> <p>而且还支持通配符*</p> <p>例如</p> <p>不混淆某个类</p> <pre> <code class="language-java">-keep public class com.shadow.example.abc { *; }</code></pre> <p>不混淆某个包</p> <pre> <code class="language-java">-keep public class com.shadow.example.** { *; }</code></pre> <p>不混淆某个类的子类</p> <pre> <code class="language-java">-keep public class * extends com.shadow.exampl.abc { *; }</code></pre> <p>了解这些基本语法后,我们就来看一下自定义配置,没有自定义混淆配置的话会构建出错的。</p> <p>Proguard 自定义配置</p> <p>接下来介绍一下自定义混淆配置</p> <p>以下是我在项目里使用的配置,我会在注释里说明。</p> <pre> <code class="language-java">//--- 基础混淆配置 --- -optimizationpasses 5 //指定代码的压缩级别 -allowaccessmodification //优化时允许访问并修改有修饰符的类和类的成员 -dontusemixedcaseclassnames //不使用大小写混合 -dontskipnonpubliclibraryclasses //指定不去忽略非公共库的类 -verbose //混淆时是否记录日志 -ignorewarnings //忽略警告,避免打包时某些警告出现,没有这个的话,构建报错 -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* //混淆时所采用的算法 -keepattributes *Annotation* //不混淆注解相关 -keepclasseswithmembernames class * { //保持 native 方法不被混淆 native <methods>; } -keepclassmembers enum * { //保持枚举 enum 类不被混淆 public static **[] values(); public static ** valueOf(java.lang.String); } //不混淆Parcelable -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } //不混淆Serializable -keep class * implements java.io.Serializable {*;} -keepnames class * implements java.io.Serializable -keepclassmembers class * implements java.io.Serializable {*;} -keepclassmembers class **.R$* { //不混淆R文件 public static <fields>; } //不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。 -dontpreverify -keepattributes Signature //过滤泛型 出现类型转换错误时,启用这个 //--- 不能被混淆的基类 --- -keep public class * extends android.app.Activity -keep public class * extends android.app.Fragment -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 * extends android.view.View -keep class org.xmlpull.v1.** { *; } //--- 不混淆android-support-v4包 --- -dontwarn android.support.v4.** -keep class android.support.v4.** { *; } -keep interface android.support.v4.app.** { *; } -keep class * extends android.support.v4.** { *; } -keep public class * extends android.support.v4.** -keep public class * extends android.support.v4.widget -keep class * extends android.support.v4.app.** {*;} -keep class * extends android.support.v4.view.** {*;} -keep public class * extends android.support.v4.app.Fragment //不混淆继承的support类 -keep public class * extends android.support.v4.** -keep public class * extends android.support.v7.** -keep public class * extends android.support.annotation.** //不混淆log -assumenosideeffects class android.util.Log { public static boolean isLoggable(java.lang.String, int); public static int v(...); public static int i(...); public static int w(...); public static int d(...); public static int e(...); } //保持Activity中参数类型为View的所有方法 -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } //--- 不混淆第三方库 这个可以去相关的第三方库官网找寻混淆代码 如果被混淆了会无法使用 --- //Gson -keepattributes *Annotation* -keep class sun.misc.Unsafe { *; } -keep class com.idea.fifaalarmclock.entity.*** -keep class com.google.gson.stream.** { *; } -keep class com.你的bean.** { *; } //OkHttp3 -dontwarn okhttp3.logging.** -keep class okhttp3.internal.**{*;} -dontwarn okio.** //Retrofit -dontwarn retrofit2.** -keep class retrofit2.** { *; } -keepattributes Signature -keepattributes Exceptions //RxJava RxAndroid -dontwarn sun.misc.** -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex; long consumerIndex; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { rx.internal.util.atomic.LinkedQueueNode producerNode; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { rx.internal.util.atomic.LinkedQueueNode consumerNode; } //微信 -keep class com.tencent.mm.** {*;} //Glide图片库 -keep class com.bumptech.glide.**{*;} //友盟 -keepclassmembers class * { public <init> (org.json.JSONObject); } -keep class com.umeng.onlineconfig.OnlineConfigAgent { public <fields>; public <methods>; } -keep class com.umeng.onlineconfig.OnlineConfigLog { public <fields>; public <methods>; } -keep interface com.umeng.onlineconfig.UmengOnlineConfigureListener { public <methods>; } //Testin -dontwarn com.testin.agent.** -keep class com.testin.agent.** {*;} //--- 一些特殊的混淆配置 --- //有用到WEBView的JS调用接口不被混淆 -keepclassmembers class fqcn.of.javascript.interface.for.webview { public *; } //对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆 -keepclassmembers class * { void *(**On*Event); void *(**On*Listener); } //抛出异常时保留代码行号 方便测试 -keepattributes SourceFile,LineNumberTable //不混淆我们自定义控件(继承自View) -keep public class * extends android.view.View{ *** get*(); void set*(***); public <init>(android.content.Context); public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); }</code></pre> <p>你用了以上的配置文件,我相信大部分项目都是没有问题的</p> <p>在初次使用 Proguard 实现混淆配置的时候,会出现很多很多的坑,如果遇上了问题,可以通过报错信息去谷歌一下,有可能会有很多你没想到遗漏,一开始我也是踩了好多坑。。</p> <p>不过,通过努力还是可以完成这个混淆实践的,混淆后的 apk 体积会让你惊讶的!</p> <p>值得试试</p> <h3>最后</h3> <p>这是一个简单的 Proguard 混淆实践,记录一下思路。</p> <p> </p> <p>来自:http://www.jianshu.com/p/ea223e0f3737</p> <p> </p>