代码混淆
代码混淆原因
Java
是一种跨平台的、解释型语言,Java
源代码编译成中间”字节码”存储于 class
文件中。
由于跨平台的需要,Java 字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法,这些符号带有许多语义信息,很容易被反编译成 Java 源代码。为了防止这种现象,我们可以使用 Java
混淆器对 Java
字节码进行混淆。
混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序的真正语义。被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译,也将难以阅读。同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。
Proguard 作用:
- 压缩
shrinks
:检查并移除代码中无用的类,字段,方法,属性。 - 优化
optimizes
:对字节码进行优化,移除无用的指令。 - 混淆
obfuscates
:使用a,b,c,d等简短而无意义的名称,对类,字段和方法进行重名,这样即使代码被逆向工程,对方也比较难以读懂。 - 预检测
Preveirfy
:在java平台上对处理后的代码进行再次检测。
Keep 关键字
有时候你是不是还想着,我不需要保持类名,我只需要把该类下的特定方法保持不被混淆就好,那你就不能用keep
方法了,keep
方法会保持类名,而需要用keepclassmembers
,如此类名就不会被保持,为了便于对这些规则进行理解,官网给出了以下表格:
保留 | 防止被移除或者被重命名 | 防止被重命名 |
---|---|---|
类和类成员 | -keep | -keepnames |
仅类成员 | -keepclassmembers | -keepclassmembernames |
如果拥有某成员,保留类和类成员 | -keepclasseswithmembers | -keepclasseswithmembernames |
keep:
keep
:包留类和类中的成员,防止他们被混淆keepnames
:保留类和类中的成员防止被混淆,但成员如果没有被引用将被删除keepclassmembers
:只保留类中的成员,防止被混淆和移除。keepclassmembernames
:只保留类中的成员,但如果成员没有被引用将被删除。keepclasseswithmembers
:如果当前类中包含指定的方法,则保留类和类成员,否则将被混淆。keepclasseswithmembernames
:如果当前类中包含指定的方法,则保留类和类成员,如果类成员没有被引用,则会被移除。
混淆原则
jni
方法不可混淆,因为需要与native
方法保持一致;- 反射用到的类不混淆(否则反射可能出现问题);
AndroidMainfest
中的类不混淆,四大组件和Application
的子类和Framework
层下所有的类默认不会进行混淆;Parcelable
的子类和Creato
r静态成员变量不混淆,否则会产生Android.os.BadParcelableException
异常;- 使用
GSON、fastjson
等框架时,所写的JSON
对象类不混淆,否则无法将JSON
解析成对应的对象; - 使用第三方开源库或者引用其他第三方的SDK包时,需要在混淆文件中加入对应的混淆规则;
- 有用到
WEBView
的JS调用也需要保证写的接口方法不混淆; - 使用
enum
类型时需要注意避免以下两个方法混淆,因为enum
类的特殊性,以下两个方法会被反射调用 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }
第三方库的混淆原则
一般的第三方库都有自身的混淆方案,可直接引用其自身的混淆配置即可 若无混淆配置,一般的可配置不混淆第三方库
基本配置
代码语言:javascript复制-optimizationpasses 5 #指定代码的压缩级别 0 - 7,一般都是5,无需改变
-dontusemixedcaseclassnames #不使用大小写混合,混淆后类名称为小写
#告诉Proguard 不要跳过对非公开类的处理,默认是跳过
-dontskipnonpubliclibraryclasses #如果应用程序引入的有jar包,并且混淆jar包里面的class
#不做预校验,preverify是proguard的4个功能之一
#android不需要preverify,去掉这一步加快混淆速度
-dontpreverify
-verbose #混淆时记录日志(混淆后生产映射文件 map 类名 -> 转化后类名的映射
-printmapping proguardMapping.txt #指定映射文件的名称
#指定混淆时的算法,后面的参数是一个过滤器
#这个过滤器是谷歌推荐的算法,一般也不会改变
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#类型转换错误 添加如下代码以便过滤泛型(不写可能会出现类型转换错误,一般情况把这个加上就是了),即避免泛型被混淆
-keepattributes Signature
#假如项目中有用到注解,应加入这行配置,对JSON实体映射也很重要,eg:fastjson
-keepattributes *Annotation*
#抛出异常时保留代码行数
-keepattributes SourceFile,LineNumberTable
比较完整的配置
代码语言:javascript复制-optimizationpasses 5 #指定代码的压缩级别 0 - 7,一般都是5,无需改变
-dontusemixedcaseclassnames #不使用大小写混合
#告诉Proguard 不要跳过对非公开类的处理,默认是跳过
-dontskipnonpubliclibraryclasses #如果应用程序引入的有jar包,并且混淆jar包里面的class
-verbose #混淆时记录日志(混淆后生产映射文件 map 类名 -> 转化后类名的映射
#指定混淆时的算法,后面的参数是一个过滤器
#这个过滤器是谷歌推荐的算法,一般也不会改变
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#类型转换错误 添加如下代码以便过滤泛型(不写可能会出现类型转换错误,一般情况把这个加上就是了),即避免泛型被混淆
-keepattributes Signature
#假如项目中有用到注解,应加入这行配置,对JSON实体映射也很重要,eg:fastjson
-keepattributes *Annotation*
#抛出异常时保留代码行数
-keepattributes SourceFile,LineNumberTable
#保持 native 的方法不去混淆
-keepclasseswithmembernames class * {
native <methods>;
}
#第三方开源框架以及第三方jar包中的代码不是我们的目标和关心的对象,因此我们全部忽略不进行混淆。
#EventBus的代码没必要混合
-keep class com.XXX.eventbus.** { *; }
-keep class com.XXX.event.** { *; }
-keep class com.XXX.eventbus.util.** { *; }
-keep class android.os.**{*;}
-keepclassmembers class ** {
public void onEvent*(**);
}
#litepal 数据库框架
-dontwarn org.litepal.*
-keep class org.litepal.** { *; }
-keep enum org.litepal.**
-keep interface org.litepal.** { *; }
-keep public class * extends org.litepal.**
-keepclassmembers class * extends org.litepal.crud.DataSupport{*;}
#v4包下的文件都不要混淆 -dontwarn 如果有警告也不终止
-dontwarn android.support.v4.**
-keep class android.support.v4.app.**{*;}
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment #所有fragment的子类不要去混淆
-keep public class * extends android.app.Activity #所有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
#保持指定规则的方法不被混淆(Android layout 布局文件中为控件配置的onClick方法不能混淆)
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.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*(...);
}
#保持枚举 enum 不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#保持 Parcelable 不被混淆(aidl文件不能去混淆)
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#需要序列化和反序列化的类不能被混淆(注:Java反射用到的类也不能被混淆)
-keepnames class * implements java.io.Serializable
#保护实现接口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文件不被混淆,否则,你的反射是获取不到资源id的
-keep class **.R$* { *; }
#以下针对App本身设置
#保护WebView对HTML页面的API不被混淆
-keep class **.Webview2JsInterface { *; }
#如果你的项目中用到了webview的复杂操作,最好加入
-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.WebChromeClient {
public void *(android.webkit.WebView,java.lang.String);
}
#转换JSON的JavaBean,类成员名称保护,使其不被混淆
-keepclassmembernames class com.cgv.cn.movie.common.bean.** { *; }
#保证自定义类不被混淆 XXX换成你自己的包名
-keep class com.XXX.view.** {*;}
# 保持实体数据结构接口不被混淆(也就是被GSON注解的实体结构)此处是自己接口的包名 XXX换成你自己的包名
-keep class com.XXX.model.** { *; }
#使用gson包解析数据时,出现 missing type parameter 异常,添加如下代码
-dontobfuscate #不混淆输入的类文件
-dontoptimize #不优化输入的类文件
# 不混淆 GSON
-keep class com.google.gson.** { *; }
-keep class com.google.gson.JsonObject {*;}
-keep class org.json.** {*;}
-keep class com.badlogic.** { *;}
-keep class * extends com.badlogic.gdx.utils.Json*
-keep class com.google.** {*;}
-keep class sun.misc.Unsafe { *; }
-keep class com.futurice.project.models.pojo.** { *; }