內(nèi)容提要
本篇文章主要有三個部分茉帅,讓讀者讀完后能自己寫規(guī)則混淆項目
- 對Android代碼怎么開啟混淆做一個簡單的介紹瞒斩。
- 對混淆規(guī)則做一個簡單介紹;
- 在混淆過后Crash日志反推代碼工具retrace.bat、可視化反推工具GUI說明苍鲜。
Proguard 混淆規(guī)則說明請參考《ProGuard 最全混淆規(guī)則說明》
對混淆的一個簡單介紹:
Android SDK 自帶了混淆工具Proguard。它位于SDK根目錄\tools\proguard下面玷犹。如果開啟了混淆混滔,Proguard默認情況下會對所有代碼,包括第三方包都進行混淆歹颓,可是有些代碼或者第三方包是不能混淆的坯屿,這就需要我們手動編寫混淆規(guī)則來保持不能被混淆的部分。
混淆有幾個作用:
- 【優(yōu)化】它能優(yōu)化java的字節(jié)碼晴股,使程序運行更快愿伴;
- 【壓縮】最直觀的就是減少App大小,在混淆過程中它會找出未被使用過的類和類成員并刪除他們电湘;
- 【混淆】這個功能使我們的java代碼中的類隔节、函數(shù)、變量名隨機變成無意義的代號形如:a,b,c...之類的寂呛,即使我們的APP即使被反編譯怎诫,也不容易理解了。
上面這幾個功能都是默認打開的贷痪,要關閉他們只需配置規(guī)則:
-dontshrink :關閉壓縮幻妓;
-dontoptimize:關閉優(yōu)化;
-dontobfuscate:關閉混淆劫拢。
寫在開始之前:
本編使用Android Studio工具作為開發(fā)環(huán)境肉津,Eclipse類似强胰;
Android Studio新建項目時會在項目根目錄下自動生成一個混淆配置文件:
proguard-rules.pro;【Eclipse 下面的文件名為:proguard.cfg或者proguard.txt】里面的內(nèi)容規(guī)則都是一樣的;
1.現(xiàn)在開始混淆:
第一步:開啟混淆妹沙,編輯項目配置文件build.gradle(Module: app)
*** minifyEnabled true偶洋;這個選項的意思為開啟混淆;
*** proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'距糖;//配置文件**
'proguard-android.txt' 是AndroidStudio默認自動導入的規(guī)則玄窝,這個文件位于Android SDK根目錄\tools\proguard\proguard-android.txt。這里面是一些比較常規(guī)的不能被混淆的代碼規(guī)則悍引。
??'proguard-rules.pro'是針對我們自己的項目需要特別定義混淆規(guī)則恩脂,它位于項目根目錄下面,里面的內(nèi)容需要我們自己編寫
zipAlignEnabled true 這個在打包時需要設置為true趣斤,能優(yōu)化我們的java字節(jié)碼俩块,提高運行效率;
build.gradle編輯完成后如下圖所示:(表示發(fā)布的release包唬渗,會按照指定規(guī)則來進行混淆)
android {
compileSdkVersion 23
buildToolsVersion "24.0.1"
defaultConfig {
}
buildTypes {
release { //主要看這部分:
zipAlignEnabled true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
....
}
第二步:現(xiàn)在就可以開始編輯自己的規(guī)則了
下面是一個比較簡單的proguard-rules.pro文件內(nèi)容典阵,做完這些就可以打包發(fā)布版本了
將混淆過的APK反編譯,你會發(fā)現(xiàn)里面的代碼變成了a镊逝、b壮啊、c之類的沒有意義的代號了蚣常,而且APK也變小了世吨。
2.重點來了【規(guī)則介紹】
現(xiàn)在對混淆規(guī)則做一個介紹:
先來看一看Android studio 默認包含的規(guī)則文件(proguard-android.txt 文件)里面的類容水援,我們選出幾個重點說明:
#混淆時不生成大小寫混合的類名
-dontusemixedcaseclassnames
#不忽略非公共的類庫
-dontskipnonpubliclibraryclasses
#混淆過程中打印詳細信息
-verbose
#關閉優(yōu)化
-dontoptimize
#不預校驗
-dontpreverify
# Annotation注釋不能混淆
-keepattributes *Annotation*
#對于NDK開發(fā) 本地的native方法不能被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
#保持View的子類里面的set妻顶、get方法不被混淆(*代替任意字符)
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
#保持Activity子類里面的參數(shù)類型為View的方法不被混淆棚愤,如被XML里面應用的onClick方法
# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
#保持枚舉類型values()抖韩、以及valueOf(java.lang.String)成員不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#保持實現(xiàn)Parcelable接口的類里面的Creator成員不被混淆
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
#保持R類靜態(tài)成員不被混淆
-keepclassmembers class **.R$* {
public static <fields>;
}
#不警告support包中不使用的引用
-dontwarn android.support.**
-keep class android.support.annotation.Keep
-keep @android.support.annotation.Keep class * {*;}
#保持使用了Keep注解的方法以及類不被混淆
-keepclasseswithmembers class * {
@android.support.annotation.Keep <methods>;
}
#保持使用了Keep注解的成員域以及類不被混淆
-keepclasseswithmembers class * {
@android.support.annotation.Keep <fields>;
}
-keepclasseswithmembers class * {
@android.support.annotation.Keep <init>(...);
}
上面默認的規(guī)則中指示了些需要保持不能別混淆的代碼缤弦,包括:
- 繼承至Android組件(Activity, Service...)的類谢鹊。
- 自定義控件浴滴,繼承至View的類(被xml文件引用到的拓萌,名字已經(jīng)固定了的)
- enum 枚舉
- 實現(xiàn)了 android.os.Parcelable 接口的
- Android R文件
- 數(shù)據(jù)庫驅(qū)動...
- Android support 包等
- Android 的注釋不能混淆
-keepattributes *Annotation*
- 對于NDK開發(fā) 本地的native方法不能被混淆
-keepclasseswithmembernames class * { native <methods>; }
對于特定的項目還有很多不能被混淆的,需要我們自己寫規(guī)則來指示升略,將在下面來說明:
- 對一些符號做一些說明
現(xiàn)在來看一下需要我們自己編寫的proguard-rules.pro:
#壓縮級別0-7微王,Android一般為5(對代碼迭代優(yōu)化的次數(shù))
-optimizationpasses 5
#不使用大小寫混合類名
-dontusemixedcaseclassnames
#混淆時記錄日志
-verbose
#不警告org.greenrobot.greendao.database包及其子包里面未應用的應用
-dontwarn org.greenrobot.greendao.database.**
-dontwarn rx.**
-dontwarn org.codehaus.jackson.**
......
#保持jackson包以及其子包的類和類成員不被混淆
-keep class org.codehaus.jackson.** {*;}
#--------重要說明-------
#-keep class 類名 {*;}
#-keepclassmembers class 類名{*;}
#一個*表示保持了該包下的類名不被混淆;
# -keep class org.codehaus.jackson.*
#二個**表示保持該包以及它包含的所有子包下的類名不被混淆
# -keep class org.codehaus.jackson.**
#------------------------
#保持類名品嚣、類里面的方法和變量不被混淆
-keep class org.codehaus.jackson.** {*;}
#不混淆類ClassTwoOne的類名以及類里面的public成員和方法
#public 可以換成其他java屬性如private炕倘、public static 、final等
#還可以使<init>表示構造方法翰撑、<methods>表示方法罩旋、<fields>表示成員,
#這些前面也可以加public等java屬性限定
-keep class com.dev.demo.two.ClassTwoOne {
public *;
}
#不混淆類名,以及里面的構造函數(shù)
-keep class com.dev.demo.ClassOne {
public <init>();
}
#不混淆類名涨醋,以及參數(shù)為int 的構造函數(shù)
-keep class com.dev.demo.two.ClassTwoTwo {
public <init>(int);
}
#不混淆類的public修飾的方法瓜饥,和private修飾的變量
-keepclassmembers class com.dev.demo.two.ClassTwoThree {
public <methods>;
private <fields>;
}
#不混淆內(nèi)部類,需要用$修飾
#不混淆內(nèi)部類ClassTwoTwoInner以及里面的全部成員
-keep class com.dev.demo.two.ClassTwoTwo$ClassTwoTwoInner{*;}
......
對一些規(guī)則的解釋:
-keepattributes {name}??保護給定的屬性不被混淆浴骂,
??如:-keepattributes *Annotation*
-dontwarn {name}??不要警告指定庫中找不到的引用压固。混淆在默認情況下會檢查每個庫的引用是否正確靠闭,但是有些第三方庫里面會有用不到的類,有些沒有正確引用坎炼,所以需要對第三方庫取消警告 否則會報錯愧膀,而且有可能混淆時間會很長。
??如:-dontwarn org.codehaus.jackson.**
-keep {Modifier} {class_specification}??保留指定的類名谣光、類成員不被混淆
-keepclassmembers {modifier} {class_specification} ??保留指定的類成員不被混淆
-keepclasseswithmembers {class_specification} ??保留指定的類名檩淋、類成員不被混淆
-keepnames {class_specification} ??保留指定的類名、類成員的名稱不被混淆
-keepclasseswithmembernames {class_specification} ??保留指定的類名萄金、類成員名稱不被混淆(如果存在的話)
3.混淆后根據(jù)Crash崩潰日志反推代碼
成功打包后會在目錄*** app\build\outputs\mapping\release ***下生成幾個文件:
說明:
dump.txt 混淆后類的內(nèi)部結(jié)構說明;
mapping.txt 混淆前與混淆后名稱對應關系;
seeds.txt 經(jīng)過了一系列keep語句的保持蟀悦,沒有被混淆的類,成員的名稱列表文件氧敢。
usage.txt 經(jīng)過壓縮后被刪除的沒有使用的代碼日戈,方法...等的名稱的列表文件
retrace工具:
混淆反推工具為retrace.sh(Mac平臺)或者retrace.bat(Windows平臺)
該工具在sdk根目錄\tools\proguard\bin\retrace.sh
(Windows平臺類似);
命令格式:
*** ./retrace.sh [mapping.txt目錄] [崩潰日歷目錄]
(Windows平臺:retrace.bat [mapping.txt目錄] [崩潰日歷目錄]) ***
- 第一步:保存Crash日志如下:截取日志保存為txt文件如:bug.txt孙乖。
03-21 03:09:32.389: E/AndroidRuntime(3582): FATAL EXCEPTION: main
03-21 03:09:32.389: E/AndroidRuntime(3582): Process: com.dev.demo, PID: 3582
03-21 03:09:32.389: E/AndroidRuntime(3582): java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
03-21 03:09:32.389: E/AndroidRuntime(3582): at com.dev.demo.two.b.b(Unknown Source)
03-21 03:09:32.389: E/AndroidRuntime(3582): at com.dev.demo.a.a.printTest(Unknown Source)
03-21 03:09:32.389: E/AndroidRuntime(3582): at com.dev.demo.MainActivity.i(Unknown Source)
03-21 03:09:32.389: E/AndroidRuntime(3582): at com.dev.demo.MainActivity.a(Unknown Source)
03-21 03:09:32.389: E/AndroidRuntime(3582): at com.dev.demo.MainActivity$1.onClick(Unknown Source)
- 第二步:刪掉Crash日志中03-21 03:09:32.389: E/AndroidRuntime(3582):部分浙炼,否則無法反推還原。刪除后如下:
FATAL EXCEPTION: main
Process: com.dev.demo, PID: 3582
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
at com.dev.demo.two.b.b(Unknown Source)
at com.dev.demo.a.a.printTest(Unknown Source)
at com.dev.demo.MainActivity.i(Unknown Source)
at com.dev.demo.MainActivity.a(Unknown Source)
at com.dev.demo.MainActivity$1.onClick(Unknown Source)
- 第三步:打開命令窗口輸入命令:如:*** ./retrace.sh mapping.txt bug.txt ***
如下:
按確認后得到結(jié)果如下:
FATAL EXCEPTION: main
Process: com.dev.demo, PID: 3582
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
at com.dev.demo.two.ClassTwoThree.void test()(Unknown Source)
at com.dev.demo.one.ClassOneOne.void printTest()(Unknown Source)
at com.dev.demo.MainActivity.void printWifi()(Unknown Source)
at com.dev.demo.MainActivity.void access$000(com.dev.demo.MainActivity)(Unknown Source)
at com.dev.demo.MainActivity$1.void onClick(android.view.View)(Unknown Source) ```
除了有retrace.sh工具外還有可視化工具唯袄,和retrace.sh同目錄下proguardgui.sh弯屈,這是一塊Progurad的可視化工具:
(Windows平臺類似)
![4.png](http://upload-images.jianshu.io/upload_images/3589324-f0d492d9da432138.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>有時為了Crash日志更容易定位可以在規(guī)則里面添加:
-keepattributes SourceFile, LineNumberTable
**這樣Crash日志里面就能保留類名稱 和行號了**
**好了,所有終于完了恋拷,希望能幫助到你资厉!謝謝!**
***