以下内容不保证正确,仅供参考
最近在完成移动智能终端安全的课程实验时研究了一下smali的语法,顺便记录一下几个比较具有迷惑性的地方
比如以下两行代码:
invoke-virtual {p0, v0}, Lcom/example/smali/MainActivity;->setContentView(I)V
iput-object v0, p0, Lcom/example/smali/MainActivity;->tv:Landroid/widget/TextView;
在这两行代码中,寄存器v0和p0究竟是拿来干什么的?
我的研究方法是:先写一段java源代码,把它反编译成smali文件,研究两者对应的关系,如果有发现结果,还可以适当的更改源代码以验证自己的结论。这样的优点是结论是自己发现的,印象会更深刻;缺点是频繁的反编译比较繁琐,要是在AS上有即时的反编译插件就好了。
话不多说,源代码如下,一个按钮注册点击事件,点击后改变TextView内容的demo:
public class MainActivity extends AppCompatActivity {
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.tv);
Button btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tv.setText("Hello Xidian!");
}
});
}
对应的smali代码如下:(MarkDown不支持Smali,所以笔者尽量优化了一下阅读体验)
.class public Lcom/example/smali/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"
.field tv:Landroid/widget/TextView;
.method public constructor <init>()V #MainActivity的构造器
.locals 0
invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()V
return-void
.end method
.method protected onCreate(Landroid/os/Bundle;)V #重写的onCreate方法
.locals 2
.param p1, "savedInstanceState"
invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
const v0, 0x7f09001c
invoke-virtual {p0, v0}, Lcom/example/smali/MainActivity;->setContentView(I)V #setContentView()
const v0, 0x7f07008e
invoke-virtual {p0, v0}, Lcom/example/smali/MainActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/TextView; #强制类型转换
iput-object v0, p0, Lcom/example/smali/MainActivity;->tv:Landroid/widget/TextView; #tv的findViewById
const v0, 0x7f070022
invoke-virtual {p0, v0}, Lcom/example/smali/MainActivity;->findViewById(I)Landroid/view/View;
move-result-object v0
check-cast v0, Landroid/widget/Button; #btn的findViewById
.local v0, "btn":Landroid/widget/Button;
new-instance v1, Lcom/example/smali/MainActivity$1; #new了一个OnClickListener,它是一个匿名内部类
invoke-direct {v1, p0}, Lcom/example/smali/MainActivity$1;-><init>(Lcom/example/smali/MainActivity;)V
invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
return-void
.end method
比对后得出的几个结论就直接写了,不一定正确
1.类和方法中的变量
- 方法内的变量寄存器有两种
- 寄存参数使用p寄存器,形如p0,p1,用
.param
定义,且从p1开始计数,因为p0为非静态方法自动创建的寄存器,存储着this,也就是该方法所属类的实例本身 - 寄存方法中的内部变量使用v寄存器,形如v0,v1,用
.locals
表示所需v寄存器数量
- 寄存参数使用p寄存器,形如p0,p1,用
- 至于类中的域则用
.field
表示
2.方法调用指令的区别与后置寄存器的作用
- invoke指令最常用的为
invoke-virtual
,invoke-direct
和invoke-static
,invoke-super
,invoke-virtual
表示android原生api,即不是你写的方法invoke-direct
表示因你而产生的方法,包含你定义的类的构造器invoke-super
和invoke-static
比上述两者具有更高优先级,当属于静态或父类方法时,优先使用invoke-static
或invoke-static
而非invoke-static
和invoke-static
- 在
invoke-virtual {v0, p0~pn}...
中,v0存有该方法所属的类,p0至pn表示该方法需要的传参,…表示v0所存类与该方法的联系
3.变量操作指令的区别与后置寄存器的作用
- s开头的指令表示对静态变量的指令符,普通的指令符则用i开头
- 如果
iget-object v0, p0,...
的源代码为a = b
,则v0存有b,p0存有a所在的类的实例,…为p0到a的联系
4.内部类的特殊格式
-
如果B为A的内部类,则B在Smali中的表示为一个单独的类文件,并且
- 在A中B的命名方式为
A$B
,如果B为匿名内部类,比如OnClickListener,则B的命名方式为A$1
- 在B中会有一个类型为A的域,A的命名方式为
.field final synthetic this$0
,并且在B的构造器中需传入一个A的实例,JVM用这种方法保证内部类对外部类的持有
- 在A中B的命名方式为
-
在B中调用A的外部方法,挑用指令统一采用
invoke-static
,方法名会采用acces$000
,acces$100
…之类的命名形式,并且方法内的参数会默认添加一个外部类