App下载 微信公众号

Android混淆proguard

技术 · 移动开发 · Android/ 作者【吾非言】/ 发布于2020-1-8/ 616次浏览
2020 1/8 5:36
摘要: Android SDK自带了混淆工具Proguard。它位于SDK根目录\tools\proguard下面。 ProGuard是一个免费的Java类文件收缩,优化,混淆和预校验器。它可以检测并删除未使用的类,字段,方法和属性。它可以优化字节码,并删除未使用的指令。它可以将类、字段和方法使用短无意义的名称进行重命名。最后,预校验的Java6或针对Java MicroEdition的所述处理后的码。

微信公众号:伴职创作
IT类、哲学、散文、叙事情感类、小说…欢迎你来投稿。

伴职创作

混淆简介

  1. Android代码混淆是一种应用源代码保护技术,用来防止别人对apk进行逆向分析;
  2. Android2.3开始,Google就在SDK中加入了ProGuard的工具,使用它来进行代码的混淆。
  3. ProGuard是一个压缩、优化和混淆Java字节码文件的免费工具, 其作用有以下几点:
  • 删除代码中的注释;
  • 删除代码中没有用到的类、字段、方法和属性;
  • 会把代码中的包名、类名、方法名,变量名等修改为abcd…这种没有意义的名字,使得反编译出来的代 码难以阅读,从而达到防止apk被破解和逆向分析的目的;
  • 经过ProGuard混淆后,apk安装包会变小;
  1. 在实际项目中,有些Java类不能进行混淆,需要配置混淆规则;
  2. 在实际项目中,打包apk时一般都需要进行混淆处理;
  3. 混淆后会生成mapping.txt文件,该文件需要存档以便用来还原和查看混淆后的出错日志;

开启混淆

在项目的build.gradle文件中打开混淆的开关,然后在proguard-rules.pro文件中添加混淆规则即可

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

minifyEnabled 是否开启混淆
proguardFiles 引入混淆文件

添加混淆规则

首先介绍一下添加混淆规则中常用到的一些命令的含义,方便我们理解并能够自行添加属于我们项目中的一些混淆规则,然后再介绍一下项目中一些通用的混淆规则。

1. 混淆规则常用到的命令含义:

  • 保留该包下的类名不会被混淆,但是该包的子包的类名还是会被混淆
-keep class packageName.*
  • 保留该包及其子包的类名不会被混淆
-keep class packageName.**
  • 保留类名及其该类的内容不会被混淆(包括变量名,方法名等)
-keep class packageName.* {*;}
  • 不保留类名只保留该类的方法名、变量名等不会被混淆
-keepclassmembers class packageName.*{*;}
  • 保留所有继承某类的子类不会被混淆(implement同理)
-keep public class * extends android.app.Activity
  • 保留该内部类中所有的public方法名、变量名不会被混淆
-keepclassmembers class packageName$内部类名 {//"$"的含义是保留某类的内部类不会被混淆
   public *;                                        
}
  • init、fields、 methods的含义和使用
<init>;                //匹配所有的构造器
<fields>;              //匹配所有的域
<methods>;             //匹配所有的方法
//可以在以上的命令前加上public、private、native等来进一步指定不被混淆的内容
//也可以在以上的命令后面加上参数,来指定含有特定的参数构造方法或者方法名不会被混淆

-keep class packageName{
    public <init>;                                  //保留所有的public的构造方法不会被混淆
}

-keep class packageName{
    public <init>(java.lang.String);              //保留所有的public的构造方法并且参数是String对象,不会被混淆
}

-keep class packageName{                              //保留所有的private的方法名不会被混淆
    private <methods>;
}

通配符

  • 含有Keep关键字的含义(移除是指在压缩时(Shrinking)是否会被删除,需要开启压缩)

Keep关键字的含义

2、基本混淆指令

# 代码混淆压缩比,在 0~7 之间
-optimizationpasses 5

# 混合时不使用大小写混合,混合后的类名为小写
-dontusemixedcaseclassnames

# 指定不忽略非公共库的类和类成员
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers

# 这句话能够使我们的项目混淆后产生映射文件
# 包含有类名->混淆后类名的映射关系
-verbose

# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度
-dontpreverify

# 保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses

# 避免混淆泛型
-keepattributes Signature

# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable

# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是 Google 推荐的算法,一般不做修改
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*

# 是否允许改变作用域的,可以提高优化效果
# 但是,如果你的代码是一个库的话,最好不要配置这个选项,因为它可能会导致一些 private 变量被改成 public,谨慎使用
#-allowaccessmodification

# 指定一些接口可能会被合并,即使一些子类没有同时实现两个接口的方法。这种情况在java源码中是不允许存在的,但是在java字节码中是允许存在的。
# 它的作用是通过合并接口减少类的数量,从而达到减少输出文件体积的效果。仅在 optimize 阶段有效。
# 如果在开启后没有任何影响可以使用,这项配置对于一些虚拟机的65535方法数限制是有一定效果的,谨慎使用
#-mergeinterfacesaggressively

# 输出所有找不到引用和一些其它错误的警告,但是继续执行处理过程。不处理警告有些危险,所以在清楚配置的具体作用的时候再使用
-ignorewarnings

3、混淆日志

# APK 包内所有 class 的内部结构
-dump proguard/class_files.txt
# 未混淆的类和成员
-printseeds proguard/seeds.txt
# 列出从 APK 中删除的代码
-printusage proguard/unused.txt
# 混淆前后的映射,这个文件在追踪异常的时候是有用的
-printmapping proguard/mapping.txt

不能混淆的情况

  • Java的反射,为什么不能混淆呢?因为代码混淆,类名、方法名、属性名都改变了,而反射它还是按照原来的名字去反射,结果只射出一个程序崩溃
  • 注解用了反射,所以不能混淆。 不混淆任何包含native方法的类的类名以及native方法名,否则找不到本地方法。
  • Activity不能混淆,因为AndroidManifest.xml文件中是完整的名字
  • 自定义view也是带了包名写在xml布局中,不能混淆

通用的一些混淆规则:

注:四大组件、Fragment、自定义控件不需要添加混淆规则,因为这些默认是不会被混淆的,所以网上很多四大组件的混淆规则是没必要添加的。

  • 注解
-keepattributes *Annotation*
  • R文件下面的资源
-keep class **.R$* {*;}
  • 本地的native方法(JNI
-keepclasseswithmembernames class * {
    native <methods>;
}
  • 反射(该packageName是被反射类的包名)
-keep class packageName{*;}
  • JavaBean中的泛型
-keepattributes Signature
  • JavaBean(如果使用了Gson进行解析Json字符串,就需要添加JavaBean的混淆规则,因为Gson使用了反射的原理)
-keep class packageName**
-keep class packageName**{*;}
  • 枚举
# 保留枚举类不被混淆
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
  • Parcelable序列化和Creator静态成员变量
-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();
}
  • 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);
}
  • JS交互
#Android4.2以上需要添加以下的两个混淆配置
-keepattributes *Annotation*
-keepattributes *JavascriptInterface*
-keepclassmembers class packageName$内部类名 {
    public *;
}

Proguard混淆模板

#############################################
#
# 对于一些基本指令的添加
#
#############################################
# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
-optimizationpasses 7

# 混合时不使用大小写混合,混合后的类名为小写
-dontusemixedcaseclassnames

# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses

# 这句话能够使我们的项目混淆后产生映射文件
# 包含有类名->混淆后类名的映射关系
-verbose

# 指定不去忽略非公共库的类成员
-dontskipnonpubliclibraryclassmembers

# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
-dontpreverify

# 保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses

# 避免混淆泛型
-keepattributes Signature

# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable

# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*


#############################################
#
# 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序列化的类不被混淆
-keepnames class * implements java.io.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);
}

# 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用
# 记得proguard-android.txt中一定不要加-dontoptimize才起作用
# 另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制
-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(...);
}

#############################################
#
# 项目中特殊处理部分
#
#############################################

#-----------处理反射类---------------

#-----------处理js交互---------------

#-----------处理实体类---------------

#-----------处理第三方依赖库---------

常见的第三方库混淆

这个就需要查看项目中添加了那些第三方的Jar包依赖SDK了,然后在其官网上或者Github找相应的混淆规则,一般都会有的,如果没有可以自己写混淆规则。根据上面介绍的一些常用的命令含义,一般都能满足自己写混淆规则了。

# ARouter
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
# If you use the byType method to obtain Service, add the following rules to protect the interface:
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider
# If single-type injection is used, that is, no interface is defined to implement IProvider, the following rules need to be added to protect the implementation
-keep class * implements com.alibaba.android.arouter.facade.template.IProvider
# If @Autowired is used for injection in non-Activity classes, add the following rules to prevent injection failures
-keepnames class * {
    @com.alibaba.android.arouter.facade.annotation.Autowired <fields>;
}
-dontwarn com.alibaba.android.arouter.facade.model.**



# 友盟
-dontwarn com.umeng.**
-dontwarn com.taobao.**
-dontwarn anet.channel.**
-dontwarn anetwork.channel.**
-dontwarn org.android.**
-dontwarn org.apache.thrift.**
-dontwarn com.xiaomi.**
-dontwarn com.huawei.**
-dontwarn com.meizu.**

-keepattributes *Annotation*

-keep class com.taobao.** {*;}
-keep class org.android.** {*;}
-keep class anet.channel.** {*;}
-keep class com.umeng.** {*;}
-keep class com.xiaomi.** {*;}
-keep class com.huawei.** {*;}
-keep class com.meizu.** {*;}
-keep class org.apache.thrift.** {*;}

-keep class com.alibaba.sdk.android.**{*;}
-keep class com.ut.**{*;}
-keep class com.ta.**{*;}

-keep public class **.R$*{
   public static final int *;
}



# EventBus
-keepattributes *Annotation*
-keepclassmembers class * {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}



# 微信分享
-keep class com.tencent.mm.opensdk.** {*;}
-keep class com.tencent.wxop.** {*;}
-keep class com.tencent.mm.sdk.** {*;}



##---------------Begin: proguard configuration for OkHttp3  ----------

# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**

# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*

# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform

# okio
-dontwarn okio.**
##---------------End: proguard configuration for OkHttp3  ----------




##---------------Begin: proguard configuration for Retrofit2  ----------

# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations

# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @retrofit2.http.* <methods>;
}

# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**

# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit

# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*

# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>

##---------------End: proguard configuration for Retrofit2  ----------




##---------------Begin: proguard configuration for 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;
}
-dontnote rx.internal.util.PlatformDependent

##---------------End: proguard configuration for RxJava RxAndroid  ----------




##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { <fields>; }

# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

##---------------End: proguard configuration for Gson  ----------




##---------------Begin: proguard configuration for Glide v3  ----------
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}
##---------------End: proguard configuration for Glide v3  ----------

-keep class com.dayi56.android.netlib.dto.** { *; }




## 3D 地图 V5.0.0之前:
#-keep class com.amap.api.maps.**{*;}
#-keep class com.autonavi.amap.mapcore.*{*;}
#-keep class com.amap.api.trace.**{*;}

# 3D 地图 V5.0.0之后:
-keep class com.amap.api.maps.**{*;}
-keep class com.autonavi.**{*;}
-keep class com.amap.api.trace.**{*;}

# 定位
-keep class com.amap.api.location.**{*;}
-keep class com.amap.api.fence.**{*;}
-keep class com.autonavi.aps.amapapi.model.**{*;}

# 搜索
-keep class com.amap.api.services.**{*;}

## 2D地图
#-keep class com.amap.api.maps2d.**{*;}
#-keep class com.amap.api.mapcore2d.**{*;}

# 导航
-keep class com.amap.api.navi.**{*;}
-keep class com.autonavi.**{*;}

-dontwarn com.amap.api.col.**



# zxing 的混淆代码
#-keep class com.google.zxing.** {*;}



# Design包不混淆
-dontwarn android.support.design.**
-keep class android.support.design.** { *; }
-keep interface android.support.design.** { *; }
-keep public class android.support.design.R$* { *; }



# RichTextView
-keep class cc.ibooker.richtext.jlatexmath.** { *; }
感谢您使用伴职平台,如有侵权,请投诉删除!

全部评价

最新
查看更多评论 加载

猜你喜欢

换一批