本文轉載於:http://blog.csdn.net/banketree/article/details/41928175html
簡介
Java代碼是很是容易反編譯的。爲了很好的保護Java源代碼,咱們每每會對編譯好的class文件進行混淆處理。
java
ProGuard是一個混淆代碼的開源項目。它的主要做用就是混淆,固然它還能對字節碼進行縮減體積、優化等,但那些對於咱們來講都算是次要的功能。官網網址是:android
http://proguard.sourceforge.net/。
詳解
一、原理
Java 是一種跨平臺的、解釋型語言,Java 源代碼編譯成中間」字節碼」存儲於 class 文件中。因爲跨平臺的須要,Java 字節碼中包括了不少源代碼信息,如變量名、方法名,而且經過這些名稱來訪問變量和方法,這些符號帶有許多語義信息,很容易被反編譯成 Java 源代碼。爲了防止這種現象,咱們可使用 Java 混淆器對 Java 字節碼進行混淆。
混淆就是對發佈出去的程序進行從新組織和處理,使得處理後的代碼與處理前代碼完成相同的功能,而混淆後的代碼很難被反編譯,即便反編譯成功也很可貴出程序的真正語義。被混淆過的程序代碼,仍然遵守原來的檔案格式和指令集,執行結果也與混淆前同樣,只是混淆器將代碼中的全部變量、函數、類的名稱變爲簡短的英文字母代號,在缺少相應的函數名和程序註釋的況下,即便被反編譯,也將難以閱讀。同時混淆是不可逆的,在混淆的過程當中一些不影響正常運行的信息將永久丟失,這些信息的丟失使程序變得更加難以理解。
混淆器的做用不只僅是保護代碼,它也有精簡編譯後程序大小的做用。因爲以上介紹的縮短變量和函數名以及丟失部分信息的緣由, 編譯後 jar 文件體積大約能減小25% ,這對當前費用較貴的無線網絡傳輸是有必定意義的。
二、語法
- -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; # 保持本身定義的類不被混淆
三、文件
在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反推。
每次發佈都要保留它方便該版本出現問題時調出日誌進行排查,它能夠根據版本號或是發佈時間命名來保存或是放進代碼版本控制中。sql
dump.txt
描述apk內全部class文件的內部結構。
seeds.txt
列出了沒有被混淆的類和成員。
usage.txt
列出了源代碼中被刪除在apk中不存在的代碼。網絡
四、不能混淆的代碼
顧名思義,不能混淆代碼,不然會出錯。app
一、放射的地方eclipse
二、系統接口ide
三、Jni接口函數
四、
android.app.backup.BackupAgentHelper
android.preference.Preference
com.android.vending.licensing.ILicensingService
……
五、bug(常見錯誤)
一、Proguard returned with error code 1. See console
一、更新proguard版本
二、android-support-v4 不進行混淆
三、添加缺乏相應的庫
二、使用gson包解析數據時,出現missing type parameter異常
一、在 proguard.cfg中添加
- -dontobfuscate
- -dontoptimize
二、在 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.** { *; }
三、類型轉換錯誤
-keepattributes Signature
四、空指針異常
混淆過濾掉相關類與方法
五、安卓代碼混淆與反射衝突,地圖沒法顯示等問題解決及反編譯方法,安卓反編譯
此前的代碼混淆,由於並無用到反射,因此常規的代碼混淆方式一遍就能經過,而此項目中某些類利用到了反射機制(本人的這個項目中有即時通信功能,因此有表情類資源,所以須要經過反射由文件名找到表情資源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.** {*;}
六、android.provider.Settings$Global
# Project target.
target=android-19
七、java.lang.reflect.UndeclaredThrowableException
-keep interface com.dev.impl.**
八、內存溢出和沒法寫入堆棧
javaOptions in proguard := Seq(...)
or
javaOptions in (Proguard, proguard) := Seq(...)
九、Error: Unable to access jarfile ..\lib\proguard.jar
路徑問題
十、java.lang.NoSuchMethodError
沒有相關方法,方法被混淆了,混淆過濾掉相關方法即可。
十一、專業網站bug解決方法
http://proguard.sourceforge.net/index.html#manual/troubleshooting.html
總結
有了混淆技術,代碼不再用擔憂被偷了…