Eclipse與Android原始碼中ProGuard工具的使用(程式碼混淆)

jia635發表於2014-07-29

由於工作需要,這兩天和同事在研究android下面的ProGuard工具的使用,通過檢視android官網對該工具的介紹以及網路上其它相關資料,再加上自己的親手實踐,算是有了一個基本瞭解。下面將自己的理解和認識簡要的做個筆記,有異議或者不解的,可以直接留言。

什麼是ProGuard工具?


ProGuard是android提供的一個免費的工具,它能夠移除工程中一些沒用的程式碼,或者使用語義上隱晦的名稱來重新命名程式碼中的類、欄位和函式等,達到壓縮、優化和混淆程式碼的功能。具體來說,使用ProGuard工具,可以達到下面兩個目的:

  • 刪除了原始檔中沒有呼叫的那部分程式碼,最大化的精簡了位元組碼檔案,使得最終生成的apk檔案更小。
  • 使用語義混淆的命名替換了程式碼中的類、欄位和函式等,使得其他人無法反編譯獲取原始碼,起到對程式碼的保護作用。

我看網上有不少人根據ProGuard工具的作用,直接稱呼其為“混淆程式碼工具”,本文也暫時用這個詞簡稱。

更多的理解,可以參考ProGuard工具的官方文件地址:http://developer.android.com/tools/help/proguard.html

ProGuard工具的整合與使用環境


其實,ProGuard工具是已經整合到我們android系統中的,所以不需要使用者手動的去整合。但是有一點需要注意,僅在程式處於Release模式時ProGuard才有效,反之在Debug模式是不能通過ProGuard來混淆程式碼的。

根據ProGuard的具體使用環境,我分在Eclipse工具和android原始碼兩種編譯環境淺談ProGuard的使用方法。

Eclipse環境中ProGuard的使用


以我電腦的android4.0環境為例,當我們在Eclipse中新建一個專案,或者匯入一個已存在專案(保證當前專案沒有語法錯誤)後,在工程的根目錄,會自動生成兩個ProGuard的混淆檔案:proguard-project.txt和project.properties(在老版本的ADT中,只會生成一個叫proguard.cfg的檔案)。我們先看下檔案project.properties :

# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt

# Project target.
target=android-16
看後面一段註釋:To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home) ,意指要讓ProGuard 來壓縮和混淆程式碼,把這句註釋去掉即可!所以,我們只要把下面一句註釋取消即可,如下所示:

看後面一段註釋:To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home) ,意指要讓ProGuard 來壓縮和混淆程式碼,把這句註釋去掉即可!所以,我們只要把下面一句註釋取消即可,如下所示:

我們仔細的看下這部分程式碼:這個地方是通過設定proguard.config屬性來指定混淆程式碼的配置檔案為本機SDK目錄下面的proguard-android.txt檔案,以及制定混淆的個性化配置檔案為當前工程(eclipse下)根目錄下面的proguard-project.txt檔案 ,而後面這個檔案,恰是我們剛才看到的原本在根目錄下自動生成的另外一個檔案!

其實開啟了這個地方,我們就已經可以混淆程式碼了,不過這裡要注意:不能試圖通過執行eclipse中的Run as 和 Debug as 選單來生成混淆程式碼,必須通過如下圖所示的方法將apk匯出才行,當然你可以選擇“簽名”或者“不簽名”:


這樣一步操作後,算是程式碼混淆完成了。那麼怎麼才能檢驗我們真的混淆了程式碼了呢?首先,我們能夠看到在工程的根目錄新生產了一個資料夾proguard,裡面有四個檔案,其內容如下:

  • dump.txt : 描述了apk中所有類 檔案中內部的結構體。( Describes the internal structure of all the class files in the .apk file )
  • mapping.txt : 列出了原始的類、方法和名稱與混淆程式碼見得對映。( Lists the mapping between the original and obfuscated class, method, and field names. )
  • seeds.txt : 列出了沒有混淆的類和方法。( Lists the classes and members that are not obfuscated )
  • usage.txt : 列出congapk中刪除的程式碼。( Lists the code that was stripped from the .apk )

同時使用反編譯軟體對新生成的apk反編譯後會發現,裡面的類名、方法和變數等,都變成了簡單的a、b、c、d等毫無含義的字母,這樣就達到了混淆的目的:



但在實際使用過程中,我們會發現當前apk中的有些方法和類,是要供外部使用的,而此時混淆了名稱,外部呼叫就會報錯了,那麼怎麼解決這個問題?此時就要用到我們剛才提到的混淆的個性化配置檔案proguard-project.txt,在其中去配置不需要混淆的類、方法和變數等。關於混淆檔案的具體配置方法,請看下面的最後一個標題會有詳述。

Android原始碼環境中ProGuard使用


在Google釋出的android原始碼中,面對那麼多程式碼和檔案目錄,此時該如何混淆程式碼與配置混淆檔案呢?

android中預設是將程式碼混淆ProGuard關閉的,在alps/build/core/proguard.flags中有下面一句,意指將預設不混淆,不需要程式碼刪除,我們將這一句註釋起來,就起到程式碼混淆編譯的作用。

 Don't obfuscate. We only need dead code striping.
-dontobfuscate
可以說,這句是android工程中程式碼混淆的總開關,然而,註釋了上面的程式碼後,整個工程就已經是程式碼混淆了嗎?不是的,這裡還要關注一個檔案alps/build/core/package.mk,在這個檔案中有這麼一段:

ifndef LOCAL_PROGUARD_ENABLED
ifneq ($(filter user userdebug, $(TARGET_BUILD_VARIANT)),)
    # turn on Proguard by default for user & userdebug build
    #LOCAL_PROGUARD_ENABLED :=full
endif
endif

切記:當我們需要對整個工程進行程式碼混淆的時候,就把此處的 #LOCAL_PROGUARD_ENABLED :=full註釋去掉,成為有效的巨集即可。如果不想對整個工程程式碼混淆,而只是相對某個模組混淆的話,就先不要動這裡的程式碼。

接著建議將真個工程new一遍,之後就可以針對具體的apk檔案進行混淆檔案的設定和定製了。下面以alps/packages/apps/Music為例說說該如何對特定模組做到混淆程式碼:

在Music目錄下,我們看到一個平時不太關注,但今天一定很在意的檔名:proguard.flags ,對了,這個檔案就是Music的混淆配置檔案,可以開啟看看(有些地方沒有這個檔案,使用者可以自己手動新建一下,最好名稱也叫proguard.flags,android下預設都是這個名字)。當然,設定了配置檔案還是不夠的,還需要在同目錄的Android.mk中如下設定如下兩句:

LOCAL_PROGUARD_ENABLED  := full
LOCAL_PROGUARD_FLAG_FILES := proguard.flags

只有這樣才能讓混淆程式碼有效,並且將混淆配置檔案關聯起來。(有些模組沒有這兩句句,就自己手動加上)

反之,如果使用者已經在alps/build/core/package.mk開啟了全工程混淆編譯的控制點後,又在針對某個模組時不想混淆編譯怎麼辦?這就簡單了,將模組下的Android.mk中設定為**LOCAL_PROGUARD_ENABLED := disabled**即可。

這樣,我們通過mm編譯後的程式碼生成的apk,或者new真個工程後生成的燒機程式碼,都是已經新增相應配置的混淆程式碼了。

反編譯後,除過proguard.flags中定製的不需要混淆的程式碼外,其他都是被混淆了,如圖所示是android中Music模組的混淆後反編譯結果:


混淆檔案的配置


在實際使用過程中,我們會發現當前apk中的有些方法和類,是要供外部使用的,而此時混淆了名稱,外部呼叫就會報錯了,那麼怎麼解決這個問題?此時就需要我們配置混淆的個性化檔案proguard-project.txt(eclipse環境中)或者proguard.flags(android原始碼環境),在其中去配置不需要混淆的類、方法和變數等。關於混淆檔案的具體配置方法,大家可以去搜尋下,我這裡提供一段網上有人共享的配置程式碼,這個配置程式碼保留了工程檔案中的Activity、Application、Service、BroadcastReceiver、ContentProvider、BackupAgentHelper、Preference和ILicensingService的子類,並保留了所有的Native變數名及類名,所有類中部分以設定了固定引數格式的建構函式,列舉等等,用來防止外部呼叫出錯,大家可以借鑑下,以後來配置自己的檔案:


-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

-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

 -keepclasseswithmembernames class * {
     native <methods>;
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

-keepclassmembers class * extends android.app.Activity {
    public void *(android.view.View);
}

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

-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}


相關文章