关于 smali:2. 从 Java 到 Smali 的映射

一、对照 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

java 复制代码
Logger.log("test");

Smali

bash 复制代码
const-string v0, "test"
invoke-static {v0}, Lcom/example/Logger;->log(Ljava/lang/String;)V

2)实例方法调用

Java

java 复制代码
User u = new User();
u.say("hello");

Smali

bash 复制代码
new-instance v0, Lcom/example/User;
invoke-direct {v0}, Lcom/example/User;-><init>()V

const-string v1, "hello"
invoke-virtual {v0, v1}, Lcom/example/User;->say(Ljava/lang/String;)V

3)构造器调用

构造器就是特殊方法**<init>**

Java

java 复制代码
Person p = new Person();

Smali

bash 复制代码
new-instance v0, Lcom/example/Person;
invoke-direct {v0}, Lcom/example/Person;-><init>()V

1.2 控制流语句差异:Java vs Smali

if-else 语句

Java

java 复制代码
if (a > b) {
    doA();
} else {
    doB();
}

Smali

bash 复制代码
# 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

java 复制代码
switch (x) {
    case 1: f1(); break;
    case 5: f5(); break;
    default: fDefault();
}

Smali

bash 复制代码
sparse-switch v0, :switch_data  # v0 是 switch 的变量
goto :default                   # 如果没匹配任何 case,跳转 default

:switch_data
    .sparse-switch
        0x1 -> :case1
        0x5 -> :case5
    .end sparse-switch

:case1
    invoke-static {}, Lcom/example/Util;->f1()V
    goto :end

:case5
    invoke-static {}, Lcom/example/Util;->f5()V
    goto :end

:default
    invoke-static {}, Lcom/example/Util;->fDefault()V

:end
    # 后续逻辑继续执行

while 循环

Java

java 复制代码
int i = 0;
while (i < 10) {
    i++;
}

Smali

bash 复制代码
const/4 v0, 0x0       # i = 0
:loop_start
const/4 v1, 0xA
if-ge v0, v1, :loop_end

add-int/lit8 v0, v0, 1
goto :loop_start

:loop_end

说明:

  • **if-ge v0, v1, :loop_end:**当 i ≥ 10 跳出循环

  • **goto:**用于跳转控制流

1.3 类字段与方法修饰符差异

字段定义差异

Java

java 复制代码
public int count;
private String name;

Smali

bash 复制代码
.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

java 复制代码
this.count = 1;
int x = this.count;

Smali

bash 复制代码
const/4 v1, 0x1
iput v1, v0, Lcom/example/MyClass;->count:I

iget v2, v0, Lcom/example/MyClass;->count:I

说明:

  • iput = instance put,写入字段

  • iget = instance get,读取字段

方法定义与修饰符

Java

java 复制代码
public void print(int n) { ... }
private int getNum() { return num; }

Smali

bash 复制代码
.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 基础格式说明

通用格式:

bash 复制代码
invoke-xxx {参数寄存器列表}, L类名;->方法名(参数类型)返回类型

说明:

  • {} 中写的是使用的寄存器(如 {v0, v1}

  • L类名; 是类路径(如 Ljava/lang/String;

  • (参数类型):方法的参数签名

  • 返回类型:返回值类型(V 表示 void)

2.3 详细讲解每个指令

1)invoke-static:调用静态方法

Java 中 Class.method(),如 Math.abs(-1)

示例:

bash 复制代码
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()**

示例:

bash 复制代码
invoke-virtual {v0}, Ljava/lang/String;->length()I

说明:

  • v0 是一个**String**对象

  • 调用它的 length() 方法

  • () :无参数,返回**int**

特点:

  • 用于非私有 的实例方法(包括**publicprotected**)

  • 支持虚方法分发(多态)

3)invoke-direct:调用构造方法、私有方法

Java 中**new MyObject()** 或 this.privateMethod()

示例 1:调用构造函数

bash 复制代码
new-instance v0, Lcom/example/MyClass;
invoke-direct {v0}, Lcom/example/MyClass;-><init>()V

说明:

  • v0 是要构造的对象

  • 调用其构造器**MyClass()**

  • **<init>()V**是构造函数签名(返回类型是 void)

示例 2:调用私有方法

bash 复制代码
invoke-direct {v0}, Lcom/example/MyClass;->myPrivateMethod()V

特点:

  • 只能调用当前类的 private 方法 或 构造函数

  • 无法用于调用继承的或 public/protected 方法

补充:invoke-super

调用父类的方法,通常在子类中使用**super.method()**

示例:

bash 复制代码
invoke-super {v0}, Lcom/example/ChildClass;->toString()Ljava/lang/String;

补充:invoke-interface

调用接口方法(Java 接口)

示例:

bash 复制代码
invoke-interface {v0}, Ljava/util/List;->size()I

2.4 几个重要的调用场景示例

调用静态方法(工具类)

java 复制代码
Utils.sayHello();
bash 复制代码
invoke-static {}, Lcom/example/Utils;->sayHello()V

调用实例方法(普通对象方法)

java 复制代码
myObj.getName();
bash 复制代码
invoke-virtual {v0}, Lcom/example/MyClass;->getName()Ljava/lang/String;

调用构造器(创建对象)

java 复制代码
new MyClass();
bash 复制代码
new-instance v0, Lcom/example/MyClass;
invoke-direct {v0}, Lcom/example/MyClass;-><init>()V

调用私有方法(类内部)

java 复制代码
this.secret();
bash 复制代码
invoke-direct {v0}, Lcom/example/MyClass;->secret()V

2.5 寄存器数量限制说明

最多可以用 5 个寄存器调用 invoke-*,如果超过 5 个参数(例如构造函数参数很多):

  • 使用 invoke-* /range 版本:
bash 复制代码
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 示例:

java 复制代码
if (v0 == 1) {
    Log.i("TAG", "Equal");
} else {
    Log.i("TAG", "Not Equal");
}

Smali 实现:

bash 复制代码
# v0 已有值
const/4 v1, 0x1
if-eq v0, v1, :equal    # 如果 v0 == 1 跳转到 :equal

:not_equal
    const-string v2, "TAG"
    const-string v3, "Not Equal"
    invoke-static {v2, v3}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
    goto :end

:equal
    const-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 示例:

java 复制代码
switch (v0) {
    case 1: f1(); break;
    case 5: f5(); break;
    default: fDefault();
}

Smali 实现(sparse-switch):

bash 复制代码
sparse-switch v0, :switch_data
goto :default

:switch_data
    .sparse-switch
        0x1 -> :case1
        0x5 -> :case5
    .end sparse-switch

:case1
    invoke-static {}, Lcom/example/Util;->f1()V
    goto :end

:case5
    invoke-static {}, Lcom/example/Util;->f5()V
    goto :end

:default
    invoke-static {}, Lcom/example/Util;->fDefault()V

:end

packed-switch vs sparse-switch

类型 用法
packed-switch case 是连续整数时使用
sparse-switch case 是稀疏整数时使用

3.3 while 循环

Java 示例:

java 复制代码
int i = 0;
while (i < 5) {
    Log.d("loop");
    i++;
}

Smali 实现:

bash 复制代码
const/4 v0, 0x0         # i = 0

:loop_start
    const/4 v1, 0x5
    if-ge v0, v1, :loop_end  # if i >= 5 -> end

    const-string v2, "loop"
    invoke-static {v2}, Landroid/util/Log;->d(Ljava/lang/String;)I

    add-int/lit8 v0, v0, 0x1 # i++
    goto :loop_start

:loop_end

3.4 for 循环(其实就是 while)

Java 示例:

java 复制代码
for (int i = 0; i < 3; i++) {
    Log.e("for");
}

Smali 实现:

bash 复制代码
const/4 v0, 0x0         # int i = 0

:for_begin
    const/4 v1, 0x3
    if-ge v0, v1, :for_end   # if i >= 3 -> end

    const-string v2, "for"
    invoke-static {v2}, Landroid/util/Log;->e(Ljava/lang/String;)I

    add-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 中,字段就是类的成员变量:

java 复制代码
public class Person {
    public String name;
    private static int count;
}

在 Smali 中,这些字段会写成:

bash 复制代码
.field public name:Ljava/lang/String;

.field private static count:I

4.2 字段的 Smali 语法结构

bash 复制代码
.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 示例:

java 复制代码
public class User {
    public String name;
    private static int count = 0;

    public void setName(String n) {
        this.name = n;
        count++;
    }
}

Smali 对应(简化版):

bash 复制代码
.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.name
    iput-object p1, p0, Lcom/demo/User;->name:Ljava/lang/String;

    # sget 静态字段
    sget v0, Lcom/demo/User;->count:I
    add-int/lit8 v0, v0, 0x1
    sput v0, Lcom/demo/User;->count:I

    return-void
.end method

4.6 几个实战技巧与细节

判断字段是静态还是实例

特征 静态字段 实例字段
修饰符 static 不带 static
读取指令 sget / sput iget / iput
所属 类级别共享 每个对象一份
典型用途 常量、计数器 每个对象的属性,如 name

Frida Hook 中字段的常用操作

javascript 复制代码
// 获取实例字段
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 高级用法

初始值:

bash 复制代码
.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 定义格式

bash 复制代码
.method [修饰符] 方法名(参数)返回类型

比如:

bash 复制代码
.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 源码

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

bash 复制代码
.method public static final log(Ljava/lang/String;)V
    .locals 1
    const-string v0, "TAG"
    invoke-static {v0, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void
.end method

.method private compute(II)I
    .locals 1
    add-int v0, p1, p2
    return v0
.end method

.method protected abstract work()V

5.4 修饰符的实际作用和实战用途

修饰符 实战应用举例
public 可直接用 Frida/Java 调用(常用于静态分析)
private 需要 Frida Bypass 或 Java.use(...).$init.overload(...)
static 无需创建实例,Frida 中直接 .valueinvokeStatic
final 子类不能 override,意味着 Hook 只能静态覆盖
abstract 没有方法体,不能直接 Hook,要 Hook 实现类
synthetic 混淆后生成的自动方法,可包含核心逻辑(Frida 分析重点)
constructor 对应 <init>,实例化时会调用,可以 hook $init
native 没有 Smali 代码体,常需要 native 层分析或 trace

5.5 Hook 时与修饰符的注意事项

1)Hook 静态方法(static)

javascript 复制代码
Java.use("com.test.Demo").log.overload('java.lang.String').implementation = function(msg) {
    console.log("log hooked: " + msg);
};

2)Hook 实例方法(非 static)

javascript 复制代码
Java.use("com.test.Demo").compute.implementation = function(a, b) {
    return a + b + 999;
};

3)Hook 构造方法 <init>

javascript 复制代码
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 编译器生成,可能是混淆后的关键方法
相关推荐
小明同学016 分钟前
[C++入门]简化的艺术---对模版的初步探索
开发语言·c++·算法
Rachelhi6 分钟前
C++.异常处理(1.9w字)
开发语言·c++
SimonKing8 分钟前
吊打面试官系列:深入理解Spring的IOC容器
java·后端·架构
wxid:yiwoxuan16 分钟前
购物商城网站 Java+Vue.js+SpringBoot,包括商家管理、商品分类管理、商品管理、在线客服管理、购物订单模块
java·vue.js·spring boot·课程设计
WispX88820 分钟前
【设计模式】门面/外观模式
java·开发语言·设计模式·系统架构·外观模式·插件·架构设计
琢磨先生David23 分钟前
简化复杂系统的优雅之道:深入解析 Java 外观模式
java·设计模式·外观模式
ademen23 分钟前
spring4第7-8课-AOP的5种通知类型+切点定义详解+执行顺序
java·spring
快乐肚皮38 分钟前
EasyExcel高级特性和技术选型
java
寒士obj1 小时前
Java对象创建过程
java·开发语言
Java知识库1 小时前
「深度拆解」Spring Boot如何用DeepSeek重构MCP通信层?从线程模型到分布式推理的架构进化
java·开发语言·spring boot·程序员·编程