关于 smali:1. Smali 基础语法入门

一、Smali 文件结构

每一个 .smali 文件代表一个 Java 类,它是 Dalvik 字节码(DEX)反编译后的汇编形式。

1.1 Smali 文件结构总览

一个 .smali 文件 = 一个 Java 类
格式是 “类头部 + 字段定义 + 方法定义”,如下:

.class public Lcom/example/MyClass;
.super Ljava/lang/Object;
.source "MyClass.java"

.field private myField:I

.method public constructor <init>()V
    .locals 1
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
    return-void
.end method

.method public myMethod(I)V
    .locals 1
    return-void
.end method

1.2 Smali 文件的位置和命名

当用工具(如 apktool)反编译 APK 后,会生成如下结构:

smali/
  └── com/
      └── example/
          └── MyClass.smali
  • MyClass.smali 对应 Java 类 com.example.MyClass

  • Smali 的类名采用 L + 包名路径 + 类名 + ; 的形式

.class public Lcom/example/MyClass;

1.3 Smali 文件组成部分详解

1) .class 声明类

.class public Lcom/example/MyClass;
  • 修饰符(如 public、final、abstract)

  • 类名:必须是 L包名/类名; 的格式

  • 不包含 .java 后缀

2).super 声明父类

.super Ljava/lang/Object;
  • 类继承自哪个父类

  • 语法和类名一样,用 L...; 格式

3).source 可选,原始源文件名

.source "MyClass.java"
  • 显示源 Java 文件名

  • 可省略,仅用于调试辅助

4).field 成员变量(字段)

.field private myField:I
.field public static sValue:Ljava/lang/String;

语法:

.field [修饰符] 字段名:类型
类型符号含义
Iint
Zboolean
Ffloat
Ddouble
Jlong
Ljava/lang/String;String
Lcom/example/MyClass;自定义类

5).method 方法定义

.method public myMethod(I)V
    .locals 1
    return-void
.end method

语法结构:

.method [修饰符] 方法名(参数类型)返回类型
    .locals N
    指令代码
.end method

修饰符:

  • public / private / static / final / constructor(构造方法)

参数和返回类型(与 Java 的签名对应):

符号含义
Iint
Vvoid
Zboolean
Ljava/lang/String;String
L...;对象
([类型])返回类型方法签名格式

6)指令区(方法体)

    .locals 1
    const v0, 0x10
    return-void
  • .locals N 定义本地寄存器数量(v0, v1...

  • 然后是 Smali 指令(如 const, move, invoke

1.4 构造方法结构

构造方法名为 <init>,必须调用父类构造:

.method public constructor <init>()V
    .locals 1
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
    return-void
.end method

1.5 完整示例

.class public Lcom/example/DemoClass;        # 定义一个公开类,类名为 com.example.DemoClass
.super Ljava/lang/Object;                    # 指定该类继承自 java.lang.Object
.source "DemoClass.java"                     # 源文件名,仅用于调试信息

.field public static final TAG:Ljava/lang/String; = "SmaliDemo"   # 定义一个静态常量 TAG,类型是 String,值为 "SmaliDemo"

.method public constructor <init>()V         # 定义构造方法(构造函数名固定为 <init>),无参数,返回 void
    .locals 1                                 # 分配 1 个局部寄存器(v0 可用)
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V   # 调用父类 Object 的构造方法,p0 是 this 指针
    return-void                               # 返回 void,构造函数结束
.end method                                   # 方法定义结束

.method public showToast(Ljava/lang/String;)V   # 定义一个公开方法 showToast,接收一个字符串参数,返回 void
    .locals 2                                   # 分配 2 个局部寄存器(v0, v1 可用)
    return-void                                 # 方法体为空,直接返回
.end method                                     # 方法定义结束

分析:

  • 类名为 com.example.DemoClass

  • 继承自 java.lang.Object

  • 定义一个静态常量字段 TAG

  • 一个构造方法 <init>()

  • 一个方法 showToast(String msg)参数是字符串,返回 void

1.6 小结

  • Smali 修改一定要注意寄存器数量(.locals)是否足够,否则会运行崩溃。

  • 修改后建议用 baksmali/smali 重新编译测试 或用 apktool rebuild。

  • 学会快速定位:类文件路径 = L包名/类名.smali,方法签名用 grep 快速找函数。

部分用途
.class定义类
.super继承父类
.field成员变量
.method定义方法
.locals本地寄存器数目
指令代码方法的实际执行逻辑

二、基本语法结构:

Smali 的基本语法结构,包括:

  • 类定义(.class、.super、.source)

  • 方法定义(.method、参数、返回类型、.locals、.end method)

  • 指令语法(Smali 的汇编语法结构)

2.1 类定义(Class Definition)

Smali 中,每个 .smali 文件对应一个 Java 类,用以下三条指令声明:

.class public Lcom/example/MyClass;
.super Ljava/lang/Object;
.source "MyClass.java"

说明:

语法含义
.class定义类本身,修饰符 + 类名
.super指定该类继承的父类
.source原始 Java 文件名(调试用,可省略)

类名格式:

Smali 中类名是用 L完整包路径/类名; 表示:

  • Java 类:com.example.MyClass

  • Smali:Lcom/example/MyClass;

2.2 方法定义(Method Definition)

一个类里可以有多个方法,每个方法使用 .method 开头,.end method 结束。

基本结构:

.method public myMethod(ILjava/lang/String;)V
    .locals 2
    ...(方法体的 Smali 指令)...
.end method

方法定义语法详解:

项目示例含义
.method.method public方法修饰符(public/private/static 等)
方法名myMethod方法名称
参数类型(I Ljava/lang/String;)参数是 int + String
返回类型V表示返回 void

常见类型缩写:

类型Smali 表达
intI
booleanZ
floatF
doubleD
longJ
voidV
StringLjava/lang/String;
自定义类Lcom/example/MyClass;
数组[I 表示 int[],[Ljava/lang/String; 表示 String[]

.locals N 说明

.locals 2
  • 声明该方法最多使用 v0 到 vN-1 这几个寄存器

  • 如果你写了 v2,那 .locals 至少要写成 3

2.3 Smali 指令语法(Instruction Syntax)

Smali 是 Dalvik 字节码的汇编语言,其指令一般结构如下:

<指令> <目标寄存器>, <来源/参数寄存器或值>

关键点:

  • 操作数之间用 逗号分隔

  • 使用的寄存器以 v(局部变量)或 p(方法参数)为前缀,如 v0, p1

  • 数字、常量用十六进制或十进制都可:0x1A, 26

示例指令语法

指令示例说明
constconst v0, 0x10将常量 0x10 存入 v0
movemove v1, v0v0 复制给 v1
return-voidreturn-void无返回值方法的结束
returnreturn v0返回一个值
invoke-virtualinvoke-virtual {p0}, Landroid/app/Activity;->finish()V调用方法
igetiget v0, p0, Lcom/example/MyClass;->field:I访问成员变量(获取)
sputsput-object v0, Lcom/example/MyClass;->TAG:Ljava/lang/String;设置静态变量
if-eqif-eq v0, v1, :label如果 v0 == v1,跳转到 label
gotogoto :label无条件跳转

标签语法(跳转用)

:label_1
    const v0, 0x1
  • : 开头的语句是标签(label),用于跳转指令中

  • 不会执行,是跳转锚点

2.4 补充说明:参数寄存器与局部寄存器

方法中的寄存器可以分两类:

类型前缀说明
参数寄存器p0, p1, p2...代表方法参数,从 p0 开始,一般 p0 是 this
局部寄存器v0, v1, v2...方法内部定义的寄存器,用于存放局部变量、临时结果等

2.5 小结

部分用法示例
类定义.class, .super, .source定义类和父类
方法定义.method, .locals, .end method定义函数结构
参数/返回类型([类型])返回(I)V, (Ljava/lang/String;)I
寄存器v0, p0本地变量 vs 参数
常用指令const, move, invoke, return, iget, sput, if-eq, goto用于数据赋值、跳转、调用函数等

三、常见指令集:

3.1 常见指令总览

指令作用Java 等价
const给寄存器赋常量值int a = 10;
move数据寄存器间复制b = a;
return / return-void返回方法return a; / return;
invoke-*方法调用obj.method()
iget读取实例字段this.a
sput写入静态字段ClassName.staticField = value;
if-eq条件跳转(相等)if (a == b)
goto无条件跳转goto label;

3.2 每条指令详解 + 示例

1)const

const v0, 0x1A        # v0 = 26(十六进制 1A)
const v1, 123         # v1 = 123

这个指令支持 intfloatlong 等,常用的是 int

Java 等价:

int a = 26;

2)move

功能: 将一个寄存器的值复制给另一个寄存器。

move v1, v0      # v1 = v0

注意是浅拷贝,类似于 Java 的引用复制。

Java 等价:

b = a;

3)return / return-void

功能: 从方法中返回,可能带返回值。

return v0         # 返回 v0 中的值
return-void       # 无返回值的方法使用

Java 等价:

return a;         // return v0;
return;           // return-void;

4)invoke-*

功能: 调用方法
Smali 中方法调用有多个形式,取决于方法类型(虚方法、静态方法、接口等):

常见形式:

invoke-virtual {p0}, Landroid/app/Activity;->finish()V

invoke-static {v0}, Ljava/lang/Math;->abs(I)I

invoke-direct {p0}, Ljava/lang/Object;-><init>()V

格式说明:

invoke-<类型> {参数列表}, 类名->方法名(参数类型)返回类型
类型用法
virtual实例方法
static静态方法
direct构造器 / 私有方法
interface接口调用
super父类方法

Java 等价:

obj.finish();
int b = Math.abs(a);
super.onCreate();

5)iget

功能: 读取对象的实例字段

iget v1, p0, Lcom/example/MyClass;->age:I
  • p0(this)读取字段 agev1

  • I 表示 int 类型

Java 等价:

int b = this.age;

6)sput

功能: 设置静态字段的值

sput-object v0, Lcom/example/MyClass;->TAG:Ljava/lang/String;
  • v0 的值赋给 MyClass 中的 TAG 静态字段

Java 等价:

MyClass.TAG = "Smali";

7)if-eq

功能: 判断两个寄存器值是否相等,如果相等则跳转

if-eq v0, v1, :cond_equal

:cond_equal
    const v2, 0x1

Java 等价:

if (a == b) {
    c = 1;
}

8)goto

功能: 无条件跳转到指定标签位置

goto :end

:skip
    const v0, 0x1

:end
    return-void

Java 等价:

// goto 等价于代码块跳过、break、continue、return 等

3.3 实战示例整合

.method public checkFlag(I)V
    .locals 1

    const v0, 0x1       # v0 = 1
    if-eq p1, v0, :ok   # if p1 == v0 跳转

    return-void         # 不相等则 return

:ok
    invoke-static {v0}, Lcom/example/Logger;->logFlag(I)V
    return-void
.end method

Java 等价:

public void checkFlag(int flag) {
    if (flag == 1) {
        Logger.logFlag(1);
    }
}

3.4 小结

指令作用Java 等价
const v0, 0x10常量赋值int a = 16;
move v1, v0值传递b = a;
return v0返回值return a;
invoke-virtual {p0}, LClass;->method()V方法调用obj.method();
iget v0, p0, LClass;->a:I获取字段this.a;
sput-object v0, LClass;->TAG:Ljava/lang/String;设置静态字段Class.TAG = ...;
if-eq v0, v1, :label条件跳转if (a == b)
goto :label跳转goto

四、Register 寄存器理解(v0p0 参数寄存器)

在 Smali 中,寄存器就像 Java 中的变量,用于临时保存数据,如参数、返回值、局部变量、对象引用等。

Smali 有两种主要寄存器:

类型命名方式用途
pX 寄存器p0, p1, p2方法参数传递
vX 寄存器v0, v1, v2局部变量、临时值

4.1 参数寄存器 p0, p1, p2...

功能:

  • 用于存放传入方法的参数(包括 this 对象)

规则:

  • 如果是 实例方法(非 static),p0 = this

  • 后续参数依次是 p1, p2...

  • 如果是 静态方法(static),从 p0 开始就是第一个真正的参数

示例一:非静态方法

.method public test(ILjava/lang/String;)V
    .locals 1

    # p0 -> this
    # p1 -> int 参数
    # p2 -> String 参数
.end method

Java 等价:

public void test(int a, String b) {
    // this = p0
    // a = p1
    // b = p2
}

示例二:静态方法

.method public static test(ILjava/lang/String;)V
    .locals 1

    # p0 -> int 参数
    # p1 -> String 参数
.end method

Java 等价:

public static void test(int a, String b) {
    // a = p0
    // b = p1
}

注意:long、double 会占用 2 个寄存器!

.method public test(JD)V
    # p0 -> this
    # p1 & p2 -> long 参数
    # p3 & p4 -> double 参数

4.2 局部寄存器 v0, v1, v2...

功能:

  • 存放方法内部的变量、临时值、运算结果等

声明方式:

在方法体开头使用 .locals N 来声明局部寄存器的个数。

.locals 2  # 表示 v0 和 v1 可用

示例:

.method public test()V
    .locals 2

    const v0, 0x10       # v0 = 16
    const v1, 0x20       # v1 = 32
.end method

4.3 参数与局部的总和限制

在每个方法中,寄存器总数不能超过 locals + 参数寄存器个数

有时候你也会看到 .registers N,这个会声明总寄存器数(包括参数 + 局部),是 .locals + 参数个数的替代写法。

实战案例:理解寄存器使用

Java 代码

public void printSum(int a, int b) {
    int c = a + b;
    Log.d("TAG", String.valueOf(c));
}

对应 Smali 代码:

.method public printSum(II)V          # 定义一个公开实例方法,参数为两个 int,无返回值(V)
    .locals 2                          # 声明局部变量寄存器 v0 和 v1

    add-int v0, p1, p2                 # v0 = p1 + p2,对传入参数相加:int c = a + b;

    invoke-static {v0}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
                                       # 调用 String.valueOf(int) 方法,把 int 转为 String

    move-result-object v1              # 将 valueOf 的返回值(String)保存到 v1 中

    const-string v0, "TAG"             # 将字符串常量 "TAG" 加载到 v0

    invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
                                       # 调用 Log.d(String tag, String msg),用于打印日志

    return-void                        # 方法无返回值,结束方法
.end method                            # 方法定义结束
寄存器内容
p0this
p1int a
p2int b
v0计算结果 / 常量
v1字符串结果

4.4 小结

名称作用特点
pX参数寄存器方法参数传入,从 p0(实例)或 p0(静态)开始
vX局部寄存器方法内部临时变量,用 .locals 声明数量
.locals N局部变量个数v0 ~ v(N-1) 可用
.registers N所有寄存器总数替代 .locals + 参数数量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值