应用混淆(ProGuard)
- ProGuard是一个免费的JAVA类文件压缩,优化,混淆器。
- 它探测并删除没有使用的类,字段,方法和属性,它删除没有用的说明并使用字节码得到最大优化,它使用无意义的名字重命名类,字段和方法。
我们先来介绍下ProGuard
我们为啥要使用ProGuard?
- 优化应用:创建紧凑的代码文档是为了更快的网络传输,快速装载和更小的内存占用;
- 防止反向:创建的程序和程序库很难使用反向工程;
- 预处理应用:充分利用JAVA的快捷加载的优点来提前检测和返回JAVA中存在的类文件。
这是我开发中用到的一些混淆规则,大家可以根据需要复制到自己的项目中的混淆规则的文件中即可。
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\androidstudio\sdk\android-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 *;
#}
#-keepattributes EnclosingMethod
#
#--------------------------基本不用动区域--------------------------
#
# -----------------------------基本 -----------------------------
#
# 指定代码的压缩级别 0 - 7(指定代码进行迭代优化的次数,在Android里面默认是5,这条指令也只有在可以优化时起作用。)
-optimizationpasses 5
# 混淆时不会产生形形色色的类名(混淆时不使用大小写混合类名)
-dontusemixedcaseclassnames
# 指定不去忽略非公共的库类(不跳过library中的非public的类)
-dontskipnonpubliclibraryclasses
# 指定不去忽略包可见的库类的成员
-dontskipnonpubliclibraryclassmembers
#不进行优化,建议使用此选项,
-dontoptimize
# 不进行预校验,Android不需要,可加快混淆速度。
-dontpreverify
# 屏蔽警告
-ignorewarnings
# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !retCode/simplification/arithmetic,!field/*,!class/merging/*
# 保护代码中的Annotation不被混淆
-keepattributes *Annotation*
# 避免混淆泛型, 这在JSON实体映射时非常重要
-keepattributes Signature
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
#优化时允许访问并修改有修饰符的类和类的成员,这可以提高优化步骤的结果。
# 比如,当内联一个公共的getter方法时,这也可能需要外地公共访问。
# 虽然java二进制规范不需要这个,要不然有的虚拟机处理这些代码会有问题。当有优化和使用-repackageclasses时才适用。
#指示语:不能用这个指令处理库中的代码,因为有的类和类成员没有设计成public ,而在api中可能变成public
-allowaccessmodification
#当有优化和使用-repackageclasses时才适用。
-repackageclasses ''
# 混淆时记录日志(打印混淆的详细信息)
# 这句话能够使我们的项目混淆后产生映射文件
# 包含有类名->混淆后类名的映射关系
-verbose
#
# ----------------------------- 默认保留 -------------------------
#
# 保持哪些类不被混淆
#继承activity,application,service,broadcastReceiver,contentprovider....不进行混淆
-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
# 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.**
# javax.annotation
#表示不混淆任何包含native方法的类的类名以及native方法名,这个和我们刚才验证的结果是一致
-keepclasseswithmembernames class * {
native <methods>;
}
#这个主要是在layout 中写的onclick方法android:onclick="onClick",不进行混淆
#表示不混淆Activity中参数是View的方法,因为有这样一种用法,在XML中配置android:onClick=”buttonClick”属性,
#当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,如果这个方法被混淆的话就找不到了
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
#表示不混淆枚举中的values()和valueOf()方法,枚举我用的非常少,这个就不评论了
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#表示不混淆任何一个View中的setXxx()和getXxx()方法,
#因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了。
-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);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
#表示不混淆Parcelable实现类中的CREATOR字段,
#毫无疑问,CREATOR字段是绝对不能改变的,包括大小写都不能变,不然整个Parcelable工作机制都会失败。
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 这指定了继承Serizalizable的类的如下成员不被移除混淆
-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();
}
# 保留R下面的资源
-keep class **.R$* {
*;
}
#不混淆资源类下static的
-keepclassmembers class **.R$* {
public static <fields>;
}
# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
# 保留我们自定义控件(继承自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);
}
#
#-----混淆保护自己项目的部分代码以及引用的第三方jar包 -------------------
#
#-libraryjars libs/source.aar
#-libraryjars libs/tbs_sdk_thirdapp_v4.3.0.67_43967_sharewithdownloadwithfile_withoutGame_obfs_20200923_120452.jar
#-libraryjars libs/com.heytap.msp-push-2.1.0.aar
#-libraryjars libs/jcore-android-2.7.6.jar
#-libraryjars libs/jpush-android-4.0.6.jar
#-libraryjars libs/jpush-android-plugin-huawei-v4.0.6.jar
#-libraryjars libs/jpush-android-plugin-meizu-v3.7.0.jar
#-libraryjars libs/jpush-android-plugin-oppo-v4.0.6.jar
#-libraryjars libs/jpush-android-plugin-vivo-v4.0.6.jar
#-libraryjars libs/jpush-android-plugin-xiaomi-v4.0.6.jar
#-libraryjars libs/meizu-push-3.9.0.jar
#-libraryjars libs/MiPush_SDK_Client_3_8_5.jar
#-libraryjars libs/push_sdk_v3.0.0.jar
#-libraryjars libs/LiteAVSDK_Professional_8.4.9947.aar
#-libraryjars libs/alipaysdk-15.8.02.210308182128.aar
#-libraryjars libs/umeng-analytics-8.1.2.jar
#-libraryjars libs/umeng-common-2.1.0.jar
#-libraryjars libs/umeng-share-core-6.9.6.jar
#-libraryjars libs/umeng-share-QQ-simplify-6.9.6.jar
#-libraryjars libs/umeng-sharetool-6.9.6.jar
#-libraryjars libs/umeng-share-wechat-simplify-6.9.6.jar
#-libraryjars libs/wechat-sdk-android-with-mta-1.0.2.jar
#
#--------------------- 腾讯X5 WebView -------------------
#
-dontwarn dalvik.**
-dontwarn com.tencent.smtt.**
-keep class com.tencent.smtt.** {
*;
}
-keep class com.tencent.tbs.** {
*;
}
#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.WebViewClient {
public void *(android.webkit.WebView, jav.lang.String);
}
#在app中与HTML5的JavaScript的交互进行特殊处理
#我们需要确保这些js要调用的原生方法不能够被混淆,于是我们需要做如下处理:
-keepclassmembers class com.centit.theatre.dsBridge.JsApi {
<methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsAudioApi {
<methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsDeviceApi {
<methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsEchoApi {
<methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsNavigationApi {
<methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsNotificationApi {
<methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsPayApi {
<methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsPermissionApi {
<methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsPushApi {
<methods>;
}
-keepclassmembers class com.centit.theatre.dsBridge.JsUtilApi {
<methods>;
}
#
#---------------------------------实体类------------------------------
#--------(实体Model不能混淆,否则找不到对应的属性获取不到值)----------------
#
-dontwarn centit.theatre.model.**
#对含有反射类的处理
-keep class centit.theatre.model.** { *; }
#
# ----------------------------- 其他的 -----------------------------
#
# 删除代码中Log相关的代码
-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(...);
}
# 保持测试相关的代码
#-dontnote junit.framework.**
#-dontnote junit.runner.**
#-dontwarn android.test.**
#-dontwarn android.support.test.**
#-dontwarn org.junit.**
#
# ----------------------------- 第三方 -----------------------------
#
# FastJson
-dontwarn com.alibaba.fastjson.**
-keep class com.alibaba.fastjson.**{*; }
# Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Exceptions
#-------------- okhttp3 -------------
-dontwarn com.squareup.okhttp.**
-keep class com.squareup.okhttp.{*;}
-keep interface com.squareup.okhttp.** { *; }
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
# Okio
-dontwarn com.squareup.**
-dontwarn okio.**
-keep public class org.codehaus.* { *; }
-keep public class java.nio.* { *; }
#----------okhttp end--------------
# butterknife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
#Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
#eventbus
-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);
}
# 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;
}
# rxandroid-1.2.1
-keepclassmembers class rx.android.**{*;}
# 路由框架 ARouter
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider
# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
-keep class * implements com.alibaba.android.arouter.facade.template.IProvider
# 报错后添加
-keep class * implements com.alibaba.android.arouter.facade.model.RouteMeta
-dontwarn com.alibaba.android.arouter.facade.model.RouteMeta
# autosize适配方案
-keep class me.jessyan.autosize.** { *; }
-keep interface me.jessyan.autosize.** { *; }
# litepal数据库框架
-keep class org.litepal.** {*;}
-keep class * extends org.litepal.crud.DataSupport {*;}
-keep class * extends org.litepal.crud.LitePalSupport {*;}
#jpush极光推送
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
# AndroidCodeUtils
-keep class com.blankj.utilcode.** { *; }
-keepclassmembers class com.blankj.utilcode.** { *; }
-dontwarn com.blankj.utilcode.**
# Bugly
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# Addidional for x5.sdk classes for apps
-keep class com.tencent.smtt.export.external.**{*;}
-keep class com.tencent.tbs.video.interfaces.IUserStateChangedListener {*;}
-keep class com.tencent.smtt.sdk.CacheManager {public *;}
-keep class com.tencent.smtt.sdk.CookieManager {public *;}
-keep class com.tencent.smtt.sdk.WebHistoryItem {public *;}
-keep class com.tencent.smtt.sdk.WebViewDatabase {public *;}
-keep class com.tencent.smtt.sdk.WebBackForwardList {public *;}
-keep public class com.tencent.smtt.sdk.WebView {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebView$HitTestResult {
public static final <fields>;
public java.lang.String getExtra();
public int getType();
}
-keep public class com.tencent.smtt.sdk.WebView$WebViewTransport {
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebView$PictureListener {
public <fields>;
public <methods>;
}
-keepattributes InnerClasses
-keep public enum com.tencent.smtt.sdk.WebSettings$** {*;}
-keep public enum com.tencent.smtt.sdk.QbSdk$** {*;}
-keep public class com.tencent.smtt.sdk.WebSettings {
public *;
}
-keep public class com.tencent.smtt.sdk.ValueCallback {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebViewClient {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.DownloadListener {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebChromeClient {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebChromeClient$FileChooserParams {
public <fields>;
public <methods>;
}
-keep class com.tencent.smtt.sdk.SystemWebChromeClient{
public *;
}
# 1. extension interfaces should be apparent
-keep public class com.tencent.smtt.export.external.extension.interfaces.* {
public protected *;
}
# 2. interfaces should be apparent
-keep public class com.tencent.smtt.export.external.interfaces.* {
public protected *;
}
-keep public class com.tencent.smtt.sdk.WebViewCallbackClient {
public protected *;
}
-keep public class com.tencent.smtt.sdk.WebStorage$QuotaUpdater {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebIconDatabase {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebStorage {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.DownloadListener {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.QbSdk {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.QbSdk$PreInitCallback {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.CookieSyncManager {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.Tbs* {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.utils.LogFileUtils {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.utils.TbsLog {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.utils.TbsLogClient {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.CookieSyncManager {
public <fields>;
public <methods>;
}
# Added for game demos
-keep public class com.tencent.smtt.sdk.TBSGamePlayer {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.TBSGamePlayerClient* {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.TBSGamePlayerClientExtension {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.TBSGamePlayerService* {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.utils.Apn {
public <fields>;
public <methods>;
}
-keep class com.tencent.smtt.** {*;}
# end
# TakePhoto,拍照、相册、裁剪
-keep class com.jph.takephoto.** { *; }
-dontwarn com.jph.takephoto.**
-keep class com.darsh.multipleimageselect.** { *; }
-dontwarn com.darsh.multipleimageselect.**
-keep class com.soundcloud.android.crop.** { *; }
-dontwarn com.soundcloud.android.crop.**
# Blankj工具类
-keep class com.blankj.utilcode.** { *; }
-keepclassmembers class com.blankj.utilcode.** { *; }
-dontwarn com.blankj.utilcode.**
# 微信过滤出去
-dontwarn com.tencent.**
-keep class com.tencent.** { *; }
# BaseRecyclerViewAdapterHelper
-keep class com.chad.library.adapter.** {
*;
}
-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
-keepclassmembers class **$** extends com.chad.library.adapter.base.BaseViewHolder {
<init>(...);
}
# banner 的混淆代码
-keep class com.youth.banner.** {
*;
}
#leakcanary
-dontwarn com.squareup.haha.guava.**
-dontwarn com.squareup.haha.perflib.**
-dontwarn com.squareup.haha.trove.**
-dontwarn com.squareup.leakcanary.**
-keep class com.squareup.haha.** { *; }
-keep class com.squareup.leakcanary.** { *; }
# Retrolambda
-dontwarn java.lang.invoke.*
# Gson
-keep class com.google.gson.stream.** { *; }
-keepattributes EnclosingMethod
-keep class org.xz_sale.entity.**{*;}
-keep class com.google.gson.** {*;}
-keep class com.google.**{*;}
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-keep class com.google.gson.examples.android.model.** { *; }
# Android10标识
-keep class com.bun.miitmdid.core.** {*;}
# 找不到类
-dontwarn com.trello.rxlifecycle2.LifecycleProvider
-dontwarn com.trello.rxlifecycle2.LifecycleTransformer
-dontwarn com.trello.rxlifecycle2.OutsideLifecycleException
-dontwarn com.trello.rxlifecycle2.RxLifecycle
# 微信
-keep class com.tencent.mm.opensdk.** {
*;
}
-keep class com.tencent.wxop.** {
*;
}
-keep class com.tencent.mm.sdk.** {
*;
}
#PictureSelector 2.0
-keep class com.luck.picture.lib.** { *; }
#Ucrop
-dontwarn com.yalantis.ucrop**
-keep class com.yalantis.ucrop** { *; }
-keep interface com.yalantis.ucrop** { *; }
# kongzue弹出框
-keep class com.kongzue.dialog.** { *; }
-dontwarn com.kongzue.dialog.**
# AndroidX版本请使用如下配置:
-dontwarn androidx.renderscript.**
-keep public class androidx.renderscript.** { *; }
# 乐播Lebo
-keep class com.hpplay.**{*;}
-keep class com.hpplay.**$*{*;}
-dontwarn com.hpplay.**
# 极光推送
-dontwarn cn.jiguang.**
-keep class cn.jiguang.** { *; }
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
-keep public class com.sina.** {
*;
}
# mp4parser相关
-dontwarn com.googlecode.mp4parser.**
-keep public class com.googlecode.mp4parser.** {*;}
-keep public class org.googlecode.mp4parser.** {*;}
-keep public class com.coremedia.** {*;}
-keep public class com.googlecode.** {*;}
# permissions权限工具
-dontwarn com.hjq.permissions.**
-keep class com.hjq.permissions.** {*;}
当设置完毕混淆规则后,紧接着在build.gradle配置如下:
// 移除无用的资源文件
shrinkResources true
// ZipAlign 优化
zipAlignEnabled true
// 设置混淆
minifyEnabled true
// 混淆配置
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-app.pro'
把属性设置为tue就大功告成啦,接着就可以打包啦。
打完包后我们把apk文件后缀名改成zip,然后解压,找到classes.dex文件。如图:
若想要阅读该文件的内容,可以使用 dex2jar 和 jd-gui 工具。下面介绍这两种工具的安装及使用方法。
一、安装 dex2jar
- 下载地址:https://sourceforge.net/projects/dex2jar/。
- 下载之后解压即可。
二、使用 dex2jar
- 1. 将需要反编译的dex文件(这里是classes.dex)复制到 dex2jar 解压目录下。
- 2. 打开命令行进入 d2j-dex2jar.bat 文件所在目录,输入命令 d2j-dex2jar.bat classes.dex 。
- 运行此命令需要安装JDK环境变量哦,否则提示不是内部命令。
- 此时可以看到目录中多出了classes-dex2jar.jar文件。
- 那么如何查看该jar文件的内容呢?此时就需要安装 jd-gui 。
三、安装 jd-gui
- 下载地址: http://jd.benow.ca/ 。
- 同样,下载之后解压即可。
四、使用 jd-gui
双击运行 jd-gui.exe 文件,将jar文件拖到工作区即可打开。
打开后就可以看混淆后的文件啦~~~