标题:
VirtualApp沙盒基本原理
[打印本页]
作者:
look_w
时间:
2017-11-18 20:52
标题:
VirtualApp沙盒基本原理
本质
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_a60u0_a60 2385 1258 996260 54456 SyS_epoll_ 00000000 S io.virtualappu0_a60 2412 1258 980940 48272 SyS_epoll_ 00000000 S io.virtualapp:xu0_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
);
}
...
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0