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解释
Vvoid只能用于返回值类型
Zboolean
Bbyte
Sshort
Cchar
Iint
Jlong(64位)
Ffloat
Ddouble(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

  • 6
    点赞
  • 93
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值