Android混淆

混淆介紹

Proguard是一個(gè)Java類文件壓縮器、優(yōu)化器、混淆器、預(yù)校驗(yàn)器。壓縮環(huán)節(jié)會(huì)檢測(cè)以及移除沒有用到的類、字段、方法以及屬性。優(yōu)化環(huán)節(jié)會(huì)分析以及優(yōu)化方法的字節(jié)碼?;煜h(huán)節(jié)會(huì)用無意義的短變量去重命名類、變量、方法。這些步驟讓代碼更精簡(jiǎn),更高效,也更難被逆向(破解)。

混淆后默認(rèn)會(huì)在工程目錄app/build/outputs/mapping/release(debug)下生成一個(gè)mapping.txt文件,這就是混淆規(guī)則,我們可以根據(jù)這個(gè)文件把混淆后的代碼反推回源本的代碼,所以這個(gè)文件很重要,注意保護(hù)好。原則上,代碼混淆后越亂越無規(guī)律越好,但有些地方我們是要避免混淆的,否則程序運(yùn)行就會(huì)出錯(cuò)。

ProGuard常用操作

壓縮(Shrinking)

壓縮(Shrinking):默認(rèn)開啟,用以減小應(yīng)用體積,移除未被使用的類和成員,并且會(huì)在優(yōu)化動(dòng)作執(zhí)行之后再次執(zhí)行(因?yàn)閮?yōu)化后可能會(huì)再次暴露一些未被使用的類和成員)。

#關(guān)閉壓縮
-dontshrink

優(yōu)化(Optimization)

優(yōu)化(Optimization):默認(rèn)開啟,在字節(jié)碼級(jí)別執(zhí)行優(yōu)化,讓應(yīng)用運(yùn)行的更快。

#關(guān)閉優(yōu)化
#-dontoptimize  

#表示proguard對(duì)代碼進(jìn)行迭代優(yōu)化的次數(shù),Android一般為5
-optimizationpasses n  

混淆(Obfuscation)

混淆(Obfuscation):默認(rèn)開啟,增大反編譯難度,類和類成員會(huì)被隨機(jī)命名,除非用keep保護(hù)。

-dontobfuscate  #關(guān)閉混淆

-Keep

一顆星表示只是保持該包下的類名,而子包下的類名還是會(huì)被混淆;

-keep class pr.tongson.bean.*

兩顆星表示把本包和所含子包下的類名都保持;

-keep class pr.tongson.bean.**

(上面兩種方式保持類后,會(huì)發(fā)現(xiàn)類名雖然未混淆,但里面的具體方法和變量命名還是變了)

既可以保持該包下的類名,又可以保持類里面的內(nèi)容不被混淆;

-keep class pr.tongson.bean.*{*;}

既可以保持該包及子包下的類名,又可以保持類里面的內(nèi)容不被混淆;

-keep class pr.tongson.bean.**{*;}

保持某個(gè)類名不被混淆(但是內(nèi)部?jī)?nèi)容會(huì)被混淆)

-keep class pr.tongson.bean.KeyBoardBean

保持某個(gè)類的 類名及內(nèi)部的所有內(nèi)容不會(huì)混淆

-keep class pr.tongson.bean.KeyBoardBean{*;}

保持類中特定內(nèi)容,而不是所有的內(nèi)容可以使用如下:

-keep class pr.tongson.bean.KeyBoardBean{
  #匹配所有構(gòu)造器
  <init>;
  #匹配所有域
  <fields>;
  #匹配所有方法
  <methods>;
}

(上面就保持住了KeyBoardBean這個(gè)類中的所有的構(gòu)造方法、變量、和方法)

可以在<fields>或<methods>前面加上private 、public、native等來進(jìn)一步指定不被混淆的內(nèi)容

-keep class pr.tongson.algorithm.Calculate{
  #保持該類下所有的共有方法不被混淆
  public <methods>;
  #保持該類下所有的共有內(nèi)容不被混淆
  public *;
  #保持該類下所有的私有方法不被混淆
  private <methods>;
  #保持該類下所有的私有內(nèi)容不被混淆
  private *;
  #保持該類的String類型的構(gòu)造方法
  public <init>(java.lang.String);
}

在方法后加入?yún)?shù),限制特定的方法(經(jīng)測(cè)試:僅限于構(gòu)造方法可以混淆)

-keep class pr.tongson.algorithm.Calculate{
      public <init>(String);
}

要保留一個(gè)類中的內(nèi)部類不被混淆需要用 $ 符號(hào)

#保持Calculate中的MyClass不被混淆
-keep class pr.tongson.algorithm.Calculate$MyClass{*;}

使用Java的基本規(guī)則來保護(hù)特定類不被混淆,比如用extends,implement等這些Java規(guī)則,如下:保持Android底層組件和類不要混淆

-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.view.View

如果不需要保持類名,只需要保持該類下的特定方法保持不被混淆,需要使用keepclassmembers,而不是keep,因?yàn)閗eep方法會(huì)保持類名。

#保持ProguardTest類下test(String)方法不被混淆
-keepclassmembernames class pr.tongson.algorithm.Calculate{
  public void test(java.lang.String);
}

如果擁有某成員,保留類和類成員

-keepclasseswithmembernames class pr.tongson.algorithm.Calculate

注意事項(xiàng)

jni方法

jni方法不可混淆,因?yàn)閚ative方法是要完整的包名類名方法名來定義的,不能修改,否則找不到;

#保持native方法不被混淆
-keepclasseswithmembernames class * {    
  native <methods>; 
}

反射

反射用到的類混淆時(shí)需要注意:只要保持反射用到的類名和方法即可,并不需要將整個(gè)被反射到的類都進(jìn)行保持

AndroidMainfest中的類

AndroidMainfest中的類不混淆,所以四大組件和Application的子類和Framework層下所有的類默認(rèn)不要進(jìn)行混淆。

自定義的View

自定義的View默認(rèn)也不會(huì)被混淆

JSON對(duì)象類

與服務(wù)端交互時(shí),使用GSON、fastjson等框架解析服務(wù)端數(shù)據(jù)時(shí),所寫的JSON對(duì)象類不混淆,否則無法將JSON解析成對(duì)應(yīng)的對(duì)象;

第三方

使用第三方開源庫(kù)或者引用其他第三方的SDK包時(shí),如果有特別要求,也需要在混淆文件中加入對(duì)應(yīng)的混淆規(guī)則;官方文檔一般都有混淆規(guī)則的,復(fù)制粘貼下即可。

有用到WebView的JS

有用到WebView的JS調(diào)用也需要保證寫的接口方法不混淆,原因和第一條一樣;

Parcelable的子類和Creator靜態(tài)成員變量

Parcelable的子類和Creator靜態(tài)成員變量不混淆,否則會(huì)產(chǎn)生Android.os.BadParcelableException異常;

-keep class * implements Android.os.Parcelable { 
  # 保持Parcelable不被混淆            
  public static final Android.os.Parcelable$Creator *;
}

enum

使用enum類型時(shí)需要注意避免以下兩個(gè)方法混淆,因?yàn)閑num類的特殊性,以下兩個(gè)方法會(huì)被反射調(diào)用,見第二條規(guī)則。

-keepclassmembers enum * {  
  public static **[] values();  
  public static ** valueOf(java.lang.String);  
}

注解不能混淆

建議

建議:
發(fā)布一款應(yīng)用除了設(shè)minifyEnabled為ture,你也應(yīng)該設(shè)置zipAlignEnabled為true,像Google Play強(qiáng)制要求開發(fā)者上傳的應(yīng)用必須是經(jīng)過zipAlign的,zipAlign可以讓安裝包中的資源按4字節(jié)對(duì)齊,這樣可以減少應(yīng)用在運(yùn)行時(shí)的內(nèi)存消耗。

混淆情況記錄

例子中使用:classA和classB,在加混淆的情況下多種結(jié)果:

  • 如果classA沒有被keep,則不會(huì)看到classA的class文件

  • 如果classA沒有被keep,classB被保持,同時(shí)classB引用到了classA,這個(gè)時(shí)候能夠看到被混淆的classA的class文件,如顯示為a

  • 如果classA中通過反射,獲取到classB,那么classB的類名及反射用到的方法必須keep住

  • jar包混淆,暴露出的類、方法、方法的參數(shù)需要keep住

  • 情況說明:工程Demo依賴了小米渠道的依賴,小米依賴又依賴了Common,對(duì)Common進(jìn)行混淆但是不對(duì)小米渠道混淆,那么小米的依賴中使用到的Common中的類都需要keep住

參考

Proguard官方文檔

Android混淆從入門到精通

Android混淆——了解這些就夠了

runoob

模版

#指定壓縮級(jí)別
-optimizationpasses 5

#不跳過非公共的庫(kù)的類成員
-dontskipnonpubliclibraryclassmembers

#混淆時(shí)采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

#把混淆類中的方法名也混淆了
-useuniqueclassmembernames

#優(yōu)化時(shí)允許訪問并修改有修飾符的類和類的成員
-allowaccessmodification

#將文件來源重命名為“SourceFile”字符串
-renamesourcefileattribute SourceFile
#保留行號(hào)
-keepattributes SourceFile,LineNumberTable
#保持泛型
-keepattributes Signature

#保持所有實(shí)現(xiàn) Serializable 接口的類成員
-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();
}

#Fragment不需要在AndroidManifest.xml中注冊(cè),需要額外保護(hù)下
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.app.Fragment

# 保持測(cè)試相關(guān)的代碼
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**

語(yǔ)法

-include {filename}    從給定的文件中讀取配置參數(shù) 
-basedirectory {directoryname}    指定基礎(chǔ)目錄為以后相對(duì)的檔案名稱 
-injars {class_path}    指定要處理的應(yīng)用程序jar,war,ear和目錄 
-outjars {class_path}    指定處理完后要輸出的jar,war,ear和目錄的名稱 
-libraryjars {classpath}    指定要處理的應(yīng)用程序jar,war,ear和目錄所需要的程序庫(kù)文件 
-dontskipnonpubliclibraryclasses    指定不去忽略非公共的庫(kù)類。 
-dontskipnonpubliclibraryclassmembers    指定不去忽略包可見的庫(kù)類的成員。

保留選項(xiàng) 
-keep {Modifier} {class_specification}    保護(hù)指定的類文件和類的成員 
-keepclassmembers {modifier} {class_specification}    保護(hù)指定類的成員,如果此類受到保護(hù)他們會(huì)保護(hù)的更好
-keepclasseswithmembers {class_specification}    保護(hù)指定的類和類的成員,但條件是所有指定的類和類成員是要存在。 
-keepnames {class_specification}    保護(hù)指定的類和類的成員的名稱(如果他們不會(huì)壓縮步驟中刪除) 
-keepclassmembernames {class_specification}    保護(hù)指定的類的成員的名稱(如果他們不會(huì)壓縮步驟中刪除) 
-keepclasseswithmembernames {class_specification}    保護(hù)指定的類和類的成員的名稱,如果所有指定的類成員出席(在壓縮步驟之后) 
-printseeds {filename}    列出類和類的成員-keep選項(xiàng)的清單,標(biāo)準(zhǔn)輸出到給定的文件 

壓縮 
-dontshrink    不壓縮輸入的類文件 
-printusage {filename} 
-dontwarn   如果有警告也不終止
-whyareyoukeeping {class_specification}     

優(yōu)化 
-dontoptimize    不優(yōu)化輸入的類文件 
-assumenosideeffects {class_specification}    優(yōu)化時(shí)假設(shè)指定的方法,沒有任何副作用 
-allowaccessmodification    優(yōu)化時(shí)允許訪問并修改有修飾符的類和類的成員 

混淆 
-dontobfuscate    不混淆輸入的類文件 
-printmapping {filename} 
-applymapping {filename}    重用映射增加混淆 
-obfuscationdictionary {filename}    使用給定文件中的關(guān)鍵字作為要混淆方法的名稱 
-overloadaggressively    混淆時(shí)應(yīng)用侵入式重載 
-useuniqueclassmembernames    確定統(tǒng)一的混淆類的成員名稱來增加混淆 
-flattenpackagehierarchy {package_name}    重新包裝所有重命名的包并放在給定的單一包中 
-repackageclass {package_name}    重新包裝所有重命名的類文件中放在給定的單一包中 
-dontusemixedcaseclassnames    混淆時(shí)不會(huì)產(chǎn)生形形色色的類名 
-keepattributes {attribute_name,...}    保護(hù)給定的可選屬性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and 

InnerClasses. 
-renamesourcefileattribute {string}    設(shè)置源文件中給定的字符串常量

常見錯(cuò)誤

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Csdn 混淆介紹 Proguard是一個(gè)Java類文件壓縮器、優(yōu)化器、混淆器、預(yù)校驗(yàn)器。壓縮環(huán)節(jié)會(huì)檢測(cè)以及移除沒...
    落魄的安卓開發(fā)閱讀 16,455評(píng)論 13 170
  • 混淆是打包過程中最重要的流程之一,在沒有特殊原因的情況下,所有 app 都應(yīng)該開啟混淆。 首先,這里說的的混淆其實(shí)...
    瀟瀟code閱讀 1,786評(píng)論 0 5
  • 本文介紹了Android中開啟混淆的好處,混淆的工作原理及如何解決開啟混淆后遇到的問題。 原文鏈接:Trouble...
    于衛(wèi)國(guó)閱讀 12,724評(píng)論 0 14
  • 概述 混淆是Android Apk打包過程中的一個(gè)重要步驟,默認(rèn)情況下,打包都是需要混淆過程的。?Android ...
    androidjp閱讀 2,756評(píng)論 1 13
  • 誰(shuí)也沒見過我,從出生起我就呆在圖書館的一個(gè)黑角落里。沒見過父母沒見過其他同類,生存靠垃圾桶生活靠逃亡。 有次在洗手...
    西瓜戰(zhàn)士閱讀 205評(píng)論 0 0

友情鏈接更多精彩內(nèi)容