smali语言还是相对纯机器码来说,比较容易理解的
--但最好是结合工具一起学--
--还可以参考其他的整理文章--
先来一个最简单的模型类
simpeModel.java
对应的smali
simpleModel.smali
(表格只是简单做一个映射关系的整理)
java | smali | description |
---|---|---|
package | .class | 包名 |
extends | .super | 父类名 |
class | .source | 类名 |
params | .field | 定义字段 |
int | I | int类型 |
Integer | Ljava/lang/Integer | Integer类型 |
引用类型 | Lxx/xx/xx | 该引用类型的完整包名 |
boolean | Z | 布尔值 |
byte | B | 字节 |
short | S | 短整型 |
char | C | 字符 |
long | J | 长整型 |
float | F | 浮点数 |
double | D | 浮点数 |
void | V | 无 |
数组类型 | [ | 数组 |
function | .method | 定义方法 |
constructor | 构造器方法 | |
.prologue | 开始了 | |
.registers | 申请寄存器的个数,貌似不用关心这个 | |
.line x | 对应源java文件行数 |
func1括号里有Lxx/xx/xx说明有形参,形参类型是String,也有返回值,返回值类型也是String
.param 指名 形参名 value
.prologue以下开始正式的代码逻辑
.line 38 ,源码38行的代码
return-object 返回该对象的引用,其实就是把p0里的值返回来。
func2无形参,返回值类型String
const-string 声明了一个String 类型的变量 ,值是 "abcdefg"
.local 说是 指定了使用的局部变量的个数,我这里简单的认为,就是把v0给了一个局部变量 func2string
没想到这么长。
这里有点绕,根据
跳转指令 if-else跳转指令
当vB为0的时候
image.png
则直接翻译smali为
L93-int类型形参num,放到寄存器p1
L96~L97-源码47行,如果p1的值小于等于0,跳到cond_3
L99~L102-源码49行,结束local寄存器p1,跳到goto_2,
返回p1的值
L104~L109-源码48行,重开寄存器p1,cond_3 入口
如果p1的值 不等于0,跳到cond_7
否则(也就是p1等于0)把 0x0这个值给p1,然后跳到goto_2
L113~L115 -也就是源码49行,cond_7入口,
neg-int对第一个p1进行求补,然后把值给第二个p1
L117~L118 -goto_2入口,end_method
是不是感觉和java代码func3看起来不一样?
我给自己的解释是类似java内存模型发生了重排序的优化,也不知道对不对,有大神知道的话评论区求解答。
不过回过头来再看这个smali,
顺序上虽然和源码顺序并不一致,但是按照直接翻译的意思去写java代码
if( p1 <= 0){
if(p1 != 0)return -p1
else return 0
} else return p1
结果是一样的。
再来一个
image.png image.png
有了func3的翻译经验
func4就很好翻译了
if-eqz p1,也就是形参num p1等于0的话,直接到cond_3,赋值0,最后返回。
其余的直接返回p1
if(p1 == 0)return 0
else return p1
这个优化感觉更能说服自己了,==操作比!=操作更简单
来一个switch跳转
switch
太长了
L145~L146-源码59行,拿p1做switch(packed-switch关键字)偏移区pswitch_data_a
要看到底部
L174~L179-实际上只有两个case需要做判断 pswitch-6和pswitch-8(也就是两个偏移区)
L156~L163-pswitch_6,把0x0给v0,跳到goto_5
L165~L172-pswitch_8,把0x1给v0,跳到goto_5
L151~L154-goto_5入口把int型的本地(.local关键字)参数result指向寄存器v0,返回v0
其余的 把0x29a给v0
int result;
switch(p1){
case pswitch_6:
result = 0;
break;
case pswitch_8:
result = 1;
break;
default:
result = 0x29a;
break;
}
return result;
相当于帮我优化了case -1,因为case -1和default都是同样的处理方法
常用的加log打印信息的方法
image.png System.out.println("sout"); Log.d("log", func2())
那加入在有些情况下,调用Log.d,该类并没有引入
android.util.Log包怎么办?
通过完整的包名调用 我发誓这两种写法,smali编译出来是一样的
放一个四哥的自定义代码添加方案
以上笔记,仅供参考。
网友评论