Smali是什么
Smali是Android虛擬機(jī)的反匯編語(yǔ)言。
我們都知道,Android代碼一般是用java編寫(xiě)的,執(zhí)行java程序一般需要用到j(luò)ava虛擬機(jī),在Android平臺(tái)上也不例外,但是出于性能上的考慮,并沒(méi)有使用標(biāo)準(zhǔn)的JVM,而是使用專(zhuān)門(mén)的Android虛擬機(jī)(5.0以下為Dalvik,5.0以上為ART)。Android虛擬機(jī)的可執(zhí)行文件并不是普通的class文件,而是再重新整合打包后生成的dex文件。dex文件反編譯之后就是Smali代碼,所以說(shuō),Smali語(yǔ)言是Android虛擬機(jī)的反匯編語(yǔ)言。
掌握Smali有哪些好處
1、動(dòng)態(tài)調(diào)試APK,通常靜態(tài)分析APK是不夠的,如果需要徹底分析APK的執(zhí)行邏輯,需要通過(guò)動(dòng)態(tài)調(diào)試來(lái)進(jìn)行。
具體教程參考:http://blog.csdn.net/hanchaohao2012/article/details/63253725
2、修改APK運(yùn)行邏輯,通過(guò)修改Smali代碼,再重新編譯打包成新的APK,是Android逆向的基本操作。
Smali基本語(yǔ)法
數(shù)據(jù)類(lèi)型
Smali | Java | 備注 |
---|---|---|
v | void | 只能用于返回值類(lèi)型 |
Z | boolean | |
B | byte | |
S | short | |
C | char | |
I | int | |
J | long | |
F | float | |
D | double | |
Lpackage/name; | 對(duì)象類(lèi)型 | L表示這是一個(gè)對(duì)象類(lèi)型,package表示該對(duì)象所在的包,;表示對(duì)象名稱的結(jié)束 |
[類(lèi)型 | 數(shù)組 | [I表示一個(gè)int型數(shù)據(jù),[Ljava/lang/String 表示一個(gè)String的對(duì)象數(shù)組 |
語(yǔ)法關(guān)鍵詞
關(guān)鍵詞 | 說(shuō)明 |
---|---|
.class | 定義java類(lèi)名 |
.super | 定義父類(lèi)名 |
.source | 定義Java源文件名 |
.filed | 定義字段 |
.method | 定義方法開(kāi)始 |
.end method | 定義方法結(jié)束 |
.annotation | 定義注解開(kāi)始 |
.end annotation | 定義注解結(jié)束 |
.implements | 定義接口指令 |
.local | 指定了方法內(nèi)局部變量的個(gè)數(shù) |
.registers | 指定方法內(nèi)使用寄存器的總數(shù) |
.prologue | 表示方法中代碼的開(kāi)始處 |
.line | 表示java源文件中指定行 |
.paramter | 指定了方法的參數(shù) |
.param | 和.paramter含義一致,但是表達(dá)格式不同 |
寄存器
Java中變量都是存放在內(nèi)存中的,Android為了提高性能,變量都是存放在寄存器中的,寄存器為32位,可以支持任何類(lèi)型。
為什么寄存器比內(nèi)存快,可以參考這篇文章:http://www.ruanyifeng.com/blog/2013/10/register.html
寄存器分為如下兩類(lèi):
1、本地寄存器
用v開(kāi)頭數(shù)字結(jié)尾的符號(hào)來(lái)表示,v0, v1, v2,...
2、參數(shù)寄存器
用p開(kāi)頭數(shù)字結(jié)尾的符號(hào)來(lái)表示,p0,p1,p2,...
注意:
在非static方法中,p0代指this,p1為方法的第一個(gè)參數(shù)。
在static方法中,p0為方法的第一個(gè)參數(shù)。
Smali代碼示例:
const/4 v0, 0x1 //把值0x1存到v0本地寄存器
iput-boolean v0,p0,Lcom/aaa;->IsRegisterd:Z //把v0中的值賦給com.aaa.IsRegistered,p0代表this,相當(dāng)于this.Isregistered=true
成員變量
成員變量定義格式為:
.field public/private [static][final] varName:<類(lèi)型>
獲取指令
iget, sget, iget-boolean, sget-boolean, iget-object, sget-object
操作指令
iput, sput, iput-boolean, sput-boolean, iput-object, sput-object
array的操作是aget和aput
指令解析
sget-object v0,Lcom/aaa;->ID:Ljava/lang/String;
獲取ID這個(gè)String類(lèi)型的成員變量并放到v0這個(gè)寄存器中
iget-object v0,p0,Lcom/aaa;->view:Lcom/aaa/view;
iget-object比sget-object多一個(gè)參數(shù)p0,這個(gè)參數(shù)代表變量所在類(lèi)的實(shí)例。這里p0就是this
Smali代碼示例1:
const/4 v3, 0x0
sput-object v3, Lcom/aaa;->timer:Lcom/aaa/timer;
相當(dāng)于java代碼:this.timer = null;
Smali代碼示例2:
.local v0, args:Landroid/os/Message;
const/4 v1, 0x12
iput v1,v0,Landroid/os/Message;->what:I
相當(dāng)于java代碼:args.what = 18;
其中args為Message的實(shí)例
函數(shù)
函數(shù)定義格式為:
.method public/private [static][final] methodName()<類(lèi)型>
.end method
Smali代碼示例:
.method private ifRegistered()Z
.locals 2 // 本地寄存器的個(gè)數(shù)
.prologue
const/4 v0, 0x1 //v0賦值為1
if-eqz v0, :cond_0 //判斷v0是否等于0,等于0則跳到cond_0執(zhí)行
const/4 v1, 0x1 //符合條件分支
:goto_0 //標(biāo)簽
return v1 //返回v1的值
:cond_0 //標(biāo)簽
const/4 v1, 0x0 //cond_0分支
goto :goto_0 //跳到goto_0執(zhí)行
.end method
函數(shù)分為兩類(lèi):direct method和virtual method
direct method就是private方法,virtual method就是指其余的方法。
調(diào)用指令:
invoke-direct
invoke-virtual
invoke-static
invoke-super
invoke-interface
調(diào)用格式:
invoke-指令類(lèi)型 {參數(shù)1, 參數(shù)2,...}, L類(lèi)名;->方法名
如果不是是靜態(tài)方法,參數(shù)1代表調(diào)用該方法的實(shí)例。
Smali代碼示例:
const-string v0, "NDKLIB"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
相當(dāng)于java代碼:System.loadLibrary("NDKLIB")
函數(shù)返回結(jié)果
Smali需要用指令move-result或move-result-object來(lái)保存函數(shù)返回的結(jié)果
Smali代碼示例:
const-string v0, "Eric"
invoke-static {v0}, Lcmb/pbi;->t(Ljava/lang/String;)Ljava/lang/String;
move-result-object v2
表示將方法t返回的String對(duì)象保存到v2中。