參考學習博客:
Android Dex文件格式(一):https://blog.csdn.net/p312011150/article/details/80501690
dex文件解析(第三篇) :https://blog.csdn.net/tabactivity/article/details/78950379
Android安全–Dex文件格式詳解:https://www.cnblogs.com/kexing/p/8890162.html
Dalvik和Art,JIT ,AOT, oat, dex, odex:https://www.colabug.com/4516410.html
官方文檔:https://source.android.com/devices/tech/dalvik/dex-format
一. Android Dex文件整體結構:
當java程序編譯成class後,還需要使用dx工具將所有的class文件整合到一個dex文件,目的是其中各個類能夠共享數據,在一定程度上降低了冗餘,同時也是文件結構更加經湊,實驗表明,dex文件是傳統jar文件大小的50%左右。
1.整體結構:
2.詳細描述:
名稱 | 格式 | 說明 |
---|---|---|
header | header_item | 標頭 |
string_ids | string_id_item[] | 字符串標識符列表。這些是此文件使用的所有字符串的標識符,用於內部命名(例如類型描述符)或用作代碼引用的常量對象。此列表必須使用 UTF-16 代碼點值按字符串內容進行排序(不採用語言區域敏感方式),且不得包含任何重複條目。 |
type_ids | type_id_item[] | 類型標識符列表。這些是此文件引用的所有類型(類、數組或原始類型)的標識符(無論文件中是否已定義)。此列表必須按 string_id 索引進行排序,且不得包含任何重複條目。 |
proto_ids | proto_id_item[] | 方法原型標識符列表。這些是此文件引用的所有原型的標識符。此列表必須按返回類型(按 type_id 索引排序)主要順序進行排序,然後按參數列表(按 type_id 索引排序的各個參數,採用字典排序方法)進行排序。該列表不得包含任何重複條目。 |
field_ids | field_id_item[] | 字段標識符列表。這些是此文件引用的所有字段的標識符(無論文件中是否已定義)。此列表必須進行排序,其中定義類型(按 type_id 索引排序)是主要順序,字段名稱(按 string_id 索引排序)是中間順序,而類型(按 type_id 索引排序)是次要順序。該列表不得包含任何重複條目。 |
method_ids | method_id_item[] | 方法標識符列表。這些是此文件引用的所有方法的標識符(無論文件中是否已定義)。此列表必須進行排序,其中定義類型(按 type_id 索引排序)是主要順序,方法名稱(按 string_id 索引排序)是中間順序,而方法原型(按 proto_id 索引排序)是次要順序。該列表不得包含任何重複條目。 |
class_defs | class_def_item[] | 類定義列表。這些類必須進行排序,以便所指定類的超類和已實現的接口比引用類更早出現在該列表中。此外,對於在該列表中多次出現的同名類,其定義是無效的。 |
call_site_ids | call_site_id_item[] | 調用站點標識符列表。這些是此文件引用的所有調用站點的標識符(無論文件中是否已定義)。此列表必須按 call_site_off 的升序進行排序。 |
method_handles | method_handle_item[] | 方法句柄列表。此文件引用的所有方法句柄的列表(無論文件中是否已定義)。此列表未進行排序,而且可能包含將在邏輯上對應於不同方法句柄實例的重複項。 |
data | ubyte[] | 數據區,包含上面所列表格的所有支持數據。不同的項有不同的對齊要求;如有必要,則在每個項之前插入填充字節,以實現所需的對齊效果。 |
link_data | ubyte[] | 靜態鏈接文件中使用的數據。本文檔尚未指定本區段中數據的格式。此區段在未鏈接文件中爲空,而運行時實現可能會在適當的情況下使用這些數據。 |
3. 採用010editor查看dex1文件:
具體可以參考https://source.android.com/devices/tech/dalvik/dex-format,查看每項的定義即情況
也可以查看dex相關的code進行結構的梳理:
https://android.googlesource.com/platform/dalvik/+/master/dx/src/com/android/dx/dex/file/DexFile.java
/art/libdexfile/
總之來說,dex是將apk中使用到的class文件信息集合在一起的文件,其中也包含了很多jar包中的類。
dex文件是對class文件中的各種函數表、變量表等進行優化過的,整體大小要小於class文件總和。
二. Android Odex,Oat,Vdex , art文件
2.1 odex文件概述( 5.0之前 )
全名Optimized DEX,即優化過的DEX。
Apk在安裝(installer)時,就會進行驗證和優化,目的是爲了校驗代碼合法性及優化代碼執行速度,驗證和優化後,會產生ODEX文件,運行Apk的時候,直接加載ODEX,避免重複驗證和優化,加快了Apk的響應時間。
注意:優化過程會根據不同設備上Dalvik虛擬機的版本、Framework庫的不同等因素而不同。在一臺設備上被優化過的ODEX文件,拷貝到另一臺設備上不一定能夠運行。
ODEX格式及生成過程:https://www.jianshu.com/p/242abfb7eb7f
整體結構盜圖如下:
2.2. oat文件(5.0及5.0之後)
參考博客:
Android運行時ART加載OAT文件的過程分析:https://blog.csdn.net/luoshengyang/article/details/39307813
oat格式(1):https://shaomi.github.io/2017/08/18/oat格式/
從Android運行時出發,打造我們的脫殼神器:
https://www.feiworks.com/wy/drops_html/從Android運行時出發,打造我們的脫殼神器.html
2.2.1 oat文件概述
oat 文件是 ART 運行的文件,是一種ELF格式的二進制可運行文件,包含 DEX 文件和編譯出的本地機器指令文件。因爲 oat 文件包含 DEX 文件,因此比 ODEX 文件佔用空間更大。
由於其在安裝時打包在裏面的classes.dex文件會被工具dex2oat翻譯成本地機器指令,最終得到一個ELF格式的OAT文件,ART 加載 OAT 文件後不需要經過處理就可以直接運行,它沒有了從字節碼裝換成機器碼的過程,因此運行速度更快。
查看三方應用如微信的安裝包如下:
怎麼還是odex,這個art文件是啥,這個vdex又是啥,==
官方回答:https://source.android.com/devices/tech/dalvik/configure
.vdex:其中包含 APK 的未壓縮 DEX 代碼,另外還有一些旨在加快驗證速度的元數據。
.odex:其中包含 APK 中已經過 AOT 編譯的方法代碼。
.art (optional):其中包含 APK 中列出的某些字符串和類的 ART 內部表示,用於加快應用啓動速度。
使用file命令查看這個base.odex
看到這個base.odex文件是ELF格式封裝的,所以這裏的odex其實就是oat文件,只是還是叫odex後綴。
查看系統自帶應用,比如system/priv-app/,system/app/中的apk,最終oat文件存放在/data/dalvik-cache/ 中:
這裏的dex文件也是oat文件,只是以dex爲後綴命名,/data/dalvik-cache/ 下也有以oat爲後綴命名的oat文件,通過如上的file命令就可以看出來。
2.2.2 readelf 查看oat文件結構
微信的安裝包中的oat文件由於是elf格式封裝,可以使用readelf命令查看文件信息如下:
chengang@mi:~$ readelf -a '/home/chengang/Documents/apk_dex_structure/com.tencent.mm-GzKZdK2dYHRkCVqs-p_ZVA==/oat/arm/base.odex'
ELF Header:
Magic: 7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: DYN (Shared object file)
Machine: ARM
Version: 0x1
Entry point address: 0x0
Start of program headers: 52 (bytes into file)
Start of section headers: 4261112 (bytes into file)
Flags: 0x5000000, Version5 EABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 40 (bytes)
Number of section headers: 11
Section header string table index: 10
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .rodata PROGBITS 00001000 001000 1c9000 00 A 0 0 4096
[ 2] .text PROGBITS 001ca000 1ca000 22f98c 00 AX 0 0 4096
[ 3] .bss NOBITS 003fa000 000000 0079b8 00 A 0 0 4096
[ 4] .dex NOBITS 00402000 000000 40dacf4 00 A 0 0 4096
[ 5] .dynstr STRTAB 044dd000 3fa000 00006d 00 A 0 0 4096
[ 6] .dynsym DYNSYM 044dd070 3fa070 0000a0 10 A 5 1 4
[ 7] .hash HASH 044dd110 3fa110 000034 04 A 6 0 4
[ 8] .dynamic DYNAMIC 044de000 3fb000 000038 08 A 5 0 4096
[ 9] .gnu_debugdata PROGBITS 00000000 3fc000 0144a4 00 0 0 4096
[10] .shstrtab STRTAB 00000000 4104a4 000051 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00000034 0x00000034 0x00100 0x00100 R 0x4
LOAD 0x000000 0x00000000 0x00000000 0x1ca000 0x1ca000 R 0x1000
LOAD 0x1ca000 0x001ca000 0x001ca000 0x22f98c 0x22f98c R E 0x1000
LOAD 0x000000 0x003fa000 0x003fa000 0x00000 0x079b8 RW 0x1000
LOAD 0x000000 0x00402000 0x00402000 0x00000 0x40dacf4 R 0x1000
LOAD 0x3fa000 0x044dd000 0x044dd000 0x00144 0x00144 R 0x1000
LOAD 0x3fb000 0x044de000 0x044de000 0x00038 0x00038 RW 0x1000
DYNAMIC 0x3fb000 0x044de000 0x044de000 0x00038 0x00038 RW 0x1000
Section to Segment mapping:
Segment Sections...
00
01 .rodata
02 .text
03 .bss
04 .dex
05 .dynstr .dynsym .hash
06 .dynamic
07 .dynamic
Dynamic section at offset 0x3fb000 contains 7 entries:
Tag Type Name/Value
0x00000004 (HASH) 0x44dd110
0x00000005 (STRTAB) 0x44dd000
0x00000006 (SYMTAB) 0x44dd070
0x0000000b (SYMENT) 16 (bytes)
0x0000000a (STRSZ) 109 (bytes)
0x0000000e (SONAME) Library soname: [base.odex]
0x00000000 (NULL) 0x0
There are no relocations in this file.
There are no unwind sections in this file.
Symbol table '.dynsym' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00001000 0x1c9000 OBJECT GLOBAL DEFAULT 1 oatdata
2: 001ca000 0 OBJECT GLOBAL DEFAULT 2 oatexec
3: 003f9988 4 OBJECT GLOBAL DEFAULT 2 oatlastword
4: 003fa000 8456 OBJECT GLOBAL DEFAULT 3 oatbss
5: 003fa000 8456 OBJECT GLOBAL DEFAULT 3 oatbssmethods
6: 003fc108 22704 OBJECT GLOBAL DEFAULT 3 oatbssroots
7: 004019b4 4 OBJECT GLOBAL DEFAULT 3 oatbsslastword
8: 00402000 0 OBJECT GLOBAL DEFAULT 4 oatdex
9: 044dccf0 4 OBJECT GLOBAL DEFAULT 4 oatdexlastword
Histogram for bucket list length (total of 1 buckets):
Length Number % of total Coverage
0 1 (100.0%)
No version information found in this file.
2.2.3 oat文件結構大致如圖:
如上圖應知:
1.oat文件中有完整的dex文件,oat data section 中對應着真正的oat文件,即 外層是elf 包含着 oat,oat 包含着dex
2.符號oatdata和oatlastword分別指定了oat文件在elf文件中的頭和尾的位置,符號oatexec指向可執行段的位置;
3.oat文件有自己的頭和格式,並且其內部包含了一個完整的dex文件。
4.oat其實就是一個Elf格式的二進制文件,跟Elf文件不同的是它內部多了oatdata、oatexec、oatlastword幾個符號。其中oatdata的起始位置相對文件頭固定爲0x1000字節,而我們通過oatdump反編譯的時候出來的地址是從0x1000開始的,所以這也是爲什麼我們在backtrace中計算地址的時候最後要減去0x1000,才能去dump裏面找對應的地址。
oat文件格式如圖所示,這裏0x1000是oatdata相對於文件頭的偏移,接着就是oatdata的大小,也就是oatdump中的executable_offset,這個值保存在Oat文件的OatHeader裏面。然後就是oatexec段,也就是機器碼。而進程運行過程中異常如果掛在oat文件中,那麼其pc一定是在oatexec段內。
2.3. art文件
看到上面有art文件,.art是一些類/filed/方法,app啓動直接map到內存,從odex中拆分出來的,art文件主要爲了加快應用的對“熱代碼”的加載與緩存
2.4. vdex文件
google在android8.0新增加了vdex文件,其中包含 APK 的未壓縮 DEX 代碼,另外還有一些旨在加快驗證速度的元數據。
VDEX 文件有助於提升軟件更新的性能和用戶體驗。VDEX 文件會存儲包含驗證程序依賴項且經過預驗證的 DEX 文件,以便 ART 在系統更新期間無需再次解壓和驗證 DEX 文件。無需執行任何操作,即可實現該功能。該功能默認處於啓用狀態。要停用該功能,請將 ART_ENABLE_VDEX 環境變量設爲 false。
定義結構:art/runtime/vdex_file.h
34// VDEX files contain extracted DEX files. The VdexFile class maps the file to
35// memory and provides tools for accessing its individual sections.
36//
37// File format:
38// VdexFile::VerifierDepsHeader fixed-length header
39// Dex file checksums
40//
41// Optionally:
42// VdexFile::DexSectionHeader fixed-length header
43//
44// quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0].
45// DEX[0] array of the input DEX files, the bytecode may have been quickened.
46// quicken_table_off[1]
47// DEX[1]
48// ...
49// DEX[D]
50//
51// VerifierDeps
52// uint8[D][] verification dependencies
53//
54// Optionally:
55// QuickeningInfo
56// uint8[D][] quickening data
57// uint32[D][] quickening data offset tables
三. 概括總結:
四.工具使用:
4.1.dx 和 dexdump工具:
在sdk目錄:~/Android/Sdk/build-tools/ 下有dex 打包工具:dx 解析dex的工具:dexdump;
源碼目錄下prebuilts下也有該工具;
手機中也有該工具: system/bin/dexdump
4.2. oatdump工具:
在手機中有該工具: /system/bin/oatdump,可以解析oat文件。
4.3.objdump工具
對於android開發是源碼下對應的arm-linux-androideabi-objdump而非電腦系統的objdump:
./prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-objdump -S ~/Documents/f3b/symbol/out/target/product/pyxis/symbols/vendor/lib/hw/camera.qcom.so | tee camera.qcom.asm
用來反編譯symbol文件爲彙編指令用於問題定位。
4.4. Vdex Extractor工具:
Vdex Extractor:從Vdex文件反編譯和提取Android Dex字節碼的工具:https://www.freebuf.com/sectool/185881.html