dex文件格式三


先來看看總體的結構,結構體定義在DexFile.h裏面
 
在dexFileSetupBasicPointers中設置各個子結構體,固然是在解析DexHeader以後
源碼在DexFile.c文件中
 
在解析每一個子結構體以前咱們先了解下leb128格式,
源碼leb128.c中解析這種格式
  LEB128 ( little endian base 128 ) 格式 ,是基於 1 個 Byte 的一種不定長度的編碼方式 。若第一個 Byte 的最高位爲 1 ,則表示還須要下一個 Byte 來描述 ,直至最後一個 Byte 的最高位爲 0 。每一個 Byte 的其他 Bit 用來表示數據, 這個數據類型的出現其實就是爲了解決一個問題,那就是減小內存的浪費,他就是表示int類型的數值,可是int類型四個字節有時候在使用的時候有點浪費,因此就應運而生了

一. string_ids數據結構
string_ids 區索引了 .dex 文件全部的字符串 

如何定位:
先在DexHeader中拿到偏移和數量
 
在文件偏移112(10進制)的地方有11713項string_ids也就是一個DexStringId數組
DexStringId只有一個結構體成員,他保存一個string_data_item的偏移值

這個數組看起來是像下面這個樣子的
 
 
DexStringId指向的是一個leb128的字符串(文件偏移)
在源碼中簡單的拿到偏移直接讀取leb128便可拿到字符串

010Editor腳本解析以下:
 
 
其餘一些子結構,如  type-ids , method_ids 也會引用到這些字符串


二. type_ids數據結構
這個數據結構中存放的數據主要是描述dex中全部的類型,好比類類型,基本類型等信息。type_ids 區索引了 dex 文件裏的全部數據類型 ,包括 class 類型 ,數組類型(array types)和基本類型(primitive types) 。 本區域裏的元素格式爲 type_ids_item , 
type_ids_item 裏面 descriptor_idx 的值的意思 ,是 string_ids 裏的 index 序號 ,是用來描述此type 的字符串
 
咱們來手工找一找
第一項保存的值爲695
 
咱們定位到字符串表第695項,成功找到B
 

源碼中調用dexStringByTypeIdx拿到指定type字符串
同JNI同樣
L 表示 class 的詳細描述 ,通常以分號表示 class 描述結束 ;
V 表示 void 返回類型 ,只有在返回值的時候有效 ;
[ 表示數組 ,[Ljava/lang/String; 能夠對應到 java 語言裏的 java.lang.String[] 類型 。

後面的其餘數據結構也會使用到type_ids類型,因此咱們這裏解析完type_ids也是須要用一個池子來存放的,後面直接用索引index來訪問便可

三. proto_ids數據結構
proto的意思是 method prototype 表明 java 語言裏的一個 method 的原型

其保存的是這樣一個結構體
shorty_idx :跟 type_ids 同樣 ,它的值是一個 string_ids 的 index 號 ,最終是一個簡短的字符串描述 ,用來講明該 method 原型 
return_type_idx :它的值是一個 type_ids 的 index 號 ,表示該 method 原型的返回值類型
parameters_off :後綴 off 是 offset , 指向 method 原型的參數列表 type_list ; 若 method 沒有參數 ,值爲0 。
 
參數列表的格式是 type_list ,結構從邏輯上以下描述 。
size 表示參數的個數 ;
type_idx 是對應參數的類型 ,它的值是一個 type_ids 的 index 號 ,跟 return_type_idx 是同一個品種的東西 

其描述Method原型算法以下:(DexProto.c)

4、field_ids數據結構
filed_ids 區裏面存放的是dex 文件引用的全部的 field 。本區的元素格式是 field_id_item
class_idx :表示本 field 所屬的 class 類型 , class_idx 的值是 type_ids 的一個 index , 而且必須指向一個class 類型 
type_idx :表示本 field 的類型 ,它的值也是 type_ids 的一個 index 
name_idx : 表示本 field 的名稱 ,它的值是 string_ids 的一個 index 
 
注意:這裏的字段都是索引值,必定要區分是哪一個池子的索引值,還有就是,這個數據結構咱們後面也要使用到,因此須要用一個池子來存儲

5、 method_ids數據結構
method_ids 是索引區的最後一個條目 ,它索引了 dex 文件裏的全部的 method.
 
method_ids 的元素格式是 method_id_item , 結構跟 fields_ids 很類似:
class_idx :表示本 method 所屬的 class 類型 , class_idx 的值是 type_ids 的一個 index , 而且必須指向一個 class 類型 
name_idx :表示本 method 的名稱 ,它的值是 string_ids 的一個 index 
proto_idx :描述該 method 的原型 ,指向 proto_ids 的一個 index 
注意:這裏的字段都是索引值,必定要區分是哪一個池子的索引值,還有就是,這個數據結構咱們後面也要使用到,因此須要用一個池子來存儲。

6、class_defs數據結構
一、class_def_item
從字面意思解釋 ,class_defs 區域裏存放着 class definitions , class 的定義 。它的結構較 dex 區都要複雜些 ,由於有些數據都直接指向了data 區裏面 。
class_defs 的數據格式爲 class_def_item 
(1) class_idx:描述具體的 class 類型 ,值是 type_ids 的一個 index 。值必須是一個 class 類型 ,不能是數組類型或者基本類型 。
(2) access_flags: 描述 class 的訪問類型 ,諸如 public , final , static 等 。在 dex-format.html 裏 「access_flagsDefinitions」 有具體的描述 。
(3) superclass_idx:描述 supperclass 的類型 ,值的形式跟 class_idx 同樣 。
(4) interfaces_off:值爲偏移地址 ,指向 class 的 interfaces , 被指向的數據結構爲 type_list 。class 若沒有interfaces ,值爲 0。
(5) source_file_idx:表示源代碼文件的信息 ,值是 string_ids 的一個 index 。若此項信息缺失 ,此項值賦值爲NO_INDEX=0xffff ffff 
(6) annotions_off:值是一個偏移地址 ,指向的內容是該 class 的註釋 ,位置在 data 區,格式爲annotations_direcotry_item 。若沒有此項內容 ,值爲 0 。
(7) class_data_off:值是一個偏移地址 ,指向的內容是該 class 的使用到的數據 ,位置在 data 區,格式爲class_data_item 。若沒有此項內容 ,值爲 0 。該結構裏有不少內容 ,詳細描述該 class 的 field ,method, method 裏的執行代碼等信息 ,後面有一個比較大的篇幅來說述 class_data_item 。
(8) static_value_off:值是一個偏移地址 ,指向 data 區裏的一個列表 ( list ) ,格式爲 encoded_array_item。若沒有此項內容 ,值爲 0 。

header 裏 class_defs_size = 0x01 , class_defs_off = 0x 0110 。則此段二進制描述爲 :


其實最初被編譯的源碼只有幾行 ,和 class_def_item 的表格對照下 ,一目瞭然 
 
source file : Hello.java
public class Hello
{
element value associated strinigs
class_idx 0x00 LHello;
access_flags 0x01 ACC_PUBLIC
superclass_idx 0x02 Ljava/lang/Object;
interface_off 0x00
source_file_idx 0x02 Hello.java
annotations_off 0x00
class_data_off 0x0234
static_value_off 0x00
public static void main(String[] argc)
{
System.out.println("Hello, Android!\n");
}
}

二、 class_def_item => class_data_item
class_data_off 指向 data 區裏的 class_data_item 結構 ,class_data_item 裏存放着本 class 使用到的各類數據
在DexClass.c中dexReadAndVerifyClassData來讀取DexClassData
相關結構體定義以下:
 
3.對於DexMethod有
(1) method_idx_diff:前綴 methd_idx 表示它的值是 method_ids 的一個 index ,後綴 _diff 表示它是於另一個 method_idx 的一個差值 ,就是相對於 encodeed_method [] 數組裏上一個元素的 method_idx 的差值 。其實 encoded_filed - > field_idx_diff 表示的也是相同的意思 ,只是編譯出來的 Hello.dex 文件裏沒有使用到class filed 因此沒有仔細講 ,詳細的參考 dex_format.html 的官網文檔 
(2) access_flags:訪問權限 , 好比 public、private、static、final 等 。
(3) code_off:一個指向 data 區的偏移地址 ,目標是本 method 的代碼實現 。被指向的結構是
code_item ,有近 10 項元素 ,後面再詳細解釋 
 

四、class_def_item => class_data_item => code_item
到這裏 ,邏輯的描述有點深刻了 。先理一下是怎麼走到這一步的 ,code_item 在 dex 裏處於一個什麼位置
遍歷過程以下:
 
(1) dex_header拿到class_def_item_list偏移,遍歷解析class_def_item
(2) 對於指定的class_def_item,每一項都有class_data_off,經過該offset定位到dex_class_data
(3) 解析dex_class_data其中在method_item中有一個code_off,便可定位到code_data在文件中的偏移


(1) registers_size:本段代碼使用到的寄存器數目。
(2) ins_size:method傳入參數的數目 。
(3) outs_size: 本段代碼調用其它method 時須要的參數個數 。
(4) tries_size: try_item 結構的個數 。
(5) debug_off:偏移地址 ,指向本段代碼的 debug 信息存放位置 ,是一個 debug_info_item 結構。
(6) insns_size:指令列表的大小 ,以 16-bit 爲單位 。 insns 是 instructions 的縮寫 (這裏就該對着dalvik去作指令解析)
(7) padding:值爲 0 ,用於對齊字節 。
(8) tries 和 handlers:用於處理 java 中的 exception , 常見的語法有 try catch 。


四、 分析 main method 的執行代碼並與 smali 反編譯的結果比較
在 8.2 節裏有 2 個 method , 由於 main 裏的執行代碼是本身寫的 ,分析它會熟悉不少 。偏移地址是
directive_method [1] -> code_off = 0x0148 ,二進制描述以下 :

insns 數組裏的 8 個二進制原始數據 , 對這些數據的解析 ,
須要對照官網的文檔 《Dalvik VM Instruction Format》和《Bytecode for Dalvik VM》。
分析思路整理以下
(1) 《Dalvik VM Instruction Format》 裏操做符 op 都是位於首個 16bit 數據的低 8 bit ,起始的是 op =0x62。
(2) 在 《Bytecode for Dalvik VM》 裏找到對應的 Syntax 和 format 。
syntax = sget_object
format = 0x21c 。
(3) 在《Dalvik VM Instruction Format》裏查找 21c , 得知 op = 0x62 的指令佔據 2 個 16 bit 數據 ,格式是 AA|op BBBB ,解釋爲 op vAA, type@BBBB 。所以這 8 組 16 bit 數據裏 ,前 2 個是一組 。對比數據得 AA=0x00, BBBB = 0x0000。
(4)返回《Bytecode for Dalvik VM》裏查閱對 sget_object 的解釋, AA 的值表示 Value Register ,即0 號寄存器; BBBB 表示 static field 的 index ,就是以前分析的field_ids 區裏 Index = 0 指向的那個東西 ,當時的 fields_ids 的分析結果以下 :

對 field 經常使用的表述是
包含 field 的類型 -> field 名稱 :field 類型 。
這次指向的就是 Ljava/lang/System; -> out:Ljava/io/printStream;
(5) 綜上 ,前 2 個 16 bit 數據 0x 0062 0000 , 解釋爲
sget_object v0, Ljava/lang/System; -> out:Ljava/io/printStream;
其他的 6 個 16 bit 數據分析思路跟這個同樣 ,依次整理以下 :
0x011a 0x0001: const-string v1, 「Hello, Android!」
0x206e 0x0002 0x0010:
invoke-virtual {v0, v1}, Ljava/io/PrintStream; -> println(Ljava/lang/String;)V
0x000e: return-void
(6) 最後再整理下 main method , 用容易理解的方式表示出來就是 。
ACC_PUBLIC ACC_STATIC LHello;->main([Ljava/lang/String;)V
{
sget_object v0, Ljava/lang/System; -> out:Ljava/io/printStream;
const-string v1,Hello, Android!
invoke-virtual {v0, v1}, Ljava/io/PrintStream; -> println(Ljava/lang/String;)V
return-void
}
看起來很像 smali 格式語言 ,不妨使用 smali 反編譯下 Hello.dex , 看看 smali 生成的代碼跟方纔推導出
來的有什麼差別 。
.method public static main([Ljava/lang/String;)V
.registers 3
.prologue
.line 5
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "Hello, Android!\n"
index 0
class_idx 0x04
type_idx 0x01
name_idx 0x0c
class string Ljava/lang/System;
type string Ljava/io/PrintStream;
name string out
invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 6
return-void
從內容上看 ,兩者形式上有些差別 ,但表述的是同一個 method 。這說明剛纔的分析走的路子是沒有跑偏
的 。另一個 method 是 <init> , 如果分析的話 ,思路和流程跟 main 同樣 。走到這裏,內心很踏實了

7、總結
到這裏咱們就解析完了dex文件的全部東東,講解的內容有點多,在這裏就來總結一下:
學習到的技術
一、咱們學習到了如何不是用任何的IDE工具,就能夠構造一個dex文件出來,主要藉助於java和dx命令。
同時,咱們也學會了一個能夠執行dex文件的命令:dalvikvm;不過這個命令須要root權限。
二、咱們瞭解到了Android中的DVM指令,如何翻譯指令代碼
三、學習了一個數據類型:uleb128,如何將uleb128類型和int類型進行轉化


咱們解析dex的目的是啥?
咱們開始的時候,並無介紹說解析dex幹啥?那麼如今能夠說,解析完dex以後咱們有不少事均可以作了。
一、咱們能夠檢測一個apk中是否包含了指定系統的api(固然這些api沒有被混淆),一樣也能夠檢測這個apk是否包含了廣告,之前咱們能夠經過解析AndroidManifest.xml文件中的service,activity,receiver,meta等信息來判斷,由於如今的廣告sdk都須要添加這些東西,若是咱們能夠解析dex的話,那麼咱們能夠獲得他的全部字符串內容,就是string_ids池,這樣就能夠判斷調用了哪些api。那麼就能夠判斷這個apk的一些行爲了,固然這裏還有一個問題,假如dex加密了咱們就蛋疼了。好吧,那就牽涉出第二件事了。
二、咱們在以前說過如何對apk進行加固,其實就是加密apk/dex文件內容,那麼這時候咱們必需要了解dex的文件結構信息,由於咱們先加密dex,而後在動態加載dex進行解密便可
三、咱們能夠更好的逆向工做,其實說到這裏,咱們看看apktool源碼也知道,他內部的反編譯原理就是這些,只是他會將指令翻譯成smail代碼,這個網上是有相對應的jar包api的,因此咱們知道了dex的數據結構,那麼原理確定就知道了,一樣還有一個dex2jar工具原理也是相似的


相關文章
相關標籤/搜索