首页 > 其他分享 >smail基本语法示例

smail基本语法示例

时间:2022-12-07 21:11:34浏览次数:63  
标签:lang v0 object const Ljava 示例 语法 smail cond

准备工作

工欲善其事必先利其器,所以要学好smail语法,好的工具是必须的,这里推荐两个工具,一个是Smali2Java,也就是将smail文件转换成java的工具;另一个工具是J2S2J1.3,这个工具可以将简单的java或者smail代码进行转换。

案例

因为我是一开始直接看源码的,所以我们今天也是直接从源码开始:

.super Ljava/lang/Object;
.source "Test.java"


# instance fields
.field private name:Ljava/lang/String;


# direct methods
.method public constructor <init>()V
    .registers 2

    .prologue
    .line 1
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    .line 2
    const-string v0, "test"

    iput-object v0, p0, LTest;->name:Ljava/lang/String;

    return-void
.end method

.method public static main([Ljava/lang/String;)V
    .registers 4

    .prologue
    const/4 v2, 0x1

    .line 14
    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v1, "Hello World!"

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

    .line 15
    new-instance v0, LTest;

    invoke-direct {v0}, LTest;-><init>()V

    invoke-virtual {v0, v2, v2}, LTest;->test(IZ)Ljava/lang/String;

    move-result-object v0

    .line 16
    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;

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

    .line 17
    return-void
.end method


# virtual methods
.method public test(IZ)Ljava/lang/String;
    .registers 4

    .prologue
    .line 21
    const-string v0, "test"

    return-object v0
.end method

上面这些代码其实很简单,就是创建一个Test类,定义了一个私有成员属性name,并进行了赋值,然后定义了一个main方法,main方法中进行了Hello World!打印输出操作,对应的java代码:

import java.io.PrintStream;

public class Test{

    public Test()    {
        name = "test";
        id = 1;
    }

    public static void main(String args[]){
        System.out.println("Hello World!");
String result = new Test().test(1, true);
System.out.println(result);
    }

    public String test(int i, boolean flag) {
return "test";
    }

    private int id;
    private String name;
}

代码拆解

下面我们来逐步讲解下smail的语法,方便大家建立它与java代码语法直接的关系。

smail文件,我的理解是它其实就类似于java编译之后的.class文件,也是一种字节码文件。

基本信息

我们先看前三行:

.super Ljava/lang/Object;
.source "Test.java"


# instance fields
.field private name:Ljava/lang/String;

目前没有找到官方的相关文档,只能结合网上的资料和自己的推测进行分析,如果有不合理的地方,希望大家不吝指教。

第一行.super表示当前类的父类,表示继承关系的

第二行.source表示当前字节码文件对应的源码文件名

第三行.field表示定义一个属性(字段),具体语法是

.field 访问权限 字段名:字段类型;

字段类型的取值范围如下:

Dalvik字节码类型 java基本数据类型
V void
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L java类类型
[ 数组类型

我们这里的Ljava/lang/String就表示String类型的数据,如果是int类型,那语法应该是这样的:

.field private id:I

这里的对照关系,和class字节码绝大部分都一样

构造方法

紧接着,下面是构造方法对应的smail代码

# direct methods
.method public constructor <init>()V
    .registers 2

    .prologue
    .line 1
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    .line 2
    const-string v0, "test"

    iput-object v0, p0, LTest;->name:Ljava/lang/String;

    return-void
.end method
.method

其中.method表示方法的开始,具体语法是:

.method 访问权限 方法名()返回结果

这里的返回结果和字段的类型取值一样,可以参考上面的表格

.end method表示方法的结束,与.method相对应,两者之间的内容为方法体

.registers

.registers 2用于声明当前方法的寄存器个数,寄存器是用来存放方法的入参和方法的局部变量的,具体语法为:

.registers N

N代表需要的寄存器的总个数

同时,还有一个关键字.locals,它用于声明非参数的寄存器个数(也就是举报变量的个数,包含在registers声明的个数当中),也叫做本地寄存器,语法是一样的。

.prologue

.prologue表示方法代码的开始处,所以在方法中增加代码,只能在.prologue下面的区域进行

.line

.line 1用于标记java代码中的行数,没有实际含义

.invoke-direct

invoke-direct {p0}, Ljava/lang/Object;-><init>()V表示调用privateinit方法,p0表示java中的this,所以这个方法的含义是调用Objectinit方法,并把返回值赋给p0,这里需要补充一个小知识点:

在smali里的所有操作都必须经过寄存器来进行,本地寄存器用v开头数字结尾的符号来表示,如v0v1v2、...

参数寄存器则使用p开头数字结尾的符号来表示,如p0p1p2、...特别注意的是,p0不一定是函数中的第一个参数,在非static函数中, p0代指“this”,p1表示函数的第一个参数,p2代表函数中的第二个参数…,而在static函数中p0才对应第一个参数(因为Javastatic方法中没有this方法)。

本地寄存器没有限制,理论上是可以任意使用的。

这里需要注意的是,再使用vxpx的时候一定不要超过定义的限制(也就是.registers.locals),虽然超过代码编译时不会报错,但是在运行时会报错,而且在代码反编译的时候也会报错:

虽然这种报错不一定是参数个数超了,但如果之前反编译正常,修改之后不正常,这块可以作为一个检查点

const-string

const-string v0, "test"表示定义一个String常量,并把值赋给v0,也就是第一个本地寄存器。

下面再补充一些和const-string相关的内容

const
  • 定义常量

const/44代表4个字节,最大只允许存放4位数值(4个二进制位),取值范围为 -8 and 7

# 定义常量2,并将值赋给v2寄存器,
const/4 v2, 0x02

const/16 同上,最大值允许存放16位数值 第一位默认为符号位,所以计算后15位的数值,比如short类型数据 取值范围为-32768~32767

# 定义定义一个容器,将数字123123赋给v0
const/16 v0 , 0x123123

const 最大只允许存放32位数据,比如int类型数据, 取值范围-2147483647~2147483647

# 定义一个容器 将数字10赋值给v0
const v0 , 0x10

const/high16 最大只允许存放高16位数值

#定义一个容器  比如0xFFFF0000末四位补0 存入高四位0XFFFF
const/high16 v0,0xFFFF0000

const-wide 占用两个寄存器vxvx+1,共64位,数值必须以L结尾,否则编译不通过

const-wide v0,30 #占用v0和v1

const-wide/16 定义两个相连容器,最大只允许存放16位数据
const-wide/32 定义两个相连容器,最大只允许存放32位数据
const-wide 定义两个相连容器,最大只允许存放64位数据
const-wide/high16 定义两个相连容器,只允许存放高16位数据

iput-object

iput-object v0, p0, LTest;->name:Ljava/lang/String;表示把v0的值(也就是字符串常量test)赋给p0(也就是this)的name字段,Ltest标记的是p0的类型,:Ljava/lang/String是标记name的类型。

具体语法是:

iput-object 要设置的值, 要设置值的对象, 对象类型;->字段名:字段类型;

这里的iput表示设置非静态成员变量,设置静态变量要用sput,语法如下

sput-object 要设置的值, 类型;->字段名:字段类型;

这里的-object表示对非基本类型赋值,如果是基本类型可以通过iputsputiput-booleansput-boolean进行设置,语法是一样的。

iget-obejct

iput-object对应,iget-object是为了获取数据,具体语法是:

iget-object 接收值的寄存器, 接受值来源对象(谁的字段), 对象类型;->字段名:字段类型;

例如:

iget-object v0, p0, Lcom/syske/android/Activity;->_view:Lcom/syske/common/View;

静态变量的字段也是一样的:

sget-object 接收值的寄存器, 静态对象类型;->字段名:字段类型;

基本类型的字段也是类似的,需要通过igetsgetiget-boolean、sget-boolean来获取

return-void

return-void表示方法没有返回值,也就是void,同时返回值还可以是:

  • return vx:返回 vx 寄存器中的值。
  • return-wide vx:返回在 vx,vx+1 寄存器的 doubl e/long 值。
  • return-object vx:返回在 vx 寄存器的对象引用。
smali方法返回关键字 java
return byte
return short
return int
return-wide long
return float
return-wide double
return char
return boolean
return-void void
return-object 数组
return-object object

静态方法

自此,构造方法相关方法我们算是基本讲解完了,下面我们分析下静态main方法的源码,前面已经分享的内容这里直接忽略了。

.method public static main([Ljava/lang/String;)V
    .registers 4

    .prologue
    const/4 v2, 0x1

    .line 14
    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v1, "Hello World!"

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

    .line 15
    new-instance v0, LTest;

    invoke-direct {v0}, LTest;-><init>()V

    invoke-virtual {v0, v2, v2}, LTest;->test(IZ)Ljava/lang/String;

    move-result-object v0

    .line 16
    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;

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

    .line 17
    return-void
.end method
invoke-virtual

invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V表示
调用protectedpublic方法(非私有实例方法),这里的v0就表示java/lang/Systemv1表示方法的入参,具体语法如下:

invoke-virtual {对象实例, 方法入参}, 实例类型;->方法名(入参类别;)返回值

如果参数是多参数则直接写多个参数即可,如上面的源码:

invoke-virtual {v0, v2, v2}, LTest;->test(IZ)Ljava/lang/String;

另外与invoke-virtual类似的语句还有几个在,这里做一个简单的总结:

方法 说明
invoke-virtual 用于非私有实例方法的调用
invoke-direct 用于构造方法以及私有方法的调用
invoke-static 调用静态方法
invoke-super 调用父类的方法
invoke-interface 调用接口方法
new-instance

new-instance v0, LTest;表示创建LTest的实例对象,并将对象引用赋值给v0寄存器
紧接着,调用LTets的实例化方法:

invoke-direct {v0}, LTest;-><init>()V表示调用实例化方法,并把返回值赋给v0

move-result-object

move-result-object v0表示将上一次(紧挨着的)方法执行结果指向v0,也就是接收方法返回值。

扩展知识

好了,至此我们已经把今天示例代码基本分析完了,同时也对smail的基本语法有了一些初步的认识,基于以上这些知识点,我们基本上已经可以阅读smail源码了,但是为了更全面地学习smail的语法,我觉得还是有必要补充一些额外的知识点,比如条件语句的语法:

条件语句

语句 说明
if-eq vA, vB, :cond_** 如果vA等于vB则跳转到:cond_** #equal
if-ne vA, vB, :cond_** 如果vA不等于vB则跳转到:cond_** # not equal
if-lt vA, vB, :cond_** 如果vA小于vB则跳转到:cond_** #less than
if-ge vA, vB, :cond_** 如果vA大于等于vB则跳转到:cond_** # greater equal
if-gt vA, vB, :cond_** 如果vA大于vB则跳转到:cond_** # greater than
if-le vA, vB, :cond_** 如果vA小于等于vB则跳转到:cond_** # less equal
if-eqz vA, :cond_** 如果vA等于0则跳转到:cond_** #zero
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_**

其中,代码中:cond_**语句对应if条件中的:cond_**,表示该条件语句结束:

语法关键词

下面是一些常用的语法关键词

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

总结

至此,我们的smail语法学习之路暂时告一段落,但是还是要多实践,多探索,毕竟学语法就是为了更好的实践,还是建议各位小伙伴找几个安卓项目亲自动手实践下,好了,今天的内容就到这里吧,感谢各位小伙伴的支持!!!

在梳理这些知识点的时候,我发现有位大佬的笔记写的很完整,感兴趣的小伙伴可以去看下:

Android逆向开发之smali语言的学习

标签:lang,v0,object,const,Ljava,示例,语法,smail,cond
From: https://www.cnblogs.com/caoleiCoding/p/16964552.html

相关文章

  • VBA-语法-Type结构体
    VBA中结构体不能定义在函数或者过程中,要定义在模块中,否则会提示无效内部过程,或者类型未定义定义:TypePersonpNameAsStringpAgeAsByteEndType使用:Di......
  • Java中的Runnable、Callable、Future、FutureTask的区别与示例
    Java中存在Runnable、Callable、Future、FutureTask这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,我们通过下面的简单示例来了解一下它们的作用于区别。Ru......
  • Java基础语法
    1.注释​ 注释是对代码的解释和说明文字。Java中的注释分为三种:单行注释://这是单行注释文字多行注释:/*这是多行注释文字这是多行注释文字这是多行注释文字......
  • 隧道代理怎么用啊?隧道代理GO语言代码示例
    经常做数据采集的人对隧道代理肯定不陌生,但对于新手小白而言,有可能还是属于未涉及的领域,今天就来看看隧道代理(动态请求)利用GO语言要如何写代码。普通模式packagemainimport......
  • PowerScript语法参考手册之数据类型
    前言:本文用于PowerBuilder12.6程序员,PowerBuilder最初由Sybase发布,能很快捷开发C/S程序或者多层应用系统。SAP以58亿美元现金收购Sybase以抗衡甲骨文,PowerBuilder12.6是SAP......
  • c#中EntityFramework连接MySql数据示例 支持Update部分字段
    c#程序如果要连接数据库,以前使用的是ADO.Net,通过DataSet、DataTable、ExecuteNoQuery等方法完成,现在微软提供EntityFramework(其实已经提供很久了)组件实现。今天的主题就是......
  • Vue基本语法
    v-bind我们成功创建了第一个Vue程序,看起来跟渲染一个字符串模板非常类似。但是Vue在背后做了大量工作。现在数据和DOM已经被建立了关联,所有东西都是响应式的。我们在......
  • <三>function函数对象类型的应用示例
    std::function是一组函数对象包装类的模板,实现了一个泛型的回调机制。function与函数指针比较相似,优点在于它允许用户在目标的实现上拥有更大的弹性,即目标既可以是普通函数......
  • ECharts3D地图(详细示例——附有具体注释)
    ECharts3D地图(详细示例——附有具体注释)3D地图图表效果如下:代码:<!DOCTYPEhtml><html><head><metacharset="UTF-8"><title>铜仁市3D地图(点......
  • 【每天一个java设计模式(完)】 - 四万字实现23种设计模式(附示例源码)
    什么是设计模式:​​【每天一个java设计模式(零)】-设计模式基本概念及七大设计原则​​创建型模式工厂模式:​​【每天一个java设计模式(一)】-工厂模式​​抽象工厂模式:......