混淆是保护apk的必要手段,通过混淆,如果apk被反编译,会对一些关键词使用a,b,c之类的字符替换,加大解读难度
在build.gradle文件中配置如下代码:
android {
buildTypes {
debug {
...
}
release {
//混淆开关
minifyEnabled true
// 是否zip对齐
zipAlignEnabled true
// 移除无用的resource文件
shrinkResources false
// 是否打开debuggable开关
debuggable false
// 是否打开jniDebuggable开关
jniDebuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.myConfig
}
}
}
minifyEnabled为true,就是打开混淆。主要的配置规则是在proguard-rules.pro文件中进行,
混淆设置参数
-optimizationpasses 5 # 代码混淆的压缩比例,值介于0-7,默认5
-verbose # 混淆时记录日志
-dontoptimize # 不优化输入的类文件
-dontshrink # 关闭压缩
-dontpreverify # 关闭预校验(作用于Java平台,Android不需要,去掉可加快混淆)
-dontoptimize # 关闭代码优化
-dontobfuscate # 关闭混淆
-ignorewarnings # 忽略警告
-dontwarn com.squareup.okhttp.** # 指定类不输出警告信息
-dontusemixedcaseclassnames # 混淆后类型都为小写
-dontskipnonpubliclibraryclasses # 不跳过非公共的库的类
-printmapping mapping.txt # 生成原类名与混淆后类名的映射文件mapping.txt
-useuniqueclassmembernames # 把混淆类中的方法名也混淆
-allowaccessmodification # 优化时允许访问并修改有修饰符的类及类的成员
-renamesourcefileattribute SourceFile # 将源码中有意义的类名转换成SourceFile,用于混淆具体崩溃代码
-keepattributes SourceFile,LineNumberTable # 保留行号
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod # 避免混淆注解、内部类、泛型、匿名类
-optimizations !code/simplification/cast,!field/ ,!class/merging/ # 指定混淆时采用的算法
保持不被混淆的设置
[保持命令] [类] {
[成员]
}
-keep # 防止类和类成员被移除或被混淆;
-keepnames # 防止类和类成员被混淆;
-keepclassmembers # 防止类成员被移除或被混淆;
-keepclassmembernames # 防止类成员被混淆;
-keepclasseswithmembers # 防止拥有该成员的类和类成员被移除或被混淆;
-keepclasseswithmembernames # 防止拥有该成员的类和类成员被混淆;
类
具体的类
访问修饰符 → public、private、protected
通配符(*) → 匹配任意长度字符,但不包含包名分隔符(.)
通配符(**) → 匹配任意长度字符,且包含包名分隔符(.)
extends → 匹配实现了某个父类的子类
implements → 匹配实现了某接口的类
$ → 内部类
成员
匹配所有构造器 → <init>
匹配所有域 → <field>
匹配所有方法 → <methods>
访问修饰符 → public、private、protected
除了 * 和 ** 通配符外,还支持 *** 通配符,匹配任意参数类型
... → 匹配任意长度的任意类型参数,如void test(...)可以匹配不同参数个数的test方法
常用自定义混淆规则范例
# 不混淆某个类的类名,及类中的内容
-keep class cn.coderpig.myapp.example.Test { *; }
# 不混淆指定包名下的类名,不包括子包下的类名
-keep class cn.coderpig.myapp*
# 不混淆指定包名下的类名,及类里的内容
-keep class cn.coderpig.myapp* {*;}
# 不混淆指定包名下的类名,包括子包下的类名
-keep class cn.coderpig.myapp**
# 不混淆某个类的子类
-keep public class * extends cn.coderpig.myapp.base.BaseFragment
# 不混淆实现了某个接口的类
-keep class * implements cn.coderpig.myapp.dao.DaoImp
# 不混淆类名中包含了"entity"的类,及类中内容
-keep class **.*entity*.** {*;}
# 不混淆内部类中的所有public内容
-keep class cn.coderpig.myapp.widget.CustomView$OnClickInterface {
public *;
}
# 不混淆指定类的所有方法
-keep cn.coderpig.myapp.example.Test {
public <methods>;
}
# 不混淆指定类的所有字段
-keep cn.coderpig.myapp.example.Test {
public <fields>;
}
# 不混淆指定类的所有构造方法
-keep cn.coderpig.myapp.example.Test {
public <init>;
}
# 不混淆指定参数作为形参的方法
-keep cn.coderpig.myapp.example.Test {
public <methods>(java.lang.String);
}
# 不混淆类的特定方法
-keep cn.coderpig.myapp.example.Test {
public test(java.lang.String);
}
# 不混淆native方法
-keepclasseswithmembernames class * {
native <methods>;
}
# 不混淆枚举类
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#不混淆资源类
-keepclassmembers class **.R$* {
public static <fields>;
}
# 不混淆自定义控件
-keep public class * entends android.view.View {
*** get*();
void set*(***);
public <init>;
}
# 不混淆实现了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();
}
# 不混淆实现了parcelable接口的类成员
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 注意事项:
#
# ① jni方法不可混淆,方法名需与native方法保持一致;
# ② 反射用到的类不混淆,否则反射可能出问题;
# ③ 四大组件、Application子类、Framework层下的类、自定义的View默认不会被混淆,无需另外配置;
# ④ WebView的JS调用接口方法不可混淆;
# ⑤ 注解相关的类不混淆;
# ⑥ GSON、Fastjson等解析的Bean数据类不可混淆;
# ⑦ 枚举enum类中的values和valuesof这两个方法不可混淆(反射调用);
# ⑧ 继承Parceable和Serializable等可序列化的类不可混淆;
# ⑨ 第三方库或SDK,请参考第三方提供的混淆规则,没提供的话,建议第三方包全部不混淆;
ProGuard在AS中默认的配置文件是proguard-rules.pro
我们常见的ProGuard文件主要包含如下几个部分:
1.基本配置,设定混淆的规则等,基本配置是每个混淆文件必须存在的,并且此块内容大部分通用,可以直接copy。
2.基本的keep项,多数Android工程都需要非混淆的内容,包括有四大组件等内容,此项内容也大部分通用,也可以直接copy。
3.三方引入的lib包混淆,这个内容需要去各自的官网去查找对应的混淆添加代码。
4.其他需要不混淆的内容,包括:实体类,json解析类,WebView及js的调用模块,与反射相关的类和方法。
基本配置
基本配置基本上变化不大,一般的项目可以直接copy
#指定压缩级别
-optimizationpasses 5
#不跳过非公共的库的类成员
-dontskipnonpubliclibraryclassmembers
#混淆时采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#把混淆类中的方法名也混淆了
-useuniqueclassmembernames
#指定不去忽略非公共的库的类
-dontskipnonpubliclibraryclasses
#不做预检验,preverify是proguard的四大步骤之一,可以加快混淆速度
#-dontpreverify
# 忽略警告(?)
#-ignorewarnings
#混淆时不使用大小写混合,混淆后的类名为小写(大小写混淆容易导致class文件相互覆盖)
-dontusemixedcaseclassnames
#优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification
#将文件来源重命名为“SourceFile”字符串
#-renamesourcefileattribute SourceFile
#保留行号
-keepattributes SourceFile,LineNumberTable
#保持泛型
-keepattributes Signature
# 保持注解
-keepattributes *Annotation*,InnerClasses
# 保持测试相关的代码
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**
基本的项目配置
多数是包括序列化,Android四大组件等基本内容的混淆keep,对于常规的项目而言区别不大,也可以进行copy
# 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 {
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$* {*;}
# 保留四大组件,自定义的Application,Fragment等这些类不被混淆
-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
## support
-dontwarn android.support.**
-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *;}
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
-keep public class * extends android.support.v4.view.ActionProvider {
public <init>(android.content.Context);
}
# 保留枚举类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保留本地native方法不被混淆
-keepclasseswithmembers class * {
native <methods>;
}
# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
#保留在Activity中的方法参数是view的方法,
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
# For XML inflating, keep views' constructoricon.png 自定义view
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
# androidx 混淆
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**
-printconfiguration
-keep,allowobfuscation @interface androidx.annotation.Keep
-keep @androidx.annotation.Keep class *
-keepclassmembers class * {
@androidx.annotation.Keep *;
}
三方SDK的混淆
三方SDK的混淆代码需要去各自的SDK官网上去查找,例如网易云信的
### nimlib
-dontwarn com.netease.nim.**
-keep class com.netease.nim.** {*;}
-dontwarn com.netease.nimlib.**
-keep class com.netease.nimlib.** {*;}
-dontwarn com.netease.share.**
-keep class com.netease.share.** {*;}
-dontwarn com.netease.mobsec.**
-keep class com.netease.mobsec.** {*;}
其他混淆内容
其他混淆内容比较杂,包括:实体类,json解析类,WebView及js的调用模块,与反射相关的类和方法。这个根据自己需要来做
# WebView
-dontwarn android.webkit.WebView
-dontwarn android.net.http.SslError
-dontwarn android.webkit.WebViewClient
-keep public class android.webkit.WebView
-keep public class android.net.http.SslError
-keep public class android.webkit.WebViewClient
-keep
class com.mandala.healthserviceresident.vo.** { *; } #实体类不参与混淆
-keep class com.mandala.healthserviceresident.http.** { *; } #实体类不参与混淆
-keep class com.hacker.okhttputil.** { *; } #实体类不参与混淆
-keep class net.sqlcipher.** { *; }
-keep class 类所在的包.** { *; }
log过滤
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
}
混淆结果
在添加混淆之后,在排查时我们可以对混淆和未混淆的类名进行记录和打印,在混淆文件中添加如下代码可以查看混淆编译的类及文件结构:
# 混淆映射,生成映射文件
-verbose
-printmapping proguardMapping.txt
#输出apk包内所有的class的内部结构
-dump dump.txt
#未混淆的类和成员
-printseeds seeds.txt
#列出从apk中删除的代码
-printusage unused.txt
添加上述代码之后,会在生成release包时也同时生成三个日志文件,我们在排查混淆问题时可以以上述三个文件作为参考,快速排查。
我这边混淆完成以后,打包测试,发现网络能访问,但是提示失败,刚开始想的办法是抓包,然后发现实体类被设置成a,b之类的,这个是被混淆了,然后在混淆文件里keep一下,但是还有其他bug ,这样打包完成以后,如果没有有效的手段,只能是看着现象和混淆文件一个一个猜了,如果能在debug的时候发现Bug就很好排查了,排查方法就是在debug模式下,打开混淆
打开方法很简单
buildTypes {
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
zipAlignEnabled true
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.myConfig
}
}
就这样,然后debug进行排查就好了。正常开发的时候记得关掉,比较耗性能
我用到的配置
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\AndroidStudio\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Optimizations: If you don't want to optimize, use the
# proguard-android.txt configuration file instead of this one, which
# turns off the optimization flags. Adding optimization introduces
# certain risks, since for example not all optimizations performed by
# ProGuard works on all versions of Dalvik. The following flags turn
# off various optimizations known to have issues, but the list may not
# be complete or up to date. (The "arithmetic" optimization can be
# used if you are only targeting Android 2.0 or later.) Make sure you
# test thoroughly if you go this route.
-optimizations !code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify
# The remainder of this file is identical to the non-optimized version
# of the Proguard configuration file (except that the other file has
# flags to turn off optimization).
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontwarn android.support.**
-dontwarn org.apache.http.**
-dontwarn com.amap.**
-dontwarn com.alibaba.**
-dontwarn com.netease.**
-dontwarn io.netty.**
-dontwarn com.autonavi.amap.**
### keep options
#system default, from android example
-keep public class * extends android.app.Activity
-keep public class * extends androidx.fragment.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
-keepattributes *Annotation*,InnerClasses
#-keepattributes SourceFile,LineNumberTable
### 3rd party jars
-keep class android.support.** {*;}
-keep class com.amap.** {*;}
-keep class android.webkit.** {*;}
### 3rd party jars(lucene)
-dontwarn java.nio.channels.SeekableByteChannel
-dontwarn org.apache.lucene.**
-keep class org.apache.lucene.** {*;}
### nimlib
-dontwarn com.netease.nim.**
-keep class com.netease.nim.** {*;}
-dontwarn com.netease.nimlib.**
-keep class com.netease.nimlib.** {*;}
-dontwarn com.netease.share.**
-keep class com.netease.share.** {*;}
-dontwarn com.netease.mobsec.**
-keep class com.netease.mobsec.** {*;}
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-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 * {
public <init> (org.json.JSONObject);
}
-keep class com.amap.api.maps.**{*;}
-keep class com.amap.api.trace.**{*;}
-keep class com.amap.api.location.**{*;}
-keep class com.amap.api.fence.**{*;}
-keep class com.alibaba.sdk.android.**{*;}
-dontwarn demo.**
-keep class demo.**{*;}
# Bugly异常上报
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
-keep class com.umeng.** {*;}
-keep class java.security.interfaces.*
-keep class java.util.Base64
-keep class org.repackage.** {*;}
-keepclassmembers class * {
public <init> (org.json.JSONObject);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class org.greenrobot.greendao.**{*;}
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao{
public static java.lang.String TABLENAME;
}
-keep class **$Properties
-keep class com.mandala.healthserviceresident.greendao.gen.**{*;}
# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use Rx:
-dontwarn rx.**
#Gson
-keep class com.google.gson.** {*;}
-keep class com.google.**{*;}
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; }
#okhttp
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.**
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
-keep class com.mandala.healthserviceresident.vo.** { *; } #实体类不参与混淆
-keep class com.mandala.healthserviceresident.http.** { *; } #实体类不参与混淆
-keep class com.hacker.okhttputil.** { *; } #实体类不参与混淆
-keep class net.sqlcipher.** { *; }
# WebView
-dontwarn android.webkit.WebView
-dontwarn android.net.http.SslError
-dontwarn android.webkit.WebViewClient
-keep public class android.webkit.WebView
-keep public class android.net.http.SslError
-keep public class android.webkit.WebViewClient
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
-ignorewarnings
重点看一下greendao的配置,如果没配置,会提示daoConfig出错
-keep class org.greenrobot.greendao.**{*;}
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao{
public static java.lang.String TABLENAME;
}
-keep class **$Properties
-keep class com.mandala.healthserviceresident.greendao.gen.**{*;}
# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use Rx:
-dontwarn rx.**