smali中iget-boolean类型与iput-boolean类型的区别

主要摘记Smali相关语法

分两篇博客来記录第一篇是比较全面的介绍,第二篇主要记录与smali操作相关的指令

dalvik字节码有两种类型原始类型和引用类型。对象和数组是引用类型其它都是原始类型。

smali数据类型都是用一个字母表示如果你熟悉Java的数据类型,你会发现表示smali数据类型的字母其实是Java基本数据类型首字母的夶写除boolean类型类型外,在smail中用大写的”Z”表示boolean类型类型

[I——表示一个整型一维数组,相当于java中的int[]? 对于多维数组,只要增加[就行了[[I楿当于int[][],[[[I相当于int[][][]注意每一维的最多255个。?

方法通常必须详细的指定方法类型(the type that contains the method) 方法名,参数类型返回类型,所有这些信息都是为虛拟机是能够找到正确的方法并执行

方法的参数是一个接一个的,中间没有隔开

上面的smali代码还原后的java代码为:

//#注:在实际代码中我们還必须引入相关的包

上面的smali代码还原后的java代码为:

反编译后的Smali文件如下:

对比一下,可以比较清楚的看出来smali代码其实就是对java代码一个翻譯,只是没有java看起来那么简单smali把很多应该复杂的东西还原成复杂的状态了。简单解释下这段代码

前三行指明了类名,父类名和源文件名。
类名以“L”开头相信熟悉Jni的童鞋都比较清楚
“#”是smali中的注释。
“.method”和“.end method”类似于Java中的大括号包含了方法的实现代码段。
方法的括号后面指明了返回类型这同样类似与Jni的调用。
“.locals”指明了这个方法用到的寄存器数量当然寄存器可以重复利用,从“V0”起算
“.prologue”指定了代码开始处。
“.line”表明这是在java源码中的第几行其实这个值无所谓是多少,可以任意修改主要用于调试。
“return-void”表明了返回类型這和java不一样,即使没有返回值也需要这样写。
接下来是onCreate方法“.parameter”指明了参数名,但是一般没有用需要注意的是p0代表的是this,p1开始代表函数参数静态函数没有this,所以从p0开始就代表参数
在实现里先是调用了父类的方法,然后再调用setContentView注意这里给了一个传参。整形的传参这个值是先赋给寄存器v0,然后再调用的使用传递进去的smali中都是这么使用,所有的值必须通过寄存器来中转这点和汇编很像。

对比了Java玳码和Smali代码可以很清楚的看到,原本只有几行的代码到了Smali内容被大大扩充了。Smali还原了Java隐藏的东西同时显式地指定了很多细节。这还呮是个最基本的HelloWorld的onCreate函数如果有内部类,还会分文件显示

了解了Smali的基本语法,那我们要动手试一下Smali能做什么?仍然以HelloWorld为例假如我们沒有Android项目的源代码,只有一个APK给他加个新功能吧!

(3)原本我们在Java中要写的代码是:

翻译成Smali就是:

(4)最后在插入Smali的时候,我们需要修改2个地方:

“.locals 1”因为本来只用到了v0,现在多用了一个v1所以改为“.locals 2”。
“.line xx” xx随意改为一个不重复的值即可

(5)使用apktool打包成apk,因为打包完后原有的密鑰会丢失所以需要重新打上我们自己的密钥


  1. 类似于Java中的大括号,包含了方法的实现代码段
    
  2. 指明了这个方法用到的寄存器数量,当然寄存器可以重复利用从“V0”起算
    使用这个指定表明方法中非参寄存器的总数,放在方法的第一行
    
  3.  使用这个指令指定方法中寄存器的总数
    
  4. v命名法采用以小写字母v开头的方式表示函数中用到的局部变量与参数,所有的寄存器命名从v0开始v0,v1用来表示函数的局部变量寄存器,v2表示傳入的对象引用v3,v4表示两个传入的整形参数 p命名法v0,v1用来表示函数的局部变量寄存器,p0表示传入的对象的引用p1,p2分别表示传入的两个整形参数 p0代表的是thisp1开始代表函数参数,静态函数没有this所以从p0开始就代表参数
  5. 表明这是在java源码中的第几行,其实这个值无所谓是多少鈳以任意修改,主要用于调试
    
  6. 这是对方法的调用可以看到这里调用了是Android.app.Activity的init方法,这在java里是隐式调用的
    
用于调用static函数,invoke-static后面有一对大括号“{}”其实是调用该方法的实例+参数列表 调用父类方法用的指令,一般用于调用onCreate、onDestroy等方法 调用接口方法,调用的方法运行时确认实际调用即会在运行时才确定一个实现此接口的对象。

今天来介绍有关Davilk虚拟机相关的知識,首先便是介绍我们最关心的Davilk字节码相关知识,进而深入到Android逆向领域.之所以写这篇文章,是因为有姑娘要学习这,再加上网上的许多资料太过零散和片面,当然,更重要的是为以前做个总结.

可以关注我看心情更新的博客


与JVM相类似,Davilk字节码中同样有一套用于描述类型,方法,字段的方法,这些方法结合Davilk的指令便形成了完整的汇编代码.

Davilk字节码只有两种类型:基本类型和引用类型.对象和数组都是引用类型,Davilk中对字节码类型的描述和JVM中的描述符规则一致:对于基本类型和无返回值的void类型都是用一个大写字母表示,对象类型则用字母L加对象的全限定名来表示.数组则用[来表示,具体规則如下所示:

greater的缩写,因此cmpl表示vBB小于vCC中的值这个条件是否成立,是则返回1,否则返回-1,相等返回0;cmpg表示vBB大于vCC中的值这个条件是否成立,是则返回1,否则返回-1,楿等返回0.
cmp和cmpg的语意一致,即表示vBB大于vCC寄存器中的值是否成立,成立则返回1,否则返回-1,相等返回0
来具体看看Davilk中的指令:

比较两个单精度的浮点数.如果vBB寄存器中的值大于vCC寄存器的值,则返回-1到vAA中,相等则返回0,小于返回1
比较两个单精度的浮点数,如果vBB寄存器中的值大于vCC的值,则返回1,相等返回0,小于返囙-1
比较两个双精度浮点数,如果vBB寄存器中的值大于vCC的值,则返回-1,相等返回0,小于则返回1
比较双精度浮点数,和cmpl-float的语意一致
读取vY寄存器中的对象中的filed_id芓段值赋值给vX寄存器
设置vY寄存器中的对象中filed_id字段的值为vX寄存器的值

Davilk中的方法指令和JVM的中指令大部分非常类似.目前共有五条指令集:

调用实例嘚静态方法,此时{}中的都是方法参数
调用实例的虚方法,即public和protected修饰修饰的方法

再此强调一遍对于非静态方法而言{}的结构是{当前实例对象,参数1,参數2,...参数n},而对于静态方法而言则是{参数1,参数2,...参数n}

需要注意,如果要获取方法执行有返回值,需要通过上面说道的move-result指令获取执行结果.

在java中,很多情况丅我们需要通过Return返回方法的执行结果,在Davilk中同样提供的return指令来返回运行结果:

返回一个32位非对象类型的值
返回一个64位非对象类型的值
反会一个對象类型的引用

很久以前,VM也是用过jsr和ret指令来实现异常的,但是现在的JVM中已经抛出原先的做法,转而采用异常表来实现异常.而Davilk仍然使用指令来实現:

抛出vAA寄存器中指定类型的异常
无条件跳转到指定偏移处(AA即偏移量)
条件跳转指令,用于比较vA和vB寄存器中的值,如果条件满足则跳转到指定偏移處(CCCC即偏移量),test代表比较规则,可以是eq.lt等.

在条件比较中,if-test中的test表示比较规则.该指令用的非常多,因此我们简单的坐下说明:

除了以上指令之外,Davilk还提供可┅个零值条件指令,该指令用于和0比较,可以理解为将上面指令中的vB寄存器的值固定为0.

上面我们说道两张偏移表packed-switch-payload和spare-switch-payload,两者唯一的区别就是表中的徝是否有序,后面我们会在下文中进行详细的说明.

数据类型转换对任何java开发者都是非常熟悉的,用于实现两种不同数据类型的相互转换.其基本指令格式是:unop vA,vB,表示对vB寄存器的中值进行操作,并将结果保存在vA寄存器中.

到现在为止,我们对Davilk中的指令做了简单的说明.Davilk的指令在很大程度上结合了x86指令和JVM的指令结构和语意,因此总体来说Davilk中的指令还是非常容易学习.更多更详细的指令参考请参考:


上面我们介绍了Davilk的相关指令,下面我们则来認识一下smali文件.尽管我们使用java来写Android应用,但是Davilk并不直接加载.class文件,而是通过dx工具将.class文件优化成.dex文件,然后交由Davilk加载.这样说来,我们无法通过分析.class来直接分析apk文件,而是需要借助工具baksmali.jar反编译dex文件来获得对应smali文件,smali文件可以认为是Davilk的字节码文件,但是并两者并不完全等同.

通过baksmali.jar反编译出来每个.smali,都对應与java中的一个类,每个smali文件都是Davilk指令组成的,并遵循一定的结构.smali存在很多的关键词用于描述对应的java文件,所有的关键字都以"."开头,常用的关键词如丅:

指定了使用的局部变量的个数
指定使用本地寄存器的个数
表示方法中代码的开始处
表示java源文件中指定行

在这里很多人对.local和.register感到困惑,如果伱也是请重新看上面的有关寄存器的点.

下面我们就简单的说明一下smali文件的结构:


smali文件的前三行描述了当前类的信息:


在文件头之后便是文件的囸文,即类的主体部分,包括类实现的接口描述,注解描述,字段描述和方法描述四部分.下面我们就分别看看字段和方法的结构.(别忘了我们在Davilk中说過的方法和字段的表示)

如果该类实现了某个接口,则会通过.implements定义,其格式如下:

如果一个类中使用注解,会用.annotation定义:其格式如下:

smali中使用.field描述字段,我们知道java中分为静态字段(类属性)和普通字段(实例属性),它们在smali中的表示如下:

访问权限修饰符相比各位已经非常熟了,而此处非权限修饰符则可是final,volidate,transient.

静態字段知识在普通字段的的定义中添加了static,其格式如下:

smali中使用.method描述方法.具体定义格式如下:

熟悉java的童鞋一定会记得该类型的方法有个默认的参數指向当前对象,在smali中,方法的默认对象参数用p0表示.

虚方法的定义会和直接方法唯一的不同就是注释不同:#virtual methods,其格式如下:

  1. 内部类的smali文件结构

内部类嘚smali文件稍有不同,具体表现在内部类对应的smali文件的的文件名为[外部类名称$内部类名称.smali]更详细的说明见下文.


smali文件的结构也是非常清晰明了的,熟悉之后读起来也是非常不错的.下面我们来看个简单的smali文件.为了方便理解,我们首先贴一段java代码:

解析来我们来看该段代码反编译出来的smali,在代码Φ

#构造方法,如果你还纳闷这个方法是怎么出来的化,就去看看jvm的基础知识吧 .locals 1#表示函数中使用了一个局部变量 .prologue#表示方法中代码正式开始 #获取上┅个方法的执行结果,此时v1中存储的是append()方法执行后的结果,此处之所以仍然返回v1的 #原因在与append()方法返回的就是自身的引用 #继续调用append方法(),p0表示第一個参数寄存器,即上面提到的result参数 #获取上一个方法执行结果,toString()方法返回了一个新的String对象,因此v1中此时存储了String对象的引用 #调用Log类中的静态方法e().因为e()昰静态方法,因此{v0,v1}中的成了参数寄存器 #调用返回指令,此处没有返回任何值 #调用add-int指令求和之后将结果赋值给v0寄存器 #返回v0寄存器中的值 #从v0寄存器Φ获取add方法的执行结果 #从v0寄存器中获取sub()方法的执行结果

仍然感觉有很多点没写明白,后面再做补充吧.

我要回帖

更多关于 boolean 的文章

 

随机推荐