轉載請標明出處:
http://blog.csdn.net/sinat_15877283/article/details/50960690;
本文出自: 【溫利東的博客】
本篇文章中介紹的混淆技術都是基於Android Studio的,Eclipse的用法也基本類似,但是就不再爲Eclipse專門做講解了。
混淆APK
在Android Studio當中混淆APK實在是太簡單了,藉助SDK中自帶的Proguard工具,只需要修改build.gradle中的一行配置即可。可以看到,現在build.gradle中minifyEnabled的值是false,這裏我們只需要把值改成true,打出來的APK包就會是混淆過的了。如下所示:
release {
minifyEnabled true //是否啓用混淆
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' //用於選定混淆配置文件
}
其中minifyEnabled用於設置是否啓用混淆,proguardFiles用於選定混淆配置文件。注意這裏是在release閉包內進行配置的,因此只有打出正式版的APK纔會進行混淆,Debug版的APK是不會混淆的。
混淆規則
默認混淆配置
其實就是剛纔在build.gradle的release閉包下配置的proguard-android.txt文件,這個文件存放於< Android SDK>/tools/proguard目錄下,我們打開來看一下:
# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
# Optimization is turned off by default. Dex does not like code run
# through the ProGuard optimize and preverify steps (and performs some
# of these optimizations on its own).
-dontoptimize
-dontpreverify
# Note that if you want to enable optimization, you cannot just
# include optimization flags in your own project configuration file;
# instead you will need to point to the
# "proguard-android-optimize.txt" file instead of this one from your
# project.properties file.
-keepattributes *Annotation*
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
-keepclasseswithmembernames class * {
native <methods>;
}
# 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*();
}
# 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);
}
# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
# The support library contains references to newer platform versions.
# Dont warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontwarn android.support.**
這個就是默認的混淆配置文件了,我們來一起逐行閱讀一下。
*-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 <methods>;
}
表示不混淆任何包含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 <fields>;
}
表示不混淆R文件中的所有靜態字段,我們都知道R文件是通過字段來記錄每個資源的id的,字段名要是被混淆了,id也就找不着了。
*-dontwarn android.support.***
表示對android.support包下的代碼不警告,因爲support包中有很多代碼都是在高版本中使用的,如果我們的項目指定的版本比較低在打包時就會給予警告。不過support包中所有的代碼都在版本兼容性上做足了判斷,因此不用擔心代碼會出問題,所以直接忽略警告就可以了。
好了,這就是proguard-android.txt文件中所有默認的配置,而我們混淆代碼也是按照這些配置的規則來進行混淆的。經過我上面的講解之後,相信大家對這些配置的內容基本都能理解了。不過proguard語法中還真有幾處非常難理解的地方,我自己也是研究了好久才搞明白,下面和大家分享一下這些難懂的語法部分。
混淆規則定義
proguard中一共有三組六個keep關鍵字,很多人搞不清楚它們的區別,這裏我們通過一個表格來直觀地看下:
關鍵字 | 描述 |
---|---|
keep | 保留類和類中的成員,防止它們被混淆或移除。 |
keepnames | 保留類和類中的成員,防止它們被混淆,但當成員沒有被引用時會被移除。 |
keepclassmembers | 只保留類中的成員,防止它們被混淆或移除。 |
keepclassmembernames | 只保留類中的成員,防止它們被混淆,但當成員沒有被引用時會被移除。 |
keepclasseswithmembers | 保留類和類中的成員,防止它們被混淆或移除,前提是指名的類中的成員必須存在,如果不存在則還是會混淆。 |
keepclasseswithmembernames | 保留類和類中的成員,防止它們被混淆,但當成員沒有被引用時會被移除,前提是指名的類中的成員必須存在,如果不存在則還是會混淆。 |
除此之外,proguard中的通配符也比較讓人難懂,proguard-android.txt中就使用到了很多通配符,我們來看一下它們之間的區別:
通配符 | 描述 |
---|---|
< field > | 匹配類中的所有字段 |
< method> | 匹配類中的所有方法 |
< init> | 匹配類中的所有構造函數 |
* | 匹配任意長度字符,但不含包名分隔符(.)。比如說我們的完整類名是com.example.test.MyActivity,使用com.,或者com.exmaple.* 都是無法匹配的,因爲* 無法匹配包名中的分隔符,正確的匹配方式是com.exmaple.* .* ,或者com.exmaple.test.* ,這些都是可以的。但如果你不寫任何其它內容,只有一個*,那就表示匹配所有的東西。 |
** | 匹配任意長度字符,並且包含包名分隔符(.)。比如proguard-android.txt中使用的-dontwarn android.support.**就可以匹配android.support包下的所有內容,包括任意長度的子包。 |
.* ** | 匹配任意參數類型。比如void set*(* * * )就能匹配任意傳入的參數類型,* * * get*()就能匹配任意返回值的類型。 |
… | 匹配任意長度的任意類型參數。比如void test(…)就能匹配任意void test(String a)或者是void test(int a, String b)這些方法。 |
雖說上面表格已經解釋的很詳細了,但是很多人對於keep和keepclasseswithmembers這兩個關鍵字的區別還是搞不懂。確實,它們之間用法有點太像了,我做了很多次試驗它們的結果都是相同的。其實唯一的區別就在於類中聲明的成員存不存在,我們還是通過一個例子來直接地看一下,先看keepclasseswithmember關鍵字:
-keepclasseswithmember class * {
native <methods>;
}
這段代碼的意思其實很明顯,就是保留所有含有native方法的類的類名和native方法名,而如果某個類中沒有含有native方法,那就還是會被混淆。
但是如果改成keep關鍵字,結果會完全不一樣:
-keep class * {
native <methods>;
}
使用keep關鍵字後,你會發現代碼中所有類的類名都不會被混淆了,因爲keep關鍵字看到class *就認爲應該將所有類名進行保留,而不會關心該類中是否含有native方法。當然這樣寫只會保證類名不會被混淆,類中的成員還是會被混淆的。
比較難懂的用法大概就這些吧,掌握了這些內容之後我們就能繼續前進了。
混淆的規則都是按照proguard-android.txt中默認的規則來的,當然我們也可以修改proguard-android.txt中的規則,但是直接在proguard-android.txt中修改會對我們本機上所有項目的混淆規則都生效,那麼有沒有什麼辦法只針對當前項目的混淆規則做修改呢?當然是有辦法的了,你會發現任何一個Android Studio項目在app模塊目錄下都有一個proguard-rules.pro文件,這個文件就是用於讓我們編寫只適用於當前項目的混淆規則的。
提示:
- proguard-rules.pro文件名可以任意,只要在配置文件中指明即可。比如,從其它項目中複製一個proguard.cfg混淆規則文件,只需將上面的代碼改成
proguardFiles getDefaultProguardFile('proguard-android.txt'),
即可,這句話表示使用項目中根目錄下的proguard.cfg文件作爲本項目的混淆規則文件。
'proguard.cfg' - 打包項目注意記得在Build Type:選項下選擇release,否則只打包不會混淆。如下圖:
推薦:《Android代碼混淆技巧》、Android安全攻防戰,反編譯與混淆技術完全解析、Proguard簡要語法手冊
、https://segmentfault.com/a/1190000004461614