Smali語法詳解
apk文件通過apktool反編譯出來的都有一個smali文件夾,裏面都是以.smali結尾的文件。
smali語言是Davlik的寄存器語言,語法上和彙編語言相似,Dalvik VM與JVM的最大的區別之一就是Dalvik VM是基於寄存器的。基於寄存器的意思是,在smali裏的所有操作都必須經過寄存器來進行。
寄存器是什麼
在smali所有的操作都必須經過寄存器來進行:本地寄存器用v開頭數字結尾的符號來表示,如v0,v1,v2…,參數寄存器用p開頭數字結尾表示,如:p0,p1,p2…。特別注意的是,p0不一定是函數中的第一個參數,在非static函數中,p0代指“this”,p1表示函數的第一個參數,p2表示第二個參數,而在static函數中p0纔對應第一個參數(因爲java的static方法中沒有this方法)
const/4 v0, 0x0
iput-boolean v0,p0,Lcom/aa;->IsRegistered:Z
上段表示的是:把0x0存寄到v0中,把這個值存放進com.aa.IsRegistered這個變量中,相當於this.IsRegistered=true,在非靜態方法中p0代表的是this,不是參數。
基本類型定義
Java Type | Type descriptor |
---|---|
boolean | Z |
char | C |
byte | B |
short | S |
int | I |
float | F |
long | J |
double | D |
Object | Ljava/lang/Object; |
int[] | [I |
Object[][] | [[Ljava/lang/object; |
內部類 | Lpackagename/objectname&subobjectname |
數組表示方式在 基本數據類型前加“[”符號,對象的則表示以L作爲開頭格式爲Ljava/lang/Object; 分號在在後面。 Ljava/lang/String;其中java/lang對應java.lang包,String就是對應的該包中的對象。
方法定義:FuncName(para-type para-type1)ReturnType
- hello(III)I ->int hello(int,int,int)方法返回的是int型
- hello(J[I[Ljava/lang/String;ZLjava/lang/String) Z->blooean hello(long,string[],blooean,string) 方法返回的是blooean型
smali基本語法
- .field private isflag:Z 定義變量爲 blooean isflag
- .method 方法
- .parameter 方法參數
- .prologue 方法開始 序幕
- .line123 此方法位於123行
- const/high16 v0,0x7fo3 把0x7fo3賦值給v0
- invoke-direct 調用private函數
- invoke-virtual 調用除private其它函數
- invoke-static 調用靜態函數
- invoke-super 調用父函數
- invoke-interface 調用接口
- return-void 函數返回void
- .end method 函數結束
- new-instance 創建實例
- iput-object 對象賦值
- iget-object 調用對象
條件跳轉分支
- "if-eq vA,vB, :cond_ ** " 如果vA等於vB,則跳轉到:cond_ **
- "if-ne vA,vB, :cond_ ** " 如果vA不等於vB,則跳轉到:cond_ **
- "if-It vA,vB, :cond_ ** " 如果vA小於vB,則跳轉到:cond_ **
- "if-ge vA,vB, :cond_ ** " 如果vA大於等於vB,則跳轉到:cond_ **
- "if-gt vA,vB, :cond_ ** " 如果vA大於vB,則跳轉到:cond_ **
- "if-le vA,vB, :cond_ ** " 如果vA小於等於vB,則跳轉到:cond_ **
- "if-eqz vA, :cond_ ** " 如果vA等於0,則跳轉到:cond_ **
- "if-nez vA, :cond_ ** " 如果vA不等於0,則跳轉到:cond_ **
- "if-Itz vA, :cond_ ** " 如果vA小於0,則跳轉到:cond_ **
- "if-gez vA, :cond_ ** " 如果vA大於等於0,則跳轉到:cond_ **
- "if-gtz vA, :cond_ ** " 如果vA大於0,則跳轉到:cond_ **
- "if-lez vA, :cond_ ** " 如果vA小於等於0,則跳轉到:cond_ **
if-eq p1, v0, :cond_8
:cond_8
invoke-direct {p0}, Lcom/paul/test/a;->d()V
上段表示如果p1和v0相等,則執行cond_8的流程:調用com.paul.test.a的d()方法
if-ne p1, v0, :cond_b
:cond_b
const/4 v0, 0x0
invoke-virtual {p0, v0}, Lcom/paul/test/a;->setPressed(Z)V
invoke-super {p0, p1, p2}, Landroid/view/View;->onKeyUp(ILandroid/view/KeyEvent;)Z
move-result v0
上段表示p1不等於p0,跳轉到:cond_b。把0x0賦值給v0,執行com/paul/test/a類中的void setPressed(blooean )公有方法。再調用View中的onkeyUp方法。返回一個基本數據類型。
move-result(返回基本數據類型)和move-result-object(返回對象)指令獲取返回結果
包信息:
- .class public Lcom/test/reversetest/MainActivity;
- .super Landroid/support/v7/app/AppCompatActivity;
- .source “MainActivity.java”
這是一個由MainActivity.java類編譯得到smali文件,它是com/test/reversetest/MainActivity下的一個類同時該類繼承了android/support/v7/app/AppCompatActivity
samli聲明:
- #annotations
- .annotation system Ldalvik/annotation/MemberClasses;
values={
Lcom/aaa&qqq;,
Lcom/aaa&www;
}
.end annotation
這個聲明是內部類的聲明:aaa 這個類有2個成員內部類qqq和www
成員變量格式:.field public /private[static][final]varName:<類型>。
- 對於不同的成員變量也有不同的指令
- 獲取的指令有:iget,sget,iget-boolean,sget-boolean,iget-object,sget-object。
- 操作的指令有:iget,sput,iput-boolean,sput-boolean,iput-object,sput-boolean,iput-object,sput-object等。
- 沒有 “-object” 表示成員變量對象是基本數據類型,帶了就是對象類型。特別的是,boolean類型則帶-boolean。
sget-object v0,Lcom/aaa;->ID:Ljava/lang/String;
sget-object用來獲取變量值並保存到挨着的參數寄存器中,它獲取的ID這個String類型的成員變量放到v0中。
注意:前面需要該變量所屬的類的類型,後面需要加冒號和該成員變量的類型,中間是 "->"表示所屬關係。
iget-object v0,p0,Lcom/aaa;->ID:Ljava/lang/String;
iget-object 比sget-object多了一個p0參數表示“this”,獲取array我們用aget和aget-object和上述指令一致。
put 指令和get指令是統一的:
const/4 v3, 0x0
sput-object v3,Lcom/aaa;->timer:Lcom/aaa/timer;
相當於:this.timer=null
這是因爲是object所以是null,,
sput-boolean v3,Lcom/aaa;->timer:Z;
相當於:this.timer=true
.local v0,args:Landroid/os/Message;
const/4 v1,0x12
iput v1,v0,Landroid/os/Message;->what:I
相當於 args.what=18;
函數的調用
smali 中的函數與變量分爲2種類型,分別爲direct和virtual。direct method就是private函數,其餘的函數都是virtual method。
- invoke-static:調用static函數。例如:
invoke-static{},Lcom/aaa;->Check()Z,
這裏invoke-static後有一對“{}”,其實調用的是該方法的實例+參數列表,由於這個方法既不需參數也是static的,所以{}爲空。
const-string v0,"NDK"
invoke-static{v0},Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
這個調用static void System.loadLibrary(v0)來編譯NDK的。
2.invoke-super:調用父類方法的指令,通常是onCreat,Ondestory。
3.invoke-direct:調用private函數。
4.invoke-virtual :調用非private函數。修改smali中不要錯用direct/virtual函數。
5.invoke-xxx/range:當方法的參數多餘5個(含5個),不能直接使用以上指令,而是在後面加上"/range",表示範圍。 invoke-direct/range{v0…v5},Lcom.aaa;->h(IIIIIZ)Z
函數返回的結果調用
在java代碼中,調用函數和返回函數結果可以用一條語句完成,而在Smali裏則需要分開來完成,在上面指令後,如果調用的函數返回非void,那麼還需要用move-result(返回基本數據類型)和move-result-object(返回對象)指令:
const-string v0,"eric"
invoke-static{v0},Lcom/aaa;->t(Ljava/lang/String;)Ljava/lang/String;
move-result-object v2
v2 保存的是t方法中的返回的String字符串
分析一段代碼
.method private ifRegister()Z
.locals 2 //有2個本地寄存器
.prologue //開始
const/4 v0, 0x1 //v0賦值爲1
.locals v0,tempFlag:Z
if-eqz v0,:cond_0
const/4 v1,0x1
:goto_0 //標籤
return v1 //返回v1的值
:cond_0 //標籤
const/4 v1,0x0
goto :goto_0 //跳到goto_0執行,返回v1的值,這裏可以改成retutn v1
.end method