Android学习笔记3--逆向2--Smali

来源:互联网 发布:zigbee实战演练 源码 编辑:程序博客网 时间:2024/05/19 15:40

Smali文件结构

文件命名
无论普通类、抽象类、接口类还是内部类,反编译的时候会为每个类单独生成一个Smali文件,但是内部类相存在相对比较特殊的地方。
1) 内部类的文件是“[外部类][].smali[][数字].smali”来命名。
2) 编译器会把外部类的引用作为第一个参数插入到会内部类的构造器参数列表。
3) 内部类的构造器是先给“合成变量”保存外部类的引用,再初始化外部类,再初始化自身。
4) 内部类访问外部类的私有方法和变量时,都要通过编译器生成的“合成方法”来间接访问。

注解定义

# 注解的作用范围可以是类、字段或方法# 如果注解的作用范围是类,指令会直接定义在smali文件中# 如果是字段或方法,“.annotation”指令则会包含在字段或方法定义中#annotations.annotation [注解属性] <注解类名> #注解类名的第一个字母代表类型    [注解字段=值] .endannotation# 以上类似于@<注解类名的提取>(注解字段=值)

类型定义

# 指定当前类的类名。.class <访问权限> [修饰关键字] <类名> # 类名开头的L是遵循Dalvik字节码的相关约定# 指定当前类的父类.super <父类名># 接口名是DexClassDef结构中interfacesOff字段指定的内容.implements <接口名> # 当前类的源文件名。.source <源文件名># “.source”行的代码可能为空:经过混淆的dex文件反编译出来的smali代码可能没有源文件信息

方法定义

方法调用的表示格式:Lpackage/name/ObjectName;->MethodName(III)Z。 Lpackage/name/ObjectName;表示包名,MethodName是方法名,III为参数(在此是3个整型参数),Z是返回类型(bool型)。函数的参数是一个接一个的,中间没有隔开。

# 虚方法的声明与直接方法相同,只是起始处的注释为“virtual methods”。#direct methods.method <访问权限> [修饰关键字] <方法原型> #方法原型述了方法的名称、参数与返回值<.locals> #指定了使用的局部变量的个数[.parameter] #指定了方法的参数,如方法中有使用到3个参数,那么就会出现3条“.parameter”指令[.prologue] #指定了代码的开始处,混淆过的代码可能去掉了该指令[.line] #指定了该处指令在源代码中的行号,混淆过的代码可能去除了行号信息    <代码体>.end method

字段定义

字段调用的表示格式:Lpackage/name/ObjectName;->FieldName:Ljava/lang/String; 即包名,字段名和字段类型,字段名与字段类型是以冒号“:”分隔。

#static fields(静态变量).field <访问权限> static [修饰关键字] <字段名>:<字段类型>#instance fields(动态变量).field <访问权限> [修饰关键字] <字段名>:<字段类型>

Smali语法指令

在Dalvik指令中一般用p0存放着this,p1~pn为形参(有父类指针的话,p1一般又指父类地址,px~pn才是形参),v0~vn为局部变量但是IDA Pro反编译后却不一样,其明确使用this关键字,后面的P寄存器依次都减了1。每条指令使用的寄存器索引范围都有限制(因为Dalvik指令字节码必须字节对齐)。这里使用一个大写字母来表示4位数据宽度的取值范围。当目的寄存器和源寄存器中有一个寄存器的编号大于15时,即需要加上/from16指令才能得到正确运行。具体示例

在以下指令中,在部分指令助记符后添加了jumbo后缀,这是在Android4.0开始的扩展指令,增加了寄存器和常量的取值范围。
需要引起注意的是,以下指令表中形如vA表示 寄存器范围为v0-v15,形如vAA表示寄存器范围为v0-v255,这一点在理解指令时容易被忽略而导致修改smali代码时编译出错。

类型介绍

类型介绍

Dalvik字节码有两种类型,基本类型和引用类型。数组和对象是引用类型,其它都是基本类型。

V--void,只能用于返回值类型Z--booleanB--byteC--charS--shortI--intJ--long(64位)F--floatD--double(64位)[--数组类型L--Java类类型

每个Dalvik寄存器都是32位大小,对于小于或者等于32位长度的类型来说,一个寄存器就可以存放该类型的值,而像J、D等64位的类型,它们的值是使用相邻两个寄存器来存储的,如v0与v1、v3与v4等
“[”类型可以表示所有基本类型的数组。[I表示一个整型一维数组,相当于java中的int[]。[[I相当于int[][],[[[I相当于int[][][]。注意每一维的最多255个。对象数组的表示:[Ljava/lang/String;表示一个String对象数组
Java中的对象在smali中以Lpackage/name/ObjectName;的形式表示。前面的L表示这是一个对象类型。

变量定义

常量定义

const vAA, #+BBBBBBBB #将32位宽度的立即数赋值给寄存器const-wide vAA, #+BBBBBBBBBBBBBBBB #将64位宽度的立即数赋给寄存器对const-class vAA, type@BBBB #将一个类的引用赋值给寄存器const-string vAA, string@BBBB #将字符串常量的引用赋值给寄存器const/4 vA, #+B #将4位宽度的立即数带符号扩展到32位,赋值给寄存器const/16 vAA, #+BBBB #将16位宽度的立即数带符号扩展到32位,赋值给寄存器const/high16 vAA, #+BBBB0000 #将16位宽度的立即数右边零扩展到32位,赋值给寄存器const-wide/16 vAA, #+BBBB #const-wide/32 vAA, #+BBBBBBBB #const-wide/high16 vAA, #+BBBB000000000000 #const-string/jumbo vAA, string@BBBBBBBB #jumbo后缀表示指令的寄存器的索引范围更大

类型转换

[con] vA, vB #数据类型转换指令# con可以是[type]-to-[type]以及int-to-byte\int-to-char\int-to-short。type类型后缀包括intlongfloatdoublenew-instance vAA, type@BBBB #构造一个指定类型的实例,并把引用赋给寄存器instance-of vA, vB, type@CCCC #判断vB寄存器的引用对象是否可以转化为指定类型,是则给vA寄存器赋1,否则赋0check-cast vAA, type@BBBB #把寄存器引用的对象转为指定的类型,如果不行则会抛出一个ClassCastException异常

数组定义

new-array vA, vB, type@CCCC #构造一个指定类型和大小的数组,并把引用赋值filled-new-array {vC, vD, vE, vF, vG}, type@BBBB #构建一个指定类型的数组,初始化后用指令move-result-object获取构建的数组的引用。数组类型由type@BBBB指定,数组大小由隐藏调用的vA指定,数组数值由寄存器列表{vC, vD, vE, vF, vG}的指定。fill-array-data vAA, +BBBBBBBB #使用指定的数据表来填充vAA指定的数组。该数组必须为基本类型array-length vA, vB #获取vB引用的数组长度赋值给vA

对象操作

# []形式为Xget[-type]和Xput[-type]# 数组X为a,对象动态属性X为i,对象静态属性X为s。# type类型后缀包括空或boolean、byte、char、short、wide、object类型。# 非静态的最后一个寄存器存放着实例[op] vAA, vBB, vCC #数组字段操作指令。用来对数组某数值进行读写[op] vA, vB, field@CCCC #普通字段操作指令。用来对对象某动态字段进行读写[op] vAA, field@BBBB #静态字段操作指令。用来对对象某静态字段进行读写

运算指令

赋值运算

move vA, vB #将vB寄存器的内容赋值给vA,非对象类型move-wide vA, vC #将vC\vD寄存器的内容赋值给vA\vB,非对象类型move-object vA, vB #将vB寄存器的内容赋值给vA,对象类型move-result vAA #必须紧跟invoke后面,把调用方法的返回值赋值给vAA,操作非对象类型move-result-wide vAA #紧跟invoke后面,把调用方法的返回值赋值给vAA、vAB,操作非对象类型move-result-object vAA #紧跟invoke后面,把调用方法的返回值赋值给vAA、vAB,操作对象类型move/16 vAAAA, vBBBBmove/from16 vAA, vBBBBmove-wide/16 vAAAA, vBBBBmove-wide/from16 vAA, vBBBBmove-object/16 vAAAA, vBBBBmove-object/from16 vAA, vBBBB

比较运算

# X可以为l或g,con可以为floatdouble。共5种,最基本的是cmp-long。cmpX-[con] vAA, vBB, vCC #比较浮点数和长整型数的大小。如果是l:vBB寄存器大于vCC存器结果为-1,相等结果则为 0,小于结果则为 1。结果赋给vAA寄存器。

四则运算

[con] vAA, vBB, vCC #对寄存器vBB和寄存器vCC做运算,并把结果赋值给vAA。[con]/lit8 vAA, vBB, #+CC #对寄存器vBB和常量CC做运算,并把结果赋值给vAA。[con]/lit16 vA, vB, #+CCCC #对寄存器vB和常量CCCC做运算,并把结果赋值给vA。[con]/2addr vA, vB #对寄存器vA和寄存器vB做运算,并把结果赋值给vA。#con可以是add-[type]\sub-[type]\mul-[type]\div-[type]\rem-[type]。type类型后缀包括int、long、float、double。

逻辑运算

[con] vAA, vBB, vCC #对寄存器vBB和寄存器vCC做运算,并把结果赋值给vAA。[con]/lit8 vAA, vBB, #+CC #对寄存器vBB和常量CC做运算,并把结果赋值给vAA。[con]/lit16 vA, vB, #+CCCC #对寄存器vB和常量CCCC做运算,并把结果赋值给vA。[con]/2addr vA, vB #对寄存器vA和寄存器vB做运算,并把结果赋值给vA。#con可以是not-[type]\neg-[type]\and-[type]\or-[type]\xor-[type]\shl-[type]\shr-[type]。type类型后缀只包括int、long。

跳转指令

if

# 条件:等于(eq)、不等于(ne)、小于(lt),大于 (gt),小于等于(le)、大于等于(ge)if-[con] vA,vB,+CCCC #有条件跳转指令,比较寄存器vA和vB的值,满足条件后跳转到指定的指令。 if-[con]z vAA, +BBBB #有条件跳转指令,比较寄存器vAA和0的值,满足条件后跳转到指定的指令。

switch

packed-switch vAA, +BBBBBBBB #根据+BBBBBBBB给定的跳转偏移列表匹配vAA寄存器值,跳转到指定指令。偏移表中的匹配值是有规律递增的。sparse-switch vAA, +BBBBBBBB #偏移表中的匹配值是无规律且可以被指定的

goto

goto +AA #无条件跳转到指定的指令。偏移量为8位宽度,且不能为零goto/16 +AAAA #无条件跳转到指定的指令。偏移量为16位宽度,且不能为零goto/32 +AAAAAAAA #无条件跳转到指定的指令。偏移量为32位宽度,且不能为零

函数指令

函数调用

invoke-[kind] {vC, vD, vE, vF, vG}, meth@BBBB #调用指定的方法,{vC,...,vG}为传入方法的参数列表。非静态的第一个寄存器存放着实例#具体使用哪种调用方式,视方法的对象类型和方法本身类型而定:#invoke-super 调用实例的父类方法#invoke-interface调用接口的方法#invoke-static 调用静态方法#invoke-virtual 调用实例的虚方法,通常成员对象实例的方法都以该指令调用#invoke-direct调用直接方法,通常私有方法都以该指令调用

函数返回

return vAA #返回32位寄存器vAA内容,非对象类型数据return-wide vAA ##返回64位寄存器vAA内容,非对象类型数据return-object vAA #返回对象类型数据sreturn-void #返回空类型指令

异常生成

throw vAA #抛出一个指定类型的异常move-exception vAA #必须作为异常捕捉块的第一条指令,把捕捉到的异常类型对象赋值给vAA寄存器

其他指令

对齐指令

nop #只是字节对齐,无特别意义

锁指令

monitor-enter vAA #获取寄存器引用的对象的同步锁monitor-exit vAA #释放寄存器引用的对象的同步锁
原创粉丝点击