Hook机制学习(四) - Activity生命周期管理

weishu

一 :需要对Activity生命周期进行管理的原因及解决方案

原因: 在Android平台上使用ClassLoader把插件的Activity,Service加载进来时,加载进来的Activity和Service是普通的类,是没有生命周期的,因为Activity,Service等组件的生命周期是由AMS管理的。所以动态加载Activity首先要解决的一个问题就是对Activity生命周期进行管理。
解决方案: 使用代理的思想,要启动一个插件Activity首先启动一个代理的StubActivity,StubActivity注册在Host程序中。再在合适的时机把StubActivity替换成插件Activity,以此来突破必须在注册Activity的限制AndroidManifest限制。

二: Activity启动流程

具体流程可以去看源码或者阅读《Android艺术探索这本书》
简化总结如下

Hook机制学习(四) - Activity生命周期管理_第1张图片
Activity启动流程

三:Hook点选择及Hook方法

选择Hook点
**1 把TargetActivity替换成StubActivity (Hook掉ActivityManagerProxy)
**选择在启动流程进入到system_server进程之前,Hook掉ActivityManagerNative.getDefault()的返回值(ActivityManagerProxy)。这样当Activity的流程走到ActivityManagerNative.getDefault().startActivity时,方法会进入到InvocationHandler的invoke内部,在这个方法内部选择替换掉ActivityManagerProxy.startActivity()的Intent参数,使得Intent显示启动目标Activity为StubActivity,因为StubActivity在AndroidManifest文件中注册过,所以能通过AMS的真实性校验。

Hook startActivity()方法,选择替换掉Intent参数

if ("startActivity".equals(method.getName())) {
    // 只拦截这个方法
    // 替换参数, 任你所为;甚至替换原始Activity启动别的Activity偷梁换柱
    // API 23:
    // public final Activity startActivityNow(Activity parent, String id,
    // Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
    // Activity.NonConfigurationInstances lastNonConfigurationInstances) {

    // 找到参数里面的第一个Intent 对象

    Intent raw;
    int index = 0;

    for (int i = 0; i < args.length; i++) {
        if (args[i] instanceof Intent) {
            index = i;
            break;
        }
    }
    raw = (Intent) args[index];

    Intent newIntent = new Intent();

    // 这里包名直接写死,如果再插件里,不同的插件有不同的包  传递插件的包名即可
    String targetPackage = "com.weishu.intercept_activity.app";

    // 这里我们把启动的Activity临时替换为 StubActivity
    ComponentName componentName = new ComponentName(targetPackage, StubActivity.class.getCanonicalName());
    newIntent.setComponent(componentName);

    // 把我们原始要启动的TargetActivity先存起来
    newIntent.putExtra(HookHelper.EXTRA_TARGET_INTENT, raw);

    // 替换掉Intent, 达到欺骗AMS的目的
    args[index] = newIntent;

    Log.d(TAG, "hook success");
    return method.invoke(mBase, args);

}

return method.invoke(mBase, args);

2 把StubActivity换回TargetActivity(Hook掉Handler.callback)
如果对于Activity的启动流程比较熟悉,可以发现当启动流程由AMS进程转到App进程时,通过ApplicationThread进行Binder IPC,这个时候ApplicationThread里面的ScheduleLaunchActivity会被调用,且运行在App进程的Binder线程池,这个方法通过H Handler发送msg转发到ActivityThread里面的handleLaunchActivity。
仔细观察Handler的dispatchMessage方法可以发现 msg.callback的优先级大于mCallback大于handleMessage. 因为msg.callback我们没办法预知,所以可以选择Hook掉mCallback,当msg.what=LAUNCH_ACTIVITY时,选择将Intent的显示启动目标为TargetActivity,然后就会顺利的创建出TargetActivity。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

Hook掉CallBack

/* package */ class ActivityThreadHandlerCallback implements Handler.Callback {

    Handler mBase;

    public ActivityThreadHandlerCallback(Handler base) {
        mBase = base;
    }

    @Override
    public boolean handleMessage(Message msg) {

        switch (msg.what) {
            // ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100
            // 本来使用反射的方式获取最好, 这里为了简便直接使用硬编码
            case 100:
                handleLaunchActivity(msg);
                break;
        }

        mBase.handleMessage(msg);
        return true;
    }

    private void handleLaunchActivity(Message msg) {
        // 这里简单起见,直接取出TargetActivity;

        Object obj = msg.obj;
        // 根据源码:
        // 这个对象是 ActivityClientRecord 类型
        // 我们修改它的intent字段为我们原来保存的即可.
/*        switch (msg.what) {
/             case LAUNCH_ACTIVITY: {
/                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
/                 final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
/
/                 r.packageInfo = getPackageInfoNoCheck(
/                         r.activityInfo.applicationInfo, r.compatInfo);
/                 handleLaunchActivity(r, null);
*/

        try {
            // 把替身恢复成真身
            Field intent = obj.getClass().getDeclaredField("intent");
            intent.setAccessible(true);
            Intent raw = (Intent) intent.get(obj);

            Intent target = raw.getParcelableExtra(HookHelper.EXTRA_TARGET_INTENT);
            raw.setComponent(target.getComponent());

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(Hook机制学习(四) - Activity生命周期管理)