美文网首页插件等 移动 前端 Python Android Java
Hook机制学习(四) - Activity生命周期管理

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

作者: shuixingge | 来源:发表于2016-10-18 19:26 被阅读57次

    weishu

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

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

    二: Activity启动流程

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

    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生命周期管理

        本文链接:https://www.haomeiwen.com/subject/lyjtuttx.html