美文网首页
Android代码混淆

Android代码混淆

作者: 涛涛123759 | 来源:发表于2020-08-05 16:09 被阅读0次

    一、配置( 项目的app/build.gradle)

        buildTypes {
            release {
                /*打开混淆*/
                minifyEnabled true
                /*打开资源压缩*/
                shrinkResources true
                zipAlignEnabled true // Zipalign优化 
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    

    二、混淆为什么要保留类名或方法名?

    • 1、让C/C++程序可以通过jni使用对应的java方法
    • 2、四大组件由于在AndroidManifest.xml里面注册了,所以需要保留。
    • 3、R文件混淆会导致引用错误。
    • 4、第三方架包有的已经经过混淆了,再次混淆会导致找不到类名或者方法名

    三、什么时候不被混淆?

    一般以下情况都会不混淆:

    • 1、使用了自定义控件那么要保证它们不参与混淆
    • 2、使用了枚举要保证枚举不被混淆
    • 3、对第三方库中的类不进行混淆
    • 4、运用了反射的类也不进行混淆
    • 5、使用了 Gson 之类的工具要使 JavaBean 类即实体类不被混淆
    • 6、静态成员JNI中调用的类
    • 7、有用到 WebViewJS 调用也需要保证写的接口方法不混淆,原因和第一条一样
    • 8、Serializable,Parcelable的子类和 Creator 静态成员变量不混淆,否则会产生 Android.os.BadParcelableException 异常
    • 9、四大组件自定义的Application

    四、混淆语法

    • 2.1 基本规则
      两个常用的混淆命令,注意一颗星表示只是保持该包下的类名,而子包下的类名还是会被混淆;而两颗星表示把本包和所包含的子包下的类名都保留。
    -keep class cn.hadcn.test.**
    -keep class cn.hadcn.test.*
    

    如果既想保持类名,又想保持里面的内容不被混淆,就执行以下方法

     -keep class com.example.bean.** { *; }
    

    在此基础上,我们也可以使用Java的基本规则来保护特定类不被混淆,比如我们可以用extend,implement等这些Java规则。如下例子就避免所有继承Activity的类被混淆

    保留我们使用的四大组件,自定义的Application等等这些类不被混淆
    因为这些子类都有可能被外部调用

    -keep public class * extends android.app.Activity
    -keep public class * extends android.app.Appliction
    -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
    
    • 2.2、基本混淆模板
      对于一些基本指令的添加
    #############################################
    #
    # 对于一些基本指令的添加
    #
    #############################################
    # 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
    -optimizationpasses 5
    # 混合时不使用大小写混合,混合后的类名为小写
    -dontusemixedcaseclassnames
    # 指定不去忽略非公共库的类
    -dontskipnonpubliclibraryclasses
    # 这句话能够使我们的项目混淆后产生映射文件# 包含有类名->混淆后类名的映射关系
    -verbose
    # 指定不去忽略非公共库的类成员
    -dontskipnonpubliclibraryclassmembers
    # 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
    -dontpreverify
    # 保留Annotation不混淆
    -keepattributes *Annotation*,InnerClasses
    # 避免混淆泛型
    -keepattributes Signature
    #将文件来源重命名为“SourceFile”字符串
    -renamesourcefileattribute SourceFile
    # 抛出异常时保留代码行号
    -keepattributes SourceFile,LineNumberTable
    # 避免混淆Annotation、内部类、泛型、匿名类
    -keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod
    # 指定混淆是采用的算法,后面的参数是一个过滤器# 这个过滤器是谷歌推荐的算法,一般不做更改
    -optimizations !code/simplification/cast,!field/*,!class/merging/*
    #把混淆类中的方法名也混淆了
    -useuniqueclassmembernames
    #优化时允许访问并修改有修饰符的类和类的成员
    -allowaccessmodification
    

    Android开发中一些需要保留的公共部分

    #############################################
    #
    # Android开发中一些需要保留的公共部分
    #
    #############################################
    # 保留我们使用的四大组件,自定义的Application等等这些类不被混淆# 因为这些子类都有可能被外部调用
    -keep public class * extends android.app.Activity
    -keep public class * extends android.app.Appliction
    -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
    
    # 保留support下的所有类及其内部类
    -keep class android.support.** {*;}
    # 保留继承的
    -keep public class * extends android.support.v4.**
    -keep public class * extends android.support.v7.**
    -keep public class * extends android.support.annotation.**
    # 保留R下面的资源
    -keep class **.R$* {*;}
    # 保留本地native方法不被混淆
    -keepclasseswithmembernames class * {
        native <methods>;
    }
    # 保留在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;
        !static !transient <fields>;
        !private <fields>;
        !private <methods>;
        private void writeObject(java.io.ObjectOutputStream);
        private void readObject(java.io.ObjectInputStream);
        java.lang.Object writeReplace();
        java.lang.Object readResolve();
    }
    # 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
    -keepclassmembers class * {
        void *(**On*Event);
        void *(**On*Listener);
    }
    # webView处理,项目中没有使用到webView忽略即可
    -keepclassmembers class fqcn.of.javascript.interface.for.webview {
        public *;
    }
    -keepclassmembers class * extends android.webkit.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, jav.lang.String);
    }
    
    # assume no side effects:删除android.util.Log输出的日志
    -assumenosideeffects class android.util.Log {
        public static *** v(...);
        public static *** d(...);
        public static *** i(...);
        public static *** w(...);
        public static *** e(...);
    }
    
    #保留Keep注解的类名和方法
    -keep,allowobfuscation @interface android.support.annotation.Keep
    -keep @android.support.annotation.Keep class *
    -keepclassmembers class * {
        @android.support.annotation.Keep *;
    }
    
    #fastjson混淆
    -keepattributes Signature
    -dontwarn com.alibaba.fastjson.**
    -keep class com.alibaba.**{*;}
    -keep class com.alibaba.fastjson.**{*; }
    -keep public class com.ninstarscf.ld.model.entity.**{*;}
    
    # 保持测试相关的代码
    -dontnote junit.framework.**
    -dontnote junit.runner.**
    -dontwarn android.test.**
    -dontwarn android.support.test.**
    -dontwarn org.junit.*
    
    # webview
    -keep class android.webkit.JavascriptInterface {*;}
    #自定义webview接口实现类
    -keep public class com.gjmetal.app.ui.ball.**{*;}
    
    # 微信支付
    -dontwarn com.tencent.mm.**
    -dontwarn com.tencent.wxop.stat.**
    -keep class com.tencent.mm.** {*;}
    -keep class com.tencent.wxop.stat.**{*;}
    
    # 支付宝钱包
    -dontwarn com.alipay.**
    -dontwarn HttpUtils.HttpFetcher
    -dontwarn com.ta.utdid2.**
    -dontwarn com.ut.device.**
    -keep class com.alipay.android.app.IAlixPay{*;}
    -keep class com.alipay.android.app.IAlixPay$Stub{*;}
    -keep class com.alipay.android.app.IRemoteServiceCallback{*;}
    -keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
    -keep class com.alipay.sdk.app.PayTask{ public *;}
    -keep class com.alipay.sdk.app.AuthTask{ public *;}
    -keep class com.alipay.mobilesecuritysdk.*
    -keep class com.ut.*
    
    

    五、其他混淆

    然后需要在项目生成的混淆脚本中添加过滤混淆的条件

    # Glide
    -dontwarn com.bumptech.glide.**
    -keep class com.bumptech.glide.**{*;}
    -keep public class * implements com.bumptech.glide.module.GlideModule
    -keep public class * extends com.bumptech.glide.AppGlideModule
    -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
      **[] $VALUES;
      public *;
    }
    
    # Gson
    -keep class sun.misc.Unsafe { *; }
    -keep class com.google.gson.stream.** { *; }
    # OkHttp3
    -keep class okhttp3.** { *; }
    -keep interface okhttp3.** { *; }
    -dontwarn okhttp3.**
    # Okio
    -dontwarn com.squareup.**
    -dontwarn okio.**
    -keep public class org.codehaus.* { *; }
    -keep public class java.nio.* { *; }
    # 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.seekfangle.pad.bean.* {*;}
    #第三方架包
    -keep class  cn.com.** { *;}
    -keep class  android_serialport_api.** { *;}
    
    #butterknife
    -dontwarn butterknife.internal.**
    -keep class **$$ViewInjector { *; }
    -keepnames class * { @butterknife.InjectView *;}
    
    #EventBus3.0
    -keepattributes *Annotation*
    -keepclassmembers class ** {
        @org.greenrobot.eventbus.Subscribe <methods>;
    }
    -keep enum org.greenrobot.eventbus.ThreadMode { *; }
    
    #友盟
    -keep class com.umeng.** {*;}
    -keepclassmembers class * {
       public <init> (org.json.JSONObject);
    }
    -keepclassmembers enum * {
        public static **[] values();
        public static ** valueOf(java.lang.String);
    }
    -keep public class com.gjmetal.app.R$*{
    public static final int *;
    }
    
    #个推
    -dontwarn com.igexin.**
    -keep class com.igexin.** { *; }
    -keep class org.json.** { *; }
    -keep class android.support.v4.app.NotificationCompat { *; }
    -keep class android.support.v4.app.NotificationCompat$Builder { *; }
    
    #routes
    -keep public class com.alibaba.android.arouter.routes.**{*;}
    -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
    
    
    #ShareSdk
    -keep class cn.sharesdk.**{*;}
    -keep class com.sina.**{*;}
    -keep class **.R$* {*;}
    -keep class **.R{*;}
    -keep class com.mob.**{*;}
    -keep class m.framework.**{*;}
    -dontwarn cn.sharesdk.**
    -dontwarn com.sina.**
    -dontwarn com.mob.**
    -dontwarn **.R$*
    
    #Picasso
    -keep class com.parse.*{ *; }
    -dontwarn com.parse.**
    -dontwarn com.squareup.picasso.**
    -keepclasseswithmembernames class * {
        native <methods>;
    }
    
    #GreenDao
    -keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
    public static java.lang.String TABLENAME;
    }
    -keep class **$Properties
    
    # If you do not use SQLCipher:
    -dontwarn org.greenrobot.greendao.database.**
    # If you do not use Rx:
    -dontwarn rx.**
    
    #百度定位混淆配置
    -keep class vi.com.gdi.** { *; }
    -keep public class com.baidu.** {*;}
    -keep public class com.mobclick.** {*;}
    -dontwarn com.baidu.mapapi.utils.*
    -dontwarn com.baidu.platform.comapi.b.*
    -dontwarn com.baidu.platform.comapi.map.*
    #百度地图混淆配置
    -keep class com.baidu.** {*;}
    -keep class vi.com.** {*;}
    -dontwarn com.baidu.**
    -keep class mapsdkvi.com.** {*;}
    
    #QQ分享
    -dontwarn com.tencent.**
    -keep class com.tencent.** {*; }
    
    #微信分享
    -keep class com.tencent.mm.opensdk.** {*;}
    -keep class com.tencent.wxop.** {*;}
    -keep class com.tencent.mm.sdk.** { *;}
    

    六、检查混淆结果

    混淆过的包必须进行检查,避免因混淆引入的bug。
    一方面,需要从代码层面检查。使用上文的配置进行混淆打包后在<module-name>/build/outputs/mapping/release/目录下会输出以下文件:
    dump.txt
    描述APK文件中所有类的内部结构
    mapping.txt
    提供混淆前后类、方法、类成员等的对照表
    seeds.txt
    列出没有被混淆的类和成员
    usage.txt
    列出被移除的代码
    我们可以根据 seeds.txt 文件检查未被混淆的类和成员中是否已包含所有期望保留的,再根据 usage.txt文件查看是否有被误移除的代码。
    另一方面,需要从测试方面检查。将混淆过的包进行全方面测试,检查是否有 bug 产生。

    七、解出混淆栈

    混淆后的类、方法名等等难以阅读,这固然会增加逆向工程的难度,但对追踪线上 crash 也造成了阻碍。我们拿到 crash 的堆栈信息后会发现很难定位,这时需要将混淆反解。
    <sdk-root>/tools/proguard/路径下有附带的的反解工具(Window 系统为proguardgui.bat,Mac 或 Linux 系统为proguardgui.sh)。
    这里以 Window 平台为例。双击运行 proguardgui.bat 后,可以看到左侧的一行菜单。点击 ReTrace,选择该混淆包对应的 mapping 文件(混淆后在 <module-name>/build/outputs/mapping/release/路径下会生成 mapping.txt 文件,它的作用是提供混淆前后类、方法、类成员等的对照表),再将 crash 的 stack trace 黏贴进输入框中,点击右下角的 ReTrace ,混淆后的堆栈信息就显示出来了。
    以上使用 GUI 程序进行操作,另一种方式是利用该路径下的 retrace 工具通过命令行进行反解,命令是
    retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
    例如:

    retrace.bat -verbose mapping.txt obfuscated_trace.txt
    注意事项:
    所有在 AndroidManifest.xml涉及到的类已经自动被保持,因此不用特意去添加这块混淆规则。(很多老的混淆文件里会加,现在已经没必要)
    proguard-android.txt已经存在一些默认混淆规则,没必要在proguard-rules.pro 重复添加

    八、自定义混淆规则

    在上文“混淆配置”中有这样一行代码

    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    

    这行代码定义了混淆规则由两部分构成:位于 SDK 的tools/proguard/ 文件夹中的 proguard-android.txt的内容以及默认放置于模块根目录的proguard-rules.pro 的内容。前者是 SDK 提供的默认混淆文件,后者是开发者自定义混淆规则的地方。

    九、常见的混淆指令

    • 1、 optimizationpasses代码混淆压缩比,在0~7之间,默认为5,一般不做修改
    • 2、 dontoptimize 不进行优化
    • 3、 dontusemixedcaseclassnames 混合时不使用大小写混合,混合后的类名为小写
    • 4、 dontskipnonpubliclibraryclasses 指定不去忽略非公共库的类
    • 5、 dontpreverify 不做预校验
    • 6、 dontwarn 不提示警告,和keep可以说是形影不离,尤其是处理引入的library时.
    • 7、 verbose 混淆时是否记录日志
    • 8、 optimizations 指定混淆是采用的算法,后面的参数是一个过滤器
    • 9、 keep 保留类和类中的成员,防止被混淆或移除
    • 10、keepnames 保留类和类中的成员,防止被混淆,成员没有被引用会被移除
    • 11、 keepclassmembers 只保留类中的成员,防止被混淆或移除
    • 12、keepclassmembernames 只保留类中的成员,防止被混淆,成员没有引用会被移除
    • 13、 keepclasseswithmembers 保留类和类中的成员,防止被混淆或移除,保留指明的成员
    • 14、 keepclasseswithmembernames 保留类和类中的成员,防止被混淆,保留指明的成员,成员没有引用会被移除
      更多详细的请到官网

    十、规则

      [保持命令] [类] {
        [成员] 
    }
    

    “类”代表类相关的限定条件,它将最终定位到某些符合该限定条件的类。它的内容可以使用:

    • 具体的类
    • 访问修饰符public、protected、private
    • 通配符*,匹配任意长度字符,但不含包名分隔符(.)
    • 通配符**,匹配任意长度字符,并且包含包名分隔符(.)
    • extends,即可以指定类的基类
      *implement,匹配实现了某接口的类
    • $,内部类

    “成员”代表类成员相关的限定条件,它将最终定位到某些符合该限定条件的类成员。它的内容可以使用:

    • <init>匹配所有构造器
    • <fields> 匹配类中的所有字段
    • <methods> 匹配所有方法
    • 通配符*,匹配任意长度字符,但不含包名分隔符(.)
    • 通配符**,匹配任意长度字符,并且包含包名分隔符(.)
    • 通配符***,匹配任意参数类型
      *,匹配任意长度的任意类型参数。比如void test(…)就能匹配任意void test(String a)或者是 void test(int a, String b)这些方法。
    • 访问修饰符public、protected、private

    举个例子,假如需要将com.biaobiao.test包下所有继承Activity的public类及其构造函数都保持住,可以这样写:

     -keep public class com.biaobiao.test.** extends Android.app.Activity {
        <init>
    }
    

    十一、常用自定义混淆规则

    1、不混淆某个类

    -keep public class com.biaobiao.example.Test { *; }
    

    2、不混淆某个包所有的类

    -keep class com.biaobiao.test.** { *; }
    

    3、不混淆某个类的子类

    -keep public class * extends com.biaobiao.example.Test { *; }
    

    4、不混淆某个接口的实现

    -keep class * implements com.biaobiao.example.TestInterface { *; }
    

    5、不混淆某个类的构造方法

    -keepclassmembers class com.biaobiao.example.Test { 
        public <init>(); 
    }
    //保持指定类的所有字段
    -keep class com.xy.myapp.MyClass { public <fields>; }
    

    6、不混淆某个类的特定的方法

    -keepclassmembers class com.biaobiao.example.Test { 
        public void test(java.lang.String); 
    }
    //保持指定类的所有方法
    -keep class com.xy.myapp.MyClass { public <methods>; }
    //保持用指定参数作为形参的方法
    -keep class com.xy.myapp.MyClass { public <methods>(java.lang.String); }
    

    7、不混淆某个类的内部类

    //所有内部类
    -keep class com.biaobiao.example.Test$* { *; }
    //内部类 Builder
    -keep class com.biaobiao.example.Test$Builder { *; }
    //保持MyClass内部类JavaScriptInterface中的所有public内容。
    -keepclassmembers class com.xy.myapp.MyClass$JavaScriptInterface { 
        #保持该类下所有的共有内容不被混淆
        public *;
        #保持该类下所有的私有方法不被混淆
        private * ;
     }
    

    8、混淆引入的 library

    //使指定的类不输出警告信息
    -dontwarn purang.purang_shop.**
    -keep class purang.purang_shop.**{*;}
    

    十二、组件模块定义混淆规则

    子模块混淆文件的指定是通过consumerProguardFiles这个属性来指定的,并不是proguardFiles属性,而且我们无需配置其他的选项,只需要配置consumerProguardFiles属性就可以。该属性表示在打包的时候会自动寻找该module下我们指定的混淆文件对代码进行混淆。可以把一些公共的混淆定义在子模块中。

       release {
                consumerProguardFiles   'proguard-rules.pro'
            }
    

    consumerProguardFiles作用:

    • proguard.txt会被打包进aar中
    • 此配置只对aar进行混淆。
    • 此配置只对库文件有效,对应用程序无效。

    当app模块将全部代码汇总混淆时,Library 模块会被打包为release aar,然后被引用汇总,通过proguard-rule.pro规则各自混淆,保证只混淆一次。

    组件化工程中具体混淆用法:
    我们可以将固定的第三方混淆放到base模块的proguard-rule.pro文件中,每个模块独有的第三方引用库混淆放到各自的proguard-rule.pro文件中,在app模块的proguard-rule.pro文件中放入Android基础属性的混淆声明,如四大组件和全局的混淆等配置。这样可以最大限度的完成混淆解耦操作。

    十三、自定义要保持的资源

    资源压缩包含了“合并资源”和“移除资源”两个流程。“合并资源”流程中,名称相同的资源被视为重复资源会被合并。需要注意的是,这一流程不受shrinkResources属性控制,也无法被禁止, gradle 必然会做这项工作。
    当我们开启了资源压缩之后,系统会默认替我们移除所有未使用的资源,假如我们需要保留某些特定的资源,可以在我们项目中创建一个被 <resources> 标记的 XML 文件(如 res/raw/keep.xml),并在tools:keep属性中指定每个要保留的资源,在tools:discard属性中指定每个要舍弃的资源。这两个属性都接受逗号分隔的资源名称列表。同样,我们可以使用字符 * 作为通配符。

    <?xml version="1.0" encoding="utf-8"?><resources xmlns:tools="http://schemas.android.com/tools"
        tools:keep="@layout/activity_video*,@layout/dialog_update_v2"
        tools:discard="@layout/unused_layout,@drawable/unused_selector" />
    

    十四、移除替代资源

    一些替代资源,例如多语言支持的 strings.xml,多分辨率支持的 layout.xml 等,在我们不需要使用又不想删除掉时,可以使用资源压缩将它们移除。我们使用 resConfig 属性来指定需要支持的属性,例如

      android {
          defaultConfig {
              ...
              resConfigs "en", "fr"
          }
      }
    

    相关文章

      网友评论

          本文标题:Android代码混淆

          本文链接:https://www.haomeiwen.com/subject/refsrktx.html