Android代码混淆之混淆规则

来源:互联网 发布:java apache 框架 编辑:程序博客网 时间:2024/05/18 07:38

ProGuard

Android开发的应用程序是用Java语言开发的, 由于Java代码是非常容易反编码的,为了很好的保护Java源代码,我们需要对编译好后的class文件进行混淆。

ProGuard是一个免费的Java类文件收缩,优化,混淆和预校验器。它可以检测并删除未使用的类,字段,方法和属性。它可以优化字节码,并删除未使用的指令。它可以将类、字段和方法使用短无意义的名称进行重命名。这些步骤让代码更精简,更高效,也更难被逆向(破解)。

首先看下混淆前后的类:

混淆前的类

这里写图片描述

从上面的两张图可以看出没有用到的字段、方法以及属性混淆后被移除了。混淆环节会用无意义的短变量去重命名类、变量、方法。

ProGuard是如何判断代码有没有被利用呢?

这里引入一个Entry Point(入口点)概念,Entry Point是在ProGuard过程中不会被处理的类或方法。在压缩的步骤中,ProGuard会从上述的Entry Point开始递归遍历,搜索哪些类和类的成员在使用,对于没有被使用的类和类的成员,就会在压缩段丢弃,在接下来的优化过程中,那些非Entry Point的类、方法都会被设置为private、static或final,不使用的参数会被移除,此外,有些方法会被标记为内联的,在混淆的步骤中,ProGuard会对非Entry Point的类和方法进行重命名。那么这个入口点怎么来呢?就是从ProGuard的配置文件来,只要这个配置了,那么就不会被移除。

如何自定义一个ProGuard文件

基本指令:

# 代码混淆压缩比,在0和7之间,默认为5,一般不需要改-optimizationpasses 5 # 混淆时不使用大小写混合,混淆后的类名为小写-dontusemixedcaseclassnames# 指定不去忽略非公共的库的类-dontskipnonpubliclibraryclasses# 指定不去忽略非公共的库的类的成员-dontskipnonpubliclibraryclassmembers# 不做预校验,preverify是proguard的4个步骤之一# Android不需要preverify,去掉这一步可加快混淆速度-dontpreverify# 有了verbose这句话,混淆后就会生成映射文件# 包含有类名->混淆后类名的映射关系# 然后使用printmapping指定映射文件的名称-verbose-printmapping proguardMapping.txt# 指定混淆时采用的算法,后面的参数是一个过滤器# 这个过滤器是谷歌推荐的算法,一般不改变-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*# 保护代码中的Annotation不被混淆,这在JSON实体映射时非常重要,比如fastJson-keepattributes *Annotation*# 避免混淆泛型,这在JSON实体映射时非常重要,比如fastJson-keepattributes Signature# 抛出异常时保留代码行号,在异常分析中可以方便定位-keepattributes SourceFile,LineNumberTable# 用于告诉ProGuard,不要跳过对非公开类的处理。默认情况下是跳过的,因为程序中不会引用它们,有些情况下人们编写的代码与类库中的类在同一个包下,并且对包中内容加以引用,此时需要加入此条声明。-dontskipnonpubliclibraryclasses# 这个是给Microsoft Windows用户的,因为ProGuard假定使用的操作系统是能区分两个只是大小写不同的文件名,但是Microsoft Windows不是这样的操作系统,所以必须为ProGuard指定-dontusemixedcaseclassnames选项-dontusemixedcaseclassnames

需要保留的东西

# 保留所有的本地native方法不被混淆-keepclasseswithmembernames class * {    native <methods>;}# 保留了继承自Activity、Application这些类的子类# 因为这些子类,都有可能被外部调用# 比如说,第一行就保证了所有Activity的子类不要被混淆-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 * extends android.view.View-keep public class com.android.vending.licensing.ILicensingService# 如果有引用android-support-v4.jar包,可以添加下面这行-keep public class com.xxxx.app.ui.fragment.** {*;}# 保留在Activity中的方法参数是view的方法,# 从而我们在layout里面编写onClick就不会被影响-keepclassmembers class * extends android.app.Activity {    public void *(android.view.View);}# 枚举类不能被混淆-keepclassmembers enum * {public static **[] values();public static ** valueOf(java.lang.String);}# 保留自定义控件(继承自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);}# 保留Parcelable序列化的类不被混淆-keep class * implements android.os.Parcelable {    public static final android.os.Parcelable$Creator *;}# 保留Serializable序列化的类不被混淆-keepclassmembers class * implements java.io.Serializable {    static final long serialVersionUID;    private static final java.io.ObjectStreamField[] serialPersistentFields;    private void writeObject(java.io.ObjectOutputStream);    private void readObject(java.io.ObjectInputStream);    java.lang.Object writeReplace();    java.lang.Object readResolve();}# 对于R(资源)下的所有类及其方法,都不能被混淆-keep class **.R$* {    *;}# 对于带有回调函数onXXEvent的,不能被混淆-keepclassmembers class * {    void *(**On*Event);}

App 中的定制:

1、保留实体类和成员被混淆

-keep public class com.xxxx.entity.** {    public void set*(***);    public *** get*();    public *** is*();}

2、内部类

内部类经常会被混淆,结果在调用的时候为空就崩溃了,最好的解决方法就是把这个内部类拿出来,单独成为一个类。如果一定要内置,那么这个类就必须在混淆的时候保留,比如如下

-keep class com.example.xxx.MainActivity$* { *; }

3、WebView的处理

-keepclassmembers class * extends android.webViewClient {    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);    public boolean *(android.webkit.WebView, java.lang.String)}-keepclassmembers class * extends android.webkit.webViewClient {    public void *(android.webkit.webView, java.lang.String)}

4、JavaScript的处理

-keepclassmembers class com.example.xxx.MainActivity$JSInterface1 {    <methods>;}

5、对于自定义View的处理

但凡在Layout目录下的XML布局文件配置的自定义View,都不能进行混淆。为此要遍历Layout下的所有的XML布局文件,找到那些自定义View,然后确认其是否在ProGuard文件中保留。有一种思路是,在我们使用自定义View时,前面都必须加上我们的包名,比如com.a.b.customeview,我们可以遍历所有Layout下的XML布局文件,查找所有匹配com.a.b的标签即可。

针对第三方jar包的解决方案

我们在Android项目中不可避免要使用很多第三方提供的SDK,一般而言,这些SDK是经过ProGuard混淆的,而我们所需要做的就是避免这些SDK的类和方法在我们APP被混淆。

比如 Bugly

-dontwarn com.tencent.bugly.**-keep public class com.tencent.bugly.**{*;}

使用方法:

1、在app的目录下创建proguard-rules.pro(默认是存在的), 在这个文件中编写混淆规则。

2、在app目录下的build.gradle 中增加如下代码:

    buildTypes {        debug {            signingConfig signingConfigs.release        }        release {            // 混淆            minifyEnabled true            //加载默认混淆配置文件            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }

build/outputs/mapping/release 下的 proguard信息:

mapping.txt
表示混淆前后代码的对照表,这个文件非常重要。如果你的代码混淆后会产生bug的话,log提示中是混淆后的代码,希望定位到源代码的话就可以根据mapping.txt反推。
每次发布都要保留它方便该版本出现问题时调出日志进行排查,它可以根据版本号或是发布时间命名来保存或是放进代码版本控制中。

dump.txt
描述apk内所有class文件的内部结构。

seeds.txt
列出了没有被混淆的类和成员。

usage.txt
列出了源代码中被删除在apk中不存在的代码。

原创粉丝点击