美文网首页2022
Android 混淆介绍

Android 混淆介绍

作者: mumuxi_ | 来源:发表于2020-08-13 22:27 被阅读0次

    目录:
    一、启用混淆文件
    二、ProGuard作用
    三、编辑混淆文件 (五步搞定)
    四、常用混淆写法
    五、其他一些混淆注意事项

    一、启用混淆文件

    开发中直接使用Android Studio即可实现启用混淆文件。Android Studio自身集成Java语言的ProGuard作为压缩,优化和混淆工具,配合Gradle构建工具使用很简单,只需要在工程应用目录的gradle文件中设置minifyEnabled为true即可。然后我们就可以到proguard-rules.pro文件中加入我们的混淆规则。

    buildTypes {
            release {
                minifyEnabled true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
    }
    

    二、ProGuard作用

    • 压缩(Shrinking):默认开启,用以减小应用体积,移除未被使用的类和成员,会在优化动作执行之后再次执行。
    • 优化(Optimization):默认开启,在字节码级别执行优化,让应用运行的更快。
    • 混淆(Obfuscation):默认开启,增大反编译难度,类和类成员会被随机命名,除非用keep保护

    三、编辑混淆文件(五步搞定)

    1.第一步
    打开项目中的proguard-rules.pro,将下面的代码复制进去。我的思路是将混淆分为2个主要部分

    • 定制化区域。这里边的内容是我们主要需要补充的部分,大致分为4个小部分,我们接下来的步骤主要是补充这4个部分的东西。

    • 基本不用动区域。顾名思义,基本不用动。这块的内容我已经写好了,除非有特殊的需求,基本不用改动。

    #-------------------------------------------定制化区域----------------------------------------------
    
    #---------------------------------1.实体类---------------------------------
    
    #-------------------------------------------------------------------------
    
    #---------------------------------2.第三方包-------------------------------
    
    #-------------------------------------------------------------------------
    
    #---------------------------------3.与js互相调用的类------------------------
    
    #-------------------------------------------------------------------------
    
    #---------------------------------4.反射相关的类和方法-----------------------
    
    #----------------------------------------------------------------------------
    
    #-------------------------------------------基本不用动区域--------------------------------------------
    
    #---------------------------------基本指令区----------------------------------
    
    # 代码混淆压缩比,在0~7之间
    -optimizationpasses 5
    
    # 混合时不使用大小写混合,混合后的类名为小写
    -dontusemixedcaseclassnames
    
    # 指定不去忽略非公共库的类
    -dontskipnonpubliclibraryclasses
    
    # 指定不忽略包可见的库类成员(字段和方法)。默认地,当解析库类的时候ProGuard会跳过这些类成员,项目类一般不会去引用它们。然而有的时候,程序里的类相当于库类存在于相同包。此时它们会引用他们的包可见的类成员。在这种情况下为了保持程序代码保持一致性去读取这些类的成员是有用的。
    -dontskipnonpubliclibraryclassmembers
    
    # 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
    -dontpreverify
    
    指定处理期间打印更多相关信息。
    -verbose
    
    -printmapping proguardMapping.txt
    
    #指定要启用和禁用的优化,在更精细的水平。只有当优化适用。 混淆时采用的算法(google推荐,一般不改变)
    -optimizations !code/simplification/cast,!field/*,!class/merging/*
    
    # 避免混淆Annotation、内部类
    -keepattributes *Annotation*,InnerClasses
    
    # 避免混淆泛型
    -keepattributes Signature
    
    # 抛出异常时保留代码行号
    -keepattributes SourceFile,LineNumberTable
    
    #----------------------------------------------------------------------------
    
    #---------------------------------默认保留区---------------------------------
    
    -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
    
    -keep class android.support.** {*;}
    
    -keepclasseswithmembernames class * {
    
        native <methods>;
    
    }
    
    -keepclassmembers class * extends android.app.Activity{
    
        public void *(android.view.View);
    
    }
    
    -keepclassmembers enum * {
    
        public static **[] values();
    
        public static ** valueOf(java.lang.String);
    
    }
    
    -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);
    
    }
    
    -keepclasseswithmembers class * {
    
        public <init>(android.content.Context, android.util.AttributeSet);
    
        public <init>(android.content.Context, android.util.AttributeSet, int);
    
    }
    
    -keep class * implements android.os.Parcelable {
    
      public static final android.os.Parcelable$Creator *;
    
    }
    
    -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();
    
    }
    
    -keep class **.R$* {
    
    *;
    
    }
    
    -keepclassmembers class * {
    
        void *(**On*Event);
    
    }
    
    #----------------------------------------------------------------------------
    
    #---------------------------------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);
    
    }
    
    #----------------------------------------------------------------------------
    

    2.第二步
    理解完战略级思想后,我们开始第一部分补充-实体类。实体类由于涉及到与服务端的交互,各种gson的交互如此等等,是要保留的。将你项目中实体类都拎出来,用以下语法进行保留。如果项目本身就是一个sdk,那么需要不混淆需要直接提供给第三方使用的类。

    #不混淆某个类
    -keep public class com.mumuxi.example.Test { *; }
    
    #不混淆某个包的所有类
    -keep class 你的实体类所在的包.** { *; }
    -keep class com.mumuxi.test.** { *; }
    
    #不混淆某个类的内部类
    keep class com.mumuxi.example.Test$* { *;}
    

    当然你的实体类肯定不止这一个,请用上边的方式一一添加,如果你的实体类都在一个包下,那你就幸福了。

    3.第三步
    第2部分是第三方包。打开你的build.gradle文件,查看你用了哪些第三方的包。
    我去他们的官网把已经写好的混淆copy下来。直接包含jar包的话,方式另外写。

    整理Android最全的混淆规则大全(最新的开源框架混淆)
    Android混淆配置总结-持续更新

    4.第四步
    第三部分与js互调的类,工程中没有直接跳过。一般你可以这样写

    -keep class 你的类所在的包.** { *; }
    

    5.第五步
    第四部分与反射有关的类,工程中没有直接跳过。类的话直接这样

    -keep class 你的类所在的包.** { *; }
    

    四、常用混淆写法

    keep 保留类和类中的成员,防止被混淆或被移除。
    keepnames 保留类和类中的成员,防止被混淆,但当成员没有被引用时会被移除。
    keepclassmembers 只保留类中的成员,防止被混淆或被移除。
    keepclassmembernames 只类中的成员,防止被混淆,但当成员没有被引用时会被移除。
    keepclasseswithmembers 保留类和类中的成员,防止被混淆或被移除。前提是指明的类中的成员必须存在。如果不存在,还是会被混淆。
    keepclasseswithmembernames 保留类和类中的成员,防止被混淆,但当成员没有被引用时会被移除。前提是指明的类中的成员必须存在。如果不存在,还是会被混淆。

    #不混淆某个类
    -keep public class com.mumuxi.example.Test { *; }
    
    #不混淆某个包的所有类
    -keep class com.mumuxi.test.** { *; }
    
    #不混淆某个类的子类
    -keep public class * extends com.biaobiao.mumuxi.Test { *; }
    
    #不混淆某个类的内部类
    keep class com.mumuxi.example.Test$* { *;}
    
    #不混淆某个接口的实现
    -keep class * implements com.mumuxi.example.TestInterface { *; }
    
    #不混淆某个类的构造方法
    -keepclassmembers class com.mumuxi.example.Test { 
        public <init>(); 
    }
    
    #不混淆某个类的特定的方法
    -keepclassmembers class com.mumuxi.example.Test { 
        public void test(java.lang.String); 
    }
    
    

    五、其他一些混淆注意事项

    • jni方法不可混淆,因为这个方法需要和native方法保持一致;
    • 反射用到的类不混淆(否则反射可能出现问题);
    • AndroidMainfest中的类不混淆,所以四大组件和Application的子类和Framework层下所有的类默认不会进行混淆。自定义的View默认也不会被混淆;所以像网上贴的很多排除自定义View,或四大组件被混淆的规则在Android Studio中是无需加入的;
    • 与服务端交互时,使用GSON、fastjson等框架解析服务端数据时,所写的JSON对象类不混淆,否则无法将JSON解析成对应的对象;
    • 使用第三方开源库或者引用其他第三方的SDK包时,如果有特别要求,也需要在混淆文件中加入对应的混淆规则;
    • 有用到WebView的JS调用也需要保证写的接口方法不混淆,原因和第一条一样;
    • Parcelable的子类和Creator静态成员变量不混淆,否则会产生Android.os.BadParcelableException异常;
    • 使用enum类型时需要注意避免以下两个方法混淆,因为enum类的特殊性,以下两个方法会被反射调用,见第二条规则。

    参考:
    混淆必知必会
    Android混淆精炼详解+常用库混淆+通用混淆模板

    相关文章

      网友评论

        本文标题:Android 混淆介绍

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