Smali语法介绍

0x00: 介绍

在Android逆向中,经常会遇到Smali语言,那什么是Smali语言呢?总的来说,Smali是Android设备使用的Dalvik虚拟机的一种汇编语言,通常用于对APK文件进行反编译、分析和修改。

从实际角度出发,当创建应用程序代码时,apk 文件包含一个 .dex 文件,其中包含二进制 Dalvik 字节码。这是Android平台实际理解的格式。然而,读取或修改二进制代码并不容易。为了方便,便产生了一些工具可以使得在Dalivik字节码与人类可读的表示形式之间进行转换。Smali即是这个人类可读的表示形式。

值得一提的是,Smali并非是官方提出,提出者应该是这个仓库的主人:https://github.com/JesusFreke/smali。仓库中包含两个关键的工具:

  • baksmali工具:将Dalvik字节码反编译为Smali代码;
  • smali工具:将Smali代码重新编译为Dalvik字节码;

工具下载:JesusFreke / smali / Downloads — Bitbucket

本篇文章让首次接触的对smali语言有大概印象,更多更详细的内容可查阅:https://pysmali.readthedocs.io/en/latest/api/smali/language.html

0x01: Demo

Demo.java:

package com.example.smalidemo;

public class Demo {
    private int num;

    public Demo() {
        num = 10;
    }

    public void Print() {
        System.out.println("Hello, Smali.");
        int a = 10;
        int res = compute(a, num);
        System.out.println(res);
    }

    public int compute(int a, int b) {
        if (a < b) {
            return a + b;
        } else {
            return a * b;
        }
    }
}

工具的基本命令:
java -jar baksmali.jar d <dex-file> -o <output-directory>
java -jar smali.jar a <input-directory> -o <dex-file>

Demo.java生成对应的Demo.smali :
.class public Lcom/example/smalidemo/Demo;
.super Ljava/lang/Object;
.source "Demo.java"


# instance fields
.field private num:I


# direct methods
.method public constructor <init>()V
    .registers 2

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

    .line 7
    const/16 v0, 0xa

    iput v0, p0, Lcom/example/smalidemo/Demo;->num:I

    .line 8
    return-void
.end method


# virtual methods
.method public Print()V
    .registers 4

    .line 11
    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v1, "Hello, Smali."

    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    .line 12
    const/16 v0, 0xa

    .line 13
    .local v0, "a":I
    iget v1, p0, Lcom/example/smalidemo/Demo;->num:I

    invoke-virtual {p0, v0, v1}, Lcom/example/smalidemo/Demo;->compute(II)I

    move-result v1

    .line 14
    .local v1, "res":I
    sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;

    invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(I)V

    .line 15
    return-void
.end method

.method public compute(II)I
    .registers 4
    .param p1, "a"    # I
    .param p2, "b"    # I

    .line 18
    if-ge p1, p2, :cond_5

    .line 19
    add-int v0, p1, p2

    return v0

    .line 21
    :cond_5
    mul-int v0, p1, p2

    return v0
.end method

0x02: 基本定义

数据类型:

Java 数据类型Smali 符号
byteB
shortS
intI
longJ
floatF
doubleD
booleanZ
charC
class 或 interfaceLclassname;
数组[类型

类的定义:

.class public Lcom/example/smalidemo/Demo;
.super Ljava/lang/Object;
.source "Demo.java"
  1. .class是定义类的关键字,public是访问修饰符,L表示类数据类型,后面紧接着类的全名;
  2. .super是定义父类的关键字;
  3. .source关键字用于定义源文件名;

(other: #是注释)


字段的定义

在Smali中,字段(Field)是类中的成员变量

.field private num:I
iput v0, p0, Lcom/example/smalidemo/Demo;->num:I
iget v1, p0, Lcom/example/smalidemo/Demo;->num:I
  1. .field是定义字段的关键字,private是访问修饰符,num是字段名,I即int类型;
  2. iput用于设置字段的值,v0->p0,v表示寄存器,p表示函数参数。p0即第一个参数,默认是this。后面则是字段的类型和名称;
  3. iget用于获取字段的值,v1<-p0

其他的字段操作指令还有:sget、sput、sget-object、sput-object

(other: .line关键字表示java源文件中指定行)


函数的定义:

.method public Print()V
    .registers 4

    .local v0, "a":I

    .local v1, "res":I

    return-void
.end method
  1. .method是定义函数的关键字,Print()是函数名,括号中会表明函数各个参数的类型,V函数返回类型,这里是void;
  2. .end method表明方法结束;
  3. .registers用于声明函数中所使用的总寄存器数量,.locals用于声明函数中局部变量的数量;
  4. .local用于定义局部变量,v0是变量所关联的寄存器,"a":I是变量名称和类型;
  5. 函数返回:
    • return-void:无返回值;
    • return:返回32位基本数据类型,如intfloatbooleanchar等;
    • return-wide: 用于返回 longdouble 类型的数据,这些类型占用两个寄存器;
    • return-object:用于返回对象引用或数组;

.param p1, "a"    # I
.param p2, "b"    # I
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
invoke-virtual {p0, v0, v1}, Lcom/example/smalidemo/Demo;->compute(II)I
  • .param:定义参数的调试信息,p1是第一个参数,"a"是名称,并注释着数据类
  • invoke-direct:调用私有方法、构造函数(<init>)或父类中的方法;
  • invoke-virtual:调用对象的实例方法;

0x03: Dalvik指令集

数据传输和常量加载:

move v0, v1  # 将 v1 中的数据传输到 v0
move-wide v0, v2  # 将 v2 和 v3 中的 64 位数据传输到 v0 和 v1
move-object v0, v1  # 将 v1 中的对象引用传输到 v0
const/4 v0, 0x3  # 将 3 (0x3) 加载到 v0 中
const/16 v0, 0x1234  # 将 0x1234 加载到 v0 中
const v0, 0x12345678  # 将 0x12345678 加载到 v0 中
const-string v0, "Hello, Smali!"  # 将字符串引用加载到 v0 中
const-class v0, Ljava/lang/String;  # 将 java.lang.String 类引用加载到 v0 中
  • move:寄存器数据传输;
  • move-wide:用于传输64位数据类型;
  • move-object:用于传输对象引用;
  • const/4:将4位常量加载到寄存器中;
  • const/16:将16位常量加载到寄存器中;
  • const:将32位常量加载到寄存器中;
  • const-string:将字符串引用加载到寄存器中;
  • const-class:将类引用加载到寄存器中;

算术运算:

add-int v0, v1, v2  # v0 = v1 + v2
sub-int v0, v1, v2  # v0 = v1 - v2
mul-int v0, v1, v2  # v0 = v1 * v2
div-int v0, v1, v2  # v0 = v1 / v2
  • add-int/add-float:执行整数或浮点数的加法;
  • sub-int/sub-float:减法;
  • mul-int/mul-float:乘法;
  • div-int/div-floar:除法;

逻辑运算:

and-int v0, v1, v2  # v0 = v1 & v2
or-int v0, v1, v2   # v0 = v1 | v2
xor-int v0, v1, v2  # v0 = v1 ^ v2
  • and-int:执行与操作;
  • or-int:或操作;
  • xor-int:异或操作

分支和跳转:

if-eq v0, v1, :label  # if (v0 == v1) goto label
if-ne v0, v1, :label  # if (v0 != v1) goto label
if-lt v0, v1, :label  # if (v0 < v1) goto label
if-ge v0, v1, :label  # if (v0 >= v1) goto label
if-nez v0, :label     # if v0 is not zero goto labal
  • if-eq:当两个寄存器的值相等时跳转;
  • if-ne:当两个寄存器的值不相等时跳转;
  • if-lt:当第一个寄存器的值小于第二个寄存器时跳转;
  • if-ge:当第一个寄存器的值大于或等于第二个寄存器时跳转;
  • if-nez:如果寄存器 v0 的值不为零,则跳转到指定的标签。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值