一、對照 Java 代碼與 Smali 代碼差異
1.1?方法調用差異:Java vs Smali
Java 方法分類:
方法類型 | Java 示例 | Smali 指令 | 特點說明 |
---|---|---|---|
靜態方法 | Utils.print("hi") | invoke-static | 沒有 this 指針 |
實例方法 | obj.show() | invoke-virtual | 有 this,虛函數調用 |
構造函數 | new Person() | invoke-direct | 用于構造對象 |
私有方法 | this.hidden() | invoke-direct | 調用私有方法 |
接口方法 | list.size() | invoke-interface | 調用接口方法 |
父類方法 | super.toString() | invoke-super | 調用父類方法 |
動態調用(API) | Java 8 lambda 等 | invoke-polymorphic | 用于一些特殊函數,如反射調用 |
示例對照
1)靜態方法調用
Java
Logger.log("test");
Smali
const-string v0, "test"
invoke-static {v0}, Lcom/example/Logger;->log(Ljava/lang/String;)V
2)實例方法調用
Java
User u = new User();
u.say("hello");
Smali
new-instance v0, Lcom/example/User;
invoke-direct {v0}, Lcom/example/User;-><init>()Vconst-string v1, "hello"
invoke-virtual {v0, v1}, Lcom/example/User;->say(Ljava/lang/String;)V
3)構造器調用
構造器就是特殊方法 <init>
Java
Person p = new Person();
Smali
new-instance v0, Lcom/example/Person;
invoke-direct {v0}, Lcom/example/Person;-><init>()V
1.2?控制流語句差異:Java vs Smali
if-else 語句
Java
if (a > b) {doA();
} else {doB();
}
Smali
# a = v0, b = v1
if-le v0, v1, :else_block
invoke-static {}, Lcom/example/Utils;->doA()V
goto :end:else_block
invoke-static {}, Lcom/example/Utils;->doB()V:end
常用比較指令:
指令 | 條件 |
---|---|
if-eq | 等于(==) |
if-ne | 不等(!=) |
if-lt | 小于(<) |
if-gt | 大于(>) |
if-le | 小于等于(≤) |
if-ge | 大于等于(≥) |
switch 語句
Java
switch (x) {case 1: f1(); break;case 5: f5(); break;default: fDefault();
}
Smali
sparse-switch v0, :switch_data # v0 是 switch 的變量
goto :default # 如果沒匹配任何 case,跳轉 default:switch_data.sparse-switch0x1 -> :case10x5 -> :case5.end sparse-switch:case1invoke-static {}, Lcom/example/Util;->f1()Vgoto :end:case5invoke-static {}, Lcom/example/Util;->f5()Vgoto :end:defaultinvoke-static {}, Lcom/example/Util;->fDefault()V:end# 后續邏輯繼續執行
while 循環
Java
int i = 0;
while (i < 10) {i++;
}
Smali
const/4 v0, 0x0 # i = 0
:loop_start
const/4 v1, 0xA
if-ge v0, v1, :loop_endadd-int/lit8 v0, v0, 1
goto :loop_start:loop_end
說明:
-
if-ge v0, v1, :loop_end
:當 i ≥ 10 跳出循環 -
goto
:用于跳轉控制流
1.3?類字段與方法修飾符差異
字段定義差異
Java
public int count;
private String name;
Smali
.field public count:I
.field private name:Ljava/lang/String;
字段類型縮寫表:
Java 類型 | Smali 類型 |
---|---|
int | I |
boolean | Z |
byte | B |
char | C |
short | S |
long | J |
float | F |
double | D |
對象 | L類路徑; |
字段訪問指令
Java
this.count = 1;
int x = this.count;
Smali
const/4 v1, 0x1
iput v1, v0, Lcom/example/MyClass;->count:Iiget v2, v0, Lcom/example/MyClass;->count:I
說明:
-
iput
= instance put,寫入字段 -
iget
= instance get,讀取字段
方法定義與修飾符
Java
public void print(int n) { ... }
private int getNum() { return num; }
Smali
.method public print(I)V.locals 1# ...代碼
.end method.method private getNum()I.locals 1# ...代碼
.end method
Java 修飾符 | Smali 前綴 |
---|---|
public | public |
private | private |
protected | protected |
static | static |
final | final |
abstract | abstract |
synchronized | synchronized |
native | native |
1.4?總結:Java ? Smali 快速對照表
Java 表達式 | Smali 表達 | 說明 |
---|---|---|
靜態方法調用 | invoke-static | 無 this |
實例方法調用 | invoke-virtual | 用 this |
構造函數調用 | invoke-direct | <init>() 構造器 |
if 條件控制 | if-eq / if-lt / if-ge 等 | 條件跳轉 |
switch 分支 | .sparse-switch / .packed-switch | 分支跳轉表 |
字段定義 | .field public/private 名:類型 | 類成員變量 |
字段訪問 | iget / iput | 實例字段讀寫 |
方法定義 | .method public 名(參數)返回值 | 類方法定義 |
二、方法調用
2.1?Smali 方法調用指令分類
指令 | 調用類型 | 用途(Java 等價) |
---|---|---|
invoke-static | 靜態方法 | ClassName.method() |
invoke-virtual | 實例方法(常用) | obj.method() |
invoke-direct | 構造方法、私有方法 | new Obj() ,this.privateMethod() |
invoke-super | 調用父類方法 | super.method() |
invoke-interface | 接口方法 | interfaceObj.method() |
invoke-virtual/range 等 | 處理參數多的 | 用于參數超過 5 個時 |
2.2?基礎格式說明
通用格式:
invoke-xxx {參數寄存器列表}, L類名;->方法名(參數類型)返回類型
說明:
-
{}
中寫的是使用的寄存器(如{v0, v1}
) -
L類名;
是類路徑(如Ljava/lang/String;
) -
(參數類型)
:方法的參數簽名 -
返回類型
:返回值類型(V
表示 void)
2.3?詳細講解每個指令
1)invoke-static
:調用靜態方法
Java 中 Class.method()
,如 Math.abs(-1)
示例:
invoke-static {v0}, Ljava/lang/Math;->abs(I)I
說明:
-
調用
Math.abs(int)
方法 -
v0
是傳入的參數 -
(I)I
表示:參數是int
,返回也是int
特點:
-
不需要實例對象
-
類方法、工具方法、
public static
方法都用這個調用
2)invoke-virtual
:調用實例方法(最常用)
Java 中 obj.method()
,例如 str.length()
示例:
invoke-virtual {v0}, Ljava/lang/String;->length()I
說明:
-
v0
是一個String
對象 -
調用它的
length()
方法 -
()
:無參數,返回int
特點:
-
用于非私有的實例方法(包括
public
、protected
) -
支持虛方法分發(多態)
3)invoke-direct
:調用構造方法、私有方法
Java 中 new MyObject()
或 this.privateMethod()
示例 1:調用構造函數
new-instance v0, Lcom/example/MyClass;
invoke-direct {v0}, Lcom/example/MyClass;-><init>()V
說明:
-
v0
是要構造的對象 -
調用其構造器
MyClass()
-
<init>()V
是構造函數簽名(返回類型是 void)
示例 2:調用私有方法
invoke-direct {v0}, Lcom/example/MyClass;->myPrivateMethod()V
特點:
-
只能調用當前類的 private 方法 或 構造函數
-
無法用于調用繼承的或 public/protected 方法
補充:invoke-super
調用父類的方法,通常在子類中使用 super.method()
示例:
invoke-super {v0}, Lcom/example/ChildClass;->toString()Ljava/lang/String;
補充:invoke-interface
調用接口方法(Java 接口)
示例:
invoke-interface {v0}, Ljava/util/List;->size()I
2.4?幾個重要的調用場景示例
調用靜態方法(工具類)
Utils.sayHello();
invoke-static {}, Lcom/example/Utils;->sayHello()V
調用實例方法(普通對象方法)
myObj.getName();
invoke-virtual {v0}, Lcom/example/MyClass;->getName()Ljava/lang/String;
調用構造器(創建對象)
new MyClass();
new-instance v0, Lcom/example/MyClass;
invoke-direct {v0}, Lcom/example/MyClass;-><init>()V
調用私有方法(類內部)
this.secret();
invoke-direct {v0}, Lcom/example/MyClass;->secret()V
2.5?寄存器數量限制說明
最多可以用 5 個寄存器調用 invoke-*
,如果超過 5 個參數(例如構造函數參數很多):
-
使用
invoke-* /range
版本:
invoke-static/range {v0 .. v6}, Lcom/example/MyClass;->bigMethod(IJFLD)V
2.6 小結
指令 | 用途 | 說明 |
---|---|---|
invoke-static | 靜態方法 | 不需要對象實例 |
invoke-virtual | 實例方法(普通調用) | 可被 override(多態) |
invoke-direct | 構造方法/私有方法 | 無法 override,調用 <init> |
invoke-super | 父類方法 | 用于 super.xxx() |
invoke-interface | 接口方法 | 用于接口對象 |
三、控制流語句
3.1?if-else
結構
Java 示例:
if (v0 == 1) {Log.i("TAG", "Equal");
} else {Log.i("TAG", "Not Equal");
}
Smali 實現:
# v0 已有值
const/4 v1, 0x1
if-eq v0, v1, :equal # 如果 v0 == 1 跳轉到 :equal:not_equalconst-string v2, "TAG"const-string v3, "Not Equal"invoke-static {v2, v3}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)Igoto :end:equalconst-string v2, "TAG"const-string v3, "Equal"invoke-static {v2, v3}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I:end
Smali 中常見條件跳轉指令:
指令 | 含義 |
---|---|
if-eq | 相等跳轉 |
if-ne | 不等跳轉 |
if-lt | 小于跳轉 |
if-gt | 大于跳轉 |
if-le | 小于等于跳轉 |
if-ge | 大于等于跳轉 |
if-eqz | v == 0 跳轉(zero) |
if-nez | v != 0 跳轉 |
3.2?switch-case
Java 示例:
switch (v0) {case 1: f1(); break;case 5: f5(); break;default: fDefault();
}
Smali 實現(sparse-switch):
sparse-switch v0, :switch_data
goto :default:switch_data.sparse-switch0x1 -> :case10x5 -> :case5.end sparse-switch:case1invoke-static {}, Lcom/example/Util;->f1()Vgoto :end:case5invoke-static {}, Lcom/example/Util;->f5()Vgoto :end:defaultinvoke-static {}, Lcom/example/Util;->fDefault()V:end
packed-switch vs sparse-switch
類型 | 用法 |
---|---|
packed-switch | case 是連續整數時使用 |
sparse-switch | case 是稀疏整數時使用 |
3.3?while
循環
Java 示例:
int i = 0;
while (i < 5) {Log.d("loop");i++;
}
Smali 實現:
const/4 v0, 0x0 # i = 0:loop_startconst/4 v1, 0x5if-ge v0, v1, :loop_end # if i >= 5 -> endconst-string v2, "loop"invoke-static {v2}, Landroid/util/Log;->d(Ljava/lang/String;)Iadd-int/lit8 v0, v0, 0x1 # i++goto :loop_start:loop_end
3.4?for
循環(其實就是 while)
Java 示例:
for (int i = 0; i < 3; i++) {Log.e("for");
}
Smali 實現:
const/4 v0, 0x0 # int i = 0:for_beginconst/4 v1, 0x3if-ge v0, v1, :for_end # if i >= 3 -> endconst-string v2, "for"invoke-static {v2}, Landroid/util/Log;->e(Ljava/lang/String;)Iadd-int/lit8 v0, v0, 0x1 # i++goto :for_begin:for_end
3.5?常見跳轉邏輯總結表
Java 控制結構 | Smali 表達方式 |
---|---|
if (a == b) | if-eq a, b, :label_true |
if (a != 0) | if-nez a, :label_true |
while (...) | 標簽 :begin + 條件跳轉 + goto :begin |
for (...) | 與 while 相同結構 |
switch | sparse-switch / packed-switch + 標簽 |
四、類字段
4.1?什么是類字段
在 Java 中,字段就是類的成員變量:
public class Person {public String name;private static int count;
}
在 Smali 中,這些字段會寫成:
.field public name:Ljava/lang/String;.field private static count:I
4.2?字段的 Smali 語法結構
.field [修飾符] 字段名:類型
常見修飾符:
修飾符 | 含義 |
---|---|
public | 公有 |
private | 私有 |
protected | 受保護 |
static | 靜態字段(屬于類) |
final | 常量 |
volatile | 多線程可見性(較少見) |
transient | 不序列化(與 Java 一致) |
synthetic | 編譯器自動生成(反混淆重要) |
4.3?字段類型表示法(Smali 中)
Java 類型 | Smali 符號 |
---|---|
int | I |
boolean | Z |
byte | B |
char | C |
short | S |
long | J |
float | F |
double | D |
void | V (方法返回專用) |
String | Ljava/lang/String; |
MyClass | Lcom/example/MyClass; |
數組 | [I (int[]),[Ljava/lang/String; (String[]) |
4.4?Smali 中訪問字段
字段分為 實例字段 和 靜態字段,對應不同的訪問指令。
1)實例字段(每個對象一份)
操作 | 指令名 | 示例 |
---|---|---|
讀取字段 | iget | iget v1, v0, Lcom/demo/User;->name:Ljava/lang/String; |
寫入字段 | iput | iput v2, v0, Lcom/demo/User;->name:Ljava/lang/String; |
2)靜態字段(類級別共享)
操作 | 指令名 | 示例 |
---|---|---|
讀取字段 | sget | sget v0, Lcom/demo/User;->count:I |
寫入字段 | sput | sput v1, Lcom/demo/User;->count:I |
4.5?例子分析:完整的字段聲明與訪問流程
Java 示例:
public class User {public String name;private static int count = 0;public void setName(String n) {this.name = n;count++;}
}
Smali 對應(簡化版):
.class public Lcom/demo/User;
.super Ljava/lang/Object;.field public name:Ljava/lang/String;
.field private static count:I.method public setName(Ljava/lang/String;)V.locals 1# 把參數 n(p1)賦值給 this.nameiput-object p1, p0, Lcom/demo/User;->name:Ljava/lang/String;# sget 靜態字段sget v0, Lcom/demo/User;->count:Iadd-int/lit8 v0, v0, 0x1sput v0, Lcom/demo/User;->count:Ireturn-void
.end method
4.6?幾個實戰技巧與細節
判斷字段是靜態還是實例
特征 | 靜態字段 | 實例字段 |
---|---|---|
修飾符 | 帶 static | 不帶 static |
讀取指令 | sget / sput | iget / iput |
所屬 | 類級別共享 | 每個對象一份 |
典型用途 | 常量、計數器 | 每個對象的屬性,如 name |
Frida Hook 中字段的常用操作
// 獲取實例字段
Java.use("com.demo.User").name.value// 獲取靜態字段
Java.use("com.demo.User").count.value// 設置字段
Java.use("com.demo.User").name.value = "hack"
4.7?.field 高級用法
初始值:
.field public static final CONST:I = 0x5
表示該字段常量為 5(int
型)
4.8 小結
類型 | 聲明指令 | 訪問讀取 | 寫入修改 |
---|---|---|---|
實例字段 | .field name:Type | iget / iget-object | iput / iput-object |
靜態字段 | .field static name:Type | sget / sget-object | sput / sput-object |
五、方法訪問修飾符在 Smali 中如何體現
5.1?方法的 Smali 定義格式
.method [修飾符] 方法名(參數)返回類型
比如:
.method public static final doSomething(I)Ljava/lang/String;
這是一個:
-
public
公有 -
static
靜態 -
final
不可重寫
的方法,接受一個int
參數,返回一個String
。
5.2?常見修飾符說明(對照 Java)
Smali 修飾符 | Java 對應 | 含義解釋 |
---|---|---|
public | public | 任何類都可以調用(最常見) |
private | private | 僅類內部調用 |
protected | protected | 同包或子類中可調用 |
static | static | 屬于類本身,不依賴實例對象 |
final | final | 不可被子類重寫(繼承中常見) |
abstract | abstract | 抽象方法(無實現體,interface、abstract class 中出現) |
synthetic | 編譯器自動添加 | 編譯生成的方法(如橋接方法、Lambda 方法),用于反混淆 |
constructor | 構造函數 | 表示這是構造方法(即 <init> ) |
native | native | 本地方法,用 JNI 實現(通常沒代碼體) |
bridge | 編譯器橋接 | 泛型擦除后生成的橋接方法(一般逆不了邏輯) |
varargs | T... args | 可變參數(只是標記,不影響 Smali 寫法) |
5.3?完整示例分析
Java 源碼
public class Demo {public static final void log(String msg) {Log.d("TAG", msg);}private int compute(int a, int b) {return a + b;}protected abstract void work(); // 抽象方法
}
對應 Smali
.method public static final log(Ljava/lang/String;)V.locals 1const-string v0, "TAG"invoke-static {v0, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)Ireturn-void
.end method.method private compute(II)I.locals 1add-int v0, p1, p2return v0
.end method.method protected abstract work()V
5.4?修飾符的實際作用和實戰用途
修飾符 | 實戰應用舉例 |
---|---|
public | 可直接用 Frida/Java 調用(常用于靜態分析) |
private | 需要 Frida Bypass 或 Java.use(...).$init.overload(...) |
static | 無需創建實例,Frida 中直接 .value 或 invokeStatic |
final | 子類不能 override,意味著 Hook 只能靜態覆蓋 |
abstract | 沒有方法體,不能直接 Hook,要 Hook 實現類 |
synthetic | 混淆后生成的自動方法,可包含核心邏輯(Frida 分析重點) |
constructor | 對應 <init> ,實例化時會調用,可以 hook $init |
native | 沒有 Smali 代碼體,常需要 native 層分析或 trace |
5.5?Hook 時與修飾符的注意事項
1)Hook 靜態方法(static)
Java.use("com.test.Demo").log.overload('java.lang.String').implementation = function(msg) {console.log("log hooked: " + msg);
};
2)Hook 實例方法(非 static)
Java.use("com.test.Demo").compute.implementation = function(a, b) {return a + b + 999;
};
3)Hook 構造方法 <init>
Java.use("com.test.Demo").$init.overload('int', 'java.lang.String').implementation = function(i, s) {console.log("構造函數攔截:" + i + ", " + s);return this.$init(i, s);
};
5.6?總結速查表
修飾符組合 | 含義 |
---|---|
.method public | 公有方法(類外可訪問) |
.method private | 私有方法(類內訪問,hook 時需繞過) |
.method static | 靜態方法(不依賴實例) |
.method constructor | 構造方法 <init> |
.method abstract | 抽象方法(無實現體) |
.method public static final | 常量型工具函數 |
.method synthetic | 編譯器生成,可能是混淆后的關鍵方法 |