這裏的dalvik彙編代碼值得是針對dalvik虛擬機設計的指令集,與一般的彙編代碼不同
1.dalvik指令的格式
Dalvik彙編代碼由一系列的dalvik指令組成,指令語法由指令位描述和格式標識來決定,這部分感覺沒用先不看了,在Android4.0及之前的安卓源碼Dalvik/docs目錄下的instruction-formats.html可以看到具體介紹
2.相關工具
smali.jar用來將smali文件彙編爲dex文件,而baksmali.jar則是用來將dex文件反彙編爲smali文件,ddx.jar將dex文件反彙編爲ddx文件,其語法規則與smali類似
使用方法分別爲:
java -jar smali.jar -o xxx.dex xxx.smali
java -jar baksmali.jar -o ouput/ XXX.dex
java -jar baksmali.jar -o ouput/ XXX.dex
3.dalvik寄存器
dalvik虛擬機採用ARM架構,將一部分寄存器映射到ARM寄存器上,另一部分使用調用棧進行模擬。Dalvik的寄存器都是32位的,64位需要用兩個相鄰寄存器表示。寄存器編號取值範圍爲v0-v65535
Android SDK中有名爲dalvik.bytecode.Opcodes的接口,處理字節碼的函數爲一個宏HANDLE_OPCODE(),每個字節碼對應一個cpp文件,在其中對這個宏進行重寫。
寄存器的命名有兩種,v命名和p命名。一個函數有m個寄存器用於儲存局部變量和隱式傳入的函數引用對象,n個用於儲存參數,v命名法將寄存器命名爲v0~vm+n-1,而p命名法前m個寄存器仍然爲v0~vm-1,儲存參數的寄存器則被命名爲p0~pn-1。
4.Dalvik字節碼的類型、方法與字段表示方法
可以參考官網https://source.android.com/devices/tech/dalvik/dalvik-bytecode的介紹,百度了好多還是官網說的比較清楚
(1)類型:
Dalvik字節碼只有基本類型和引用類型兩種,這兩種類型可以表示Java語言的全部類型,除了對象和數組屬於引用對象以外,其它的Java類型都是基本類型
- J、D這種64位的類型需要使用相鄰的兩個寄存器儲存
- L可以代表Java類型的任何類,這些類在java代碼中以package.name.ObjectName的方式引用,對應的彙編形式爲Lpackage/name/ObjectName;最後有一個分號。例如Ljava/lang/String;對應於java.lang.String
- [表示所有基本類型的數組,[後面緊跟類型描述符,例如[I代表java中的int[],n個[表示n維數組,與L組合就可以表示對象數組,[Ljava/lang/String就表示字符串數組
(2)方法
方法的格式爲Lpackage/name/ObjectName;->MethodName(III)Z III表示該方法有三個int型參數,Z表示返回值爲boolean型,轉換爲java方法應該爲Boolean MethodName(int,int, int)
(3)字段
格式爲Lpackage/name/ObjectName;->FildName:Ljava/lang/String;
5.Dalvik指令集
(1)特點
參數順序先是目的操作數,後是源操作數
名稱後綴:64位字節碼添加-wide後綴,特殊類型字節碼根據具體類型添加後綴,例如-boolean -byte -class -void。
字節碼後綴:根據佈局和選項的不同,添加後綴用“/”分隔開例如/from16表示源爲16位的寄存器變量
(2)空操作指令
助記符爲nop,被用來對齊代碼
(3)數據操作指令
move vA, vB 將vB的值賦給vA ; move-wide vA, vB 賦值爲64位
寄存器默認爲4位,/16表示源寄存器和目的寄存器都爲16位,/from16表示源爲16位 -object表示爲對象賦值,
move-result vA表示將上一個invoke類型操作的單字非對象結果賦值給vA寄存器,這裏只有destination
move-exception vA 保存一個異常到vA寄存器
(4)返回指令
return-void 從一個void方法返回
return-vAA 表示返回一個32位非對象類型的值,返回值寄存器爲8位的寄存器
return-wide vAA 表示函數返回64位非對象類型的值,返回值寄存器爲8位的寄存器
return-object vAA 表示函數返回對象類型的值,返回值寄存器爲8位的寄存器
(5)數據定義指令
const-wide/16 vAA, #+BBBB 將該常量值符號拓展至64位後賦值給vAA
......
(6)鎖指令
monitor-enter vAA 爲指定的對象獲取鎖
monitor-exit vAA 釋放指定對象的鎖
(7)實例操作指令
包括實例的類型轉換、檢查以及新建等
check-cast vAA,type@BBBB 將vAA寄存器中的對象引用轉換爲指定類型,失敗會拋出異常
instance-of vA , vB ,type@CCCC 判斷vB寄存器中的對象引用是否可以轉換爲指定類型,可以則給vA賦值爲1,否則爲0
new-instance vAA,type@BBBB 構造一個制定類型對象的新實例,並將對象引用賦值給vAA寄存器,type不能是數組類型
這三種指令加上/jumbo之後功能一樣,但是寄存器值與指令索引取值範圍更大
(8)數組操作指令
array-length vA, vB 獲取vB寄存器數組的長度並將值賦給vA寄存器,數組長度指的是數組條目個數
new-array vA, vB, type@CCCC 構造指定類型的與大小的數組,將索引賦給vA,大小由vB決定
filled-new-array {vC,vD,vE,vF,vG} , type@BBBB 構造指定類型與大小的數組並填充數組內容,大小由vA隱含提供,並將值賦給vA
fill-array-data vAA,+BBBBBBBB 用指定的數據填充數組,vAA寄存器爲數組引用,引用必須爲基礎類型的數組
(9)異常指令
throw vAA 拋出vAA寄存器中制定的異常
(10)跳轉指令
分爲無條件跳轉goto、分支跳轉switch與條件跳轉if
goto +AA 跳轉AA的偏移量
packed-switch vAA, +BBBBBBBB分支跳轉指令,vAA寄存器爲switch分支需要判斷的值。+BBBBBBB指向一個packed-switch-payload格式的偏移表
sparse-switch vAA, +BBBBBBBB分支跳轉指令,vAA寄存器爲switch分支需要判斷的值。+BBBBBBB指向一個sparse-switch-payload格式的偏移表
if-xx vA,vB, +CCCC 比較vA和vB,滿足xx條件跳轉到CCCC的偏移處,條件包括eq(等於)、ne(不等於)、lt(小於)、ge(大於等於)等等
if-xxx vA, +CCCC 把vA和0比較,滿足xxx條件跳轉到CCCC的偏移處,條件包括eqz(等於)、nez(不等於)、ltz(小於)、gez(大於等於)等等
(11)比較指令
cmpl-xxx vAA,vBB,vCC 如果vBB小於vCC,則結果爲1,相等爲0,否則-1,xxx指的是float、double
cmpg-xxx vAA,vBB,vCC 如果vBB大於vCC,則結果爲1,相等爲0,否則-1,xxx指的是float、double
cmp-long vAA,vBB,vCC 如果vBB大於vCC,則結果爲1,相等爲0,否則-1
(12)字段操作指令
這些指令用來對對象實例的字段進行讀寫操作,按照普通字段和靜態字段分爲兩類:
iop vA,vB, field@CCCC 和 sop vAA, field@BBBB
iop代表iget iget-wide iget-object iget-boolean iget-byte iput iput-wide......
sop代表sget sget-wide sget-object sget-boolean sget-byte sput sput-wide......
其中get表示讀操作,put表示寫操作
(13)方法調用指令
invoke-kind {vC,vD,vE,vF,vG},meth@BBBB和 invoke-kind/range {vCCCC..vNNN},meth@BBBB ,二者相似,只是後者使用range指定寄存器範圍。按照調用方法的kind具體分爲以下幾種:
invoke-virtual 或 invoke-virtual/range調用實例的虛方法
invoke-super 或 invoke-super/range調用實例的父類方法
invoke-direct 或 invoke-direct/range調用實例的直接方法
invoke-static 或 invoke-static/range調用實例的靜態方法invoke-interface 或 invoke-interface/ragne調用實例的接口方法
(14)數據轉換指令
op vA,vB 表示將VB寄存器的數據進行轉換然後儲存到vA寄存器.op分爲以下幾種
neg-int/long/float/double 求補
not-int/long/float/double 求補
int-to-long int轉換爲long
float-to-int
.......
(15)數據運算指令
數據運算指令包括算術運算指令(加減乘除移位等)和邏輯運算指令(與或非異或得等)
指令格式爲以下幾種:
op vAA,vBB,vCC 將vBB,vCC 進行運算,保存到vAA
op/2addr vA,vB 將 vA,vB進行運算,保存到vA
op/lit16 vA,vB,#+CCCC 將vB與常量CCCC運算,保存到vA
op/lit8 vAA,vBB,#+CC 將vBB與常量CC運算,保存到vAA
op包含add-type sub-type mul-type div-type rem-type and-type or-type xor-type shl-type shr-type ushr-type
6.smali代碼實例
.class public LHelloWorld; #定義類名
.super Ljava/lang/Object; #定義父類
.method public static main([Ljava/lang/String;)V
.registers 4 #程序中使用4個寄存器,v0,v1,v2和一個參數寄存器
.parameter #一個參數,有n個參數則有n行
.prologue #代碼起始的指令
#空指令
nop
nop
nop
nop
#數據定義指令
const/16 v0, 0x8
const/4 v1, 0x5
const/4 v2, 0x3
#數據操作指令
move v1, v2
#數組操作指令
new-array v0, v0, [I
array-length v1, v0
#實例操作指令
new-instance v1, Ljava/lang/StringBuilder;
#方法調用指令
invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
#跳轉指令
if-nez v0, :cond_0
goto :goto_0
:cond_0
#數據轉換指令
int-to-float v2, v2
#數據運算指令
add-float v2, v2, v2
#比較指令
cmpl-float v0, v2, v2
#字段操作指令
sget-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;)V
#返回指令
:goto_0
return-void
.end method