(三)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;
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为:<init>)
5. smali 字段的表现形式
java 中字段的表现形式 | smali 中字段的表现形式 |
---|---|
private objectType fieldName; | .field fieldName:objectType |
# instance fields 实例字段
.field <访问权限> [修饰关键字] <字段名> : 类型# static field 静态字段.field <访问权限> static [修饰关键字] <字段名> : 类型
例如:字段的声明对比如下:
在java中:
smali中:
文件格式:
无论是 普通类、抽象类、接口类、内部类,反编译是,都会用单独的smali文件存放。
java中:
smali中:
.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,0x12if-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 <init>()V.locals 9#调用了一个方法invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()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,长度为v6new-array v5, v6, [Ljava/lang/Object;const/4 v7,0x0aput-object v0, v5, v7 # 将v0的数据放入v5中,下标位置为v7const/4 v8,0x1aput-object v1, v5, v8 # 将v1的数据放入v5中,下标位置为v8const/4 v9,0x2const-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 <init>()Vinvoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()Vconst/16 v0, 0x2iput v0, p0, Lcom/example/hello/Regist;->a:I.end method# virtual methods
.method public test()V#准备一个0const/4 v1, 0x0#准备一个i.local v2, "i":I#开始循环:goto_0 # 准备一个4const/4 v1, 0x4#判断i>=4if-ge v2, v1, :cond_0#这里中间是循环体中的内容# i自增add-int/lit8 v2, v2, 0x1#再跳转到循环goto :goto_0.end local v2 # "i":I#判断不成功,跳出循环:cond_0iget v0, p0, Lcom/example/hello/LoginSuccess;->a:I # 取出a的值iput v0, p0, Lcom/example/hello/LoginSuccess;->aa:I # 赋值给aareturn-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
(三)smali 入门相关推荐
- python 三分钟入门_Cython 三分钟入门教程
作者:perrygeo 译者:赖勇浩(http://laiyonghao.com) 原文:http://www.perrygeo.net/wordpress/?p=116 我最喜欢的是Python,它 ...
- scrapy 的三个入门应用场景
说明: 本文参照了官网的 dmoz 爬虫例子. 不过这个例子有些年头了,而 dmoz.org 的网页结构已经不同以前.所以我对xpath也相应地进行了修改. 概要: 本文提出了scrapy 的三个入门 ...
- 米筐量化不支持c语言_AQ答疑:三分钟入门量化投资
答疑丨量化投资三分钟入门(一) 量化投资在国外的发展已经有很长时间的历史,虽然国内市场目前仍处于初起阶段,但是因其投资业绩稳定,市场规模和份额不断扩大,得到了越来越多投资人的认可,各个大型券商也在发展 ...
- 学习平面设计的三个入门步骤
学习平面设计的三个入门步骤 设计的学习可能有很多不同的路,由于这是有设计的多元化学问构造决议的,不论你以前是做什么的`,不论你曾经如何如何,在进入设计范畴之后,你以前的阅历都将影响你,你都将面临应战与 ...
- 三分钟入门大数据之用户画像标签的分类
哈喽,大家好,我是汉斯老师.近几年来,互联网行业由于较高的薪资收入,受到许多人的追捧.很多年轻的学子,或是其他行业的有志青年,都想要投身到这个行业中来.然而一方面受到"互联网寒冬" ...
- 三天入门Python---基本语法(第一天)
序言 本文作为三天入门Python的第一篇,先来个序言: 为什么写这样的文章,Python作为当下一个热门流行的语言,以及其在深度学习框架中的广泛应用,已经严重影响到了我的学习与工作,尤其是一个想要入 ...
- 【算法笔记题解】《算法笔记知识点记录》第三章——入门模拟1——简单模拟
如果喜欢大家还希望给个收藏点赞呀0.0 相关知识点大家没基础的还是要看一下的,链接: <算法笔记知识点记录>第三章--入门模拟 由于放原题的话文章实在太长,所以题多的话我只放思路和题解,大 ...
- (三)webpack入门——webpack功能集合的demo
ErduYang 自律的人生才自由 博客园 首页 新随笔 联系 订阅 管理 随笔 - 37文章 - 0评论 - 8 (三)webpack入门--webpack功能集合的demo 此篇文章来源于http ...
- 计量经济学学习与Stata应用笔记(三)Stata入门实例
计量经济学学习与Stata应用笔记(三)Stata入门实例 使用的版本为stata15. 电力行业规模报酬的经典研究 本例为Nerlove(1963)对电力行业规模的经典研究. 导入数据 Excel的 ...
- 三分钟入门大数据之什么是用户画像?
哈喽,大家好,我是汉斯老师.近几年来,互联网行业由于较高的薪资收入,受到许多人的追捧.很多年轻的学子,或是其他行业的有志青年,都想要投身到这个行业中来.然而一方面受到"互联网寒冬" ...
最新文章
- SendEmail使用TLS发送邮件
- 为什么企业光纤比家用光纤贵那么多,一般至少10倍以上?—Vecloud微云
- ubuntu21.04安装微信3.2.1(deepin-wine方式)
- oracle 运营维护_oracle运维(持续更新)
- redis2.2.8版本的安装详情
- python爬取qq音乐评论_爬虫:QQ音乐评论
- python---之阿partial
- springsecurity 登录失败_145-Spring Security
- mysql环境变量配置还是不行_mysql环境变量配置与Error 1045的解决方案
- Http请求get、post工具类
- 安卓原生系统_你没有用过的安卓系统:原生安卓桌面体验
- pox控制器学习笔记
- Windows域/域树/域林的简单区别
- 写好英语科技论文的诀窍: 主动迎合读者期望,预先回答专家可能质疑--周耀旗教授...
- 通过Bottledwater同步PostgreSQL中的数据变化到Kafka消息队列
- Gentoo安装教程(Systemd+Gnome)
- 电脑结构和CPU、内存、硬盘三者之间的关系
- 本科大学计算机专业排名2015,2015年美国大学本科计算机专业排名
- 浅谈Dubbox原理
- 股市小幽默-炒股第一天
热门文章
- 计算机任务栏隐藏恢复,电脑看不到任务栏怎么办 电脑任务栏不见了如何恢复...
- 简单管理Android手机程序的小程序
- 公司要一个网站,是选模板建站还是定制化建站?
- java jconsole_jconsole与jvisualvm
- asp毕业设计—— 基于asp+access的网上购物系统设计与实现(毕业论文+程序源码)——网上购物系统
- verilog Booth算法乘法器的实现(有无符号)
- 2021年武大计算机学院研究生,武汉大学
- 【postgresql 数据库运维文档】
- 小米 信号测试软件,如何测试出本地的联通、电信、移动信号网络的强弱?
- python傅里叶变换相位谱图_图像傅里叶变换的幅度谱、相位谱以及双谱重构原图像...