代碼混淆
一般情況下,Android 的 gradle 中都會默認寫着:
1 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
默認 Proguard 文件:官方自帶的混淆規則文件路徑:proguard-android.txt。
這個默認文件中幫我們聲明了許多混淆規則內容,包括:keep 所有繼承自 View 的類,keep 所有繼承自 Activity 的類,keep 所有 JavascriptInterface、native 方法聲明,以及 keep 一些注解了 @Keep 的內容。
所以默認情況下,即使一條規則都沒有加入,自定義 View 和 Activity 都被保留下來,至少類名都沒有被混淆。
那么為什么官方默認會幫我們寫下這些?為什么 View 和 Activity 默認情況下應該被保留呢?
簡單來說,因為 Proguard 原本是為 Java 打造的,它無法搜索到我們 AndroidManifest、布局等文件中引用了哪些 Java 類,因此如果 Java 代碼變了而 XML 文件中的引用沒變,就會造成反射失敗。所以這些被 XML 使用到的類需要 keep 住。
(1)對於View 和 Activity混淆問題:
餓了么 的團隊提供了一個鮮為人知的 gradle 插件 用來無傷混淆 Activity 和 View,這個項目叫 Mess:https://github.com/eleme/Mess
具體內容各位可以稍后自行去閱讀其文檔和教程,鏈接最后都還會附於末尾。簡單來說,Mess 彌補了 Proguard 不能檢索 XML 文件的缺點,幫 Proguard 完成了 Activity 和 View 的改名及 mapping。
(2)資源文件的混淆問題:
Proguard 完全不會管我們的資源文件,因此如果資源文件名沒有做保護的話,很容易被順藤摸瓜找到關聯的 Java 代碼,對此,微信團隊提供了一個好用的資源混淆工具,它不僅能幫你全面混淆資源文件,還能幫你縮減資源文件的整體體積,工具叫 AndResGuard,開源地址:https://github.com/shwenzhang/AndResGuard
開啟混淆:
1 release { 2 debuggable false 3 //對齊 4 zipAlignEnabled true 5 //移除無用的resource 文件 6 shrinkResources true 7 // 混淆 8 minifyEnabled true 9 signingConfig signingConfigs.release 10 proguardFile getDefaultProguardFile('proguard-android.txt') 11 proguardFile 'proguard-rules.pro' 12 }
(3)Proguard rules
-repackageclasses
-repackageclasses + 包名 這種做法存在混淆 bug,而默認 -repackageclasses 不加包名不會出現 bug,所以初次使用此法需要進行測試。
-obfuscationdictionary
-obfuscationdictionary 后面加一個純文本文件路徑,它的作用是指定一個字典文件作為混淆字典。默認情況下我們的代碼命名會被混淆成 abcdefg... 字母組合的內容,需要修改可以使用這個配置項將字典修改成更加可怕的內容,亂花漸欲迷人眼的效果。
-assumenosideeffects
在 release 混淆過程中刪除 Log 代碼,使用 -assumenosideeffects 這個配置項可以幫我們在編譯成 APK 之前把日志代碼全部刪掉,這么做有助於提升性能,而且日志代碼往往會保留很多我們的意圖和許多可被反編譯的字符串:
1 -assumenosideeffects class android.util.Log { 2 public static boolean isLoggable(java.lang.String, int); 3 public static int d(...); 4 public static int w(...); 5 public static int v(...); 6 public static int i(...); 7 }
注:assumenosideeffects 需要啟用代碼優化才能生效
Proguard File:
1 # 代碼混淆壓縮比,在0~7之間,默認為5,一般不下需要修改 2 -optimizationpasses 5 3 4 # 混淆時不使用大小寫混合,混淆后的類名為小寫 5 # windows下的同學還是加入這個選項吧(windows大小寫不敏感) 6 -dontusemixedcaseclassnames 7 8 # 指定不去忽略非公共的庫的類 9 # 默認跳過,有些情況下編寫的代碼與類庫中的類在同一個包下,並且持有包中內容的引用,此時就需要加入此條聲明 10 -dontskipnonpubliclibraryclasses 11 12 # 指定不去忽略非公共的庫的類的成員 13 -dontskipnonpubliclibraryclassmembers 14 15 # 不做預檢驗,preverify是proguard的四個步驟之一 16 # Android不需要preverify,去掉這一步可以加快混淆速度 17 -dontpreverify 18 19 # 有了verbose這句話,混淆后就會生成映射文件 20 # 包含有類名->混淆后類名的映射關系 21 # 然后使用printmapping指定映射文件的名稱 22 -verbose 23 -printmapping priguardMapping.txt 24 25 # 指定混淆時采用的算法,后面的參數是一個過濾器 26 # 這個過濾器是谷歌推薦的算法,一般不改變 27 -optimizations !code/simplification/artithmetic,!field/*,!class/merging/* 28 29 # 保護代碼中的Annotation不被混淆 30 # 這在JSON實體映射時非常重要,比如fastJson 31 -keepattributes *Annotation* 32 33 # 避免混淆泛型 34 # 這在JSON實體映射時非常重要,比如fastJson 35 -keepattributes Signature 36 37 # 拋出異常時保留代碼行號 38 -keepattributes SourceFile,LineNumberTable
要Keep的:
1 # 保留所有的本地native方法不被混淆 2 -keepclasseswithmembernames class * { 3 native <methods>; 4 } 5 6 # 保留了繼承自Activity、Application這些類的子類 7 # 因為這些子類有可能被外部調用 8 # 比如第一行就保證了所有Activity的子類不要被混淆 9 -keep public class * extends android.app.Activity 10 -keep public class * extends android.app.Application 11 -keep public class * extends android.app.Service 12 -keep public class * extends android.content.BroadcastReceiver 13 -keep public class * extends android.content.ContentProvider 14 -keep public class * extends android.app.backup.BackupAgentHelper 15 -keep public class * extends android.preference.Preference 16 -keep public class * extends android.view.View 17 -keep public class com.android.vending.licensing.ILicensingService 18 19 # 如果有引用android-support-v4.jar包,可以添加下面這行 20 -keep public class com.null.test.ui.fragment.** {*;} 21 22 # 保留Activity中的方法參數是view的方法, 23 # 從而我們在layout里面編寫onClick就不會影響 24 -keepclassmembers class * extends android.app.Activity { 25 public void * (android.view.View); 26 } 27 28 # 枚舉類不能被混淆 29 -keepclassmembers enum * { 30 public static **[] values(); 31 public static ** valueOf(java.lang.String); 32 } 33 34 # 保留自定義控件(繼承自View)不能被混淆 35 -keep public class * extends android.view.View { 36 public <init>(android.content.Context); 37 public <init>(android.content.Context, android.util.AttributeSet); 38 public <init>(android.content.Context, android.util.AttributeSet, int); 39 public void set*(***); 40 *** get* (); 41 } 42 43 # 保留Parcelable序列化的類不能被混淆 44 -keep class * implements android.os.Parcelable{ 45 public static final android.os.Parcelable$Creator *; 46 } 47 48 # 保留Serializable 序列化的類不被混淆 49 -keepclassmembers class * implements java.io.Serializable { 50 static final long serialVersionUID; 51 private static final java.io.ObjectStreamField[] serialPersistentFields; 52 !static !transient <fields>; 53 private void writeObject(java.io.ObjectOutputStream); 54 private void readObject(java.io.ObjectInputStream); 55 java.lang.Object writeReplace(); 56 java.lang.Object readResolve(); 57 } 58 59 # 對R文件下的所有類及其方法,都不能被混淆 60 -keepclassmembers class **.R$* { 61 *; 62 } 63 64 # 對於帶有回調函數onXXEvent的,不能混淆 65 -keepclassmembers class * { 66 void *(**On*Event); 67 }
針對App的量身定制
1.保留實體類和成員不被混淆
對於實體,要保留它們的get和set方法,對於boolean型get方法有的命名是isXXX類型,不要遺漏
1 -keep class com.null.test.entities.** { 2 //全部忽略 3 *; 4 } 5 -keep class com.null.test.entities.** { 6 //忽略get和set方法 7 public void set*(***); 8 public *** get*(); 9 public *** is*(); 10 } 11 //以上兩種任意一種都行
2.內嵌類
內嵌類經常容易被混淆,結果調用的時候為空就崩潰了。最好的辦法就是不用內嵌類(有點扯淡),如果MainActivity中使用了,就用如下代碼
1 -keep class com.null.test.MainActivity$* { 2 *; 3 }
注:$這個符號就是用來分割內嵌類與其母體的標志
3.對於自定義類庫的混淆處理
對於自定義的類庫,我們一般是保留類和類的成員。
4.對WebView的處理
如果項目中用到了WebView的復雜操作,請加入以下代碼:
1 -keepclassmembers class * extends android.webkit.WebViewClient { 2 public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap); 3 public boolean *(android.webkit.WebView, java.lang.String); 4 } 5 -keepclassmembers class * extends android.webkit.WebViewClient { 6 public void *(android.webkit.WebView, java.lang.String); 7 }
5.對JavaScript的處理
1 -keepclassmembers class com.null.test.MainActivity$JSInterfacel { 2 <methods>; 3 }
Smali:
其實我們主要關注的是 smali 這個目錄,里面是按照 android 程序編寫的時候 java 文件的目錄格式生成的,但是里面的文件並不是 java 格式的,而是 smali 格式的,類似 MainActivity.smali。
那么什么是 smali 文件呢?
-
Smali 是 Android 的 Dalvik 虛擬機所使用的一種 dex 格式的中間語言
-
可以理解為,C 語言和匯編語言的編譯與反編譯,把 smali 理解為一種匯編語言
我們可以打開一個 smali 文件看看,我們可以使用 notepad++ 打開,然后定一下 smali 語法的高亮顯示
可以參看 http://www.ourunix.org/post/1... 操作。
打開 MainActivity.smali 文件,頭三行代碼大致如下:
1 .class public Lcom/example/hacktest/MainActivity; 2 .super Landroid/app/Activity; 3 .source "MainActivity.java"
smali 語法這里就不介紹了,自己查資料就好 smali 文件語法參考 http://my.oschina.net/xiahuaw
google 最早給的就是代碼混淆的方案,其實一般的混淆只是降低了代碼的可讀性,讓你對反編譯出來的函數命名等不知道什么意思,不過解讀出來只是時間問題。后來還有資源混淆的,但是意義不大。
后來有了核心代碼用 C 實現,寫成 SO,加花指令的辦法,這個辦法確實會阻止一大部分人的繼續破解,但是對於經常做逆向的工程師來說也不是什么難題。
其實做這么多大多數軟件的初衷就是不想軟件被盜版,然后被注入亂七八糟的廣告,或者被盜取信息等,后來就有了盜版檢測機制。比如:JAVA 層的簽名校驗,NDK 層校驗,分段存放簽名 Hash 串,服務器校驗等等,但是這些方法我都在上面說了破解方法
現在國內的一般應用市場都有對 APP 簽名的檢測,在你下載的時候會告訴你這個 APP 是不是盜版的,從而讓用戶區分出來。但是應用市場自己本身又被盜版了怎么辦呢?
再后來,就有了加固等產品,so 做了加密,真正的 dex 也藏起來了,不過個人覺得,就算真正的 dex 也需要變成 odex 了,root 的手機取到 odex,再轉回 dex,就能拿到真正的 dex(雖然我沒試過,但是我覺得可能是一個思路),所以這個方法就更難破解了
雖然加固產品很厲害,但是也會有他的缺陷,Android 系統不斷的更新升級,也許就換了某些模式等等,比如 ART 剛出來的時候,加固保加固后的 Apk,在 ART 模式運行下就會 Crash。這些加固產品要不斷的適配各種型號的手機,CPU 類型,運行模式等等,所以很多 APP 為了考慮兼容性,他們也不會輕易去加固自己的產品。
應用加固:
1. 阿里聚安全 鏈接:http://jaq.alibaba.com/
- 上傳應用
- 提供安全掃描(漏洞掃描、惡意代碼掃描、仿冒應用掃描)
- 可以從結果知道漏洞總數,如果是認證過的開發者,可以直接得知漏洞的具體位置。其中還有部分漏洞需要付費掃描。
- 然后我們可以進行應用加固,其中分快速加固和多渠道加固,可以按需選擇
- 加固包下載(應用需要重新簽名)
2. 騰訊雲應用樂固 鏈接:https://www.qcloud.com/product/cr
- 上傳應用
- 默認服務類型:
- 應用加固
- 漏洞檢測
- 渠道監控
- 可選服務類型:
- 適配分析(每天可以有一次)
- 質量跟蹤(即接入騰訊的bugly進行異常追蹤)
- 之后我們可以得到應用的基本信息、加固包、缺陷分析、應用檢測
- 選擇下載加固包(應用需要重新簽名)的同時還提供了簽名及多渠道打包工具、自動加固工具的下載使用
3. 360加固保 鏈接:http://jiagu.360.cn/
- 上傳應用
- 提供加固基礎服務:
- DEX文件加密
- 防二次打包
- APK大小優化
- 防DEX內存截取
- 應用盜版檢測
- 加固數據分析服務
- 可選增強服務:
- 崩潰日志分析(即接入360的bug追蹤)
- 支持x86框架(約增大apk大小400k左右)
- 應用升級通知(一鍵接入增量更新,約增大apk大小20k左右,詳情地址:http://jiagu.360.cn/qcmshtml/details.html#update)
- 選擇下載加固包(應用需要重新簽名)的同時還提供了簽名工具、加固工具(鏈接:http://jiagu.360.cn/qcmshtml/details.html#helper)的下載使用
4. 梆梆加固 鏈接:https://dev.bangcle.com/
- 上傳應用
- 提供功能:安全評估(更多定制化的評估,需聯系客戶並且收費)、應用加固(認證過的開發者還提供:報告下載,多渠道打包)
- 評估結果提供了風險的詳情,位置及解決方案
- 提供對加固后的包再進行快速評估
- 選擇下載加固包(應用需要重新簽名)的同時還提供了加固工具的下載使用
5. 愛加密 鏈接:http://safe.ijiami.cn/
-
上傳應用並直接執行加密
需要注意其中有一個選項:防止二次打包,請按需選擇方式
1:加密后的apk不允許任何人修改方式
2: 加密后的apk只允許應用所有和修改
-
可以對加固后的包申請渠道檢測,除了加固包的下載,還提供了簽名工具的下載
-
使用智游愛加密服務的開發者APP,並已成功提交到Google 官方應用市場並上架的,可得到智游官方網站應用的下載推薦
總結:
體積(體積小的為優):360 > 騰訊 > 愛加密 > 阿里 > 梆梆
兼容性: 阿里 > 騰訊 > 360 = 梆梆 > 愛加密
啟動速度(時間短為優): 阿里 > 愛加密 > 360 = 梆梆 > 騰訊
漏洞: 騰訊 > 愛加密 > 360 > 梆梆 > 阿里
app加固的好處是進一步保護了自己的核心代碼,提升了盜版的難度,但同時也影響的應用的兼容性,程序的執行效率,還有部分的市場會拒絕加殼后的應用上架。所以請大家結合自身的情況來選擇。
引用:
逆向:
Android反編譯工具的使用-Android Killer
混淆保護
Android 混淆那些事兒(Bugly)
微信Android資源混淆工具(Github ReadMe)
