如需轉(zhuǎn)載請評論或簡信耕皮,并注明出處庆尘,未經(jīng)允許不得轉(zhuǎn)載
目錄
前言
一個發(fā)布后的apk可以通過反編譯代碼
允懂、反編譯資源
鳄梅、重新打包
后變成一個新的apk叠国。使用代碼混淆技術(shù)可以讓反編譯后的代碼難以閱讀(注意混淆不是讓apk不能閱讀,而是加大閱讀的難度)戴尸,還可以通過壓縮優(yōu)化代碼粟焊,從而減小APK的體積,甚至在一定程度上還能減小應用運行時的內(nèi)存
混淆原理
混淆包括四個功能校赤,shrinker
(壓縮), optimizer
(優(yōu)化),obfuscator
(混淆),preverifier
(預校驗)
- shrink: 檢測并移除沒有用到的類吆玖,變量,方法和屬性马篮;
- optimize: 優(yōu)化代碼沾乘,非入口節(jié)點類會加上
private
/static
/final
, 沒有用到的參數(shù)會被刪除,一些方法可能會變成內(nèi)聯(lián)代碼浑测。 - obfuscate: 使用短又沒有語義的名字重命名非入口類的類名翅阵,變量名,方法名迁央。入口類的名字保持不變掷匠。
- preverify: 預校驗代碼是否符合Java1.6或者更高的規(guī)范(唯一一個與入口類不相關(guān)的步驟)
哪些不應該混淆
- 使用了自定義控件那么要保證它們不參與混淆
- 使用了枚舉要保證枚舉不被混淆
- 對第三方庫中的類不進行混淆
- 運用了反射的類也不進行混淆
- 使用了
Gson
之類的工具要使JavaBean
類即實體類不被混淆 - 在引用第三方庫的時候,一般會標明庫的混淆規(guī)則的岖圈,建議在使用的時候就把混淆規(guī)則添加上去讹语,免得到最后才去找
- 有用到
WebView
的 JS 調(diào)用也需要保證寫的接口方法不混淆,原因和第一條一樣 -
Parcelable
的子類和Creator
靜態(tài)成員變量不混淆蜂科,否則會產(chǎn)生Android.os.BadParcelableException
異常 - 使用的四大組件顽决,自定義的
Application
實體類 -
JNI
中調(diào)用的類 -
Layout
布局使用的View
構(gòu)造函數(shù)(自定義控件)、android:onClick
等 -
SDK
(Jar导匣、aar)提供給外部調(diào)用的方法
混淆步驟
打開混淆
在build.gradle文件下才菠,將minifyEnabled
的值設為true
代表開啟混淆,proguardFiles
代表混淆文件的地址
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
因為開啟混淆會使編譯時間變長贡定,且不利于斷點調(diào)試赋访,所以debug模式下不開啟
擴展:release下還有一些配置是可以加上的
zipAlignEnabled true // Zipalign優(yōu)化
shrinkResources false // 刪除無用資源
debuggable false // 是否debug
signingConfig signingConfigs.relealse // 簽名
編寫混淆文件
這里提供一個模板,基本指令區(qū)
、默認保留區(qū)
蚓耽、WebView
都是可以基本所有項目共用的渠牲,我們要做的事根據(jù)自己的實際項目區(qū)完善實體類
、第三方包
步悠、與js互相調(diào)用的類
嘱兼、反射相關(guān)的類和方法
#---------------------------------1.實體類---------------------------------
#-------------------------------------------------------------------------
#---------------------------------2.第三方包-------------------------------
#-------------------------------------------------------------------------
#---------------------------------3.與js互相調(diào)用的類------------------------
#-------------------------------------------------------------------------
#---------------------------------4.反射相關(guān)的類和方法-----------------------
#----------------------------------------------------------------------------
#---------------------------------基本指令區(qū)----------------------------------
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-printmapping proguardMapping.txt
-optimizations !code/simplification/cast,!field/*,!class/merging/*
-keepattributes *Annotation*,InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
#----------------------------------------------------------------------------
#---------------------------------默認保留區(qū)---------------------------------
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-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
-keep class android.support.** {*;}
-keepclasseswithmembernames class * {
native ;
}
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public (android.content.Context);
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public (android.content.Context, android.util.AttributeSet);
public (android.content.Context, android.util.AttributeSet, int);
}
-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 * {
void *(**On*Event);
}
#----------------------------------------------------------------------------
#---------------------------------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);
}
#----------------------------------------------------------------------------
1.實體類
你可以針對單個實體類不進行混淆
-keep class 你的實體類所在的包.** { *; }
如果你的實體類都放在一個包下,如路徑是com/geekholt/demo/bean贤徒,也可以用下面的方法對整個包下的類都不混淆
-keep class com.geekholt.demo.bean.** { *; }
2.第三方包
可以在build.gradle
下查看項目引用到了哪些第三方包芹壕,一般可以到對應的官網(wǎng)查看混淆配置,這里列出了一些常用的第三方包的混淆配置
- Gson
-dontwarn com.google.**
-keep class com.google.gson.** {*;}
- Eventbus3
-keepattributes *Annotation*
-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);
}
- Glide4
-keep public class * implements com.bumptech.glide.module.AppGlideModule
-keep public class * implements com.bumptech.glide.module.LibraryGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
- 友盟統(tǒng)計
-keepclassmembers class * {
public <init> (org.json.JSONObject);
}
#友盟統(tǒng)計5.0.0以上SDK需要
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#友盟統(tǒng)計R.java刪除問題
-keep public class com.gdhbgh.activity.R$*{
public static final int *;
}
- OkHttp3
-dontwarn com.squareup.okhttp3.**
-keep class com.squareup.okhttp3.** { *;}
-dontwarn okio.**
- 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;
}
- 支付寶
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{
public *;
}
-keep class com.alipay.sdk.app.AuthTask{
public *;
}
- JPUSH
-dontwarn cn.jpush.**
-keep class cn.jpush.** {*;}
# protobuf(jpush依賴)
-dontwarn com.google.**
-keep class com.google.protobuf.** {*;}
- GreenDAO 3
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use Rx:
-dontwarn rx.**
3.與js互相調(diào)用的類
第三部分與js互調(diào)的類踢涌,工程中沒有直接跳過
-keep class 你的類所在的包.** { *; }
如果是內(nèi)部類的話,你可以這樣
-keepclasseswithmembers class 你的類所在的包.父類$子類 { ; }
-keepclasseswithmembers class com.demo.login.bean.ui.MainActivity$JSInterface {
;
}
4.與反射有關(guān)的類
工程中沒有直接跳過
-keep class 你的類所在的包.** { *; }
Keep配置
保留 | 防止被移除或者被重命名 | 防止被重命名 |
---|---|---|
類和類成員 | -keep | -keepnames |
僅類成員 | -keepmembers | -keepmembernames |
如果擁有某成員序宦,保留類和類成員 | -keepclasseswithmembers | -keepclasseswithmembernames |
如果不確定自己該用哪個的話睁壁,就用-keep
,它能保證匹配的類在壓縮這一階段不被移除互捌,并且在混淆階段不會被重新命名潘明。
- 如果只聲明保護一個類,并沒有指定受保護的成員秕噪。proguard只會保護它的類名和它的無參構(gòu)造函數(shù)钳降。其它成員依舊會被壓縮、優(yōu)化腌巾、混淆遂填。
- 如果聲明保護一個方法,proguard會把它當作程序的入口點澈蝙,方法名不會變吓坚,但它里面的代碼依舊會被優(yōu)化、混淆灯荧。
Proguard通配符
Proguard通配符 | 描述 |
---|---|
<field> | 匹配類中的所有字段 |
<method> | 匹配類中所有的方法 |
<init> | 匹配類中所有的構(gòu)造函數(shù) |
* | 匹配任意長度字符礁击,不包含包名分隔符(.) |
** | 匹配任意長度字符,包含包名分隔符(.) |
*** | 匹配任意參數(shù)類型 |
... | ... |
常用自定義混淆規(guī)則
- 不混淆某個類
-keep public class com.geekholt.example.Test { *; }
- 不混淆某個包所有的類
-keep class com.geekholt.test.** { *; }
}
- 不混淆某個類的子類
-keep public class * extends com.geekholt.example.Test { *; }
- 不混淆所有類名中包含了“model”的類及其成員
-keep public class **.*model*.** { *; }
- 不混淆某個接口的實現(xiàn)
-keep class * implements com.geekholt.example.TestInterface { *; }
- 不混淆某個類的構(gòu)造方法
-keepclassmembers class com.geekholt.example.Test {
public <init>();
}
- 不混淆某個類的特定的方法
-keepclassmembers class com.geekholt.example.Test {
public void test(java.lang.String);
}
}
- 不混淆某個類的內(nèi)部類
-keep class com.geekholt.example.Test$* {
*;
}
注意事項
混淆過的包必須進行檢查逗载,避免因混淆引入的bug
需要從代碼層面檢查哆窿。混淆打包后在
/build/outputs/mapping/release/
目錄下會輸出mapping.txt
文件撕贞,該文件提供混淆前后類更耻、方法测垛、類成員等的對照表捏膨,可以檢查重點關(guān)注的類的混淆結(jié)果需要從測試方面檢查。將混淆過的包進行全方面測試,檢查是否有 bug 產(chǎn)生