Android混淆(Proguard)詳解

LockJo發表於2018-05-01

目錄:

1、混淆的作用及好處
2、混淆的原理
3、混淆的具體使用
--- 3.1、混淆的基本語法
--- 3.2、去除日誌資訊
4、更詳細的語法及demo

1、混淆的作用及好處

混淆屬於整個應用程式開發生命週期偏後期階段的技術了,所以要考慮應用的安全性及效能的問題,混淆就是為了這種需求產生的一種技術,簡單說,混淆就是將關鍵字和關鍵類名,修改為無意義的字元以起到迷惑試圖反編譯去檢視原始碼的人。在一定程度上能過濾掉起碼95%以上的反編譯者,混淆是保障Android程式原始碼安全的第一道門檻,這是我的個人理解。
以上談了下混淆的作用,而混淆的好處除了能保證原始碼安全性之外就大概是通過修改關鍵字為無意義字串,或者剔除某些輔助類,比如Log,從而減少檔案大小。

2、混淆的原理

以上混淆均指的是Proguard,Proguard是混淆程式碼的一個開源專案(proguard官網)

Java 是一種跨平臺的、解釋型語言,Java 原始碼編譯成中間”位元組碼”儲存於 class 檔案中。由於跨平臺的需要,Java 位元組碼中包括了很多原始碼資訊,如變數名、方法名,並且通過這些名稱來訪問變數和方法,這些符號帶有許多語義資訊,很容易被反編譯成 Java 原始碼。為了防止這種現象,我們可以使用 Java 混淆器對 Java 位元組碼進行混淆。
混淆就是對釋出出去的程式進行重新組織和處理,使得處理後的程式碼與處理前程式碼完成相同的功能,而混淆後的程式碼很難被反編譯,即使反編譯成功也很難得出程式的真正語義。被混淆過的程式程式碼,仍然遵照原來的檔案格式和指令集,執行結果也與混淆前一樣,只是混淆器將程式碼中的所有變數、函式、類的名稱變為簡短的英文字母代號,在缺乏相應的函式名和程式註釋的況下,即使被反編譯,也將難以閱讀。同時混淆是不可逆的,在混淆的過程中一些不影響正常執行的資訊將永久丟失,這些資訊的丟失使程式變得更加難以理解。
混淆器的作用不僅僅是保護程式碼,它也有精簡編譯後程式大小的作用。由於以上介紹的縮短變數和函式名以及丟失部分資訊的原因, 編譯後 jar 檔案體積大約能減少25%,這對當前費用較貴的無線網路傳輸是有一定意義的。

反編譯之後得到的類名

3、混淆的具體使用

3.1、模組(Module)下的build.gradle的配置
android{
  buildTypes{
    release { 
       // 是否進行混淆  
       minifyEnabled false  
       // 混淆檔案的位置,其中'proguard-android.txt'為sdk預設的混淆配置,
       //'proguard-rules.pro' 是該模組下的混淆配置
       proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
    } 
  }
}
複製程式碼

以上是proguard在模組下build.gradle檔案中的配置資訊,其中proguard-android.txt為sdk預設的混淆配置,proguard-rules.pro是在預設配置的基礎上針對本模組做出的針對性混淆處理。

  • 注:proguard-android.txt的位置位於android-sdk/tools/proguard/proguard-android.txt
3.1、混淆的基本語法

這個語法的作用是定義出不需要混淆的原始碼,那麼編譯時會自動將未定義的部分全都混淆。至於為什麼要保留類名或方法名,主要有三個原因:

  • 讓C/C++程式可以通過jni使用對應的java方法。
  • 四大元件在AndroidManifest.xml裡面註冊了,所以要保留。
  • R檔案混淆會導致引用錯誤。

1、保留類名 2、保留方法名 3、保留類名和方法名

#保留位於View類中的get和set方法
-keepclassmembers public class * extends android.view.View{
    void set*(***);
    *** get*();
}
#保留在Activity中以View為引數的方法不變
-keepclassmembers class * extends android.app.Activity{
    public void *(android.view.View);
}
#保留實現了Parcelable的類名不變,
-keep class * implements android.os.Parcelable{ 
    public static final android.os.Parcelable$Creator *;
}
 #保留R$*類中靜態成員的變數名
-keepclassmembers class **.R$*{
    public static <fields>;
}
複製程式碼
3.2 去除日誌資訊

通過配置proguard,將android.util.Log置為無效程式碼,則可以去除apk中列印日誌的程式碼。 1、將build.gradle新增'proguard-android-optimize.txt'檔案,該檔案中預設開啟了優化開關。

android{
  buildTypes{
    release { 
       // 是否進行混淆  
       minifyEnabled false  
       // 混淆檔案的位置,其中'proguard-android-optimize.txt'為sdk預設的混淆配置,
       //'proguard-rules.pro' 是該模組下的混淆配置
       proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'  
    } 
  }
}
複製程式碼

2、配置語法

-assumenosideeffects class android.util.Log{
   public static boolean isLoggable(java.lang.String,int);
   public static int v(...);
   public static int i(...);
   public static int w(...);
   public static int d(...);
   public static int e(...);
}
-assumenosideeffects class com.example.Log.Logger{
   public static int v(...);
   public static int i(...);
   public static int w(...);
   public static int d(...);
   public static int e(...);
}
複製程式碼

4、更詳細的語法

-include {filename}    從給定的檔案中讀取配置引數   
-basedirectory {directoryname}    指定基礎目錄為以後相對的檔案名稱   
-injars {class_path}    指定要處理的應用程式jar,war,ear和目錄   
-outjars {class_path}    指定處理完後要輸出的jar,war,ear和目錄的名稱   
-libraryjars {classpath}    指定要處理的應用程式jar,war,ear和目錄所需要的程式庫檔案   
-dontskipnonpubliclibraryclasses    指定不去忽略非公共的庫類。   
-dontskipnonpubliclibraryclassmembers    指定不去忽略包可見的庫類的成員。  
  
保留選項   
-keep {Modifier} {class_specification}    保護指定的類檔案和類的成員   
-keepclassmembers {modifier} {class_specification}    保護指定類的成員,如果此類受到保護他們會保護的更好  
-keepclasseswithmembers {class_specification}    保護指定的類和類的成員,但條件是所有指定的類和類成員是要存在。   
-keepnames {class_specification}    保護指定的類和類的成員的名稱(如果他們不會壓縮步驟中刪除)   
-keepclassmembernames {class_specification}    保護指定的類的成員的名稱(如果他們不會壓縮步驟中刪除)   
-keepclasseswithmembernames {class_specification}    保護指定的類和類的成員的名稱,如果所有指定的類成員出席(在壓縮步驟之後)   
-printseeds {filename}    列出類和類的成員-keep選項的清單,標準輸出到給定的檔案   
  
壓縮   
-dontshrink    不壓縮輸入的類檔案   
-printusage {filename}   
-whyareyoukeeping {class_specification}       
  
優化   
-dontoptimize    不優化輸入的類檔案   
-assumenosideeffects {class_specification}    優化時假設指定的方法,沒有任何副作用   
-allowaccessmodification    優化時允許訪問並修改有修飾符的類和類的成員   
  
混淆   
-dontobfuscate    不混淆輸入的類檔案   
-printmapping {filename}   
-applymapping {filename}    重用對映增加混淆   
-obfuscationdictionary {filename}    使用給定檔案中的關鍵字作為要混淆方法的名稱   
-overloadaggressively    混淆時應用侵入式過載   
-useuniqueclassmembernames    確定統一的混淆類的成員名稱來增加混淆   
-flattenpackagehierarchy {package_name}    重新包裝所有重新命名的包並放在給定的單一包中   
-repackageclass {package_name}    重新包裝所有重新命名的類檔案中放在給定的單一包中   
-dontusemixedcaseclassnames    混淆時不會產生形形色色的類名   
-keepattributes {attribute_name,...}    保護給定的可選屬性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and   
  
InnerClasses.   
-renamesourcefileattribute {string}    設定原始檔中給定的字串常量  
複製程式碼

demo

-ignorewarnings                     # 忽略警告,避免打包時某些警告出現  
-optimizationpasses 5               # 指定程式碼的壓縮級別  
-dontusemixedcaseclassnames         # 是否使用大小寫混合  
-dontskipnonpubliclibraryclasses    # 是否混淆第三方jar  
-dontpreverify                      # 混淆時是否做預校驗  
-verbose                            # 混淆時是否記錄日誌  
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*        # 混淆時所採用的演算法  
  
-libraryjars   libs/treecore.jar  
  
-dontwarn android.support.v4.**     #預設proguard 會檢查每一個引用是否正確,但是第三方庫裡面往往有些不會用到的類,沒有正確引用。如果不配置的話,系統就會報錯。  
-dontwarn android.os.**  
-keep class android.support.v4.** { *; }        # 保持哪些類不被混淆  
-keep class com.baidu.** { *; }    
-keep class vi.com.gdi.bgl.android.**{*;}  
-keep class android.os.**{*;}  
  
-keep interface android.support.v4.app.** { *; }    
-keep public class * extends android.support.v4.**    
-keep public class * extends android.app.Fragment  
  
-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.support.v4.widget  
-keep public class * extends com.sqlcrypt.database  
-keep public class * extends com.sqlcrypt.database.sqlite  
-keep public class * extends com.treecore.**  
-keep public class * extends de.greenrobot.dao.**  
  
  
-keepclasseswithmembernames class * {       # 保持 native 方法不被混淆  
    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 * {                  # 保持列舉 enum 類不被混淆  
    public static **[] values();  
    public static ** valueOf(java.lang.String);  
}  
  
-keep class * implements android.os.Parcelable {    # 保持 Parcelable 不被混淆  
  public static final android.os.Parcelable$Creator *;  
}  
  
-keep class MyClass;                              # 保持自己定義的類不被混淆  
複製程式碼

參考

1、MOOC-Android程式碼混淆與加固技術
2、CSDN-Android proguard 詳解
3、簡書-ProGuard 最全混淆規則說明

相關文章