Smali代码语法

Smali是什么?

简介

Smali支持注解、调试信息、行数信息等基本Java的基本特性,可以说是很接近Java编译在JVM上的中间语言了,一般用来做Android程序的逆向工程

由于Smali是用于Dalvik和Art(Android虚拟机)的反汇编程序实现

APK文件>dex文件>smali文件>修改代码

所以smali语言是Dalvik的反汇编语言

B---byte
C---char
D---double
F---float
I---int
J---long
S---short
V---void
Z---boolean
[XXX---array
Lxxx/yyy---object

这里解析下最后两项,数组的表示方式是:在基本类型前加上前中括号“[”,例如int数组和float数组分别表示为:[I、[F;对象的表示则以L作为开头,格式是LpackageName/objectName;(注意必须有个分号跟在最后),例如String对象在smali中为:Ljava/lang/String;,其中java/lang对应java.lang包,String就是定义在该包中的一个对象。

或许有人问,既然类是用LpackageName/objectName;来表示,那类里面的内部类又如何在smali中引用呢?答案是:LpackageName/objectName$subObjectName;。也就是在内部类前加“$”符号
二、函数的定义

函数的定义一般为:

Func-Name (Para-Type1Para-Type2Para-Type3...)Return-Type

参数与参数之间没有任何分隔符:

1. foo ()V

>>>>void foo()。

2. foo (III)Z

>>>>boolean foo(int, int, int)。

3. foo (Z[I[ILjava/lang/String;J)Ljava/lang/String;

>>>>String foo (boolean, int[], int[], String, long)

Smali 格式结构

.class <访问权限> [修饰关键字] <类名>.super <父类名>.source <源文件名>

 1 .class public Lcom/disney/WMW/WMWActivity; #是com.disney.WMW这个package下的一个类
 2 .super Lcom/disney/common/BaseActivity;#继承自com.disney.common.BaseActivity
 3 .source "WMWActivity.java"    #这是一个由WMWActivity.java编译得到的smali文件
 4
 5 # interfaces接口信息
 6 .implements Lcom/burstly/lib/ui/IBurstlyAdListener;#这个WMWActivity实现了一个com.burstly.lib.ui这个package下(一个广告SDK)的IBurstyAdListener接口。
 7
 8 # annotations内部类
 9 .annotation system Ldalvik/annotation/MemberClasses;
10     value = {#有两个成员内部类
11         Lcom/disney/WMW/WMWActivity$MessageHandler;,
12         Lcom/disney/WMW/WMWActivity$FinishActivityArgs;
13     }
14 .end annotation
15  

由此可以的出源java文件

class WMWActivity extends BaseActivity implements IBurstlyAdListener{
    //...             继承                 接口
    class MessageHandler {
        //...
    }
    class FinishActivityArgs{
        //...
    }
}
# static fields静态域         .field private static final PREFS_INSTALLATION_ID:Ljava/lang/String; = "installationId"
# instance fields实例域
.field private _activityPackageName:Ljava/lang/String;
static fields和instance fields均为成员变量

instance field 是对象的变量,每一个这个类的对象都会有一个该instance field  #static fields

    .field <访问权限> static [修饰关键字] <字段名>:<字段类型>

   baksmali 在生成 Smali 文件时,会在静态字段声明的起始处添加“static fields”注释,Smali 文件中的注释与 Dalvik 语法一样,也是以井号“#”开头。“.field”指令后面跟着的是访问权限,可以是 public、private、protected 之一。修饰关键字描述了字段的其它属性,如synthetic。指令的最后是字段名与字段类型,使用冒号“:”分隔,语法上与 Dalvik 也是一样的。实例字段的声明与静态字段类似,只是少了 static 关键字,它的格式如下。

    #instance fields

    .field <访问权限> [修饰关键字] <字段名>:<字段类型>

比如以下的实例字段声明。

 
1   #instance fields
2   .field private btn:Landroid/widget/Button;
第 1 行的“instance fields”是 baksmali 生成的注释,第 2 行表示一个私有字段 btn,它的类型为“Landroid/widget/Button;”。如果一个类中含有方法,那么类中必然会有相关方法的反汇编代码,Smali 文件中方法的声明使用“.method”指令。方法有直接方法与虚方法两种。直接方法的声明格式如下。


   #direct methods

    .method <访问权限> [修饰关键字] <方法原型>

   <.locals>

   [.parameter]

   [.prologue]

   [.line]

   <代码体>

    .end method 
举例:
# direct methods   private函数

    .method static constructor <clinit>()V
        .locals 3 #在这个函数中最少要用到的本地寄存器的个数

        .prologue
        //...

        return-void
    .end method

    .method public constructor <init>()V
        .locals 3

        .prologue
        //...

        return-void
    .end method

“direct methods”是 baksmali 添加的注释,访问权限和修饰关键字与字段的描述相同,方法原型描述了方法的名称、参数与返回值。“.locals ”指定了使用的局部变量的个数。“.parameter”指定了方法的参数,与 Dalvik 语法中使用“.parameters”指定参数个数不同,每个“.parameter”指令表明使用一个参数,比如方法中有使用到 3 个参数,那么就会出现3 条“.parameter”指令。“.prologue”指定了代码的开始处,混淆过的代码可能去掉了该指令。“.line”指定了该处指令在源代码中的行号,同样的,混淆过的代码可能去除了行号信息。

虚方法的声明与直接方法相同,只是起始处的注释为“virtual methods”。如果一个类实现了接口,会在 smali 文件中使用“.implements”指令指出。相应的格式声明如下。

#interfaces

  .implements <接口名> 

“#interfaces”是 baksmali 添加的接口注释,“.implements”是接口关键字,后面的接口名是 DexClassDef 结构中 interfacesOff 字段指定的内容。如果一个类使用了注解,会在 smali 文件中使用“.annotation”指令指出。注解的格式声明如下。

   #annotations

   .annotation [注解属性] <注解类名>

   [注解字段=值]

   .endannotation 

注解的作用范围可以是类、方法或字段。如果注解的作用范围是类,“.annotation”指令会直接定义在 smali 文件中,如果是方法或字段,“.annotation”指令则会包含在方法或字段定义中。例如下面的代码。

   #instance fields

   .field public sayWhat:Ljava/lang/String;

   .annotation runtime LMyAnnoField;

    info="Hellomyfriend"

   .end annotation

   .end field 

实例字段 sayWhat 为 String 类型,它使用了 MyAnnoField 注解,注解字段 info 值为“Hellomyfriend”。将其转换为 Java 代码为:

 @MyAnnoField(info="Hellomyfriend")

     public String sayWhat;

。。。。。。。。。。。。。。。。。。。

虚方法的声明与直接方法相同,只是起始处的注释为“virtual methods”。如果一个类实现了接口,会在 smali 文件中使用“.implements”指令指出。

 Lpackage/name/ObjectName;->MethodName(III)Z

第一部分Lpackage/name/ObjectName;用于声明具体的类型,以便JVM寻找

第二部分MethodName(III)Z,其中MethodName为具体的方法名,()中的字符,表示了参数数量和类型,即3个int型参数,Z为返回值的类型,即返回Boolean类型

寄存器声明及使用

.method private test(I)V
    .registers 4  # 声明总共需要使用4个寄存器

    const-string v0, "LOG"  # 将v0寄存器赋值为字符串常量"LOG"

    move v1, p1  # 将int型参数的值赋给v1寄存器

    return-void
.end method

结合Dalvik常用的指令进行操作,即可实现一些需要的功能

那么,如何确定需要使用的寄存器的个数?

由于非static方法,需要占用一个寄存器以保存this指针,那么这类方法的寄存器个数,最低就为1,如果还需要处理传入的参数,则需要再次叠加,此时还需要考虑Double和Float这种需要占用两个寄存器的参数类型,举例来看:

如果一个Java方法声明如下:

myMethod(int p1, float p2, boolean p3)

那么对应的Smali则为:

method LMyObject;->myMethod(IJZ)V

此时,寄存器的对应情况如下:
寄存器名称     对应的引用
p0     this
p1     int型的p1参数
p2, p3     float型的p2参数
p4     boolean型的p3参数

那么最少需要的寄存器个数则为:5

如果方法体内含有常量、变量等定义,则需要根据情况增加寄存器个数,数量只要满足需求,保证需要获取的值不被后面的赋值冲掉即可,方法有:存入类中的字段中(存入后,寄存器可被重新赋值),或者长期占用一个寄存器
Dalvik指令集

如果需要使用Smali编写程序,还需要掌握常用的Dalvik虚拟机指令,其合集称为Dalvik指令集。这些指令有点类似x86汇编的指令,但指令更多,使用也非常简单方便。最详尽的介绍,可以参考Android官方的Dalvik相关文档:https://source.android.com/devices/tech/dalvik/dalvik-bytecode#instructions

一般的指令格式为:[op]-[type](可选)/[位宽,默认4位] [目标寄存器],[源寄存器](可选),比如:move v1,v2,move-wide/from16 v1,v2

这里也列举一些常用的指令,并结合Smali进行说明:

移位操作:

此类操作常用于赋值

- 返回操作:

用于返回值,对应Java中的return语句

- 常量操作:

用于声明常量,比如字符串常量(仅声明,String a = “abc”这种语句包含声明和赋值)

- 调用操作:

用于调用方法,基本格式:invoke-kind {vC, vD, vE, vF, vG}, meth@BBBB,其中,BBBB代表方法引用(参见上面介绍的方法定义及调用),vC~G为需要的参数,根据顺序一一对应

- 判断操作:

判断操作用来比较一个寄存器中的值,是否与目标寄存器中的值相等或不等,对应Java中的if语句,格式为:if-[test] v1,v2, [condition],其衍生操作还有专门与0进行比较的if-[test]z v1, [condition],其中[condition]为符合判断结果后的跳转条件,需要提前定义好。判断操作也通常和goto配合使用,用来实现循环或者if-else语句

if-eq vA, VB, cond_** 如果vA等于vB则跳转到cond_**。相当于if (vA==vB)
if-ne vA, VB, cond_** 如果vA不等于vB则跳转到cond_**。相当于if (vA!=vB)
if-lt vA, VB, cond_** 如果vA小于vB则跳转到cond_**。相当于if (vA<vB)
if-le vA, VB, cond_** 如果vA小于等于vB则跳转到cond_**。相当于if (vA<=vB)
if-gt vA, VB, cond_** 如果vA大于vB则跳转到cond_**。相当于if (vA>vB)
if-ge vA, VB, cond_** 如果vA大于等于vB则跳转到cond_**。相当于if (vA>=vB)

if-eqz vA, :cond_** 如果vA等于0则跳转到:cond_** 相当于if (VA==0)
if-nez vA, :cond_** 如果vA不等于0则跳转到:cond_**相当于if (VA!=0)
if-ltz vA, :cond_** 如果vA小于0则跳转到:cond_**相当于if (VA<0)
if-lez vA, :cond_** 如果vA小于等于0则跳转到:cond_**相当于if (VA<=0)
if-gtz vA, :cond_** 如果vA大于0则跳转到:cond_**相当于if (VA>0)
if-gez vA, :cond_** 如果vA大于等于0则跳转到:cond_**相当于if (VA>=0)

需要注意的是,在Java中编写的if语句,往往在对应的Smali中,会变成相反的判断逻辑,如下面所示:

 1     private void test() {
 2         int a = 0;
 3         int b = 1;
 4         String result;
 5         if (a > b) {
 6             result = "a great than b";
 7         } else {
 8             result = "a less than or equals b";
 9         }
10     }

上面的Java代码逻辑很简单——一个很简单的if语句,为了在Smali中看的更清楚,我只做了字符串赋值操作。下面是对应的Smali代码:

 1 .method private test()V
 2     .registers 4
 3
 4     .line 24
 5     const/4 v0, 0x0
 6
 7     .line 25
 8     .local v0, "a":I
 9     const/4 v1, 0x1
10
11     .line 27
12     .local v1, "b":I
13     if-le v0, v1, :cond_7
14
15     .line 28
16     const-string v2, "a great than b"
17
18     .line 28
19     .local v2, "result":Ljava/lang/String;
20     goto :goto_9
21
22     .line 30
23     .end local v2    # "result":Ljava/lang/String;
24     :cond_7
25     const-string v2, "a less than or equals b"
26
27     .line 32
28     .restart local v2    # "result":Ljava/lang/String;
29     :goto_9
30     return-void
31 .end method

仔细观察可以发现:

if判断

属性操作:

属性操作的分为:取值(get)和赋值(put)

目标类型分为:数组(array)、实例(instance)和静态(static)三种,对应的缩写前缀就是a、i、s

长度类型分为:默认(什么都不写)、wide(宽,64位)、object(对象)、boolean、byte、char、short(后面几种就不解释了,和Java一致)

指令格式:[指令名] [源寄存器], [目标字段所在对象寄存器], [字段指针],示例代码如下,操作是为int型的类成员变量mIntA赋值为100:

1 const/16 v0, 0x64
2
3 iput v0, p0, Lcom/coderyuan/smali/MainActivity;->mIntA:I

下面列出用于实例字段的指令,其中i都可以换成a或者s,分别用于操作数组字段或者静态字段

举例:

以下Java代码是进行的是最基本的类成员变量的赋值、取值操作

   private String mStringA;
   private int mIntA;
   private Activity mActivityA;

   public void fieldTest() {
        mStringA = "Put String to mStringA";
        mIntA = 100;
        mActivityA = this;

        int len = mStringA.length();
    }

对应的Smali代码如下:

# instance fields
.field private mActivityA:Landroid/app/Activity;

.field private mIntA:I

.field private mStringA:Ljava/lang/String;

# virtual methods
.method public fieldTest()V
    .registers 2

    .line 55
    const-string v0, "Put String to mStringA"

    iput-object v0, p0, Lcom/coderyuan/smali/MainActivity;->mStringA:Ljava/lang/String;

    .line 56
    const/16 v0, 0x64

    iput v0, p0, Lcom/coderyuan/smali/MainActivity;->mIntA:I

    .line 57
    iput-object p0, p0, Lcom/coderyuan/smali/MainActivity;->mActivityA:Landroid/app/Activity;

    .line 59
    iget-object v0, p0, Lcom/coderyuan/smali/MainActivity;->mStringA:Ljava/lang/String;

    invoke-virtual {v0}, Ljava/lang/String;->length()I

    move-result v0

    .line 60
    .local v0, "len":I
    return-void
.end method

根据Java和Smali代码的对比,值得注意的是,Smali获取类成员变量的方法,比较接近函数调用,只不过没有函数调用时的参数

其他指令:

参考:https://blog.csdn.net/yuanguozhengjust/article/details/80493963

   https://www.52pojie.cn/thread-687375-1-2.html

原文地址:https://www.cnblogs.com/hilfloser/p/10517119.html

时间: 2024-10-03 02:55:02

Smali代码语法的相关文章

android smali代码注入 实战一

有同学在通服里面干活,最近一直忙着4g基站搭建的干活,测试设备(android)测量移动网络数据,没有自动保存记录的功能,只能手动记录各种测试参数,不知道测试软件供应商是怎样想的,竟然不提供的这样的功能! 要我帮忙把测试数据自动导入excel表格中,我硬着头皮去尝试下.网上有smali的语法和注入的介绍,但参考价值不大,分享下自己的smali注入的过程和心得(这里smali语法就不讲了). 案例 1.需求 需要提取数据界面如下: 提取数据字段信息: 地点address 小区识别码 cellId,

smali 语言语法

Androidkiller 可以反编译Android的apk,生成一种.smali代码,下面是网上找的一篇关于smali的语法介绍: 文章来源:http://www.brogrammer.cn/android/smali/ 1.smali apk文件通过apktool反编译出来的都有一个smali文件夹,里面都是以.smali结尾的文件.smali语言是Davlik的寄存器语言,语法上和汇编语言相似,Dalvik VM与JVM的最大的区别之一就是Dalvik VM是基于寄存器的.基于寄存器的意思

[转]Haroopad Markdown 编辑器代码语法高亮支持

代码语法高亮 书写格式为: ` ` ` language_key if (condition){ return true } ` ` ` 在 ` ` ` (三个反引号)之间的是代码,其中language_key的值,请参考 支持的语言和缩写标记 例如,将language_key用JavaScript替换,效果如下: if (condition){ return true } 支持的语言和缩写标记 Language language_key 1C 1c ActionScript actionscr

Smali文件语法解析

大家都应该知道APK文件其实就是一个MIME为ZIP的压缩包,我们修改ZIP后缀名方式可以看到内部的文件结构,例如修改后缀后用RAR打开鳄鱼小顽皮APK能看到的是(Google Play下载的完整版版本):Where's My Water.zip\ asset\                        <资源目录1:asset和res都是资源目录但有所区别,见下面说明> lib\                             <so库存放位置,一般由NDK编译得到,常见于

如何把java代码转换成smali代码

1.概述 Smali是Android系统中Dalvik虚拟机指令语言,在apk逆向过程中有许多工具可以把smali代码转化成java代码.但是在学习Smali语法的过程中,有时候需要进行java代码和smali代码的对照,如果可以把java代码转换成smali代码,学习起来岂不是很方便.于是网上搜了一把,很失望,都是各种转smali为java的工具.后来想了想,java变成smali不就是写android程序—>dex—>smali的过程嘛,然而java代码编译完是class文件,如何变成de

Dalvik指令分析(一) 字节码转换为smali代码

有过android应用反编译或者再打包的朋友都有使用过apktool的经验,apktool能将dex文件的 字节码转换为smali代码,这个工具是怎么做到对dex进行解析并生成smali代码的呢?这就需要对 dex文件的格式很熟悉.需要掌握dalvik指令的字节码格式,并能翻译成对应的smali代码. 我准备写一系列的文章来分析dex文件的格式.dalvik字节码的格式.以及dex to smali的方法, 基于此可以做很多的应用,比如安全扫描.应用加固等等! Dalvik指令介绍请参考官方文档

CSS代码语法

css 样式由选择符和声明组成,而声明又由属性和值组成,如下图所示: 选择符:又称选择器,指明网页中要应用样式规则的元素,如本例中是网页中所有的段(p)的文字将变成蓝色,而其他的元素(如ol)不会受到影响. 声明:在英文大括号"{}"中的的就是声明,属性和值之间用英文冒号":"分隔.当有多条声明时,中间可以英文分号";"分隔,如下所示: p{font-size:12px;color:red;} 注意: 1.最后一条声明可以没有分号,但是为了以后修

分享15个美化源码的代码语法着色器

语法高亮是文本编辑器用来显示文本的,特别是源代码,根据不同的类别来用不同的颜色和字体显示.这个功能有助于编写结构化的语言,比如编程语言,标记语言,这些语言的语法错误显示是有区别的.语法高亮并不会影响文本自身的意义,而且能很好的符合人们的阅读习惯. 语法高亮同时也能帮助开发者很快的找到他们程序中的错误.例如,大部分编辑器会用不同的颜色突出字符串常量.所以,非常容易发现是否遗漏了分隔符,因为相对于其他文本颜色不同. 现在有各种各样的语法高亮工具,可以格式化语言,并且根据不同的编程语言进行高亮显示.无

Android调试系列—使用android studio调试smali代码

1.工具介绍 使用工具 android killer:用于反编译apk包,得到smali代码 android studio:调试smali代码工具,或者使用idea,android studio就是在idea的基础上修改的 smalidea-0.03.zip:  as插件,需要安装到android studio中 , 下载地址 模拟器或者或者真机:运行要调试的程序 样本:使用网络上的某apk程序,  下载地址 2.前期准备 2.1  手机或者模拟器安装应用 adb instll GGNdkTes