[免费专栏] Android安全之APK应用程序分析「附带Smali基础语法解析」
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大
少走了弯路,也就错过了风景,无论如何,感谢经历
Android安全付费专栏长期更新,本篇最新内容请前往:
- [车联网安全自学篇] Android安全之APK应用程序分析「附带Smali基础语法解析」
0x01 Android 应用程序分析
随着物联网的普及,许多智能设备网联化、例如智能门锁、智能手环、智能汽车等智能设备都是由APP来进行远程控制的,比如启动、点火、远程控制,那么由此可看出,APP也是车联网安全中必不可少的一个环节,存在的安全隐患以及攻击面也是相当大的。
0x02 Android apk安装包分析
2.1 Android 是啥?
Android是基于Linux系统的开源操作系统,是由Andy Rubin于2003年在美国加州创建,后被Google于2005年收购。在2008年的时候发布了第一部Android智能手机,随后Android不断发展更新,占据了全球大部分的手机市场。
Android每一个版本都会用一个按照A-Z开头顺序的甜品来命名,但从Android P之后Google改变了这一传统的命名规则,可能是没有那么多让人熟知的甜品代号供使用以及甜品名字并不能让人直观的了解到哪一个甜品有什么特性,于是Google直接采用数字来命令系统,并且加深了logo的颜色,不再使用甜品作为代号。
Android各版本代号、图片及市场占有率(感兴趣的可以https://blog.csdn.net/csdnxia/article/details/101513820 了解)
2.2 Android 应用组件
Android 四大组件分别是:
活动(Activity)
服务(Service)
广播接收器(BroadcasReceiver)
内容提供者(Content Provider)
Activity:通俗来讲就是提供一个界面让用户点击和各种滑动操作,例如,你正在QQ软件跟人聊天,突然微信软件有信息来了,你点击上方弹出的信息提示,你跳转到了微信软件去跟人回复信息,此时就相当于是一个Activity,QQ被暂时放到了栈底。
- 生命周期
启动Activity: onCreate()—>onStart()—>onResume(),Activity进入运行状态。
Activity退居后台: 当前Activity转到新的Activity界面或按Home键回到主屏: onPause()—>onStop(),进入停滞状态。
Activity返回前台: onRestart()—>onStart()—>onResume(),再次回到运行状态。
Activity退居后台,且系统内存不足, 系统会杀死这个后台状态的Activity,若再次回到这个Activity,则会走onCreate()–>onStart()—>onResume()
锁定屏与解锁屏幕 只会调用onPause(),而不会调用onStop方法,开屏后则调用onResume()
- Service:可以在后台执行长时间运行操作而没有用户界面的应用组件(Service 分为两种工作状态,一种是启动状态,主要用于执行后台计算;另一种是绑定状态,主要用于其他组件和 Service 的交互。)
0x03 反编译工具的使用
jaa | smali | 含义 |
---|---|---|
if(A==B) | if-eq A,B | 如果A等于B |
if(A!=B) | if-ne A,B | 如果A不等于B |
if(A<B) | if-lt A,B | 如果A小于B |
if(A<=B) | if-le A,B | 如果A小于等于B |
if(A>B) | if-gt A,B | 如果A大于B |
if(A>=B) | if-ge A,B | 如果A大于等于B |
if(A==0) | if-eqz A | 如果A等于0 |
if(A!=0) | if-nez A | 如果A不等于0 |
if(A<08) | if-ltz A | 如果A小于0 |
if(A>0) | if-gtz A | 如果A大于0 |
if(A>=0) | if-gez A | 如果A大于等于0 |
if(A<=0) | if-lez A | 如果A小于等于0 |
0x04 Smali 寄存器
在Smali中,如果需要存储变量,必须先声明足够数量的寄存器,1个寄存器可以存储32位长度的类型,比如Int,而两个寄存器可以存储64位长度类型的数据,比如Long或Double。
声明可使用的寄存器数量的方式为:.registers N,N代表需要的寄存器的总个数,同时,还有一个关键字 .local ,它用于声明非参数的寄存器个数(包含在registers声明的个数当中),也叫做本地寄存器,只在一个方法内有效,但不常用,一般使用registers即可。
.registers 3说明该方法有三个寄存器,其中一个本地寄存器v0,两个参数寄存器p0,p1,细心的人可能会注意到没有看到p0,原因是p0存放的是this。如果是静态方法的话就只有2个寄存器了,不需要存this了。
1.本地寄存器(local register,非参寄存器)用v开头数字结尾的符号来表示,如v0、v1、v2、…,
2.参数寄存器(parameter register)用p开头数字结尾的符号来表示,如p0、p1、p2、…,
3…registers 用来标明方法中寄存器的总数,即参数寄存器和非参寄存器的总数。
4…local 0,标明在这个函数中最少要用到的本地寄存器的个数,出现在方法中的第一行。在这里,由于只需要调用一个父类的onDestroy()处理,所以只需要用到p0,所以使用到的本地寄存器数为0,在植入代码后不要忘记可能要修改.local的值。
如 .local 4,则可以使用的寄存器是v0-v3。
5.当一个方法被调用的时候,方法的参数被置于最后N个寄存器中。
6.在实例函数中,p0代指“this”,p1表示函数的第一个参数,p2代表函数中的第二个参数…,
7.在static函数中,p1表示函数的第一个参数,p2代表函数中的第二个参数…,因为Java的static方法中没有this方法。
那么,如何确定需要使用的寄存器的个数?
由于非static方法,需要占用一个寄存器以保存this指针,那么这类方法的寄存器个数,最低就为1,如果还需要处理传入的参数,则需要再次叠加,此时还需要考虑Double
4.1 Smali 介绍
Smali是Davlik的寄存器语言,语法上和汇编语言类似. Davlik是基于寄存器的,就是说smali里的所有操作都必须经过寄存器来进行.
4.2 Smali 函数定义
- 函数的定义一般为:函数名(参数类型1参数类型2…)返回值类型
- 基本数据类型的参数与参数之间没有任何分隔符(,)对象数据类型使用分号(;)结束
test()Vvoid test()test(lll)Zboolean test(int,int,int)test(Z[l[lLjava/lang/String;J)Ljava/lang/String;String test(boolean,int[],int[],String,long)
PS:注意: 参数之间没有任何分隔符,返回值在最后
4.3 Smail 基本语法
语法 | 语法含义 |
---|---|
.field private isFlag:z | 定义变量 |
.method | 方法 |
.parameter | 方法参数 |
.prologue | 方法开始 |
.line 12 | 此方法位于第12行 |
invoke-super | 调用父函数 |
const/high16 v0, 0x7fo3 | 把0x7fo3赋值给v0 |
invoke-direct | 调用函数 |
return-void | 函数返回void |
.end method | 函数结束 |
new-instance | 创建实例 |
iput-object | 对象赋值 |
iget-object | 调用对象 |
invoke-static | 调用静态函数 |
invoke-virtual | 调用protected或public方法 |
# | smali注释 |
.locals | 指定方法中非参寄存器总数,出现在方法第一行 |
.registers | 指定方法中寄存器总数 |
p0 | 在静态方法中表示当前对象实例 |
p1 | 表示当前onCreate方法参数 |
v0 | 表示本地(局部)变量,存放在locals寄存器 |
move-result | 获取方法返回基本数据类型 |
move-result-object | 获取方法返回对象 |
.class public Lcom/disney/WMW/WMWActivty; | 类名 |
.super Lcom/XXX/XXX/XXX; | 父类名 |
.source “XXX.java” | 源文件名 |
.implements Lcom/XXX/XXX/XXX; | 实现了接口 |
.annotation | 内部类 |
4.4 Smail 基本数据类型
smali类型 | java类型 |
---|---|
V | void |
Z | boolean |
B | byte |
S | short |
C | char |
I | int |
J | long (64位 需要2个寄存器存储) |
F | float |
D | double (64位 需要2个寄存器存储) |
L | Java类类型 |
[ | 数组类型 |
4.5 Smail 对象
smali对象 | java对象 |
---|---|
Lpackage/name/ObjectName; | package.name.ObjectName |
Ljava/lang/String; | java.lang.String |
L 表示对象类型 | |
package/name 表示包名 | |
; 表示结束 |
4.6 Smail 数组
smali数组 | java数组 |
---|---|
[I | int[] 一维数组 |
[[I | int[][] 二维数组 |
[Ljava/lang/String | String[] 对象数组 |
- 注:每一维最多255个
- 一维数组在类型的左边加一个方括号,比如:[I 等同于Java的 int[ ] ,[F 等同于Java的 float[ ] ,每多一维就加一个方括号,最多可以设置255维。
4.7 Smail 类字段/变量
Lpackage/name/ObjectName;——>FieldName:Ljava/lang/String;
smali字段 | java字段 |
---|---|
public f1:Z | public boolean f1; |
public f2:I | public int f2; |
public f3:L | java/lang/String; public String f3; |
4.7.1 赋值
- 赋值
- 静态static
const-string v0, "Hello Smali"sput-object v0, Lcom/MyActivity;->name:Ljava/lang/String;
相当于java代码 MyActivity.name = “Hello Smali”
- 赋值
- 非静态instance
.local v0, act:Lcom/MyActivity;const/4 v1, 0x2iput v1, v0, Lcom/MyActivity;->name:Ljava/lang/String;
相当于java代码 act.name = “Hello Smali”
4.7.2 取值
- 取值
- 静态(static fields)
sget-object v0, Lcom/MyActivity;->name:Ljava/lang/String;
相当于java代码 v0 = MyActivity.name;
- 取值
- 非静态(instance fields)
.local v0, act:Lcom/MyActivity;iget-object v1, v0 Lcom/MyActivity;->name:Ljava/lang/String;
相当于java代码 v1 = act.name;
4.8 类方法/函数
smali方法 | java方法 |
---|---|
myMethod([I)Ljava/lang/String; | String myMethod(int[]) |
例如:
Lpackage/name/ObjectName;->MethodName(III)Z
第一部分:Lpackage/name/ObjectName;用于声明具体的类型,以便JVM寻找
第二部分:MethodName(III)Z,其中 MethodName 为具体的方法名,()中的字符,表示了参数数量和类型,即3个int型参数,Z为返回值的类型,返回Boolean类型
由于方法的参数列表没有使用逗号这样的分隔符进行划分,所以只能从左到右,根据类型定义来区分参数个数。
通过几个例子来说明,以java.lang.String为例:
java方法:public char charAt(int index){...}
Davilk描述:Ljava/lang/String;->charAt(I)Cjava方法:public void getChars(int srcBegin,int srcEnd,char dst[],int dstBegin){...}
Davilk描述:Ljava/lang/String;->getChars(II[CI)Vjava方法:public boolean equals(Object anObject){...}
Davilk描述:Ljava/lang/String;->equals(Ljava/lang/Object)Z
如果需要调用构造方法,则MethodName为:
例子
String对象在Smali中为:Ljava/lang/StringClass1对象的一个Boolean成员表示为:Lcom/disney/Class1;->isRunning:ZClass2对象的一个String对象成员表示为:Lcom/disney/Class2;->name:Ljava/lang/String
- 总结为
对象类型 -> 成员名 : 成员类型
-> 表示所属关系,类型尾部必须包括一个分号
//Samli代码
.method protected onCreate(Landroid/os/Bundle;)V.locals 1.parameter "savedInstanceState".prologue.line 8invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V.line 9const/high16 v0, 0x7f03invoke-virtual {p0, v0}, Lcom/fusijie/helloworld/MainActivity;->setContentView(I)V.line 10return-void.end method
//Java代码
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}
4.8.1 Smali函数分析
4.8.1.1 类型
smali中的函数和成员变量一样也分为两种:
- direct
- virtual
(1) direct method 就是 private 函数.
(2)其余的public 和 protected 函数都属于 virtual method.
eg:
invoke-direct,invoke-virtual,invole-static
invole-XXX/range (参数大于等于5个时候调用的指令)
4.8.1.1.1 invoke-static
用于调用static 函数
eg:
invoke-static{},Lcom/aaa;->CheckSignature()Zstatic后面的一对大括号{},实际就是调用该方法的实例 + 参数列表, 由于方法即不需要参数也是static, 所以{}为空.
eg:
const-string v0,“NDKLIB”
invoke-staticP{v0},Ljava/lang/System;->loadLibrary(Ljava/lang/String;)Vstatic void System.loadLibrary(String) 来加载 NDK编译的so库用的方法, 就是说这里的v0 就是参数"NDKLIB"
4.8.1.1.2 invoke-super
- 用于调用父类方法用的指令, 一般用于onCreate,onDestory等方法
4.8.1.1.3 invoke-direct
调用private 函数
eg:
invoke-direct{p0},Landroid/app/TabActivity;->()V这里init()就是定义在TabActivity中的一个private函数
4.8.1.1.4 invoke-virtual
用于调用protectd或 public函数, 同时要注意修改smali时候不要错用invoke-direct或 invoke-static
eg:
sget-object v0,Lcom/ddd;->bbb:Lcom/ccc;invoke-virtual{v0,v1},Lcom/ccc;->Message(Ljava/lang/Object;)V
v0 就是 bbb:Lcom/ccc
v1 就是 Ljava/lang/Object参数
4.8.1.1.5 invoke-xxx/range
当方法的参数大于等于5个时候,需要在后面加上 “/range”, range表示范围,使用方法也与以上不同
eg:
invoke-direct/range{v0…v5
},Lcom/pb/ui/TestActivity;->
h(ILjava/lang/String,Ljava/lang/String;Landroid/content/intent;I)Z表示需要传递v0到v5 一共6个参数,大括号里的参数采用省略形式, 且需要连续
4.8.1.1.6 Smali 中函数返回结果的操作
Smali中需要分开来表示调用函数和返回函数结果
如果调用的函数返回非void, 还需要
move-result(返回基本数据类型)和move-result-object(返回对象)指令:eg:
const-string v0,“Eric”
invoke-static{v0},Lcom/pbi;->t(Ljava/lang/String;)Ljava/lang/String;
move-result-object v2;这里 v2保存的就是调用t方法返回的字符串
4.8.2 Smali局部变量
- 本地寄存器 (local register, 非参寄存器)
常用v开头数字结尾的符号表示 v0,v1,v2…
- 参数寄存器 (parameter regisgter)
常用p开头数字结尾的符号来表示 p1,p2,p3…
- .register 用来标明方法中寄存器的总数,即参数寄存器和非参寄存器
.local 标明在这个函数中最少要用到本地寄存器的个数,出现在方法第一行.
4.8.2.1 条件判断语句(if)
- if-gt 大于
- if-ge 大于等于
- if-lt 小于
- if-le 小于等于
java | smali | 含义 |
---|---|---|
if(vA==vB) | if-eq vA,vB | 如果vA等于vB |
if(vA!=vB) | if-ne vA,vB | 如果vA不等于vB |
if(vA<vB) | if-lt vA,vB | 如果vA小于vB |
if(vA<=vB) | if-le vA,vB | 如果vA小于等于vB |
if(vA>vB) | if-gt vA,vB | 如果vA大于vB |
if(vA>=vB) | if-ge vA,vB | 如果vA大于等于vB |
if(vA==0) | if-eqz vA | 如果vA等于0 |
if(vA!=0) | if-nez vA | 如果vA不等于0 |
if(vA<0) | if-ltz vA | 如果vA小于0 |
if(vA>0) | if-gtz vA | 如果vA大于0 |
if(vA>=0) | if-gez vA | 如果vA大于等于0 |
if(vA<=0) | if-lez vA | 如果vA小于等于0 |
private boolean isFlag | .field private isFlag:z | 定义变量 |
Package |
.class .super .local .method .parameter .prologue .line 12 |
指定当前的类名 所继承的父类 定义使用局部变量 方法 方法参数 方法开始 此方法位于.java中的第12行,可以在混淆稳重去除,去除不影响运行结果 |
super |
invoke-super const/high16 v0, 0x7fo3 invoke-direct |
调用父函数 把0x7fo3赋值给v0 调用函数 |
return |
Return-void .end method |
函数返回void 函数结束 |
new |
new-instance iput-object iget-object |
创建实例 对象赋值 调用对象 |
定义一个函数的时候会首先定义这个函数里面有几个寄存器,寄存器版本不同,寄存器的数量也是不一定是一样的
4.8.3 Smali的包信息
- .class public Lcom/aaa;
- .super Lcom/bbb;
- .source “ccc.java”
4.8.3.1 说明
- 是com.aaa 这个package下的一个类
- 继承自 com.bbb这个类
- 是由 ccc.java这个类编译得到的smali文件
4.8.4 Smali字段声明指令
smali文件中字段的声明使用“.field”指令,字段有静态字段与实例字段两种:
静态字段:
# static fields
.field <访问权限> static [修饰关键字] <字段名>:<字段类型>
实例字段:
# instance fields
.field <访问权限> [修饰关键字] <字段名>:<字段类型>
Java代码:
//实例字段
private byte byteType=0;
private short shortType=0;
private int intType=0;
private long longType=8L;
private final int[] intArray = {0,1,2,3,4};//静态字段
private static float floatType=0F;
private static double doubleType=6D;
private static boolean booleanType=Boolean.TRUE;
private static char charType='a';
private static final String stringObject = "ABC";
Smali代码:
# static fields
.field private static booleanType:Z = false
.field private static charType:C = '\u0000'
.field private static doubleType:D = 0.0
.field private static floatType:F = 0.0f
.field private static final stringObject:Ljava/lang/String; = "ABC"# instance fields
.field private byteType:B
.field private final intArray:[I
.field private intType:I
.field private longType:J
.field private shortType:S
4.8.5 Smali数据定义指令、字段操作指令
Smali数据定义指令
指令 | 描述 |
---|---|
const/4 vA,#+B | 将数值符号扩展为32后赋值给寄存器vA |
const-wide/16 vAA,#+BBBB | 将数值符号扩展为64位后赋值个寄存器对vAA |
const-string vAA,string@BBBB | 通过字符串索引高走字符串赋值给寄存器vAA |
const-class vAA,type@BBBB | 通过类型索引获取一个类的引用赋值给寄存器vAA |
Smali字段操作指令-实例字段
指令 | 描述 |
---|---|
iget vX,pY,filed_id | 取值,读取pY寄存器中的对象中的filed_id字段值赋值给vX寄存器 |
iput vX,pY,filed_id | 赋值,设置pY寄存器中的对象中filed_id字段的值为vX寄存器的值 |
iget-wide vX,pY,filed_id | 64位,解释见 iget |
iput-wide vX,pY,filed_id | 64位,解释见 iput |
iget-object vX,pY,filed_id | 解释见 iget |
iput-object vX,pY,filed_id | 解释见 iput |
iget-bype、iget-short、iget-long、iget-float、iget-double、iget-boolean、iget-char | |
iput-bype、iput-short、iput-long、iput-float、iput-double、iput-boolean、iput-char |
Smali字段操作指令-静态字段
指令 | 描述 |
---|---|
sget vX,pY,filed_id | 取值,读取pY寄存器中的对象中的filed_id字段值赋值给vX寄存器 |
sput vX,pY,filed_id | 赋值,设置pY寄存器中的对象中filed_id字段的值为vX寄存器的值 |
sget-wide vX,pY,filed_id | 64位,解释见 sget |
sput-wide vX,pY,filed_id | 64位,解释见 sput |
sget-object vX,pY,filed_id | 解释见 sget |
sput-object vX,pY,filed_id | 解释见 sput |
sget-bype、sget-short、sget-long、sget-float、sget-double、sget-boolean、sget-char | |
sput-bype、sput-short、sput-long、sput-float、sput-double、sput-boolean、sput-char |
Java代码:
//实例字段
private byte byteType=0;
private short shortType=0;
private int intType=0;
private long longType=8L;
private final int[] intArray = {0,1,2,3,4};//静态字段
private static float floatType=0F;
private static double doubleType=6D;
private static boolean booleanType=Boolean.TRUE;
private static char charType='a';
private static final String stringObject = "ABC";
Smali代码:
# direct methods 静态字段赋值
.method static constructor <clinit>()V.registers 2 #.registers指令表示有2个寄存器可用.prologue #.prologue 方法开始.line 10 #行号const/4 v0, 0x0 #sput v0, Lcom/erlin/smali/SmaliParse;->floatType:F.line 11const-wide/16 v0, 0x0sput-wide v0, Lcom/erlin/smali/SmaliParse;->doubleType:D.line 12sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean; #取Boolean对象实例,赋值给v0invoke-virtual {v0}, Ljava/lang/Boolean;->booleanValue()Z move-result v0 #将Boolean.TRUE值赋于v0寄存器sput-boolean v0, Lcom/erlin/smali/SmaliParse;->booleanType:Z #将v0寄存器的值赋于booleanType字段.line 13const/16 v0, 0x61sput-char v0, Lcom/erlin/smali/SmaliParse;->charType:Creturn-void
.end method# direct methods 实例字段赋值
.method public constructor <init>()V.registers 3 #.registers指令表示有3个寄存器可用.prologueconst/4 v0, 0x0 #将0赋值给v0.line 3invoke-direct {p0}, Ljava/lang/Object;-><init>()V.line 4iput-byte v0, p0, Lcom/erlin/smali/SmaliParse;->byteType:B #p0代表this,将v0的值赋值给byteType字段.line 5iput-short v0, p0, Lcom/erlin/smali/SmaliParse;->shortType:S.line 6iput v0, p0, Lcom/erlin/smali/SmaliParse;->intType:I.line 7const-wide/16 v0, 0x0iput-wide v0, p0, Lcom/erlin/smali/SmaliParse;->longType:J.line 8const/4 v0, 0x5 #数组长度赋值给v0寄存器new-array v0, v0, [I #创建指定类型[I即int数组,长度为v0即5,并将数组引用赋值于v0寄存器fill-array-data v0, :array_18 #用指定标记array_18处的数据填充数组iput-object v0, p0, Lcom/erlin/smali/SmaliParse;->intArray:[I #为数组赋值return-void nop #空 指令:array_18.array-data 40x00x10x20x30x4.end array-data
.end method
4.8.6 Smali空指令
指令 | 描述 |
---|---|
nop | 空操作指令,通常用于代码对齐,不进行实际操作,值为00 |
Smali数组操作指令
数组操作指令包括读取数组长度、新建数组、数组赋值、数组元素取值与赋值等操作。
指令 | 描述 |
---|---|
array-length vA,vB | 获取给定vB寄存器中数组的长度并将值赋给vA寄存器 |
new-array vA,vB,type@CCCC | 构造指定类型(type@CCCC)与大小(vB)的数组,并将值赋给vA寄存器 |
new-array/jumbo vAAAA,vBBBB,type@CCCCCCCC | 指令功能与上一条指令相同,只是寄存器与指令的索引取值范围更大 |
filled-new-array {vC,vD,vE,vF,vG},type@BBBB | 构造指定类型(type@BBBB)与大小(vA)的数组并填充数组内容。vA寄存器是隐含使用的,除了指定数组的大小外还制订了参数的个数,vC~vG是使用到的参数寄存器序列 |
filled-new-array/range {vCCCC, … ,vNNNN},type@BBBB | 指定功能与上一条指令相同,只是参数寄存器使用range字节码后缀指定了取值范围,vC是第一个参数寄存器, N=A+C-1。 |
filled-new-array/jumbo {vCCCC, … ,vNNNN},type@BBBBBBBB | 指令功能与上一条指令相同,只是寄存器与指令的索引取值范围更大. |
arrayop vAA,vBB,vCC | 对vBB寄存器指定的数组元素进入取值与赋值。vCC寄存器指定数组元素索引,vAA寄存器用来寄放读取的或需要设置的数组元素的值。读取元素使用 aget类指令,元素赋值使用aput指令,元素赋值使用aput类指令,根据数组中存储的类型指令后面会紧跟不同的指令后缀,指令列表有aget、 aget-wide、aget-object、aget-boolean、aget-byte、aget-char、aget-short、aput、 aput-wide、aput-boolean、aput-byte、aput-char、aput-short。 |
java代码:
public void array() {int[] intArray = {10, -1, 9};int len = intArray.length;String[] stringArray = new String[len];stringArray[0] = "A";stringArray[1] = "B";stringArray[2] = "C";
}
Smali代码:
.method public array()V.registers 6 #.registers 声明6个寄存器.prologue.line 5const/4 v3, 0x3 #将0x3寄存给v3寄存器new-array v0, v3, [I #创建[I类型长度为v3寄存器数组,引用赋值给v0寄存器fill-array-data v0, :array_1a #用array_1a标记处数据,赋值于v0寄存器.line 6.local v0, "intArray":[I #创建指定类型数组,并用v0寄存器中的值填充数据,赋于寄存器v0array-length v1, v0 #获取v0寄存器长度,赋值给v1寄存器.line 8.local v1, "len":Inew-array v2, v1, [Ljava/lang/String;.line 9.local v2, "stringArray":[Ljava/lang/String;const/4 v3, 0x0const-string v4, "A"aput-object v4, v2, v3 #v4寄存器值,赋值给v2寄存器数组,数组索引为v3.line 10const/4 v3, 0x1const-string v4, "B"aput-object v4, v2, v3.line 11const/4 v3, 0x2const-string v4, "C"aput-object v4, v2, v3.line 12return-void.line 5nop:array_1a.array-data 40xa-0x10x9.end array-data
.end method
4.8.8 Smali类指令,Smali方法指令,Smali返回指令
Smali类指令
类指令 | 描述 |
---|---|
.class <访问权限> [修饰关键字] L<完整类名>; | 表示类 |
.super L<父类完整类名>; | 父类 |
.source “Java类名” | java文件名 |
注解指令 | |
.annotation | [注解属性] <注解类名> |
value = {值列表} | |
.end annotation | 注解结束 |
接口指令 | |
.implements<接口名> | 接口名 |
案例一:Java类
//java
package com.erlin.smali;
public class SmaliParse {//基类Object//类
}
//Smali
.class public Lcom/erlin/smali/SmaliParse;
.super Ljava/lang/Object;
.source "SmaliParse.java"
案例二:Java final类
//Java
package com.erlin.smali;
public final class SmaliParse {//基类Object//类
}//Smali
.class public final Lcom/erlin/smali/SmaliParse;
.super Ljava/lang/Object;
.source "SmaliParse.java"
案例三:Java Interface类
//Java
package com.erlin.smali;
public interface Interface {void interfaceMethod();
}//Smali
.class public interface abstract Lcom/erlin/smali/Interface;
.super Ljava/lang/Object;
.source "Interface.java"# virtual methods
.method public abstract interfaceMethod()V
.end method
案例四:Java Interface类实现
//Java
package com.erlin.smali;
public class InterfaceImpl implements Interface{@Overridepublic void interfaceMethod() {}
}//Smali
.class public Lcom/erlin/smali/InterfaceImpl;
.super Ljava/lang/Object;
.source "InterfaceImpl.java"# interfaces
.implements Lcom/erlin/smali/Interface;# direct methods
.method public constructor <init>()V.registers 1.prologue.line 3invoke-direct {p0}, Ljava/lang/Object;-><init>()Vreturn-void
.end method# virtual methods
.method public interfaceMethod()V.registers 1.prologue.line 7return-void
.end method
案例五:抽象类
//Java
package com.erlin.smali;
public abstract class AbstractClass {abstract void abstractMethod();public void method(){}
}//Smali
.class public abstract Lcom/erlin/smali/AbstractClass;
.super Ljava/lang/Object;
.source "AbstractClass.java"# direct methods
.method public constructor <init>()V.registers 1.prologue.line 3invoke-direct {p0}, Ljava/lang/Object;-><init>()Vreturn-void
.end method# virtual methods
.method abstract abstractMethod()V
.end method.method public method()V.registers 1.prologue.line 8return-void
.end method
案例六:内部类、内部接口、内部抽象类
SmaliParse.java
//Java
package com.erlin.smali;
public final class SmaliParse {//内部类public class InnerClass{public void method(){}}//内部接口public interface InnerInterface{void interfaceMethod();}//内部抽象类public abstract class InnerAbstractClass{abstract void interfaceMethod();public void method(){}}//内部类继承public class InnerClassExtends extends InnerClass{public void method(){}}//内部接口实现class InnerInterfaceImpl implements InnerInterface{public void method(){}@Overridepublic void interfaceMethod() {}}//内部抽象类继承class InnerAbstractClassExtends extends InnerAbstractClass{public void method(){}@Overridevoid interfaceMethod() {}}
}
SmaliParse.Smali文件
.class public final Lcom/erlin/smali/SmaliParse;
.super Ljava/lang/Object;
.source "SmaliParse.java"# annotations
.annotation system Ldalvik/annotation/MemberClasses;value = {Lcom/erlin/smali/SmaliParse$InnerAbstractClassExtends;,Lcom/erlin/smali/SmaliParse$InnerInterfaceImpl;,Lcom/erlin/smali/SmaliParse$InnerClassExtends;,Lcom/erlin/smali/SmaliParse$InnerAbstractClass;,Lcom/erlin/smali/SmaliParse$InnerInterface1;,Lcom/erlin/smali/SmaliParse$InnerInterface;,Lcom/erlin/smali/SmaliParse$InnerClass;}
.end annotation# direct methods
.method public constructor <init>()V.registers 1.prologue.line 3invoke-direct {p0}, Ljava/lang/Object;-><init>()Vreturn-void
.end method
SmaliParse$InnerClass.smali文件
.class public Lcom/erlin/smali/SmaliParse$InnerClass;
.super Ljava/lang/Object;
.source "SmaliParse.java"# annotations
.annotation system Ldalvik/annotation/EnclosingClass;value = Lcom/erlin/smali/SmaliParse;
.end annotation.annotation system Ldalvik/annotation/InnerClass;accessFlags = 0x1name = "InnerClass"
.end annotation# instance fields
.field final synthetic this$0:Lcom/erlin/smali/SmaliParse;# direct methods
.method public constructor <init>(Lcom/erlin/smali/SmaliParse;)V.registers 2.param p1, "this$0" # Lcom/erlin/smali/SmaliParse;.prologue.line 4iput-object p1, p0, Lcom/erlin/smali/SmaliParse$InnerClass;->this$0:Lcom/erlin/smali/SmaliParse;invoke-direct {p0}, Ljava/lang/Object;-><init>()Vreturn-void
.end method# virtual methods
.method public method()V.registers 1.prologue.line 5return-void
.end method
SmaliParse$InnerInterface.smali
.class public interface abstract Lcom/erlin/smali/SmaliParse$InnerInterface;
.super Ljava/lang/Object;
.source "SmaliParse.java"# annotations
.annotation system Ldalvik/annotation/EnclosingClass;value = Lcom/erlin/smali/SmaliParse;
.end annotation.annotation system Ldalvik/annotation/InnerClass;accessFlags = 0x609name = "InnerInterface"
.end annotation# virtual methods
.method public abstract interfaceMethod()V
.end method
SmaliParse$InnerAbstractClass.smali
.class public abstract Lcom/erlin/smali/SmaliParse$InnerAbstractClass;
.super Ljava/lang/Object;
.source "SmaliParse.java"# annotations
.annotation system Ldalvik/annotation/EnclosingClass;value = Lcom/erlin/smali/SmaliParse;
.end annotation.annotation system Ldalvik/annotation/InnerClass;accessFlags = 0x401name = "InnerAbstractClass"
.end annotation# instance fields
.field final synthetic this$0:Lcom/erlin/smali/SmaliParse;# direct methods
.method public constructor <init>(Lcom/erlin/smali/SmaliParse;)V.registers 2.param p1, "this$0" # Lcom/erlin/smali/SmaliParse;.prologue.line 15iput-object p1, p0, Lcom/erlin/smali/SmaliParse$InnerAbstractClass;->this$0:Lcom/erlin/smali/SmaliParse;invoke-direct {p0}, Ljava/lang/Object;-><init>()Vreturn-void
.end method# virtual methods
.method abstract interfaceMethod()V
.end method.method public method()V.registers 1.prologue.line 17return-void
.end method
SmaliParse$InnerInterfaceImpl.smali
.class Lcom/erlin/smali/SmaliParse$InnerInterfaceImpl;
.super Ljava/lang/Object;
.source "SmaliParse.java"# interfaces
.implements Lcom/erlin/smali/SmaliParse$InnerInterface;
.implements Lcom/erlin/smali/SmaliParse$InnerInterface1;# annotations
.annotation system Ldalvik/annotation/EnclosingClass;value = Lcom/erlin/smali/SmaliParse;
.end annotation.annotation system Ldalvik/annotation/InnerClass;accessFlags = 0x0name = "InnerInterfaceImpl"
.end annotation# instance fields
.field final synthetic this$0:Lcom/erlin/smali/SmaliParse;# direct methods
.method constructor <init>(Lcom/erlin/smali/SmaliParse;)V.registers 2.param p1, "this$0" # Lcom/erlin/smali/SmaliParse;.prologue.line 24iput-object p1, p0, Lcom/erlin/smali/SmaliParse$InnerInterfaceImpl;->this$0:Lcom/erlin/smali/SmaliParse;invoke-direct {p0}, Ljava/lang/Object;-><init>()Vreturn-void
.end method# virtual methods
.method public interfaceMethod()V.registers 1.prologue.line 29return-void
.end method.method public interfaceMethod1()V.registers 1.prologue.line 34return-void
.end method.method public method()V.registers 1.prologue.line 25return-void
.end method
SmaliParse$InnerClassExtends.smali
.class public Lcom/erlin/smali/SmaliParse$InnerClassExtends;
.super Lcom/erlin/smali/SmaliParse$InnerClass;
.source "SmaliParse.java"# annotations
.annotation system Ldalvik/annotation/EnclosingClass;value = Lcom/erlin/smali/SmaliParse;
.end annotation.annotation system Ldalvik/annotation/InnerClass;accessFlags = 0x1name = "InnerClassExtends"
.end annotation# instance fields
.field final synthetic this$0:Lcom/erlin/smali/SmaliParse;# direct methods
.method public constructor <init>(Lcom/erlin/smali/SmaliParse;)V.registers 2.param p1, "this$0" # Lcom/erlin/smali/SmaliParse;.prologue.line 20iput-object p1, p0, Lcom/erlin/smali/SmaliParse$InnerClassExtends;->this$0:Lcom/erlin/smali/SmaliParse;invoke-direct {p0, p1}, Lcom/erlin/smali/SmaliParse$InnerClass;-><init>(Lcom/erlin/smali/SmaliParse;)Vreturn-void
.end method# virtual methods
.method public method()V.registers 1.prologue.line 21return-void
.end method
SmaliParse$InnerAbstractClassExtends.smali
.class Lcom/erlin/smali/SmaliParse$InnerAbstractClassExtends;
.super Lcom/erlin/smali/SmaliParse$InnerAbstractClass;
.source "SmaliParse.java"# annotations
.annotation system Ldalvik/annotation/EnclosingClass;value = Lcom/erlin/smali/SmaliParse;
.end annotation.annotation system Ldalvik/annotation/InnerClass;accessFlags = 0x0name = "InnerAbstractClassExtends"
.end annotation# instance fields
.field final synthetic this$0:Lcom/erlin/smali/SmaliParse;# direct methods
.method constructor <init>(Lcom/erlin/smali/SmaliParse;)V.registers 2.param p1, "this$0" # Lcom/erlin/smali/SmaliParse;.prologue.line 37iput-object p1, p0, Lcom/erlin/smali/SmaliParse$InnerAbstractClassExtends;->this$0:Lcom/erlin/smali/SmaliParse;invoke-direct {p0, p1}, Lcom/erlin/smali/SmaliParse$InnerAbstractClass;-><init>(Lcom/erlin/smali/SmaliParse;)Vreturn-void
.end method# virtual methods
.method interfaceMethod()V.registers 1.prologue.line 42return-void
.end method.method public method()V.registers 1.prologue.line 38return-void
.end method
- Smali方法指令-静态方法
指令 | 描述 |
---|---|
.method <访问权限> static [修饰关键字] methodName()<类型> | |
.registers count | 方法内使用寄存器数量 |
.prologue | 方法开始 |
return-void | 方法返回数据类型 |
.end method | 方法结束 |
案例一:
//Java
public static void methodStaticSmali(){//静态方法
}//Smali
.method public static methodStaticSmali()V.registers 0.prologuereturn-void
.end method
案例二:
//Java
public final static void methodStaticFinalSmali() {//静态方法
}//Smail
.method public static final methodStaticFinalSmali()V.registers 0.prologuereturn-void
.end method
- Smali返回指令
指令 | 描述 |
---|---|
return-void | 返回Void类型 |
return vAA | 返回非32位对象类型值,返回值为8位寄存器vAA |
return-wide vAA | 返回非64位对象类型值,返回值为8位寄存器对vAA |
return-object vAA | 返回对象类型,返回值为8位寄存器对vAA |
案例一:
// Java
public void methodSmali() {
}//Smali
.method public methodSmali()V.registers 1.prologue.line 6return-void
.end method
案例二:
//Java
public int methodSmaliInt(){return Integer.MAX_VALUE;
}//Smali
.method public methodSmaliInt()I.registers 2.prologue.line 9const v0, 0x7fffffffreturn v0
.end method
案例三:
//Java
public long methodSmaliLong(){return Long.MAX_VALUE;
}//Smali
.method public methodSmaliLong()J.registers 3.prologue.line 13const-wide v0, 0x7fffffffffffffffLreturn-wide v0
.end method
案例四:
//Java
public String methodSmaliString(){return "String";
}//Smali
.method public methodSmaliString()Ljava/lang/String;.registers 2.prologue.line 17const-string v0, "String"return-object v0
.end method
4.8.8.1 方法操作指令
方法调用指令负责调用类实例的方法,基础指令为invoke。
指令 | 描述 |
---|---|
invoke-virtual{parameters}, methodtocall | 虚方法调用,调用的方法运行时确认实际调用,和实例引用的实际对象有关,动态确认的,一般是带有修饰符protected或public的方法. |
invoke-super {parameter},methodtocall | 直接调用父类的虚方法,编译时,静态确认的。 |
invoke-direct { parameters }, methodtocall | 没有被覆盖方法的调用,即不用动态根据实例所引用的调用,编译时,静态确认的,一般是private或方法; |
invoke-static {parameters}, methodtocall | 是类静态方法的调用,编译时,静态确定的 |
invoke-interface {parameters},methodtocall | 调用接口方法,调用的方法运行时确认实际调用,即会在运行时才确定一个实现此接口的对象 |
案例一:
//Java
public void invokeMethod(){BaseClassImpl baseClassImpl = new BaseClassImpl();baseClassImpl.baseFinalMethod();super.baseFinalMethod();baseClassImpl.staticMethod();((Interface)baseClassImpl).interfaceMethod();baseClassImpl.method();
}
//Smali
.method public invokeMethod()V.registers 2.prologue.line 17new-instance v0, Lcom/erlin/smali/BaseClassImpl;invoke-direct {v0}, Lcom/erlin/smali/BaseClassImpl;-><init>()V.line 18.local v0, "baseClassImpl":Lcom/erlin/smali/BaseClassImpl;invoke-virtual {v0}, Lcom/erlin/smali/BaseClassImpl;->baseFinalMethod()V.line 19invoke-super {p0}, Lcom/erlin/smali/BaseClass;->baseFinalMethod()V.line 20invoke-static {}, Lcom/erlin/smali/BaseClassImpl;->staticMethod()V.line 21invoke-interface {v0}, Lcom/erlin/smali/Interface;->interfaceMethod()V.line 22invoke-virtual {v0}, Lcom/erlin/smali/BaseClassImpl;->method()V.line 23return-void
.end method
4.8.9 实例操作指令
指令 | 描述 |
---|---|
instance-of vA, vB, type@CCCC | 用于判断vB寄存器中对象引用是否可以转换成指定的类型,如果可以转换vA寄存器值为1,否则vA寄存器值为0 |
例:if(innerClassExtends instanceof InnerClass){ | |
} | |
check-cast vAA, type@BBBB | 将vAA寄存器中的对象引用转换成指定的类型,如果不能转换则抛出ClassCatException异常。如果type@BBBB指定的是基本类型,那么对非基本类型的类型vAA来说,运行将会失败。 |
new-instance vAA,type@CCCC | 用于构造一个指定类型对象的新实例,并将对象引用赋值给vAA寄存器。 |
案例一:
//Java
public void instanceOperationMethod(){InnerClass innerClass = new InnerClass();InnerClassExtends innerClassExtends = (InnerClassExtends)innerClass;if(innerClass instanceof InnerClassExtends){}
}
//Smali
# virtual methods
.method public instanceOperationMethod()V.registers 4.prologue.line 41new-instance v0, Lcom/erlin/smali/SmaliParse$InnerClass;invoke-direct {v0, p0}, Lcom/erlin/smali/SmaliParse$InnerClass;-><init>(Lcom/erlin/smali/SmaliParse;)V.local v0, "innerClass":Lcom/erlin/smali/SmaliParse$InnerClass;move-object v1, v0.line 42check-cast v1, Lcom/erlin/smali/SmaliParse$InnerClassExtends;.line 44.local v1, "innerClassExtends":Lcom/erlin/smali/SmaliParse$InnerClassExtends;instance-of v2, v0, Lcom/erlin/smali/SmaliParse$InnerClassExtends;if-eqz v2, :cond_c.line 47:cond_creturn-void
.end method
4.8.11 Smali数据运算指令
Smali算数运算:加、减、乘、除、模(取余)
指令 | 描述 |
---|---|
add-type vAA, vBB, vCC | 加:type 类型后缀包括 int、long、float、double,vAA=(vBB+vCC) |
sub-type vAA, vBB, vCC | 减:type 类型后缀包括 int、long、float、double,vAA=(vBB-vCC) |
mul-type vAA, vBB, vCC | 乘:type 类型后缀包括 int、long、float、double,vAA=(vBB*vCC) |
div-type vAA, vBB, vCC | 除:type 类型后缀包括 int、long、float、double,vAA=(vBB/vCC) |
rem-type vAA, vBB, vCC | 模:type 类型后缀包括 int、long、float、double,vAA=(vBB%vCC) |
Java代码:
public void number(){int a = 3;int b = 7;int add = a+b;int sub = b-a;int mul = a*b;int div = b/a;int rem = a%b;a++;b--;a+=b;b+=a;a*=b;b*=a;a/=b;b/=a;a%=b;b%=a;
}
Smali代码:
.method public number()V.registers 8.prologue.line 5const/4 v0, 0x3.line 6.local v0, "a":Iconst/4 v2, 0x7.line 8.local v2, "b":Iadd-int v1, v0, v2 #v1 = v0+v2.line 9.local v1, "add":Isub-int v6, v2, v0 #v6=v2-v0.line 10.local v6, "sub":Imul-int v4, v0, v2 #v4=v0*v2.line 11.local v4, "mul":Idiv-int v3, v2, v0 #v3=v2/v0.line 12.local v3, "div":Irem-int v5, v0, v2 #v5=v0%v2.line 14.local v5, "rem":Iadd-int/lit8 v0, v0, 0x1 #v0=v0+0x1即 ++ 运算符.line 15add-int/lit8 v2, v2, -0x1 #v2=v2-0x1即 -- 运算符.line 17add-int/lit8 v0, v0, 0x6 #v0=v0+0x6.line 18add-int/lit8 v2, v2, 0xa #v2=v2+0xa.line 20mul-int/lit8 v0, v0, 0x10.line 21mul-int/lit16 v2, v2, 0xa0.line 23div-int/2addr v0, v2 #v0=v0/v2.line 24div-int/2addr v2, v0.line 26rem-int/2addr v0, v2 #v0=v0%v2.line 27rem-int/2addr v2, v0.line 28return-void
.end method
Smali位运算符:&、|、^、<<、>>
指令 | 描述 |
---|---|
and-type vAA, vBB, vCC | and:type类型后缀包括 int、long、float、double,vAA=(vBB and vCC) |
or-type vAA, vBB, vCC | or:type类型后缀包括 int、long、float、double,vAA=(vBB or vCC) |
xor-type vAA, vBB, vCC | xor:type类型后缀包括 int、long、float、double,vAA=(vBB xor vCC) |
shl-type vAA, vBB, vCC | 有符号左移:type类型后缀包括 int、long、float、double,vAA=(vBB << vCC) |
shr-type vAA, vBB, vCC | 有符号右移:type类型后缀包括 int、long、float、double,vAA=(vBB >> vCC) |
ushr-type vAA, vBB, vCC | 无符号右移:type类型后缀包括 int、long、float、double,vAA=(vBB >> vCC) |
Java代码:
public void number(){int a = 3;int b = 7;int c = (a&b);c = (a|b);c = (a^b);c=(a<<b);c=(a>>b);
}
Smali代码:
.method public number()V.registers 4.prologue.line 5const/4 v0, 0x3.line 6.local v0, "a":Iconst/4 v1, 0x7.line 8.local v1, "b":Iand-int v2, v0, v1.line 9.local v2, "c":Ior-int v2, v0, v1.line 10xor-int v2, v0, v1.line 11shl-int v2, v0, v1.line 12shr-int v2, v0, v1.line 13return-void
.end method
4.8.12 goto跳转指令
无条件跳转
指令 | 描述 |
---|---|
goto +AA | 无条件跳转到指定偏移处,偏移量不能为0,偏移宽度为8位 |
goto/16 +AAAA | 无条件跳转到指定偏移处,偏移量不能为0,偏移宽度为16位 |
goto/32 +AAAAAAAA | 无条件跳转到指定偏移处,偏移量不能为0,偏移宽度为32位 |
PS:代码参见 for/whlie 循环
4.8.13 if跳转指令
if-test vA, vB, +CCCC:
指令 | 描述 |
---|---|
if-ne vA, vB, +CCCC | 如果vA!=vB,则跳转到CCCC位置 |
if-eq vA, vB, +CCCC | 如果vA==vB,则跳转到CCCC位置 |
if-lt vA, vB, +CCCC | 如果vA < vB,则跳转到CCCC位置 |
if-le vA, vB, +CCCC | 如果vA <= vB,则跳转到CCCC位置 |
if-gt vA, vB, +CCCC | 如果vA > vB,则跳转到CCCC位置 |
if-ge vA, vB, :CCCC | 如果vA >= vB,则跳转到CCCC位置 |
Java代码:
public void ifSmali(){int a = 8;int b = 8;if(a == b){a+=a;}else if(a!=b){b+=b;}else if(a<b){a+=b;}else if(a>b){b+=a;}else if(a>=b){a+=1;}else if(a<=b){b+=1;}else{a+=2;}
}
Smali代码:
.method public ifSmali()V.registers 3.prologue.line 19const/16 v0, 0x8.line 20.local v0, "a":Iconst/16 v1, 0x8.line 21.local v1, "b":Iif-ne v0, v1, :cond_8.line 22add-int/2addr v0, v0.line 36:goto_7return-void.line 23:cond_8if-eq v0, v1, :cond_c.line 24add-int/2addr v1, v1goto :goto_7.line 25:cond_cif-ge v0, v1, :cond_10.line 26add-int/2addr v0, v1goto :goto_7.line 27:cond_10if-le v0, v1, :cond_14.line 28add-int/2addr v1, v0goto :goto_7.line 29:cond_14if-lt v0, v1, :cond_19.line 30add-int/lit8 v0, v0, 0x1goto :goto_7.line 31:cond_19if-gt v0, v1, :cond_1e.line 32add-int/lit8 v1, v1, 0x1goto :goto_7.line 34:cond_1eadd-int/lit8 v0, v0, 0x2goto :goto_7
.end method
if-testz vAA, +BBBB
指令 | 描述 |
---|---|
if-eqz vAA,+BBBB | 如果vAA==0,则跳转到BBBB |
if-nez vAA,+BBBB | 如果vAA!=0,则跳转到BBBB |
if-ltz vAA,+BBBB | 如果vAA<0,则跳转到BBBB |
if-lez vAA,+BBBB | 如果vAA<=0,则跳转到BBBB |
if-gtz vAA,+BBBB | 如果vAA>0,则跳转到BBBB |
if-gez vAA,+BBBB | 如果vAA>=0,则跳转到BBBB |
Java代码
public void ifSmali(){int a = 0;boolean b = false;if(b){a++;}else if(!b){b=!b;}else if(a<0){a++;}else if(a>0){a++;}else if(a>=0){a+=1;}else if(a<=0){a++;}else{a+=2;}
}
Smali代码
.method public ifSmali()V.registers 3.prologue.line 39const/4 v0, 0x0.line 40.local v0, "a":Iconst/4 v1, 0x0.line 41.local v1, "b":Zif-eqz v1, :cond_7.line 42add-int/lit8 v0, v0, 0x1.line 56:goto_6return-void.line 43:cond_7if-nez v1, :cond_f.line 44if-nez v1, :cond_dconst/4 v1, 0x1:goto_cgoto :goto_6:cond_dconst/4 v1, 0x0goto :goto_c.line 45:cond_fif-gez v0, :cond_14.line 46add-int/lit8 v0, v0, 0x1goto :goto_6.line 47:cond_14if-lez v0, :cond_19.line 48add-int/lit8 v0, v0, 0x1goto :goto_6.line 49:cond_19if-ltz v0, :cond_1e.line 50add-int/lit8 v0, v0, 0x1goto :goto_6.line 51:cond_1eif-gtz v0, :cond_23.line 52add-int/lit8 v0, v0, 0x1goto :goto_6.line 54:cond_23add-int/lit8 v0, v0, 0x2goto :goto_6
.end method
4.8.13 switch跳转指令
指令 | 描述 |
---|---|
sparse-switch vAA, +BBBBBBBB | 分支跳转指令:vAA寄存器为switch分支中需要判断的值,BBBBBBBB指向一个sparse-switch-payload格式的偏移表,表中的值是无规律的偏移量。 |
Smali偏移表-整形顺序结构:
packed-switch p1, :pswitch_data_c #偏移表pswitch_data_c
add-int/lit8 p1, p1, 0x1 #如果 switch 表没有命中case,则顺序执行代码,即default:pswitch_6
add-int/lit8 p1, p1, 0x1:pswitch_9
add-int/lit8 p1, p1, -0x1:pswitch_data_c #偏移表位置
.packed-switch 0x0 #指定头偏移为0x0 :pswitch_6 #case 0:pswitch_9 #case 1
.end packed-switch
Smali偏移表-整形非顺序结构:
sparse-switch p1, :sswitch_data_c
add-int/lit8 p1, p1, 0x1 #如果 switch 表没有命中case,则顺序执行代码,即default:sswitch_6
add-int/lit8 p1, p1, 0x1:sswitch_9
add-int/lit8 p1, p1, -0x1:sswitch_data_c
.sparse-switch0x50 -> :sswitch_6 #case 800x3f1 -> :sswitch_9 #case 1009
.end sparse-switch
Java代码:
public void switchSmali(int value) {switch (value) {case 0:value++;break;case 1:value--;break;default:++value;break;}
}
Smali顺序结构代码:
.method public switchSmali(I)V.registers 2.param p1, "value" # I.prologue.line 5packed-switch p1, :pswitch_data_c.line 13add-int/lit8 p1, p1, 0x1.line 16:goto_5return-void.line 7:pswitch_6add-int/lit8 p1, p1, 0x1.line 8goto :goto_5.line 10:pswitch_9add-int/lit8 p1, p1, -0x1.line 11goto :goto_5.line 5:pswitch_data_c.packed-switch 0x0:pswitch_6:pswitch_9.end packed-switch
.end method
Java代码
public void switchSmali(int value) {switch (value) {case 80:value++;break;case 1009:value--;break;default:++value;break;}
}
Smali非顺序结构代码:
.method public switchSmali(I)V.registers 2.param p1, "value" # I.prologue.line 7sparse-switch p1, :sswitch_data_c.line 15add-int/lit8 p1, p1, 0x1.line 18:goto_5return-void.line 9:sswitch_6add-int/lit8 p1, p1, 0x1.line 10goto :goto_5.line 12:sswitch_9add-int/lit8 p1, p1, -0x1.line 13goto :goto_5.line 7:sswitch_data_c.sparse-switch0x50 -> :sswitch_60x3f1 -> :sswitch_9.end sparse-switch
.end method
4.8.14 for/whlie 循环
for/whlie循环最终表现的Smali指令形式是goto指令,参见 八、goto跳转指令
Smali循环基础代码:
:goto_1 #goto标签
if-ge vAA, vBB, :+CCCCCCCC #vAA寄存器>=vBB寄存器,跳至+CCCCCCCC即循环体外add-int/lit8 vAA, vAA, 0x1 #条件递增或递减goto :goto_1 #goto跳转到goto_1标签:+CCCCCCCC
#for循环之外的代码
for循环代码:
//Java代码
public void forSmali(){for(int i=0;i<100;i++){}
}
//Smail代码
.method public forSmali()V.registers 3.prologue.line 66const/4 v0, 0x0.local v0, "i":I:goto_1const/16 v1, 0x64if-ge v0, v1, :cond_8add-int/lit8 v0, v0, 0x1goto :goto_1.line 70:cond_8return-void
.end method
while循环代码:
//Java
public void whileSmali(){int i = 0;while (i<100){i++;}
}
//Smali
.method public whileSmali()V.registers 3.prologue.line 73const/4 v0, 0x0.line 74.local v0, "i":I:goto_1const/16 v1, 0x64if-ge v0, v1, :cond_8.line 75add-int/lit8 v0, v0, 0x1goto :goto_1.line 77:cond_8return-void
.end method
4.8.15 try/catch
指令 | 描述 |
---|---|
:try_start_标号 | try的开始 |
:try_end_标号 | try的结束 |
.catch <异常类型> {< try_start_标号> … < try_end_标号>} < catch_标号> | .catch指令:catch指令指明异常类型,指明try块的开始和结束,如果异常捕获成功,则会跳转到catch_标号处,如果捕获不成功,则顺序执行代码 |
try/catch Smali基础代码:
:try_start_0
const-string v0, "a"invoke-static {v0}, Ljava/lang/Integer;->valueOf(Ljava/lang/String;)Ljava/lang/Integer;
:try_end_5
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_5} :catch_6:goto_5
return-void:catch_6
move-exception v0goto :goto_5
try/catch代码
//Java
public void tryCatchSmali() {try {Integer.valueOf("a");} catch (Exception e) {}
}
//Smali
.method public tryCatchSmali()V.registers 2.prologue.line 81:try_start_0const-string v0, "a"invoke-static {v0}, Ljava/lang/Integer;->valueOf(Ljava/lang/String;)Ljava/lang/Integer;:try_end_5.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_5} :catch_6.line 85:goto_5return-void.line 82:catch_6move-exception v0goto :goto_5
.end method
参考链接:
https://www.jianshu.com/p/2766b76da201
https://www.cnblogs.com/lee0oo0/p/3728271.html
https://blog.csdn.net/pwelyn/article/details/50487404/
https://blog.csdn.net/qq_36252044/article/details/86636885
你以为你有很多路可以选择,其实你只有一条路可以走
[免费专栏] Android安全之APK应用程序分析「附带Smali基础语法解析」相关推荐
- [免费专栏] Android安全之APK逆向入门介绍
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...
- [免费专栏] Android安全之数据存储与数据安全「详解」
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...
- java数据类型入门程序_「JAVA零基础入门系列」Day3 Java基本数据类型
前两篇已经将开发环境搭建完成,如果你已经按之前的教程按部就班的完成了部署,那么世界上最优秀的编程语言之一和世界上最优秀的IDE之一已经出现在你的电脑上(此处应有掌声),如果你还没入门,或者正在台阶上踱 ...
- [免费专栏] Android安全之检测APK中调试代码是否暴露敏感信息
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...
- [免费专栏] Android安全之静态逆向APK应用浅析「手动注入smali」+「IDA Pro静态分析so文件」+「IDA Pro基础使用讲解」
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...
- [免费专栏] Android安全之Android APP应用程序的汉化功能 (修改so中的字符串内容)
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...
- [免费专栏] Android安全之Android Xposed插件开发,小白都能看得懂的教程
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...
- [免费专栏] Android安全之Android so文件分析「详细版」
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...
- [免费专栏] Android安全之动态调试APP的一些技巧「Android Studio调试」
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...
最新文章
- 《MongoDB管理与开发精要》——3.2节查询语法
- 有关c++中const用法
- 16个不错的 git 别名
- 天天早上慢跑一小时对身体好吗?
- 活着是一种罪过,是上帝对你的另一种眷顾,叫做惩罚!活着痛苦!
- QT5+VC2012配置以及常见错误
- ISO14001环境管理体系认证申报要求有哪些
- Node.js学习笔记--进阶之路
- 金三银四跳槽季,教你这几招提高面试成功率
- spring boot网上眼镜商场毕业设计-附源码241659
- Universal Style Transfer via Feature Transforms (WCT,风格迁移,NIPS2017)
- 新手学习 python 的好工具:PyScripter
- 开启阿里云80端口:如何配置阿里云服务器安全组
- C# XmlDocument处理XML元素节点
- 浅析SaaS软件和传统软件交付模式的区别
- Raft 协议 - Fabric
- 老码识途之对象函数调用
- 阿维塔科技在重庆联合产权交易所挂牌;福伊特助力杨房沟水电站首台机组成功投运 | 能动...
- 系统安全知识之系统安全的最小特权原则
- 吃肉不吃蒜 营养减一半(转)