Smali語法學習摘要
一、Smali 簡介
首先,提到smali就先說下逆向,逆向通常是安全工程師(逆向工程師),系統分析三方APP以及做破解等惡意分子因爲某些利益在做(apk二次打包插入廣告、破解收費應用、惡意代碼植入、剽竊api等)的一種手段。
當然技術是一把雙刃劍,在於使用技術的人而不再技術本身。
迴歸正題,Smali是一種寬鬆式的Jasmin/dedexer語法,是Davlik的寄存器語言,語法上和彙編語言相似,Dalvik VM與JVM的最大的區別之一就是Dalvik VM是基於寄存器的。基於寄存器的意思是,在smali裏的所有操作都必須經過寄存器來進行。
Smali,Baksmali分別是冰島語中編譯器,反編譯器的叫法。也許你會問爲什麼是冰島語呢,因爲Dalvik是一個冰島漁村名字。
二、Smali 語法
1. 數據類型
Davlik字節碼中,寄存器都是32位的,能夠支持任何類型,64位類型(Long/Double)用2個寄存器表示。
Dalvik字節碼有兩種類型:基本類型;引用類型(包括對象和數組)。
(1)基本類型
type | 解釋 |
---|---|
V | void只能用於返回值類型 |
Z | boolean |
B | byte |
S | short |
C | char |
I | int |
J | long(64位) |
F | float |
D | double(64位) |
(2)引用類型
type | 解釋 |
---|---|
L | 對象類型(Lpackage/ObjectName; 相當於java中的package.ObjectName;) |
[I | 表示一個整形的一維數組,相當於java的int[]; |
[Ljava/lang/String | 表示一個String的對象數組 |
① 對象類型:
“L“:表示這是一個對象類型
”package/ObjectName“:該對象所在的包與類名,比如Ljava/lang/String =>java.lang.String
”;“:表示對象名稱的結束
② 數組的表示形式:
” [I “ :表示一個整形的一維數組,相當於java的int[];
對於多維數組,只要增加”[“ 就行了,[[I => int[][]; 注:每一維最多255個;
③ 對象數組的表示形式:
[Ljava/lang/String 表示一個String的對象數組;
2. 基礎語法
2.1 表達式
Java源代碼:
public void smaliExpression(){
//加法運算
int a = 1;
double b = 2.5;
double c = a + b;
//減法運算
double d = b - a;
//乘法運算
double e = a * b;
//除法運算
double f = b / a;
//異或運算
int g = 3;
int h = a ^ g;
//三目運算
int i = a > b?a:g;
}
Smali代碼:
.method public smaliExpression()V
.locals 15
.line 16
const/4 v0, 0x1 #1
.line 17
.local v0, "a":I
const-wide/high16 v1, 0x4004000000000000L # 2.5
.line 18
.local v1, "b":D
int-to-double v3, v0 //將int型的 1 強轉爲double的 1.0
add-double/2addr v3, v1 //兩個double類型相加
.line 21
.local v3, "c":D
int-to-double v5, v0
sub-double v5, v1, v5 //減法 V5 = V1-V5
.line 24
.local v5, "d":D
int-to-double v7, v0
mul-double/2addr v7, v1 //乘法
.line 27
.local v7, "e":D
int-to-double v9, v0
div-double v9, v1, v9 //除法
.line 30
.local v9, "f":D
const/4 v11, 0x3
.line 31
.local v11, "g":I
xor-int v12, v0, v11 //異或
//異或語句,代條件語句
.line 34
.local v12, "h":I
int-to-double v13, v0
cmpl-double v13, v13, v1 //cmpl-double 比較
if-lez v13, :cond_0
move v13, v0
goto :goto_0
:cond_0
move v13, v11 //將寄存器v11值給v13
.line 35
.local v13, "i":I
:goto_0
return-void
.end method
整理如下:
java運算符 | samli運算符 |
---|---|
加法 | add-double/2addr |
減法 | sub-double |
乘法 | mul-double/2addr |
除法 | div-double |
異或 | xor-int |
三目運算 | 條件語句 |
可以看到三目運算符本身就是條件語句,所以我們看下詳細的條件語句是怎麼樣的。
2.2 條件語句
先上對比代碼。
java:
public void smaliIf(){
int a = 1, b = 2;
int c = 0;
if(a > b){
c = a;
}
if(a < b){
c = b;
}
if(a>=b){
c = a;
}
if(a <= b){
c = b;
}
if(a==b){
c = a;
}
if(a != b){
c = b;
}
}
samli:
.method public smaliIf()V
.locals 3
.line 38
const/4 v0, 0x1
.local v0, "a":I
const/4 v1, 0x2
.line 39
.local v1, "b":I
const/4 v2, 0x0
.line 40
.local v2, "c":I
if-le v0, v1, :cond_0
.line 41
move v2, v0
.line 43
:cond_0
if-ge v0, v1, :cond_1
.line 44
move v2, v1
.line 46
:cond_1
if-lt v0, v1, :cond_2
.line 47
move v2, v0
.line 49
:cond_2
if-gt v0, v1, :cond_3
.line 50
move v2, v1
.line 52
:cond_3
if-ne v0, v1, :cond_4
.line 53
move v2, v0
.line 55
:cond_4
if-eq v0, v1, :cond_5
.line 56
move v2, v1
.line 58
:cond_5
return-void
.end method
歸納總結整理如下:
“if-eq vA, vB, :cond_*” 如果vA等於vB則跳轉到:cond_*,否則繼續向下執行
“if-ne vA, vB, :cond_*” 如果vA不等於vB則跳轉到:cond_*,否則繼續向下執行
“if-lt 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-ltz 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_*,否則繼續向下執行
其中的:cond_* 中 “ * ” 是編號(代表1,2,3.。。),在一個方法裏如果有多個條件則這個編號不可以重複。
2.3 循環語句
還是一樣,先上對照代碼:
java:
public void smaliWhile(){
//while
int a = 0;
while(a<=3){
a++;
}
//for
int b = 0;
for(int i = 0;i<3;i++){
b++;
}
//do...while
int c = 0;
do{
c++;
}while (c <= 3);
}
smali:
.method public smaliWhile()V
.locals 5
.line 62
const/4 v0, 0x0
move v1, v0
.line 63
.local v1, "a":I
:goto_0
const/4 v2, 0x3
if-gt v1, v2, :cond_0 //如果a > 3跳轉 cond_0
.line 64
add-int/lit8 v1, v1, 0x1 //上面if條件不存在,a自增1
goto :goto_0 //循環主體,繼續從goto_0向下執行
.line 68
:cond_0
const/4 v3, 0x0
.line 69
.local v3, "b":I
move v4, v3
move v3, v0
.local v3, "i":I
.local v4, "b":I
:goto_1
if-ge v3, v2, :cond_1 // i >= 3
.line 70
add-int/lit8 v4, v4, 0x1 // b++;
.line 69
add-int/lit8 v3, v3, 0x1 //i++;
goto :goto_1 //循環主體
.line 74
.end local v3 # "i":I
:cond_1
nop //表示空操作,什麼都不幹
.line 76
.local v0, "c":I
:cond_2
add-int/lit8 v0, v0, 0x1
.line 77
if-le v0, v2, :cond_2
.line 78
return-void
.end method
所以循環關鍵點在於循環體開始地方使用 :goto_* 標識,執行循環的地方使用 goto :goto_*,判斷是否跳出循環仍然使用上面我們將的條件語句。
2.4 try-catch語句
繼續上代碼
java:
public void smaliTryCatch(){
Object a = null;
try {
a = null;
}catch (Exception e){
a.toString();
}finally {
a = new Object();
}
}
smali:
.method public smaliTryCatch()V
.locals 2
.line 81
const/4 v0, 0x0
.line 83
.local v0, "a":Ljava/lang/Object;
const/4 v0, 0x0
.line 87 //v1: new Object();
new-instance v1, Ljava/lang/Object;
invoke-direct {v1}, Ljava/lang/Object;-><init>()V
move-object v0, v1
.line 88
nop
.line 89
return-void
.end method
這裏的try-catch很奇怪,好像catch部分執行了nop也就是空操作,是smali不會去管異常嗎?我也不清楚了,如果大家有知道的可以告訴我。
這裏主要就是有一個new Object過程,new一個對象在smali中是怎麼樣的?就是上面這樣的.line87的內容。
說到這裏,基本的語法就先到這裏了,下面用一個簡單的類文件來看下smali類文件結構。
3. 類文件結構
因爲一個類文件的Smali代碼比較長,所以我們分開來解釋。
3.1 頭信息
.class public Lcom/justart/samlidemo/MainActivity;
.super Landroid/app/Activity;
.source "MainActivity.java"
.class 表示類路徑 包+類名
.super 表示父類的路徑和地址
.source 表示源碼文件名
3.2 構造方法
# direct methods
.method public constructor <init>()V
.locals 0
.line 6
invoke-direct {p0}, Landroid/app/Activity;-><init>()V
return-void
.end method
因爲源碼中我沒有重寫構造方法,所以默認的無參構造方法裏直接調用父類Activity的無參構造方法。
3.3 其他方法
這裏以Activity的onCreate方法爲例:
# virtual methods //表示是一個虛方法
.method protected onCreate(Landroid/os/Bundle;)V
.locals 1
.param p1, "savedInstanceState" # Landroid/os/Bundle;
.line 10
invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
.line 11
const/high16 v0, 0x7f050000
invoke-virtual {p0, v0}, Lcom/justart/samlidemo/MainActivity;->setContentView(I)V
.line 12
return-void
.end method
(1)方法以 .method開始, .end method 結束;
(2)方法第一行最後 V表示返回類型爲void,其他返回類型見第一節數據類型;
(3)方法參數也遵循smali數據類型,這裏表示參數是一個Bundle對象類型;
(4).param 表示 方法的參數,默認參數使用p0表示;
(5)最後是方法的返回類型 這裏表示返回void。
3.4 附錄
下面簡單總結一下類中常用的一些關鍵詞:
關鍵詞 | 說明 |
---|---|
.class | 定義類類型 包名+類名 |
.super | 定義父類的路徑和地址 |
.source | 表示源碼文件名 |
filed | 定義字段 |
.method…end method | 定義方法 |
.annotation…end annotation | 定義註解 |
.implements | 定義接口指令 |
.local | 指定了方法內局部變量的個數 |
.registers | 指定方法內使用寄存器的總數 |
.prologue | 表示方法中代碼的開始處 |
.line | 表示java源文件中指定行 |
.paramter .param | 指定了方法的參數 |
三、總結
文章寫得比較粗糙,希望大家給點建議,有錯誤的地方可以指出來,謝謝大家!
說下我的學習感受吧,梳理一遍smali語法,發現同時有助於瞭解java代碼的執行流程,也可以瞭解寄存器相關的入門知識,同時熟悉smali之後可以調試三方或者修改APP。簡單的修改三方APP代碼可以參考我的另外一篇文章反編譯三方apk並添加debug log。