Hook技术(一)对startActivity进行简单Hook

目的

这篇文章是让大家体会hook技术大概是什么样子,在那些地方可以应用到。后面文章将深入hook技术,以及解析市面上现有的hook开源框架原理。

在前面的文章中我们已经说过什么是代理模式,那我们就先使用最简单的静态代理模式应用在我们的hook技术中。我们先复习一下静态代理模式

/** 
 * 定义Demo接口 
 */  
public interface Demo {    
    public void save();  
}
/** 
 * DemoImpl实现Demo接口并覆写save()方法 
 * 真实主题,执行具体业务 
 */  
public class DemoImpl implements Demo {    
    public void save() {  
        System.out.println("调用save()方法");  
    }  
}  
/** 
 * DemoImplProxy 也实现了Demo接口,并覆写了save()方法,增加了自己的业务  
 * 代理主题,负责其他业务的处理 
 */  
public class DemoImplProxy implements Demo {    
    Demo demoImpl = new DemoImpl();  

    public void save() {  
        System.out.println("开始记录日志");  
        demoImpl.save();  
        System.out.println("开始结束日志");  
    }  
}  

我们看出来,代理是对原有功能的拓展,而且很多时候我们在开发的过程中并不能改变我们原有的功能,比如系统打电话,startActivity等等。这时候我们就想,如果我们对他们进行拓展,让他们添加我们自己的一些元素,是不是就可以达到意想不到的效果。所以我们就引出了Hook的概念。其实Hook就是干偷梁换柱之事。

项目代码

++分析在下面模块++

我先把项目写上去,然后我们在分析,这样我觉得比先一大堆理论要好。我们目的很简单就是让我们在startActivity的时候我们拓展一下我们的功能(打印一些信息)

首先实现自己的Application子类,原因是这里执行比较早

public class APP extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        setEvilInstrumentation();
    }

    public void setEvilInstrumentation() {
        try {
            //先获取到当前的ActivityThread对象
            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");//得到这个方法
            currentActivityThreadMethod.setAccessible(true);
            Object currentActivityThread = currentActivityThreadMethod.invoke(null);//此时得到的就是ActivityThread(因为在attach的时候调用赋值)

            Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");//得到ActvitiyThread中的mInstrumentation字段
            mInstrumentationField.setAccessible(true);
            Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);

            //创建代理对象
            EvilInstrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation,APP.this);
            mInstrumentationField.set(currentActivityThread, evilInstrumentation);//将系统中mInstrumentation换成自己的EvilInstrumentation
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我们要替换的类EvilInstrumentation

public class EvilInstrumentation extends Instrumentation {

    private static final String TAG = "EvilInstrumentation";
    // ActivityThread中原始的对象, 保存起来
    Instrumentation mBase;
    private Context mContext = null;

    public EvilInstrumentation(Instrumentation base) {
        mBase = base;
    }

    public EvilInstrumentation(Instrumentation mInstrumentation, APP app) {
        this(mInstrumentation);
        mContext = app.getApplicationContext();
    }

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        Toast.makeText(mContext, "\n执行了startActivity, 参数如下: \n" + "who = [" + who + "], " +
                "\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +
                "\ntarget = [" + target + "], \nintent = [" + intent +
                "], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]", Toast.LENGTH_LONG).show();

        // 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法
        try {
            Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Activity.class,
                    Intent.class, int.class, Bundle.class);
            execStartActivity.setAccessible(true);
            return (ActivityResult) execStartActivity.invoke(mBase, who,
                    contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            throw new RuntimeException("需要重新适配了,framework层代码被谷歌改了");
        }
    }
}

MainActivity

public class MainActivity extends AppCompatActivity {
    private Button btn ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn = findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this,Activity2.class));
            }
        });
    }
}
public class Activity2 extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_2);
    }
}

代码分析

原本如果我们要处理一件事物,我们就需要如下使用

graph LR
A-->B.Method

A直接调用B的方法,当我们Hook的时候就需要多一层调用。

image

将B完全封装到C中,A直接和C进行通信即可,在我们项目中如何体现呢?

我们项目中,就是通过Hook将原本Android framework中startActivity过程的一个处理方法拦截下来,从而执行我们的方法。具体做法如下:

这个就是我们的核心方法

public void setEvilInstrumentation() {
    try {
        //先获取到当前的ActivityThread对象
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");//得到这个方法
        currentActivityThreadMethod.setAccessible(true);
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);//此时得到的就是ActivityThread(因为在attach的时候调用赋值)

        Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");//得到ActvitiyThread中的mInstrumentation字段
        mInstrumentationField.setAccessible(true);
        Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);

        //创建代理对象
        EvilInstrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation,APP.this);
        mInstrumentationField.set(currentActivityThread, evilInstrumentation);//将系统中mInstrumentation换成自己的EvilInstrumentation
    } catch (Exception e) {
        e.printStackTrace();
    }
}

我们首先要熟悉startActivity的大概流程,要知道从哪里拦截比较好,这样我们心中就有一个目的,从而通过反射达到那个目的。

小结过程

  • 获取ActivityThread类的Class(这样就能得到其中所有的方法和变量)
  • 获取ActivityThread的currentActivityThread方法
  • 执行currentActivityThread方法就会返回ActivityThread实例
  • 通过ActivityThread实例我们拿到mInstrumentation成员变量
  • 通过mInstrumentation成员变量我们就可以对这个变量进行代理,因为他是启动Activity的核心
  • 将mInstrumentation对象包装到EvilInstrumentation实例中
  • 将我们的系统中启动Activity的核心类mInstrumentation替换成EvilInstrumentation
  • 然后startActivity就会执行我们代理类的逻辑也就是(execStartActivity)方法中的内容

但是有坑,有的委托类不让继承,我们就没办法控制其他类调用,怎么办?

  • 第一种办法就是将Instrumentation类中所有方法进行代理(好处就是对于每个方法有不同的处理逻辑)
  • 使用动态代理(不管执行什么方法都会执行我们动态代理里面写的逻辑)

下一节,我们继续拓展Hook技术

猜你喜欢

转载自blog.csdn.net/wangwei708846696/article/details/79525457