天天看點

Android Proguard 詳解

關注微信号:javalearns   随時随地學Java

或掃一掃

Android Proguard 詳解

随時随地學Java

簡介

Java代碼是非常容易反編譯的。為了很好的保護Java源代碼,我們往往會對編譯好的class檔案進行混淆處理。

ProGuard是一個混淆代碼的開源項目。它的主要作用就是混淆,當然它還能對位元組碼進行縮減體積、優化等,但那些對于我們來說都算是次要的功能。官網網址是:

http://proguard.sourceforge.net/。

詳解

1、原理

Java 是一種跨平台的、解釋型語言,Java 源代碼編譯成中間”位元組碼”存儲于 class 檔案中。由于跨平台的需要,Java 位元組碼中包括了很多源代碼資訊,如變量名、方法名,并且通過這些名稱來通路變量和方法,這些符号帶有許多語義資訊,很容易被反編譯成 Java 源代碼。為了防止這種現象,我們可以使用 Java 混淆器對 Java 位元組碼進行混淆。

混淆就是對釋出出去的程式進行重新組織和處理,使得處理後的代碼與處理前代碼完成相同的功能,而混淆後的代碼很難被反編譯,即使反編譯成功也很難得出程式的真正語義。被混淆過的程式代碼,仍然遵照原來的檔案格式和指令集,執行結果也與混淆前一樣,隻是混淆器将代碼中的所有變量、函數、類的名稱變為簡短的英文字母代号,在缺乏相應的函數名和程式注釋的況下,即使被反編譯,也将難以閱讀。同時混淆是不可逆的,在混淆的過程中一些不影響正常運作的資訊将永久丢失,這些資訊的丢失使程式變得更加難以了解。

混淆器的作用不僅僅是保護代碼,它也有精簡編譯後程式大小的作用。由于以上介紹的縮短變量和函數名以及丢失部分資訊的原因, 編譯後 jar 檔案體積大約能減少25% ,這對目前費用較貴的無線網絡傳輸是有一定意義的。

2、文法
-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;                              # 保持自己定義的類不被混淆      
3、檔案

在release模式下打包apk時會自動運作ProGuard,這裡的release模式指的是通過ant release指令或eclipse project->android tools->export signed(unsigned)

application package生成apk。

在debug模式下為了更快調試并不會調用proguard。

如果是ant指令打包apk,proguard資訊檔案會儲存于<project_root>/bin/proguard檔案夾内;

如果用eclipse export指令打包,會在<project_root>/proguard檔案夾内。其中包含以下檔案:

mapping.txt

表示混淆前後代碼的對照表,這個檔案非常重要。如果你的代碼混淆後會産生bug的話,log提示中是混淆後的代碼,希望定位到源代碼的話就可以根據mapping.txt反推。

每次釋出都要保留它友善該版本出現問題時調出日志進行排查,它可以根據版本号或是釋出時間命名來儲存或是放進代碼版本控制中。

dump.txt

描述apk内所有class檔案的内部結構。

seeds.txt

列出了沒有被混淆的類和成員。

usage.txt

列出了源代碼中被删除在apk中不存在的代碼。

4、不能混淆的代碼

顧名思義,不能混淆代碼,否則會出錯。

1、放射的地方

2、系統接口

3、Jni接口

4、

android.app.backup.BackupAgentHelper

android.preference.Preference

com.android.vending.licensing.ILicensingService

……

5、bug(常見錯誤)

1、Proguard returned with error code 1. See console

1、更新proguard版本

2、android-support-v4 不進行混淆

3、添加缺少相應的庫

2、使用gson包解析資料時,出現missing type parameter異常

1、在 proguard.cfg中添加

-dontobfuscate
-dontoptimize      

2、在 proguard.cfg中添加

# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { *; }      

3、類型轉換錯誤

-keepattributes Signature

4、空指針異常

混淆過濾掉相關類與方法

5、安卓代碼混淆與反射沖突,地圖無法顯示等問題解決及反編譯方法,安卓反編譯

此前的代碼混淆,因為并沒有用到反射,是以正常的代碼混淆方式一遍就能通過,而此項目中某些類利用到了反射機制(本人的這個項目中有即時通訊功能,是以有表情類資源,是以需要通過反射由檔案名找到表情資源id),當由檔案名去尋找資源id時就報空指針異常了,期初我并不知道什麼原因,通過反編譯已經混淆的apk,一步一步尋找到出錯的地方,才恍然大悟,正是反射那一步出現了問題:Field field = R.drawable.class.getDeclaredField(name);走到這一步就挂了,程式直接崩潰。

解決辦法:

1.在proguard.cfg檔案中,将反射用到的類中的變量不被混淆:

如:-keep public class com.byl.bean.Expressions { *; },表示Expressions 這個類及類中的所有變量及方法不被混淆,注意要寫全路徑;

2.過濾泛型:-keepattributes Signature

3.最重要的一點:保持R檔案不被混淆,否則,你的反射是擷取不到資源id的:-keep class **.R$* {*;}

補充一下:上個問題解決後,接下來又遇到了一個問題就是混淆後,地圖無法正常使用了,部落客使用的是百度地圖,在proguard.cfg也已經寫明了:

-keep class com.baidu.**   {*;}

-keep class vi.com.**   {*;}

6、android.provider.Settings$Global

# Project target.

target=android-19

7、java.lang.reflect.UndeclaredThrowableException

-keep interface com.dev.impl.**

8、記憶體溢出和無法寫入堆棧

javaOptions in proguard := Seq(…)

or

javaOptions in (Proguard, proguard) := Seq(…)

9、Error: Unable to access jarfile ..\lib\proguard.jar

路徑問題

10、java.lang.NoSuchMethodError

沒有相關方法,方法被混淆了,混淆過濾掉相關方法便可。

11、專業網站bug解決方法

http://proguard.sourceforge.net/index.html#manual/troubleshooting.html

總結

有了混淆技術,代碼再也不用擔心被偷了…

.................... 【.........閱讀全文】

Java免費學習   Java自學網 http://www.javalearns.com

關注微信号:javalearns   随時随地學Java

或掃一掃

Android Proguard 詳解

随時随地學Java

繼續閱讀