轉自: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.HookHandler
的invoke()
方法中,查找表 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時,便會執行HookHandler
的invoke()
方法,進而查找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等高級功能。