淺談Android混淆

1.What and why引几?

  • What?

代碼混淆(Obfuscated code)亦稱花指令,是將計算機程序的代碼挽铁,轉(zhuǎn)換成一種功能上等價伟桅,但是難于閱讀和理解的形式的行為。

  • Why?

混淆的目的是為了加大反編譯的成本,但是并不能徹底防止反編譯.

2.How叽掘?

ProGuard由shrink楣铁、optimize、obfuscate和preverify四個步驟組成更扁,每個步驟都是可選的盖腕,需要哪些步驟都可以在腳本中配置。參見ProGuard官方介紹浓镜。

ProGuard_build_process.png

Entry Points(入口點):

為了確定哪些代碼應該被保留溃列,哪些代碼應該被移除或混淆,需要確定一個或多個Entry Point膛薛。Entry Point經(jīng)常是帶有main methods,applets,midlets的classes,它們在混淆過程中會被保留听隐。

What does each step do?


  • shrink: Proguard從上述EntryPoints開始遍歷搜索哪些類和類成員被使用哄啄。其他沒有被使用的類和類成員會移除雅任。

  • optimize: 優(yōu)化代碼,非EntryPoints類會加上private/static/final, 沒有用到的參數(shù)會被刪除咨跌,一些方法可能會變成內(nèi)聯(lián)代碼沪么。

  • obfuscate: 使用短又沒有語義的名字重命名非EntryPoints的類名,變量名锌半,方法名禽车。EntryPoints的名字保持不變。

  • preverify: 預校驗代碼是否符合Java1.6或者更高的規(guī)范(唯一一個與入口類不相關的步驟)

3.Usage

要執(zhí)行proguard,可以直接執(zhí)行命令:

java -jar proguard.jar options ...

如果有Android SDK的同學可以在{ANDROID_SDK_ROOT}/tools/proguard/lib/目錄下找到proguard.jar這個jar包刊殉⊙乘ぃ或者,也可以在{ANDROID_SDK_ROOT}/tools/proguard/bin目錄下直接使用腳本執(zhí)行命令冗澈。

我們也可以把proguard的參數(shù)寫到一個配置文件中钦勘,比如說proguard.cfg。那我們的命令可以這樣寫:

java -jar proguard.jar @proguard.cfg

這個文件也就是我們在Android Studio中經(jīng)常配置的混淆文件了亚亲。我們在編譯正式包的時候打包腳本自動幫我們執(zhí)行了這條命令彻采。通過這個腳本可以避免重復輸入?yún)?shù)。

當然捌归,我們也可以配置文件與命令行參數(shù)混用肛响,例如:

java -jar proguard.jar @proguard.cfg -verbose

AndroidStudio中開啟混淆


參考Android官方文檔
如果需要開啟混淆,在build.gradle文件中相應的BuildType下將minifyEnabled 設置為true惜索,開啟混淆會降低構(gòu)建速度特笋,因此避免在debug版本中開啟混淆。
以下是以release版本為例巾兆,開啟混淆的gradle腳本片段:

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

其中proguardFiles屬性用于定義 ProGuard 規(guī)則猎物,與上文中直接使用proguard.jar進行混淆時指定的文件選項是一個意思虎囚。

  • getDefaultProguardFile(‘proguard-android.txt’) 方法可從 Android SDK tools/proguard/ 文件夾獲取默認的 ProGuard 設置。要想做進一步的代碼壓縮蔫磨,請嘗試使用位于同一位置的 proguard-android-optimize.txt 文件淘讥。它包括相同的 ProGuard 規(guī)則,但還包括其他在字節(jié)碼一級(方法內(nèi)和方法間)執(zhí)行分析的優(yōu)化堤如,以進一步減小 APK 大小和幫助提高其運行速度蒲列。
  • proguard-rules.pro 文件用于添加自定義 ProGuard 規(guī)則。默認情況下搀罢,該文件位于模塊根目錄(build.gradle 文件旁)蝗岖,內(nèi)容為空。
    構(gòu)建輸出

構(gòu)建時Proguard都會輸出下列文件:

  • dump.txt 說明APK中所有類文件的內(nèi)部結(jié)構(gòu)
  • mapping.txt 提供原始與混淆過的類榔至、方法和字段名稱之間的轉(zhuǎn)換
  • seeds.txt 列出未進行混淆的類和成員
  • usage.txt 列出從APK移除的代碼

這些文件保存在 <module-name>/build/outputs/mapping/release/目錄下抵赢。
每新發(fā)布一個版本,都會產(chǎn)生新的 mapping.txt文件洛退,所以要保存好相應的 mapping.txt文件瓣俯,方便解碼混淆過的stack trace。

解碼混淆過的stack trace


使用位于 <sdk-root>/tools/proguard/目錄下的retrace腳本兵怯,將混效果的stack trace 和mapping.txt作為輸入彩匕,可以使輸出已解碼的stack trace.
例如:

retrace.bat -verbose mapping.txt obfuscated_trace.txt

proguard-android.txt 解讀


不使用大小寫混寫類名,默認情況下混淆的類名可以包含大小寫字符的混合媒区,以防止在大小寫不敏感的系統(tǒng)驼仪,比如windows上出現(xiàn)問題。

-dontusemixedcaseclassnames

不忽略公共類庫

-dontskipnonpubliclibraryclasses

關閉optimize和preverify選項袜漩,因為Android的dex并不像Java虛擬機需要optimize(優(yōu)化)和previrify(預檢)兩個步驟绪爸。

-dontoptimize
-dontpreverify

指定哪個屬性不要混淆,可一次指定多個屬性

-keepattributes [attribute_filter]

通常Exceptions, Signature, Deprecated, SourceFile, SourceDir, LineNumberTable, LocalVariableTable, LocalVariableTypeTable, Synthetic, EnclosingMethod, RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations, and AnnotationDefault屬性需要被保留宙攻,根據(jù)項目具體使用情況保留池充。

這里需要特別注意的一點是,gradle默認的keepattributes屬性不全韵吨,只保留了Annotation,Signature,InnerClasses,EnclosingMethod,為了混淆之后定位csh代碼方便逾礁,我們需要在proguard_rules.pro中手動添加拋出異常時保留代碼行號,并且重命名拋出異常時的文件名稱,這樣能方便定位問題:

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

重命名拋出異常時的文件名稱
-renamesourcefileattribute SourceFile

Keep配置


***-keep [,modifier, ...] class_specification ***
指定類和類成員(變量和方法)不被混淆

指定類名不被改變
-keep public class com.google.vending.licensing.ILicensingService

指定使用了Keep注解的類和類成員都不被改變
-keep @android.support.annotation.Keep class * {*;}

-keepclassmembers
指定類成員不被混淆,類名會被混淆
eg.keep setters in views 使得animations仍然能夠工作

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

***-keepclasseswithmembers ***
指定類和類成員都不被混淆
eg.包含native方法的類名和native方法都不能被混淆霹购,如果native方法未被調(diào)用,則被移除溢陪。由于native方法與對應so庫中的方法名稱對應萍虽,方法名被混淆會導致調(diào)用出現(xiàn)問題,所以native方法不能被混淆形真。

-keepclasseswithmembernames class * {
   native <methods>;
}

-keepnames
是 -keep,allowshrinking class_pecification 的簡寫杉编。指定一些類名受到保護,前提是他們在shrink這一階段沒有被去掉。也就是說沒有被入口節(jié)點直接或間接引用的類還是會被刪除邓馒。
-keepclassmembernames
與-keepclassmember相似嘶朱。保護指定的類成員,前提是這些成員在shrink階段沒有被刪除绒净。
-keepclasseswithmembernames
與-keepclasseswithmembers類似见咒。保護指定的類,如果它們沒有在shrink階段被刪除挂疆。
注意

If you specify a class, without class members, ProGuard only preserves the class and its parameterless constructor as entry points. It may still remove, optimize, or obfuscate its other class members.

以上六種keep配置類型,以names結(jié)尾的配置不保證被Keep的類或者成員不被刪除下翎,只有在obfuscation 這一階段有效缤言,如果不確定使用哪種,只需要使用不帶names結(jié)尾的Keep配置即可视事,因為不帶names的keep在shrink階段有效胆萧,可以保證被Keep的類或者屬性不被刪除。

通用Options:

-verbose 打印混淆詳細信息
-dontnote:指定不去輸出打印該類產(chǎn)生的錯誤或遺漏

-dontnote com.android.vending.licensing.ILicensingService

-dontnote android.support.**

-dontwarn:指定不去warn unresolved references和其他重要的problem

-dontwarn android.support.**

自定義混淆文件

Keep配置后面要如何寫類的信息俐东?
[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname
    [extends|implements [@annotationtype] classname]
[{
    [@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> |
                                                                      (fieldtype fieldname);
    [@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> |
                                                                                           <init>(argumenttype,...) |
                                                                                           classname(argumenttype,...) |
                                                                                           (returntype methodname(argumenttype,...));
    [@annotationtype] [[!]public|private|protected|static ... ] *;
    ...
}]

Filters

?    matches any single character in a name.(匹配一個字符)
*    matches any part of a name not containing the directory separator.(匹配一個名字跌穗,除了目錄分隔符外的任意部分)
**    matches any part of a name, possibly containing any number of directory separators.(匹配任意名,可能包含任意路徑分隔符)
!  exclude
<field>     匹配類中的所有字段
<method>    匹配類中所有的方法
<init>      匹配類中所有的構(gòu)造函數(shù)
-keep class com.lily.test.** 本包和所包含子包下的類名都保持
-keep class com.lily.test.* 保持該包下的類名
-keep class com.lily.test.** {*;} 保持包和子包的類名和里面的內(nèi)容均不被混淆
-keepclassmembers class **.R$* { 
    public static <fields>; 
} 

assumenosideeffects選項
指定一些方法被刪除也沒有影響(盡管這些方法可能有返回值),在optimize階段虏辫,如果確定這些方法的返回值沒有使用蚌吸,那么就會刪除這些方法的調(diào)用。proguard會自動的分析你的代碼砌庄,但不會分析處理類庫中的代碼羹唠。例如,可以指定System.currentTimeMillis(),這樣在optimize階段就會刪除所有的它的調(diào)用娄昆。還可以用它來刪除打印Log的調(diào)用佩微。這條配置選項只在optimizate階段有用。
注意:Only use this option if you know what you’re doing!
eg:

# 刪除代碼中Log相關的代碼
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}

下面是自定義混淆文件的一個范例,四大組件,native方法萌焰,反射用到的類哺眯,一些引入的第三方庫等,都不能進行混淆:

# 代碼混淆壓縮比,在0~7之間
-optimizationpasses 5# 混合時不使用大小寫混合扒俯,混合后的類名為小寫
-dontusemixedcaseclassnames

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

# 不做預校驗奶卓,preverify是proguard的四個步驟之一,Android不需要preverify陵珍,去掉這一步能夠加快混淆速度寝杖。
-dontpreverify

-verbose

#google推薦算法
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*

# 避免混淆Annotation、內(nèi)部類互纯、泛型瑟幕、匿名類
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod

# 重命名拋出異常時的文件名稱
-renamesourcefileattribute SourceFile

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

# 處理support包
-dontnote android.support.**
-dontwarn android.support.**

# 保留四大組件,自定義的Application等這些類不被混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-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.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService

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

# 保留枚舉類不被混淆
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# 保留Parcelable序列化類不被混淆
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}

#第三方jar包不被混淆
-keep class com.github.test.** {*;}

#保留自定義的Test類和類成員不被混淆
-keep class com.lily.Test {*;}
#保留自定義的xlog文件夾下面的類、類成員和方法不被混淆
-keep class com.test.xlog.** {
    <fields>;
    <methods>;
}

#assume no side effects:刪除android.util.Log輸出的日志
-assumenosideeffects class android.util.Log {
    public static *** v(...);
    public static *** d(...);
    public static *** i(...);
    public static *** w(...);
    public static *** e(...);
}

#保留Keep注解的類名和方法
-keep,allowobfuscation @interface android.support.annotation.Keep
-keep @android.support.annotation.Keep class *
-keepclassmembers class * {
    @android.support.annotation.Keep *;
}

下面的Proguard的思路可以參考:5分鐘搞定android混淆

主要將自定義Proguard分成幾個區(qū)域:

#--------------------------------定制化區(qū)域------------------------------
#---------------------------------1.實體類--------------------------------

#-------------------------------------------------------------------------

#---------------------------------2.第三方包-------------------------

#-------------------------------------------------------------------------

#---------------------------------3.與js互相調(diào)用的類----------------

#-------------------------------------------------------------------------

#---------------------------------4.反射相關的類和方法-----------------

#----------------------------5.基本不用動區(qū)域(可參考上文進行區(qū)分)-------------

4.資源文件的混淆

上面講述了如何進行代碼混淆只盹,再來講講如何對資源文件進行混淆辣往。對資源文件進行混淆操作本質(zhì)上是通過修改resources.arsc(參見文末鏈接詳見resources.arsc作用及文件格式)。現(xiàn)針對兩種資源混淆方案進行簡要說明殖卑。第一種是微信的資源混淆方案站削,第二種是美團的資源混淆方案,兩篇文章中都對原理進行了詳細的闡述孵稽。

5.混淆時常見的問題解決

TroubleShooting
Error:Uncaught translation error: com.android.dex.util.ExceptionWithContext: name already added: string{"a"}
參考:
Proguard官方文檔中的一些關于Android混淆的例子

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末许起,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子菩鲜,更是在濱河造成了極大的恐慌园细,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件接校,死亡現(xiàn)場離奇詭異猛频,居然都是意外死亡,警方通過查閱死者的電腦和手機蛛勉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門鹿寻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诽凌,你說我怎么就攤上這事毡熏。” “怎么了皿淋?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵招刹,是天一觀的道長。 經(jīng)常有香客問我窝趣,道長疯暑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任哑舒,我火速辦了婚禮妇拯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘洗鸵。我一直安慰自己越锈,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布膘滨。 她就那樣靜靜地躺著甘凭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪火邓。 梳的紋絲不亂的頭發(fā)上丹弱,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天德撬,我揣著相機與錄音,去河邊找鬼躲胳。 笑死蜓洪,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的坯苹。 我是一名探鬼主播隆檀,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼粹湃!你這毒婦竟也來了恐仑?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤再芋,失蹤者是張志新(化名)和其女友劉穎菊霜,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體济赎,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年记某,在試婚紗的時候發(fā)現(xiàn)自己被綠了司训。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡液南,死狀恐怖壳猜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情滑凉,我是刑警寧澤统扳,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站畅姊,受9級特大地震影響咒钟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜若未,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一朱嘴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粗合,春花似錦萍嬉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至供屉,卻和暖如春行冰,著一層夾襖步出監(jiān)牢的瞬間溺蕉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工资柔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留焙贷,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓贿堰,卻偏偏與公主長得像辙芍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子羹与,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容