Android代碼混淆
1 代碼混淆的作用 Java 是一種跨平台的、解釋型語言,Java 原始碼編譯成 class 檔案中。由於跨平台的需要,Java 位元組碼中包括了很多原始碼資訊,如變數名、方法名,並且通過這些名稱來訪問變數和方法,這些符號帶有許多語義資訊,很容易被反編譯成 Java 原始碼。為了防止這種現象,我們可以使用 Java 混淆器對 Java 位元組碼進行混淆。 混淆就是對發布出去的程式進行
重新組織和處理,使得處理後的代碼與處理前代碼
完成相同的功能,而混淆後的代碼很難被
反編譯,即使反編譯成功也很難得出程式的真正語義。被混淆過的程式碼,仍然遵照原來的檔案格式和指令集,執行結果也與混淆前一樣,只是混淆器將代碼中的所有變數、函數、類的名稱變為簡短的英文字母代號,在缺乏相應的函數名和程式注釋的況下,即使被反編譯,也將難以閱讀。同時混淆是無法復原的,在混淆的過程中一些不影響正常啟動並執行資訊將永久丟失,這些資訊的丟失使程式變得更加難以理解。 混淆器的作用不僅僅是保護代碼,它也有
精簡編譯後程式大小的作用。由於以上介紹的縮短變數和函數名以及丟失部分資訊的原因, 編譯後 jar 檔案體積大約能減少25% ,這對當前費用較貴的無線網路傳輸是有一定意義的。 同樣,在android開發過程中,代碼的安全也很重要,從2.3開始,我們在建立android工程時會自動建立兩個檔案proguard-project.txt和project.properties檔案,其中預設情況下並沒有開啟代碼混淆,如果需要開啟,將project.properties檔案中的#proguard.config=${sdk.dir}/tools/ proguard/ proguard-android.txt:proguard-project.txt這一行中的#去掉即可,然後編寫混淆檔案proguard-project.txt.
2 混淆文法說明
-optimizationpasses 5 # 指定代碼的壓縮層級-dontusemixedcaseclassnames # 是否使用大小寫混合-dontskipnonpubliclibraryclasses # 是否混淆第三方jar-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 # 保持哪些類不被混淆-keep public class com.android.vending.licensing.ILicensingService # 保持哪些類不被混淆-keepclasseswithmembernames class * { # 保持 native 方法不被混淆 native ;}-keepclasseswithmembers class * { # 保持自訂控制項類不被混淆 public (android.content.Context, android.util.AttributeSet);}-keepclasseswithmembers class * { public (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 混淆執行個體 介紹幾個例子:
# 指定代碼的壓縮層級-optimizationpasses 5 -dontusemixedcaseclassnames # 是否混淆第三方jar -dontskipnonpubliclibraryclasses -dontpreverify -keepattributes SourceFile,LineNumberTable -verbose -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* -libraryjars libs/httpmime-4.1.3.jar-libraryjars libs/libammsdk.jar-libraryjars libs/fastjson-1.1.34.android.jar-libraryjars libs/commons-lang.jar-libraryjars libs/weibosdkcore.jar# webview + js# keep 使用 webview 的類-keepclassmembers class com.goldnet.mobile.activity.InfoDetailActivity { public *;}# keep 使用 webview 的類的所有的內部類-keepclassmembers class com.goldnet.mobile.activity.InfoDetailActivity$*{ *;}# 保持哪些類不被混淆-keep class android.** {*; }-keep public class * extends android.view -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.pm -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 #ACRA specifics# we need line numbers in our stack traces otherwise they are pretty useless-renamesourcefileattribute SourceFile-keepattributes SourceFile,LineNumberTable# ACRA needs annotations so add this...-keepattributes *Annotation*# 保持 native 方法不被混淆-keepclasseswithmembernames class * { native ;}# 保持自訂控制項類不被混淆-keepclasseswithmembers class * { public (android.content.Context, android.util.AttributeSet);}-keepclasseswithmembers class * { public (android.content.Context, android.util.AttributeSet, int); }-keepclasseswithmembers class * { void onClick*(...);}-keepclasseswithmembers class * { *** *Callback(...);}# keep setters in Views so that animations can still work.# see http://proguard.sourceforge.net/manual/examples.html#beans-keepclassmembers public class * extends android.view.View { void set*(***); *** get*();}# 保持自訂控制項類不被混淆-keepclassmembers class * extends android.app.Activity { public void *(android.view.View);}# 保持枚舉 enum 類不被混淆-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String);}# 保持 Parcelable 不被混淆-keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *;}-keepclassmembers class **.R$* { public static ;}# http client-keep class org.apache.http.** {*; }# -keep class com.tencent.mm.sdk.openapi.WXMediaMessage {*;}-keep class com.tencent.mm.sdk.openapi.** implements com.tencent.mm.sdk.openapi.WXMediaMessage$IMediaObject {*;}# fastjson-keep class com.alibaba.fastjson.**{*;}# keep 所有的 javabean-keep class com.goldnet.mobile.entity.**{*;} # keep 泛型-keepattributes Signature #-keep public class * implements java.io.Serializable { # public *; #} #-keepclassmembers class * implements java.io.Serializable { # static final long serialVersionUID; # private static final java.io.ObjectStreamField[] serialPersistentFields; # private void writeObject(java.io.ObjectOutputStream); # private void readObject(java.io.ObjectInputStream); # java.lang.Object writeReplace(); # java.lang.Object readResolve(); #}-keep class org.apache.commons.lang.**{*;}# 新浪微博-keep class com.sina.**{*;}######## 其它第三方庫#######-dontwarn org.bouncycastle.**-keep class org.bouncycastle.**{*;}-dontwarn android-async-http-1.4.4.jar.**-keep class android-async-http-1.4.4.jar.**{*;}-dontwarn Decoder.**-keep class Decoder.**{*;}# volley-dontwarn com.android.volley.jar.**-keep class com.android.volley.**{*;}# actionbarsherlock-dontwarn com.actionbarsherlock.**-keep class com.actionbarsherlock.**{*;}# slidingmenu-dontwarn com.jeremyfeinstein.slidingmenu.lib.**-keep class com.jeremyfeinstein.slidingmenu.lib.**{*;}-dontwarn com.cairh.app.sjkh.**-keep class com.cairh.app.sjkh.**{*;}
再看一個執行個體
# To enable ProGuard in your project, edit project.properties# to define the proguard.config property as described in that file.## Add project specific ProGuard rules here.# By default, the flags in this file are appended to flags specified# in ${sdk.dir}/tools/proguard/proguard-android.txt# You can edit the include path and order by changing the ProGuard# include property in project.properties.## For more details, see# http://developer.android.com/guide/developing/tools/proguard.html# Add any project specific keep options here:# If your project uses WebView with JS, uncomment the following# and specify the fully qualified class name to the JavaScript interface# class:#-keepclassmembers class fqcn.of.javascript.interface.for.webview {# public *;#}-optimizationpasses 5-dontusemixedcaseclassnames-dontskipnonpubliclibraryclasses-dontpreverify-verbose-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*-keepattributes *Annotation*-keepattributes Signature-libraryjars libs/apns_1.0.6.jar-libraryjars libs/armeabi/libBaiduMapSDK_v2_3_1.so-libraryjars libs/armeabi/liblocSDK4.so-libraryjars libs/baidumapapi_v2_3_1.jar-libraryjars libs/core.jar-libraryjars libs/gesture-imageview.jar-libraryjars libs/gson-2.0.jar-libraryjars libs/infogracesound.jar-libraryjars libs/locSDK_4.0.jar-libraryjars libs/ormlite-android-4.48.jar-libraryjars libs/ormlite-core-4.48.jar-libraryjars libs/universal-image-loader-1.9.0.jar-keep class com.baidu.** { *; }-keep class vi.com.gdi.bgl.android.**{*;}-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.app.backup.BackupAgentHelper-keep public class * extends android.preference.Preference-keep public class * extends android.support.v4.**-keep public class com.android.vending.licensing.ILicensingService-keep class com.google.gson.stream.** { *; }-keep class com.google.gson.examples.android.model.** { *; }-keep class com.uuhelper.Application.** { *; }-keep class net.sourceforge.zbar.** { *; }-keep class com.google.android.gms.** { *; }-keep class com.bank.pingan.model.** { *; }-keep public class * extends com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper-keep public class * extends com.j256.ormlite.android.apptools.OpenHelperManager-keep class com.android.vending.licensing.ILicensingService-keep class android.support.v4.** { *; } -keep class org.apache.commons.net.** { *; } -keep class com.tencent.** { *; } -keep class com.umeng.** { *; } -keep class com.umeng.analytics.** { *; } -keep class com.umeng.common.** { *; } -keep class com.umeng.newxp.** { *; } -keep class com.j256.ormlite.** { *; } -keep class com.j256.ormlite.android.** { *; } -keep class com.j256.ormlite.field.** { *; } -keep class com.j256.ormlite.stmt.** { *; }-dontwarn android.support.v4.** -dontwarn org.apache.commons.net.**-dontwarn com.tencent.** -keepclasseswithmembernames class * { native ;}-keepclasseswithmembernames class * { public (android.content.Context, android.util.AttributeSet);}-keepclasseswithmembernames class * { public (android.content.Context, android.util.AttributeSet, int);}-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String);}-keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *;}-keepclasseswithmembers class * { public (android.content.Context);}-dontshrink-dontoptimize-dontwarn com.google.android.maps.**-dontwarn android.webkit.WebView-dontwarn com.umeng.**-dontwarn com.tencent.weibo.sdk.**-dontwarn com.facebook.**-keep enum com.facebook.**-keepattributes Exceptions,InnerClasses,Signature-keepattributes *Annotation*-keepattributes SourceFile,LineNumberTable-keep public interface com.facebook.**-keep public interface com.tencent.**-keep public interface com.umeng.socialize.**-keep public interface com.umeng.socialize.sensor.**-keep public interface com.umeng.scrshot.**-keep public class com.umeng.socialize.* {*;}-keep public class javax.**-keep public class android.webkit.**-keep class com.facebook.**-keep class com.umeng.scrshot.**-keep public class com.tencent.** {*;}-keep class com.umeng.socialize.sensor.**-keep class com.tencent.mm.sdk.openapi.WXMediaMessage {*;}-keep class com.tencent.mm.sdk.openapi.** implements com.tencent.mm.sdk.openapi.WXMediaMessage$IMediaObject {*;}-keep class im.yixin.sdk.api.YXMessage {*;}-keep class im.yixin.sdk.api.** implements im.yixin.sdk.api.YXMessage$YXMessageData{*;}-keep public class [your_pkg].R$*{ public static final int *;}
編寫完混淆檔案之後,接下來就是產生Apk了。這裡需要注意的是,如果你用Eclipse裡的Run或Build Project/Build All來產生Apk,是不會混淆代碼的!在Eclipse的Package Explorer裡選中工程的根結點,在右鍵菜單裡找到Android Tools,如所示,其下有兩個子功能表項:“Export Signed Application Package...”和“Export Unsigned Application Package...”,一個是帶RSA數位簽章,一個是不帶數位簽章,根據需要選一個,然後按照嚮導操作即可,最終產生的Apk就是混淆過代碼的了!查看效果可以用7z等壓縮程式(Ubuntu中直接用歸檔管理器)開啟,將其中的classes.dex解壓,用dex2jar轉成jar格式後,再用Java反編碼工具開啟,就會看到代碼的混淆效果。