原始类型
-
V
void
(只能用于返回值类型) -
Z
boolean
-
B
byte
-
[B
byte array
-
S
short
-
C
char
-
I
int
-
J
long
-
F
float
-
D
Double
寄存器与变量
java
中变量都是存放在内存中的,android
为了提高性能,变量都是存放在寄存器中的,寄存器为32
位,可以支持任何类型,其中/Long
和double
是64
位的,需要使用两个寄存器保存。
寄存器采用v
和p
来命名
v
表示本地寄存器
p
表示参数寄存器,关系如下
如果一个方法有两个本地变量,有三个参数
v0
第一个本地寄存器
v1
第二个本地寄存器
v2
p0
(this)
v3
p1
第一个参数
v4
p2
第二个参数
v5
p3
第三个参数
当然,如果是静态方法的话就只有5
个寄存器了,不需要存this
了。
.registers
指定方法中寄存器的总数
.locals
指定表明方法中不是参数寄存器的总数,放在方法的第一行。
名词解释
- 寄存器
. 点开头
.local
A
add-int/lit8
用法:
const/16 v30, 0x0
invoke-virtual/range {v17 .. v17}, Ljava/lang/StringBuffer;->length()I
move-result v31
add-int/lit8 v31, v31, -0x1
invoke-virtual/range {v29 .. v31}, Ljava/lang/String;->substring(II)Ljava/lang/String;
move-result-object v16
定义v31 = 0
v17是某个字符串变量的长度,最终赋给v31
v31将自身-1,再赋给v31,像v31 = v31 - 1 或 v31 = v31 --
v29是一个字符串
截取v29的字符串,最终赋给v16,像这样 v16 = v29.substring(0, v17字符串长度 -1)
aput-object
aput-object v4, v2, v3
将v4
加入v2
的数组中,索引是v3
S
sput-object
将一个对象引用值赋给一个变量
用法:
const-string/jumbo v0, "bec58193ad7a9f2bc143acdb060ecec6"
sput-object v0, Lcom/wangzhi/mallLib/MaMaHelp/base/utils/HttpRequest;->myKey:Ljava/lang/String;
myKey是类的静态变量,一般在构造函数前定义,像这样:
.field public static myKey:Ljava/lang/String;
上面语句最终结果是给myKey静态变量赋值
基本语法
.field private isFlag:z
定义变量
.method
方法
.parameter
方法参数
.prologue
方法开始
.line 12
此方法位于第12行
invoke-super
调用父函数
const/high16
v0, 0x7fo3 把0x7fo3赋值给v0
const/4
表示4bit数字,最大15
用法:
const/4 vA, 0xB
const/16
定义一个16位的常量
用法:
const/16 vAA, 0xBBBB
0xBBBB是16进制数字,表示10进制的48059,0x开头的表示16进制
表示16bit数字
const vAA, 0xBBBBBBBB
表示32bit数字
const-string/jumbo
支持的字符串引用数更大,如果dex内字符串数量超过65536,则需要使用 const-string/jumbo
const-string/jumbo vAA, string@BBBBBBBB
invoke-direct
调用函数
invoke-virtual
如果invoke-virtual
出现
invoke-virtual {v0, v1, v2, v3}, Ljavax/crypto/Cipher;->init(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V
这种多了个参数v3
的情况是因为long 和 double 类型都是64位的,必须使用两个寄存器来存储。
new-array
定义数组
new-array v1, v1, [B
array-length vA,vB
获取vB寄存器中数组的长度并赋值给vA寄存器
array-length v2, v1 //获取v1数组长度,赋值给v2
return-void
函数返回void
.end method
函数结束
new-instance
创建实例
iput-object
对象赋值
iget-object
调用对象
check-cast v0, Landroid/widget/Button;
:强制类型转换 mBtn = (Button) findViewById(R.id.btn);
move-result
返回基本数据类型v1
用法:
move-result v1
move-result-object
返回对象
from16
字节码后缀(opcode suffix),标示源(vBBBB)为一个16的寄存器引用变量
//保存到的寄存器,变量所属的实例所在寄存器,某个类某个类型的变量
iget-object v0, p0(this), Lxx/xx/Xx;->memberInstanceVariable:type
iget-object的作用是:iget-object vx,vy,field_id,Reads an object reference instance field into vx. The instance is referenced by vy。翻译过来就是vx = vy.field_id,那么上面这句话的意思就是 v1 = this.b 这个b是javax.crypto.spec.SecretKeySpec的一个对象(可以想想到,在这个类中会有这么一句话SecretKeySpec b;)
invoke-static
调用静态函数
方法和字段
方法签名
methodName(III)Lpackage/name/ObjectName;
如果做过ndk开发的对于这样的签名应该很熟悉的,就是这样来标识一个方法的。
上面methodName
标识方法名,III
表示三个整形参数,Lpackage/name/ObjectName;
表示返回值的类型。
方法
Lpackage/name/ObjectName;——>methodName(III)Z
即 package.name.ObjectName
中的 function boolean methondName(int a, int b, int c)
类似这样子
字段
Lpackage/name/ObjectName;——>FieldName:Ljava/lang/String;
即表示: 包名,字段名和各字段类型
逻辑判断
-
==
if-eq vA, vB, :cond**" 如果vA等于vB则跳转到:cond** -
!=
if-ne vA, vB, :cond**" 如果vA不等于vB则跳转到:cond** -
<
if-lt vA, vB, :cond**" 如果vA小于vB则跳转到:cond** -
>=
if-ge vA, vB, :cond**" 如果vA大于等于vB则跳转到:cond** -
>
if-gt vA, vB, :cond**" 如果vA大于vB则跳转到:cond** -
<=
if-le vA, vB, :cond**" 如果vA小于等于vB则跳转到:cond**
if-eqz
if equels zero 缩写,判断是否等于0
用法:
//如果vA等于0则跳转到:cond_**
if-eqz vA, :cond_**"
-
!= 0
if-nez vA, :cond**" 如果vA不等于0则跳转到:cond** -
< 0
if-ltz vA, :cond**" 如果vA小于0则跳转到:cond** -
>= 0
if-gez vA, :cond**" 如果vA大于等于0则跳转到:cond** -
> 0
if-gtz vA, :cond**" 如果vA大于0则跳转到:cond** -
<= 0
if-lez vA, :cond**" 如果vA小于等于0则跳转到:cond**
运算
aget-byte vx,vy,vz
获取vy byte数组索引为vz的值,交给vx//v6 = v3[v0] aget-byte v6, v3, v0
-
add-int/lit8 vx,vy,lit8
将int vy的值加上lit8,交给vx//v7 = v1 + 0x1 add-int/lit8 v7,v1,0x1
-
ushr-int/lit8 vx,vy,lit8
将int vy无符号右移(>>>)lit8位,交给vx//v7 = v1 >>> 1 add-int/lit8 v7,v1,0x1
-
aget-char vx, vy,vz
获取char vy数组索引为vz的值,交给vx//v6 = v3[v0] aget-char v6, v3, v0
-
aput-char vx,vy,vz
将char vx的值添加到vy数组,索引为vz//v3[v0] = v6 aput-char v6, v3, v0
java 与 smali 源码对比
java
public class smaliTest {
public static void main(String[] args){
System.out.println("hello smali");
}
}
smali
.class LsmaliTest;
.super Ljava/lang/Object;
.source "smaliTest.java"
# direct methods
.method constructor <init>()V
.registers 1
.prologue
.line 3
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method public static main([Ljava/lang/String;)V
.registers 3
.parameter
.prologue
.line 5
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "hello smali"
invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 6
return-void
.end method
requests.get() 获取返回的html内容
res = requests.get(url)
print res.text
move-object/from16
移动对象引用,从vy到vx。vy可以处理64K寄存器地址,vx可以处理256寄存器地址。
用法:
move-object/from16 v0, v26
将v26的对象赋给v0
invoke-static/range
英译中
registers
寄存器
特殊字符
L
结尾的类名
像这样Ljava/lang/StringBuffer
,这里的L表示java/lang/StringBuffer
是类
或接口
[
开头
表示数组
像这样[Ljava.lang.Object;
,表示该对象返回数组
将logcat代码植入smali文件
const-string v5, "iwillseeyou"
invoke-static {v5, v0}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
v5是tag,v0是要打印的string,v5的变量名称要看.locals 5
是多少,如果是4,那只能最多4个缓存器,那v5要改成v4
参考:
http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html