Android进阶解密③—Hook

源码的执行是按照一定流程思路进行的,hook就是在源码的执行流程之间插入一步操作,起到拦截,替换的作用;被改变的对象称为hook点,一般将不易发生变化的类作为hook点;

常见的hook点有:
  • 静态变量
  • 单例

代理模式:

学习hook必须了解代理模式,可以参考我这篇文章:反射和动态代理

Hook startActivity

首先需要知道startactivity的流程:Android进阶解密①——activity的启动过程

我们知道startActivity会通过mInstrumentation这个类,我们可以将这个类作为hook点;

创建一个自定义的Instrumentation:

public class InstrumentationProxy extends Instrumentation {
    Instrumentation instrumentation;

    public InstrumentationProxy(Instrumentation instrumentation) {
        this.instrumentation = instrumentation;
    }

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

        // TODO:    hook 操作

        try {
            Method methodExecStartActivity = Instrumentation
                    .class.getDeclaredMethod("execStartActivity",
                    Context.class,IBinder.class,
                    IBinder.class,Activity.class,
                    Intent.class,int.class,Bundle.class);
            return (ActivityResult) methodExecStartActivity.invoke(instrumentation,who,contextThread,token,target,intent,requestCode,options);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

自定义一个Instrumentation,在activity的工作过程中通过反射替换原来的Instrumentation,将原来的Instrumentation传到代理类里面,通过method invoke保证原来的功能不变,然后可以添加自己的自定义操作;

替换原来流程的Instrumentation:

    private void replaceInstrumentation(Activity activity) throws NoSuchFieldException, IllegalAccessException {
        Field field = activity.getClass().getDeclaredField("mInstrumentation");
        field.setAccessible(true);
        Instrumentation instrumentation = (Instrumentation) field.get(activity);
        Instrumentation instrumentationProxy = new InstrumentationProxy(instrumentation);
        field.set(activity,instrumentationProxy);
    }

首先拿到activity原来的Instrumentation对象,通过原来的Instrumentation构建出一个InstrumentationProxy对象,将Proxy设置给activity,然后只要在startActivity()之前调用这个方法替换就可以了;