(三)smali 入门

目录

        • 1. smali 数据类型
        • 2. smali 对象(引用类型的对象)
        • 3. smali 数组
        • 4. smali 方法的表现形式
        • 5. smali 字段的表现形式
        • 6. smali 寄存器指令
        • 7. smali 指令
          • 7.1 空指令:
          • 7.2 数据操作指令(此类操作常用于赋值):
          • 7.3 返回操作指令:
          • 7.4 数据定义指令:
          • 7.5 方法调用指令:
          • 7.6 比较指令:
          • 7.7 跳转指令:
          • 7.8 字段操作指令:
          • 7.9 数据运算指令:
          • 7.10 数据转换指令
          • 7.11 锁与异常操作指令
          • 7.12 实例操作指令

Android代码在 Dalvik虚拟机 运行
 
baksmali 是安卓系统中Dalvik中所用的汇编器 和 反汇编器
 
dex文件 反汇编后得到 smali文件
 
smali代码拥有自己特定的格式语法
 
smali目前在Google code是一个开源项目,被广泛的用于广告注入、汉化、破解等方面

1. smali 数据类型

java 数据类型 Type descriptor 字节码类型描述符
int I
float F
double D
boolean Z
char C
byte B
short S
long J
void V

2. smali 对象(引用类型的对象)

java 对象类型 Type descriptor 字节码类型描述符
package.name.Object Lpackage/name/Object;
  L表示 对象类型,这是一个对象
  xxx.yyy.zzz 变成 xxx/yyy/zzz 格式的目录层级结构
  ; 表示对象名称结束

String字符串本身是个对象

3. smali 数组

java 数据类型 Type descriptor 字节码类型描述符
int [ ] [ I
object [ ] [ ] [ [ Ljava/lang/Object

二维数组:
在java中:
在这里插入图片描述
在smali中:

#声明了三个不同类型的二维数组
.field private double_int:[[I

.field private double_obj:[[Ljava/lang/Object;

.field private double_string:[[Ljava/lang/String;

(三)smali 入门_第1张图片
在这里插入图片描述
在这里插入图片描述
(三)smali 入门_第2张图片

4. smali 方法的表现形式

方法在smali中以.method 指令声明

# direct methods
.method <访问权限> [关键字] 方法名(参数)返回值类型
.locals 指定局部变量的个数
.param  方法的参数
.line  代码在原文件中的行号
.end  结尾

Lcom/example/hello/MainActivity;->methodName(IZD)Z

  • Lcom/example/hello/MainActivity;:用于声明具体的类型,以便JVM寻找
  • methodName:表示方法名称
  • IZD:连写的,没有分隔符,表示该方法需要三个参数,分别为int、boolean、double
  • Z:最后括号后的Z,表示这个函数的返回对象类型是boolean型,如果是V,表示返回值为空
  • (如果需要调用构造方法,则MethodName为:

5. smali 字段的表现形式

java 中字段的表现形式 smali 中字段的表现形式
private objectType fieldName; .field fieldName:objectType
# instance fields   实例字段
.field <访问权限> [修饰关键字] <字段名> : 类型

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

例如:字段的声明对比如下:
在java中:
(三)smali 入门_第3张图片
smali中:
(三)smali 入门_第4张图片

文件格式:
无论是 普通类、抽象类、接口类、内部类,反编译是,都会用单独的smali文件存放。
java中:
(三)smali 入门_第5张图片
smali中:
(三)smali 入门_第6张图片

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

每个smali文件开头都是如下三行:

.class public Lcom/example/hello/MainActivity;
.super Landroidx/appcompat/app/AppCompatActivity;
.source "MainActivity.java"
  • 第一行:.class 指令 指定当前的类名称。此处是public
  • 第二行:.super 指令 指定当前类的父类
  • 第三行:.source 指令指定当前的源文件名称

6. smali 寄存器指令

在Dalvil虚拟机字节码中,寄存器有两种命名方式:

  • v 命名法(v0、v1、v2……)
  • p 命名法(p0、p1、p2……)
v 命名法 p 命名法 寄存器含义
v0 v0 第一个局部变量寄存器
v1 v1 第二个局部变量寄存器
…… …… 中间的局部变量寄存器依次递增且名称相同
vM-N p0 第一个参数寄存器
…… …… 中间的参数寄存器分别依次递增
vM-1 pN-1 第N局部变量寄存器

比较v命名法与p命名法:

v 命名法 p 命名法
以 .registers开头 以 .limit开头
以 v2 作为this的引用 以 p0 作为this的引用
以M-N命名N 个参数的寄存器 用 p 命名法

v 指向寄存器
p 一般是某个java原有的代码有参数,或者指向this的或者直接变量

Dalvil参数传递规则:如果一个函数使用了M个寄存器、拥有N个参数,则参数使用最后N个寄存器,局部变量从v0开始一直递增到前M-N个(被传入的“隐藏”的对象引用this)

7. smali 指令

7.1 空指令:
指令助记符 指令功能描述
nop 代码对齐,无操作
7.2 数据操作指令(此类操作常用于赋值):

指令基本格式:[op]-[type](可选)/[位宽,默认4位] [目标寄存器],[源寄存器](可选)

指令助记符 指令功能描述
move v0,v1 将v1寄存器的数据赋值给v0寄存器,非对象类型
move-object v0,v1 将v1寄存器的数据赋值给v0寄存器,对象类型
move-wide v0,v1 将寄存器对v1中的值移入到v0寄存器对中
move-result v1 将这个指令的上一条指令计算结果,移入到v1寄存器中(需要配合invoke-static、invoke-virtual等指令使用)
move-result-object v1 将上条计算结果的对象指针移入v1寄存器(多用于函数的返回值)
move-result-wide v1 将上条计算结果(双字)的对象指针移入v1寄存器
move-exception v1 将异常移入v1寄存器,用于捕获try-catch语句中的异常
7.3 返回操作指令:
指令助记符 指令功能描述
return-void 返回void
return v1 返回v1寄存器中的值
return-object v1 返回v1寄存器中的对象指针
return-void 返回void
7.4 数据定义指令:
  • const:数据定义 指令
  • /number:表示赋值到寄存器的数据的长度
  • vA:寄存器
  • #+B……:表示数据
指令助记符 指令功能描述
const/4 vA,#+B 将4位宽度的立即数 带符号 扩展到32位,赋值到vA寄存器
const/16 vA,#+BBBB 将16位宽度的立即数 带符号 扩展到32位,赋值到vA寄存器
const vA,#+BBBBBBBB 将32位宽度的立即数 赋值到vA寄存器
const wide/16 vA,#+BBBB 将16位宽度的立即数 带符号 扩展64位,赋值到vA寄存器
const wide/32 vA,#+BBBB 将32位宽度的立即数 带符号 扩展到64位,赋值到vA寄存器
const wide vA,#+BBBBBB 将64位宽度的立即数 ,赋值到vA寄存器
const-string vA,string@aaaa 将字符串常量的引用 赋值给vA
const/high16 vA+BBBB0000 将16位宽度的立即数 右扩展扩展到32位,赋值给vA寄存器
const-class vA,type@AAA 将一个类class 的引用赋值给vA寄存器

const/4和const/16的区别:

  • const/4 表示半个字节,也就是4位。-23~23-1 -8~7

  • const/16 表示两个字节,16位。-215~215-1 -32768~32767

局部变量定义:

.local v0,“a”:I:将v0的值赋值给 变量a

7.5 方法调用指令:

调用方法的基本格式:invoke-kind{vA,vB,vC,vD},meth@BBBB

  • invoke:调用方法 指令
  • -xxxx:调用的方法 的类型
  • vA~vD:为需要的参数,根据顺序一一对应
  • BBBB:代表方法引用
指令助记符 指令功能描述
invoke-virtual 调用实例的虚方法,通常成员对象实例的方法都有该指令调用(用于调用一般的,非private、非static、非final、非构造函数的方法,它的第一个参数往往会传p0,也就是this指针)
invode-super 调用父类的虚拟方法(用于调用父类中的方法,其他和invoke-virtual保持一致)
invoke-direct 调用直接方法,通常私有方法都以该指令调用(用于调用private修饰的方法,或者构造方法)
invoke-static 调用静态方法(比如一些工具类)
invoke-interface 用于调用interface中的方法
invoke-kind / range {vB-vN},method@BBB 参数列表是连续的寄存器列表
7.6 比较指令:

cmp:比较两个寄存器中值的大小,并将结果存储在目标寄存器中。
基本格式为:cmp 目标寄存器 vB vC

指令助记符 指令功能描述
cmpl vA,vB,vC(less than) 比较 vB,vC 较小值。如果vB=vC ,则vA=0 ;如果 vC较小 ,则vA存储正数。vB小 ,则vA存储负数
cmpg vA,vB,vC(greater than) 比较 vB,vC 较大值。如果vB=vC ,则vA=0 ;如果 vC较大 ,则vA存储正数。vB大 ,则vA存储负数
指令助记符 指令功能描述
cmpl-float vA,vB,vC 比较浮点型和长整型的数据。
cmpg-float vA,vB,vC 比较浮点型和长整型的数据。
cmpl-double vA,vB,vC 比较double型的数据
cmpg-double vA,vB,vC 比较double型的数据
cmpg-long vA,vB,vC 比较long型的数据
7.7 跳转指令:
指令助记符 指令功能描述
if-test vA,vB,+CCC 条件跳转指令,比较两个寄存器vA和vB的值,然后进行条件跳转
if-eq v1,v2,:cond_0 如果v1 = v2,则进入分支cond_0
if-ne v1,v2,:cond_0 如果v1 != v2,则进入分支cond_0
if-gt v1,v2,:cond_0 如果v1 > v2,则进入分支cond_0
if-ge v1,v2,:cond_0 如果v1 >= v2,则进入分支cond_0
if-lt v1,v2,:cond_0 如果v1 < v2,则进入分支cond_0
if-le v1,v2,:cond_0 如果v1 <= v2,则进入分支cond_0
   
就与0比较:
if-testz vA,+cccc 条件跳转指令,比较寄存器vA与0的值,满足后跳转
if-eqz v1,:cond_0 如果寄存器v1的值 ==0,则跳转到cond_0
if-gtz v1,:cond_0 如果寄存器v1的值 >0,则跳转到cond_0
if-gez v1,:cond_0 如果寄存器v1的值 >=0,则跳转到cond_0
if-ltz v1,:cond_0 如果寄存器v1的值 <0,则跳转到cond_0
if-lez v1,:cond_0 如果寄存器v1的值 <=0,则跳转到cond_0
   
其他跳转:
goto +cccc 无条件跳转到指定位置,偏移量为8位 宽度,且不为0
goto/16 +cccc 无条件跳转到指定位置,偏移量为16位 宽度,且不为0
goto/32 +cccc 无条件跳转到指定位置,偏移量为32位 宽度,且不为0
   
packed-switch vA,+bbbbbb 根据+bbbbbb给定的跳转偏移列表进行匹配vA寄存器的值,跳转到指定指令。其中,偏移量的匹配值是 有规律递增的
sparse-switch vA,+bbbbbb 根据+bbbbbb给定的跳转偏移列表进行匹配vA寄存器的值,跳转到指定指令。其中,偏移量的匹配值是 无规律的

作业1

在java中:

	# 实际中不这样写,需要用一个函数,这里只说逻辑
    private int age = 20;
    private String  identity;
if  (age>18){
        identity = "成年";
    }else{
        identity = "未成年";
    }

在smali中:

.field private age:I
.field private identity:Ljava/lang/String;
const/4 v0,0x14
const/4 v1,0x12

if-le v0,v1,:cond_0     #这里与原java代码是反着判断的
const-string v4,"成年"

cond_0:
const-string v3,"未成年"

7.8 字段操作指令:

基本格式:iinstance-op vA,vB,field @CCCC

  • vA寄存器存放读的结果或写的数据
  • vB字段的引用
  • field是字段值的引用
  • type是类型后缀,如char、byte、short、
指令助记符 指令功能描述
普通字段操作:
iget 取值,用于操作int这种的值类型
iget-wide 取值,用于操作wide型字段
iget-object 取值,用于操作对象引用
iget-boolean 取值,用于操作布尔类型
iget-byte 取值,用于操作字节类型
iget-char 取值,用于操作字符类型
iget-short 取值,用于操作short类型
iput 赋值,用于操作int这种的值类型
iput-wide 赋值,用于操作wide型字段
iput-object 赋值,用于操作对象引用
iput-boolean 赋值,用于操作布尔类型
iput-byte 赋值,用于操作字节类型
iput-char 赋值,用于操作字符类型
iput-short 赋值,用于操作short类型

以上的 i 都可以换成 a 或者 s,分别用于操作 数组字段 或者 静态字段

作业2

在java中:

    private String name1 = "zhangsan";
    private String name2 = "lisi";
    private int age1 = 20;
    private Object names[]= {name1,name2,"wangwu"};

在smali中:

# instance fields
.field private name1:Ljava/lang/String;
.field private name2:Ljava/lang/String;
.field private age1:I
.field private names:[Ljava/lang/Object;

# direct methods
.method public constructor ()V
	.locals 9
	#调用了一个方法
	invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;->()V
	
	#上面是声明了,这里需要为声明的对象赋值
	const-string v0, "zhangsan"
    iput-object v0, p0, Lcom/example/hello/Regist;->name1:Ljava/lang/String;
    const-string v1, "lisi"
    iput-object v1, p0, Lcom/example/hello/Regist;->name2:Ljava/lang/String;
	const/4 v2,0x14
	# 用来放数组长度
	const/4 v6,0x3
	
	# 声明一个新数组,寄存器位置为v5,长度为v6
	new-array v5, v6, [Ljava/lang/Object;
	
	const/4 v7,0x0
	aput-object v0, v5, v7    # 将v0的数据放入v5中,下标位置为v7
	
	const/4 v8,0x1
	aput-object v1, v5, v8    # 将v1的数据放入v5中,下标位置为v8
	
	const/4 v9,0x2
	const-string v4,"wangwu"
	aput-object v4, v5, v9    # 将v4的数据放入v5中,下标位置为v9
	# 将v5中的数据放入对象names中
	 iput-object v5, p0, Lcom/example/hello/MainActivity;->names:[Ljava/lang/Object;
	 return-void
	 .end method

作业3

循环:

private int a=2;
private int aa;
public void test(){
for(int i=0;i<4;i++){}
aa=a;
}

.class public Lcom/example/hello/Regist;
.super Landroidx/appcompat/app/AppCompatActivity;
.source "Regist.java"

# instance fields
.field private a:I
.field private aa:I

# direct methods
.method public constructor ()V
    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;->()V
    
    const/16 v0, 0x2
    iput v0, p0, Lcom/example/hello/Regist;->a:I
 .end method
 
 # virtual methods
.method public test()V
	#准备一个0
	 const/4 v1, 0x0
	 #准备一个i
	.local v2, "i":I
	#开始循环
	 :goto_0    
	 # 准备一个4
	const/4 v1, 0x4
	#判断i>=4
	if-ge v2, v1, :cond_0
	#这里中间是循环体中的内容
	# i自增
	add-int/lit8 v2, v2, 0x1
	#再跳转到循环
	 goto :goto_0
	 .end local v2    # "i":I
	 #判断不成功,跳出循环
    :cond_0
    iget v0, p0, Lcom/example/hello/LoginSuccess;->a:I   # 取出a的值

    iput v0, p0, Lcom/example/hello/LoginSuccess;->aa:I   # 赋值给aa
    return-void
.end method
7.9 数据运算指令:

基本格式:bin-op vA,vB,vC(对寄存器vB和vC进行binop算数运算,结果复制到vA寄存器)

指令助记符 指令功能描述
算数运算:
add-type 加法(type有:int、long、float、double)
sub-type 减法
mul-type 乘法
div-type 除法
rem-type 取模
逻辑运算与位运算:
and-type2 逻辑与(type2只有:int、long)
or-type2 逻辑或
xor-type2 逻辑非
shl-type2 有符号数左移
shr-type2 有符号数右移
ushr-type2 无符号数右移
 
binop/lit8 vA,vB,#+cccc 将寄存器vB的值与常量cccc进行算数运算,结果赋值给vA
binop/lit16 vA,vB,#+cccc
binop/2addr vA,vB 将vA寄存器与vB寄存器的值进行binop运算,值赋值给vA
7.10 数据转换指令
指令助记符 指令功能描述
unop vA,vB 数据类型转换
neg-type vx,vy 计算 vx =- vy(type-后可以接类型int、long、float、double)
not-type vx,vy
type1-to-type2 转换类型
int-to-type
int-to-char
int-to-short
例如:
double-to-int v0,v1
7.11 锁与异常操作指令
指令助记符 指令功能描述
锁:(多用在多线程程序中,对同一对象操作)
monitor-enter vA 为指定的对象获取锁
monitor-exit vA 释放指定对象的锁
异常:
throw 抛出一个指定类型的异常
7.12 实例操作指令
指令助记符 指令功能描述
new-instance vA,type@BB 构造一个指定类型的实例,并把引用值给寄存器
instance-of vA,vB,type@CC 判断vB寄存器的引用对象是否可以转为指定类型,是vA就复制1,否vA复制0
check-cast vA,type@BB 把寄存器引用对象转为指定类型,不行就抛出异常

**指令学习英文文档**:http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html

你可能感兴趣的:(Android逆向分析)