Android代碼混淆使用手冊

如需轉(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

  1. 需要從代碼層面檢查哆窿。混淆打包后在/build/outputs/mapping/release/目錄下會輸出mapping.txt文件撕贞,該文件提供混淆前后類更耻、方法测垛、類成員等的對照表捏膨,可以檢查重點關(guān)注的類的混淆結(jié)果

  2. 需要從測試方面檢查。將混淆過的包進行全方面測試,檢查是否有 bug 產(chǎn)生

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末号涯,一起剝皮案震驚了整個濱河市目胡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌链快,老刑警劉巖誉己,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異域蜗,居然都是意外死亡巨双,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門霉祸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來筑累,“玉大人,你說我怎么就攤上這事丝蹭÷冢” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵奔穿,是天一觀的道長镜沽。 經(jīng)常有香客問我,道長贱田,這世上最難降的妖魔是什么缅茉? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮男摧,結(jié)果婚禮上宾舅,老公的妹妹穿的比我還像新娘。我一直安慰自己彩倚,他們只是感情好筹我,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著帆离,像睡著了一般蔬蕊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哥谷,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天岸夯,我揣著相機與錄音,去河邊找鬼们妥。 笑死猜扮,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的监婶。 我是一名探鬼主播旅赢,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼齿桃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了煮盼?” 一聲冷哼從身側(cè)響起短纵,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎僵控,沒想到半個月后香到,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡报破,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年悠就,在試婚紗的時候發(fā)現(xiàn)自己被綠了邀窃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臊岸。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖余佃,靈堂內(nèi)的尸體忽然破棺而出蔽氨,到底是詐尸還是另有隱情藐唠,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布鹉究,位于F島的核電站宇立,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏自赔。R本人自食惡果不足惜妈嘹,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绍妨。 院中可真熱鬧润脸,春花似錦、人聲如沸他去。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灾测。三九已至爆价,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間媳搪,已是汗流浹背铭段。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留秦爆,地道東北人序愚。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像等限,于是被迫代替她去往敵國和親爸吮。 傳聞我的和親對象是個殘疾皇子芬膝,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348