android代碼混淆

Android代碼混淆與反編譯


有問題可以加羣討論:517018699
這裏寫圖片描述

第一步:混淆apk
* 1.AS藉助了SDK的自帶Proguard工具,我們只需要修改build.gradle中的一行配置即可。
* build.gradle中minifyEnabled的值是false,這裏我們只需要把值改成true,打出來的APK包就會是混淆過的了。如下所示

* minifyEnabled用於設置是否啓用混淆,proguardFiles用於選定混淆配置文件。注意這裏是在release閉包內進行配置的,因此只有打出正式版的APK纔會進行混淆,Debug版的APK是不會混淆的。
* 這種混淆後的代碼:
“`
1.普通類肯定是會被混淆的,不管是類名、方法名還是變量都不會放過
2.爲是多餘的代碼,在打包的時候就給移除掉了。不僅僅是代碼,沒有被調用的資源同樣也會被移除掉,因此minifyEnabled除了混淆代碼之外,還可以起到壓縮APK包的作用
3.AndroidManifest.xml中去註冊的所有類的類名以及從父類重寫的方法名都自動不會被混淆,Activity之外,這份規則同樣也適用於Service、BroadcastReceiver和ContentProvider
4.第三方的Jar包都是會被混淆的


   * 2. 當然我們也可以自己定義代碼混淆規則:在build.gradle的release閉包下配置的proguard-android.txt文件 

-dontusemixedcaseclassnames 表示混淆時不使用大小寫混合類名。
-dontskipnonpubliclibraryclasses 表示不跳過library中的非public的類。
-verbose 表示打印混淆的詳細信息。
-dontoptimize 表示不進行優化,建議使用此選項,因爲根據proguard-android-optimize.txt中的描述,優化可能會造成一些潛在風險,不能保證在所有版本的Dalvik上都正常運行。
-dontpreverify 表示不進行預校驗。這個預校驗是作用在Java平臺上的,Android平臺上不需要這項功能,去掉之後還可以加快混淆速度。
-keepattributes Annotation 表示對註解中的參數進行保留。
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
表示不混淆上述聲明的兩個類,這兩個類我們基本也用不上,是接入Google原生的一些服務時使用的
-keepclasseswithmembernames class * {
native ;
}

表示不混淆任何包含native方法的類的類名以及native方法名,這個和我們剛纔驗證的結果是一致的。

-keepclassmembers public class * extends android.view.View {
void set*(*);
* get*();
}

表示不混淆任何一個View中的setXxx()和getXxx()方法,因爲屬性動畫需要有相應的setter和getter的方法實現,混淆了就無法工作了。

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

表示不混淆Activity中參數是View的方法,因爲有這樣一種用法,在XML中配置android:onClick=”buttonClick”屬性,當用戶點擊該按鈕時就會調用Activity中的buttonClick(View view)方法,如果這個方法被混淆的話就找不到了。

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

表示不混淆枚舉中的values()和valueOf()方法,枚舉我用的非常少,這個就不評論了。

-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}

表示不混淆Parcelable實現類中的CREATOR字段,毫無疑問,CREATOR字段是絕對不能改變的,包括大小寫都不能變,不然整個Parcelable工作機制都會失敗。

-keepclassmembers class *.R$ {
public static ;
}

表示不混淆R文件中的所有靜態字段,我們都知道R文件是通過字段來記錄每個資源的id的,字段名要是被混淆了,id也就找不着了。
-dontwarn android.support.** 表示對android.support包下的代碼不警告,因爲support包中有很多代碼都是在高版本中使用的,如果我們的項目指定的版本比較低在打包時就會給予警告。不過support包中所有的代碼都在版本兼容性上做足了判斷,因此不用擔心代碼會出問題,所以直接忽略警告就可以了。
好了,這就是proguard-android.txt文件中所有默認的配置,而我們混淆代碼也是按照這些配置的規則來進行混淆的。經過我上面的講解之後,相信大家對這些配置的內容基本都能理解了。不過proguard語法中還真有幾處非常難理解的地方,我自己也是研究了好久才搞明白,下面和大家分享一下這些難懂的語法部分。
proguard中一共有三組六個keep關鍵字,很多人搞不清楚它們的區別,這裏我們通過一個表格來直觀地看下:

雖說上面表格已經解釋的很詳細了,但是很多人對於keep和keepclasseswithmembers這兩個關鍵字的區別還是搞不懂。確實,它們之間用法有點太像了,我做了很多次試驗它們的結果都是相同的。其實唯一的區別就在於類中聲明的成員存不存在,我們還是通過一個例子來直接地看一下,先看keepclasseswithmember關鍵字:


![](http://i.imgur.com/b5FDdY4.png)

雖說上面表格已經解釋的很詳細了,但是很多人對於keep和keepclasseswithmembers這兩個關鍵字的區別還是搞不懂。確實,它們之間用法有點太像了,我做了很多次試驗它們的結果都是相同的。其實唯一的區別就在於類中聲明的成員存不存在,我們還是通過一個例子來直接地看一下,先看keepclasseswithmember關鍵字:
-keepclasseswithmember class * {
native ;
}

這段代碼的意思其實很明顯,就是保留所有含有native方法的類的類名和native方法名,而如果某個類中沒有含有native方法,那就還是會被混淆。
但是如果改成keep關鍵字,結果會完全不一樣:

-keep class * {
native ;
}

使用keep關鍵字後,你會發現代碼中所有類的類名都不會被混淆了,因爲keep關鍵字看到class *就認爲應該將所有類名進行保留,而不會關心該類中是否含有native方法。當然這樣寫只會保證類名不會被混淆,類中的成員還是會被混淆的。

```
-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}    設置源文件中給定的字符串常量  

-optimizationpasses 5  
#包明不混合大小寫  
-dontusemixedcaseclassnames  
#不去忽略非公共的庫類  
-dontskipnonpubliclibraryclasses  
 #優化  不優化輸入的類文件  
-dontoptimize  
 #預校驗  
-dontpreverify  
 # 混淆時所採用的算法  
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*  
#保護註解  
-keepattributes *Annotation*  


# 保持哪些類不被混淆  
-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 com.android.vending.licensing.ILicensingService  
#如果有引用v4包可以添加下面這行  
#-keep public class * extends android.support.v4.app.Fragment  
-keep public class * extends android.support.** { *; }  
#如果引用了v4或者v7包  
-dontwarn android.support.*  
#忽略警告  
-ignorewarning  


#####################記錄生成的日誌數據,gradle build時在本項目根目錄輸出################  
 #混淆時是否記錄日誌  
-verbose  
#apk 包內所有 class 的內部結構  
-dump class_files.txt  
#未混淆的類和成員  
-printseeds seeds.txt  
#列出從 apk 中刪除的代碼  
-printusage unused.txt  
#混淆前後的映射  
-printmapping mapping.txt  


#####################記錄生成的日誌數據,gradle build時 在本項目根目錄輸出-end################  


#####混淆保護自己項目的部分代碼以及引用的第三方jar包library - start #######  


#如果不想混淆 keep 掉  保留一個完整的包  
#-keep class com.lippi.recorder.iirfilterdesigner.** {*; }  
#項目特殊處理代碼  
#忽略警告  
#-dontwarn com.lippi.recorder.utils**  


#如果用用到Gson解析包的,直接添加下面這幾行就能成功混淆,不然會報錯。  
#//原因分析,可能是高版本的 sdk 通過 proguard 混淆代碼時默認已經將 lib目錄中的 jar 都已經添加到打包腳本中,所以不需要再次手動添加  
# 混淆jar  
#-libraryjars libs/gson-2.2.4.jar  
# 混淆類  
#-keep class sun.misc.Unsafe { *; }  
# 混淆包  
#-keep class com.google.gson.examples.android.model.** { *; }  
#dialog  
-keep class me.drakeet.materialdialog.** { *; }  
#加載框  
-keep class com.kaopiz.kprogresshud.** { *; }  
#下拉刷新  
-keep class in.srain.cube.views.ptr.** { *; }  
#實體類不混淆  


-keep class com.ousrslook.shimao.commen.ioc.** { *; } #不能混淆 否則註解無效  
-keep class com.ousrslook.shimao.model.** { *; } #不能混淆  
-keep class com.ousrslook.shimao.net.XaResult{ *; }#統一返回的實體類泛型不能混淆  
#-keep class com.ousrslook.shimao.net.** { *; }  


####混淆保護自己項目的部分代碼以及引用的第三方jar包library-end####  


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


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


#保持自定義控件類不被混淆  
-keepclasseswithmembers class * {  
    public <init>(android.content.Context, android.util.AttributeSet);  
}  


#保持自定義控件類不被混淆  
-keepclassmembers class * extends android.app.Activity {  
   public void *(android.view.View);  
}  


#保持 Parcelable 不被混淆  
-keep class * implements android.os.Parcelable {  
  public static final android.os.Parcelable$Creator *;  
}  


#保持 Serializable 不被混淆  
-keepnames class * implements java.io.Serializable  


#保持 Serializable 不被混淆並且enum 類也不被混淆  
-keepclassmembers class * implements java.io.Serializable {  
    static final long serialVersionUID;  
    private static final java.io.ObjectStreamField[] serialPersistentFields;  
    !static !transient <fields>;  
    !private <fields>;  
    !private <methods>;  
    private void writeObject(java.io.ObjectOutputStream);  
    private void readObject(java.io.ObjectInputStream);  
    java.lang.Object writeReplace();  
    java.lang.Object readResolve();  
}  


#保持枚舉 enum 類不被混淆 如果混淆報錯,建議直接使用上面的 -keepclassmembers class * implements java.io.Serializable即可  
-keepclassmembers enum * {  
  public static **[] values();  
  public static ** valueOf(java.lang.String);  
}  


-keepclassmembers class * {  
    public void *ButtonClicked(android.view.View);  
}  


#不混淆資源類  
-keepclassmembers class **.R$* {  
    public static <fields>;  
}  
 -keep class **.R$* { *; }  
#避免混淆泛型 如果混淆報錯建議關掉  
-keepattributes Signature</span> 
Android中不能混淆的代碼有
1,Android系統組件,系統組件有固定的回調方法被系統調用的
2,被Resource 文件引用到的,名字已經固定,也不能混淆,比如自定義的View ,
3, Parcelable ,Serializable,需要使用android 序列化的。
4,其他Anroid 官方建議 不混淆的,如
   android.app.backup.BackupAgentHelper
    android.preference.Preference
   com.android.vending.licensing.ILicensingService
5,enum 枚舉 ,系統需要處理枚舉的固定方法。
6,native 本地方法,不能修改本地方法名
7,annotations 註釋
8,數據庫驅動jdbc
9,使用反射方法的
10,使用第三方庫引用不能混淆
11,運行時動態調用的函數或者成員變量
12,我們代碼依賴於系統的接口,比如被系統代碼調用的回調方法,這種情況最複雜,最費時間地方。
如果你不確定哪些需要手動配置,可以以默認的配置生成程序,當運行中發現ClassNotFoundException異常時,即可找到哪個類不該被混淆。
所以使用proguard時,我們需要有個配置文件告訴proguard 那些 元素是不能混淆的。

第二步:瞭解命令

1.代碼混淆的壓縮比例,值在0-7之間:
  -optimizationpasses  

2.混淆後的類名爲小寫:
  -dontusemixedcaseclassnames

3.指定不去忽略非公共的庫的類:
  -dontskipnonpubliclibraryclasses

4.指定不去忽略非公共的庫的類的成員:
  -dontskipnonpubliclibraryclassmembers

5.不做預校驗的操作
  -dontpreverify

6.生成原類名和混淆後的類名的映射文件
  -verbose
  -printmapping proguardMapping.txt

7.指定混淆是採用的算法
  -optimizations !code/simplification/cast,!field/*,!class/merging/*

8.不混淆Annotation
  -keepattributes *Annotation*,InnerClasses

9.不混淆泛型
  -keepattributes Signature

10.拋出異常時保留代碼行號
  -keepattributes SourceFile,LineNumberTable

11.保留類名不變,也就是類名不混淆,而類中的成員名不保證。當然也可以是繼承XXX類的所有類名不混淆。
  -keep class XXXX

12.保留類名和成員名。當然也可以是類中特定方法,代碼不貼了。
  -keepclasseswithmembers class XXXX

13.不要筆記
   -dontnote

14.防止類和成員被移除或者被重命名
  -keep

15.防止類和成員被重命名
  -keepnames

16.防止成員被移除或者被重命名
  -keepclassmembers

17.防止成員被重命名
  -keepnames

18.防止擁有該成員的類和成員被移除或者被重命名
  -keepclasseswithmembers

19.防止擁有該成員的類和成員被重命名
  -keepclasseswithmembernames
  • 2.保持元素不參與混淆規則:
[保持命令] [類] {
    [成員] 
}

“類”代表類相關的限定條件,它將最終定位到某些符合該限定條件的類。它的內容可以使用:
* 1.具體的類
* 2.訪問修飾符(public、protected、private)
* 3.通配符**,匹配任意長度字符,並且包含包名分隔符(.)
* 4.extends,即可以指定類的基類
* implement,匹配實現了某接口的類
* 5.$,內部類
“成員”代表類成員相關的限定條件,它將最終定位到某些符合該限定條件的類成員。它的內容可以使用:
* 匹配所有構造器
* 匹配所有域
* 匹配所有方法
* 通配符*,匹配任意長度字符,但不含包名分隔符(.)
* 通配符**,匹配任意長度字符,並且包含包名分隔符(.)
* 通配符*,匹配任意參數類型
* …,匹配任意長度的任意類型參數。比如void test(…)就能匹配任意 void test(String a) 或者是 void test(int a, String b) 這些方法。
* 訪問修飾符(public、protected、private)
舉個例子,假如需要將name.huihui.test包下所有繼承Activity的public類及其構造函數都保持住,可以這樣寫:

-keep public class name.huihui.test.** extends Android.app.Activity {
    <init>
}

常用的自定義混淆規則

1.不混淆某個類:
  -keep public class name.huihui.example.Test { *; }

2.不混淆某個包所有的類
  -keep class name.huihui.test.** { *; }

3.不混淆某個類的子類
  -keep public class * extends name.huihui.example.Test { *; }

4.不混淆所有類名中包含了“model”的類及其成員
  -keep public class **.*model*.** {*;}

5.不混淆某個接口的實現
  -keep class * implements name.huihui.example.TestInterface { *; }

6.不混淆某個類的構造方法
  -keepclassmembers class name.huihui.example.Test { 
   public <init>(); 
  }

7.不混淆某個類的特定的方法
 -keepclassmembers class name.huihui.example.Test { 
  public void test(java.lang.String); 
  }

8.不混淆某個類的內部類
 -keep class name.huihui.example.Test$* {
      *;
 }

9.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章