android IO流_Android虚拟化引擎VirtualApp探究

首先需要说明的是,VirtualApp并不是那个滴滴开源的插件化框架VirtualApk。

VirtualApp是一个更加黑科技的东西,他可以创建一个虚拟空间,你可以在虚拟空间内任意的安装、启动和卸载APK,如同一个沙盒与外部隔离,APK无需在手机上真正安装。

小试牛刀

启动VirtualApp后,界面是这样的。

6966d080-9714-eb11-8da9-e4434bdf6706.png

显示的是已经通过VirtualApp安装的APK,可以直接从SD卡或者系统中已有的APK中选择安装。安装后直接点开图标,就能跟安装在外部的应用一样打开APP。简单尝试了一下,Nexus 6P,Android 7.0,知乎和微博都能正常工作,并且运行速度跟外部安装的差异不大。而且还可以安装多个相同的应用,实现多开的效果。

如图,我们开启了两个知乎APP,第二个显示为[Space2]:

6b66d080-9714-eb11-8da9-e4434bdf6706.png

粗略观察

首先,我们来看一下它在开启APP后的进程信息,

u0_a200   22932 494   1034396 84008 SyS_epoll_ 0000000000 S io.virtualapp
u0_a200 22955 494 1064388 70408 SyS_epoll_ 0000000000 S io.virtualapp:x
u0_a200 22983 494 1530416 266948 0000000000 R com.zhihu.android
u0_a200 23320 494 1410736 214680 SyS_epoll_ 0000000000 S com.sina.weibo
u0_a200 23387 494 1174928 76848 SyS_epoll_ 0000000000 S com.sina.weibo.image
u0_a200 23415 494 1186076 81648 SyS_epoll_ 0000000000 S com.sina.weibo:remote
u0_a200 23455 494 1173888 76572 SyS_epoll_ 0000000000 S com.sina.weibo.imageservant
u0_a200 24028 494 1182780 74408 SyS_epoll_ 0000000000 S com.sina.weibo.servant
u0_a200 24425 494 1027636 66116 SyS_epoll_ 0000000000 S com.test
u0_a200 24492 494 1334412 174708 SyS_epoll_ 0000000000 S com.zhihu.android

可以看到,所有被ViralApp打开的应用,都和VirtalApp属于同一个uid:u0_a200。其中,VirtualApp本身有两个进程:io.virtualapp 和 io.virtualapp:x

io.virtualapp 就是可见的交互界面,同时也负责APK包的管理和安装。io.virtualapp:x 作为一个单独的服务进程,虚拟了一些系统服务。后面我们还会提到。

以这里安装的微博为例,查看一下它的进程的内存空间,可以看到相关路径全都被映射到了/data/data/io.virtualapp/virtual下面,

... ...
b6d0f000-b7017000 r--p 00000000 fd:00 410335 /data/data/io.virtualapp/virtual/data/user/0/com.sina.weibo/Plugin/com.weibo.app.movie/dalvik-cache/base-1.dex
b7017000-b71d4000 r-xp 00308000 fd:00 410335 /data/data/io.virtualapp/virtual/data/user/0/com.sina.weibo/Plugin/com.weibo.app.movie/dalvik-cache/base-1.dex
... ...
bb745000-bb831000 r--p 00000000 fd:00 410247 /data/data/io.virtualapp/virtual/data/user/0/com.sina.weibo/code_cache/secondary-dexes/composer1312fd1cbada0e5074c9f9961b16aefb.dex
bb831000-bb8f0000 r-xp 000ec000 fd:00 410247 /data/data/io.virtualapp/virtual/data/user/0/com.sina.weibo/code_cache/secondary-dexes/composer1312fd1cbada0e5074c9f9961b16aefb.dex
... ...
bf448000-bf978000 r-xp 00000000 fd:00 410129 /data/data/io.virtualapp/virtual/data/app/com.sina.weibo/lib/libweiboffmpeg.so
bf978000-bf979000 ---p 00000000 00:00 0
bf979000-bf9ab000 r--p 00530000 fd:00 410129 /data/data/io.virtualapp/virtual/data/app/com.sina.weibo/lib/libweiboffmpeg.so
bf9ab000-bf9af000 rw-p 00562000 fd:00 410129 /data/data/io.virtualapp/virtual/data/app/com.sina.weibo/lib/libweiboffmpeg.so
... ...
c335a000-c33a9000 r-xp 00000000 fd:00 410127 /data/data/io.virtualapp/virtual/data/app/com.sina.weibo/lib/libweiboplayer.so
c33aa000-c33ad000 r--p 0004f000 fd:00 410127 /data/data/io.virtualapp/virtual/data/app/com.sina.weibo/lib/libweiboplayer.so
c33ad000-c33ae000 rw-p 00052000 fd:00 410127 /data/data/io.virtualapp/virtual/data/app/com.sina.weibo/lib/libweiboplayer.so
... ...

可见,这里面对路径做过了重新映射。具体实现我们还是来看一下代码吧。

注入逻辑

要想实现对一个APP的虚拟化,就是不直接把APP安装进系统,同时又要提供APP运行过程中所需的一切,从而可以让它误以为自己是运行在正常系统中。这里就需要实现系统服务的虚拟化和相关路径的虚拟化。

其中,系统服务的虚拟化主要靠注入大量framework组件来实现的。

// @VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java

这个注入过程是发生在io.virtualapp.VApp.attachBaseContext中,因此,每次启动一个子进程都会执行到这里,这会区分是isMainProcess(io.virtualapp)或者isServerProcess(io.virtualapp:x)或者isVAppProcess(被安装APP)来进行不同的注入,可以看到,注入最多的还是在被安装APP的进程中。

可以看到,之前在injectInternaladdInjector的所有Stub都会调用它的inject方法。

// @VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java

由此实现对各个系统类的替换。

而在底层,VirtualApp还实现了对原本路径的替换,在java层传入需要重定向的所有路径

private void startIOUniformer() {

这些路径最终会添加进JNI层的一个映射表中

void IOUniformer::redirect(

然后,会hook所有的C标准库函数,这些函数在调用的时候,就会替换路径为新路径。由于hook的是libc的函数,java层和虚拟机的文件访问最终也会调用到这里,从而受到影响。

void IOUniformer::startUniformer(

以chmod函数为例,

// int chmod(const char *path, mode_t mode);

可以看到,它会把原先的pathname,通过match_redirected_path找到映射后的新路径,然后用syscall来调用它,这样就实现了所有路径的重定向。

启动原理

最后我们来看下,一个APP是如何在VirtualApp里启动的。

启动一个app是在LoadingActivitylaunch方法

public static void launch(Context context, String packageName, int userId) {

然后会调用到VActivityManagerstartActivity

public int startActivity(Intent intent, ActivityInfo info, IBinder resultTo, Bundle options, String resultWho, int requestCode, int userId) {

这里的service会通过binder最终找到io.virtualapp:x进程的VActivityManagerService

@Override

Service端的startActivity会调用ActivityStackstartActivityLocked,然后调用到startActivityInNewTaskLocked方法

private void startActivityInNewTaskLocked(int userId, Intent intent, ActivityInfo info, Bundle options) {

这里的intent会在startActivityProcess的时候进行替换

private Intent startActivityProcess(int userId, ActivityRecord sourceRecord, Intent intent, ActivityInfo info) {

targetIntent.setClassName会设置为VirtualApp的包名,同时fetchStubActivity用来获取插桩Activity

private String fetchStubActivity(int vpid, ActivityInfo targetInfo) {

这里最终返回的是VASettings.getStubActivityName

public 

可见,最终返回的Activity名是com.lody.virtual.client.stub.StubActivity$C??表示具体数字。

而它们,是预先在AndroidManifest里面写好的。

<activityandroid:name="com.lody.virtual.client.stub.StubActivity$C0"android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale"android:process=":p0"android:taskAffinity="com.lody.virtual.vt"android:theme="@style/VATheme" />

这么一来,startActivity最终会启动到这里的StubActivity中。

并且每次都会新建一个子进程p?,在开启一个进程时,都会先执行到io.virtualapp.VApp.attachBaseContext中,这样,就会走到刚才提到的injectInternal方法中,实现所有注入逻辑,把所有与系统交互的地方都进行替换。而StubActivityonCreate的方法,虽然在代码里面有声明,却永远执行不到:

public 

在替换后,执行流就被改变了,可以看到,在执行到APP的真正Application前,能发现被注入的代码:

com.test.MyApplication.onCreate(MyApplication.java:33)
android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1024)
com.lody.virtual.client.hook.delegate.InstrumentationDelegate.callApplicationOnCreate(InstrumentationDelegate.java:225)
com.lody.virtual.client.hook.delegate.AppInstrumentation.callApplicationOnCreate(AppInstrumentation.java:137)
com.lody.virtual.client.VClientImpl.bindApplicationNoCheck(VClientImpl.java:312)
com.lody.virtual.client.VClientImpl.bindApplication(VClientImpl.java:192)
com.lody.virtual.client.hook.proxies.am.HCallbackStub.handleLaunchActivity(HCallbackStub.java:114)
com.lody.virtual.client.hook.proxies.am.HCallbackStub.handleMessage(HCallbackStub.java:71)
android.os.Handler.dispatchMessage(Handler.java:98)
android.os.Looper.loop(Looper.java:154)
android.app.ActivityThread.main(ActivityThread.java:6077)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

思考

我们已经大致分析了VirtualApp的工作原理,当然里面有非常多细节没有覆盖到,有兴趣的同学可以自己研究一下。

VirtualApp可以加载外部APK的特性,能够引发无穷的想象。像一些简单的安全脱壳,就可以用VirtualApp先加载APK,然后在运行期间直接把内存中脱壳后的DEX文件dump出来。

另外也可以做一些有趣的尝试。比如,由于阿里Sophix热修复方案也是非侵入的,如果把Sophix集成进VirtualApp中,就能够在启动APP前加载一个补丁,替换原有APP中的某些类,实现不重新打包原有APK的情况下对原先APP的逻辑的修改。


6d66d080-9714-eb11-8da9-e4434bdf6706.jpeg

长按关注「安卓尖端技术研究

带给你不同安卓技术开发视角


往期推荐      

 Android Native禁止使用系统私有库详解 Android App Bundles实现原理与插件化未来之路
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
简介VirtualApp是一个App虚拟化引擎(简称VA)。VirtualApp已兼容Android 0(8.0 Preview)。VirtualApp在你的App内创建一个虚拟空间,你可以在虚拟空间内任意的安装、启动和卸载APK,这一切都与外部隔离,如同一个沙盒。运行在VA中的APK无需在外部安装,即VA支持免安装运行APK。VA目前被广泛应用于双开/多开、应用市场、模拟定位、一键改机、隐私保护、游戏修改、自动化测试、无感知热更新等技术领域,但它决不仅限于此,Android本身就是一个极其开放的平台,免安装运行APK这一Feature打开了无限可能--------这都取决于您的想象力。申明当您需要将VirtualApp用于商业用途时,请务必联系QQ:10890 购买商业授权。您如果未经授权将VirtualAppApp模块作为您自己的App用于牟利或上传软件市场,我们取证后将直接报警(侵犯著作权罪)。购买商业授权是对我们最大的支持和认可,我们将投入更多精力和时间来不断完善优化VirtualApp,作为购买商业授权的回报,您可以获得未开放的商业版本和1vs1的支持(技术、运营、预警)!同时我们也支持基于VirtualAppAPP订制开发,请联系:QQ:10890 洽谈。请注意VirtualApp代码的更新频率非常快(以小时为单位),每一次代码的更新都有可能修复重大BUG,所以请 watch 本项目,并注意随时更新代码,以免给您带来损失!已支持的加固(不断更新)360加固腾讯加固梆梆加固梆梆企业版(12306客户端 Pass)爱加密百度加固娜迦加固乐变加固网易易盾通付盾(已支持的加固均可通过VA来脱壳,本技术不公开)在VA使用Google服务VA支持运行官方的Google服务套件,同时我们也提供了对MicroG的支持。您可以通过在VA中安装MicroG来支持Google服务,这样,即使外部没有Google服务,用户也可以在VA中享受Google服务。MicroG套件可在此下载:Download MicroGMicroG的必要模块:Services CoreServices Framework ProxyStore如果您需要在VA中使用官方的Google服务套件(外部已安装的前提下),则可以通过 GmsSupport.installGms(userId) 来安装。注意,您不能同时安装MicroGms和官方的Gms。使用说明前往你的Application并添加如下代码:@Override     protected void attachBaseContext(Context base) {         super.attachBaseContext(base);         try {             VirtualCore.getCore().startup(base);         } catch (Throwable e) {             e.printStackTrace();         }     }安装App:VirtualCore.getCore().installApp({APK PATH}, flags);启动App:VirtualCore.getCore().uninstallApp({PackageName});该App的基本信息:VirtualCore.getCore().findApp({PackageName});

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值