Smali学习笔记
该文转载自乱码三千 – 分享实用IT技术
1.smali和Java基本数据类型对比
Java | smali |
---|---|
byte | B |
short | S |
int | I |
long | J |
float | F |
double | D |
char | C |
boolean | B |
void | V |
数组 | [ |
object | L+全类名路径(用/分割) |
2.注释
在smli语言中注释用"#"表示
# 我是注释
3.类声明
.class +权限修饰符 +类名;
比如以下Java代码:
public class Test{
}
用smali代码表示为:
.class public LTest; # 声明类(必须)
.super Ljava/lang/Object; # 声明父类 默认继承Object(必须)
.implements Ljava/lang/CharSequence;#如果实现了接口则添加
.source "Test.java" # 源码文件(非必须)
4.关于分号;
凡是L开头全包名路径结尾都需要加分号
5.字段声明(成员/全局变量)
.field 权限修饰符+静态修饰符(如果是) +变量名:变量全类名路径;
比如以下Java代码:
private static String a;
用smali代码表示为:
.field private static a:Ljava/lang/String;
补充:
基本数据类型示例:
.method public final pubFinalMethod()V
.field private boType:Z # boolean
.field private byteType:B # byte
.field private shortType:S # short
.field private charType:C # char
.field private intType:I # int
.field private longType:J # long
.field private floatType:F # float
.field private doubleType:D # double
6.常量声明
.field 权限修饰符+静态修饰符(如果有) final +变量名:变量全类名路径;=常量值
比如以下Java代码:
private static final String a="hello";
用smali代码表示为:
.field private static final a:Ljava/lang/String;="hello"
注意:
-
静态属性赋值在clinit代码块中进行:public static String a=“A”;
.field public static a:Ljava/lang/String; .method static constructor <clinit>()V const-string v0,"A" sput-object v0,LTest;->a:Ljava/lang/String; return-void .end method
-
非静态属性赋值(包括final)在init构造器中进行:public final String a=“A”;
.field public final a:Ljava/lang/String; .method public constructor <init>()V invoke-direct {p0},Ljava/lang/Object;-><init>() const-string v0,"A" iput-object v0,p0,LTest;->a:Ljava/lang/String; return-void .end method
7.成员方法/函数声明
.method 权限修饰符+静态修饰符(如果有) +方法名(参数类型)返回值类型
# 方法体
.end method # 方法结尾标志
比如以下Java代码:
public static void getName(){}
用smali代码表示为:
.method public static getName()V
return-void
.end method
7.1如果是带参并且带有返回值的方法
比如以下Java代码:
public String getName(String p){
return "hello"
}
用smali代码表示为:
.method public getName(Ljava/lang/String;)Ljava/lang/String;
const-string v0,"hello"
return-object v0
.end method
8.关于方法返回关键字
主要有以下四种:
return-void
return-object
return
return-wide # 表示返回值为64位非对象类型的值(8个字节) 如:long、double
数据类型队友关系表如下:
Java | smali方法返回关键字 |
---|---|
char | return |
boolean | return |
byte | return |
short | return |
int | return |
float | return |
long | return-wide |
double | return-wide |
void | return-void |
数组 | return-object |
object | return-object |
9.构造方法/构造函数声明
.method 权限修饰符 constructor <init>(参数类型)返回值类型
//super();
invoke-direct {p0},Ljava/lang/Object;-><init>()V
//逻辑代码
return-void
.end method
比如以下Java代码
public class Test{
public Test(String a){}
}
用smali代码表示为:
.method public constructor <init>(Ljava/lang/String;)V
invoke-direct {p0},Ljava/lang/Object;-><init>()V
return-void
.end method
需要注意的是内部类构造函数时,会先调用外部类的默认初始化
-
反射表示为:使用反射获取内部类实例对象时,需要将外部类实例对象传入,且只能通过clazz2.getConstructors()[0].newInstance(instance)获取实例对象
public class Test{ public class Test2{} } //反射获取Test2的对象 Class<?> clazz = Class.forName("Test");//外部类 test=clazz.getConstructor().newInstance();//外部类实例对象 Class<?> clazz2 = Class.forName("Test$Test2");//内部类 test2=clazz2.getConstructors()[0].newInstance(test);//内部类对象 //错误的 //test2=clazz2.getConstructor().newInstance(test); //如果是静态内部类,则不需要传入外部类的引用 test2=clazz2.getConstructors()[0].newInstance();
-
smali表示为:内部类newInstance的第一个参数必须是外部类实例的引用(如果是static内部类,则不需要传入外部类的实例)
new-instance v0,LTest$Test2; //p0表示外部类this invoke-direct {v0,p0},LTest$Test2;-><init>(LTest;)V //静态内部类 invoke-static {v0},LTest$Test2;-><init>()V
原因:内部类定义了外部类的对象引用,在内部类初始化时,会通过iput方法将外部类的实例赋值给这个属性
//synthetic合成的 固定写法 .field final synthetic this$0:Lcom/hza/mytestdemo/TestDemo; .method public constructor <init>(Lcom/hza/mytestdemo/TestDemo;)V //将外部类实例赋值给this$0属性 iput-object p1, p0, Lcom/hza/mytestdemo/TestDemo$InterClass;->this$0:Lcom/hza/mytestdemo/TestDemo; invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-void .end method
this$0说明:表示类内的第一层内部类,即类的内部类。
this$1说明:表示类内的第二层内部类,即类的内部类的内部类。
public class Test{ public TestInner{}//this$0 } //this$1表示类内的第二层内部类 public class Test{ public TestInner{ public TestInner_Inner{}//this$1 }//this$0 }
-
匿名内部类当成普通属性反射即可
class OuterClass { public Runnable runnable = new Runnable() { public void run() { System.out.println("runnable."); } }; } Class<?> test = Class.forName("OuterClass"); Field runnable = test.getDeclaredField("runnable"); //获取属性对象 Object o=runnable.get(test.newInstance()); //设置属性对象 runnable.set(test.newInstance(),new Runnable() { @Override public void run() { } });
smali中匿名内部类:跟内部类一样,生成的文件是Test$1
第二个匿名内部类是Test$2,依次类推,其余跟内部类一样
new-instance v0, LTest$1; invoke-direct {v0, p0}, LTest$1;-><init>(LTest;)V
-
smali中内部类的一些说明:
system Ldalvik/annotation/EnclosingMethod; 表示外部为方法,常见于匿名内部类 system Ldalvik/annotation/EnclosingClass; 表示外部为类,常见于内部类 system Ldalvik/annotation/InnerClass; 表示内部类 accessFlags 访问标识,一般内部类为0x0,内部接口为0x609 system Ldalvik/annotation/MemberClasses; 外部类中指明内部类 //在内部类中 .annotation system Ldalvik/annotation/EnclosingClass;//外部类说明 value = LTest;//外部类全路径 .end annotation .annotation system Ldalvik/annotation/InnerClass;//内部类说明 accessFlags = 0x0 //访问标识,表示为普通内部类 name = "InterClass" //内部类名称,表示LTest类的"InterClass"内部类 .end annotation //在外部类中 .annotation system Ldalvik/annotation/MemberClasses;//类中的内部类成员 value = { Lcom/hza/mytestdemo/TestDemo$InterClass1;, Lcom/hza/mytestdemo/TestDemo$InterClass2;, } .end annotation
10.静态代码块的声明
.method static constructor <clinit>()V
.end method
比如以下Java代码:
public class Test{
static{
}
}
用smali代码表示为:
.method public static constructor <clinit>()V
.end method
11.方法调用
11.1关键字
invoke-virtual 用于非私有实例方法的调用
invoke-direct 用于构造方法及私有方法的调用
invoke-static 静态方法的调用
invoke-super 调用父类方法
invoke-interface 调用接口方法
11.2非私有实例方法得调用
invoke-virtual {参数}, 方法所属类名;->方法名(参数类型)返回值类型
比如以下Java代码:
public class Test{
pubic Test(String a){
getName();
}
public String getName(){
return "hello";
}
}
用smali代码表示为:
.method public constructor <init>(Ljava/lang/String;)V
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
invoke-virtual {p0}, LTest;->getName()Ljava/lang/String;
move-result-object v0
return-void
.end method
.method public getName()Ljava/lang/String;
const-string v0,"hello"
return-object v0
.end method
11.3私有方法或者构造方法的调用
invoke-direct {参数}, 方法所属类名;->方法名(参数类型)返回值类型
私有方法调用:
比如以下Java代码:
public class Test{
pubic Test(String a){
getName();
}
private String getName(){
return "hello";
}
}
用smali代码表示为:
.method public constructor <init>(Ljava/lang/String;)V
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
invoke-direct {p0}, LTest;->getName()Ljava/lang/String;
move-result-object v0
return-void
.end method
.method private getName()Ljava/lang/String;
const-string v0,"hello"
return-object v0
.end method
构造方法调用
比如以下代码:
public class Test{
public Test(String a){
new Test2("hello");
}
public class Test2{
public Test2(String a){}
}
}
用smali代码表示为:
# 匿名内部类得声明
.annotation system Ldalvik/annotation/MemberClasses;
value={
LTest$Test2;
}
.end annotation
.method public constructor <init>(Ljava/lang/String;)V
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
# 创建类对象
new-instance v0,LTest$Test2;
# 定义参数
const-string v1,"hello"
# 创建对象
invoke-direct {v0,p0,v1}, LTest&Test2;-><init>(LTest;Ljava/lang/String;)
return-void
.end method
11.4静态方法的调用并返回值
invoke-static {参数}, 方法所属类名;->方法名(参数类型)返回值类型;
比如以下Java代码:
public class Test{
public Test(String a){
String b=getName();
System.out.print(b);
}
public static String getName(){
return "hello";
}
}
用smali代码表示为:
.method public constructor <init>(Ljava/lang/String;)V
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
invoke-static {}, LTest;->getName()Ljava/lang/String;
move-result-object v0
return-void
.end method
.method public static getName()Ljava/lang/String;
const-string v0,"hello"
return-object v0
.end method
11.5父类成员的方法调用
invoke-super
比如以下Java代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
用smali代码表示为:
.method protected onCreate(Landroid/os/Bundle;)V
invoke-super {p0,p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
return-void
.end method
11.6接口的调用
invoke-interface {参数}, 方法所属类名;->方法名(参数类型)返回值类型;
比如以下java代码:
public class Test{
private InterTest a=new Test2();
public Test(String a){}
public void setAa(){
InterTest aa=a;
aa.est2();
}
public class Test2 implements InterTest{
public Test2(){}
public void est2(){}
}
interface InterTest{
public void est2();
}
}
用smali代码表示为:
.method public constructor <init>(Ljava/lang/String;)V
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
new-instance v0, LTest$Test2;
invoke-direct {v0, p0}, Lest$Test2;-><init>(LTest;)V
return-void
.end method
.method public setAa()V
iget-object v0,p0,LTest;->a:
invoke-interface {v0},LTest$InterTest;->est2()V
.end method
12.类使用注解
12.1注解说明
.annotation 注解属性(runtime、或者build之类的) 注解类目
注解字段=注解值
.end annotation
如以下Java代码:
//属性
@MyAnnotation("Hello World!")
public String sayWhat;
//注释
public @interface MyAnnotation {
String value();
}
转换为smali代码:
.field
.annotation
value=
.end annotation
.end field
.field public sayWhat:Ljava/lang/String;
.annotation build Lcom/hza/mytestdemo/MyAnnotation;//注解属性 注解类目
value = "Hello World!"//注解字段=注解值
.end annotation
.end field
13.创建对象
13.1对象的创建分多步进行
//声明实例
new-instance v寄存器(存放对象引用) 对象全包名路径;
//调用构造方法实例化(如果调用有参构造,那么在调用之前还需要提前声明,然后在invoke的时候当作参数一并传入)
invoke-direct {变量名},对象全包名路径;-><init>(参数)返回类型
如:
new-instance v0,Ljava/lang/StringBuilder;
//参数
const-string v1,"hehe"
//调用有参构造方法
invoke-direct {v0,v1},Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
13.2数组的创建(数组本身也是对象)
//指定数组个数
const/4 v1,0x4
//声明数组实例
new-array v寄存器(存放数组对象引用),v寄存器(个数),[I
//填充数组
fill-array-data v寄存器(数组对象引用),:ayyay_8
:array_8
//填充
.array-data
0x0
.end array-data
如:
const/4 v1,0x4
new-array v0,v1,[I
fill-array-data v0, :array_8
:array_8
.array-data 4
0x0
0x1
0x2
0x3
.end array-data
14.数据的定义
14.1分三大类
-
字符串类型数据:const-string
-
字节码数据:const-class
-
数值型数据:const
数值型数据拆分
第一种 const开头 占用一个容器(寄存器) 32位/容器(第一位默认为符号位) const (/4/16/high16) vA,xx表示将xx赋值给vA const v寄存器,数值 *const/4:最大值允许存放4位数值(4个二进制位) 符号位第二位第三位第四位(-8~7) *const/16最大值允许存放16位数值 *const/32最大32位 *const/high16 v0,0xFFFF0000 第二种 const-wide 占用两个容器(寄存器) 64位 const-wide(/16/32/high16) vAA,xxxx表示将xxxx赋值给寄存器vAA、vAA+1 const-wide v寄存器,数值L //占用v0和v1
14.2总结
1.const-string
//String str="hello";
const-string v0,"hello" //定义字符串 将字符串hello赋值给v0
2.const-class
//Class clazz=GoActivity.class;
const-class v0,LGoActivity;
3.const
//以下数据定义高位默认为符号位
const/4 v0,0x2 # 定义一个容器 最大只允许存放半字节4位数据 取值范围为 -8 and 7
const/16 v0 , 0xABCD # 定义定义一个容器 最大只允许存放16位数据 比如short类型数据 取值范围为-32768~32767
const v0 , 0xA# 定义一个容器 最大只允许存放32位数据,比如int类型数据 将数字10赋值给v0 取值范围-2147483647~2147483647
const/high16 #定义一个容器 最大只允许存放高16位数值 比如0xFFFF0000末四位补0 存入高四位0XFFFF
//const-wide 占用两个寄存器vx和v(x+1)且这两个寄存器相连, 数值必须以L结尾 否则编译不通过
const-wide/16 # 定义两个相连容器 最大只允许存放16位数据
const-wide/32 # 定义两个相连容器 最大只允许存放32位数据
const-wide # 定义两个相连容器 最大只允许存放64位数据
const-wide/high16 # 定义两个相连容器 只允许存放高16位数据
15.字段赋值/取值
赋值(iput-object/sput-object):->
- static显式初始化在clinit构造器中,非static显式初始化在init构造器中
- iput-object v0,p0,LTest;->a:Ljava/lang/String;//将v0的值赋给p0的a属性
- sput-object v0,LTest;->a:Ljava/lang/String;//将v0的值赋给Test.a
取值(iget-object/sget-object):<-
- iget-object v0,p0,LTest;->a:Ljava/lang/String;//将p0的a属性的值赋给v0寄存器
- sget-object v0,LTest;->a:Ljava/lang/String;//将Test.a赋给v0寄存器
15.1静态字段赋值
关键代码
sput-object 源寄存器(赋值内容),类全包名路径;->字段:字段类型全包名路径//s指static
比如以下Java代码:
public class Test{
private static String a="g";
}
用smali代码表示为:
.class public LTest;
.super Ljava/lang/Object;
.source "Test.java"
.field private static a:Ljava/lang/String;
.method public constructor <clinit>(Ljava/lang/String;)V
const-string v0, "g"
sput-object v0,LTest;->a:Ljava/lang/String;
return-void
.end method
15.2类非静态字段赋值
关键代码
iput-object 源寄存器(赋值内容),目的寄存器(对象引用),类全包名路径;->字段:字段类型全包名路径//i指instance
如以下Java代码:
public class Test{
private String a="g";
public Test(String a){
}
public void setAa(){
a="b";
}
}
用smali代码表示为:
.class public LTest;
.super Ljava/lang/Object;
.source "Test.java"
.field private a:Ljava/lang/String;
.method public constructor <init>(Ljava/lang/String;)V
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
const-string v0, "g"
iput-object v0, p0, LTest;->a:Ljava/lang/String;
return-void
.end method
.method public setAa()V
const-string v0, "b"
iput-object v0, p0, LTest;->a:Ljava/lang/String;
return-void
.end method
15.3静态字段取值
关键代码
sget-object 目的寄存器,类全包名路径;->字段:字段类型全包名路径
比如以下Java代码:
public class Test{
private static String a="hello";
public Test(String a){
}
public void setAa(){
String aa=a;
}
}
用smali代码表示为:
.class public LTest;
.super Ljava/lang/Object;
.source "Test.java"
.field private a:Ljava/lang/String;
.method public constructor <clinit>()V
const-string v0, "hello"
iput-object v0, p0, LTest;->a:Ljava/lang/String;
return-void
.end method
.method public constructor <init>(Ljava/lang/String;)V
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method public setAa()V
sget-object v0,LTest;->a:Ljava/lang/String;
return-void
.end method
15.4类非静态字段取值
关键代码:
iget-object 目的寄存器,源寄存器,类全包名路径;->字段:字段类型全包名路径
比如以下Java代码:
public class Test
{
private String a="hello";
public Test(String a){
}
public void getA(){
String aa=a;
}
}
用smali代码表示为:
.class public LTest;
.super Ljava/lang/Object;
.source "Test.java"
.field private a:Ljava/lang/String;
.method public constructor <init>(Ljava/lang/String;)V
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
const-string v0, "hello"
iput-object v0, p0, LTest;->a:Ljava/lang/String;
return-void
.end method
.method public getA()V
iget-object v0, p0, LTest;->a:Ljava/lang/String;
return-void
.end method
15.5注意:以上赋值/取值方法都是以String对象举例,如果是基本数据类型,那么按照如下处理
const/4 v0,0x1 //实例变量值内容定义 值皆为十六进制
smali取值赋值和值定义关键字 | java |
---|---|
iget-byte、iput-byte、const/4 | byte |
iget-short、iput-short、const/4 | short |
iget-char、iput-char、const/16 | char |
iget-boolean、iput-boolean、const/4 | boolean |
iget、iput、const/4 | int |
iget、iput、const/high16 | float |
iget-wide、iput-wide、const/high16 | double |
iget-wide、iput-wide、const-wide/16 | long |
iget-object、iput-object | 引用数据类型 |
16.基本运算
加减乘除 | 描述 |
---|---|
int-to-double v1,v0 | 基本运算转换(转换v0存入v1) |
add-int/2addr v1,v0 | 两个int数值v0,v1相加(结果存入v1) |
add-int v2,v1,v0 | 两个int数值v0,v1相加(结果存入v2) |
位运算 | 描述 |
or-int/lit8 v1,v0,0x1 | v0进行或运算|结果放入v1 |
and-int/lit8 v1,v0,0x1 | v0进行与运算&结果放入v1 |
shr-int/lit8 v1,v0,0x1 | v0进行与右移>结果放入v1 |
shl-int/lit8 v1,v0,0x1 | v0进行左移<结果放入v1 |
rem-int/lit8 v1,v0,0x1 | v0进行取模结果放入v1 |
16.1加法(减法sub、乘法mul、除法div)
如以下Java代码:
int a = 1;
float b = 1.5f;
return a + b;
对应的smali代码为:
const/4 v0, 0x1 //int a=1;
const/high16 v1, 0x3fc00000 //float b=1.5f
int-to-float v2, v0 //将int转为float
add-float/2addr v2, v1 //将v1 v2的值相加,结果存入v2
add-float v3 v2, v1 //将v1 v2的值相加,结果存入v3
16.2布尔运算(了解 &有对应的smali关键字,&&没有按条件语句判断)
如以下Java代码:
private boolean bool(boolean a, boolean b,boolean c) {
return a && b || c;
}
对应的smali代码为:
.method private bool(ZZZ)Z
.registers 5
.param p1, "a" # Z
.param p2, "b" # Z
.param p3, "c" # Z
.prologue
.line 11
if-eqz p1, :cond_4
if-nez p2, :cond_6
:cond_4
if-eqz p3, :cond_8
:cond_6
const/4 v0, 0x1
:goto_7
return v0
:cond_8
const/4 v0, 0x0
goto :goto_7
.end method
17.逻辑语句
17.1条件跳转分支
//v0与v1比较 比如if-lt v0,v1 如果v0<v1
if-eq v0,v1, :cond_1//equal
if-ne v0,v1, :cond_1//not equal
if-lt v0,v1, :cond_1//less than
if-ge v0,v1, :cond_1//great equal
if-le v0,v1, :cond_1//less equal
if-eqz v0,v1, :cond_1//equal 0
if-nez v0,v1, :cond_1//not equal 0
if-ltz v0,v1, :cond_1//less than 0
if-lez v0,v1, :cond_1//less equal 0
if-gez v0,v1, :cond_1//great equal 0
if-gtz v0,v1, :cond_1//great than 0
17.2循环
const/4 v0,0x0 | int i=0 |
---|---|
const/4 v1,0xa | 10为终止条件 |
if-ge v0,v1 | 如果i>=10 |
add-int/lit8 v0,v0,0x1 | i=i+1 |
比如以下Java代码:
for(int i=0;i<=10;i++){}
对应的smali代码为:
const/4 v0, 0x0 //int i=0
:goto_1
const/16 v1, 0xa //10
if-ge v0, v1, :cond_8 //if i>=10
add-int/lit8 v0, v0, 0x1
goto :goto_1 //继续循环
:cond_8 //退出循环
return-void
18.smali语法关键字
.line:表示Java中的一行
:cond_0:条件分支,配合if使用
.prologue:程序的开始,可省略
:goto_0:跳转分支,配合goto关键字使用
.local:显示局部变量别名信息
.local v2, "b":D //v2存储double类型的变量b
.locals N:标明这个函数中最少要用到的本地寄存器的个数,即v寄存器(v0~v15)
.register N:标明这个函数中最少要用到的寄存器个数,即p寄存器+v寄存器
19.一些Dalvik指令操作
-
返回操作
- return
- return-void
- return-object
- return-wide
-
赋值操作
- const
- const-string
- const-class
-
get/put操作
- get/put-byte
-
get/put-char
- get/put-short
- get/put
- get/put-object
- get/put-wide
-
移位操作
-
move
move v1,v2 将v2的值移到v1 move/from 16 v1,v2 将16位的v2的值移到8位的v1 move/16 v1,v2 将16位的v2的值移到16位的v1 -
move-wide
move-wide v1,v2 将v2的值移到v1 move-wide/from 16 v1,v2 将16位的v2的值移到8位的v1 move-wide/16 v1,v2 将16位的v2的值移到16位的v1 -
move-object
move-object v1,v2 将v2的值移到v1 move-object/from 16 v1,v2 将16位的v2的值移到8位的v1 move-object/16 v1,v2 将16位的v2的值移到16位的v1 -
move-result
move-result v1 将结果移到v1 -
move-result-object
move-result-object v1 将结果移到v1 -
move-result-wide
move-result-wide v1 将结果移到v1寄存器对 -
move-exception
move-exception v1 将异常移到v1
-
PDF下载地址smali学习笔记