精华内容
下载资源
问答
  • VirtualApp

    千次阅读 2018-05-15 20:40:52
    VirtualApp
    
    

    转自:http://rk700.github.io/2017/03/15/virtualapp-basic/

    VirtualApp是一个开源的Android App虚拟化引擎,允许在其中创建虚拟空间,并在这个虚拟空间中运行其他应用。通过阅读源码及动态调试,基本了解了其运行原理,在此记录。

    本质

    Android应用隔离是基于Linux系统的多用户机制实现的,即每个应用在安装时被分配了不同的Linux用户uid/gid。而在VirtualApp中,client应用(通过VirtualApp安装的应用)与host应用(即VirtualApp本身)是具有相同用户uid的。

    因此,VirtualApp在运行时,包含以下三部分:

    • Main Process,进程名io.virtualapp,主要负责VirtualApp用户界面及应用管理
    • Server Process,进程名io.virtualapp:x,主要负责系统服务的代理,是通过Content Provider启动的
    • VApp Process,进程名io.virtualapp:p[0-…],作为将来运行client应用的进程,当client应用启动后,其进程名会更新为client应用的包名

    下面是在VirtualApp中运行应用后通过ps命令得到的结果:

    generic_x86:/ ps |grep u0_a60
    u0_a60    2385  1258  996260 54456 SyS_epoll_ 00000000 S io.virtualapp
    u0_a60    2412  1258  980940 48272 SyS_epoll_ 00000000 S io.virtualapp:x
    u0_a60    3705  1258  993632 54472 SyS_epoll_ 00000000 S org.galaxy.simpleapp

    可以看到,以上进程,均是以VirtualApp的用户uid运行的。因此,Android应用隔离此时不再适用,我们可以对client应用进行hook而无需root权限。


    运行流程

    从启动VirtualApp到运行其中的应用,大致流程如下:

    启动host应用

    我们启动VirtualApp,其Application为io.virtualapp.VApp。在attachBaseContext()方法中会调用到com.lody.virtual.client.core.PatchManager#injectInternal,但此时为Main Process,不进行系统服务的替换。

    启动Server Process

    host应用会进行一些初始化,其中就包括获取全部已安装应用,这会调用到com.lody.virtual.client.core.VirtualCore#getAllApps。而这一方法最终会访问com.lody.virtual.server.BinderProvider。由AndroidManifest.xml可知,该provider会运行在新进程io.virtualapp:x中,即Server Process。

    由于在新进程中启动组件,同样会首先创建该应用的Application,因此也会调用到com.lody.virtual.client.core.PatchManager#injectInternal。此时,会进行相应系统服务(ActivityManager和PackageManager)的代理构造和替换。

    启动VApp Process

    点击一个已安装应用,此时会通过替换掉的系统服务访问真实的系统服务(主要是ActivityManager),并在新进程中启动组件com.lody.virtual.client.stub.StubActivity.C0。由AndroidManifest.xml可知,该进程具有后缀:p0。

    同样的,在该Activity组件启动之前会初始化io.virtualapp.VApp,并在com.lody.virtual.client.core.PatchManager#injectInternal中完成系统服务的代理构造和替换。

    启动client应用

    此时,真正的client应用尚未启动,进程io.virtualapp:p0仅仅是作为一个placeholder。StubActivity会从Intent中获取到client应用的相关信息,并修改自身ActivityThread的handler。随后调用startActivity启动client应用。

    由于之前Server Process和VApp Process都已完成了相关系统服务的替换,这里会完成client应用的bindApplication调用、构造client应用的LoadedApk,并通过反射完成真正的Application和Activity的创建。

    最终,client应用便运行在了我们的VApp Process中。


    系统服务的代理和替换

    VirtualApp之所以能够实现虚拟空间,是因为其对许多系统服务进行了代理和替换。因此,这部分便是整个框架的核心。系统服务运行在system_server中,Android应用调用系统服务,是通过Binder机制进行IPC。因此,应用所持有的是系统服务的BinderProxy,通过对这些BinderProxer构造代理并替换,便实现了对系统服务的代理和替换。

    具体地,我们以com.lody.virtual.client.hook.patchs.am.ActivityManagerPatch为例,这个类实现了对ActivityManager服务的代理和替换。

    代理的构造

    可以看到,这个类的注记中包含了大量类名:

    @Patch({StartActivity.class, StartActivityAsCaller.class,
            StartActivityAndWait.class, StartActivityWithConfig.class, StartActivityIntentSender.class,
            StartNextMatchingActivity.class, StartVoiceActivity.class,
            GetIntentSender.class, RegisterReceiver.class, GetContentProvider.class,
            GetContentProviderExternal.class,
            ...

    而这些列出的每一个类,对应于一个方法的hook,例如,com.lody.virtual.client.hook.patchs.am.StartActivity是ActivityManager服务的startActivity方法的hook。这些类均继承自com.lody.virtual.client.hook.base.Hook,包含了方法beforeCall()call()afterCall(),这些方法便是hook的具体内容。

    ActivityManagerPatch在创建时,会调用到其父类的方法com.lody.virtual.client.hook.base.PatchDelegate#onBindHooks。这里会检查上述注记中列出的hook,并对符合条件的hook调用addHook()方法:

            ...
            Class<? extends PatchDelegate> clazz = getClass();
            Patch patch = clazz.getAnnotation(Patch.class);
            int version = Build.VERSION.SDK_INT;
            if (patch != null) {
                Class<?>[] hookTypes = patch.value();
                for (Class<?> hookType : hookTypes) {
                    ApiLimit apiLimit = hookType.getAnnotation(ApiLimit.class);
                    boolean needToAddHook = true;
                    if (apiLimit != null) {
                        int apiStart = apiLimit.start();
                        int apiEnd = apiLimit.end();
                        boolean highThanStart = apiStart == -1 || version > apiStart;
                        boolean lowThanEnd = apiEnd == -1 || version < apiEnd;
                        if (!highThanStart || !lowThanEnd) {
                            needToAddHook = false;
                        }
                    }
                    if (needToAddHook) {
                        addHook(hookType);
                    }
            ...

    addHook()最终会调用到com.lody.virtual.client.hook.base.HookDelegate#addHook,其实质便是将这个hook添加至映射表internalHookTable中:

        public Hook addHook(Hook hook) {
            if (hook != null && !TextUtils.isEmpty(hook.getName())) {
                if (internalHookTable.containsKey(hook.getName())) {
                    VLog.w(TAG, "The Hook(%s, %s) you added has been in existence.", hook.getName(),
                            hook.getClass().getName());
                    return hook;
                }
                internalHookTable.put(hook.getName(), hook);
            }
            return hook;
        }

    internalHookTable维护了所有的hook,以hook的名称(一般就是所hook的方法的名称)作为key。随后,在com.lody.virtual.client.hook.base.HookDelegate.HookHandlerinvoke()方法中,查找表 internalHookTable中是否包含将要执行的方法名;如果有,则依次执行对应hook的beforeCall()call()afterCall()

        private class HookHandler implements InvocationHandler {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Hook hook = getHook(method.getName());
                try {
                    if (hook != null && hook.isEnable()) {
                        if (hook.beforeCall(mBaseInterface, method, args)) {
                            Object res = hook.call(mBaseInterface, method, args);
                            res = hook.afterCall(mBaseInterface, method, args, res);
                            return res;
                        }
                    }
                    return method.invoke(mBaseInterface, args);

    而这里的类HookHandler,就是构造的Java代理的Handler:

        public HookDelegate(T baseInterface, Class<?>... proxyInterfaces) {
            this.mBaseInterface = baseInterface;
            if (baseInterface != null) {
                if (proxyInterfaces == null) {
                    proxyInterfaces = HookUtils.getAllInterface(baseInterface.getClass());
                }
                mProxyInterface = (T) Proxy.newProxyInstance(baseInterface.getClass().getClassLoader(), proxyInterfaces, new HookHandler());

    对于ActivityManagerPatch来说,这里的baseInterface便是原始的BinderProxy:ActivityManagerProxy

        public ActivityManagerPatch() {
            super(new HookDelegate<IInterface>(ActivityManagerNative.getDefault.call()));
        }

    综上,我们根据baseInterface,为其构造了代理mProxyInterface。从而访问mProxyInterface时,便会执行HookHandlerinvoke()方法,进而查找internalHookTable,对设置了hook的方法执行hook。

    系统服务的替换

    如之前所说,对系统服务的替换,是通过对应用所持有的系统服务的BinderProxy进行替换的。以上是构造代理的基本过程,那么如何将应用所持有的BinderProxy替换成我们构造的代理呢?回到ActivityManagerPatch,这个类的inject()方法完成了实际的替换工作:

        @Override
        public void inject() throws Throwable {
            if (ActivityManagerNative.gDefault.type() == IActivityManager.TYPE) {
                ActivityManagerNative.gDefault.set(getHookDelegate().getProxyInterface());
    
            } else if (ActivityManagerNative.gDefault.type() == Singleton.TYPE) {
                Object gDefault = ActivityManagerNative.gDefault.get();
                Singleton.mInstance.set(gDefault, getHookDelegate().getProxyInterface());
            }
            ...

    ActivityManagerNative.gDefault便是应用所持有的原始ActivityManagerProxy对象,通过Java反射,将替换成为getHookDelegate().getProxyInterface()。而替换的内容,便是我们所构造的代理mProxyInterface

    由此,我们完成了对系统服务进行代理和替换的整个过程。随后,在调用系统服务时,便会执行以下操作:

    • 访问BinderProxy的代理,即我们设置了hook的代理
    • 根据hook的具体内容操作,对数据进行处理;需要调用原始系统服务时,访问原始的BinderProxy
    • 真正的系统服务接收到Binder,进行处理并返回

    总结

    通过以上介绍可以看到,VirtualApp在原有系统服务之上构造了代理,进而为其中的应用搭建了一套虚拟环境,应用可以无感知地运行在这其中。更进一步,我们可以设置这套虚拟环境,使其实现应用多开、非侵入式应用hook等高级功能。


    参考资料

    展开全文
  • VirtualApp工程 全新体验,多种优化 特性 高级,高稳定性,修复重建错误等等 本内核仅供开发人员参考,请勿构建成品并发表到任何地方 仅供自行测试使用 如有修改建议欢迎提交PR 本项目为商业版开源,修改可看commit ...
  • Android代码-VirtualApp

    2019-08-05 17:14:30
    VirtualApp is an open platform for Android that allows you to create a Virtual Space, you can install and run apk inside. Beyond that, VirtualApp is also a Plugin Framework, the plugins running on ...
  • virtualapp编译好的

    2017-10-08 18:46:40
    大家要是环境没配好,不好编译,就先用我编译的virtualapp吧。
  • VirtualApp是一个开源的Android App虚拟化引擎,允许在其中创建虚拟空间,并在这个虚拟空间中运行其他应用。通过阅读源码及动态调试,基本了解了其运行原理,在此记录。 本质 Android应用隔离是基于Linux...

    转自:http://www.voidcn.com/article/p-mhioxytj-ys.html

    VirtualApp是一个开源的Android App虚拟化引擎,允许在其中创建虚拟空间,并在这个虚拟空间中运行其他应用。通过阅读源码及动态调试,基本了解了其运行原理,在此记录。

    本质

    Android应用隔离是基于Linux系统的多用户机制实现的,即每个应用在安装时被分配了不同的Linux用户uid/gid。而在VirtualApp中,client应用(通过VirtualApp安装的应用)与host应用(即VirtualApp本身)是具有相同用户uid的。

    因此,VirtualApp在运行时,包含以下三部分:

    • Main Process,进程名io.virtualapp,主要负责VirtualApp用户界面及应用管理
    • Server Process,进程名io.virtualapp:x,主要负责系统服务的代理,是通过Content Provider启动的
    • VApp Process,进程名io.virtualapp:p[0-…],作为将来运行client应用的进程,当client应用启动后,其进程名会更新为client应用的包名

    下面是在VirtualApp中运行应用后通过ps命令得到的结果:

    generic_x86:/ ps |grep u0_a60
    u0_a60    2385  1258  996260 54456 SyS_epoll_ 00000000 S io.virtualapp
    u0_a60    2412  1258  980940 48272 SyS_epoll_ 00000000 S io.virtualapp:x
    u0_a60    3705  1258  993632 54472 SyS_epoll_ 00000000 S org.galaxy.simpleapp

    可以看到,以上进程,均是以VirtualApp的用户uid运行的。因此,Android应用隔离此时不再适用,我们可以对client应用进行hook而无需root权限。


    运行流程

    从启动VirtualApp到运行其中的应用,大致流程如下:

    启动host应用

    我们启动VirtualApp,其Application为io.virtualapp.VApp。在attachBaseContext()方法中会调用到com.lody.virtual.client.core.PatchManager#injectInternal,但此时为Main Process,不进行系统服务的替换。

    启动Server Process

    host应用会进行一些初始化,其中就包括获取全部已安装应用,这会调用到com.lody.virtual.client.core.VirtualCore#getAllApps。而这一方法最终会访问com.lody.virtual.server.BinderProvider。由AndroidManifest.xml可知,该provider会运行在新进程io.virtualapp:x中,即Server Process。

    由于在新进程中启动组件,同样会首先创建该应用的Application,因此也会调用到com.lody.virtual.client.core.PatchManager#injectInternal。此时,会进行相应系统服务(ActivityManager和PackageManager)的代理构造和替换。

    启动VApp Process

    点击一个已安装应用,此时会通过替换掉的系统服务访问真实的系统服务(主要是ActivityManager),并在新进程中启动组件com.lody.virtual.client.stub.StubActivity.C0。由AndroidManifest.xml可知,该进程具有后缀:p0。

    同样的,在该Activity组件启动之前会初始化io.virtualapp.VApp,并在com.lody.virtual.client.core.PatchManager#injectInternal中完成系统服务的代理构造和替换。

    启动client应用

    此时,真正的client应用尚未启动,进程io.virtualapp:p0仅仅是作为一个placeholder。StubActivity会从Intent中获取到client应用的相关信息,并修改自身ActivityThread的handler。随后调用startActivity启动client应用。

    由于之前Server Process和VApp Process都已完成了相关系统服务的替换,这里会完成client应用的bindApplication调用、构造client应用的LoadedApk,并通过反射完成真正的Application和Activity的创建。

    最终,client应用便运行在了我们的VApp Process中。


    系统服务的代理和替换

    VirtualApp之所以能够实现虚拟空间,是因为其对许多系统服务进行了代理和替换。因此,这部分便是整个框架的核心。系统服务运行在system_server中,Android应用调用系统服务,是通过Binder机制进行IPC。因此,应用所持有的是系统服务的BinderProxy,通过对这些BinderProxer构造代理并替换,便实现了对系统服务的代理和替换。

    具体地,我们以com.lody.virtual.client.hook.patchs.am.ActivityManagerPatch为例,这个类实现了对ActivityManager服务的代理和替换。

    代理的构造

    可以看到,这个类的注记中包含了大量类名:

    @Patch({StartActivity.class, StartActivityAsCaller.class,
            StartActivityAndWait.class, StartActivityWithConfig.class, StartActivityIntentSender.class,
            StartNextMatchingActivity.class, StartVoiceActivity.class,
            GetIntentSender.class, RegisterReceiver.class, GetContentProvider.class,
            GetContentProviderExternal.class,
            ...

    而这些列出的每一个类,对应于一个方法的hook,例如,com.lody.virtual.client.hook.patchs.am.StartActivity是ActivityManager服务的startActivity方法的hook。这些类均继承自com.lody.virtual.client.hook.base.Hook,包含了方法beforeCall()call()afterCall(),这些方法便是hook的具体内容。

    ActivityManagerPatch在创建时,会调用到其父类的方法com.lody.virtual.client.hook.base.PatchDelegate#onBindHooks。这里会检查上述注记中列出的hook,并对符合条件的hook调用addHook()方法:

    ...
            Class<? extends PatchDelegate> clazz = getClass();
            Patch patch = clazz.getAnnotation(Patch.class);
            int version = Build.VERSION.SDK_INT;
            if (patch != null) {
                Class<?>[] hookTypes = patch.value();
                for (Class<?> hookType : hookTypes) {
                    ApiLimit apiLimit = hookType.getAnnotation(ApiLimit.class);
                    boolean needToAddHook = true;
                    if (apiLimit != null) {
                        int apiStart = apiLimit.start();
                        int apiEnd = apiLimit.end();
                        boolean highThanStart = apiStart == -1 || version > apiStart;
                        boolean lowThanEnd = apiEnd == -1 || version < apiEnd;
                        if (!highThanStart || !lowThanEnd) {
                            needToAddHook = false;
                        }
                    }
                    if (needToAddHook) {
                        addHook(hookType);
                    }
            ...

    addHook()最终会调用到com.lody.virtual.client.hook.base.HookDelegate#addHook,其实质便是将这个hook添加至映射表internalHookTable中:

    public Hook addHook(Hook hook) {
            if (hook != null && !TextUtils.isEmpty(hook.getName())) {
                if (internalHookTable.containsKey(hook.getName())) {
                    VLog.w(TAG, "The Hook(%s, %s) you added has been in existence.", hook.getName(),
                            hook.getClass().getName());
                    return hook;
                }
                internalHookTable.put(hook.getName(), hook);
            }
            return hook;
        }

    internalHookTable维护了所有的hook,以hook的名称(一般就是所hook的方法的名称)作为key。随后,在com.lody.virtual.client.hook.base.HookDelegate.HookHandlerinvoke()方法中,查找表 internalHookTable中是否包含将要执行的方法名;如果有,则依次执行对应hook的beforeCall()call()afterCall()

    private class HookHandler implements InvocationHandler {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Hook hook = getHook(method.getName());
                try {
                    if (hook != null && hook.isEnable()) {
                        if (hook.beforeCall(mBaseInterface, method, args)) {
                            Object res = hook.call(mBaseInterface, method, args);
                            res = hook.afterCall(mBaseInterface, method, args, res);
                            return res;
                        }
                    }
                    return method.invoke(mBaseInterface, args);

    而这里的类HookHandler,就是构造的Java代理的Handler:

    public HookDelegate(T baseInterface, Class<?>... proxyInterfaces) {
            this.mBaseInterface = baseInterface;
            if (baseInterface != null) {
                if (proxyInterfaces == null) {
                    proxyInterfaces = HookUtils.getAllInterface(baseInterface.getClass());
                }
                mProxyInterface = (T) Proxy.newProxyInstance(baseInterface.getClass().getClassLoader(), proxyInterfaces, new HookHandler());

    对于ActivityManagerPatch来说,这里的baseInterface便是原始的BinderProxy:ActivityManagerProxy

    public ActivityManagerPatch() {
            super(new HookDelegate<IInterface>(ActivityManagerNative.getDefault.call()));
        }

    综上,我们根据baseInterface,为其构造了代理mProxyInterface。从而访问mProxyInterface时,便会执行HookHandlerinvoke()方法,进而查找internalHookTable,对设置了hook的方法执行hook。

    系统服务的替换

    如之前所说,对系统服务的替换,是通过对应用所持有的系统服务的BinderProxy进行替换的。以上是构造代理的基本过程,那么如何将应用所持有的BinderProxy替换成我们构造的代理呢?回到ActivityManagerPatch,这个类的inject()方法完成了实际的替换工作:

    @Override
        public void inject() throws Throwable {
            if (ActivityManagerNative.gDefault.type() == IActivityManager.TYPE) {
                ActivityManagerNative.gDefault.set(getHookDelegate().getProxyInterface());
            } else if (ActivityManagerNative.gDefault.type() == Singleton.TYPE) {
                Object gDefault = ActivityManagerNative.gDefault.get();
                Singleton.mInstance.set(gDefault, getHookDelegate().getProxyInterface());
            }
            ...

    ActivityManagerNative.gDefault便是应用所持有的原始ActivityManagerProxy对象,通过Java反射,将替换成为getHookDelegate().getProxyInterface()。而替换的内容,便是我们所构造的代理mProxyInterface

    由此,我们完成了对系统服务进行代理和替换的整个过程。随后,在调用系统服务时,便会执行以下操作:

    • 访问BinderProxy的代理,即我们设置了hook的代理
    • 根据hook的具体内容操作,对数据进行处理;需要调用原始系统服务时,访问原始的BinderProxy
    • 真正的系统服务接收到Binder,进行处理并返回

    总结

    通过以上介绍可以看到,VirtualApp在原有系统服务之上构造了代理,进而为其中的应用搭建了一套虚拟环境,应用可以无感知地运行在这其中。更进一步,我们可以设置这套虚拟环境,使其实现应用多开、非侵入式应用hook等高级功能。


    参考资料

    展开全文
  • VirtualApp中静默安装App

    2019-11-18 11:27:39
    最近学习看到android沙箱VirtualApp,小编资历尚浅,只是从VA运用的角度记录了下来如何在VA中静默安装app VirtualApp github地址https://github.com/asLody/VirtualApp 1、将需要安装的app包放置在assets目录下 2...

    最近学习看到android沙箱VirtualApp,小编资历尚浅,只是从VA运用的角度记录了下来如何在VA中静默安装app

    VirtualApp github地址https://github.com/android-hacker/VirtualXposed

    1、将需要安装的app包放置在assets目录下

    2、构建app安装需要的方法如下

        private void installApp(String path) {
            if (checkRequiredPermission()) {
                try {
                    File localFile = extractApp(path);
                    //com.app.test是测试安装的app包名
                    AppInfoLite localAppInfoLite = new AppInfoLite("com.app.test", localFile.getAbsolutePath(), true);
                    mPresenter.addApp(localAppInfoLite);
                } catch (Exception localException) {
                    Toast.makeText(this, "安装失败", Toast.LENGTH_SHORT).show();
                }
            } else {
                ActivityCompat.requestPermissions(this, new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"}, 200);
            }
        }
        private File extractApp(String path) {
            try {
                File localFile = new File(path);
                if (localFile.exists()) {
                    localFile.delete();
                }
                //提取的app文件,testApp是我测试的app名称
                FileUtils.writeToFile(getAssets().open("testApp.apk"), localFile);
                return localFile;
            } catch (IOException localIOException) {
            }
            return null;
        }
        private boolean checkRequiredPermission() {
            return (Build.VERSION.SDK_INT < 23) || (ContextCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") == 0);
        }

     3、在需要安装App代码的地方调用installApp方法即可

    String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/testApp.apk";
                //判断app是否已经安装
                if (!VirtualCore.get().isAppInstalled("com.app.test")) {
                    installApp(apkPath);
                }else{
                    Toast.makeText(this, "当前APP已经安装,请勿重复安装", Toast.LENGTH_SHORT).show();
                }

     

    展开全文
  • VirtualApp源码(完整工程),基于android studio (ndk,sdk)开发,实现安卓双开。
  • VirtualApp 9.0适\
  • Android-VirtualApp.zip

    2019-09-17 11:51:57
    Android-VirtualApp.zip,Android虚拟引擎(商务版支持10.0),安卓系统是谷歌在2008年设计和制造的。操作系统主要写在爪哇,C和C 的核心组件。它是在linux内核之上构建的,具有安全性优势。
  • VirtualApp沙盒基本原理

    千次阅读 2020-01-08 09:08:45
    VirtualApp沙盒基本原理

    VirtualApp是一个开源的Android App虚拟化引擎,允许在其中创建虚拟空间,并在这个虚拟空间中运行其他应用。通过阅读源码及动态调试,基本了解了其运行原理,在此记录。

    本质

    Android应用隔离是基于Linux系统的多用户机制实现的,即每个应用在安装时被分配了不同的Linux用户uid/gid。而在VirtualApp中,client应用(通过VirtualApp安装的应用)与host应用(即VirtualApp本身)是具有相同用户uid的。

    因此,VirtualApp在运行时,包含以下三部分:

    • Main Process,进程名io.virtualapp,主要负责VirtualApp用户界面及应用管理
    • Server Process,进程名io.virtualapp:x,主要负责系统服务的代理,是通过Content Provider启动的
    • VApp Process,进程名io.virtualapp:p[0-…],作为将来运行client应用的进程,当client应用启动后,其进程名会更新为client应用的包名

    下面是在VirtualApp中运行应用后通过ps命令得到的结果:

    
     
    1. generic_x86:/ ps |grep u0_a60
    2. u0_a60 2385 1258 996260 54456 SyS_epoll_ 00000000 S io.virtualapp
    3. u0_a60 2412 1258 980940 48272 SyS_epoll_ 00000000 S io.virtualapp:x
    4. u0_a60 3705 1258 993632 54472 SyS_epoll_ 00000000 S org.galaxy.simpleapp

    可以看到,以上进程,均是以VirtualApp的用户uid运行的。因此,Android应用隔离此时不再适用,我们可以对client应用进行hook而无需root权限。


    运行流程

    从启动VirtualApp到运行其中的应用,大致流程如下:

    启动host应用

    我们启动VirtualApp,其Application为io.virtualapp.VApp。在attachBaseContext()方法中会调用到com.lody.virtual.client.core.PatchManager#injectInternal,但此时为Main Process,不进行系统服务的替换。

    启动Server Process

    host应用会进行一些初始化,其中就包括获取全部已安装应用,这会调用到com.lody.virtual.client.core.VirtualCore#getAllApps。而这一方法最终会访问com.lody.virtual.server.BinderProvider。由AndroidManifest.xml可知,该provider会运行在新进程io.virtualapp:x中,即Server Process。

    由于在新进程中启动组件,同样会首先创建该应用的Application,因此也会调用到com.lody.virtual.client.core.PatchManager#injectInternal。此时,会进行相应系统服务(ActivityManager和PackageManager)的代理构造和替换。

    启动VApp Process

    点击一个已安装应用,此时会通过替换掉的系统服务访问真实的系统服务(主要是ActivityManager),并在新进程中启动组件com.lody.virtual.client.stub.StubActivity.C0。由AndroidManifest.xml可知,该进程具有后缀:p0。

    同样的,在该Activity组件启动之前会初始化io.virtualapp.VApp,并在com.lody.virtual.client.core.PatchManager#injectInternal中完成系统服务的代理构造和替换。

    启动client应用

    此时,真正的client应用尚未启动,进程io.virtualapp:p0仅仅是作为一个placeholder。StubActivity会从Intent中获取到client应用的相关信息,并修改自身ActivityThread的handler。随后调用startActivity启动client应用。

    由于之前Server Process和VApp Process都已完成了相关系统服务的替换,这里会完成client应用的bindApplication调用、构造client应用的LoadedApk,并通过反射完成真正的Application和Activity的创建。

    最终,client应用便运行在了我们的VApp Process中。


    系统服务的代理和替换

    VirtualApp之所以能够实现虚拟空间,是因为其对许多系统服务进行了代理和替换。因此,这部分便是整个框架的核心。系统服务运行在system_server中,Android应用调用系统服务,是通过Binder机制进行IPC。因此,应用所持有的是系统服务的BinderProxy,通过对这些BinderProxer构造代理并替换,便实现了对系统服务的代理和替换。

    具体地,我们以com.lody.virtual.client.hook.patchs.am.ActivityManagerPatch为例,这个类实现了对ActivityManager服务的代理和替换。

    代理的构造

    可以看到,这个类的注记中包含了大量类名:

    
     
    1. @Patch ({ StartActivity . class , StartActivityAsCaller . class ,
    2. StartActivityAndWait . class , StartActivityWithConfig . class , StartActivityIntentSender . class ,
    3. StartNextMatchingActivity . class , StartVoiceActivity . class ,
    4. GetIntentSender . class , RegisterReceiver . class , GetContentProvider . class ,
    5. GetContentProviderExternal . class ,
    6. ...

    而这些列出的每一个类,对应于一个方法的hook,例如,com.lody.virtual.client.hook.patchs.am.StartActivity是ActivityManager服务的startActivity方法的hook。这些类均继承自com.lody.virtual.client.hook.base.Hook,包含了方法beforeCall()call()afterCall(),这些方法便是hook的具体内容。

    ActivityManagerPatch在创建时,会调用到其父类的方法com.lody.virtual.client.hook.base.PatchDelegate#onBindHooks。这里会检查上述注记中列出的hook,并对符合条件的hook调用addHook()方法:

    
     
    1. ...
    2. Class <? extends PatchDelegate > clazz = getClass ();
    3. Patch patch = clazz . getAnnotation ( Patch . class );
    4. int version = Build . VERSION . SDK_INT ;
    5. if ( patch != null ) {
    6. Class <?>[] hookTypes = patch . value ();
    7. for ( Class <?> hookType : hookTypes ) {
    8. ApiLimit apiLimit = hookType . getAnnotation ( ApiLimit . class );
    9. boolean needToAddHook = true ;
    10. if ( apiLimit != null ) {
    11. int apiStart = apiLimit . start ();
    12. int apiEnd = apiLimit . end ();
    13. boolean highThanStart = apiStart == - 1 || version > apiStart ;
    14. boolean lowThanEnd = apiEnd == - 1 || version < apiEnd ;
    15. if (! highThanStart || ! lowThanEnd ) {
    16. needToAddHook = false ;
    17. }
    18. }
    19. if ( needToAddHook ) {
    20. addHook ( hookType );
    21. }
    22. ...

    addHook()最终会调用到com.lody.virtual.client.hook.base.HookDelegate#addHook,其实质便是将这个hook添加至映射表internalHookTable中:

    
     
    1. public Hook addHook ( Hook hook ) {
    2. if ( hook != null && ! TextUtils . isEmpty ( hook . getName ())) {
    3. if ( internalHookTable . containsKey ( hook . getName ())) {
    4. VLog . w ( TAG , "The Hook(%s, %s) you added has been in existence." , hook . getName (),
    5. hook . getClass (). getName ());
    6. return hook ;
    7. }
    8. internalHookTable . put ( hook . getName (), hook );
    9. }
    10. return hook ;
    11. }

    internalHookTable维护了所有的hook,以hook的名称(一般就是所hook的方法的名称)作为key。随后,在com.lody.virtual.client.hook.base.HookDelegate.HookHandlerinvoke()方法中,查找表 internalHookTable中是否包含将要执行的方法名;如果有,则依次执行对应hook的beforeCall()call()afterCall()

    
     
    1. private class HookHandler implements InvocationHandler {
    2. @Override
    3. public Object invoke ( Object proxy , Method method , Object [] args ) throws Throwable {
    4. Hook hook = getHook ( method . getName ());
    5. try {
    6. if ( hook != null && hook . isEnable ()) {
    7. if ( hook . beforeCall ( mBaseInterface , method , args )) {
    8. Object res = hook . call ( mBaseInterface , method , args );
    9. res = hook . afterCall ( mBaseInterface , method , args , res );
    10. return res ;
    11. }
    12. }
    13. return method . invoke ( mBaseInterface , args );

    而这里的类HookHandler,就是构造的Java代理的Handler:

    
     
    1. public HookDelegate ( T baseInterface , Class <?>... proxyInterfaces ) {
    2. this . mBaseInterface = baseInterface ;
    3. if ( baseInterface != null ) {
    4. if ( proxyInterfaces == null ) {
    5. proxyInterfaces = HookUtils . getAllInterface ( baseInterface . getClass ());
    6. }
    7. mProxyInterface = ( T ) Proxy . newProxyInstance ( baseInterface . getClass (). getClassLoader (), proxyInterfaces , new HookHandler ());

    对于ActivityManagerPatch来说,这里的baseInterface便是原始的BinderProxy:ActivityManagerProxy

    
     
    1. public ActivityManagerPatch () {
    2. super ( new HookDelegate < IInterface >( ActivityManagerNative . getDefault . call ()));
    3. }

    综上,我们根据baseInterface,为其构造了代理mProxyInterface。从而访问mProxyInterface时,便会执行HookHandlerinvoke()方法,进而查找internalHookTable,对设置了hook的方法执行hook。

    系统服务的替换

    如之前所说,对系统服务的替换,是通过对应用所持有的系统服务的BinderProxy进行替换的。以上是构造代理的基本过程,那么如何将应用所持有的BinderProxy替换成我们构造的代理呢?回到ActivityManagerPatch,这个类的inject()方法完成了实际的替换工作:

    
     
    1. @Override
    2. public void inject () throws Throwable {
    3. if ( ActivityManagerNative . gDefault . type () == IActivityManager . TYPE ) {
    4. ActivityManagerNative . gDefault . set ( getHookDelegate (). getProxyInterface ());
    5. } else if ( ActivityManagerNative . gDefault . type () == Singleton . TYPE ) {
    6. Object gDefault = ActivityManagerNative . gDefault . get ();
    7. Singleton . mInstance . set ( gDefault , getHookDelegate (). getProxyInterface ());
    8. }
    9. ...

    ActivityManagerNative.gDefault便是应用所持有的原始ActivityManagerProxy对象,通过Java反射,将替换成为getHookDelegate().getProxyInterface()。而替换的内容,便是我们所构造的代理mProxyInterface

    由此,我们完成了对系统服务进行代理和替换的整个过程。随后,在调用系统服务时,便会执行以下操作:

    • 访问BinderProxy的代理,即我们设置了hook的代理
    • 根据hook的具体内容操作,对数据进行处理;需要调用原始系统服务时,访问原始的BinderProxy
    • 真正的系统服务接收到Binder,进行处理并返回

    总结

    通过以上介绍可以看到,VirtualApp在原有系统服务之上构造了代理,进而为其中的应用搭建了一套虚拟环境,应用可以无感知地运行在这其中。更进一步,我们可以设置这套虚拟环境,使其实现应用多开、非侵入式应用hook等高级功能。


    参考资料

    展开全文
  • 类似LBE平行空间, VirtualApp是一个App虚拟引擎的开源实现。 VirtualApp在你的App进程内创建一个虚拟空间,你可以在虚拟空间内任意的安装、启动和卸载APK, 这一切都与外部隔离,就如同一个沙盒。VirtualApp亦是一...
  • VirtualAPP 双开系列02】进程管理

    千次阅读 2021-04-07 14:48:47
    1.运行 VirtualAPP,查看进程 2.VirtualAPP 分成四种类型的进程 3.关于 Stub 4.关于 ServiceManager 1. 运行 VirtualAPP,查看进程 adb shell; top; 在 VirtualAPP 中打开百度 App, 可以看到如下输出: ...
  • virtualapp上如何捕捉安装里面app的日志和网络请求?
  • MFPlugin 参考@Replugin @VirtualApp @VirtualApk 实现一套动态加载框架
  • VirtualApp Java层Hook基础

    千次阅读 2017-07-13 12:29:15
    VirtualApp Java层Hook基础-反射注入Hook技术是VirtualApp(后续简称VA)的核心实现原理之一。0x00. 什么是Hook Hook是Windows中提供的一种用以替换DOS下“中断”的系统机制,中文译为“挂钩”或“钩子”。在对特定的...
  • Android虚拟化引擎VirtualApp探究

    千次阅读 2018-07-23 15:58:19
    摘要: 来一探Android虚拟化引擎VirtualApp的究竟! 介绍 首先需要说明的是,VirtualApp并不是前些阵子滴滴开源的插件化框架VirtualApk。 VirtualApp是一个更加黑科技的东西,他可以创建一个虚拟空间,你可以在...
  • AndroidHook机制——VirtualApp

    千次阅读 2018-08-15 21:32:24
    VirtualApp是一个开源的Android App虚拟化引擎,允许在其中创建虚拟空间,并在这个虚拟空间中运行其他应用。通过阅读源码及动态调试,基本了解了其运行原理,在此记录。 本质 Android应用隔离是基于Linux系统的多...
  • VirtualApp hook so及activity回调

    千次阅读 2018-08-22 11:35:24
    1. http://rk700.github.io/2017/03/15/virtualapp-basic/ 2. https://blog.csdn.net/ganyao939543405/article/details/76146760?ref=myread VA项目地址: https://github.com/asLody/VirtualApp IOUnifor...
  • VirtualApp加载apk文件的时候,读取APK的信息,修改读取到的versionName和versionCode 基本上可以实现版本更新失效。大多数APK文件是基于这两个属性判断
  • 研究virtualapp多开,写了一个安卓多开demo,发现多开的微信或者qq在部分机型上出现无法发送录音的问题,发送语音时, 提示没有给录音权限,但事实是给了录音以及读写的权限了, 求教大神
  • VirtualApp With Compatibility Of Android 10/11/12 VirtualApp 工程 全新体验,多种优化 特性 高性能、高稳定性、修复构建错误等等 本内核仅供开发人员参考,请勿构建成品并发表到任何地方 为了避免SB骚扰,请...
  • VirtualApp原理解析 初始化及注入流程 VirtualApp是一款可以达到应用双开效果的开源项目。VirtualApp整体流程,包含启动双开应用Activity的过程。
  • VirtualApp出现无法编译问题整理

    千次阅读 2016-08-25 15:37:49
    最近看到github上出现了VirtualApp 看了作者的介绍感觉很不错,然后就clone下来编译并研究一下,但是编译过程出现了如下错误提示,看着有些头疼 由于VirtualApp是需要将提供的android.jar和sdk里面的jar替换 这里就...
  • VirtualApp - 独立式 Resource hook

    千次阅读 2018-02-09 11:31:46
    基于之前对插件化的了解,大部分的插件对于资源都是合并式的,通过...VirtualApp中可以安装任意的第三方APK,如果采用合并式资源加载方案,那肯定也会有resource id冲突问题,但上面说的解决方案都需要第三方APK去...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 89,997
精华内容 35,998
关键字:

virtualapp