Android逆向基礎之Dalvik指令集

Dalvik是Google公司自己設計的用于Android平臺的虛擬機。Dalvik虛擬機是Google等廠商合作開發的Android移動設備平臺的核心組成部分之一。它可以支持已轉換為 .dex(即Dalvik Executable)格式的Java應用程序的運行,.dex格式是專為Dalvik設計的一種壓縮格式,適合內存和處理器速度有限的系統。Dalvik 經過優化,允許在有限的內存中同時運行多個虛擬機的實例,并且每一個Dalvik 應用作為一個獨立的Linux 進程執行。獨立的進程可以防止在虛擬機崩潰的時候所有程序都被關閉。

1. 指令特點

Dalvik指令在調用格式上模仿了C語言的調用約定。Dalvik指令的語法與助詞符有如下特點:

  • 參數采用從目標(destination)到源(source)的方式。
  • 根據字節碼的大小與類型不同,一些字節碼添加了名稱后綴以消除岐義。
    • 32位常規類型的字節碼末添加任何后綴。
    • 64位常規類型的字節碼添加 -wide后綴。
    • 特殊類型的字節碼根據具體類型添加后綴。它們可以是 -boolean,-byte,-char,-short,-int,-long,-float,-double,-object,-string,-class,-void之一。
  • 根據字節碼的布局與選項不同,一些字節碼添加了字節碼后綴以消除岐義。這些后綴通過在字節碼主名稱后添加斜杠“/”來分隔開。
  • 在指令集的描述中,寬度值中每個字母表示寬度為4位。

例如這條指令:move-wide/from16 vAA, vBBBB

  • move為基礎字節碼(base opcode),標識這是基本操作。
  • wide為名稱后綴(name suffix),標識指令操作的數據寬度(64位)。
  • from16為字節碼后綴(opcode suffix),標識源為一個16位的寄存器引用變量。
  • vAA為目的寄存器,它始終在源的前面,取值范圍為v0~v255。
  • vBBBB為源寄存器,取值范圍為v0~v65535。

Dalvik指令集中大多數指令用到了寄存器作為目的操作數或源操作數,其中:

  • A/B/C/D/E/F/G/H代表一個4位的數值,可用來表示0~15的數值或v0~v15的寄存器;
  • AA/BB/CC/DD/EE/FF/GG/HH代表一個8位的數值,可用來表示0255的數值或v0v255的寄存器;
  • AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/HHHH代表一個16位的數值,可用來表示0~65535的數值或v0~v65535的寄存器。

注意:

  • Android官方指令文檔描述寄存器時,對不同取值范圍的寄存器以括號說明其大小,如A:destination register(4 bits),A:destination register(16 bits)。
  • Dalvik虛擬機中的每個寄存器都是32位的,描述指令時所說的位數表示的是寄存器數值的取值范圍。

2. 空操作指令

空操作指令的助記符為nop。它的值為00,通常nop指令被用來作對齊代碼之用,無實際操作。

3. 數據操作指令

數據操作指令為move。move指令的原型為move destination,source,move指令根據字節碼的大小與類型不同,后面會跟上不同的后綴。

指令 說明
move vA, vB 將vB寄存器的值賦給vA寄存器,源寄存器與目的寄存器都為4位
move/from16 vAA, vBBBB 將vBBBB寄存器的值賦給vAA寄存器,源寄存器為16位,目的寄存器為8位
move/16 vAAAA, vBBBB 將vBBBB寄存器的值賦給vAAAA寄存器,源寄存器與目的寄存器都為16位
move-wide vA, vB 為4位的寄存器對賦值。源寄存器與目的寄存器都為4位
move-wide/from16 vAA, vBBBB move-wide相同
move-wide/16 vAAAA, vBBBB move-wide相同
move-object vA, vB 為對象賦值。源寄存器與目的寄存器都為4位
move-object/from16 vAA, vBBBB 為對象賦值。源寄存器為16位,目的寄存器為8位
move-object/16 vAA, vBBBB 為對象賦值。源寄存器與目的寄存器都為16位
move-result vAA 將上一個invoke類型指令操作的單字非對象結果賦給vAA寄存器
move-result-wide vAA 將上一個invoke類型指令操作的雙字非對象結果賦給vAA寄存器
move-result-object vAA 將上一個invoke類型指令操作的對象結果賦給vAA寄存器
move-exception vAA 保存一個運行時發生的異常到vAA寄存器,這條指令必須是異常發生時的異常處理器的一條指令,否則指令無效

4. 返回指令

返回指令指的是函數結尾時運行的最后一條指令。它的基礎字節碼為teturn,共有以下四條返回指令:

指令 說明
return-void 表示函數從一個void方法返回
return vAA 表示函數返回一個32位非對象類型的值,返回值寄存器為8位的寄存器vAA
return-wide vAA 表示函數返回一個64位非對象類型的值,返回值為8位的寄存器對vAA
return-object vAA 表示函數返回一個對象類型的值。返回值為8位的寄存器vAA

5. 數據定義指令

數據定義指令用來定義程序中用到的常量,字符串,類等數據。它的基礎字節碼為const

指令 說明
const/4 vA, #+B 將數值符號擴展為32位后賦給寄存器vA
const/16 vAA, #+BBBB 將數據符號擴展為32位后賦給寄存器vAA
const vAA, #+BBBBBBBB 將數值賦給寄存器vAA
const/high16 vAA, #+BBBB0000 將數值右邊零擴展為32位后賦給寄存器vAA
const-wide/16 vAA, #+BBBB 將數值符號擴展為64位后賦給寄存器對vAA
const-wide/32 vAA, #+BBBBBBBB 將數值符號擴展為64位后賦給寄存器對vAA
const-wide vAA, #+BBBBBBBBBBBBBBBB 將數值賦給寄存器對vAA
const-wide/high16 vAA, #+BBBB000000000000 將數值右邊零擴展為64位后賦給寄存器對vAA
const-string vAA, string@BBBB 通過字符串索引構造一個字符串并賦給寄存器vAA
const-string/jumbo vAA, string@BBBBBBBB 通過字符串索引(較大)構造一個字符串并賦給寄存器vAA
const-class vAA, type@BBBB 通過類型索引獲取一個類引用并賦給寄存器vAA
const-class/jumbo vAAAA, type@BBBBBBBB 通過給定的類型索引獲取一個類引用并賦給寄存器vAAAA。
這條指令占用兩個字節,值為0xooff(Android4.0中新增的指令)

6. 鎖指令

鎖指令多用在多線程程序中對同一對象的操作。Dalvik指令集中有兩條鎖指令:

指令 說明
monitor-enter vAA 為指定的對象獲取鎖
monitor-exit vAA 釋放指定的對象的鎖

7. 實例操作指令

與實例相關的操作包括實例的類型轉換,檢查及新建等:

指令 說明
check-cast vAA, type@BBBB 將vAA寄存器中的對象引用轉換成指定的類型,如果失敗會拋出ClassCastException異常。
如果類型B指定的是基本類型,對于非基本類型的A來說,運行時始終會失敗
instance-of vA, vB, type@CCCC 判斷vB寄存器中的對象引用是否可以轉換成指定的類型,如果可以vA寄存器賦值為1,否則vA寄存器賦值為0
new-instance vAA, type@BBBB 構造一個指定類型對象的新實例,并將對象引用賦值給vAA寄存器,類型符type指定的類型不能是數組類
check-cast/jumbo vAAAA, type@BBBBBBBB 指令功能與check-cast vAA, type@BBBB相同,只是寄存器值與指令的索引取值范圍更大(Android4.0中新增的指令)
instance-of/jumbo vAAAA, vBBBB, type@CCCCCCCC 指令功能與instance-of vA, vB, type@CCCC相同,只是寄存器值與指令的索引取值范圍更大(Android4.0中新增的指令)
new-instance/jumbo vAAAA, type@BBBBBBBB 指令功能與new-instance vAA, type@BBBB相同,只是寄存器值與指令的索引取值范圍更大(Android4.0中新增的指令)

8. 數組操作指令

數組操作包括獲取數組長度,新建數組,數組賦值,數組元素取值與賦值等操作。

指令 說明
array-length vA, vB 獲取給定vB寄存器中數組的長度并將值賦給vA寄存器,數組長度指的是數組的條目個數
new-array vA, vB, type@CCCC 構造指定類型(type@CCCC)與大小(vB)的數組,并將值賦給vA寄存器
filled-new-array {vC, vD, vE, vF, vG},type@BBBB 構造指定類型(type@BBBB)與大小(vA)的數組并填充數組內容。
vA寄存器是隱含使用的,除了指定數組的大小外還指定了參數的個數,vC~vG是使用到的參數寄存序列
filled-new-array/range {vCCCC ..vNNNN}, type@BBBB 指令功能與filled-new-array {vC, vD, vE, vF, vG},type@BBBB相同,只是參數寄存器使用range字節碼后綴指定了取值范圍 ,vC是第一個參數寄存器,N = A +C -1
fill-array-data vAA, +BBBBBBBB 用指定的數據來填充數組,vAA寄存器為數組引用,引用必須為基礎類型的數組,在指令后面會緊跟一個數據表
new-array/jumbo vAAAA, vBBBB,type@CCCCCCCC 指令功能與new-array vA,vB,type@CCCC相同,只是寄存器值與指令的索引取值范圍更大(Android4.0中新增的指令)
filled-new-array/jumbo {vCCCC ..vNNNN},type@BBBBBBBB 指令功能與filled-new-array/range {vCCCC ..vNNNN},type@BBBB相同,只是索引取值范圍更大(Android4.0中新增的指令)
arrayop vAA, vBB, vCC 對vBB寄存器指定的數組元素進入取值與賦值。
vCC寄存器指定數組元素索引,vAA寄存器用來存放讀取的或需要設置的數組元素的值

讀取元素使用aget類指令,元素賦值使用aput類指定,根據數組中存儲的類型指令后面會緊跟不同的指令后綴,指令列表如下:

aget, aget-wide, aget-object, aget-boolean, aget-byte,aget-char, aget-short, 
aput, aput-wide, aput-object, aput-boolean, aput-byte, aput-char, aput-short

9. 異常指令

Dalvik指令集中有一條指令用來拋出異常。

throw vAA用來拋出vAA寄存器中指定類型的異常。

10. 跳轉指令

跳轉指令用于從當前地址跳轉到指定的偏移處。Dalvik指令集中有三種跳轉指令:無條件跳轉(goto),分支跳轉(switch)與條件跳轉(if)。

指令 說明
goto +AA 無條件跳轉到指定偏移處,偏移量AA不能為0
goto/16 +AAAA 無條件跳轉到指定偏移處,偏量AAAA不能為0
goto/32 +AAAAAAAA 無條件跳轉到指定偏移處
packed-switch vAA, +BBBBBBBB 分支跳轉指令。
vAA寄存器為switch分支中需要判斷的值,
BBBBBBBB指向一個packed-switch-payload格式的偏移表,表中的值是有規律遞增的
sparse-switch vAA, +BBBBBBBB 分支跳轉指令。
vAA寄存器為switch分支中需要判斷的值,
BBBBBBBB指向一個sparse-switch-payload格式的偏移表,表中的值是無規律的偏移量
if-test vA, vB, +CCCC 條件跳轉指令。
比較vA寄存器與vB寄存器的值,如果比較結果滿足就跳轉到CCCC指定的偏移處。
偏移量CCCC不能為0
if-testz vAA, +BBBB 條件跳轉指令。
拿vAA寄存器與0比較,如果比較結果滿足或值為0時就跳轉到BBBB指定的偏移處。
偏移量BBBB不能為0

if-test類型的指令有以下幾條:

指令 說明 Java語法
if-eq 如果vA等于vB則跳轉 if(vA == vB)
if-ne 如果vA不等于vB則跳轉 if(vA != vB)
if-lt 如果vA小于vB則跳轉 if(vA < vB)
if-ge 如果vA大于等于vB則跳轉 if(vA >= vB)
if-gt 如果vA大于vB則跳轉 if(vA > vB)
if-le 如果vA小于等于vB則跳轉 if(vA <= vB)

if-testz類型的指令有以下幾條:

指令 說明 Java語法
if-eqz 如果vAA為0則跳轉 if(vAA == 0)
if-nez 如果vAA不為0則跳轉 if(vAA != 0)
if-ltz 如果vAA小于0則跳轉 if(vAA < 0)
if-gez 如果vAA大于等于0則跳轉 if(vAA >= 0)
if-gtz 如果vAA大于0則跳轉 if(vAA > 0)
if-lez 如果vAA小于等于0則跳轉 if(vAA <= 0)

11. 比較指令

比較指令用于對兩個寄存器的值(浮點型或長整型)進行比較。它的格式為cmpkind vAA, vBB, vCC,其中vBB寄存器與vCC寄存器是需要比較的兩個寄存器或寄存器對,比較的結果放到vAA寄存器。Dalvik指令集中共有5條比較指令:

指令 說明
cmpl-float 比較兩個單精度浮點數。
如果vBB寄存器大于vCC寄存器,結果為-1,相等則結果為0,小于的話結果為1
cmpg-float 比較兩個單精度浮點數。
如果vBB寄存器大于vCC寄存器,則結果為1,相等則結果為0,小于的話結果為-1
cmpl-double 比較兩個雙精度浮點數。
如果vBB寄存器對大于vCC寄存器對,則結果為-1,相等則結果為0,小于則結果為1
cmpg-double 比較兩個雙精度浮點數。
如果vBB寄存器對大于vCC寄存器對,則結果為1,相等則結果為0,小于的話,則結果為-1
cmp-long 比較兩個長整型數。
如果vBB寄存器大于vCC寄存器,則結果為1,相等則結果為0,小則結果為-1

12. 字段操作指令

字段操作指令用來對對象實例的字段進入讀寫操作。字段的類型可以是Java中有效的數據類型。對普通字段與靜態字段操作有兩種指令集,分別是iinstanceop vA, vB, fidld@CCCCsstaticop vAA, field@BBBB

普通字段指令的指令前綴為i,如對普通字段讀操作使用iget指令,寫操作使用iput指令;靜態字段的指令前綴為s,如對靜態字段讀操作使用 sget 指令,寫操作使用 sput 指令。

根據訪問的字段類型不同,字段操作指令后面會緊跟字段類型的后綴,如 iget-byte指令表示讀取實例字段 的值類型為字節類型,iput-short指令表示設置實例字段的值類型為短整型。兩類指令操作結果都是一樣,只是指令前綴與操作的字段類型不同。

普通字段操作指令有:

iget,iget-wide,iget-object,iget-boolean,iget-byte,iget-char,iget-short,
iput,iput-wide,iput-object,iput-boolean,iput-byte,iput-char,iput-short。

靜態字段操作指令有:

sget,sget-wide,sget-object,sget-boolean,sget-byte,sget-char,sget-short,
sput,sput-wide,sput-object,sput-boolean,sput-byte,sput-char,sput-short。

在Android4.0系統中,Dalvik指令集中增加了iinstanceop/jumbo vAAAA, vBBBB, field@CCCCCCCCsstaticop/jumbo vAAAA, field@BBBBBBBB兩類指令,它們與上面介紹的兩類指令作用相同,只是在指令中增加了jumbo字節碼后綴,且寄存器值與指令的索引取值范圍更大。

13. 方法調用指令

方法調用指令負責調用類實例的方法。它的基礎指令為invoke,方法調用指令有invoke-kind {vC, vD, vE, vF, vG},meth@BBBBinvoke-kind/range {vCCCC .. vNNNN},meth@BBBB兩類,兩類指令在作用上并無不同,只是后者在設置參數寄存器時使用了range來指定寄存器的范圍。根據方法類型的不同,共有如下五條方法調用指令:

指令 說明
invoke-virtual 或 invoke-virtual/range 調用實例的虛方法
invoke-super 或 invoke-super/range 調用實例的父類方法
invoke-direct 或 invoke-direct/range 調用實例的直接方法
invoke-static 或 invoke-static/range 調用實例的靜態方法
invoke-interface 或 invoke-interface/range 調用實例的接口方法

在Android4.0系統中,Dalvik指令集中增加了invoke-kind/jumbo {vCCCC .. vNNNN},meth@BBBBBBBB這類指令,它與上面介紹的兩類指令作用相同,只是在指令中增加了jumbo字節碼后綴,且寄存器值與指令的索引取值范圍更大。

方法調用指令的返回值必須使用move-result*指令來獲取。如下面兩條指令:

invoke-static {}, Landroid/os/Parcel;->obtain() Landroid/os/Parcel;
move-result-object v0

14. 數據轉換指令

數據轉換指令用于將一種類型的數值轉換成另一種類型。它的格式為unop vA, vB,vB寄存器或vB寄存器對存放需要轉換的數據,轉換后的結果保存在vA寄存器或vA寄存器對中。

指令 說明
neg-int 對整型數求補
not-int 對整型數求反
neg-long 對長整型數求補
not-long 對長整型數求反
neg-float 對單精度浮點型數求補
neg-double 對雙精度浮點型數求補
int-to-long 將整型數轉換為長整型
int-to-float 將整型數轉換為單精度浮點型數
int-to-dobule 將整型數轉換為雙精度浮點數
long-to-int 將長整型數轉換為整型
long-to-float 將長整型數轉換為單精度浮點型
long-to-double 將長整型數轉換為雙精度浮點型
float-to-int 將單精度浮點數轉換為整型
float-to-long 將單精度浮點數轉換為長整型數
float-to-double 將單精度浮點數轉換為雙精度浮點型數
double-to-int 將雙精度浮點數轉換為整型
double-to-long 將雙精度浮點數轉換為長整型
double-to-float 將雙精度浮點數轉換為單精度浮點型
int-to-byte 將整型轉換為字節型
int-to-char 將整型轉換為字符型
int-to-short 將整型轉換為短整型

15. 數據運行指令

數據運算指令包括算術運算指令與邏輯運算指令。算術運算指令主要進行數值間如加,減,乘,除,模,移位等運算。邏輯運算指令主要進行數值間與,或,非,抑或等運算。數據運算指令有以下四類(數據運算時可能是在寄存器或寄存器對間進行,下面的指令作用講解時使用寄存器來描述):

指令 說明
binop vAA, vBB, vCC 將vBB寄存器與vCC寄存器進行運算,結果保存到vAA寄存器
binop/2addr vA, vB 將vA寄存器與vB寄存器進行運算,結果保存到vA寄存器
binop/lit16 vA, vB, #+CCCC 將vB寄存器與常量 CCCC進行運算,結果保存到vA寄存器
binop/lit8 vAA, vBB, #+CC 將vBB寄存器與常量CC進行運算,結果保存到vAA寄存器

后面3類指令比第1類指令分別多出了2addr,lit16,lit8等指令后綴。四類指令中基礎字節碼相同的指令執行的運算操作是類似的,第1類指令中,根據數據的類型不同會在基礎字節碼后面加上數據類型后綴,如 -int 或 -long 分別表示操作的數據類型為整型與長整型。第1類指令可歸類如下:

指令 說明
add-type vBB寄存器與vCC寄存器值進行加法運算(vBB + vCC)
sub-type vBB寄存器與vCC寄存器值進行減法運算(vBB - vCC)
mul-type vBB寄存器與vCC寄存器值進行乘法運算(vBB * vCC)
div-type vBB寄存器與vCC寄存器值進行除法運算(vBB / vCC)
rem-type vBB寄存器與vCC寄存器值進行模運算(vBB % vCC)
and-type vBB寄存器與vCC寄存器值進行與運算(vBB & vCC)
or-type vBB寄存器與vCC寄存器值進行或運算(vBB | vCC)
xor-type vBB寄存器與vCC寄存器值進行異或運算(vBB ^ vCC)
shl-type vBB寄存器值(有符號數)左移vCC位(vBB << vCC )
shr-type vBB寄存器值(有符號)右移vCC位(vBB >> vCC)
ushr-type vBB寄存器值(無符號數)右移vCC位(vBB >>> vCC)

其中基礎字節碼后面的-type可以是-int,-long, -float,-double。后面3類指令與之類似。

至此,Dalvik虛擬機支持的所有指令就介紹完了。在android4.0系統以前,每個指令的字節碼只占用一個字節,范圍是0x0~0x0ff。在android4.0系統中,又擴充了一部分指令,這些指令被稱為擴展指令,主要是在指令助記符后添加了jumbo后綴,增加了寄存器與常量的取值范圍。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容