常見 Android 文件格式


庫文件

一系列代碼功能接口與資源數據的有機集合 android SDK等,三方庫有風險(供應鏈攻擊)

jar包:zip格式壓縮包,存放編譯后java的class文件集合,簽名信息保存在 META-INF 目錄。主要分析工具有jadx,jd-gui,JEB等。

aar包:可包含代碼,任何在開發中用到的資源數據,AS使用的全新庫文件格式。

  • AndroidManifest.xml:可用於定義 aar 包的名稱、編譯器版本等
  • classes.jar:包含 aar 庫文件中所有代碼生成后的 class 文件
  • res 目錄:存放所有資源
  • aidl 目錄(本文件不存在):存放 AIDL 接口文件
  • assets 目錄(本文件不存在):存放 Asset 資源
  • jni 目錄(本文件不存在):存放編譯好的不同版本的 so 庫
  • libs 目錄:存放 aar 包引用的第三方 jar 包

APK

Android Package:android系統軟件安裝包文件

apk文件結構:

就是zip文件格式,完整的apk文件包含:

  • AndroidManifest.xml:編譯好的 AXML 二進制格式的文件
  • META-INF 目錄:保存 APK 的簽名信息
  • classes.dex:程序的可執行代碼。如果開啟了 MultiDex,則會有多個 DEX 文件
  • res 目錄:程序中使用的資源信息。針對不同分辨率的設備,可使用不同的資源文件
  • resources.arsc:編譯好的二進制格式的資源信息
  • assets 目錄:程序使用 Asset 系統來存放 Raw 資源,所有的資源都將存放在這個目錄下

apk生成流程

Android Studio 時代整個打包過程被抽象成了將編譯模塊和依賴項打包成 DEX 文件、將 APK Packager 打包成 APK 和簽名兩個步驟,底層的操作細節被掩蓋

 

APK 的安裝流程

  • 通過系統程序安裝(開機時安裝):通過開機啟動的packagemanagerservice服務完成,在啟動時掃描 /system/app 並重新安裝所有程序
  • 通過android市場安裝:如果放到/system/app下,apk自動安裝;或者等待下載完成apk包,根據提示引導用戶安裝(Android 6.0 開始,軟件安裝完成后會彈出權限控制界面,允許用戶對軟件所使用的權限進行細粒度的管理)
  • 通過adb安裝:android sdk工具,通過連接PC進行安裝,命令 adb install apk路徑
  • 手機SD卡安裝:運行apk包,調用 Android 系統程序 packageinstaller.apk,提示引導用戶安裝。

分析 packageinstaller.apk 的實現步驟

通過 Android 手機的文件管理程序打開apk,彈出安裝界面,這個界面就是packageinstaller的packageinstallerActivity,請求安裝的時候就會啟動,並接收通過intent傳遞的apk信息

源碼位於android源碼的packages/apps/PackageInstaller目錄,activity啟動時,會初始化PackageManager對象與PackageParser.package對象,然后調用packageUtil.getpackageinfo()解析程序包內容,解析錯誤就失敗返回,成功就調用setContentView(),設置packageinstallerActivity顯示視圖,調用PackageUtil.getAppSnippet()和PackageUtil.initSnippetForNewApp()來設置packageinstallerActivity控件,顯示程序名、圖標,最后調用initiateInstall()方法進行初始化。

PackageInstallerActivity 的 onCreate() 方法如下:

  • 首先調用 parsePackageName() 從 AndroidManifest.xml 中獲取程序包名,接着構建了一個 Package 對象,然后逐一處理 AndroidManifest.xml 中的標簽。在處理 application 標簽時使用了 parseApplication(),該方法負責解析 activity、receiver、service、provider,並將它們添加到傳遞進來的 Package 對象的 ArrayList 中
  • 在 onCreate() 中,getPackageInfo() 返回后調用了 initiateInstall()、initiateInstall() 負責檢測該程序是否已安裝。若已安裝,就調用 startInstallConfirm() 顯示安裝界面;若沒安裝,則調用 showDialogInner(DLG_REPLACE_APP) 彈出替換程序對話框。
  • startInstallConfirm() 設置了安裝與取消按鈕的監視器。最后是 onClick() 的按鈕點擊響應,安裝按鈕使用 startActivity() 啟動一個 Activity 類 InstallAppProgress,該類在初始化 onCreate() 時調用 initView(),后者最終調用 PackageManager 類的installPackage() 來安裝 APK
  • installPackage() 是 PackageManager 類的一個虛函數,PackageManagerService.java 實現了它。installPackage() 調用了 installPackageWithVerification(),該方法先驗證調用者是否具有安裝程序的權限,然后通過消息處理的方式調用 processPendingInstall() 進行安裝。processPendingInstall() 調用了 installPackageLI()。installPackageLI() 經過一系列驗證,最終調用 replacePackageLI() 或 installNewPackageLI() 來替換或安裝程序。
  • 最終會調用 scanPackageLI(),實例化一個 PackageParser 對象,然后調用其 parsePackage() 來解析 APK 程序包。在代碼的最后調用了 scanPackageLI() 的另一版本,此版本的 scanPackageLI() 將完成 APK 的依賴庫檢測、簽名驗證、sharedUser 的簽名檢查、Native 庫目錄文件的更新、組件名稱的檢查等工作,並調用 mInstaller 的 install() 來安裝程序。
  • install() 執行完畢,會通過 socket 回傳結果,最終由 PackageInstaller 根據返回結果進行相應的處理, 到此一個APK 就安裝完成

classes.dex

其中包含 APK 的可執行代碼,是分析 Android 軟件時最常見的目標

DEX 文件結構

參考:https://blog.csdn.net/zlmm741/article/details/104706841

在 Android 源碼文件 dalvik/libdex/DexFile.h 中,有 DEX 文件可能用到的所有數據結構和常量定義。

AndroidManifest.xml

參考:https://blog.csdn.net/zlmm741/article/details/104717139

其中存放了 APK 的大量配置信息:軟件名稱、圖標、主題、包名、組件配置等

合理、安全地配置組件是安全開發中最重要的一課

AndroidManifest.xml 文件的格式

采用 XML 文本格式,在開發階段,所有的配置信息都可直接以可視化的方式編輯。

所有的配置都屬於 manifest 標簽,與程序配置相關的部分屬於 Android 標簽,程序中使用的四大組件也在這里聲明

  • App Manifest 的詳細信息可在 Android 的 API Guides(https://developer.android.google.cn/guide/topics/manifest/application-element )中找到
  • android:allowBackup 允許系統在進行備份操作時備份程序的應用數據,典型的操作是在終端執行 adb backup 命令,或點擊手機設置界面上的“備份操作”按鈕。對數據安全比較敏感的話,可設置為“false”

AXML 文件格式

  • AS 在編譯 APK 時,會將 AndroidManifest.xml 處理后打包進去。打包進去的 AndroidManifest.xml 被編譯成二進制格式的文件。解壓 APK 后,用文本編輯器打開它,會發現內容是亂碼。這個打包后的 AndroidManifest.xml 稱“AXML”,其格式稱“AXML 文件格式”
  • APK 用 AXML 而非純文本格式 XML 存放數據,主要目的應是解決 APK 加載時的性能問題。在 Android 設備內存資源與能耗極其有限的情況下,二進制的 AXML 在分析處理速度和內存占用方面都比純文本的 XML 有明顯優勢。但 AXML 的內容不能直接顯示,因此,逆向分析 APK 時,對其格式有所了解,才能知道它原來的內容
  • Android 官方沒明確給出 AXML 的二進制布局規范,可通過閱讀 APK 打包流程和系統加載 APK 的代碼掌握它的文件格式。在 Android 系統源碼文件 frameworks/base/include/androidfw/ResourceType.h 中列舉了 AXML 使用的大部分數據結構和常量定義
  • 學習 AXML 文件格式過程中,在了解數據結構的同時,可使用 010 Editor 輔助分析(官方模板比較粗糙,可下載鏈接: https://pan.baidu.com/s/1hr0sH5UWvzvz5wqUenARPw 提取碼: r9c4)
  • AXML 文件格式簡圖。在 AXML 中,數據塊用 chunk 表示。從整體結構看,一個 AXML 由文件頭 ResFileHeader、字符串池 ResStringPool、資源 ID 塊 ResIDs、XML 數據內容塊 ResXMLTree 四部分線性地組成

 

AXML 文件的修改

部分 APK 保護工具及一些廠商的加固方案利用 Android 系統解析 AXML 的漏洞,在編譯 APK 時構造畸形的 AXML,是系統能正常安裝 APK,但無法運行 ApkTool 等反編譯工具。這時就要對 AXML 進行修改。最直接的修改方式:配合使用 010 Editor 及 AXML 模板查看文件格式,找到異常部分進行修改

對一些已出現的 AXML 加固方案,可用現成的工具修改:

  • AmBinaryEditor:https://github.com/ele7enxxh/AmBinaryEditor
  • AndroidManifestFix:https://github.com/zylc369/AndroidManifestFix

resources.arsc

參考:https://blog.csdn.net/zlmm741/article/details/104724669

Android 軟件開發過程中有個重要的目錄,即 app 工程下的 java/res 目錄。這里存放了軟件使用的各種類型的資源文件。這些資源文件在編譯成 APK 時會被統一打包存放在 APK 的 res 目錄。其中,jpg、png 圖片文件按照原樣存放,layout、drawbale、color 目錄的 xml 配置文件會以 AXML 格式存放,所有的文件在打包時會以原來的文件名保存。連接程序代碼和資源的橋梁是 R.java,該文件由編譯器自動生成,里面保存的是不同類型的資源的 ID 值。這些 ID 值要以一種方式定位自己屬於哪個資源,而這正是 res/values/public.xml 要解決的問題。第二章中就是通過 res/values/public.xml 文件定位字符串“unsuccessed”所對應的 ID 值的

一個資源包含資源的名稱、類型、值及所在的 Package。resources.arsc 包含不同語言環境中 res 目錄下所有資源的類型、名稱與 ID 對應的信息

ARSC 文件格式

resources.arsc 文件的格式稱“ARSC 文件格式”,在 APK 中只有 resources.arsc 文件用這種格式

ARSC 使用的數據結構同樣位於 ResourceTypes.h 文件中。和 AXML 一樣,它表示數據塊使用 chunk。ARSC 中也引用了不同的 AXML 中的數據結構。一個 ARSC 從整體結構上看,由文件頭 ResTableHeader、資源項值字符串池 ResStringPool、Package 數據內容塊 ResTablePackage 三部分線性地組成

分析 ARSC 文件格式,同樣用 010 Editor。使用模板庫中的 AndroidResource.bt 模板。該模板同時支持 AXML 和 ARSC 文件的解析

下面是一幅 ARSC 文件格式簡圖:

 

ARSC 文件的修改

修改 ARSC 文件的目的:阻止或修正 APK 的反編譯(一些經過特殊處理的 ARSC,不僅在 APK 安裝時能正常被系統加載,且能阻止 ApkTool 等工具的反編譯);修改 ARSC 里 string 類別中的字符串或其他資源信息,通常用於軟件的漢化與修改

修改方式:

  • 使用 010 Editor 編輯特定的字段
  • 若修改的地方很多,建議使用 ApkTool 反編譯后修改 XML 文件

META-INF 目錄

參考:https://blog.csdn.net/zlmm741/article/details/104740233

APK 包中有個“META_INF”目錄,里面存儲了 APK 簽名有關的信息

通過unzip解壓apk可以在“META_INF”目錄獲取簽名信息

CERT.RSA

CERT.RSA 文件中存放了 APK 的開發者證書與簽名信息。通過該文件可試別開發者的身份,以及判斷 APK 是否被篡改。CERT.RSA 文件是由 DER 編碼的證書。由於在 DER 內部使用了 ASN1 進行編碼,使用任何 ASN1 解碼庫都能對其進行解碼,如 GNU 的 libtasn1 庫。這里使用 OpenSSL 提供的解碼功能來查看 CERT.RSA 的證書內容

  • OpenSSL 是一款跨平台的加解密庫管理套件
  • 安裝:
  • Windows:訪問 OpenSSL 官網下載完整的可執行文件包,或在 Cygwin 中搜索安裝
  • Ubuntu:執行 sudo apt-get install openssl 命令
  • macOS:執行 brew install openssl
  • 安裝完成后,執行openssl 命令可查看 CERT.RSA 中開發者證書的內容

 

  • 輸出的證書信息是 APK 合法和有效的憑證,在對 APK 進行保護時,其中的許多項都是用來鑒別 APK 是否已被修改的有力證據

MANIFEST.MF

  • MANIFEST.MF 是簽名的清單文件,是一個文本文件

  • 可看出,打包該文件的工具是 Android Gradle 3.5.3,下面的每組信息都包括 Name 和 SHA1-Digest,表示 APK 中每個文件的路徑和它的 SHA-1 散列值的 Base64 值
  • 以 res/layout/notification_action.xml 文件為例,執行下述命令,對比它的值:

  • “vUd0FW2wrq1c1vqQxRuZryvEKPg=” 與 MANIFEST.MF 文件中的值是一樣的。這證明了在 MANIFEST.MF 中存放的是 APK 中所有包含的文件列表的 SHA-1 散列值的 Base64 值,從而保證了在進行 APK 簽名驗證時 APK 中所有文件均未被修改

CERT.SF

  • CERT.SF 是簽名信息文件,也是一個文本文件

 

  • 比較發現CERT.SF比MANIFEST.MF多了一個SHA1-Digest-Manifest的值,這個值其實是MANIFEST.MF文件的SHA1再base64編碼的值,可以手動驗證,也可以從android源碼分析。

ODEX

參考:https://blog.csdn.net/zlmm741/article/details/104758848

  • 在 Android 5.0 前,主要使用的虛擬機是 Dalvik。當 APK 首次安裝,或系統升級、重新啟動時,為提高 DEX 的執行效率,Dalvik 會對 APK 中的 DEX 進行一定程度的優化。具體做法:解析 DEX 並生成一個 ODEX 文件,將其存放在 Android 設備的 /data/dalvikcache 目錄下。以后在運行這個程序時,就不會讀取 APK 中的 DEX,而是直接加載這個優化過的 ODEX,從而大大節省每次運行程序時在優化上花費的時間
  • 系統生成 ODEX 的方法是內部調用系統命令 dexopt。此命令不允許直接調用生成 ODEX,但 Android 在 Dalvik 時代的早期版本中,會在系統源碼的 build/tools/dexpreopt/dexopt-wrapper 目錄下提供 dexopt-wrapper 工具,可用於手動生成 ODEX

  • ODEX 比 DEX 多了如下內容:
  • DexOptHeader:ODEX 文件頭,描述 ODEX 的基本信息
  • Dependences:依賴庫列表,描述 ODEX 加載時可能使用的依賴庫
  • ClassLookups:優化數據塊的類索引列表信息,用於提高類搜索速度
  • RegisterMaps:優化數據塊的寄存器圖(Register Map)信息,主要用於幫助 Dalvik 虛擬機進行精確的垃圾回收(Garbage Collection)

ODEX 轉換成 DEX 

  • 經過優化的 ODEX 中包含與設備相關的依賴庫列表 Dependeces 結構信息,不同的 Android 設備的底層 bootClassPath 環境變量中存放的系統加載庫列表不盡相同,因此,將 ODEX 轉換成 DEX 的過程是設備相關的
  • 為將 ODEX 還原成 DEX,要先將 ODEX 反編譯成 smali 文件,再將 smali 文件編譯成 DEX。這個過程稱“deodex”
  • 反編譯和編譯 smali 文件使用的工具都是 smali。在使用 baksmali 命令反編譯 ODEX 時,要加入參數 -d,以指定與 ODEX 相關的設備的 framework 目錄。因為依賴庫都來源於 Android 設備的 /system/framework 目錄,所以第一步操作是將設備上的 framework 目錄拉(pull)到本地。

1.執行命令要有 Root 權限,命令如下:

adb pull /system/framework ~/Program/framework_19

2.執行如下命令完成反編譯:

baksmali -a 19 -x crackme0201.odex -d ./framework -o ./outdex

  • -a 參數指定 Android 設備的版本,“19”表示當前設備是 4.4 版本。-x 參數指定要操作的 ODEX。-d 參數指定剛 pull 下來的 framework 目錄。-o 參數指定輸出 smali 文件的目錄。完成上述操作后,若沒出現錯誤,會在 outdex 目錄生成一系列 smali 文件。這時只要執行 smali 命令即可生成 DEX

OAT

參考:https://blog.csdn.net/zlmm741/article/details/104774994

  • OAT 文件是在 Android 4.4 中引入的。OAT 是優化過的、用於 ART 虛擬機執行的 DEX 文件,類似 Dalvik 的 ODEX
  • 對新版本的 Android 程序進行安全研究,無論是對 OAT 文件的 Hook 還是加密保護,都要了解 OAT 文件格式

ART 虛擬機

ART 使用 AOT(Ahead-of-Time)編譯技術,在 APK 第一次安裝或系統升級、重啟時,通過調用 dex2oat 命令將 APK 中的 DEX 文件靜態編譯成 OAT 並存放到 Android 設備的 /data/dalvik-cache 或 /data/app/package 目錄。

dex2oat 與 dexopt 不同,dex2oat 更像一個編譯器,將 DEX 中的 Dalvik 字節碼編譯成 Native 機器碼。經過這樣的操作,以后啟動程序時,ART 就會提高 APK 的啟動速度,從而執行生成的 OAT 文件而不是 APK 中的 DEX 文件。

從 Android 5.0 開始,系統默認將 ART 作為虛擬機,程序運行速度明顯變快。但有個缺點,即 OAT 的靜態編譯操作會影響 APK 的安裝效率,導致 Android 4.4 后 APK 安裝速度更長。為了使運行和安裝速度都更快,Android 7.0 增加了 JIT(just-in-Time)編譯。新版本的 Android 使用的是基於 JIT on AOT 的編譯技術

生成 OAT 文件

系統在安裝 APK 時,會調用 dex2oat 自動生成 OAT 文件。也可手動執行 dex2oat 命令,為指定的 DEX 生成 OAT 文件

具體操作:在執行 dex2oat 命令時使用 --dex-file 參數指定傳入的 DEX 路徑(若有多個 DEX,可多次指定該參數),然后為 --oat-file 參數指定 OAT 的輸出路徑(在指定傳入的路徑時,該路徑要對當前的 adb shell 用戶有可寫權限)

OAT 文件格式

和 ODEX 一樣,OAT 也有自己的格式。Android 原生程序以 ELF 格式作為程序格式基礎,並在此基礎上增加了屬於 Android 的應用程序二進制接口(Application Binary Interface, ABI)

 

arm-linux-androideabi 中包含 ELF 文件格式在 Android 上的參數傳遞、浮點指針、棧展開、異常處理等規范信息。OAT 文件格式在設計之初就考慮到最終執行的程序是 ELF 格式的這一事實,為避免過多的文件格式設計帶來的麻煩,最終將 OAT 文件格式完全融入 Android 所特有的 ELF 格式

  • file 命令根據文件頭 magic 與布局格式,識別出這是一個 ARM Linux 動態鏈接的 ELF 文件。Android 特有的 ELF 文件格式之后會學到,這里只關注 OAT 在 ELF 上的特別部分

一個 OAT 文件必須包含 oatdata、oatexec、oatlastword 三個符號

  • oatdata 符號指向的地址是 OAT 所在 ELF 的 .rodata 段,這里存放的是 OAT 文件頭 OATHeader、OAT 的 DEX 文件頭 OATDexFile、原始的 DEX 文件 DexFile、OAT 的 DEX 類 OatClass 等信息
  • oatexec 符號指向的地址是 OAT 所在 ELF 的 .text 段,這里存放的是編譯生成的 Native 指令代碼
  • oatlastword 符號指向的地址是 OAT 文件結束處在 ELF 中的文件偏移,通過它可確定 OAT 文件的內容在哪里結束

將 OAT 文件轉換成 DEX 文件

  • 方法:OAT 文件中包含完整的 DEX 文件,定位 OAT 中的 DexFile 結構體,將其完整數據導出即可


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM