VirtualAPK分析

别来无恙 提交于 2019-11-30 18:08:23

VirtualAPK分析

VirtualAPK是由滴滴公司发布的一款插件化的Android APK框架。 其使用的也是动态代理的方式实现的。请看下面官方给的框架图: avatar

从图中可以发现,在Core和Manager之间添加了一层代理。

先说宿主apk


	public class VAApplication extends Application {

	    @Override
	    protected void attachBaseContext(Context base) {
	        super.attachBaseContext(base);
	        long start = System.currentTimeMillis();
	        PluginManager.getInstance(base).init();
	        Log.d("ryg", "use time:" + (System.currentTimeMillis() - start));
	    }
	
	    @Override
	    public void onCreate() {
	        super.onCreate();
	    }
	}


首先宿主APK重写了Application,并且重写了attachBaseContext方法,此方法是在onCreate之前调用的。在attachBaseContext里面初始化了PluginManager,先看看在创建PluginManager的时候做了什么:


    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();
    }

刚开始是对mApplication和mContext的赋值,这个就不多说了。我们主要说说hookCurrentProcess()的实现:


    protected void hookCurrentProcess() {
        hookInstrumentationAndHandler();
        hookSystemServices();
        hookDataBindingUtil();
    }

这里调用了三个方法,大家该猜到这是代理初始化的地方,先看hookInstrumentationAndHandler()的实现:


    protected void hookInstrumentationAndHandler() {
        try {
            ActivityThread activityThread = ActivityThread.currentActivityThread();
            Instrumentation baseInstrumentation = activityThread.getInstrumentation();

            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);
        }
    }

这里是先获取了ActivityThreadInstrumenttation的实例,然后创建自己的VAInstrumentation,并将系统的Instrumenttation作为自己的变量,然后再把自己的VAInstrumentation设置到系统里面:

 Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
Reflector.with(mainHandler).field("mCallback").set(instrumentation);

VAInstrumentation已经继承了系统的Instrumentation以及Handler.Callback,并在继承的类里面重写了插件apk需要的方法:


 	@Override
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) {
        injectIntent(intent);
        return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode);
    }

    @Override
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
        injectIntent(intent);
        return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options);
    }

    @Override
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Fragment target, Intent intent, int requestCode, Bundle options) {
        injectIntent(intent);
        return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options);
    }

    @Override
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options) {
        injectIntent(intent);
        return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options);
    }
    
    protected void injectIntent(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);
        }
    }

相比系统的调用多了injectIntent(Intent intent)的调用,这个方法主要是寻找插件里面相应的component,并将intent填写完整。 这一就可以完成插件里面组件的无感知调用。

下面再看对service的代理:


   	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);
        }
    }

方法其实和上面是类似的,首先获取系统的ActivityManager,然后再将代理的activityManagerProxy注入到系统里面:

Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy);

这样要是再启动组件的时候就会调用到我们的代理类里面,代理类里面可以去寻找插件里面的类。以·startService为例:


    protected Object startService(Object proxy, Method method, Object[] args) throws Throwable {
        IApplicationThread appThread = (IApplicationThread) args[0];
        Intent target = (Intent) args[1];
        ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
        if (null == resolveInfo || null == resolveInfo.serviceInfo) {
            // is host service
            return method.invoke(this.mActivityManager, args);
        }

        return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null, RemoteService.EXTRA_COMMAND_START_SERVICE);
    }

再看startDelegateServiceForTarget的实现:


    protected ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
        Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command);
        return mPluginManager.getHostContext().startService(wrapperIntent);
    }

这个会启动插件里面的Service。

最后一个是数据的绑定,主要对插件里面资源的注入:


   	 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);
            }
        }
    }

将代理的DataBinderMapperProxy设置到系统里面:

reflector.set(callback);

通过主动的将插件的资源路径插入到系统的map里面:


    @Override
    public void onAddedLoadedPlugin(LoadedPlugin plugin) {
        try {
            String clsName = "android.databinding.DataBinderMapper_" + plugin.getPackageName().replace('.', '_');
            Log.d(TAG, "Try to find the class: " + clsName);
            Class cls = Class.forName(clsName, true, plugin.getClassLoader());
            Object obj = cls.newInstance();
    
            addMapper((DataBinderMapper) obj);
            
        } catch (Exception e) {
            Log.w(TAG, e);
        }
    }

在获取的过程中就可以从这里取到资源信息:


    @Override
    public int getLayoutId(String tag) {
        int layoutId;
    
        for (DataBinderMapper mapper : getCache()) {
            layoutId = mapper.getLayoutId(tag);
            if (layoutId != 0) {
                return layoutId;
            }
        }
    
        return 0;
    }

插件的加载

插件的加载是在主Activity启动的时候做了加载:


    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = (TextView)findViewById(R.id.textView);
        String cpuArch;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            cpuArch = Build.SUPPORTED_ABIS[0];
        } else {
            cpuArch = Build.CPU_ABI;
        }
        textView.setText(cpuArch);
        Log.d("ryg", "onCreate cpu arch is "+ cpuArch);
        Log.d("ryg", "onCreate classloader is "+ getClassLoader());

        if (hasPermission()) {
            Log.d(TAG,"loadPlugin");

            this.loadPlugin(this);
        } else {
            requestPermission();
        }
    }

  	 private void loadPlugin(Context base) {
        if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            Toast.makeText(this, "sdcard was NOT MOUNTED!", Toast.LENGTH_SHORT).show();
        }

        PluginManager pluginManager = PluginManager.getInstance(base);
        File apk = new File(Environment.getExternalStorageDirectory(), "Test.apk");
        
        if (apk.exists()) {
            try {
                pluginManager.loadPlugin(apk);
                Log.i(TAG, "Loaded plugin from apk: " + apk);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            try {
                File file = new File(base.getFilesDir(), "Test.apk");
                java.io.InputStream inputStream = base.getAssets().open("Test.apk", 2);
                java.io.FileOutputStream outputStream = new java.io.FileOutputStream(file);
                byte[] buf = new byte[1024];
                int len;
    
                while ((len = inputStream.read(buf)) > 0) {
                    outputStream.write(buf, 0, len);
                }
    
                outputStream.close();
                inputStream.close();
                pluginManager.loadPlugin(file);
                Log.i(TAG, "Loaded plugin from assets: " + file);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

从上面两段代码可以看出,从外部存储器加载了一个叫Test.apk的文件。这个就是插件的apk,这个APK是一个完成的APK,可以自己独立运行。但是在这里它并没有安装,只是存放在外部存储器上。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!