为什么要进行代码混淆?
混淆在我们实际的Android开发中是必不可少的,如果不混淆, 发布出去,别人一反编译 就可以直接看你的源码了,那么在应用发布前,就需要对代码进行混淆处理,从而让我们代码即使被反编译,也难以阅读。一句话,使用混淆增强了应用软件的安全性!
认识Proguard工具
ProGuard is a Java class file shrinker, optimizer, obfuscator, and preverifier.
从官方文档这段话中可以看出,Proguard可以对Java class文件执行shrink,optimize,obfuscate和preverify四项优化。这四项优化也代表了Proguard的四项功能。
-
压缩(Shrink):侦测并移除代码中无用的类、字段、方法、和特性(Attribute)。
具体解释:shrink功能的作用是移除代码中没有使用到的类,方法和成员变量,从而减少文件大小。这些没有使用到的类,方法和成员变量的产生主要有两种情况:
1、自身代码由于功能需求的变更,或者代码重构,导致原本的代码不会再使用,或者不删除以备后续的使用,这些代码对程序的运行没有任何作用。
2、项目中经常会包含一些开源代码或第三方SDK,而项目通常只会使用到开源代码或第三方SDK中的部分功能,那些没有用到的功能对应的代码同样是可以去掉的。
Proguard的shrink功能就是用来执行这项操作的,它会自动分析jar包中各个类,方法之类的调用和依赖关系,对那些没有用到的类,方法和成员变量进行剔除。 -
优化(OPtimize):对字节码进行优化,移除无用指令。
optimize过程会在Java字节码层面上进行优化,剔除方法中一些冗余的调用。帮助文档中列出了一些当前Proguard支持的优化。 -
混淆(Obfuscate):使用a、b、c、d这样简短而无意义的名称,对类、字段和方法进行重命名。
obfuscator也就是Proguard最常被提到的代码混淆功能。由于Java代码编译之后的class文件中仍然包含了调试信息,源文件名,行号,类名,方法名,成员变量名,参数名,局部变量名等信息,通过反编译可以很容易的将这些信息还原出来。
通过obfuscator,可以将jar包中不需要对外暴露的类名、方法名和变量名替换成一些简短的,对人来说没有意义的名字。 -
预检(Preveirfy):在java平台上对处理后的代码进行预检。
preverifier用来对Java class进行预验证。预验证主要是针对Java ME开发来说的,Android中没有预验证过程,所以不需要用到这项功能。
Android开发中的Proguard混淆
在开发工具Android Studio中集成了ProGuard混淆的功能,在Android Sdk “tools\proguard\lib\proguard.jar“目录下,可以自行的查看、替换。它结合了gradle搭配使用。
在app下的build.gradle中增加如下配置:
buildTypes {
debug {
versionNameSuffix ".debug"
}
release {
debuggable false
minifyEnabled true //将minifyEnabled 属性设置为true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
这些代码会在app下生成与build.gradle同级的proguard-rules.pro文件,这还需要我们进行混淆配置。
Tips:新生成的proguard-rules.pro都是没有添加混淆规则的,这需要我们手动的去编写、添加。
混淆配置
项目中的混淆分为两类:(具体代码为了不影响阅读,在后文贴)
- 基本不变的混淆(系统推荐的不可混淆的文件配置)
- 自己自行添加的配置(实体类、依赖的第三方框架、与wevview交互的js、一些反射的类)
Tips:
1.反射用到的类不混淆
2.JNI方法不混淆
3.AndroidMainfest中的类不混淆,四大组件和Application的子类和Framework层下所有的类默认不会进行混淆
4.Parcelable的子类和Creator静态成员变量不混淆,否则会产生android.os.BadParcelableException异常
5.使用GSON、fastjson等框架时,所写的JSON对象类不混淆,否则无法将JSON解析成对应的对象
6.使用第三方开源库或者引用其他第三方的SDK包时,需要在混淆文件中加入对应的混淆规则
7.有用到WebView的JS调用也需要保证写的接口方法不混淆
总结
Proguard经常被看做Android平台的代码混淆工具,这种看法是比较片面的。Proguard项目诞生于2002年,而Android 1.0是2008年才发布的,也就是说早在Android发布之前Proguard就已经存在很久了。Proguard不仅适用于Android项目,也适合对其他使用Java开发项目的优化。
此外,Proguard也不仅仅是一个代码混淆工具,代码混淆只是Proguard四项功能中的其中一项。它之所以被认为是Android平台的代码混淆工具,是因为Google将其集成到了Android SDK和Android项目的编译过程中,成为Android开发默认的代码优化工具,从而被广大的Android开发者所熟悉。
总而言之,Android开发者对代码混淆功能的需求比其他功能要迫切的多,Proguard代码混淆功能成为开发者必选的一项功能。
基本不变的混淆:
# 代码混淆压缩比,在0~7之间,默认为5,一般不下需要修改
-optimizationpasses 5
# 混淆时不使用大小写混合,混淆后的类名为小写 windows下的同学还是加入这个选项吧(windows大小写不敏感)
-dontusemixedcaseclassnames
# 指定不去忽略非公共的库的类
# 默认跳过,有些情况下编写的代码与类库中的类在同一个包下,并且持有包中内容的引用,此时就需要加入此条声明
-dontskipnonpubliclibraryclasses
# 指定不去忽略非公共的库的类的成员
-dontskipnonpubliclibraryclassmembers
# 不做预检验,preverify是proguard的四个步骤之一 Android不需要preverify,去掉这一步可以加快混淆速度
-dontpreverify
# 混淆后就会生成映射文件 包含有类名->混淆后类名的映射关系 然后使用printmapping指定映射文件的名称
-verbose
-printmapping priguardMapping.txt
# 保护代码中的Annotation不被混淆 这在JSON实体映射时非常重要,比如fastJson
-keepattributes *Annotation*
# 避免混淆泛型 这在JSON实体映射时非常重要,比如fastJson
-keepattributes Signature
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
# 指定混淆时采用的算法,后面的参数是一个过滤器 这个过滤器是谷歌推荐的算法,一般不改变
-optimizations !code/simplification/artithmetic,!field/*,!class/merging/*
--------------------------------------------------------------------------
# 保留了继承自Activity、Application这些类的子类 不被混淆
-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
# 保留所有的本地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) 并且 get、set方法 不被混淆
-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*(***);
*** get* ();
}
-keepclasseswithmembers class * {
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 void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 对R文件下的所有类及其方法,都不能被混淆
-keepclassmembers class **.R$* {
*;
}
# 对于带有回调函数onXXEvent的,不能混淆
-keepclassmembers class * {
void *(**On*Event);
}
# 如果有引用android-support-v4.jar包,可以添加下面这行
-keep public class fragment所在文件的路径.** {*;}
# 避免NoClassDefFoundError异常 同上一起使用
-libraryjars ./libs/android-support-v4.jar
-dontwarn android.support.v4.**
-dontwarn **CompatHoneycomb
-dontwarn **CompatHoneycombMR2
-dontwarn **CompatCreatorHoneycombMR2
-keep interface android.support.v4.app.** { *; }
-keep class android.support.v4.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment
# 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);
}
自己增添的混淆配置
# com.example.entity是存放实体类的路径
-keep class com.xx.entity.** {
//全部忽略,实体类不被混淆
*;
}
一些常见的第三方混淆配置:
#---------------------------------常见第三方配置(可选)-------------------------------
#eventBus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
#glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
#butterknife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
#gson
-keepattributes Signature-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson 下面替换成自己的实体类
-keep class com.example.bean.** { *; }
#retrofit2
-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;
}
#okhttp3
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.**
#友盟统计
-keepclassmembers class * {
public <init> (org.json.JSONObject);
}
-keep public class [您的应用包名].R$*{
public static final int *;
}
-keepclassmembers enum * {
public static **[] values(); public static ** valueOf(java.lang.String);
}
#极光推送
-dontoptimize
-dontpreverify
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
-keep class * extends cn.jpush.android.helpers.JPushMessageReceiver { *; }
-dontwarn cn.jiguang.**
-keep class cn.jiguang.** { *; }
#-------------------------------------------------------------------------
网友评论