前言
Dalvik指令語(yǔ)法詳解
該篇文章為本人的學(xué)習(xí)筆記,如有不對(duì)之處,請(qǐng)指教.
附參考鏈接:smali文件語(yǔ)法參考
類(lèi)型
字節(jié)碼類(lèi)型描述符
語(yǔ)法 | 含義 |
---|---|
V |
void ,只用于返回值類(lèi)型 |
Z |
boolean |
B |
byte |
S |
short |
C |
char |
I |
int |
J |
long |
F |
float |
D |
double |
L |
java類(lèi)類(lèi)型 |
[ |
數(shù)組類(lèi)型 |
其中L
類(lèi)型可以表示Java類(lèi)型中的任何類(lèi).
例如
java.lang.String
在smali語(yǔ)法中表示為:
Ljava.lang.String;
注意后面有個(gè)分號(hào),L
類(lèi)型最后的分號(hào)表示對(duì)象名結(jié)束.
[
類(lèi)型可以表示所有基本類(lèi)型的數(shù)組. [
后面緊跟基本數(shù)據(jù)類(lèi)型描述符. 如[I
相當(dāng)于Java中的int[]
,即一維數(shù)組. [[I
相當(dāng)于Java中的int[][]
,即二維數(shù)組.
三維、四維等等數(shù)值以此類(lèi)推. 注意多維數(shù)組的維數(shù)最大為255
個(gè).
L
與 [
可以同時(shí)使用用來(lái)表示對(duì)象數(shù)組. 如[Ljava.lang.String;
就表示這是一個(gè)String
類(lèi)型的數(shù)組.
方法及字段
方法的表現(xiàn)格式如下
Lpackage/name/ObjectName;->MethodName(III)Z
其中 Lpackage/name/ObjectName;
應(yīng)該理解為該方法所在的類(lèi),MethodName
為具體方法名,(III)Z
這是方法具體的傳參和返回部分,其中括號(hào)內(nèi)的III
為方法參數(shù)(在這里是表示三個(gè)int
類(lèi)型的參數(shù)),Z表示方法多維返回值(在這里返回值為boolean
類(lèi)型).
字段的格式和方法很像,只是方法的括號(hào)禀综、括號(hào)里面的參數(shù)及返回值,這些字段都是沒(méi)有的,后面取而代之的是字段自己的類(lèi)型.字段格式如下
Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;
其中Lpackage/name/ObjectName;
不用說(shuō)還是該字段所在的類(lèi),FieldName
為字段名,Ljava/lang/String;
為字段類(lèi)型.其中字段名與字段類(lèi)型之間用冒號(hào):
隔開(kāi).
Dalvik指令
首先咱們來(lái)解析一條指令
move-wide/from16 vAA,vBBBB
move
為基礎(chǔ)字節(jié)碼,即操作符 . wide
為名稱(chēng)后綴,標(biāo)識(shí)操作的數(shù)組為64位. from16
位字節(jié)碼的后綴,標(biāo)識(shí)源操作數(shù)是一個(gè)16位寄存器引用變量. vAA
為目的寄存器,他始終在源寄存器的前面.
vBBBB
為源寄存器. 若沒(méi)有wide
后綴,默認(rèn)為32位.
move指令
move
指令的作用是將源寄存器的值賦值給目的寄存器,即
move vA,vB
move-wide
作用同上,只是賦值的為64位.</br> move-object
是為對(duì)象賦值.
move-result
指令的作用是將上一個(gè)invoke
類(lèi)型指令的操作結(jié)果賦值給目的寄存器,即
move-result vAA
move-result-wide
作用同上,只是賦值的為64位. </br> move-object
同上,只是賦值為對(duì)象類(lèi)型.
返回指令
return-void
表示函數(shù)從一個(gè)void方法返回.
return
表示函數(shù)返回一個(gè)32位非對(duì)象的值.
return-wide
表示函數(shù)返回一個(gè)64位非對(duì)象的值.
return-object
表示函數(shù)返回一個(gè)對(duì)象類(lèi)型.
數(shù)據(jù)定義
const
常用來(lái)定義程序中用到是常量邻奠、字符串框全、類(lèi)等數(shù)據(jù).</br> const 佑女、const/4割笙、const/16
給寄存器賦值基本數(shù)據(jù)類(lèi)型.即
const/4 v1, 0x2
當(dāng)const-string
給寄存器賦字符串,即
const-string v0, "\u60a8\u7684\u8bd5"
</br>const-class
給寄存器賦值一個(gè)類(lèi)引用.
鎖指令
鎖指令用于在多線(xiàn)程程序中對(duì)同一對(duì)象的操作.
monitor-enter v0
為指定的對(duì)象獲取鎖.
monitor-exit v0
釋放指定對(duì)象的鎖.
實(shí)例操作指令
- 類(lèi)型轉(zhuǎn)換指令
check-cast v0,type@BBBB
將v0
寄存器轉(zhuǎn)換成指定的類(lèi)型.
- 檢查指令
instance-of v0,v1,type@BBBB
檢測(cè)v1
是否可以轉(zhuǎn)換成指定類(lèi)型,可以轉(zhuǎn)換v0
賦值為1,否則賦值 0.
- 創(chuàng)建指令
new-instance v0,type@BBBB
構(gòu)造一個(gè)指定類(lèi)型的實(shí)例,并把實(shí)例對(duì)象的引用賦值給v0
.類(lèi)型符 type
指定類(lèi)型不能為數(shù)組.
數(shù)組操作指令
- 創(chuàng)建數(shù)組
new-array v0,v1,type@BBBB
構(gòu)造指定類(lèi)型的數(shù)組,v1
表示數(shù)組的大小,并將數(shù)組賦值給v0
.
filed-new-array {v1,v2,v3},type@BBBB
構(gòu)造數(shù)組的另一種方式,即相當(dāng)于Java中的
int[] arrays= {1,2,3,4};
- 獲取數(shù)組長(zhǎng)度
array-length v0,v1
獲取v1
寄存器中的數(shù)組長(zhǎng)度,并賦值給v0
寄存器.
跳轉(zhuǎn)指令
- goto指令
goto +AA
無(wú)條件跳轉(zhuǎn)到指定偏移量處,偏移量不能為0.
- switch指令
packed-switch v0,+BBBB
分支跳轉(zhuǎn),v0
寄存器為switch分支中的判斷值,+BBBB
指向的是packed-switch-payload
格式的偏移表,表中的值是有規(guī)律的.
sparse-switch v0,+BBBB
作用同上,唯一不同是偏移表中的值是無(wú)規(guī)律的.
- if指令
if指令格式如下
if-eq(此處可替換) v0,v1,+BBBB
比較兩個(gè)寄存器的值,符合條件進(jìn)行跳轉(zhuǎn).
操作符 | 作用 | 對(duì)應(yīng)java語(yǔ)句 |
---|---|---|
if-eq |
如果v0 等于v1 則跳轉(zhuǎn). |
if(v0==v1) |
if-ne |
如果v0 不等于v1 則跳轉(zhuǎn). |
if(v0!=v1) |
if-lt |
如果v0 小于v1 則跳轉(zhuǎn). |
if(v0<v1) |
if-gt |
如果v0 大于v1 則跳轉(zhuǎn). |
if(v0>v1) |
if-le |
如果v0 小于等于v1 則跳轉(zhuǎn). |
if(v0<=v1) |
if-ge |
如果v0 大于等于v1 則跳轉(zhuǎn). |
if(v0>=v1) |
if-eq(此處可替換) v0,+BBBB
用寄存器中的值和0
進(jìn)行比較,符合跳轉(zhuǎn)跳轉(zhuǎn).
操作符 | 作用 | 對(duì)應(yīng)java語(yǔ)句 |
---|---|---|
if-eqz |
如果v0 等于0 則跳轉(zhuǎn). |
if(v0==0) |
if-nez |
如果v0 不等于0 則跳轉(zhuǎn). |
if(v0!=0) |
if-ltz |
如果v0 小于0 則跳轉(zhuǎn). |
if(v0<0) |
if-gtz |
如果v0 大于0 則跳轉(zhuǎn). |
if(v0>0) |
if-lez |
如果v0 小于等于0 則跳轉(zhuǎn). |
if(v0<=0) |
if-gez |
如果v0 大于等于0 則跳轉(zhuǎn). |
if(v0>=0) |
比較指令
用于比較兩個(gè)寄存器的值(浮點(diǎn)型或長(zhǎng)整型),比較結(jié)果放到v0
寄存器中.
格式
cmpl-float(此處可替換) v0,v1,v2
操作符 | 作用 |
---|---|
cmpl-float |
如果v1 小于v2 則結(jié)果為1 ,相等則結(jié)果為0 ,大于則結(jié)果為-1 . |
cmpg-float |
如果v1 大于v2 則結(jié)果為1 ,相等則結(jié)果為0 ,小于則結(jié)果為-1 . |
cmpl-double |
如果v1 小于v2 則結(jié)果為1 ,相等則結(jié)果為0 ,大于則結(jié)果為-1 . |
cmpg-double |
如果v1 大于v2 則結(jié)果為1 ,相等則結(jié)果為0 ,小于則結(jié)果為-1 . |
cmp-long |
如果v1 大于v2 則結(jié)果為1 ,相等則結(jié)果為0 ,小于則結(jié)果為-1 . |
字段操作指令
字段操作指令分兩大類(lèi):普通字段和靜態(tài)字段,普通字段指令的前綴為i
,靜態(tài)字段指令的前綴為s
.
字段的讀操作指令為get
,寫(xiě)操作指令為put
,因此普通字段的操作指令為iget
,iput
.靜態(tài)字段的操作指令為sget
,sput
.
指令格式如下
.line 16
iput-object p1, p0, Lcom/view/dialogapplication/PhoneInfo;->context:Landroid/content/Context;
上面是一段iput
指令代碼,它所對(duì)應(yīng)的java代碼如下
this.context = context;
沒(méi)錯(cuò),它就會(huì)一個(gè)簡(jiǎn)單的賦值context
的代碼;
由此,可以看出來(lái), p1
是要賦值的context
,p0
是源,而后面的第三個(gè)參數(shù)
Lcom/view/dialogapplication/PhoneInfo;->context:Landroid/content/Context;
可以看出來(lái)是p1
的字段名.
此外還有一組以a
為前綴的的操作指令,分別為aput
和aget
,不過(guò)它們應(yīng)該不算在字段的范疇了,應(yīng)該為數(shù)組操作范疇,但因?yàn)橐彩呛妥x寫(xiě)操作有關(guān),所以就寫(xiě)在這里了,具體格式如下
aput-object v2,v1,v0
其具體作用為將v2
的值放入到v1
數(shù)組的v0
位置處.所以可以看出,v2
為要放入的值,v1
代表著存放v2
值的數(shù)組,而v0
則是v2
要存放在數(shù)組的位置,即v0
為index(數(shù)組角標(biāo)).
方法調(diào)用指令
方法調(diào)用指令賦值調(diào)用類(lèi)實(shí)例(也就是對(duì)象)的方法,它的基礎(chǔ)指令為invoke
.指令格式如下
invoke-virtual(名稱(chēng)后綴可替換) {v0,v1},method@BBBB(具體的方法)
其中{v0,v1}
大括號(hào)中第一位放的是調(diào)用方法的對(duì)象,之后的為方法中的參數(shù).若沒(méi)有參數(shù)則只需傳入調(diào)用方法的對(duì)象,即{v0}
.
指令 | 作用 |
---|---|
invoke-virtual 或invoke-virtual/range
|
調(diào)用實(shí)例的虛方法. |
invoke-super 或invoke-super/range
|
調(diào)用實(shí)例父類(lèi)的方法. |
invoke-direct 或invoke-direct/range
|
調(diào)用實(shí)例的直接方法. |
invoke-static 或invoke-static/range
|
調(diào)用實(shí)例的靜態(tài)方法. |
invoke-interface 或invoke-interface/range
|
調(diào)用實(shí)例的接口方法. |
數(shù)字轉(zhuǎn)換指令
數(shù)據(jù)轉(zhuǎn)換指令用于將一種類(lèi)型的數(shù)值轉(zhuǎn)換成另一種類(lèi)型.格式如下
neg-int(可替換如下) v0,v1
指令中,v1
存放需要轉(zhuǎn)換的數(shù)據(jù),v0
存放轉(zhuǎn)換后的結(jié)果.
指令 | 作用 |
---|---|
neg-int |
對(duì)整型輸求補(bǔ). |
not-int |
對(duì)整型輸求反. |
neg-long |
對(duì)長(zhǎng)整型數(shù)求補(bǔ). |
not-long |
對(duì)長(zhǎng)整型數(shù)求反. |
neg-float |
對(duì)單精度浮點(diǎn)型數(shù)求補(bǔ). |
neg-double |
對(duì)雙精度浮點(diǎn)數(shù)求補(bǔ). |
int-to-long |
將整型數(shù)轉(zhuǎn)換為長(zhǎng)整型. |
int-to-float |
將整型數(shù)轉(zhuǎn)換為單精度浮點(diǎn)型. |
int-to-double |
將整型數(shù)轉(zhuǎn)換為雙精度浮點(diǎn)型. |
long-to-int |
將長(zhǎng)整型數(shù)轉(zhuǎn)換位整型. |
long-to-float |
將長(zhǎng)整型數(shù)轉(zhuǎn)換為單精度浮點(diǎn)型. |
long-to-double |
將長(zhǎng)整型數(shù)轉(zhuǎn)換為雙精度浮點(diǎn)型. |
float-to-int |
將單精度浮點(diǎn)轉(zhuǎn)換為整型. |
float-to-long |
將單精度浮點(diǎn)型轉(zhuǎn)換為長(zhǎng)整型. |
float-to-double |
將單精度浮點(diǎn)型轉(zhuǎn)換為雙精度浮點(diǎn)型. |
double-to-int |
將雙精度浮點(diǎn)型轉(zhuǎn)換為整型. |
double-to-long |
將雙精度浮點(diǎn)型轉(zhuǎn)換為長(zhǎng)整型. |
double-to-float |
將雙精度浮點(diǎn)型轉(zhuǎn)換為單精度浮點(diǎn)型. |
int-to-byte |
將整型轉(zhuǎn)換為字節(jié)型. |
int-to-char |
將整型轉(zhuǎn)換為字符串. |
int-to-short |
將整型轉(zhuǎn)換為短整型. |
數(shù)據(jù)運(yùn)算指令
數(shù)據(jù)運(yùn)算指令分為算術(shù)運(yùn)算指令和邏輯運(yùn)算指令,即 加昼捍、減、乘迁筛、除煤蚌、取模、位移及與细卧、或尉桩、非、異或等.
格式如下
add-int(可替換如下) v0,v1,v2
指令中,將v1
和v2
進(jìn)行運(yùn)算,結(jié)果存到v0
.
指令 | 作用 |
---|---|
add-type |
將v1 和v2 進(jìn)行加法運(yùn)算,即v1+v2 . |
sub-type |
將v1 和v2 進(jìn)行減法運(yùn)算,即v1-v2 . |
mul-type |
將v1 和v2 進(jìn)行乘法運(yùn)算,即v1*v2 . |
div-type |
將v1 和v2 進(jìn)行除法運(yùn)算,即v1/v2 . |
rem-type |
將v1 和v2 進(jìn)行取模運(yùn)算,即v1%v2 . |
and-type |
將v1 和v2 進(jìn)行與運(yùn)算,即v1 AND v2 . |
or-type |
將v1 和v2 進(jìn)行或運(yùn)算,即v1 OR v2 . |
xor-type |
將v1 和v2 進(jìn)行異或運(yùn)算,即v1 XOR v2 . |
shl-type |
將v1 進(jìn)行(有符號(hào)位)左移v2 位,即v1<<v2 . |
shr-type |
將v1 進(jìn)行(有符號(hào)位)右移v2 位,即v1>>v2 . |
ushr-type |
將v1 進(jìn)行(無(wú)符號(hào)位)右移v2 位,即v1>>v2 . |
其中后面的-type
可以是-int贪庙、-long蜘犁、-float、-double
.
至此,Dalvik指令集基本就都介紹完了