前言
首先我们来思考这样一个问题,为什么我们需要混淆项目代码?
原因很简单,作为开发者或者企业,你总不会希望自己辛辛苦苦创造出来的东西轻易的被黑客反编译或破解吧,尤其是现在很多app项目嵌入了支付的功能,如果被发现了代码漏洞,后果不堪设想,为了有效预防这个问题,Android中提供了proguard文件来对项目进行混淆。
proguard的官方介绍
Proguard通过移除没有用到的代码以及通过特定规则重命名类、变量、方法来压缩、优化、混淆你的代码。这样做可以让你的apk更小,更难被逆向分析。由于可以提高被逆向分析的难度,对相关功能安全敏感的应用使用它是十分必要的
proguard的语法介绍
buildTypes {
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
首先Android需要在项目的buildTypes中将minifyEnabled的值设为true
proguard-android.txt:SDK中默认proguard的配置规则
proguard-rules.pro:自定义proguard的配置规则
压缩
压缩会移除未被使用的类和成员,并且会在优化动作执行之后再次执行
# 关闭压缩,默认打开
-dontshrink
优化
优化会在字节码级别上做优化,让应用运行的更快
# 关闭优化,默认打开
-dontoptimize
# 表示对代码优化的次数,一般为5
-optimizationpasses n
# 指定更精细级别的优化
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
混淆
混淆会将简短的无意义的名称,对类,字段和方法进行重命名
# 关闭混淆,默认打开
-dontobfuscate
预验证
预验证将对Java class进行预验证,Android中没有预验证过程
# 关闭预验证,默认关闭
-dontpreverify
类名
对类名进行keep操作只是将类名keep住,但方法和变量仍然会被混淆
# 一颗星表示keep当前本包下的类名,子包下的类名是会被混淆的
-keep class com.example.ljz.*
# 两颗星表示keep当前本包下的类名和子包下的类名
-keep class com.example.ljz.**
# 表示keep当前类名
-keep class com.example.ljz.net.NetWorkCache
# 表示keep当前类的内部类的类名
-keep class com.example.ljz.net.NetWorkCache$NetWorkBean
内容
对内容进行keep操作不仅可以将类名keep住,还可以对方法和变量keep住
# 一颗星表示keep当前本包下的类名、类的内容
-keep class com.example.ljz.*{*;}
# 两颗星表示keep当前本包下的类名、类的内容和子包下的类名、类的内容
-keep class com.example.ljz.**{*;}
# 表示keep当前类名、类的内容
-keep class com.example.ljz.net.NetWorkCache{*;}
# 表示keep当前类的内部类的类名、内部类的内容
-keep class com.example.ljz.net.NetWorkCache$NetWorkBean{*;}
特定内容
对特定的内容进行keep操作
-keep class com.example.ljz.net.NetWorkCache{
<init>;# 匹配所有构造器
<fields>;# 匹配所有变量
<methods>;# 匹配所有方法
public <methods>;# 匹配所有共有的方法
private <methods>;# 匹配所有私有的方法
public *;# 匹配所有共有的内容
private *;# 匹配所有私有的内容
public <init>(java.lang.String);# 匹配特定参数的构造函数
public void getCache(...);# 匹配任意长度类型参数的方法
类成员
对类名不需要keep,只需要对类下的方法进行keep操作
# 表示keep特定类下的特定参数的方法,但类名不会被keep
-keepclassmembernames class com.example.ljz.net.NetWorkCache{
public void getCache(java.lang.String);
}
注意事项
安卓底层组件和类名不可混淆
将底层的keep住,插件化才能准确的hook到底层组件
-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.ContentProvider
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.view.View
-keep public class * extends android.preference.Preference
jni方法不可混淆
native方法要完整的包名类名方法来定义,不可修改,否则找不到
-keepclasswithmembernames class *{
native <methods>;
}
反射用到的类名和方法不可混淆
native方法要完整的包名类名方法来定义,不可修改,否则找不到
-keep public class com.example.ljz.** {
public void set*(***);
public *** get*();
public *** is*();
}
自定义View不可混淆
只要是继承自系统组件,都要keep住
-keep public class * extend 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);
}
第三方框架不可混淆
将第三方框架当作为系统组件即可
-keep class android.support.** { *; }
-keep class android.support.v4.** { *; }
-keep class android.support.v7.** { *; }
-keep class * extends android.support.v4.**
-keep class * extends android.support.v7.**
-keep class * extends android.support.annotation.**
WebView和Js互调接口不可混淆
-keepclassmembers class ** {
@android.webkit.JavascriptInterface public *;
}
序列化的类不可混淆
-keepclassmembers class * implements android.os.Parcelable {
static ** CREATOR;
<fields>;
<methods>;
}
-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();
}
enum类的特殊性
以下方法会被发射调用
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
public static ** valueOf(int);
}
其他场景
# 指定文件为映射文件,包括类和类成员的混淆名称,文件未提及的类和类成员将生成新的名称
-applymapping mapping.txt
# 指定一个文本文件,其中所有有效字词都用作混淆字段和方法名称
-obfuscationdictionary obfuscationdictionary.txt
# 指定一个文本文件,其中所有有效词都用作混淆类名
-classobfuscationdictionary obfuscationdictionary.txt
# 混淆时不生成大小写混合的类名
-dontusemixedcaseclassnames
# 不忽略指定jars中的非public calsses
-dontskipnonpubliclibraryclasses
# 不忽略指定类库的public类成员(变量和方法)
-dontskipnonpubliclibraryclassmembers
# 混淆过程中打印详细信息,如果异常终止则打印整个堆栈信息
-verbose
# 忽略警告继续处理
-ignorewarnings
# 不对指定的类、包中的不完整的引用发出警告
-dontwarn android.support.v4.**
-dontwarn All
# 避免混淆内部类、泛型、匿名类
-keepattributes InnerClasses,Signature,EnclosingMethod
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
# 保留注释成员变量,如Activity被@Override注释的方法onCreate、onDestroy方法
-keepattributes *Annotation*
# 资源类变量需要保留
-keep public class **.R$*{
public static final int *;
}