Android混淆
ProGuard是一個(gè)混淆代碼的開(kāi)源項(xiàng)目。它的主要作用就是混淆,當(dāng)然它還能對(duì)字節(jié)碼進(jìn)行縮減體積、優(yōu)化等,但那些對(duì)于我們來(lái)說(shuō)都算是次要的功能。
Java代碼是非常容易反編譯的。為了很好的保護(hù)Java源代碼,我們往往會(huì)對(duì)編譯好的class文件進(jìn)行混淆處理。
1、原理
Java 是一種跨平臺(tái)的、解釋型語(yǔ)言,Java 源代碼編譯成中間”字節(jié)碼”存儲(chǔ)于 class 文件中。由于跨平臺(tái)的需要,Java 字節(jié)碼中包括了很多源代碼信息,如變量名、方法名,并且通過(guò)這些名稱(chēng)來(lái)訪(fǎng)問(wèn)變量和方法,這些符號(hào)帶有許多語(yǔ)義信息,很容易被反編譯成 Java 源代碼。為了防止這種現(xiàn)象,我們可以使用 Java 混淆器對(duì) Java 字節(jié)碼進(jìn)行混淆。
混淆就是對(duì)發(fā)布出去的程序進(jìn)行重新組織和處理,使得處理后的代碼與處理前代碼完成相同的功能,而混淆后的代碼很難被反編譯,即使反編譯成功也很難得出程序 的真正語(yǔ)義。被混淆過(guò)的程序代碼,仍然遵照原來(lái)的檔案格式和指令集,執(zhí)行結(jié)果也與混淆前一樣,只是混淆器將代碼中的所有變量、函數(shù)、類(lèi)的名稱(chēng)變?yōu)楹?jiǎn)短的英 文字母代號(hào),在缺乏相應(yīng)的函數(shù)名和程序注釋的況下,即使被反編譯,也將難以閱讀。同時(shí)混淆是不可逆的,在混淆的過(guò)程中一些不影響正常運(yùn)行的信息將永久丟 失,這些信息的丟失使程序變得更加難以理解。
混淆器的作用不僅僅是保護(hù)代碼,它也有精簡(jiǎn)編譯后程序大小的作用。由于以上介紹的縮短變量和函數(shù)名以及丟失部分信息的原因, 編譯后 jar 文件體積大約能減少25% ,這對(duì)當(dāng)前費(fèi)用較貴的無(wú)線(xiàn)網(wǎng)絡(luò)傳輸是有一定意義的。
2、語(yǔ)法
配置
-ignorewarnings # 忽略警告,避免打包時(shí)某些警告出現(xiàn)
-optimizationpasses 5 # 指定代碼的壓縮級(jí)別
-dontusemixedcaseclassnames # 是否使用大小寫(xiě)混合
-dontskipnonpubliclibraryclasses # 是否混淆第三方j(luò)ar
-dontpreverify # 混淆時(shí)是否做預(yù)校驗(yàn)
-verbose # 混淆時(shí)是否記錄日志
-optimizations !code/simplification/arithmetic,!field/,!class/merging/ # 混淆時(shí)所采用的算法
-include {filename} 從給定的文件中讀取配置參數(shù)
-basedirectory {directoryname} 指定基礎(chǔ)目錄為以后相對(duì)的檔案名稱(chēng)
-injars {class_path} 指定要處理的應(yīng)用程序jar,war,ear和目錄
-outjars {class_path} 指定處理完后要輸出的jar,war,ear和目錄的名稱(chēng)
-libraryjars {classpath} 指定要處理的應(yīng)用程序jar,war,ear和目錄所需要的程序庫(kù)文件
-dontskipnonpubliclibraryclasses 指定不去忽略非公共的庫(kù)類(lèi)。
-dontskipnonpubliclibraryclassmembers 指定不去忽略包可見(jiàn)的庫(kù)類(lèi)的成員。
保留選項(xiàng)
-dontwarn {Modifier} 對(duì)指定的類(lèi)忽略警告
-keep {Modifier} {class_specification} 保護(hù)指定的類(lèi)文件和類(lèi)的成員
-keepclassmembers {modifier} {class_specification} 保護(hù)指定類(lèi)的成員,如果此類(lèi)受到保護(hù)他們會(huì)保護(hù)的更好
-keepclasseswithmembers {class_specification} 保護(hù)指定的類(lèi)和類(lèi)的成員,但條件是所有指定的類(lèi)和類(lèi)成員是要存在。
-keepnames {class_specification} 保護(hù)指定的類(lèi)和類(lèi)的成員的名稱(chēng)(如果他們不會(huì)壓縮步驟中刪除)
-keepclassmembernames {class_specification} 保護(hù)指定的類(lèi)的成員的名稱(chēng)(如果他們不會(huì)壓縮步驟中刪除)
-keepclasseswithmembernames {class_specification} 保護(hù)指定的類(lèi)和類(lèi)的成員的名稱(chēng),如果所有指定的類(lèi)成員出席(在壓縮步驟之后)
-printseeds {filename} 列出類(lèi)和類(lèi)的成員-keep選項(xiàng)的清單,標(biāo)準(zhǔn)輸出到給定的文件
壓縮
-dontshrink 不壓縮輸入的類(lèi)文件
-printusage {filename}
-whyareyoukeeping {class_specification}
優(yōu)化
-dontoptimize 不優(yōu)化輸入的類(lèi)文件
-assumenosideeffects {class_specification} 優(yōu)化時(shí)假設(shè)指定的方法,沒(méi)有任何副作用
-allowaccessmodification 優(yōu)化時(shí)允許訪(fǎng)問(wèn)并修改有修飾符的類(lèi)和類(lèi)的成員
混淆
-dontobfuscate 不混淆輸入的類(lèi)文件
-printmapping {filename}
-applymapping {filename} 重用映射增加混淆
-obfuscationdictionary {filename} 使用給定文件中的關(guān)鍵字作為要混淆方法的名稱(chēng)
-overloadaggressively 混淆時(shí)應(yīng)用侵入式重載
-useuniqueclassmembernames 確定統(tǒng)一的混淆類(lèi)的成員名稱(chēng)來(lái)增加混淆
-flattenpackagehierarchy {package_name} 重新包裝所有重命名的包并放在給定的單一包中
-repackageclass {package_name} 重新包裝所有重命名的類(lèi)文件中放在給定的單一包中
-dontusemixedcaseclassnames 混淆時(shí)不會(huì)產(chǎn)生形形色色的類(lèi)名
-keepattributes {attribute_name,...} 保護(hù)給定的可選屬性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.
-renamesourcefileattribute {string} 設(shè)置源文件中給定的字符串常量
Android Studio中的混淆
Android工程目錄下有個(gè)文件,proguard-rules.pro是AS中專(zhuān)用的proguard配置文件,其實(shí)只是后綴名不同,與Eclipse中的proguard-project.txt是一樣的。
在gradle中處理混淆的語(yǔ)句是:
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
其中proguard-android.txt是sdk中g(shù)roguard默認(rèn)的文件,而文件是存在于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.
# Don't 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.**
混淆的輸出文件及用處
混淆之后,會(huì)給我們輸出一些文件,在gradle方式下是在 /build/output/ 目錄下,ant是在 /bin/proguard 目錄,eclipse構(gòu)建在 /proguard 目錄像。
分別有以下文件:
dump.txt 描述apk文件中所有類(lèi)文件間的內(nèi)部結(jié)構(gòu)。
mapping.txt 列出了原始的類(lèi),方法,和字段名與混淆后代碼之間的映射。
seeds.txt 列出了未被混淆的類(lèi)和成員
usage.txt 列出了從apk中刪除的代碼
當(dāng)我們發(fā)布的release版本的程序出現(xiàn)bug時(shí),可以通過(guò)以上文件(特別時(shí)mapping.txt)文件找到錯(cuò)誤原始的位置,進(jìn)行bug修改。同時(shí),可能一開(kāi)始的proguard配置有錯(cuò)誤,也可以通過(guò)錯(cuò)誤日志,根據(jù)這些文件,找到哪些文件不應(yīng)該混淆,從而修改proguard的配置。
一些混淆的配置
sharesdk混淆
-keep class android.net.http.SslError
-keep class android.webkit.**{*;}
-keep class cn.sharesdk.**{*;}
-keep class com.sina.**{*;}
-keep class m.framework.**{*;}
Gson混淆
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.idea.fifaalarmclock.entity.***
-keep class com.google.gson.stream.** { *; }
Umeng sdk混淆
-keepclassmembers class * {
public <init>(org.json.JSONObject);
}
-keep class com.umeng.**
-keep public class com.idea.fifaalarmclock.app.R$*{
public static final int *;
}
-keep public class com.umeng.fb.ui.ThreadView {
}
-dontwarn com.umeng.**
-dontwarn org.apache.commons.**
-keep public class * extends com.umeng.**
-keep class com.umeng.** {*; }
Eclipse中常用的混淆配置
-ignorewarnings # 忽略警告,避免打包時(shí)某些警告出現(xiàn)
-optimizationpasses 5 # 指定代碼的壓縮級(jí)別
-dontusemixedcaseclassnames # 是否使用大小寫(xiě)混合
-dontskipnonpubliclibraryclasses # 是否混淆第三方j(luò)ar
-dontpreverify # 混淆時(shí)是否做預(yù)校驗(yàn)
-verbose # 混淆時(shí)是否記錄日志
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆時(shí)所采用的算法
-keep public class * extends android.app.Activity # 保持哪些類(lèi)不被混淆
-keep public class * extends android.app.Application # 保持哪些類(lèi)不被混淆
-keep public class * extends android.app.Service # 保持哪些類(lèi)不被混淆
-keep public class * extends android.content.BroadcastReceiver # 保持哪些類(lèi)不被混淆
-keep public class * extends android.content.ContentProvider # 保持哪些類(lèi)不被混淆
-keep public class * extends android.app.backup.BackupAgentHelper # 保持哪些類(lèi)不被混淆
-keep public class * extends android.preference.Preference # 保持哪些類(lèi)不被混淆
-keep public class com.android.vending.licensing.ILicensingService # 保持哪些類(lèi)不被混淆
-keepclasseswithmembernames class * { # 保持 native 方法不被混淆
native <methods>;
}
-keepclasseswithmembers class * { # 保持自定義控件類(lèi)不被混淆
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {# 保持自定義控件類(lèi)不被混淆
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity { # 保持自定義控件類(lèi)不被混淆
public void *(android.view.View);
}
-keepclassmembers enum * { # 保持枚舉 enum 類(lèi)不被混淆
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable { # 保持 Parcelable 不被混淆
public static final android.os.Parcelable$Creator *;
}