美文网首页
详解smali文件

详解smali文件

作者: 布道课堂 | 来源:发表于2018-11-18 09:43 被阅读0次

    详解smali文件

    上面我们介绍了Dalvik的相关指令,下面我们则来认识一下smali文件.尽管我们使用java来写Android应用,但是Dalvik并不直接加载.class文件,而是通过dx工具将.class文件优化成.dex文件,然后交由Dalvik加载.这样说来,我们无法通过分析.class来直接分析apk文件,而是需要借助工具baksmali.jar反编译dex文件来获得对应smali文件,smali文件可以认为是Davilk的字节码文件,但是并两者并不完全等同.通过baksmali.jar反编译出来每个.smali,都对应与java中的一个类,每个smali文件都是Davilk指令组成的,并遵循一定的结构.smali存在很多的指令用于描述对应的java文件,所有的指令都以”.”开头,常用的指令如下:

    关键词 说明
    filed 定义字段
    .method…end method 定义方法
    .annotation…end annotation 定义注解
    .implements 定义接口指令
    .local 指定了方法内局部变量的个数
    .registers 指定方法内使用寄存器的总数
    .prologue 表示方法中代码的开始处
    .line 表示java源文件中指定行
    .paramter 指定了方法的参数
    .param 和.paramter含义一致,但是表达格式不同

    文件头描述

    关键词 说明
    .class <访问权限修饰符> [非权限修饰符] <类名>
    .super <父类名>
    .source <源文件名称>

    <>中的内容表示必不可缺的,[]表示的是可选择的.
    访问权限修饰符即所谓的public,protected,private即default.而非权限修饰符则指的是final,abstract.

    关键词 说明
    .class public final Lcom/sbbic/demo/Device;
    .super Ljava/lang/Object;
    .source "Device.java"

    文件正文
    在文件头之后便是文件的正文,即类的主体部分,包括类实现的接口描述,注解描述,字段描述和方法描述四部分.下面我们就分别看看字段和方法的结构.(别忘了我们在Davilk中说过的方法和字段的表示)

    接口描述

    关键词 说明
    .implements <接口名称>
    .implements Landroid/view/View$OnClickListener;

    普通字段:

    访问权限修饰符相比各位已经非常熟了,而此处非权限修饰符则可是final,volidate,transient.

    关键词 说明
    .field <访问权限修饰符> [非权限修饰符] <字段名>:<字段类型>
    .field private TAG:Ljava/lang/String;

    静态字段

    需要注意:smali文件还为静态字段,普通字段分别添加#static field和#instan filed注释.

    关键词 说明
    .field <访问权限> static [修饰词] <字段名>:<字段类型>
    .field private static final pi:F = 3.14f

    直接方法

    重点解释一下parameter:
    parameter的个数和方法参数的数量相对应,即有几个参数便有几个.parameter,默认从1开始,即p1,p2,p2….
    熟悉java的童鞋一定会记得该类型的方法有个默认的参数指向当前对象,在smali中,方法的默认对象参数用p0表示.
    直接方法即所谓的direct methods,还记的Davilk中方法调用指令invoke-direct么。

    #direct methods
    .method <访问权限修饰符> [非访问权限修饰符] <方法原型>
          <.locals>
          [.parameter]
          [.prologue]
          [.line]
          <代码逻辑>
    .end
    
    # direct methods
    .method public constructor <init>()V
        .registers 2
    
        .prologue
        .line 8
        invoke-direct {p0}, Landroid/app/Activity;-><init>()V
    
        .line 10
        const-string v0, "MainActivity"
    
        iput-object v0, p0, Lcom/social_touch/demo/MainActivity;->TAG:Ljava/lang/String;
    
        .line 13
        const/4 v0, 0x0
    
        iput-boolean v0, p0, Lcom/social_touch/demo/MainActivity;->running:Z
    
        return-void
    .end method
    

    虚方法

    虚方法的定义会和直接方法唯一的不同就是注释不同:#virtual methods,其格式如下:

    #virtual methods
    .method <访问权限> [修饰关键词] <方法原想>
          <.locals>
          [.parameter1]
          [.parameter2]
          [.prologue]
          [.line]
          <代码逻辑>
    .end
    

    Activity类Java代码和Smali代码对比

    下面以一个Activity类的Java代码

    public class MainActivity extends Activity implements View.OnClickListener {
    
        private String TAG = "MainActivity";
        private static final float pi = (float) 3.14;
    
        public volatile boolean running = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        @Override
        public void onClick(View view) {
            int result = add(4, 5);
            System.out.println(result);
    
            result = sub(9, 3);
    
            if (result > 4) {
                log(result);
            }
        }
    
        public int add(int x, int y) {
            return x + y;
        }
    
        public synchronized int sub(int x, int y) {
            return x + y;
        }
    
        public static void log(int result) {
            Log.d("MainActivity", "the result:" + result);
        }
    
    
    }
    

    以下是该类的smali代码

    #文件头描述
    
    .class public Lcom/social_touch/demo/MainActivity;
    .super Landroid/app/Activity;#指定MainActivity的父类
    .source "MainActivity.java"#源文件名称
    
    #表明实现了View.OnClickListener接口
    # interfaces
    .implements Landroid/view/View$OnClickListener;
    
    #定义float静态字段pi
    # static fields
    .field private static final pi:F = 3.14f
    
    #定义了String类型字段TAG
    # instance fields
    .field private TAG:Ljava/lang/String;
    
    #定义了boolean类型的字段running
    .field public volatile running:Z
    
    #构造方法,如果你还纳闷这个方法是怎么出来的化,就去看看jvm的基础知识吧
    # direct methods
    .method public constructor <init>()V
        .locals 1#表示函数中使用了一个局部变量
    
        .prologue#表示方法中代码正式开始
        .line 8#表示对应与java源文件的低8行
        #调用Activity中的init()方法
        invoke-direct {p0}, Landroid/app/Activity;-><init>()V
    
        .line 10
        const-string v0, "MainActivity"
    
        iput-object v0, p0, Lcom/social_touch/demo/MainActivity;->TAG:Ljava/lang/String;
    
        .line 13
        const/4 v0, 0x0
    
        iput-boolean v0, p0, Lcom/social_touch/demo/MainActivity;->running:Z
    
        return-void
    .end method
    
    #静态方法log()
    .method public static log(I)V
        .locals 3
        .parameter "result"#表示result参数
    
        .prologue
        .line 42
        #v0寄存器中赋值为"MainActivity"
        const-string v0, "MainActivity"
        #创建StringBuilder对象,并将其引用赋值给v1寄存器
        new-instance v1, Ljava/lang/StringBuilder;
    
        #调用StringBuilder中的构造方法
        invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
    
        #v2寄存器中赋值为ther result:
        const-string v2, "the result:"
    
        #{v1,v2}大括号中v1寄存器中存储的是StringBuilder对象的引用.
        #调用StringBuilder中的append(String str)方法,v2寄存器则是参数寄存器.
        invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    
        #获取上一个方法的执行结果,此时v1中存储的是append()方法执行后的结果,此处之所以仍然返回v1的    #原因在与append()方法返回的就是自身的引用
        move-result-object v1
    
        #继续调用append方法(),p0表示第一个参数寄存器,即上面提到的result参数
        invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
    
        #同上
        move-result-object v1
    
        #调用StringBuilder对象的toString()方法
        invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
    
        #获取上一个方法执行结果,toString()方法返回了一个新的String对象,因此v1中此时存储了String对象的引用
        move-result-object v1
    
        #调用Log类中的静态方法e().因为e()是静态方法,因此{v0,v1}中的成了参数寄存器
        invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    
        .line 43
        #调用返回指令,此处没有返回任何值
        return-void
    .end method
    
    
    # virtual methods
    .method public add(II)I
        .locals 1
        .parameter "x"#第一个参数
        .parameter "y"#第二个参数
    
        .prologue
        .line 34
    
        #调用add-int指令求和之后将结果赋值给v0寄存器
        add-int v0, p1, p2
    
        #返回v0寄存器中的值
        return v0
    .end method
    
    
    .method public onClick(Landroid/view/View;)V
        .locals 4
        .parameter "view" #参数view
    
        .prologue
        const/4 v3, 0x4 #v3寄存器中赋值为4
    
        .line 23#java源文件中的第23行
        const/4 v1, 0x5#v1寄存器中赋值为5
    
        #调用add()方法
        invoke-virtual {p0, v3, v1}, Lcom/social_touch/demo/MainActivity;->add(II)I
    
        #从v0寄存器中获取add方法的执行结果
        move-result v0
    
        .line 24#java源文件中的24行
        .local v0, result:I
    
        #v1寄存器中赋值为PrintStream对象的引用out
        sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
    
        #执行out对象的println()方法
        invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V
    
        .line 26
    
        const/16 v1, 0x9#v1寄存器中赋值为9
        const/4 v2, 0x3#v2寄存器中赋值为3
    
        #调用sub()方法,{p0,v1,v2},p0指的是this,即当前对象,v1,v2则是参数
        invoke-virtual {p0, v1, v2}, Lcom/social_touch/demo/MainActivity;->sub(II)I
        #从v0寄存器中获取sub()方法的执行结果
        move-result v0
    
        .line 28
        if-le v0, v3, :cond_0#如果v0寄存器的值小于v3寄存器中的值,则跳转到cond_0处继续执行
    
        .line 29
    
        #调用静态方法log()
        invoke-static {v0}, Lcom/social_touch/demo/MainActivity;->log(I)V
    
        .line 31
        :cond_0
        return-void
    .end method
    
    .method protected onCreate(Landroid/os/Bundle;)V
        .locals 1
        .parameter "savedInstanceState" #参数savedInstancestate
    
        .prologue
        .line 17
    
        #调用父类方法onCreate()
        invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
    
        .line 18
    
        const v0, 0x7f04001a#v0寄存器赋值为0x7f04001a
    
        #调用方法setContentView()
        invoke-virtual {p0, v0}, Lcom/social_touch/demo/MainActivity;->setContentView(I)V
    
        .line 19
        return-void
    .end method
    
    #declared-synchronized表示该方法是同步方法
    .method public declared-synchronized sub(II)I
        .locals 1
        .parameter "x"
        .parameter "y"
    
        .prologue
        .line 38
    
        monitor-enter p0#为该方法添加锁对象p0
         add-int v0, p1, p2
        #释放锁对象
        monitor-exit p0
    
        return v0
    .end method
    

    相关文章

      网友评论

          本文标题:详解smali文件

          本文链接:https://www.haomeiwen.com/subject/imiwfqtx.html