ByteScope

Android APK 檔案格式

Android 應用程式套件——裝著編譯後程式碼、資源與 manifest 的 ZIP 壓縮檔。

MIME 類型
application/vnd.android.package-archive
副檔名
.apk
分類
壓縮與封存

Magic number(檔案簽章)

0x00000000504B0304PK··'PK' — it is a ZIP

ZIP 內含 'AndroidManifest.xml' 項目(通常還有 'classes.dex')。

關於 Android APK 格式

APK 是一個內部配置有硬性要求的 ZIP,外加一個不尋常的東西:一個簽章區塊,卡在最後一個檔案的資料與 ZIP 中央目錄之間,塞在這個格式本來沒有宣告用途的空隙裡。正因如此,APK 能以「整個檔案」為單位驗證,而不是逐項驗證;也正因如此,在 APK 後面追加任何東西、或用普通 ZIP 工具重新打包,都會無聲地毀掉 v2 以後的簽章。內部的 AndroidManifest.xml 是以 Android 二進位 XML 而不是文字儲存的,資源由 resources.arsc 建索引,程式碼則住在一個或多個 classes.dex 裡。

檔頭結構,一個位元組一個位元組看

位移長度欄位意義
04Local file header signature50 4B 03 04——APK 逐位元組就是 ZIP,檔案前段沒有任何東西寫著 Android。每個儲存的項目都以這種標頭開頭,只有位移 0 那個位置是固定的。
42Version needed to extract最低讀取端版本乘以 10:直接儲存的項目是 10,deflate 是 20——APK 裡幾乎每個項目就這兩種,因為 Android 執行環境不接受一般 ZIP 允許的那些冷門壓縮方法。
62General purpose bit flag位元 11 宣告項目名稱是 UTF-8,現代建置工具都會設。位元 3 會把大小與 CRC 移到資料後面,APK 裡不得設定,因為執行環境要直接把項目映射進記憶體,必須事先知道大小。
82Compression method0 是直接儲存、8 是 deflate。resources.arsc 必須未壓縮儲存並對齊,執行環境才能把它記憶體映射;lib/ 底下的原生程式庫通常也是直接儲存,所以 APK 的 hex dump 裡會出現一長串看得懂的資料,換成普通 ZIP 那裡會是壓縮過的雜訊。
144CRC-32項目未壓縮內容的 CRC-32,位在位移 14,壓縮後與未壓縮大小接在位移 18 和 22。安裝程式解包時檢查的是這些值,和 APK 的簽章是兩件互不相干的事。
262File name length從位移 30 開始的項目名稱長度(位元組)。它和位移 28 的額外欄位長度一起決定這個項目的資料從哪開始——而 APK 的額外欄位常常是 zipalign 塞進去的填補,用來把下一個項目推到 4 位元組或 16 KiB 的邊界上。
30variableFirst entry name標頭裡唯一暗示「這跟 Android 有關」的地方。哪個名稱排第一取決於建置工具,所以把它當線索、不要當判準:真正認出 APK 的是中央目錄裡有 AndroidManifest.xml,通常還有 classes.dex。
variable16APK Signing Block magic16 個 ASCII 位元組 'APK Sig Block 42',就緊貼在 ZIP 中央目錄前面,前面還有一個 8 位元組的大小,重複記載這個區塊自己的長度。驗證端就是從中央目錄位移往回讀這個區塊,找出 v2、v3 與 v3.1 的簽章記錄。
-224End of central directory signature50 4B 05 06,沒有壓縮檔註解時位於倒數第 22 個位元組。每個讀取端都從這裡起步、往回走到中央目錄,然後才知道檔案裡有什麼——所以第一個項目完好的 APK 照樣可能裝不起來。
-64Offset of the central directory中央目錄從哪裡開始,從檔案開頭起算。它同時也是簽章區塊的錨點,所以任何工具只要重寫壓縮檔、改動這個值又沒有重新簽章,做出來的 APK 安裝程式就會拒收。

怎麼跟長得像的格式分開

容易搞混的怎麼分
ZIP檔案前段完全沒有位元組層級的差異——APK 就是 ZIP。認出它靠的是內容:壓縮檔根目錄的 AndroidManifest.xml、通常有的 classes.dex、多半有的 resources.arsc,以及裝著 v1 簽章檔案的 META-INF。這些項目都不在的話,不管副檔名寫什麼,它就是個普通 ZIP。
Java JAR兩者都是帶 META-INF 目錄的 ZIP,而且 APK 在歷史上本來就是 JAR。分界在其他東西:JAR 裝的是 .class 項目與 META-INF/MANIFEST.MF,APK 裝的是 .dex 項目與 AndroidManifest.xml,而且只有 APK 會在中央目錄前面帶著 'APK Sig Block 42' magic。
aab/apks/xapk/apkm全都是 ZIP,全都不是能安裝的 APK。bundletool 產出的 .apks 裡有 toc.pb 加上各個 split APK;.xapk 裡有 manifest.json 加 APK 與 OBB 資料;.apkm 帶的是 info.json。這些標記都放在壓縮檔根目錄,所以分辨它們看的是中央目錄裡最前面那幾個項目名稱——而裡面那些 APK 才是真正的套件。
AndroidManifest.xml (binary XML)值得知道,因為很多人把它解出來之後看到一堆亂碼。APK 裡的 manifest 不是文字:它是用 chunk 結構組成的二進位 XML,第一個 16 位元值是 chunk 型別 0x0003,後面接 16 位元的標頭大小,所以 hex dump 開頭是 03 00 08 00。要解它需要字串池與資源對應表,不是 XML 解析器。

常見的壞法

  • 用普通 ZIP 工具重新打包。把 APK 解開再壓回去,會做出一個結構上完美無缺的壓縮檔,只是簽章區塊被丟掉了,而且 resources.arsc 現在被壓縮過、也沒對齊。這個檔案用任何壓縮軟體開都沒問題,安裝程式就是不收——這是最常見的自傷型 APK 失敗。
  • 在檔案後面追加任何東西。有些散布用的包裝會把資料塞在中央目錄後面,用來夾帶通路或推薦來源 id。ZIP 容忍這件事,但 v2 簽章涵蓋的是「到中央目錄位移為止」的整個檔案,所以簽章檢查看到的已經不是當初被簽的那個檔案了。
  • 被截斷。和任何 ZIP 一樣,先陣亡的是 end of central directory 記錄,所以下載一半的 APK 每個本地標頭都完好,卻沒辦法列出內容。救援方式是往後掃 50 4B 03 04,而這樣重組出來的 APK 就算每個項目都救回來了,也一定過不了驗證。
  • 重新打包的工具改了項目名稱或順序。簽章涵蓋項目名稱,所以哪怕只重新命名一個檔案,或是讓工具把順序正規化、順手丟掉額外欄位裡的 zipalign 填補,v2 以後的簽章就失效了,而留下來的仍然是一個完全合法的 ZIP。

這個格式現在還在用嗎

Android 只裝 APK。App Bundle 改變的是發布方式——開發者上傳 .aab,Google Play 從中產生各機型的 split APK——但落到手機上的成品仍然是 APK,而側載、企業散布、F-Droid 與所有第三方商店處理的也直接就是 APK。所以周邊才會長出那一堆容器格式:發布用 .aab,把一組 split 併成單一下載的 .apks、.xapk、.apkm,它們全都是包著真正套件的 ZIP。就檢視而言這個格式的回報特別好:權限、元件、最低 SDK、簽章憑證、原生架構與完整類別清單,全都不必執行任何東西、直接從壓縮檔讀得出來,APK 分析工具做的就是這件事。

在這個站上打開 Android APK 檔

這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。

相關格式

容易和 Android APK 搞混的格式,可以比對它們的開頭位元組。

格式開頭位元組副檔名
ZIP50 4B 03 04.zip
Java JAR50 4B 03 04.jar
Android DEX64 65 78 0A.dex

常見問題

檔案的 magic number 是什麼?

檔案開頭的前幾個位元組,不管檔名寫什麼,它標明的是真正的格式。PNG 一定以 89 50 4E 47 開頭,ZIP 則是 50 4B 03 04。作業系統、瀏覽器和 file 指令讀的都是這幾個位元組,而不是相信副檔名 — 這就是為什麼把檔案改名並不會改變它實際上是什麼。

我要怎麼自己檢查 magic number?

在 macOS 或 Linux 上,xxd -l 16 檔名 會印出前 16 個位元組;在 Windows 上,PowerShell 的 Format-Hex -Path 檔名 -Count 16。或者把檔案丟進本站的檔案類型辨識工具,它會在本機讀取位元組並告訴你是什麼格式,不會上傳任何東西。

為什麼兩種格式的 magic number 會一樣?

因為其中一種是建立在另一種之上。docx、xlsx、pptx、apk、jar 與 epub 骨子裡都是 ZIP 壓縮檔,所以全都以 ZIP 的簽章開頭;要分辨它們得看裡面的項目。Java class 檔和 macOS 的通用二進位檔都以 CA FE BA BE 開頭,靠緊接其後的欄位區分。

有完全沒有 magic number 的檔案嗎?

有。XML、HTML、JSON、SVG、CSV 這類以文字為基礎的格式沒有固定的位元組簽章,所以改用結構來辨識:開頭的 <?xml 宣告、<svg 元素,或是 { 後面接著帶引號的鍵。這種偵測是合理推測而非確定,因此本參考頁會把這些格式標示為結構偵測。

magic number 相符就代表檔案安全嗎?

不代表。它告訴你的是格式,不是內容是否無害 — 一個貨真價實的 PDF 或 docx 仍然可能夾帶惡意內容。辨識真實類型是有用的第一道檢查,尤其在副檔名和位元組不一致的時候,但它不是惡意軟體掃描。

手上有檔案可能是 Android APK?用檔案類型辨識工具查出來 — 它在本機讀取 magic number,不上傳任何東西。或者瀏覽所有格式。