Android混淆詳解

ProGuard簡介

在最新的Android Studio 2.2.2版本創建的Android工程中,module中的build.gradle有如下一段配置。這里的minifyEnabled即用來控制在編譯時是否需要啟用Proguard,將minifyEnabled修改為true,即表示啟用Proguard。’proguard-android.txt’是Android SDK中自帶的一個基本Progurad配置文件,默認是空白的,需要由開發者自行添加哪些需要混淆哪些不混淆,形如:

-ignorewarning                 # 是否忽略檢測,(是)
-optimizationpasses 5          # 指定代碼的壓縮級別
-dontusemixedcaseclassnames   # 是否使用大小寫混合
-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 # 保持哪些類不被混淆

-keepattributes Annotation #保持注解 -keep public class extends android.widget.BaseAdapter {;}

-keepclasseswithmembernames class * { # 保持 native 方法不被混淆 native <methods>; }</code></pre>

在Android中一提起ProGuard,我們就會認為他是用來混淆代碼的,殊不知ProGuard一共包括以下4步。

  • 壓縮(Shrink):偵測并移除代碼中無用的類、字段、方法、和特性(Attribute)。
  • 優化(OPtimize):對字節碼進行優化,移除無用指令。
  • 混淆(Obfuscate):使用a、b、c、d這樣簡短而無意義的名稱,對類、字段和方法進行重命名。
  • 預檢(Preveirfy): 在java平臺上對處理后的代碼進行預檢。
    說到這里我們需要對Android打包的原理有一個簡單的了解,首先來看一下在Proguard幫助文檔中給出了一個Proguard工作流程圖

Proguard按如下流程進行打包:

Input jars、Library jars-shrink->Shrunk code-optimize->Optim.code-obfuscate->Obfusc.code-preverify->Output >jars、Library jars

Proguard使用library jars來輔助對input jars類之間的依賴關系進行解析, library jars自身不會被處理,也不會被包含到output jars中。

這里我們引入Entry Point的概念。Entry Point是在ProGuard過程中不會被處理的類或方法。再壓縮的步驟中,ProGuard或從上述的EntryPoint開始遞歸遍歷,搜索那些類和類成員在使用。對于沒有被使用的類和類的成員,就會在壓縮階段丟棄。

接下來優化的步驟中,那些非EntryPoint的類、方法都會被設置為private、static或final,不使用的參數會被移除,此外,有些方法會被標記為內聯的。在混淆的步驟中,ProGuard會對非EntryPoint的類和方法進行重命名。

Proguard使用

Proguard工具目錄結構

lib目錄

lib目錄中包含了Proguard工具對應的jar文件,其中又包含三個文件:proguard.jar,proguardgui.jar和retrace.jar。

Proguard四項核心功能shrink,optimize,obfuscate和preverify的執行都是由proguard.jar來完成的,不過proguard.jar只能通過命令行方式來使用。

proguardgui.jar是Proguard提供的一個圖形界面工具,通過proguardgui.jar可以方便的查看和編輯Proguard配置,以及調用proguard.jar來執行一次優化過程。

retrace.jar主要在debug時使用。混淆之后的jar文件執行過程如果出現異常,生成的異常信息將很難被解讀,方法調用的堆棧都是一些混淆之后的名字,通過retrace.jar可以將異常的堆棧信息中的方法名還原成混淆前的名字,方便程序解決bug。

bin目錄

bin目錄中包含了幾個bat和shell腳本,通過這些腳本可以直接執行proguard.jar,proguardgui.jar和retrace.jar。如果將bin目錄添加到環境變量中,就可以直接在命令行中執行proguard,proguardgui和retrace命令了,避免每次都要輸入java -jar +

proguard.jar的使用

使用proguard.jar有幾種方式:

1,通過命令行執行”java -jar +

java -jar proguard.jar -injars myapp. jar -outjars myapp_out.jar -libraryjars 'D:\android-sdk\platforms\android-23\android.jar' // 只使用配置選項
java -jar proguard.jar @myconfig.pro                // 只使用配置文件
java -jar proguard.jar @myconfig.pro -verbose       // 混合使用配置文件和配置選項

proguardgui.jar的使用

使用proguardgui.jar有幾種方式:

1,通過命令行執行”java -jar +

java -jar proguardgui.jar                   // 不使用配置文件
java -jar proguardgui.jar @myconfig.pro     // 使用配置文件

retrace.jar的使用

使用retrace.jar有幾種方式:

1,通過命令行執行”java -jar +

java -jar retrace.jar mapping_file
java -jar retrace.jar mapping_file exception_statck_file.txt
java -jar retrace.jar -verbose mapping_file exception_statck_file.txt

如何寫一個ProGuard文件

如何寫一個ProGuard文件呢?主要有三步驟:

基本混淆

# 代碼混淆壓縮比,在0~7之間,默認為5,一般不下需要修改
-optimizationpasses 5

混淆時不使用大小寫混合,混淆后的類名為小寫

windows下的同學還是加入這個選項吧(windows大小寫不敏感)

-dontusemixedcaseclassnames

指定不去忽略非公共的庫的類

默認跳過,有些情況下編寫的代碼與類庫中的類在同一個包下,并且持有包中內容的引用,此時就需要加入此條聲明

-dontskipnonpubliclibraryclasses

指定不去忽略非公共的庫的類的成員

-dontskipnonpubliclibraryclassmembers

不做預檢驗,preverify是proguard的四個步驟之一

Android不需要preverify,去掉這一步可以加快混淆速度

-dontpreverify

有了verbose這句話,混淆后就會生成映射文件

包含有類名->混淆后類名的映射關系

然后使用printmapping指定映射文件的名稱

-verbose -printmapping priguardMapping.txt

指定混淆時采用的算法,后面的參數是一個過濾器

這個過濾器是谷歌推薦的算法,一般不改變

-optimizations !code/simplification/artithmetic,!field/,!class/merging/

保護代碼中的Annotation不被混淆

這在JSON實體映射時非常重要,比如fastJson

-keepattributes Annotation

避免混淆泛型

這在JSON實體映射時非常重要,比如fastJson

-keepattributes Signature

拋出異常時保留代碼行號

-keepattributes SourceFile,LineNumberTable</code></pre>

不混淆,需要保留的東西

# 保留所有的本地native方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}

保留了繼承自Activity、Application這些類的子類

因為這些子類有可能被外部調用

比如第一行就保證了所有Activity的子類不要被混淆

-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.view.View -keep public class com.android.vending.licensing.ILicensingService

如果有引用android-support-v4.jar包,可以添加下面這行

-keep public class com.null.test.ui.fragment.* {;}

保留Activity中的方法參數是view的方法,

從而我們在layout里面編寫onClick就不會影響

-keepclassmembers class extends android.app.Activity { public void (android.view.View); }

枚舉類不能被混淆

-keepclassmembers enum * { public static [] values(); public static valueOf(java.lang.String); }

保留自定義控件(繼承自View)不能被混淆

-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(); get* (); }

保留Parcelable序列化的類不能被混淆

-keep class implements android.os.Parcelable{ public static final android.os.Parcelable$Creator ; }

保留Serializable 序列化的類不被混淆

-keepclassmembers class * implements java.io.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(); }

對R文件下的所有類及其方法,都不能被混淆

-keepclassmembers class *.R$ { *; }

對于帶有回調函數onXXEvent的,不能混淆

-keepclassmembers class { void (On*Event); }</code></pre>

一般第三方和自己的bean文件是不需要混淆的。

-keep class com.null.test.entities. {
    //全部忽略
    ;
}
-keep class com.null.test.entities.** {
    //忽略get和set方法
    public void set();
    public  get();
    public ** is();
}
//以上兩種任意一種都行</code></pre> 
  

內嵌類

-keep class com.null.test.MainActivity$ {
    ;
}</code></pre> 
  

WebView的處理

-keepclassmembers class  extends android.webkit.WebViewClient {
    public void (android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
    public boolean (android.webkit.WebView, java.lang.String);
}
-keepclassmembers class  extends android.webkit.WebViewClient {
    public void (android.webkit.WebView, java.lang.String);
}</code></pre> 
  

第三方庫

-libraryjars ./libs/android-support-v4.jar
-dontwarn android.support.v4.** 
-dontwarn **CompatHoneycomb
-dontwarn **CompatHoneycombMR2
-dontwarn **CompatCreatorHoneycombMR2
-keep interface android.support.v4.app.** { *; }
-keep class android.support.v4.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.app.Fragment

混淆注意事項

  • 混淆必須對項目不造成任何崩潰問題。
  • 打包時忽略警告
    當在導出時,發現很多could not reference class之類的warning信息,如果確認app運行中和那些引用沒有什么關系的話,就可以添加-dontwarn標簽,就不會在提示這些warning信息了。如-dontwarn org.apache.**。
  • 使用annotation避免混淆
@Keep
@KeepPublicGettersSetters
public class Bean {
    public boolean booleanProperty;
    public int intProperty;
    public String stringProperty;
    public boolean isBooleanProperty() {
        return booleanProperty;
    }
}

注:android studio 是在build.gradle修改buildTypes如下:

buildTypes {
    release {
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

 

來自:http://blog.csdn.net/xiangzhihong8/article/details/53790660

 

 本文由用戶 NanWiltshir 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!