簡介
?
Java代碼是非常容易反編譯的。為了很好的保護Java源代碼,我們往往會對編譯好的class文件進行混淆處理。
?
ProGuard是一個混淆代碼的開源項目。它的主要作用就是混淆,當然它還能對字節碼進行縮減體積、優化等,但那些對于我們來說都算是次要的功能。
官網地址: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} -dontwarn 如果有警告也不終止 -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} 設置源文件中給定的字符串常量
?
?
Android Eclipse開發環境與ProGuard
在Android 2.3以前,混淆Android代碼只能手動添加proguard來實現代碼混淆,非常不方便。而2.3以后,Google已經將這個工具加入到了SDK的工具集里。具體路徑:SDK\tools\proguard。當創建一個新的Android工程時,在工程目錄的根路徑下,會出現一個proguard的配置文件proguard.cfg。也就是說,我們可以通過簡單的配置,在我們的elipse工程中直接使用ProGuard混淆Android工程。
?
具體混淆的步驟非常簡單。首先,我們需要在工程描述文件project.properties中,添加一句話,啟用ProGuard。如下所示:
?
# 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-19
這樣,Proguard就可以使用了。當我們正常通過Android Tools導出Application Package時(或者使用ant執行release打包),Proguard就會自動啟用,優化混淆你的代碼。
??
導出成功后,你可以反編譯看看混淆的效果。一些類名、方法名和變量名等,都變成了一些無意義的字母或者數字。證明混淆成功!
?
實例(proguard 文件代碼解讀)
-optimizationpasses 7 #指定代碼的壓縮級別 0 - 7 -dontusemixedcaseclassnames #是否使用大小寫混合 -dontskipnonpubliclibraryclasses #如果應用程序引入的有jar包,并且想混淆jar包里面的class -dontpreverify #混淆時是否做預校驗(可去掉加快混淆速度) -verbose #混淆時是否記錄日志(混淆后生產映射文件?map?類名 -> 轉化后類名的映射 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* #淆采用的算法 -keep public class * extends android.app.Activity #所有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 com.android.vending.licensing.ILicensingService #指定具體類不要去混淆 -keepclasseswithmembernames class * {native <methods>; #保持 native 的方法不去混淆 }-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); #保持指定規則的方法不被混淆(Android layout 布局文件中為控件配置的onClick方法不能混淆) }-keep public class * extends android.view.View { #保持自定義控件指定規則的方法不被混淆public <init>(android.content.Context);public <init>(android.content.Context, android.util.AttributeSet);public <init>(android.content.Context, android.util.AttributeSet, int);public void set*(...); }-keepclassmembers enum * { #保持枚舉 enum 不被混淆public static **[] values();public static ** valueOf(java.lang.String); }-keep class * implements android.os.Parcelable { #保持 Parcelable 不被混淆(aidl文件不能去混淆)public static final android.os.Parcelable$Creator *; }-keepnames class * implements java.io.Serializable #需要序列化和反序列化的類不能被混淆(注:Java反射用到的類也不能被混淆) -keepclassmembers class * implements java.io.Serializable { #保護實現接口Serializable的類中,指定規則的類成員不被混淆static final long serialVersionUID;private static final java.io.ObjectStreamField[] serialPersistentFields;!static !transient <fields>;private void writeObject(java.io.ObjectOutputStream);private void readObject(java.io.ObjectInputStream);java.lang.Object writeReplace();java.lang.Object readResolve(); }-keepattributes Signature #過濾泛型(不寫可能會出現類型轉換錯誤,一般情況把這個加上就是了)-keepattributes *Annotation* #假如項目中有用到注解,應加入這行配置-keep class **.R$* { *; } #保持R文件不被混淆,否則,你的反射是獲取不到資源id的-keep class **.Webview2JsInterface { *; } #保護WebView對HTML頁面的API不被混淆 -keepclassmembers class * extends android.webkit.WebViewClient { #如果你的項目中用到了webview的復雜操作 ,最好加入public void *(android.webkit.WebView,java.lang.String,android.graphics.Bitmap);public boolean *(android.webkit.WebView,java.lang.String); } -keepclassmembers class * extends android.webkit.WebChromeClient { #如果你的項目中用到了webview的復雜操作 ,最好加入public void *(android.webkit.WebView,java.lang.String); } #對WebView的簡單說明下:經過實戰檢驗,做騰訊QQ登錄,如果引用他們提供的jar,若不加防止WebChromeClient混淆的代碼,oauth認證無法回調,反編譯基代碼后可看到他們有用到WebChromeClient,加入此代碼即可。-keepclassmembernames class com.cgv.cn.movie.common.bean.** { *; } #轉換JSON的JavaBean,類成員名稱保護,使其不被混淆################################################################## # 下面都是項目中引入的第三方 jar 包。第三方 jar 包中的代碼不是我們的目標和關心的對象,故而對此我們全部忽略不進行混淆。 ################################################################## -libraryjars libs/android-support-v4.jar -dontwarn android.support.v4.** -keep class android.support.v4.** { *; } -keep interface android.support.v4.** { *; } -keep public class * extends android.support.v4.** -keep public class * extends android.app.Fragment -libraryjars libs/gson-2.3.1-sources.jar -libraryjars libs/gson-2.3.1.jar -dontwarn com.google.gson.** -keep class sun.misc.Unsafe { *; } -keep class com.google.gson.** { *; }-libraryjars libs/alipaySDK-20150602.jar -dontwarn com.alipay.** -dontwarn com.ta.utdid2.** -dontwarn com.ut.device.** -keep class com.alipay.** { *; } -keep class com.ta.utdid2.** { *; } -keep class com.ut.device.** { *; }-libraryjars libs/android-async-http-1.4.6.jar -dontwarn com.loopj.android.http.** -keep class com.loopj.android.http.** { *; }-libraryjars libs/baidumapapi_v2_4_1.jar -dontwarn com.baidu.** -keep class com.baidu.** {*; } -keep class assets.** {*; } -keep class vi.com.gdi.bgl.** {*; }-libraryjars libs/libammsdk.jar -dontwarn com.tencent.** -keep class com.tencent.** { *; }-libraryjars libs/locSDK_4.1.jar -dontwarn com.baidu.location.** -keep class com.baidu.location.** { *; }-libraryjars libs/mframework.jar -dontwarn m.framework.** -keep class m.framework.** { *; }-libraryjars libs/mta-sdk-1.6.2.jar -dontwarn com.tencent.stat.** -keep class com.tencent.stat.** { *; }-libraryjars libs/nineoldandroids-library-2.4.0.jar -dontwarn com.nineoldandroids.** -keep class com.nineoldandroids.** { *; }-libraryjars libs/open_sdk_r4889.jar -dontwarn com.tencent.** -keep class com.tencent.** { *; } -libraryjars libs/ShareSDK-Core-2.5.9.jar -dontwarn cn.sharesdk.framework.** -keep class cn.sharesdk.framework.** { *; } -libraryjars libs/ShareSDK-ShortMessage-2.5.9.jar -dontwarn cn.sharesdk.system.text.** -keep class cn.sharesdk.system.text.** { *; } -libraryjars libs/ShareSDK-SinaWeibo-2.5.9.jar -dontwarn cn.sharesdk.sina.weibo.** -keep class cn.sharesdk.sina.weibo.** { *; } -libraryjars libs/ShareSDK-Wechat-2.5.9.jar -dontwarn cn.sharesdk.wechat.friends.** -keep class cn.sharesdk.wechat.friends.** { *; } -libraryjars libs/ShareSDK-Wechat-Core-2.5.9.jar -dontwarn cn.sharesdk.wechat.utils.** -keep class cn.sharesdk.wechat.utils.** { *; } -libraryjars libs/ShareSDK-Wechat-Favorite-2.5.9.jar -dontwarn cn.sharesdk.wechat.favorite.** -keep class cn.sharesdk.wechat.favorite.** { *; } -libraryjars libs/ShareSDK-Wechat-Moments-2.5.9.jar -dontwarn cn.sharesdk.wechat.moments.** -keep class cn.sharesdk.wechat.moments.** { *; } -libraryjars libs/universal-image-loader-1.9.2-SNAPSHOT-with-sources.jar -dontwarn com.nostra13.universalimageloader.** -keep class com.nostra13.universalimageloader.** { *; } -libraryjars libs/weibosdkcore.jar -dontwarn com.sina.weibo.sdk.** -keep class com.sina.weibo.sdk.** { *; }
?
關于如何配置忽略第三方jar,附上一個圖進行說明。?
?
說明一下,第三方jar包中如果有.so文件,不用去理會,引入的第三方jar文件不要混淆,否則可能會報異常。
?
文件
在release模式下打包apk時會自動運行ProGuard,這里的release模式指的是通過ant release命令或eclipse project->android tools->export signed(unsigned)?application package生成apk。
在debug模式下為了更快調試并不會調用proguard。?
如果是ant命令打包apk,proguard信息文件會保存于工程代碼下的/bin/proguard文件夾內;
如果用eclipse export命令打包,會在/proguard文件夾內。其中包含以下文件:
?
mapping.txt
表示混淆前后代碼的對照表,這個文件非常重要。如果你的代碼混淆后會產生bug的話,log提示中是混淆后的代碼,希望定位到源代碼的話就可以根據mapping.txt反推。
每次發布都要保留它方便該版本出現問題時調出日志進行排查,它可以根據版本號或是發布時間命名來保存或是放進代碼版本控制中。
dump.txt
描述apk內所有class文件的內部結構。
seeds.txt
列出了沒有被混淆的類和成員。
usage.txt
列出了源代碼中被刪除在apk中不存在的代碼。
不能混淆的代碼
?
顧名思義,不能混淆代碼如果被混淆了,就會出現錯誤。
1、需要反射的代碼
2、系統接口
3、Jni接口
4、需要序列號和反序列化的代碼(即實現Serializable接口的JavaBean)
5、與服務端進行元數據交互的JavaBean(JSON、XML中對應的類)
……
常見錯誤
?
1) Proguard returned with error code 1. See console
? > 更新proguard版本
? > android-support-v4 不進行混淆
? > 添加缺少相應的庫
2) 使用gson包解析數據時,出現 missing type parameter 異常
? > 在 proguard-project.txt 中添加
? ? -dontobfuscate
? ? -dontoptimize
? > 在 proguard-project.txt 中添加
? ? # 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) 類型轉換錯誤
? > 在 proguard-project.txt 中添加
? ? -keepattributes Signature
4) 空指針異常
? > 混淆過濾掉相關類與方法
5) java.lang.reflect.UndeclaredThrowableException
? > -keep interface com.dev.impl.**
6) Error: Unable to access jarfile ..libproguard.jar
? > 路徑問題
7) java.lang.NoSuchMethodError
? > 這也是最常見的問題,因為找不到相關方法,方法被混淆了,混淆過濾掉相關方法便可。
?
?
----------------------
(完)
?
?
---------------------
原文:https://yq.aliyun.com/articles/1328
版權聲明:本文為作者原創文章,轉載請附上博文鏈接!
?