Gradle For Android(四)Gradle 編譯中神祕的混淆

Gradle For Android(二) 多渠道打包與簽名配置中說過在平常開發中進行打包apk的一些往事,打包測試、打包上傳應用商店,這些都避免不了。固然在這過程當中,除了簽名打包,固然少不了代碼的混淆了。代碼混淆,說白了就是代碼壓縮、代碼混淆以及資源壓縮的優化。依靠 ProGuard,將全部類名、方法名重命名爲無心義的簡單名稱,增長了逆向工程難度。依靠Gradle插件,移除了沒有使用的資源,減小了apk大小 。java

Gradle For Android 導讀

正文:

1、Android Gradle 混淆中的配置

通常,在咱們的application moudle中的gradle配置項buildTypes中,例如以下:android

release { //release類型
            minifyEnabled false
            // 啓用混淆
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }複製代碼

這裏minifyEnabled表示啓動混淆,通常啓動混淆編譯速度會比較慢,所以在編譯debug版本的時候通常默認不開啓。proguard-rules.pro這個表示該module默認的混淆文件,咱們能夠直接將混淆內容寫在這裏。另外,這裏也要說下shrinkResources 這個字段,當爲true的時候,表示打開資源壓縮,編譯時會去掉沒被使用的資源文件。git

修改後的配置代碼以下:github

buildTypes {//表示構建類型 通常有release debug 兩種

        debug{
            buildConfigField 'String','STATE_TEST','"debug"'//buildConfigField
            resValue "string", "test_value", "AGradle_debug"//resValue
        }

        release { //release類型
            minifyEnabled true  // 啓用混淆
            shrinkResources true  // 資源壓縮
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            buildConfigField 'String','STATE_TEST','"release"' //buildConfigField
            resValue "string", "test_value", "AGradle_release" //resValue
        }

    }複製代碼

2、混淆規則舉例

這裏貼下網上認爲比較"通用"的混淆規則以下:算法

#指定壓縮級別
-optimizationpasses 5

#不跳過非公共的庫的類成員
-dontskipnonpubliclibraryclassmembers

#混淆時採用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

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

#優化時容許訪問並修改有修飾符的類和類的成員 
-allowaccessmodification

#將文件來源重命名爲「SourceFile」字符串
-renamesourcefileattribute SourceFile
#保留行號
-keepattributes SourceFile,LineNumberTable

#保持全部實現 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中註冊,須要額外保護下
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.app.Fragment

# 保持測試相關的代碼
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**複製代碼

"通用",固然真正實現通用,除了上面的混淆規則,須要根據項目須要添加自定義的混淆規則json

例如:api

  • 第三方庫混淆規則。這個比較常見,直接接入官方說明文檔。app

  • model實體類,典型在轉化json的時候,必須保證model不被混淆,所以需加入--keep public class函數

JNI中調用的類以及方法不可被混淆佈局

  • WebView中JavaScript調用的接口不混淆

  • AndroidMainfest、四大組件以及Application的子類不混淆

  • Parcelable的子類和Creator靜態成員變量不混淆,不然會產生Android.os.BadParcelableException異常

  • Layout佈局使用的View構造函數、android:onClick等。

3、混淆結果的檢測

在開啓混淆後,經過混淆配置打包後,會在目錄:

 
  
  /outputs/mapping/ <渠道名> / <編譯類型> /.. 
 
   
 
   

 複製代碼

如圖所示:

gradle.png

下面大概說明下:

  • dump.txt :描述APK文件中全部類的內部結構
  • mapping.txt :提供混淆先後類、方法、類成員等的對照表
  • seeds.txt:列出沒有被混淆的類和成員
  • usage.txt: 列出被移除的代碼

咱們能夠根據 seeds.txt 文件檢查未被混淆的類和成員中是否已包含全部指望保留的,再根據 usage.txt 文件查看是否有被誤移除的代碼。建議讀者都試下去查看下,必須有收穫~

4、自定義混淆規則

(一) proguard 參數
  • include {filename} 從給定的文件中讀取配置參數

  • basedirectory {directoryname} 指定基礎目錄爲之後相對的檔案名稱

  • injars {class_path} 指定要處理的應用程序jar,war,ear和目錄

  • outjars {class_path} 指定處理完後要輸出的jar,war,ear和目錄的名稱

  • libraryjars {classpath} 指定要處理的應用程序jar,war,ear和目錄所須要的程序庫文件

  • dontskipnonpubliclibraryclasses 指定不去忽略非公共的庫類。

  • dontskipnonpubliclibraryclassmembers 不跳過非公共的庫的類成員

上面部分參數,平常移動開發的應用混淆中也不是常用到的,若是讀者想進一步深刻了解每一個參數實際做用,能夠去官網進行查閱,這裏筆者不作太多說明哈。

######(二)keep 參數

  • keep {Modifier(屬性,例如public,如下說明一致)} {class_specification(具體類或者成員的位置,如下說明一致)} 防止類和成員被移除或者被重命名

  • keepclassmembers {modifier} {class_specification} 防止成員被移除或者被重命名

  • keepnames {class_specification} 防止成員被重命名)

  • keepclassmembernames {class_specification} 防止擁有該成員的類和成員被移除或者被重命名

  • keepclasseswithmembernames {class_specification} 防止擁有該成員的類和成員被重命名

(三)混淆的必須知道的一些規則

一、通常規則形式:

[命令] [類] {
    [成員] 
}複製代碼

:表明相關指定條件的類,例如

  • 具體的類
  • 訪問修飾符(publicprotectedprivate
  • 通配符*,匹配任意長度字符,但不含包名分隔符(.)
  • 通配符**,匹配任意長度字符,而且包含包名分隔符(.)
  • extends,便可以指定類的基類
  • implement,匹配實現了某接口的類
  • $,內部類

成員:表明指定類後符合指定條件的成員變量,例如

  • 匹配全部構造器
  • 匹配全部域
  • 匹配全部方法
  • 通配符*,匹配任意長度字符,但不含包名分隔符(.)
  • 通配符**,匹配任意長度字符,而且包含包名分隔符(.)
  • 通配符***,匹配任意參數類型
  • ,匹配任意長度的任意類型參數。好比void test(…)就能匹配任意 void test(String a) 或者是 void test(int a, String b) 這些方法。
  • 訪問修飾符(publicprotectedprivate

舉個例子,假如須要將name.huihui.test包下全部繼承Activitypublic類及其構造函數都保持住,能夠這樣寫:

-keep public class name.huihui.test.** extends Android.app.Activity {
    
  
  
  

 
  
  } 

 複製代碼

二、經常使用自定義混淆規則

  • 不混淆某個類
-keep public class name.huihui.example.Test { *; }複製代碼
  • 不混淆某個包全部的類
-keep class name.huihui.test.** { *; }複製代碼
  • 不混淆某個類的子類
-keep public class * extends name.huihui.example.Test { *; }複製代碼
  • 不混淆全部類名中包含了「model」的類及其成員
-keep public class **.*model*.** {*;}複製代碼
  • 不混淆某個接口的實現
-keep class * implements name.huihui.example.TestInterface { *; }複製代碼
  • 不混淆某個類的構造方法
-keepclassmembers class name.huihui.example.Test { 
  public 
  
  
  

 
  
  (); } 

 複製代碼
  • 不混淆某個類的特定的方法
-keepclassmembers class name.huihui.example.Test { 
  public void test(java.lang.String); 
}複製代碼

5、自定義資源保持規則

前面說的經過shrinkResources true開啓資源壓縮後,未被使用的資源默認不會被打包進去。若是開發者是自定義保留指定的資源文件,能夠在res/raw/ 路徑下建立一個 xml 文件,例如 keep.xml,進行自定義混淆規則。

通常屬性有:

  • tools:keep 定義哪些資源須要被保留(資源之間用「,」隔開)
  • tools:discard 定義哪些資源須要被移除(資源之間用「,」隔開)
  • tools:shrinkMode 開啓嚴格模式

例如:

 

  
  
  

 複製代碼

最後感謝@ 光源coder提供的混淆手冊,總結的不錯~

但願對有些開發者有幫助~具體查看能夠github上的demo,也歡迎加入開發交流羣哈,詳情看我的簡介。下一篇是對gradle的混淆說明,歡迎讀者閱讀~

DEMO

Gradle For Android(四)Gradle編譯中神祕的混淆

傻小孩b mark共勉,寫給在成長路上奮鬥的你

相關文章
相關標籤/搜索