即前篇文章Android 静态分析 smali,我一直在思考以下几个问题:
1、子类使用的父类中的方法,那么对应的smail是invoke-super还是invoke-virtual呢?对应的类是父类还是子类呢?
解释:invoke-direct {p0}, Landroid/app/Activity;-><init>()V,这里所说的父类还是子类指的是invoke参数后面跟着的类,在这个例子中是Landroid/app/Activity。
2、在抽象的父类中使用抽象的方法,但是这个方法的实现却在子类,那么对应的smali的类是父类还是子类呢?
3、生成子类的对象后,通过这个对象去调用父类的方法,那么对应的smali的类是父类还是子类呢?
4、如果子类向上转型为父类,再调用被子类覆盖的方法时,那么对应的smali的类是父类还是子类呢?
5、如果子类向上转型为父类,并且父类是接口,再调用被子类覆盖的接口中的方法时,那么对应的smali的类是父类还是子类呢?
带着这些疑问,我们先建一个Android工程。
MainActivity.java
package com.example.crackdemo;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//生成子类对象(子类public构造函数),可以使用子类public方法和父类未被覆盖的public方法。
SubClass subClass = new SubClass(this);
subClass.doSomething();
subClass.doSuperSomething(this);
//如果向上转型生成父类对象,只能调用父类的public方法如果子类复写了该方法,那么调用子类的对应方法。
SuperClass subClass1 = new SubClass(this);
subClass1.doSubSomething();
//如果向下转型,可以使用子类public方法和父类未被覆盖的public方法。
//如果被覆盖了,则调用子类的对应方法
SubClass subClass2 = (SubClass) subClass1;
subClass2.doSubSomething();
//如果向上转型生成父类对象,只能调用父类的public方法如果子类复写了该方法,那么调用子类的对应方法。
SuperInterface superInterface = new SubClass(this);
superInterface.doInterfaceSomething();
}
}
SubClass.java
package com.example.crackdemo;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
public class SubClass extends SuperClass implements SuperInterface {
private Context mContext;
public SubClass(Context context) {
mContext = context;
}
@Override
public void doSubSomething() {
Toast.makeText(mContext, "doSubSomething", Toast.LENGTH_LONG).show();
}
public void doSomething() {
doSuperSomething(mContext);
doOwnSomething();
}
private void doOwnSomething() {
Toast.makeText(mContext, "doOwnSomething", Toast.LENGTH_LONG).show();
}
@Override
public void doInterfaceSomething() {
Log.d("jltxgcy", "doInterfaceSomething");
}
}
SuperClass.java
package com.example.crackdemo;
import android.content.Context;
import android.widget.Toast;
public abstract class SuperClass {
public abstract void doSubSomething();
public void doSuperSomething(Context context){
doSubSomething();
Toast.makeText(context, "doSuperSomething", Toast.LENGTH_LONG).show();
}
}
SuperInterface.java
package com.example.crackdemo;
public interface SuperInterface {
public void doInterfaceSomething();
}
反编译,生成的smail:
MainActivity.smali
.class public Lcom/example/crackdemo/MainActivity;
.super Landroid/app/Activity;
# direct methods
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Landroid/app/Activity;-><init>()V
return-void
.end method
# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
.locals 1
invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
const/high16 v0, 0x7f030000
invoke-virtual {p0, v0}, Lcom/example/crackdemo/MainActivity;->setContentView(I)V
new-instance v0, Lcom/example/crackdemo/a;
invoke-direct {v0, p0}, Lcom/example/crackdemo/a;-><init>(Landroid/content/Context;)V
invoke-virtual {v0}, Lcom/example/crackdemo/a;->b()V
invoke-virtual {v0, p0}, Lcom/example/crackdemo/a;->a(Landroid/content/Context;)V //调用了父类的doSuperSomething
new-instance v0, Lcom/example/crackdemo/a;
invoke-direct {v0, p0}, Lcom/example/crackdemo/a;-><init>(Landroid/content/Context;)V
invoke-virtual {v0}, Lcom/example/crackdemo/b;->a()V //调用了子类的doSubSomething
check-cast v0, Lcom/example/crackdemo/a;
invoke-virtual {v0}, Lcom/example/crackdemo/a;->a()V //调用了子类的doSubSomething
new-instance v0, Lcom/example/crackdemo/a;
invoke-direct {v0, p0}, Lcom/example/crackdemo/a;-><init>(Landroid/content/Context;)V
invoke-interface {v0}, Lcom/example/crackdemo/c;->c()V //调用了子类的doInterfaceSomething
return-void
.end method
a.smail(SubClass.java)
.class public Lcom/example/crackdemo/a;
.super Lcom/example/crackdemo/b;
# interfaces
.implements Lcom/example/crackdemo/c;
# instance fields
.field private a:Landroid/content/Context;
# direct methods
.method public constructor <init>(Landroid/content/Context;)V
.locals 0
invoke-direct {p0}, Lcom/example/crackdemo/b;-><init>()V
iput-object p1, p0, Lcom/example/crackdemo/a;->a:Landroid/content/Context;
return-void
.end method
.method private d()V //private void doOwnSomething()
.locals 3
iget-object v0, p0, Lcom/example/crackdemo/a;->a:Landroid/content/Context;
const-string v1, "doOwnSomething"
const/4 v2, 0x1
invoke-static {v0, v1, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v0
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
return-void
.end method
# virtual methods
.method public a()V //public void doSubSomething()
.locals 3
iget-object v0, p0, Lcom/example/crackdemo/a;->a:Landroid/content/Context;
const-string v1, "doSubSomething"
const/4 v2, 0x1
invoke-static {v0, v1, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v0
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
return-void
.end method
.method public b()V //public void doSomething()
.locals 1
iget-object v0, p0, Lcom/example/crackdemo/a;->a:Landroid/content/Context;
invoke-virtual {p0, v0}, Lcom/example/crackdemo/a;->a(Landroid/content/Context;)V //调用了父类的doSuperSomething
invoke-direct {p0}, Lcom/example/crackdemo/a;->d()V
return-void
.end method
.method public c()V //public void doInterfaceSomething()
.locals 2
const-string v0, "jltxgcy"
const-string v1, "doInterfaceSomething"
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
return-void
.end method
b.smali(SuperClass.java)
.class public abstract Lcom/example/crackdemo/b;
.super Ljava/lang/Object;
# direct methods
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
# virtual methods
.method public abstract a()V
.end method
.method public a(Landroid/content/Context;)V
.locals 2
invoke-virtual {p0}, Lcom/example/crackdemo/b;->a()V //调用了子类的doSubSomething
const-string v0, "doSuperSomething"
const/4 v1, 0x1
invoke-static {p1, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v0
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
return-void
.end method
c.smali(SuperInterface.java)
.class public interface abstract Lcom/example/crackdemo/c;
.super Ljava/lang/Object;
# virtual methods
.method public abstract c()V
.end method
回答第一个问题:
在a.smail中的方法b,对应SubClass类中doSomething中调用了父类的doSuperSomething,如下:
invoke-virtual {p0, v0}, Lcom/example/crackdemo/a;->a(Landroid/content/Context;)V ,我们看到这里使用的是invoke-virtual,并且->前面使用的是子类,但是->后面的方法a(Landroid/content/Context;)V,却是父类的方法。
如果在java中是这样写的:
public void doSomething() {
super.doSuperSomething(mContext);
doOwnSomething();
}
那么对应的smail是这样的, invoke-super{p0, v0}, Lcom/example/crackdemo/b;->a(Landroid/content/Context;)V,此时使用的invoke-super,并且->前面使用的是父类。
回答第二个问题:
在b.smali的方法a,对应SuperClass类中doSuperSomething中调用了doSubSomething,如下:
invoke-virtual {p0}, Lcom/example/crackdemo/b;->a()V,我们看到这里使用的是invoke-virtual,此时->前面Lcom/example/crackdemo/b使用的是父类,但->后面a()V实际调用的却是子类中方法。
回答第三个问题:
SubClass subClass = new SubClass(this);
subClass.doSomething();
subClass.doSuperSomething(this);
doSuperSomething对应的smali如下,在MainActivity.smali的onCreate方法:
invoke-virtual {v0, p0}, Lcom/example/crackdemo/a;->a(Landroid/content/Context;)V
我们可以看到->前面Lcom/example/crackdemo/a使用的是子类,但->后面a(Landroid/content/Context;)V实际调用的是父类中的方法。
回答第四个问题:
//如果向上转型生成父类对象,只能调用父类的public方法如果子类复写了该方法,那么调用子类的对应方法。
SuperClass subClass1 = new SubClass(this);
subClass1.doSubSomething();
doSubSomething 对应的smali如下,在MainActivity.smali的onCreate方法:
invoke-virtual {v0}, Lcom/example/crackdemo/b;->a()V
我们可以看到->前面Lcom/example/crackdemo/b使用的是父类的b,但 ->后面a()V实际上调用的是子类中的方法。
回答第五个问题:
//如果向上转型生成父类对象,只能调用父类的public方法如果子类复写了该方法,那么调用子类的对应方法。
SuperInterface superInterface = new SubClass(this);
superInterface.doInterfaceSomething();
doInterfaceSomething 对应的smali如下,在MainActivity.smali的onCreate方法:
invoke-interface {v0}, Lcom/example/crackdemo/c;->c()V //调用了子类的doInterfaceSomething
我们可以看到->前面 Lcom/example/crackdemo/c 使用的是父类接口c,但 ->后面c()V 实际上调用的是子类中的方法。