015 Android之可执行文件dex
文章目錄
- 從一個hello world開始
- Dex文件結構
- 文件頭
- 各種數據的數組
- 字符串表
- 類型表
- 原型表
- 字段表
- 方法表
- 類數據
- 手工解析Smali代碼
從一個hello world開始
smali代碼和dex之間有著千絲萬縷的聯系,先從一個最簡單的dex文件hello world開始,學習整個dex文件的結構
.class public LHelloWorld; .super Ljava/lang/Object;.method public static main([Ljava/lang/String;)V.registers 2sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;const-string v1, "Hello World!"invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)Vreturn-void .end method以上面這段smali代碼為例
D:\Android\tools>java -jar smali.jar -o classes.dex HelloWorld.smali然后將smali代碼轉成dex文件,并打成壓縮包,命名為HelloWorld.zip
確保adb和模擬器連接成功
再將打包好的zip文件上傳到模擬器
接著執行dex文件,可以看到成功打印了Hello World
Dex文件結構
Dex文件主要可以分為三大塊:
文件頭
首先來看文件頭的部分
總共占0x70個字節大小,比較重要的字段有四個
除了這四個字段以外,文件頭部還有其他一些字段
各種表的大小及偏移
各種數據的數組
dex文件第二部分是各種數據的數組,包括字符串 類型 方法原型 字段 方法
字符串表
字符串表項,是一個字符串數據的偏移,偏移指向的是一個string_data結構。string_data結構中有兩個字段
字段1:字符串長度,數據類型是uleb128,安卓中特有的變長的數據類型
字段2:存儲數據,字符串以0結尾
類型表
類型表,保存的是一個索引值,指向的是字符串表
例如:索引值為3表示的是字符串表的下標為3的位置指向的是L/java/lang/Object這個字符串。
原型表
原型表中存儲的是函數原型的各部分描述信息。包括短類型(shorty_idx),返回類型(return_type_idx),參數的類型(parameters_off),最終還是一個指向字符串表的數組下標。
注意:字段為返回類型(return_type_idx)的值,是類型表中的索引
字段表
存儲的是字段信息,包括字段所在類(class_idx),字段的類型(type_idx),字段的名稱(name_idx)。
class_idx是類型表中的索引,type_idx是類型表中的索引,字段名稱的索引是字符串的數組下標
方法表
方法表中存儲的是方法的信息,包括方法所在的類(class_dex),方法的原型(proto_idx),方法的名稱(name_idx)。
其中class_idx是類型表的索引,proto_idx是原型表的索引,方法名稱的索引(name_idx)是字符串表的數組下標
類數據
類數據也是一個數組,每一個元素就是一個類的相關信息。現在所分析的這個文件因為只有一個類,所以只有一個類的信息。
在表項中的class_data中存儲的是類數據,包括類名索引,訪問屬性,父類索引,接口偏移,源碼索引,注解偏移,類數據偏移
其中,整個類的數據在class_data_item這個結構中。
method_list是類內所有方法的列表,因為當前這個文件只有一個Main方法,所以列表內只有一個結構體。結構體中有方法的基本信息,包括方法索引,訪問標志,代碼偏移,代碼信息
其中code_item是整個代碼的信息,里面有兩個字段特別重要
ins_size:指令長度
ushort insns[8]:指令數組
這個數組存放的就是被翻譯成smali代碼的虛擬機指令,也就是OpCode
手工解析Smali代碼
62 00 00 00 1A 01 00 00 6E 20 01 00 10 00 0E 00接下來復制這一段十六進制,手工將這段代碼解析成Smali代碼。
這里還需要借助一個文檔《(中文)Dalvik操作碼》,里面有所有的Opcode和對應的操作碼以及示例。
首先找到62,62代表的指令含義是根據字段ID讀取靜態對象引用字段到vx,接著,還需要看懂旁邊的示例
6201 0C00解析為smali代碼是
sget‐object v1, Test3.os1:Lja va/lang/Object; // field@000c讀取 Object 的靜態對象引用字段 os1(字段表#CH 條目)到 v1。
也就說這條指令一共4個字節
62 代表操作碼 sget‐object 01 代表的是序號為1的寄存器v1 000C 代表字段表索引為0xC的字段接著再來看我們要解析的Opcode
62 00 00 00 1A 01 00 00 6E 20 01 00 10 00 0E 00先解析前四個字節
62 00 00 00具體含義如下
62 代表操作碼 sget‐object 00 代表的是序號為0的寄存器v0 0000 代表字段表索引為00的字段接下來在字段表中找到第0個字段
java.io.PrintStream java.lang.System.out第0個字段就是out這個對象,將這個字段翻譯為smali代碼
Ljava.lang.System;->out:java.io.PrintStream那么前四個字節
62 00 00 00解析為smali代碼就是
sget‐object v0, Ljava.lang.System;->out:java.io.PrintStream接著來看1A
1A08 0000解析為
const‐string v8, "" // string @0000存入 string@0000(字符串表#0 條目)的引用到 v 8
需要解析的Opcode
1A 01 00 00接著找到字符串表索引為0的字符串
那么這條指令解析為Smali代碼就是
const‐string v1, "Hello World!"接下來查找6E
直接看例子
6E53 0600 0421 ‐ invoke‐virtual { v4, v0, v1, v2, v3}, Test2.method5:(IIII)V // me thod@0006這條指令比較復雜,一共6個字節,其中
6E---> invoke‐virtual 5---> 參數數量 3---> v3 0600--->method@0006 0421--->v4, v0, v1, v2調用參數表的編譯比較詭異,如果參數的數量大于4,第5個參數將編譯在指令字節的下一個字節的4個最低位
那么我們要解析的Opcode
6E 20 01 00 10 00就可以解析為
6E---> invoke‐virtual 2---> 參數數量 01 00--->method@0001 10 00--->v1 v0翻譯出來就是
invoke‐virtual {v0,v1},method@01找到函數表下標為1的方法
void java.io.PrintStream.println(java.lang.String)轉為Smali代碼
Ljava/io/PrintStream;->println(Ljava/lang/String;)V完整的smali代碼就是
invoke‐virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V最后看0E
表示返回值為空
62 00 00 00 1A 01 00 00 6E 20 01 00 10 00 0E 00解析出來完整的smali代碼就是
sget‐object v0, Ljava.lang.System;->out:java.io.PrintStream;const‐string v1, "Hello World!"invoke‐virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)Vreturn‐void和HelloWorld.smali的源碼沒有區別
總結
以上是生活随笔為你收集整理的015 Android之可执行文件dex的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 014 Android之Cydia与Xp
- 下一篇: 016 Android之NDK开发