快速搞定 Android Library 工程混淆問題

最近公司項目的庫須要發佈給第三方使用,代碼安全的問題就暴露出來,原來都是交由內部的其餘安卓團隊處理,可是處理方式很是暴力就是直接不混淆咱們的庫工程,這樣形成代碼很容易就被反編譯了。我只好硬上研究了一波。android

本文記錄如何進行安卓Libray工程混淆經驗。安卓混淆上的確定是大名鼎鼎的 ProGuard, 那咱們開始吧。安全

1. ProGuard基礎

1.1 ProGuard基本配置

網上關於混淆的學習記錄文章已經不少了,這邊我整理出了一些基本的配置選項app

# 指定代碼的壓縮級別,值在0-7之間。通常設置5足矣
-optimizationpasses 5

# 打印混淆信息
-verbose

# 代碼優化選項,不加該行會將沒有用到的類刪除,發佈的是代碼庫這個選項須要
# 在作混淆以前最開始會默認對代碼進行壓縮,爲了增長反編譯的難度能夠選擇不壓縮 
-dontshrink

# 保留參數的名稱和方法,該選項能夠保留調試級別的屬性。
-keepparameternames

# 過濾泛型,出現類型轉換錯誤時再啓用這個。目前的項目暫時無泛型類型,我先註釋了
#-keepattributes Signature

# 保護代碼中的Annotation不被混淆
-keepattributes *Annotation*

# 指定不去忽略非公共的庫的類的成員
-dontskipnonpubliclibraryclassmembers

# 指定不去忽略非公共的庫類(不跳過library中的非public的類)
-dontskipnonpubliclibraryclasses

複製代碼

若是有不合理或更好的選項記得告訴我喲😄很是感謝ide

1.2 Proguard 關於Keep的關鍵字

關鍵字 描述
keep 保留類和類中的成員,防止被混淆或移除
keepnames 保留類和類中的成員,防止被混淆,成員沒有被引用會被移除
keepclassmembers 只保留類中的成員,防止被混淆或移除
keepclassmembernames 只保留類中的成員,防止被混淆,成員沒有引用會被移除
keepclasseswithmembers 保留類和類中的成員,防止被混淆或移除,保留指明的成員
keepclasseswithmembernames 保留類和類中的成員,防止被混淆,保留指明的成員,成員沒有引用會被移除

這邊值得注意的是第一種關鍵字 keep函數

若是你只想保留類的名字咱們能夠直接配置以下:工具

# 以下配置只保留了類的名字MyClass,類的全部成員依然會被混淆
-keep class com.your.class.name.MyClass

# 只有寫明具體類成員的匹配規則才能讓身體裏面的混淆規則生效。
-keep class com.your.class.name.MyClass {*;}

# 舉個🌰(例子)以下配置是我用來處理庫工程類,指望全部public方法都開放出來,不但願被混淆的配置
-keep class com.your.class.name.MyClass {
	  public <fields>;
    public <methods>;
    public static final <fields>;
}

複製代碼

1.3 Proguard通配符

通配符 描述
<field> 匹配類中的全部字段
<method> 匹配類中全部的方法
<init> 匹配類中全部的構造函數
* 匹配任意長度字符,不包含包名分隔符(.)
** 匹配任意長度字符,包含包名分隔符(.)
*** 匹配任意參數類型
... 其餘

1.4 Android 特有的 @Keep 標籤

默認狀況下就算寫了@Keep仍是會被混淆的,由於默認狀況下 Android Studio 並無開啓該選項 在混淆的配置文件 proguard-rules.pro 中加入如下配置:學習

-keep class android.support.annotation.Keep

-keep @android.support.annotation.Keep class *

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}
複製代碼

⚠️注意: 目前從網上的一些資料顯示若是將 @Keep 添加到類上邏輯上是隻不混淆類的名稱,可是從實際使用結果上看,若是@Keep 添加到類名上,則整個類沒法被混淆。測試

1.5 保留Native方法

JNI層相關的方法理論上就算沒有調用到,也是不能夠混淆後直接被移除的,不然 NDK 中採用註冊模式的JNI方法的時候會出現註冊失敗的問題。優化

-keepclasseswithmembers class * {
    native <methods>;
}
複製代碼

結合Keep關鍵字說明,經過以上配置能夠保留類和類中被指名的native方法成員函數。ui

1.6 保留Library工程的UI相關類

二次封裝的庫工程難免有些UI相關的工具類,這部分若是被混淆了,引入方就變成沒法正常使用。咱們須要對這部分也進行不混淆的控制。

-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
-keepclassmembers class * extends android.app.Activity{
    public void *(android.view.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);
}
# 不混淆資源部分的配置
-keep class **.R$* {*;}
複製代碼

2. 啓用混淆

默認在Android Studio的配置下是沒有啓用的咱們須要手動開啓。

android {
		...
    buildTypes {
        release {
            minifyEnabled true // open ProGuard
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
複製代碼

可是這個只有在每次Release打包以後才生效,咱們有時候須要在Debug的時候就進行混淆測試,這時咱們能夠添加一個新的buildType

android {
		...
    buildTypes {
        release {
            minifyEnabled true // open ProGuard
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debugMini {
            initWith debug
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            matchingFallbacks = ['debug']
        }

    }
}
複製代碼

這樣咱們就能夠在默認Run的時候選擇buildVariants指定咱們剛剛新加入的debugMini

3. 總結

這篇短短的文章應該能夠幫助很多小夥伴跳出深坑,可是更多使用仍是須要在工做中去積累。一些小小的體會不求一條混淆命令吃了所有的混淆配置,咱們須要精細的配置,通用的配置總會有些不如意地方,那咱們就針對單個Class進行一對一的配置,我相信能夠獲得更好的混淆結果。

本次的分享大概就這些內容了歡迎你們留言一塊兒討論學習與進步。

謝謝你們。

相關文章
相關標籤/搜索