Android逆向系列之Smali语法篇

androidd

Dalvik字节码

Dalvik是google专门为Android操作系统设计的一个虚拟机,Dalvik VM是基于寄存器的,而Java VM是基于栈的;Dalvik有特定的文件执行格式dex(dalvik executable),有点类似于机器码,比如0110代表move v0, v1,而这里的move v0, v1就是我们所说的smali代码。

下载

smali指令特点

Dalvik指定在调用格式上模仿了C语言的调用约定。Dalvik指令的语法与助词符有如下特点:

* 参数采用从目标(destination)到源(source)的方式。
* 根据字节码的大小与类型不同,一些字节码添加了名称后缀以消除岐义。
* 32位常规类型的字节码末添加任何后缀。
* 64位常规类型的字节码添加 -wide后缀。
* 特殊类型的字节码根据具体类型添加后缀。
    它们可以是 -boolean,-byte,-char,-short,-int,-long,-float,-double,-object,-string,-class,-void之一。
* 根据字节码的布局与选项不同,一些字节码添加了字节码后缀以消除岐义。这些后缀通过在字节码主名称后添加斜杠“/”来分隔开。
* 在指令集的描述中,宽度值中每个字母表示宽度为4位。
    例如这条指令:“move-wide/from16 vAA, vBBBB”

move为基础字节码(base opcode),标识这是基本操作。wide为名称后缀(name suffix),标识指令操作的数据宽度(64位)。from16为字节码后缀(opcode suffix),标识源为一个16位的寄存器引用变量。vAA为目的寄存器,它始终在源的前面,取值范围为v0~v255。vBBBB为源寄存器,取值范围为v0~v65535。

Dalvik指令集中大多数指令用到了寄存器作为目的操作数或源操作数,其中 A/B/C/D/E/F/G/H 代表一个4位的数值,可用来表示0~15的数值或v0~v15的寄存器,而 AA/BB/CC/DD/EE/FF/GG/HH 代表一个8位的数值,可用来表示0~255的数值或v0~v255的寄存器,AAAA/BBBB/CCCC/DDDD/EEEE/FFFF/GGGG/HHHH 代表一个16位的数值,可用来表示0~65535的数值或v0~v65535的寄存器。注意:Android官方指令文档描述寄存器时,对不同取值范围的寄存器以括号说明其大小,如A:destination register(4 bits),A:destination register(16 bits)。请注意,Dalvik虚拟机中的每个寄存器都是32位的。描述指令时所说的位数表示的是寄存器数值的取值范围。

smali常用基本类型

    B---byte
    C---char
    D---double
    F---float
    I---int
    S---short
    V---void
    J---long
    Z---boolean
    [X---array
    Lxxx/yyy;---object

解释:

数组:

[x 代表一维数组,这里的x代表以上的基本类型,比如[I代表int []

[[x 代表二维数组,同理x代表以上基本类型,比如[[B代表byte [][]

对象:

Java语言存在大量对象,比如String对象,需要使用java.lang.String来引用,翻译成smali语法则为:

Ljava/lang/String; 注意这里的分号,一定要加上

smali寄存器与变量

Java中变量都是存放在内存中的,android为了提高性能,变量都是存放在寄存器中的,寄存器为32位,可以支持任何类型。
注意其中long和double是64位的,需要使用两个寄存器保存。

寄存器采用v和p来命名
v表示本地寄存器,p表示参数寄存器,关系如下

如果一个方法有两个本地变量(local var),有三个参数

    v0 第一个本地寄存器
    v1 第二个本地寄存器
    v2 p0 (this)
    v3 p1 第一个参数
    v4 p2 第二个参数
    v5 p3 第三个参数

当然,如果是静态方法的话就只有5个寄存器了,则不需要存this。
使用P命名方式是为了防止以后如果要在方法中增加寄存器,需要对参数寄存器重新进行编号的缺点。

.registers 使用这个指令指定方法中寄存器的总数
.locals 使用这个指令表明方法中非参寄存器的总数,放在方法的第一行。

方法的传参

当一个方法被调用的时候,方法的参数被置于最后N个寄存器中。如果一个方法有2个参数,5个寄存器(v0-v4),那么参数将置于最后2个寄存器——v3和v4。 非静态方法中的第一个参数总是调用该方法的对象(this)。

例如,非静态方法LMyObject;->tasfa(II)V有2个整型参数,另外还有一个隐含的LMyObject;参数,所以总共有3个参数。假如在该方法中指定了5个寄存器(v0-v4),以.registers 5或以.locals 2方式指定寄存器个数(即2个local寄存器+3个参数寄存器)。当该方法被调用的时候,调用该方法的对象(即this引用)存放在v2中,第一个整型参数存放在v3中,第二个整型参数存放在v4中。
对于静态方法除了没有隐含的this参数外其它都一样。

原文参考

smali方法

方法表示:

Lpackage/name/ObjectName;->MethodName(III)Z

代表: bool package.name.Object.MethodName(int,int,int)

复杂点的:

method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;

代表: String method(int, int[][], int, String, Object[])

字段表示:

Lpackage/name/ObjectName;—>FieldName:Ljava/lang/String;

代表: package.name.ObjectName.FieldName

smali基本语法

.field private isFlag:z  定义变量
.method  方法
.parameter  方法参数

.prologue  方法开始
.line 12  此方法位于第12行
invoke-super  调用父函数

const/high16  v0, 0x7fo3  把0x7fo3赋值给v0
invoke-direct  调用函数
return-void  函数返回void

.end method  函数结束
new-instance  创建实例
iput-object  对象赋值
iget-object  调用对象
invoke-static  调用静态函数

smali常用条件跳转分支

"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_**

smali基本语法结构分析

if结构

java代码:

if (1 == flagx)
    flagx = 2
else
    flagx = 3

smali代码:

const/4 v1, 0x1             
if-ne v0, v1, :cond_0      #v0为参数flagx
const/4 v2, 0x2
move v0,v2
goto :goto_0
:cond_0                    #跳转的地址
const/4 v2, 0x3
move v0,v2
:goto_0

for结构

Java代码:

for (int i = 0; i < 16; i++) 
{
    do_something();
}

smali代码:

const/4 v0, 0x0
const/4 v3,0x10
.local v0, i:I
:goto_0
if-lt v0, v3, :cond_0    //  if-lt判断数值v0小于v3 , 如不符合往下走, 符合执行分支 :cond_0
do_something.....
add-int/lit8 v0, v0, 0x1 // 将第二个v0寄存器中的值,加上0x1的值放入第一个寄存器中, 实现自增长
goto :goto_0

实例分析

Java:

package cn.tasfa.vulexploit;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;


public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";
    private static final float PI = (float) 3.14;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public void onClick(View view) {
        float result = add(4, 5);
        System.out.println(result);

        if (result > 6) {
            log(result);
        }
    }
    public float add(int x, int y) {
        return x + y + PI;
    }
    public static void log(float result) {
        Log.d(TAG, "the result:" + result);
    }
}

Smali:

.class public Lcn/tasfa/vulexploit/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"

# interfaces
.implements Landroid/view/View$OnClickListener;


# static fields
.field private static final PI:F = 3.14f

.field private static final TAG:Ljava/lang/String; = "MainActivity"


# direct methods
.method public constructor <init>()V
    .locals 0

    .line 9
    invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V

    return-void
.end method

.method public static log(F)V
    .locals 3
    .param p0, "result"    # F

    .line 31
    const-string v0, "MainActivity"

    new-instance v1, Ljava/lang/StringBuilder;

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

    const-string v2, "the result:"

    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder;

    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v1

    invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

    .line 32
    return-void
.end method


.method public onClick(Landroid/view/View;)V
    .locals 2
    .param p1, "view"    # Landroid/view/View;

    .line 20
    const/4 v0, 0x4

    const/4 v1, 0x5

    invoke-virtual {p0, v0, v1}, Lcn/tasfa/vulexploit/MainActivity;->add(II)F

    move-result v0

    .line 21
    .local v0, "result":F
    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;

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

    .line 23
    const/high16 v1, 0x40c00000    # 6.0f

    cmpl-float v1, v0, v1

    if-lez v1, :cond_0

    .line 24
    invoke-static {v0}, Lcn/tasfa/vulexploit/MainActivity;->log(F)V

    .line 26
    :cond_0
    return-void
.end method

.method protected onCreate(Landroid/os/Bundle;)V
    .locals 1
    .param p1, "savedInstanceState"    # Landroid/os/Bundle;

    .line 14
    invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V

    .line 15
    const v0, 0x7f09001c

    invoke-virtual {p0, v0}, Lcn/tasfa/vulexploit/MainActivity;->setContentView(I)V

    .line 16
    return-void
.end method

参考资料

Smali 介绍-CTF WIKI

本文属学习笔记原创,转载请注明来自tasfa.cn。如有问题请联系管理员root@tasfa.cn

发表评论

电子邮件地址不会被公开。

You must enable javascript to see captcha here!