Android 應用防止被二次打包指南

codeGoogle發表於2018-11-23

前言

“Android APP二次打包”則是盜版正規Android APP,破解後植入惡意程式碼重新打包。不管從效能、使用者體驗、外觀它都跟正規APP一模一樣但是背後它確悄悄執行著可怕的程式,它會在不知不覺中浪費手機電量、流量,惡意扣費、偷窺隱私等等行為。

二次打包問題只是Android應用安全風險中的一部分, 一般是通過反編譯工具嚮應用中插入廣告程式碼與相關配置,再在第三方應用市場、論壇釋出。

對於打包黨對於移動App帶來的危害有以下幾種:

  • 1. 插入自己廣告或者刪除原來廣告;

  • 2. 惡意程式碼, 惡意扣費、木馬等;

  • 3. 修改原來支付邏輯。

上述惡意行為嚴重危害移動產品和使用者利益,同時也影響企業口碑。

關於移動App破解、資料洩露風險問題,以金融行業為例:眾所周知資料是金融類應用產品重要資源之一,關乎企業生存與發展、但移動應用經常被破解、資料被抓包,導致本地儲存資料以及使用者名稱、密碼等重要資訊洩露。下面舉例說明資料洩露案例。

金融類本地儲存資料洩漏

資料抓包,洩漏使用者名稱和密碼

面對二次打包不少公司都有自己的防範措施,知名公司的APP幾乎都是自己在程式內部做過處理防止其APP被二次打包,一旦打包後重新執行則程式自動退出。接下來,我就來詳解一下如何防止APP被二次打包。

如何解決移動App安全風險問題?

基於多年的移動安全經驗積累,網易雲易盾在移動APP安全風險問題上,從起始的開發階段、中間的測試階段再到結尾釋出階段,針對移動APP全生命週期進行安全防護。

開發階段——移動應用開發時接入安全元件,保護資料安全。其中,針對安全通訊方面,實現資料高強度加密,結合傳統的對稱、非對稱加密演算法和hash演算法,客戶端加密資料只有認證服務端才能解密,從而防止了資料洩漏、資料竊取和篡改。另外,為了實現加強資料的安全強度,安全元件結合自適應特徵演算法和隨機切換演算法,保證不同時間、不同終端的演算法和金鑰的差異性。

測試階段——對移動應用進行人工滲透測試,發現漏洞解決產品修復相關問題,包括:APP滲透測試和服務端滲透測試。

釋出階段——APP上架之前針對應用做安全加固,提高安全防護等級,上架之後做盜版監測。網易雲易盾可針對dex檔案進行加固防護,防止被靜態反編譯獲取程式碼邏輯;保護應用在被非法二次打包後不能正常執行;防止通過使用偵錯程式工具對應用進行非法破解;提供自研高穩定的裝置指紋,防止潛在的刷單風險;加密資原始檔,防止apk資原始檔被破解;對so檔案進行加固保護,防止native程式碼被逆向分析;對遊戲提供加固保護,讓遊戲免受破解、外掛等威脅。

要實現程式碼內部防止APP被二次打包首先得了解APK的機器識別原理,APK的唯一識別是依靠包名和簽名來做鑑定的,類似豌豆夾的洗白白、360手機衛士等安全軟體對APK的山寨識別,他們就是依賴包名來確定APK然後通過簽名來確定其是否山寨。所以說自己的程式內部在啟動的時候可以通過獲取APK本身的簽名然後和正確的簽名做對比來識別自己是否被二次打包。

通過PackageManag物件可以獲取APK自身的簽名。

過對簽名的碼的分解得到一串20左右的字串,此字串則是APK的簽名的MD5值,通過獲取的簽名MD5值與正確的MD5值進行對比,就可以識別其APK是否被盜版。

下圖是一些已做過保護的APP的程式碼塊分析:

上圖是“XX省電王“的防止二次打包的關鍵程式碼

上圖是”XX電池管家”的防止二次打包的關鍵程式碼

以上兩處都是smali層的程式碼,以上2處程式碼的截圖都是下載量非常高的APP所做的防止二次打包的處理,其處理的程式碼肯定會使用到的關鍵程式碼是

Landroid/content/pm/PackageInfo;->signatures:[Landroid/content/pm/Signature。

此方法能夠起到一定的安全作用,一般的打包黨面對它是無可奈何的,如果你瞭解一些smali語法它的作用就等於0了。

Android 應用加固的技術細節

此外,針對如何加固的技術細節問題,網易雲資深安全工程師鍾亞平在安卓巴士全球開發者論壇的活動中也做過《安卓App逆向與保護》的主題演講:

安卓App安全包含很多內容,包括混淆程式碼、整體Dex加固、拆分 Dex 加固、虛擬機器加固等方面。事實上,這些內容也是國內近幾年Android App安全保護的一種主要趨勢。

一、混淆程式碼

Java程式碼是非常容易反編譯的,作為一種跨平臺的、解釋型語言,Java 原始碼被編譯成中間“位元組碼”儲存於class檔案中。由於跨平臺的需要,這些位元組碼帶有許多的語義資訊,很容易被反編譯成Java原始碼。為了很好地保護Java原始碼,開發者往往會對編譯好的class檔案進行混淆處理。

混淆就是對釋出出去的程式進行重新組織和處理,使得處理後的程式碼與處理前程式碼完成相同的功能,而混淆後的程式碼很難被反編譯,即使反編譯成功也很難得出程式的真正語義。ProGuard就是一個混淆程式碼的開源專案,能夠對位元組碼進行混淆、縮減體積、優化等處理。

Proguard處理流程圖如下所示,包含壓縮、優化、混淆、預檢四個主要環節:

    1. 壓縮(Shrink):檢測並移除程式碼中無用的類、欄位、方法和特性(Attribute);
    1. 優化(Optimize):對位元組碼進行優化,移除無用的指令。優化程式碼,非入口節點類會加上private/static/final,沒有用到的引數會被刪除,一些方法可能會變成內聯程式碼;
    1. 混淆(Obfuscate):使用a、b、c、d這樣簡短而無意義的名稱,對類、欄位和方法進行重新命名;
    1. 預檢(Preveirfy):在Java平臺上對處理後的程式碼進行預檢,確保載入的class檔案是可執行的。

混淆程式碼逆向分析

如果想要反編譯混淆程式碼,鍾亞平分享了一個國外的工具DEGUADR,它能夠通過統計的方式來解混淆。雖然這個工具的正確率達不到100%,但是能在一定程度上幫助反編譯程式碼。

com.xxxxx.common.util.CryptoUtil網站也提供了一種反編譯服務,如下所示:

java.lang.String a(byte[]) -> encodeToString

java.lang.String a(byte[],boolean,java.lang.String) ->a

byte[] a(byte[],byte[]) -> encrypt

byte[] b(byte[]) -> getKey

byte[] b(byte[],byte[]) -> decrypt

byte[] d(java.lang.String) -> getKey

java.lang.String a(byte,char[]) -> a

java.lang.String a(java.io.File) -> getHash

java.lang.String a(java.lang.String) -> c

java.lang.String b(java.lang.String) -> encode
複製程式碼
二、整體Dex加固

為了加強Android保護強度,隨著安全技術的發展,又出現了新型的“加固技術”。Dex加固是對Dex檔案進行加殼防護,防止被靜態反編譯工具破解而洩露原始碼,最剛開始出現的是整體加固技術方案。

整體加固技術的原理如上所示,包括替換application/classes.dex、解密/動態載入原classes.dex、呼叫原application相關方法、將原application物件/名稱設定到系統內部相關變數四大環節。其中最為關鍵的一步就是解密/動態載入原classes.dex,通過加密編譯好的最終ex原始碼檔案,然後在一個新專案中用新專案的application啟動來解密原專案程式碼並載入到記憶體中,再把當前程式替換為解密後的程式碼,能夠很好地隱藏原始碼並防止直接性的反編譯。

整體Dex加固逆向分析

整體Dex加固逆向分析有兩種常用的方法。其一是在記憶體中暴力搜尋 dex\n035,再 dump。以下是在32位系統中的效果示例:

另一種方法就是通過Hook dvmDexFileOpenPartial(void* addr, int len, DvmDex**)。

三、拆分Dex加固

隨著業務規模發展到一定程度,不斷地加入新功能、新增新的類庫,程式碼在急劇膨脹的同時,相應的apk包的大小也急劇增加,那麼簡單的整體加固方案就不能很好地滿足安全需求,在整體加固方案之外又出現了拆分加固的技術方案。

但是如上所示,Dex檔案在加固時,針對中間缺失的一部分資料會以解密後的資料來替換,有的時候這種拆分替換也會導致資料不準確。那麼到底應該拆分什麼樣的資料呢?就需要了解一下Dex檔案的資料結構。

Dex檔案結構極為複雜,以下圖示選取了其中較為重要的內容。事實上,Dex檔案是一個以class為核心組裝起來的檔案,其中最重要的是classdata和classcode兩部分,有其特定的介面和指令資料,選取這兩部分來拆分的話,即使拆分出來也不會洩露class資料和位元組碼資料,反編譯出來也不完整,安全性較高。

拆分Dex加固逆向分析

對於Dex拆分加固的逆向分析,如下所示,可以用classdata替換從而組裝成新的Dex檔案,雖然和原來的Dex檔案不會完全一致,但也在一定程度上覆原了被拆分資料的樣子。

但要注意的是,這種方法僅適用於被拆分出去的資料變形一次性完成,也就是說,在有其他保護思路的情況下儘量避免使用,而且即使有需要也儘量選在用到這個類的時候才去恢復。

此外還有一個更底層一些的工具dexhunter,這個工具較為前衛,但同時也有一些侷限性,譬如部分指令資料會被優化,形成的程式碼介面不是很美觀等等。

四、虛擬機器加固

虛擬機器加固也屬於Dex拆分加固的一種,它是對位元組做了一些變化處理。如下所示,這是一個正常安卓系統中的程式碼,在其中進行了虛擬機器加固操作:

以add-int v0, v1, v2、sub-int v0, v1, v2、mul-int v0, v1, v2這三條指令進行替換,然後進行加固編譯,這樣子操作後,即使把替換後的資料恢復了,也不會以add-int v0, v1, v2、sub-int v0, v1, v2、mul-int v0, v1, v2這三條指令進行替換,然後進行加固編譯,這樣子操作後,即使把替換後的資料恢復了,也不會變形成為之前的位元組碼,安全係數較高。

虛擬機器加固逆向分析—HOOK JNI 介面

這種方式下的逆向分析,一方面可以通過HOOK JNI 介面來實現,它有兩種實現方式。

其一是類成員/靜態變數操作相關介面,比如:

  • GetStaticDoubleField SetStaticDoubleField GetDoubleField SetDoubleField …

(byte, object, int, long…)

其二是反射呼叫類方法,比如:

*   CallVoidMethodA CallBooleanMethodA CallShortMethodA CallObjectMethodA …

*   CallStaticVoidMethodA CallStaticBooleanMethodA CallStaticShortMethodA CallStaticObjectMethodA …

*   (byte, int, long,double …)

*   CallObjectMethodA(JNIEnv* env, jobject object, jmethoID method, …)
複製程式碼

通過HOOK JNI 介面實現虛擬機器加固逆向分析

通過HOOK JNI 介面不用逆向底層,就可以瞭解App大致的呼叫流程。但是對於複雜的呼叫過程,或者虛擬化方法數量較多的情況,這種逆向分析手段看起來會比較混亂;對於不需要返射到Java層執行的指令,如算術、邏輯運算等,則無法監控到。

虛擬機器加固逆向分析—分析指令操作碼對映

另一方面,也可以通過分析指令操作碼對映來逆向分析。在同一加固版本,或者對映關係相同的情況下,可以採取以下所示的方法:

但在實際情況中,每次加固時的對映關係都是隨機變化的,如下所示,這種情況下就無法直接建立對映關係。

不依賴於操作碼的對映關係只與虛擬機器結構有關,所以需要根據偏移關係建立對映關係,從而進行逆向分析。

安卓App逆向保護作為開發工作中的重要內容,一直是網易雲易盾致力提供的應用服務。後續,我們將在SO加密保護方面進行更加深入地研究,對SO裡面的邏輯進行分析,保護Native程式碼不被逆向分析

總結

Android應用做不到完全的防止二次打包。 1、簡單型

最簡單的就像簽名驗證。驗證編譯之後的dex簽名,md5等資料。

2、微有點技術含量型

由native在執行時釋放class到記憶體。釋放bytes資料直接餵給classloader。

3、難度型

對native本身再進行一些加固,加逆向坑(暗樁)增加破解難度。

4、較難度型

Java程式碼執行程式碼的時候呼叫native操作,實現破解必須破解native部分。

最後如果需要,部分必要演算法轉移到雲端執行,最大限度的去保證防被破解和二次打包

作者:yijian2595 連結:www.jianshu.com/p/b6c91d2fa…

閱讀更多

react-native技術的優劣

 一招教你讀懂JVM和Dalvik之間的區別

一招教你打造一個滑動置頂的視覺特效

NDK專案實戰—高仿360手機助手之解除安裝監聽

(Android)面試題級答案(精選版)

相信自己,沒有做不到的,只有想不到的

在這裡獲得的不僅僅是技術!

相關文章