Android 混淆解析

  • 本文已授權(quán)微信公眾號(hào):鴻洋【hongyangAndroid】獨(dú)家發(fā)布

2018年第一篇强衡,新年快樂端盆!

一闪幽、混淆的目的

一款發(fā)布到市場(chǎng)的軟件原則上都應(yīng)該做代碼混淆料皇,可能有人會(huì)說(shuō)誰(shuí)有功夫破解你的爛代碼谓松,這個(gè)嘛,開心就好......

通過(guò)代碼混淆可以將項(xiàng)目中的類践剂、方法鬼譬、變量等信息進(jìn)行重命名,變成一些無(wú)意義的簡(jiǎn)短名字逊脯,同時(shí)也可以移除未被使用的類优质、方法、變量等军洼。所以直觀的看巩螃,通過(guò)混淆可以提高程序的安全性,增加逆向工程的難度匕争,同時(shí)也有效縮減了apk的體積避乏。一起來(lái)get這個(gè)技能吧!

二甘桑、開啟混淆

在基于Android Studio項(xiàng)目的app modulebuild.gradle中有如下默認(rèn)代碼片段:

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

代表要發(fā)布的release包的混淆配置拍皮,默認(rèn)不開啟混淆,要開啟混淆首先做如下修改:

minifyEnabled true

開啟混淆后還可以添加shrinkResources true配置跑杭,代表開啟資源文件壓縮铆帽。
這樣就在release模式下開啟了混淆。一般在debug模式下不開啟混淆艘蹋,因?yàn)榛煜龝?huì)導(dǎo)致編譯時(shí)間變長(zhǎng)锄贼、無(wú)法debug問(wèn)題,畢竟也是內(nèi)部測(cè)試嘛女阀,沒必要宅荤!

開啟混淆后,接下來(lái)就是用混淆配置文件來(lái)設(shè)置混淆規(guī)則:

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
  • proguard-android.txt代表系統(tǒng)默認(rèn)的混淆規(guī)則配置文件浸策,該文件在<Android SDK目錄>/tools/proguard下冯键,一般不要更改該配置文件,因?yàn)橐矔?huì)作用于其它項(xiàng)目庸汗,除非你能確保所做的更改不影響其它項(xiàng)目的混淆惫确。
  • proguard-rules.pro代碼表當(dāng)前project的混淆配置文件,在app module下,可以通過(guò)修改該文件來(lái)添加適用當(dāng)前項(xiàng)目的混淆規(guī)則改化。

三掩蛤、編寫混淆配置文件

為了更好的編寫proguard-rules.pro,先學(xué)習(xí)下系統(tǒng)的proguard-android.txt

# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html

# 混淆時(shí)不使用大小寫混合類名
-dontusemixedcaseclassnames
# 不跳過(guò)library中的非public的類
-dontskipnonpubliclibraryclasses
# 打印混淆的詳細(xì)信息
-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).
# 關(guān)閉優(yōu)化(原因見上邊的原英文注釋)
-dontoptimize
# 不進(jìn)行預(yù)校驗(yàn)陈肛,可加快混淆速度
-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.

# 保留注解中的參數(shù)
-keepattributes *Annotation*
# 不混淆如下兩個(gè)谷歌服務(wù)類
-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
# 不混淆包含native方法的類的類名以及native方法名
-keepclasseswithmembernames class * {
    native <methods>;
}

# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
# 不混淆View中的setXxx()和getXxx()方法揍鸟,以保證屬性動(dòng)畫正常工作
-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
# 不混淆Activity中參數(shù)是View的方法,例如句旱,一個(gè)控件通過(guò)android:onClick="clickMethodName"綁定點(diǎn)擊事件阳藻,混淆后會(huì)導(dǎo)致點(diǎn)擊事件失效
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
# 不混淆枚舉類中的values()和valueOf()方法
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# 不混淆Parcelable實(shí)現(xiàn)類中的CREATOR字段,以保證Parcelable機(jī)制正常工作
-keepclassmembers class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator CREATOR;
}

# 不混淆R文件中的所有靜態(tài)字段谈撒,以保證正確找到每個(gè)資源的id
-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.
# 不對(duì)android.support包下的代碼警告(如果我們打包的版本低于support包下某些類的使用版本腥泥,會(huì)出現(xiàn)警告的問(wèn)題)
-dontwarn android.support.**

# Understand the @Keep support annotation.
# 不混淆Keep類
-keep class android.support.annotation.Keep

# 不混淆使用了注解的類及類成員
-keep @android.support.annotation.Keep class * {*;}

# 如果類中有使用了注解的方法,則不混淆類和類成員
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

# 如果類中有使用了注解的字段啃匿,則不混淆類和類成員
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

# 如果類中有使用了注解的構(gòu)造函數(shù)蛔外,則不混淆類和類成員
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

可以看出proguard-android.txt主要作用是防止指定內(nèi)容被混淆,其中使用了以-開頭溯乒,結(jié)合keep類關(guān)鍵字冒萄,*<>等通配符的語(yǔ)法橙数,先get這些語(yǔ)法吧尊流!

  • 首先看keep類關(guān)鍵字:
關(guān)鍵字 含義
keep 保留類和類成員,防止被混淆或移除
keepnames 保留類和類成員灯帮,防止被混淆崖技,但沒有被引用的類成員會(huì)被移除
keepclassmembers 只保留類成員,防止被混淆或移除
keepclassmembernames 只保留類成員钟哥,防止被混淆迎献,但沒有被引用的成員會(huì)被移除
keepclasseswithmembers 保留類和類成員,防止被混淆或移除腻贰,如果指定的類成員不存在還是會(huì)被混淆
keepclasseswithmembernames 保留類和類成員吁恍,防止被混淆,如果指定的類成員不存在還是會(huì)被混淆播演,沒有被引用的類成員會(huì)被移除
  • 相關(guān)通配符:
通配符 含義
* 匹配任意長(zhǎng)度字符冀瓦,但不含包名分隔符.。例如一個(gè)類的全包名路徑是com.othershe.test.Person写烤,使用com.othershe.test.*翼闽、com.othershe.test.*都是可以匹配的,但com.othershe.*就不能匹配
** 匹配任意長(zhǎng)度字符洲炊,并包含包名分隔符.感局。例如要匹配com.othershe.test.**包下的所有內(nèi)容
*** 匹配任意參數(shù)類型尼啡。例如*** getName(***)可匹配String getName(String)
... 匹配任意長(zhǎng)度的任意類型參數(shù)。例如void setName(...)可匹配void setName(String firstName, String secondName)
<fileds> 匹配類询微、接口中所有字段
<methods> 匹配類崖瞭、接口中所有方法
<init> 匹配類中所有構(gòu)造函數(shù)

到這里對(duì)混淆已經(jīng)有了基本的了解,系統(tǒng)的proguard-android.txt已經(jīng)為我們完成了大部分基礎(chǔ)的混淆配置工作撑毛,至于編寫當(dāng)前app module下的proguard-rules.pro读恃,只需要針對(duì)當(dāng)前項(xiàng)目添加一些特有的配置,避免某些重要的東西被混淆掉導(dǎo)致錯(cuò)誤代态,我們主要考慮以下幾點(diǎn):

  • AndroidManifest.xml中注冊(cè)的繼承四大組件的子類的類名以及重寫的方法名都不會(huì)被混淆。
    如果希望項(xiàng)目中android.support.v4.app.Fragment子類的類名和重寫父類的方法名不被混淆可以添加如下配置:
# 不混淆Fragment的子類類名以及onCreate()疹吃、onCreateView()方法名
-keep public class * extends android.support.v4.app.Fragment {
    public void onCreate(android.os.Bundle);
    public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle);
}
  • 不混淆某個(gè)特定的類和類中所有成員
-keep class com.othershe.test.utils.CommonUtil { *; }
  • 不混淆某個(gè)目錄下的文件蹦疑,例如使用Gson時(shí),數(shù)據(jù)bean不能被混淆萨驶,需要如下配置:
# com.othershe.test.model代表數(shù)據(jù)bean所在的全包名目錄
-keep class com.othershe.test.model.** { *; }
  • 上一條的具體原因是因?yàn)?code>Gson用到了反射歉摧。如果我們自己使用了反射,例如
Field field = service.getField("BASE_URL");

BASE_URL是service所屬類的一個(gè)字段名腔呜,則該字段不能被混淆叁温。

  • 保留泛型
-keepattributes Signature
  • 保留用于調(diào)試堆棧跟蹤的行號(hào)信息(為了后期調(diào)試方便,建議配置)
-keepattributes SourceFile,LineNumberTable

如果使用了上一行配置核畴,還需要添加如下配置將源文件重命名為SourceFile膝但,以便通過(guò)鼠標(biāo)點(diǎn)擊直達(dá)源文件:

-renamesourcefileattribute SourceFile
  • WebView中使用了JS調(diào)用,需要添加如下配置:
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
   public *;
}
  • 項(xiàng)目中使用的第三方library混淆規(guī)則谤草,列舉了幾個(gè)常用的:
# okhttp
-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

# Retrofit
-dontwarn okio.**
-dontwarn javax.annotation.**
-dontnote retrofit2.Platform
-dontwarn retrofit2.Platform$Java8
-keepattributes Signature
-keepattributes Exceptions

# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
    long producerIndex;
    long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode consumerNode;
}

# Gson
-keep class com.google.gson.stream.** { *; }
-keepattributes EnclosingMethod
# xxx代表model類的全包名路徑
-keep class xxx.** { *; }

# butterknie
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
    @butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
    @butterknife.* <methods>;
}

# eventbus
-keepattributes *Annotation*
-keepclassmembers class ** {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}

以上這些可以按需添加到proguard-rules.pro跟束!

四、查看混淆結(jié)果

混淆后打包丑孩,會(huì)在app module/build/outputs/mapping/release目錄下生成如下文件(動(dòng)不動(dòng)就幾萬(wàn)行冀宴,是在沒法看):

  • dump.txt:描述apk文件中所有類的內(nèi)部結(jié)構(gòu)
  • mapping.txt:混淆前后的類、類成員温学、方法的對(duì)照關(guān)系(重要略贮,追溯Crash堆棧信息要用到)
  • resources.txt:資源文件的壓縮信息
  • seeds.txt:未被混淆的類和成員
  • usage.txt:被移除的代碼

混淆后的apk包,需要系統(tǒng)的測(cè)試仗岖,防止混淆導(dǎo)致的潛在bug逃延。

我們還是有必要看一下混淆后的代碼結(jié)構(gòu),驗(yàn)證混淆是否成功轧拄。一個(gè)簡(jiǎn)單的辦法真友,Android StudioBuild菜單下有一個(gè)Analyze APK選項(xiàng),只需要先選擇要分析的apk包紧帕,在之后的界面點(diǎn)擊classes.dex即可看到混淆后的代碼結(jié)構(gòu):

Analyze APK

但是這樣只能看到一個(gè)類的成員變量和方法的結(jié)構(gòu)盔然,如果要看一個(gè)類的具體內(nèi)容桅打,就需要反編譯apk包了,具體可參考Android apk反編譯及重新打包流程愈案,希望一切順利挺尾!

五、 追溯Crash堆棧信息

代碼混淆后站绪,也會(huì)導(dǎo)致Crash堆棧信息被混淆遭铺,難以閱讀,增加定位問(wèn)題位置的難度恢准,一個(gè)混淆后的Crash堆棧信息類似這樣魂挂,核心的信息都沒了:


Crash Info

為了解決這個(gè)問(wèn)題,可以使用<SDK目錄>\tools\proguard\bin下的proguardgui.bat腳本將Crash堆棧信息還原到混淆前的狀態(tài)馁筐。步驟如下:

  1. 雙擊打開腳本涂召,選擇左邊的ReTrace選項(xiàng)
  2. 選擇Mapping file文件,也就是混淆后打包后在app module/build/outputs/mapping/release下生成的mapping.txt
  3. 拷貝混淆后的堆棧信息
  4. 點(diǎn)擊右下角的ReTrace!按鈕敏沉,完成Crash堆棧信息的追溯

如下圖中間部分就是追溯到的原Crash堆棧信息:

捕獲_看圖王.png

代碼混淆常用的內(nèi)容就這些了果正,重點(diǎn)還是要理解混淆的相關(guān)語(yǔ)法,靈活運(yùn)用盟迟!混淆一定程度增加逆向工程的難度秋泳,但還是能被破解的,如果你的代碼有價(jià)值攒菠,也難免被有心的人利用迫皱!除了混淆之外,一些廠商也提供了apk加固的服務(wù)來(lái)保證軟件的安全性辖众,但也是可以被脫殼的舍杜!有興趣的可自行了解!下次再見......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赵辕,一起剝皮案震驚了整個(gè)濱河市既绩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌还惠,老刑警劉巖饲握,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蚕键,居然都是意外死亡救欧,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門锣光,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)笆怠,“玉大人,你說(shuō)我怎么就攤上這事誊爹〉潘ⅲ” “怎么了瓢捉?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)办成。 經(jīng)常有香客問(wèn)我泡态,道長(zhǎng),這世上最難降的妖魔是什么迂卢? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任某弦,我火速辦了婚禮,結(jié)果婚禮上而克,老公的妹妹穿的比我還像新娘靶壮。我一直安慰自己,他們只是感情好员萍,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布腾降。 她就那樣靜靜地躺著,像睡著了一般充活。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜡娶,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天混卵,我揣著相機(jī)與錄音,去河邊找鬼窖张。 笑死幕随,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宿接。 我是一名探鬼主播赘淮,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼睦霎!你這毒婦竟也來(lái)了梢卸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤副女,失蹤者是張志新(化名)和其女友劉穎蛤高,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碑幅,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戴陡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沟涨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恤批。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖裹赴,靈堂內(nèi)的尸體忽然破棺而出喜庞,到底是詐尸還是另有隱情诀浪,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布赋荆,位于F島的核電站笋妥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏窄潭。R本人自食惡果不足惜春宣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嫉你。 院中可真熱鬧月帝,春花似錦、人聲如沸幽污。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)距误。三九已至簸搞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間准潭,已是汗流浹背趁俊。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留刑然,地道東北人寺擂。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像泼掠,于是被迫代替她去往敵國(guó)和親怔软。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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