Android 混淆详解
轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/zhaoyanjun6/article/details/69388246
本文出自【趙彥軍的博客】
文章目錄
- 混淆的基本概念
- 開啟混淆
- 混淆規(guī)則理解
- 1、有一些固定的混淆規(guī)則不需要更改:
- 2、理解通配符
- 3、保證指定包名下的所有類及子包中所有的類不被混淆
- 4、保證指定的類不被混淆
- 5、不混淆指定類的子類
- 6、指定接口不混淆
- 7、指定接口的實現(xiàn)類不混淆
- 8、指定類的內(nèi)部類不混淆
- 9、構(gòu)造函數(shù)不混淆
- 10、指定類的屬性和方法不被混淆
- 11、不混淆類中所有的 public 方法
- 12、不混淆類中所有的 public 字段
- 13、不混淆構(gòu)造函數(shù)
- 13、不混淆 bean 對象里面的 set 、get 方法
- 常見不混淆的類和屬性
- 混淆后的項目目錄資源
- 實戰(zhàn)1
- aar自動混淆
- 參考資料
混淆的基本概念
- 什么是混淆?
代碼混淆亦稱花指令,是將計算機(jī)程序的代碼,轉(zhuǎn)換成一種功能上等價,但是難于閱讀和理解的形式的行為。
- 混淆的目的
1、混淆的目的是為了加大反編譯的成本,但是并不能徹底防止反編譯.
2、壓縮apk 資源文件
開啟混淆
一般我們做項目的時候,都是分為 release 和 debug 版本,release 版本混淆,debug 版本不混淆。設(shè)置如下:
buildTypes {release {minifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}debug {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}混淆規(guī)則理解
1、有一些固定的混淆規(guī)則不需要更改:
#指定代碼的壓縮級別 -optimizationpasses 5#包明不混合大小寫 -dontusemixedcaseclassnames#不去忽略非公共的庫類 -dontskipnonpubliclibraryclasses#優(yōu)化 不優(yōu)化輸入的類文件 -dontoptimize#預(yù)校驗 -dontpreverify#混淆時是否記錄日志 -verbose# 混淆時所采用的算法 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*#保護(hù)注解 -keepattributes *Annotation*2、理解通配符
-keep class cn.hadcn.test.** -keep class cn.hadcn.test.*很多同學(xué)都搞不懂下面兩個的區(qū)別,一顆星表示只是保持該包下的類名,而子包下的類名還是會被混淆;兩顆星表示把本包和所含子包下的類名都保持;用以上方法保持類后,你會發(fā)現(xiàn)類名雖然未混淆,但里面的具體方法和變量命名還是變了,這時如果既想保持類名,又想保持里面的內(nèi)容不被混淆,我們就需要以下方法了:
-keep class cn.hadcn.test.* {*;}3、保證指定包名下的所有類及子包中所有的類不被混淆
- 例子1:com.lib.manager 包下面的類不混淆
- 例子2:v4包下面的所有類不混淆
4、保證指定的類不被混淆
- 例子1:YiBaWiFiActivity 類不被混淆, YiBaWiFiActivity 完整包名:com.yiba.wifi.sdk.lib.activity.YiBaWiFiActivity
- 例子2:v4 包下面的 ActivityCompat 類不被混淆
5、不混淆指定類的子類
-keep class * extends pp.lib.User { *; }User 類的子類不混淆
6、指定接口不混淆
Callback 是一個接口,完整包名為:com.lib.impl.Callback
-keep interface com.lib.impl.Callback{ * ; }7、指定接口的實現(xiàn)類不混淆
Callback 是一個接口,完整包名為:com.lib.impl.Callback . 所有的實現(xiàn)類都不會混淆。
-keep class * implements com.lib.impl.Callback { *; }8、指定類的內(nèi)部類不混淆
PhoneUtil 的源碼如下:
package com.lib.manager; import android.content.Context; import android.os.AsyncTask;/*** Created by yiba_zyj on 2017/4/5.*/public class PhoneUtil {public final static int APPID = 100 ;public PhoneUtil( Context context ){}class MyTask extends AsyncTask {@Overrideprotected Object doInBackground(Object[] params) {return null;}} }PhoneUtil 有一個常量、一個構(gòu)造函數(shù)、一個內(nèi)部類.
混淆規(guī)則:
-keep class com.lib.manager.PhoneUtil$* {* ; }混淆后的結(jié)果如下:
可以看到混淆后的 PhoneUtil ,常量和 構(gòu)造函數(shù) 都被移除了,只留下了 內(nèi)部類 MyTask 。
9、構(gòu)造函數(shù)不混淆
PhoneUtil 的源碼如所示:
package com.lib;/*** Created by yiba_zyj on 2017/4/7.*/public class PhoneUtil {public static final String aa = "1222" ;public void run(){}}混淆規(guī)則:PhoneUtil 的無參構(gòu)造函數(shù)不混淆 。PhoneUtil 里面沒有用到的 屬性和方法將會被移除。
-keep class com.lib.PhoneUtil {public <init>(); }那么有參的構(gòu)造函數(shù)的混淆規(guī)則是怎么樣的?
混淆規(guī)則:PhoneUtil 的 參數(shù)為 Context 的構(gòu)造函數(shù)不混淆
-keep class com.lib.PhoneUtil { public <init>( android.content.Context ); }10、指定類的屬性和方法不被混淆
- 原始類 PhoneUtil 代碼如下,完整的包名為:com.lib.manager.PhoneUtil
PhoneUtil 類中包含1個常量及2個方法.
- 類中的變量不被混淆:
- 混淆后的效果如下:
可以看到混淆后的類中,AppID 常量被保存下來了,getScreenWidth 、 getScreenHeight 這兩個方法被移除了。 為什么會有方法被移除? 因為在混淆時,Android 會默認(rèn)移除沒有使用的的方法和資源,保證apk 合理的被壓縮。
- 保留類中的方法不被混淆
- 混淆后的方法如下:
11、不混淆類中所有的 public 方法
# <methods> 匹配所有的方法-keep class cn.hadcn.test.One {public <methods>; }表示One類下的所有public方法都不會被混淆
12、不混淆類中所有的 public 字段
-keep class pp.lib.PhoneUtil{public <fields> ; }表示 PhoneUtil 類中所有的 public 屬性將保留,其他類型的字段
13、不混淆構(gòu)造函數(shù)
-keep class pp.lib.PhoneUtil{<init>(***) ;<init>(*** , *** ) ; }不混淆 PhoneUtil 類中所有 一個參數(shù) 和 兩個參數(shù)的 的構(gòu)造函數(shù)。
13、不混淆 bean 對象里面的 set 、get 方法
User 對象如下圖所示:
package pp.lib;/*** Created by ${zhaoyanjun} on 2017/4/10.*/public class User {private String name ;private String age ;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;} }混淆規(guī)則:不混淆 User 類中所有的 set 和 get 方法,*** 代表 通配符
-keep class pp.lib.User{void set*( *** ) ;*** get*() ; }常見不混淆的類和屬性
- 不混淆四大組件 和 Application
因為四大組件和 Application 需要在 AndroidManifest.xml 中注冊,不能混淆,否則就會報類找不到異常。
-keep public class * extends android.app.Activity -keep public class * extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider特殊情況:
如果 BroadcastReceiver 是動態(tài)注冊的,則是可以加入混淆的。
- 不混淆任何包含native方法的類的類名以及native方法名
- 不混淆任何一個View中的setXxx()和getXxx()方法,因為屬性動畫需要有相應(yīng)的setter和getter的方法實現(xiàn),混淆了就無法工作了。
- 不混淆Activity中參數(shù)是View的方法,因為有這樣一種用法,在XML中配置android:onClick=”buttonClick”屬性,當(dāng)用戶點(diǎn)擊該按鈕時就會調(diào)用Activity中的buttonClick(View view)方法,如果這個方法被混淆的話就找不到了。
- 不混淆枚舉中的values()和valueOf()方法
- 不混淆Parcelable實現(xiàn)類中的CREATOR字段,毫無疑問,CREATOR字段是絕對不能改變的,包括大小寫都不能變,不然整個Parcelable工作機(jī)制都會失敗。
- 不混淆R文件中的所有靜態(tài)字段,我們都知道R文件是通過字段來記錄每個資源的id的,字段名要是被混淆了,id也就找不著了。
混淆后的項目目錄資源
混淆完以后會在 build/outputs/mapping/release 目錄下生成4個文件。如下圖所示:
- mapping.txt :代表源碼的 包名/類名/變量名/方法名 和混淆后 的轉(zhuǎn)換關(guān)系。
混淆規(guī)則
-keep class com.lib.manager.PhoneUtil{public static int getScreenWidth(android.content.Context);}混淆后的 mapping 文件如下圖所示:
從上面的 mapping 文件可以看出:
PhoneUtil 轉(zhuǎn)換為 PhoneUtil , 名字沒有發(fā)生變化,相當(dāng)于沒有混淆。
getScreenWidth 轉(zhuǎn)換為 getScreenWidth , 名字沒有發(fā)生變換,相當(dāng)于沒有混淆。
getScreenHeight 轉(zhuǎn)換位 a , 名字已經(jīng)變換了,增加了反編譯的難度。
混淆后 PhontUtil 源碼如下圖所示:
- usage.txt : 代表本次混淆過程中被移除的類或者方法。
舉例說明
從上圖中可以看出,本次混淆過程中 , 移除了很多沒用的類。
com.lib.BuildConfig //移除了 com.lib 包下的 BuildConfig 類 com.lib.activity.A //移除了 com.lib.activity 包下的 A 類 com.lib.manager.PhoneUtil:public static final int AppID //移除了 com.lib.manager 包下的 PhoneUtil 類 中的 AppID 常量 com.lib.manager 包下的 PhoneUtil 類 中的 getScreenHeight 方法 。 com.lib.manager.SS //移除了 com.lib.manager 包下的 SS 類 com.lib.manager.Util //移除了 com.lib.manager 包下的 Util 類 com.lib.manager.hj.PP //移除了 com.lib.manager.hj 包下的 PP 類 com.lib.manager.hj.PP$1實戰(zhàn)1
User 類源碼:
package pp.lib;/*** Created by ${zhaoyanjun} on 2017/4/10.*/public class User {private String name ;private String age ;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;} }User1 類源碼:
package pp.lib;/*** Created by ${zhaoyanjun} on 2017/4/10.*/public class User1 extends User {private String id ;public void run(){}}混淆規(guī)則:
-keep class * extends pp.lib.User { *; }混淆結(jié)果: User 類的 類名已經(jīng)被混淆了,字段沒有混淆
User1 類,類名和字段都沒有混淆
aar自動混淆
解決第三方SDK的混淆配置管理問題.
盡管有些SDK提供方非??孔V的給了混淆規(guī)則, 但是畢竟要添加到自己項目的proguard配置里, 以后如果混淆規(guī)則改了, 配置還得同步改, 對于那些用maven倉庫管理的SDK, 非常不友好.
也許有人注意到了, Android歸檔文件, 也就是俗稱的aar文件里可以攜帶一個proguard.txt文件, 這似乎表明aar文件可以自帶混淆配置.
事實也是如此, aar在構(gòu)建時, 可以通過consumerProguardFiles屬性指定一個proguard配置, 這個配置會被打入aar, 它和proguardFiles屬性指定的proguard配置不同, proguardFiles是用于構(gòu)建aar的混淆規(guī)則, consumerProguardFiles則是aar的接入方在構(gòu)建時會使用的混淆規(guī)則.
第一步定義兩個 .pro 文件 proguard-rules.pro, consumer-rules.pro
proguard-rules.pro : 輸出aar 時需要的混淆規(guī)則,一般是 keep 暴露給接入方的類、方法、接口
consumer-rules.pro:接入方輸出 apk 時需要的混淆規(guī)則,一般是 keep 一些不能混淆的 bean 對象。
最后一步:在 aar 的 build.gradle 中添加 consumerProguardFiles 屬性
android {defaultConfig {...consumerProguardFiles 'consumer-rules.pro'}buildTypes {release {minifyEnabled trueproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}} }最后輸出的 aar 包會帶有 consumer-rules.pro 文件,最后會作用于構(gòu)建 apk 的混淆過程中。
參考資料
-
騰訊 Bugly Android 混淆那些事兒
-
常用的反編譯工具 http://git.oschina.net/zyj1609/AndroidReverseProject)
-
Android安全攻防戰(zhàn),反編譯與混淆技術(shù)完全解析(上)
-
Android安全攻防戰(zhàn),反編譯與混淆技術(shù)完全解析(下)
-
知乎:關(guān)于混淆的思考
總結(jié)
以上是生活随笔為你收集整理的Android 混淆详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android ToolBar的使用
- 下一篇: Android Studio 版本号详解