最近由于工作的需要,研究了下反编译相关,自己整理了些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类里截取的一段代码,如果有汇编基础的话,看上面代码应该是没压力的,那么要看懂上面代码,我们需要了解以下几点:
数据类型 | 含义 |
---|---|
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;
格式:.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-virtual {v1, p2}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
这就是调用StringBuilder的append(int)方法,其中v1为StringBuilder对象,p2为方法传进来的参数,对应Java代码:v1.append(p2); 需要注意的是当此方法不是静态方法时,第一个参数p0表示该类的对象,如果是静态方法并且有参数的话,则p0表示第一个参数。
格式:.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。
直接看代码:
.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的锁 |
咱上面把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 //方法结束标志
没错,上面就是开篇引入的代码,我都加上注释了,大家跟着看,就不在一行一行解释了。在这里提几个重要的地方:
constructor ()
和constructor ()
他们有啥区别呢?第一个的作用是在jvm第一次加载class文件时调用,包括静态变量初始化语句和静态块的执行。第二个的作用是在实例创建出来的时候调用,包括调用new操作符。之后找静态变量赋值的地方,就找constructor ()
就可以了。到此,smali语法的整理就结束了,这是我利用空余时间写的第一篇博客,前前后后也花了两三天时间,非常荣幸屏前的你能一直看完,相信这篇文章能帮助到你,如果你觉得这篇文章对你有用,那么赞一个或者留个言吧~
接下来我会写一篇如何修改smali文件相关的博客,希望大家前来学习和讨论!