天天看點

淺析 Android 打包流程

Apk 的組成結構

apk 檔案本質上其實是一個 zip 包。想要知道其中包含了什麼,改字尾直接解壓便可以看到了。 這裡我們解壓了某個未經過加強或者其他手段加密的 Android 安裝封包件,以下為結果截圖

淺析 Android 打包流程

圖中選中的這些檔案和檔案夾是一個 Android 應用基本都具備的。而其他的一些檔案和檔案夾則是一些第三方庫,或者是其他一些代碼生成的。 接下來,依次大概介紹一下這些檔案和檔案夾的作用

AndroidManifest.xml

這是 Android 應用的全局配置檔案,它包含了這個應用的很多配置資訊,例如包名、版本号、所需權限、注冊的服務等。可以根據這個檔案在相當程度上了解這個應用的一些資訊。該檔案目前狀态是被編譯為二進制的 XML 檔案,可以通過一些工具(如 apktool)反編譯後進行檢視

Android Studio的Analyze apk是一個很不錯的 apk 分析工具。我們可以通過它直接反編譯看到原始的 AndroidManifest.xml 檔案,如何使用可以點選以下文章

Android Studio逆向分析APK(Analyze APK)

淺析 Android 打包流程

assets 檔案夾

assets 檔案夾用于儲存需要保持原始檔案的資源檔案夾,開發過程中拖了什麼到裡面,打包完之後裡面還是什麼。一般用于存放音頻,網頁(幫助頁面之類的),字型等檔案。主要需要知道的點是,它與 res 檔案夾的區分以及如何在應用中通路該檔案夾的資源,如它可以有多級目錄而 res 則隻有兩級

淺析 Android 打包流程

dex 檔案

classes.dex 檔案是 Android 系統運作于 Dalvik Virtual Machine 上的可執行檔案,也是Android 應用程式的核心所在。項目工程中的 Java 源碼通過 javac 生成 class 檔案,再通過 dx 工具轉換為 classes.dex,注意到我們這裡有 classes2.dex 和 classes3.dex等。這是方法數超過一個 dex 的上限,分 dex 的結果。分 dex 在 Android 5.0 之前需要開發者自行完成,5.0 後 dx 自帶支援。dex 檔案的資料結構不算複雜

淺析 Android 打包流程

lib 檔案夾

該目錄存放着應用需要的 native 庫檔案。比如一些底層實作的圖檔處理、音視訊處理、資料加密的庫以 so 庫的形式在該檔案夾中。而該檔案夾下有時會多一個層級,這是根據不同CPU 型号而劃分的,如 ARM,ARM-v7a,x86等

淺析 Android 打包流程

META-INF 檔案夾

該目錄的主要作用是用于保證 APK 的完整性以及安全性。該檔案夾下,主要有三個檔案

淺析 Android 打包流程

先說 MANIFEST.MF,這個檔案儲存了整個 apk 檔案中所有檔案的檔案名 + SHA-1後的 base64 編碼值。這也就意味着,MANIFEST.MF 象征着 apk 包的完整性。再說 CERT.RSA,這個檔案儲存了公鑰和加密方式的資訊。最後說 CERT.SF,這個檔案與 MANIFEST.MF 的結構一樣,隻是其編碼會被被私鑰加密。這樣一來每次安裝時,通過該檔案夾中的檔案,就可以完成驗證的過程。如果 apk 包被改變了,而篡改者沒有私鑰生成 CERT.SF,則無法完成校驗

res 檔案夾

該檔案夾是資源檔案夾。它裡面存放的所有檔案都會被映射到 R 檔案中,生成對應的資源 ID,便于代碼中通過 ID 直接通路。其中的資源檔案包括了動畫(anim),圖像(drwable),布局(layout),常量值(values),顔色值(colors),尺寸值(dimens),字元串(strings),自定義樣式(styles)等

淺析 Android 打包流程

resource.arsc 檔案

它記錄了資源檔案,資源檔案位置(各個次元的路徑)和資源 id 的映射關系。并且将所有的 string 都存放在了 string pool 中,節省了在查找資源時,字元串處理的開銷

我們可以使用AS中的Analyze apk 來看看它到底包含了些什麼

淺析 Android 打包流程

我們可以看到,首先是有個 package 可選,實際上 resource.arsc 是可以包含多個 package 的資源的。 然後可以看到一個 Resource Types 的清單。這裡看到的是 drawable 的 type。 右邊顯示了有多少個 drawable 以及多少項 configurations,以及表的具體内容為 ID - Name - 各個次元的值(在這裡即是資源的路徑),通過這個,我們可以完成通過 id + 對應的 configuration 擷取對應資源的操作。

而後面要提到資源混淆的原理,就是修改這裡各個次元的值,并修改對應 res 裡面的檔案夾以及檔案名實作的

Android 打包流程

資源

Android 打包流程的第一步,是處理資源檔案

在這個步驟中,起主要作用的是 aapt

剛剛提及的 AndroidManifest.xml, res 檔案夾,resource.arsc 檔案的生成都與其有關,簡單來說,aapt 解析項目代碼中的 AndroidManifest.xml,收集項目中 res 檔案夾的資源檔案及 xml 檔案,對其做壓縮以及編譯的處理。在此過程中,配置設定了資源 id 并生成了 R.java 檔案 以及 arsc 檔案

代碼

上一步得到了 R.java 檔案後,将其與項目代碼一起編譯得到 .class檔案,然後打包為 jar 包。這個過程中,還會有混淆代碼這一步驟。之後,再通過 dx 工具,将生成的 jar 包與第三方庫的 jar 包一起編譯為 dex 檔案。這個過程中,如果是 5.0 以前的系統且超過了 65535 方法數的限制,需要人為的分 dex,5.0 以後則由 dx 工具包辦

到這一步,實際上 apk 所需要的主要内容已經大緻齊全了。隻需要把上面生成的 AndroidManifest.xml,classes.dex,res檔案夾,resource.arsc 打包進 apk,并且将項目工程中的 assets 以及 lib 目錄一并放入,就有了一個未經簽名的 Android 安裝包了

簽名

接下來還缺簡單但是卻關鍵的最後一步,那便是 apk 包的簽名,這一步在之前對 META-INF 的介紹中,實際已有提及。隻需要按步驟生成 MANIFEST.MF, CERT.RSA,CERT.SF 并放入META-INF 檔案夾即可

以上便是 Android 打包的基本流程,宏觀來看實際并不複雜,但是其中的一些步驟展開來講,卻是很有内容的,比如 appt 對資源處理的那個部分,R.java 是如何生成的,resource.arsc 又是如何生成的,Android 是怎樣完成對資源的擷取的等

業内有關技術小結

apk 加強

目前業内已經有很多 apk 加強的服務,這裡提及最常見的一種加強方案

一個比較典型的加強流程如下圖:

淺析 Android 打包流程

實際上是通過外包一層殼,将我們自己的 dex 文藏在加密 apk 的 dex 中。由于破壞了正常的 dex 結構,是以一般的反編譯工具,如 apktool 并不能直接反編譯 apk。但是如果了解了加密的方式以及方案,實際上要破解得到脫殼 dex 并不複雜,這隻是一定程度上提高了 hack 的成本,不過針對一般的 hack 依然很有效

快速多管道

由于國内有着數不勝數的 Android 應用市場,是以越來越多的管道包成為了每一個應用的必須

在之前,開發者一般通過 AndroidManifest.xml 中的meta 資訊來區分管道。在了解了打包流程後,大家應該明白,一旦改變了 AndroidManifest.xml 就意味着要重新打包

ts(多管道包的時間) = t (打每個包的耗時) * n (n為管道數量), n 在大到一定程度後,ts 就會變得非常的大。這顯然是讓開發者十分痛苦的

要解決這個問題,實際上需要攻破的是,META-INF 的完整性校驗機制

目前業界比較常用的兩種方案是:

  1. META-INF 下添加空檔案不會破壞簽名(檔案名為管道号,若 Google 更改簽名機制,有可能失效)
  2. apk 檔案末尾寫入資訊(本質是利用了 zip 檔案可以添加 comment 資料結構的特點)

這兩種改動方案都不會導緻 MANIFEST.MF 檔案的改變,也就不需要再次打包,隻需要簡單的讀改檔案即可。為廣大開發者節省了上線前漫長的等待管道包的時間

資源混淆

資源混淆通過混淆資源路徑名以及資源檔案名,比如:

res/drawable/icon -> r/s/a
           

這樣不但可以減少安裝包的體積,一定程度上也可以提高破解難度

該方案目前業内也有兩種實作,但是原理基本一緻。入手點皆為resource.arsc

第一種方案是修改我們剛剛提及的 aapt,使得在生成 resource.arsc 的過程中,就修改掉項目中資源的名稱,實作了資源的混淆

第二種方案則是在打出 apk 包之後,讀入已生成的 resource.arsc 檔案,進行混淆,改寫,同時修改掉 res 檔案夾下的資源名稱,完成混淆。最後再重新打包得到混淆資源後的新 apk 檔案

既可以減小體積,又可以一定程度增加被 hack 的難度,如果還有沒有用起來的同學,可以嘗試用起來了

熱更新檔

由于移動平台的特性,移動應用版本的更新率并不高。這種時候,如果應用也可以像網頁那般動态部署,無疑可以帶來非常大的優勢,比如線上修複 bug,小版本的更新,臨時特性的上線等

目前業界已經有比較多的技術方案了,在這裡我們提及兩種比較有代表性的方案。

  1. 通過 native hook 的方式,替換 class 中的方法實作完成熱更新檔。
  2. classloader 加載新 dex 覆寫原有 class 完成替換的方案。

    因為實際上每種方案都并非幾句話可以講解清楚,本文主要目的也隻是做個大概介紹,若讀者有興趣可以谷歌後進一步研究

實際上還有一些技術,比如使用 Anotation 自動生成代碼,buck exopackage 提高打包速度等。有興趣的話,可以自行查閱,相信可以學到不少知識

★,°:.☆( ̄▽ ̄)/$:.°★ 。

感謝大佬Dz

繼續閱讀