美文网首页
VirtualApk 启动插件Activity的过程

VirtualApk 启动插件Activity的过程

作者: Lilee丶 | 来源:发表于2018-09-25 15:00 被阅读0次
    1. VirtualApk的Activity启动,是通过动态代理hook Activity启动流程,动态替换为插桩的activity,来绕过系统对插件中未注册在manifest的activity的检查。
    2. 在使用VirtualApk时,首先要在Application的attachBaseContext(Context base)方法中调用 PluginManager.getInstance(base).init();通过查看源码,我们可以看到,最终会调用PluginManger的构造方法。

    PluginManager

    • PluginManger中调用了两个方法,createComponentsHandler()创建ComponentsHandler对象 和hookCurrentProcess();
        /**
         * 1.此方法存储App 的 Context 
         * 2.调用了 createComponentsHandler()和hookCurrentProcess()
         * @param context
         */
        protected PluginManager(Context context) {
            if (context instanceof Application) {
                this.mApplication = (Application) context;
                this.mContext = mApplication.getBaseContext();
            } else {
                final Context app = context.getApplicationContext();
                if (app == null) {
                    this.mContext = context;
                    this.mApplication = ActivityThread.currentApplication();
                } else {
                    this.mApplication = (Application) app;
                    this.mContext = mApplication.getBaseContext();
                }
            }
            
            mComponentsHandler = createComponentsHandler();
            hookCurrentProcess();
        }
        
    
    1. createComponentsHandler()
        public ComponentsHandler(PluginManager pluginManager) {
            mPluginManager = pluginManager;
            mContext = pluginManager.getHostContext();
        }
    
    
    1. hookCurrentProcess() 调用了三个hook方法。就是这几个hook起到了动态替换activity来绕过系统检查,启动插件中的activity。
        /**
         * 调用了三个hook方法。初始化hook
         */
        protected void hookCurrentProcess() {
            hookInstrumentationAndHandler();
            hookSystemServices();
            hookDataBindingUtil();
        }
    
    • hookInstrumentationAndHandler()
      • 拦截Instrumentation 和 Handler的CallBack方法。
            protected void hookInstrumentationAndHandler() {
            try {
                ActivityThread activityThread = ActivityThread.currentActivityThread();
                Instrumentation baseInstrumentation = activityThread.getInstrumentation();
    //            if (baseInstrumentation.getClass().getName().contains("lbe")) {
    //                // reject executing in paralell space, for example, lbe.
    //                System.exit(0);
    //            }
        
                final VAInstrumentation instrumentation = createInstrumentation(baseInstrumentation);
                
                Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
                Handler mainHandler = Reflector.with(activityThread).method("getHandler").call();
                Reflector.with(mainHandler).field("mCallback").set(instrumentation);
                this.mInstrumentation = instrumentation;
                Log.d(TAG, "hookInstrumentationAndHandler succeed : " + mInstrumentation);
            } catch (Exception e) {
                Log.w(TAG, e);
            }
        }
    
    • hookSystemServices();
      • hookSystemServices, 动态代理ActivityManager对象,实现在ActivityManagerProxy类中,拦截startService,stopService等方法。
          /**
           * hookSystemServices, but need to compatible with Android O in future.
           */
          protected void hookSystemServices() {
          try {
              Singleton<IActivityManager> defaultSingleton;
      
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                  defaultSingleton = Reflector.on(ActivityManager.class).field("IActivityManagerSingleton").get();
              } else {
                  defaultSingleton = Reflector.on(ActivityManagerNative.class).field("gDefault").get();
              }
              IActivityManager origin = defaultSingleton.get();
              IActivityManager activityManagerProxy = (IActivityManager) Proxy.newProxyInstance(
                      mContext.getClassLoader()
                      , new Class[]{IActivityManager.class}
                      , createActivityManagerProxy(origin));
      
              // Hook IActivityManager from ActivityManagerNative
              Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy);
      
              if (defaultSingleton.get() == activityManagerProxy) {
                  this.mActivityManager = activityManagerProxy;
                  Log.d(TAG, "hookSystemServices succeed : " + mActivityManager);
              }
          } catch (Exception e) {
              Log.w(TAG, e);
          }
      }
      
    • hookDataBindingUtil();
        protected void hookDataBindingUtil() {
            Reflector.QuietReflector reflector = Reflector.QuietReflector.on("android.databinding.DataBindingUtil").field("sMapper");
            Object old = reflector.get();
            if (old != null) {
                try {
                    Callback callback = Reflector.on("android.databinding.DataBinderMapperProxy").constructor().newInstance();
                    reflector.set(callback);
                    addCallback(callback);
                    Log.d(TAG, "hookDataBindingUtil succeed : " + callback);
                } catch (Reflector.ReflectedException e) {
                    Log.w(TAG, e);
                }
            }
        }
    

    VAInstrumentation

    frameWork通过Instrumentation来创建启动Activity,这个动态代理类的作用就是在启动的时候,拦截特定的方法,启动插件中的activity。

    • injectIntent(Intent intent)方法
        protected void injectIntent(Intent intent) {
            //将intent由隐式转换为显示。
            mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);
            // null component is an implicitly intent
            if (intent.getComponent() != null) {
                Log.i(TAG, String.format("execStartActivity[%s : %s]", intent.getComponent().getPackageName(), intent.getComponent().getClassName()));
                // resolve intent with Stub Activity if needed
                this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
            }
        }
    
    • this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent); 如果是插件,将信息存储在Intent中。
        public void markIntentIfNeeded(Intent intent) {
            if (intent.getComponent() == null) {
                return;
            }
    
            String targetPackageName = intent.getComponent().getPackageName();
            String targetClassName = intent.getComponent().getClassName();
            // search map and return specific launchmode stub activity
            if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null) {
                intent.putExtra(Constants.KEY_IS_PLUGIN, true);
                intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName);
                intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName);
                dispatchStubActivity(intent);
            }
        }
    
    • 分发宿主中插桩的Activity,并根据插件中Activity的launchMode和Theme,来选取合适的StubActivity,并将主题等信息设置到stubActivity中。
        private void dispatchStubActivity(Intent intent) {
            ComponentName component = intent.getComponent();
            String targetClassName = intent.getComponent().getClassName();
            LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent);
            ActivityInfo info = loadedPlugin.getActivityInfo(component);
            if (info == null) {
                throw new RuntimeException("can not find " + component);
            }
            
            int launchMode = info.launchMode;
            Resources.Theme themeObj = loadedPlugin.getResources().newTheme();
            themeObj.applyStyle(info.theme, true);
            String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
            Log.i(TAG, String.format("dispatchStubActivity,[%s -> %s]", targetClassName, stubActivity));
            intent.setClassName(mContext, stubActivity);
        }
    
    • newActivity()用于创建一个Activity
      • public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {...}
      • 先用当前的classloader去加载Activity,如果catch到ClassNotFoundException,则说明宿主Apk中不包含当前要启动的这个class,这时就用插件中的classLoader去加载这个class。
      • 用插件的classloader去加载时,如果根据刚才injectIntent方法中的component找不到plugin且是debug状态,则抛出异常ActivityNotFoundException("error intent: " + intent.toURI());如果不是debug,则启动StubActivity。
      • 最后将activity加入到list集合中。
        @Override
        public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            try {
                cl.loadClass(className);
                Log.i(TAG, String.format("newActivity[%s]", className));
                
            } catch (ClassNotFoundException e) {
                ComponentName component = PluginUtil.getComponent(intent);
                
                if (component == null) {
                    return newActivity(mBase.newActivity(cl, className, intent));
                }
        
                String targetClassName = component.getClassName();
                Log.i(TAG, String.format("newActivity[%s : %s/%s]", className, component.getPackageName(), targetClassName));
        
                LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component);
        
                if (plugin == null) {
                    // Not found then goto stub activity.
                    boolean debuggable = false;
                    try {
                        Context context = this.mPluginManager.getHostContext();
                        debuggable = (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
                    } catch (Throwable ex) {
            
                    }
        
                    if (debuggable) {
                        throw new ActivityNotFoundException("error intent: " + intent.toURI());
                    }
                    
                    Log.i(TAG, "Not found. starting the stub activity: " + StubActivity.class);
                    return newActivity(mBase.newActivity(cl, StubActivity.class.getName(), intent));
                }
                
                Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
                activity.setIntent(intent);
        
                // for 4.1+
                Reflector.QuietReflector.with(activity).field("mResources").set(plugin.getResources());
        
                return newActivity(activity);
            }
    
            return newActivity(mBase.newActivity(cl, className, intent));
        }
    
    • callActivityOnCreate()方法,在调用原本的callActivityOnCreate方法时,先调用了 injectActivity(activity);方法。
        @Override
        public void callActivityOnCreate(Activity activity, Bundle icicle) {
            injectActivity(activity);
            mBase.callActivityOnCreate(activity, icicle);
        }
        
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
            injectActivity(activity);
            mBase.callActivityOnCreate(activity, icicle, persistentState);
        }
    
    • injectActivity(activity);方法
      • 取出Intent原本的想启动的activity信息Component,创建新的Intent wrapperIntent,设置给activity对象。
    protected void injectActivity(Activity activity) {
            final Intent intent = activity.getIntent();
            if (PluginUtil.isIntentFromPlugin(intent)) {
                Context base = activity.getBaseContext();
                try {
                    LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
                    Reflector.with(base).field("mResources").set(plugin.getResources());
                    Reflector reflector = Reflector.with(activity);
                    reflector.field("mBase").set(plugin.createPluginContext(activity.getBaseContext()));
                    reflector.field("mApplication").set(plugin.getApplication());
    
                    // set screenOrientation
                    ActivityInfo activityInfo = plugin.getActivityInfo(PluginUtil.getComponent(intent));
                    if (activityInfo.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
                        activity.setRequestedOrientation(activityInfo.screenOrientation);
                    }
        
                    // for native activity
                    ComponentName component = PluginUtil.getComponent(intent);
                    Intent wrapperIntent = new Intent(intent);
                    wrapperIntent.setClassName(component.getPackageName(), component.getClassName());
                    activity.setIntent(wrapperIntent);
                    
                } catch (Exception e) {
                    Log.w(TAG, e);
                }
            }
        }
    
    • 在启动activcity返回时是使用Handler发送消息到主队列,因为之前已经通过反射给handler设置了callback对象,查看handler源码。
        /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    //先执行callback的handleMessage(msg)方法。
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    
    • VAInstrumentation的callback的具体实现
      • 可以看到,方法始终返回false,所以上面handler中的dispatchMessage(msg)在msg没有callback的情况下,始终会调用 handleMessage(msg)去处理消息。
      • 而handler的callback则在这里替换了activity主题。
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == LAUNCH_ACTIVITY) {
                // ActivityClientRecord r
                Object r = msg.obj;
                try {
                    Reflector reflector = Reflector.with(r);
                    Intent intent = reflector.field("intent").get();
                    intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());
                    ActivityInfo activityInfo = reflector.field("activityInfo").get();
    
                    if (PluginUtil.isIntentFromPlugin(intent)) {
                        int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
                        if (theme != 0) {
                            Log.i(TAG, "resolve theme, current theme:" + activityInfo.theme + "  after :0x" + Integer.toHexString(theme));
                            activityInfo.theme = theme;
                        }
                    }
                } catch (Exception e) {
                    Log.w(TAG, e);
                }
            }
    
            return false;
        }
    

    总结

    上面这些是VA框架,对于Activity启动的一部分内容。实现主要是通过动态代理技术,在Activity启动过程中,将插件信息存储在Intent中,而传递给系统的是宿主中注册StubActivity,在系统返回时,通过handler的callback,取出Inent中存储的插件信息,创建新的Intent设置给Intent,再去启动。达到启动插件中Activity的目的。

    相关文章

      网友评论

          本文标题:VirtualApk 启动插件Activity的过程

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