smali语法整理

1.概续

最近由于工作的需要,研究了下反编译相关,自己整理了些smali语法相关的资料,在这里分享给大家,如果有错误的地方还望大家能指出来,一起讨论学习~

好了,先上代码,咱跟着代码一步一步学习。

.class public Lcom/example/tex/MainActivity;
.super Landroid/app/Activity;
.source "MainActivity.java"

# annotations
.annotation system Ldalvik/annotation/MemberClasses;
    value = {
        Lcom/example/tex/MainActivity$ProtocolState;
    }
.end annotation

# static fields
.field private static final LOGIN_TYPE:J

# instance fields
.field private bb:I
.field d:J

# direct methods
.method static constructor ()V
    .locals 2 

    .prologue 
    .line 16 
    invoke-static {}, Ljava/lang/System;->currentTimeMillis()J

    move-result-wide v0

    sput-wide v0, Lcom/example/tex/MainActivity;->LOGIN_TYPE:J

    return-void
.end method

.method public constructor ()V 
    .locals 2
    .prologue 
    .line 12 
    invoke-direct {p0}, Landroid/app/Activity;->()V

    .line 14
    const/4 v0, 0x1 
    iput v0, p0, Lcom/example/tex/MainActivity;->bb:I

    .line 48
    const-wide/32 v0, 0x87eccb 
    iput-wide v0, p0, Lcom/example/tex/MainActivity;->d:J

    .line 12
    return-void 
.end method 

这是从activity类里截取的一段代码,如果有汇编基础的话,看上面代码应该是没压力的,那么要看懂上面代码,我们需要了解以下几点:

  • smali的数据类型
  • smali的方法定义和调用
  • smali的成员变量定义和调用
  • smali的逻辑定义
  • smali文件内容分析

1.smali的数据类型

数据类型 含义
I int
B byte
C char
D double
F float
J long
S short
V void
Z boolean
[XXX 数组
LXXX/YY 对象

前面几种类型相信大家都清楚,后面两个说一下:数组的表示方式是:在类型前加上前中括号“[”,例如int数组和String数组分别表示为:[I、[Ljava/lang/String;对象的表示则以L作为开头,格式是LpackageName/objectName;

2.smali的方法定义和调用

格式:.method modify FunctionName(ParamType,ParamType1….) ReturnType

例子:.method public setName (Ljava/lang/String;)v 这就是设置名字的方法,返回值为空,对应java方法:public void setName(String name){}

注意地方:当参数是基本类型时,参数之间没有“;”如:

.method public setXXX (ZII)v —> public void setXXX(boolean b,int i,int j){}

调用方式:

  • invoke-direct:调用private修饰的方法
  • invoke-virtual:调用除private修饰外的方法
  • invoke-static : 调用静态方法
  • invoke-super:在重写父类方法里,调用父类方法
  • invoke-interface:调用接口方法
  • invoke-xxxxx/range:当方法的参数大于4个时,用这种方式调用

例子:
invoke-virtual {v1, p2}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
这就是调用StringBuilder的append(int)方法,其中v1为StringBuilder对象,p2为方法传进来的参数,对应Java代码:v1.append(p2); 需要注意的是当此方法不是静态方法时,第一个参数p0表示该类的对象,如果是静态方法并且有参数的话,则p0表示第一个参数。

3.smali的成员函数定义

格式:.field modify fieldName:fieldType;(如果不是基本类型的话,最后这个“;”不能丢)

例子:.field private final u:Ljava/lang/Runnable; 定义了个全局的runnable对象。

调用方式:sget、iget、sget-boolean、iget-object、iget-boolean、sget-object、sput、iput、sput-boolean、iput-object、iput-boolean、sput-object等。一般地,没有“-object”后缀的表示操作的成员变量对象是基本数据类型,带“-object”表示操作的成员变量是对象类型,而”iget”或“iput”前缀表示得到或设置成员变量值,“sget”或“sput”前缀表示得到或设置静态变量值。

例子:iget v1, p0, Lcom/example/tex/MainActivity;->bb:I 表示:获取MainActivity的成员变量bb的值并赋给v1。

4.smali的逻辑定义

直接看代码:

.line 1452
const-string v0, "tag"
const-string v1, "demo"
if-eqz v0, :cond_0
invoke-static {v1, v0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
cond_0:
return-void

对应的java代码:
String msg = “demo”;
if(msg != null){
Log.i(“tag”,msg);
}
相信大家都已经明白了,这里就不在啰嗦。下面是我整理的一些常用smali逻辑相关语法:

逻辑语句 含义
if-eq vx,vy,目标 如果vx==vy,跳转到目标
if-ne vx,vy,目标 如果vx!=vy,跳转到目标
if-lt vx,vy,目标 如果vx < vy,跳转到目标
if-ge vx,vy,目标 如果vx>=vy,跳转到目标
if-gt vx,vy,目标 如果vx > vy,跳转到目标
if-le vx,vy,目标 如果vx<=vy,跳转到目标
if-eqz vx,目标 如果vx==0,跳转到目标
if-nez vx,目标 如果vx!=0,跳转到目标
if-ltz vx,目标 如果vx < 0,跳转到目标
if-gez vx,目标 如果vx>=0,跳转到目标
if-gtz vx,目标 如果vx > 0,跳转到目标
if-lez vx,目标 如果vx<=0,跳转到目标
if-eqz vx,vy,目标 如果vx==vy,跳转到目标
movevx,vy 将vx的值赋给vy
move-result vx 将上一次调用方法的返回值赋给vx
return-void 返回空值
const/4 vx,值 存入4位常量到vx
new-instance vx,类型ID 创建一个类型ID的对象
aget vx,vy,vz 从int数组vy中获取索引为vz的值存到vx
aget-wide vx,vy,vz 从long/double数组vy中获取索引为vz的值存到vx
aget-boolean vx,vy,vz 从boolean数组vy中获取索引为vz的值存到vx
aget-object vx,vy,vz 从对象数组vy中获取索引为vz的值存到vx
aput vx,vy,vz 将vx的值存入int数组vy的vz索引处
aput-object vx,vy,vz 将vx的值存入对象数组vy的vz索引处
iget vx,vy,x 将引用vy获取int类型字段x的值赋给vx
iget-object vx,vy,x 将引用vy获取字段x的值赋给vx
iput vx,vy, x 通过引用vy将vx的值赋给int类型的字段x
iput-object vx,vy,x 通过引用vy将vx的值赋给对象x
sget vx,字段ID 获取某个类的int类型的静态字段的值赋给vx
sget-object vx,字段ID 获取某个类的静态对象赋给vx
int-to-float vx,vy 将int类型的vy的值强转成float类型存入vx
float-to-int vx,vy 将float类型的vy的值强转成int类型存入vx
add-int vx,vy,vz vx = vy + vz (vx,vy,vz都为int类型)
sub-int vx,vy,vz vx = vy - vz
mul-int vx,vy,vz vx = vy * vz
div-int vx,vy,vz vx = vy / vz
rem-int vx,vy,vz vx = vy % vz
and-int vx,vy,vz vx = vy & vz
or-int vx,vy,vz vx = vy
add-int/2addr vx,vy vx = vx + vy
sub-int/2addr vx,vy vx = vx - vy
mul-int/2addr vx,vy vx = vx * vy
div-int/2addr vx,vy vx = vx / vy
neg-float vx, vy vx = -vy (将vy值变成负值给vx)
monitor-enter vx 为指定的对象vx获取锁
monitor-exit vx 释放指定的对象vx的锁

5.smali文件内容分析

咱上面把smali文件中常用的语法都过了一遍,然后接下来我们将内容在分析一遍,基本上你就可以很轻松的看懂smali文件了,废话不多说,上代码~

.class public Lcom/example/tex/MainActivity;//类的全名
.super Landroid/app/Activity;//此类的父类
.source "MainActivity.java"//文件名

# annotations//内部类ProtocolState声明
.annotation system Ldalvik/annotation/MemberClasses;
    value = {
        Lcom/example/tex/MainActivity$ProtocolState;
    }
.end annotation

# static fields
.field private static final LOGIN_TYPE:J //long类型的静态变量

# instance fields
.field private bb:I//int类型的成员变量bb
.field d:J//long类型的成员变量d

.method static constructor ()V
    .locals 2 //表示此方法里用了2个寄存器,寄存器是用来存局部变量的值

    .prologue //表示方法开始
    .line 16 //行数
    invoke-static {}, Ljava/lang/System;->currentTimeMillis()J //获取系统当前时间

    move-result-wide v0 //将系统当前时间存入v0

    sput-wide v0, Lcom/example/tex/MainActivity;->LOGIN_TYPE:J //将v0赋值给静态变量LOGIN_TYPE

    return-void //空返回
.end method //方法结束标志

# direct methods
.method public constructor ()V //构造函数
    .locals 2//表示此方法里用了2个寄存器,寄存器是用来存局部变量的值
    .prologue //表示方法开始
    .line 12 //行数
    invoke-direct {p0}, Landroid/app/Activity;->()V//调用init()方法

    .line 14
    const/4 v0, 0x1 //定义一个局部变量v0 值为1(十六进制)
    iput v0, p0, Lcom/example/tex/MainActivity;->bb:I//将v0的值赋给成员变量bb

    .line 48
    const-wide/32 v0, 0x87eccb //将0x87eccb存入32位常量
    iput-wide v0, p0, Lcom/example/tex/MainActivity;->d:J//将v0的值赋给成员变量d

    .line 12
    return-void //空返回
.end method //方法结束标志

没错,上面就是开篇引入的代码,我都加上注释了,大家跟着看,就不在一行一行解释了。在这里提几个重要的地方:

  1. 代码中的“#”是smali里面的注释标识。
  2. 大家看得仔细点就会看到该类有两个构造函数,constructor ()constructor ()他们有啥区别呢?第一个的作用是在jvm第一次加载class文件时调用,包括静态变量初始化语句和静态块的执行。第二个的作用是在实例创建出来的时候调用,包括调用new操作符。之后找静态变量赋值的地方,就找constructor ()就可以了。
  3. 第一个构造方法里的.locals 2:表示此方法里用了2个寄存器v0,v1。寄存器是用来存局部变量的值,这个字段很重要,当你在植入代码时,如果用到了寄存器,但是没有修改此值,那就很可能报VerifyError错误。
  4. 第一个构造方法里的.prologue:表示方法的开始。这个也同上面重要,要是你植入的代码在这个字段前就不会达到预期的效果。

到此,smali语法的整理就结束了,这是我利用空余时间写的第一篇博客,前前后后也花了两三天时间,非常荣幸屏前的你能一直看完,相信这篇文章能帮助到你,如果你觉得这篇文章对你有用,那么赞一个或者留个言吧~

接下来我会写一篇如何修改smali文件相关的博客,希望大家前来学习和讨论!

你可能感兴趣的:(反编译技术,smali语法,android,反编译,android,smali,java,annotation)