天天看点

Hook 系统源码探究

众所周知,一个Activity要想启动,必须在AndroidManifest里面注册,否则会在跳转到当前Activity的时候崩溃并报错:have you declared this activity in your AndroidManifest.xml?

而我们要做的插件化就是要跳过注册这一步,同时保证当前Activity能够正常加载。如何做到这一步呢?

下面我们基于API28去查看源码:

 当我们调用startActivity方法时,ActivityManagerService(AMS)会去检测,当前要启动的Activity是否注册了

Hook 系统源码探究
Hook 系统源码探究

在Instrumentation.java中搜索execStartActivity方法,在方法里有一行关键代码:ActivityManager.getService()

int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
           

ActivityManager.getService()(旧的挨批使用ActivityManager.getDefault() )返回的是IActivityManager,这是AIDL里的一个接口,这里会调用到AMS去检测当前activity是否注册,如果没注册就会崩溃,所以我们要在调用AMS之前去hook住.

public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }
           

返回的是单例,调用了get方法。

private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };
           

可以进入Singleton这个类里面看到具体的get方法。

public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}
           

这里迭代get里面调用了一个抽象的create方法,而这个create方法在上面的 IActivityManagerSingleton中被实现了。

在create方法中,通过IBinder 获取到了当前activity并返回了IActivityManager,这样这种抽象方法中的mInstance就被赋值为IActivityManager了。我们就需要把mInstance替换掉(动态代理)。

思路有了,下面我们通过代码去实现它:

1.创建一个application:HookApplication 基础系统的Application。

2.在oncreate方法中加入一个方法hookAmsAction,目的是:要在执行 AMS之前,替换可用的 Activity,替换在AndroidManifest里面配置的Activity

动态代理:由于执行startActivity之前,我们需要先执行我们的代码(把未注册的TestActivity 替换成 已经注册的 Activity)

3.ASM检查过后,要把这个代理Activity换回来 --> TestActivity

整体流程如下:

startActivity ---> TestActivity -- (Hook ProxyActivity)(AMS)检测,当前要启动的Activity是否注册了)ok ---》 ActivityThread(即将加载启动Activity)----(要把这个ProxyActivity 换回来 --> TestActivity)