Android(安卓)双开沙箱 VirtualApp 源码分析(三)App 启动
上一章:Android 双开沙箱 VirtualApp 源码分析(二)
在这之前,我们还是要先了解一下 VA Client Framework 和 VAService 之间的通讯方式
VAService 与通讯
VAService
首先,VAService 是指 VA 仿造 Android 原生 framework 层 Service 实现的一套副本,举例有 VActivityManagerService,它和系统 AMS 一样,只不过他管理的是 VA 内部 Client App 的组件会话。
VAService 统一管理
首先所有 VAService 直接继承与 XXX.Stub,也就是 Binder,并且直接使用了一个 Map 储存在 VAService 进程空间中,并没有注册到系统 AMS 中,事实上在 VAService 进程中,每个 Service 都被当作一个普通对象 new 和 初始化。
最终,他们被添加到了 ServiceCache 中:
public class ServiceCache { private static final Map sCache = new ArrayMap<>(5); public static void addService(String name, IBinder service) { sCache.put(name, service); } public static IBinder removeService(String name) { return sCache.remove(name); } public static IBinder getService(String name) { return sCache.get(name); }}
这个 cache 很简单,就是一个 Map。
而被添加的时机则在 BinderProvider 的 onCreate() 回掉中:
@Override public boolean onCreate() { Context context = getContext(); // 这是一个空前台服务,目的是为了保活 VAService 进程,即 :x 进程 DaemonService.startup(context); if (!VirtualCore.get().isStartup()) { return true; } VPackageManagerService.systemReady(); addService(ServiceManagerNative.PACKAGE, VPackageManagerService.get()); VActivityManagerService.systemReady(context); addService(ServiceManagerNative.ACTIVITY, VActivityManagerService.get()); addService(ServiceManagerNative.USER, VUserManagerService.get()); VAppManagerService.systemReady(); addService(ServiceManagerNative.APP, VAppManagerService.get()); BroadcastSystem.attach(VActivityManagerService.get(), VAppManagerService.get()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { addService(ServiceManagerNative.JOB, VJobSchedulerService.get()); } VNotificationManagerService.systemReady(context); addService(ServiceManagerNative.NOTIFICATION, VNotificationManagerService.get()); VAppManagerService.get().scanApps(); VAccountManagerService.systemReady(); addService(ServiceManagerNative.ACCOUNT, VAccountManagerService.get()); addService(ServiceManagerNative.VS, VirtualStorageService.get()); addService(ServiceManagerNative.DEVICE, VDeviceManagerService.get()); return true; } private void addService(String name, IBinder service) { ServiceCache.addService(name, service); }
需要注意的是 DeamonService 是一个空前台服务,目的是为了保活 VAService 进程,即 :x 进程,因为现在后台服务很容易被杀,在 Android 8.0 以后后台服务只能在后台存活5S,而前台服务则不受影响。
ServiceFetcher
VA 设计了一个单独的 ServiceFetcher 服务用于向外部暴露 VAService 中的所有服务的 IBinder 句柄,而 ServiceFetcher 本身也是 Binder 服务,也就是说,ServiceFetcher 的 Ibinder 句柄是拿到其他 VAServic IBinder 的钥匙。
private class ServiceFetcher extends IServiceFetcher.Stub { @Override public IBinder getService(String name) throws RemoteException { if (name != null) { return ServiceCache.getService(name); } return null; } @Override public void addService(String name, IBinder service) throws RemoteException { if (name != null && service != null) { ServiceCache.addService(name, service); } } @Override public void removeService(String name) throws RemoteException { if (name != null) { ServiceCache.removeService(name); } } }
ServicecFetcher 自身的 IBnder 则通过 BinderProvicer 这个ContentProvider 暴露给其他进程:
@Override public Bundle call(String method, String arg, Bundle extras) { if ("@".equals(method)) { Bundle bundle = new Bundle(); BundleCompat.putBinder(bundle, "_VA_|_binder_", mServiceFetcher); return bundle; } return null; }
那么,在 Client App 中 VA Client 就可以通过 IServiceFetcher 这个 IBinder 拿到其他服务的 IBinder 了:
private static IServiceFetcher sFetcher; private static IServiceFetcher getServiceFetcher() { if (sFetcher == null || !sFetcher.asBinder().isBinderAlive()) { synchronized (ServiceManagerNative.class) { Context context = VirtualCore.get().getContext(); Bundle response = new ProviderCall.Builder(context, SERVICE_CP_AUTH).methodName("@").call(); if (response != null) { IBinder binder = BundleCompat.getBinder(response, "_VA_|_binder_"); linkBinderDied(binder); sFetcher = IServiceFetcher.Stub.asInterface(binder); } } } return sFetcher; } // 返回服务的 IBinder 句柄 public static IBinder getService(String name) { // 如果是本地服务,直接本地返回 if (VirtualCore.get().isServerProcess()) { return ServiceCache.getService(name); } // 通过 ServiceFetcher 的句柄找到远程 Service 的句柄 IServiceFetcher fetcher = getServiceFetcher(); if (fetcher != null) { try { return fetcher.getService(name); } catch (RemoteException e) { e.printStackTrace(); } } VLog.e(TAG, "GetService(%s) return null.", name); return null; }
启动 App
首先要了解的是 Android App 是组件化的,Apk 其实是 N 多个组件的集合,以及一些资源文件和 Assert,App 的启动有多种情况,只要在一个新的进程中调起了 apk 中任何一个组件,App 将被初始化,Application 将被初始化。
启动 Activity
我们先看启动 Activity 的情况:
Hook startActivity(重定位 Intent 到 StubActivity)
首先在 Client App 中,startActivity 方法必须被 Hook 掉,不然 Client App 调用 startActivity 就直指外部 Activity 去了。
这部分的原理其实与 DroidPlugin 大同小异,由于插件(Client App)中的 Activity 是没有在 AMS 中注册的,AMS 自然无法找到我们的插件 Activity。
Hook 的目的是我们拿到用户的 Intent,把他替换成指向 VA 在 Menifest 中站好坑的 StubActivity 的 Intent,然后将原 Intent 当作 data 打包进新 Intent 以便日后流程再次进入 VA 时恢复。
Hook 的方法就是用我们动态代理生成的代理类对象替换系统原来的 ActiityManagerNative.geDefault 对象。
@Override public void inject() throws Throwable { if (BuildCompat.isOreo()) { //Android Oreo(8.X) Object singleton = ActivityManagerOreo.IActivityManagerSingleton.get(); Singleton.mInstance.set(singleton, getInvocationStub().getProxyInterface()); } else { if (ActivityManagerNative.gDefault.type() == IActivityManager.TYPE) { ActivityManagerNative.gDefault.set(getInvocationStub().getProxyInterface()); } else if (ActivityManagerNative.gDefault.type() == Singleton.TYPE) { Object gDefault = ActivityManagerNative.gDefault.get(); Singleton.mInstance.set(gDefault, getInvocationStub().getProxyInterface()); } } BinderInvocationStub hookAMBinder = new BinderInvocationStub(getInvocationStub().getBaseInterface()); hookAMBinder.copyMethodProxies(getInvocationStub()); ServiceManager.sCache.get().put(Context.ACTIVITY_SERVICE, hookAMBinder); }
好了,下面只要调用到 startActivity 就会被 Hook 到 call。
这个函数需要注意以下几点:
1. VA 有意将安装和卸载 APP 的请求重定向到了卸载 VA 内部 APK 的逻辑。
2. resolveActivityInfo 调用到了 VPM 的 resolveIntent,最终会远程调用到 VPMS 的 resolveIntent,然后 VPMS 就会去查询 VPackage 找到目标 Activity 并将信息附加在 ResolveInfo 中返回 VPM。
3. 最后也是最重要的一点,startActivity 会调用到 VAM.startActivity,同样最终会远程调用到 VAMS 的 startActivity。
// Hook startActivity static class StartActivity extends MethodProxy { private static final String SCHEME_FILE = "file"; private static final String SCHEME_PACKAGE = "package"; @Override public String getMethodName() { return "startActivity"; } @Override public Object call(Object who, Method method, Object... args) throws Throwable { int intentIndex = ArrayUtils.indexOfObject(args, Intent.class, 1); if (intentIndex < 0) { return ActivityManagerCompat.START_INTENT_NOT_RESOLVED; } int resultToIndex = ArrayUtils.indexOfObject(args, IBinder.class, 2); String resolvedType = (String) args[intentIndex + 1]; Intent intent = (Intent) args[intentIndex]; intent.setDataAndType(intent.getData(), resolvedType); IBinder resultTo = resultToIndex >= 0 ? (IBinder) args[resultToIndex] : null; int userId = VUserHandle.myUserId(); if (ComponentUtils.isStubComponent(intent)) { return method.invoke(who, args); } // 请求安装和卸载界面 if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction()) || (Intent.ACTION_VIEW.equals(intent.getAction()) && "application/vnd.android.package-archive".equals(intent.getType()))) { if (handleInstallRequest(intent)) { return 0; } } else if ((Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction()) || Intent.ACTION_DELETE.equals(intent.getAction())) && "package".equals(intent.getScheme())) { if (handleUninstallRequest(intent)) { return 0; } } String resultWho = null; int requestCode = 0; Bundle options = ArrayUtils.getFirst(args, Bundle.class); if (resultTo != null) { resultWho = (String) args[resultToIndex + 1]; requestCode = (int) args[resultToIndex + 2]; } // chooser 调用选择界面 if (ChooserActivity.check(intent)) { intent.setComponent(new ComponentName(getHostContext(), ChooserActivity.class)); intent.putExtra(Constants.EXTRA_USER_HANDLE, userId); intent.putExtra(ChooserActivity.EXTRA_DATA, options); intent.putExtra(ChooserActivity.EXTRA_WHO, resultWho); intent.putExtra(ChooserActivity.EXTRA_REQUEST_CODE, requestCode); return method.invoke(who, args); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { args[intentIndex - 1] = getHostPkg(); } //解析 ActivityInfo ActivityInfo activityInfo = VirtualCore.get().resolveActivityInfo(intent, userId); if (activityInfo == null) { VLog.e("VActivityManager", "Unable to resolve activityInfo : " + intent); if (intent.getPackage() != null && isAppPkg(intent.getPackage())) { return ActivityManagerCompat.START_INTENT_NOT_RESOLVED; } return method.invoke(who, args); } // 调用远程 VAMS.startActivity int res = VActivityManager.get().startActivity(intent, activityInfo, resultTo, options, resultWho, requestCode, VUserHandle.myUserId()); if (res != 0 && resultTo != null && requestCode > 0) { VActivityManager.get().sendActivityResult(resultTo, resultWho, requestCode); } // 处理 Activity 切换动画,因为此时动画还是 Host 的 Stub Activity 默认动画,需要覆盖成子程序包的动画 if (resultTo != null) { ActivityClientRecord r = VActivityManager.get().getActivityRecord(resultTo); if (r != null && r.activity != null) { try { TypedValue out = new TypedValue(); Resources.Theme theme = r.activity.getResources().newTheme(); theme.applyStyle(activityInfo.getThemeResource(), true); if (theme.resolveAttribute(android.R.attr.windowAnimationStyle, out, true)) { TypedArray array = theme.obtainStyledAttributes(out.data, new int[]{ android.R.attr.activityOpenEnterAnimation, android.R.attr.activityOpenExitAnimation }); r.activity.overridePendingTransition(array.getResourceId(0, 0), array.getResourceId(1, 0)); array.recycle(); } } catch (Throwable e) { // Ignore } } } return res; } private boolean handleInstallRequest(Intent intent) { IAppRequestListener listener = VirtualCore.get().getAppRequestListener(); if (listener != null) { Uri packageUri = intent.getData(); if (SCHEME_FILE.equals(packageUri.getScheme())) { File sourceFile = new File(packageUri.getPath()); try { listener.onRequestInstall(sourceFile.getPath()); return true; } catch (RemoteException e) { e.printStackTrace(); } } } return false; } private boolean handleUninstallRequest(Intent intent) { IAppRequestListener listener = VirtualCore.get().getAppRequestListener(); if (listener != null) { Uri packageUri = intent.getData(); if (SCHEME_PACKAGE.equals(packageUri.getScheme())) { String pkg = packageUri.getSchemeSpecificPart(); try { listener.onRequestUninstall(pkg); return true; } catch (RemoteException e) { e.printStackTrace(); } } } return false; } }
逻辑最终走到 VAMS 后,VAMS 调用 ActivityStack.startActivityLocked
// 参考 framework 的实现 int startActivityLocked(int userId, Intent intent, ActivityInfo info, IBinder resultTo, Bundle options, String resultWho, int requestCode) { optimizeTasksLocked(); Intent destIntent; ActivityRecord sourceRecord = findActivityByToken(userId, resultTo); TaskRecord sourceTask = sourceRecord != null ? sourceRecord.task : null; // 忽略一大堆对 Flag 的处理 ............................. String affinity = ComponentUtils.getTaskAffinity(info); // 根据 Flag 寻找合适的 Task TaskRecord reuseTask = null; switch (reuseTarget) { case AFFINITY: reuseTask = findTaskByAffinityLocked(userId, affinity); break; case DOCUMENT: reuseTask = findTaskByIntentLocked(userId, intent); break; case CURRENT: reuseTask = sourceTask; break; default: break; } boolean taskMarked = false; if (reuseTask == null) { startActivityInNewTaskLocked(userId, intent, info, options); } else { boolean delivered = false; mAM.moveTaskToFront(reuseTask.taskId, 0); boolean startTaskToFront = !clearTask && !clearTop && ComponentUtils.isSameIntent(intent, reuseTask.taskRoot); if (clearTarget.deliverIntent || singleTop) { taskMarked = markTaskByClearTarget(reuseTask, clearTarget, intent.getComponent()); ActivityRecord topRecord = topActivityInTask(reuseTask); if (clearTop && !singleTop && topRecord != null && taskMarked) { topRecord.marked = true; } // Target activity is on top if (topRecord != null && !topRecord.marked && topRecord.component.equals(intent.getComponent())) { deliverNewIntentLocked(sourceRecord, topRecord, intent); delivered = true; } } if (taskMarked) { synchronized (mHistory) { scheduleFinishMarkedActivityLocked(); } } if (!startTaskToFront) { if (!delivered) { destIntent = startActivityProcess(userId, sourceRecord, intent, info); if (destIntent != null) { startActivityFromSourceTask(reuseTask, destIntent, info, resultWho, requestCode, options); } } } } return 0; }
然后 call 到了 startActivityProcess ,这就是真正替换 Intent 的地方:
// 使用 Host Stub Activity 的 Intent 包装原 Intent 瞒天过海 private Intent startActivityProcess(int userId, ActivityRecord sourceRecord, Intent intent, ActivityInfo info) { intent = new Intent(intent); // 获得 Activity 对应的 ProcessRecorder,如果没有则表示这是 Process 第一个打开的组件,需要初始化 Application ProcessRecord targetApp = mService.startProcessIfNeedLocked(info.processName, userId, info.packageName); if (targetApp == null) { return null; } Intent targetIntent = new Intent(); // 根据 Client App 的 PID 获取 StubActivity String stubActivityPath = fetchStubActivity(targetApp.vpid, info); Log.e("gy", "map activity:" + intent.getComponent().getClassName() + " -> " + stubActivityPath); targetIntent.setClassName(VirtualCore.get().getHostPkg(), stubActivityPath); ComponentName component = intent.getComponent(); if (component == null) { component = ComponentUtils.toComponentName(info); } targetIntent.setType(component.flattenToString()); StubActivityRecord saveInstance = new StubActivityRecord(intent, info, sourceRecord != null ? sourceRecord.component : null, userId); saveInstance.saveToIntent(targetIntent); return targetIntent; }
fetchStubActivity 会根据相同的进程 id 在 VA 的 Menifest 中找到那个提前占坑的 StubActivity:
// 获取合适的 StubActivity,返回 StubActivity 全限定名 private String fetchStubActivity(int vpid, ActivityInfo targetInfo) { boolean isFloating = false; boolean isTranslucent = false; boolean showWallpaper = false; try { int[] R_Styleable_Window = R_Hide.styleable.Window.get(); int R_Styleable_Window_windowIsTranslucent = R_Hide.styleable.Window_windowIsTranslucent.get(); int R_Styleable_Window_windowIsFloating = R_Hide.styleable.Window_windowIsFloating.get(); int R_Styleable_Window_windowShowWallpaper = R_Hide.styleable.Window_windowShowWallpaper.get(); AttributeCache.Entry ent = AttributeCache.instance().get(targetInfo.packageName, targetInfo.theme, R_Styleable_Window); if (ent != null && ent.array != null) { showWallpaper = ent.array.getBoolean(R_Styleable_Window_windowShowWallpaper, false); isTranslucent = ent.array.getBoolean(R_Styleable_Window_windowIsTranslucent, false); isFloating = ent.array.getBoolean(R_Styleable_Window_windowIsFloating, false); } } catch (Throwable e) { e.printStackTrace(); } boolean isDialogStyle = isFloating || isTranslucent || showWallpaper; // 根据在 Menifest 中注册的 pid if (isDialogStyle) { return VASettings.getStubDialogName(vpid); } else { return VASettings.getStubActivityName(vpid); } }
这里需要特别注意,VA 占坑的方式和 DroidPlugin 有些小不同,VA 没有为每个 Process 注册多个 Activity,也没有为不同的启动方式注册多个 Activity,这里确实是有改进的。
这里根本原因是因为 VA 对 VAMS 实现的更为完整,实现了原版 AMS 的基本功能,包括完整的 Recorder 管理,Task Stack 管理等,这样的话 StubActivity 的唯一作用便是携带 Client App 真正的 Intent 交给 VAMS 处理。这套机制衍生到其他的组件也是一样的。
可以简单看一下 ActivityStack, ActivityRecorder,ActivityRecord
class ActivityStack { private final ActivityManager mAM; private final VActivityManagerService mService; /** * [Key] = TaskId [Value] = TaskRecord */ private final SparseArray mHistory = new SparseArray<>();
class TaskRecord { public final List activities = Collections.synchronizedList(new ArrayList()); public int taskId; public int userId; public String affinity; public Intent taskRoot;
/* package */ class ActivityRecord { public TaskRecord task; public ComponentName component; public ComponentName caller; // Client App 中 Activity 的句柄 public IBinder token; public int userId; public ProcessRecord process; public int launchMode; public int flags; public boolean marked; public String affinity;
StubActivityRecorder:
public class StubActivityRecord { public Intent intent; public ActivityInfo info; public ComponentName caller; public int userId; public StubActivityRecord(Intent intent, ActivityInfo info, ComponentName caller, int userId) { this.intent = intent; this.info = info; this.caller = caller; this.userId = userId; } // 获取原版 Intent 和一些其他信息 public StubActivityRecord(Intent stub) { this.intent = stub.getParcelableExtra("_VA_|_intent_"); this.info = stub.getParcelableExtra("_VA_|_info_"); this.caller = stub.getParcelableExtra("_VA_|_caller_"); this.userId = stub.getIntExtra("_VA_|_user_id_", 0); } // 将原版 Intent 塞到 Stub Intent public void saveToIntent(Intent stub) { stub.putExtra("_VA_|_intent_", intent); stub.putExtra("_VA_|_info_", info); stub.putExtra("_VA_|_caller_", caller); stub.putExtra("_VA_|_user_id_", userId); }}
初始化 Application
还有一个非常重要的事情,注意到这一行:
// 获得 Activity 对应的 ProcessRecorder,如果没有则表示这是 Process 第一个打开的组件,需要初始化 Application ProcessRecord targetApp = mService.startProcessIfNeedLocked(info.processName, userId, info.packageName);
这里会先去找对应 Client App 进程的 ProcessRecorder,找不到代表 Application 刚启动尚未初始化:
private ProcessRecord performStartProcessLocked(int vuid, int vpid, ApplicationInfo info, String processName) { ProcessRecord app = new ProcessRecord(info, processName, vuid, vpid); Bundle extras = new Bundle(); BundleCompat.putBinder(extras, "_VA_|_binder_", app); extras.putInt("_VA_|_vuid_", vuid); extras.putString("_VA_|_process_", processName); extras.putString("_VA_|_pkg_", info.packageName); // 调用子程序包的 init_process 方法,并且得到子程序包 IBinder 句柄 Bundle res = ProviderCall.call(VASettings.getStubAuthority(vpid), "_VA_|_init_process_", null, extras); if (res == null) { return null; } int pid = res.getInt("_VA_|_pid_"); IBinder clientBinder = BundleCompat.getBinder(res, "_VA_|_client_"); // attach 到 Client 的 VAM , 将子程序句柄推入服务端 VAMS attachClient(pid, clientBinder); return app; }
ProviderCall.call 向 Client App 的 StubContentProvider 发起远程调用:
@Override public Bundle call(String method, String arg, Bundle extras) { if ("_VA_|_init_process_".equals(method)) { return initProcess(extras); } return null; } private Bundle initProcess(Bundle extras) { ConditionVariable lock = VirtualCore.get().getInitLock(); if (lock != null) { lock.block(); } IBinder token = BundleCompat.getBinder(extras,"_VA_|_binder_"); int vuid = extras.getInt("_VA_|_vuid_"); VClientImpl client = VClientImpl.get(); client.initProcess(token, vuid); Bundle res = new Bundle(); BundleCompat.putBinder(res, "_VA_|_client_", client.asBinder()); res.putInt("_VA_|_pid_", Process.myPid()); return res; }
Client App 的 IBinder 句柄(VClientImpl.asBinder) 被打包在了 Bundle 中返回给 VAMS。
最终, VAMS 调用原生 AM 的 startActivity 向真正的 AMS 发送替换成 StubActivity 的伪造 Intent。
mirror.android.app.IActivityManager.startActivity.call(ActivityManagerNative.getDefault.call(), (Object[]) args);
恢复原 Intent 重定向到原 Activity
当 AMS 收到伪装的 Intent 后,就会找到 StubActivity,这时流程回到 VA 里的主线程中的消息队列中。
Hook 过程就是用我们自己的 Handler 替换 android.os.Handler.mCallback 因为主线程在这里分发一些操作:
@Override public void inject() throws Throwable { otherCallback = getHCallback(); mirror.android.os.Handler.mCallback.set(getH(), this); }
handlerMessage 判断是 LAUNCH_ACTIVITY Action 后直接调用了 handlerLaunchActivity 方法,和原版其实很像。
private boolean handleLaunchActivity(Message msg) { Object r = msg.obj; Intent stubIntent = ActivityThread.ActivityClientRecord.intent.get(r); // 获取原版 Intent 信息 StubActivityRecord saveInstance = new StubActivityRecord(stubIntent); if (saveInstance.intent == null) { return true; } // 原版 Intent Intent intent = saveInstance.intent; ComponentName caller = saveInstance.caller; IBinder token = ActivityThread.ActivityClientRecord.token.get(r); ActivityInfo info = saveInstance.info; // 如果 token 还没初始化,代表 App 刚刚启动第一个组件 if (VClientImpl.get().getToken() == null) { VActivityManager.get().processRestarted(info.packageName, info.processName, saveInstance.userId); getH().sendMessageAtFrontOfQueue(Message.obtain(msg)); return false; } // AppBindData 为空,则 App 信息不明 if (!VClientImpl.get().isBound()) { // 初始化并绑定 Application VClientImpl.get().bindApplication(info.packageName, info.processName); getH().sendMessageAtFrontOfQueue(Message.obtain(msg)); return false; } // 获取 TaskId int taskId = IActivityManager.getTaskForActivity.call( ActivityManagerNative.getDefault.call(), token, false ); // 1.将 ActivityRecorder 加入 mActivities 2.通知服务端 VAMS Activity 创建完成 VActivityManager.get().onActivityCreate(ComponentUtils.toComponentName(info), caller, token, info, intent, ComponentUtils.getTaskAffinity(info), taskId, info.launchMode, info.flags); ClassLoader appClassLoader = VClientImpl.get().getClassLoader(info.applicationInfo); intent.setExtrasClassLoader(appClassLoader); // 将 Host Stub Activity Intent 替换为原版 Intent ActivityThread.ActivityClientRecord.intent.set(r, intent); // 同上 ActivityThread.ActivityClientRecord.activityInfo.set(r, info); return true; }
需要注意的是,如果这个 Activity 是这个 Apk 启动的第一个组件,则需要 bindApplication 初始化 Application 操作:
private void bindApplicationNoCheck(String packageName, String processName, ConditionVariable lock) { mTempLock = lock; try { // 设置未捕获异常的 Callback setupUncaughtHandler(); } catch (Throwable e) { e.printStackTrace(); } try { // 修复 Provider 信息 fixInstalledProviders(); } catch (Throwable e) { e.printStackTrace(); } mirror.android.os.Build.SERIAL.set(deviceInfo.serial); mirror.android.os.Build.DEVICE.set(Build.DEVICE.replace(" ", "_")); ActivityThread.mInitialApplication.set( VirtualCore.mainThread(), null ); // 从 VPMS 获取 apk 信息 AppBindData data = new AppBindData(); InstalledAppInfo info = VirtualCore.get().getInstalledAppInfo(packageName, 0); if (info == null) { new Exception("App not exist!").printStackTrace(); Process.killProcess(0); System.exit(0); } // dex 优化的开关,dalvik 和 art 处理不同 if (!info.dependSystem && info.skipDexOpt) { VLog.d(TAG, "Dex opt skipped."); if (VirtualRuntime.isArt()) { ARTUtils.init(VirtualCore.get().getContext()); ARTUtils.setIsDex2oatEnabled(false); } else { DalvikUtils.init(); DalvikUtils.setDexOptMode(DalvikUtils.OPTIMIZE_MODE_NONE); } } data.appInfo = VPackageManager.get().getApplicationInfo(packageName, 0, getUserId(vuid)); data.processName = processName; data.providers = VPackageManager.get().queryContentProviders(processName, getVUid(), PackageManager.GET_META_DATA); Log.i(TAG, "Binding application " + data.appInfo.packageName + " (" + data.processName + ")"); mBoundApplication = data; // 主要设置进程的名字 VirtualRuntime.setupRuntime(data.processName, data.appInfo); int targetSdkVersion = data.appInfo.targetSdkVersion; if (targetSdkVersion < Build.VERSION_CODES.GINGERBREAD) { StrictMode.ThreadPolicy newPolicy = new StrictMode.ThreadPolicy.Builder(StrictMode.getThreadPolicy()).permitNetwork().build(); StrictMode.setThreadPolicy(newPolicy); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (mirror.android.os.StrictMode.sVmPolicyMask != null) { mirror.android.os.StrictMode.sVmPolicyMask.set(0); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) { mirror.android.os.Message.updateCheckRecycle.call(targetSdkVersion); } if (VASettings.ENABLE_IO_REDIRECT) { // IO 重定向 startIOUniformer(); } // hook native 函数 NativeEngine.hookNative(); Object mainThread = VirtualCore.mainThread(); // 准备 dex 列表 NativeEngine.startDexOverride(); // 获得子 pkg 的 Context 前提是必须在系统中安装的(疑问?) Context context = createPackageContext(data.appInfo.packageName); // 设置虚拟机系统环境 临时文件夹 codeCacheDir System.setProperty("java.io.tmpdir", context.getCacheDir().getAbsolutePath()); // oat 的 cache 目录 File codeCacheDir; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { codeCacheDir = context.getCodeCacheDir(); } else { codeCacheDir = context.getCacheDir(); } // 硬件加速的 cache 目录 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { if (HardwareRenderer.setupDiskCache != null) { HardwareRenderer.setupDiskCache.call(codeCacheDir); } } else { if (ThreadedRenderer.setupDiskCache != null) { ThreadedRenderer.setupDiskCache.call(codeCacheDir); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (RenderScriptCacheDir.setupDiskCache != null) { RenderScriptCacheDir.setupDiskCache.call(codeCacheDir); } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { if (RenderScript.setupDiskCache != null) { RenderScript.setupDiskCache.call(codeCacheDir); } } // 修复子 App 中 ActivityThread.AppBinderData 的参数,因为之前用的是在 Host 程序中注册的 Stub 的信息 Object boundApp = fixBoundApp(mBoundApplication); mBoundApplication.info = ContextImpl.mPackageInfo.get(context); mirror.android.app.ActivityThread.AppBindData.info.set(boundApp, data.info); // 同样修复 targetSdkVersion 原来也是可 Host 程序一样的 VMRuntime.setTargetSdkVersion.call(VMRuntime.getRuntime.call(), data.appInfo.targetSdkVersion); boolean conflict = SpecialComponentList.isConflictingInstrumentation(packageName); if (!conflict) { InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class); } // 开始构建子程序包的 Application 对象,并且替换原来通过 Host Stub 生成的 mInitialApplication mInitialApplication = LoadedApk.makeApplication.call(data.info, false, null); mirror.android.app.ActivityThread.mInitialApplication.set(mainThread, mInitialApplication); ContextFixer.fixContext(mInitialApplication); if (data.providers != null) { // 注册 Providers installContentProviders(mInitialApplication, data.providers); } // 初始化锁开,异步调用的初始化函数可以返回了 if (lock != null) { lock.open(); mTempLock = null; } try { // 调用 Application.onCreate mInstrumentation.callApplicationOnCreate(mInitialApplication); InvocationStubManager.getInstance().checkEnv(HCallbackStub.class); if (conflict) { InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class); } Application createdApp = ActivityThread.mInitialApplication.get(mainThread); if (createdApp != null) { mInitialApplication = createdApp; } } catch (Exception e) { if (!mInstrumentation.onException(mInitialApplication, e)) { throw new RuntimeException( "Unable to create application " + mInitialApplication.getClass().getName() + ": " + e.toString(), e); } } VActivityManager.get().appDoneExecuting(); } private void setupUncaughtHandler() { ThreadGroup root = Thread.currentThread().getThreadGroup(); while (root.getParent() != null) { root = root.getParent(); } ThreadGroup newRoot = new RootThreadGroup(root); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { final List groups = mirror.java.lang.ThreadGroup.groups.get(root); //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (groups) { List newGroups = new ArrayList<>(groups); newGroups.remove(newRoot); mirror.java.lang.ThreadGroup.groups.set(newRoot, newGroups); groups.clear(); groups.add(newRoot); mirror.java.lang.ThreadGroup.groups.set(root, groups); for (ThreadGroup group : newGroups) { mirror.java.lang.ThreadGroup.parent.set(group, newRoot); } } } else { final ThreadGroup[] groups = ThreadGroupN.groups.get(root); //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (groups) { ThreadGroup[] newGroups = groups.clone(); ThreadGroupN.groups.set(newRoot, newGroups); ThreadGroupN.groups.set(root, new ThreadGroup[]{newRoot}); for (Object group : newGroups) { ThreadGroupN.parent.set(group, newRoot); } ThreadGroupN.ngroups.set(root, 1); } } }
bindApplication 主要做了以下几个事情:
1. 从 VPMS 获取 APK 的信息,根据设置控制 Dex 优化的开关。
2. 调用 mirror.android.os.Process.setArgV0.call(processName); 设置进程的名称,如果不设置则还是 p0 p1 这样。
3. 做 nativeHook 主要 Hook 一些 native 的函数,主要是一些 IO 函数,包括文件访问重定向等等。
4. 准备一些 cache 临时文件夹。
5. 设置 AppBinderData,AppBinderData 内部包含了 ApplicationInfo 和 provider 信息等重要的 apk 信息。可以理解为 framework 所需要的关键数据结构。
6. 安装 ContentProvider
7. 初始化用户的 Application 对象,并通过 Instrumentation 调用其 onCreate,代表着 Client App 的生命周期正式开始。
最后成功从 StubActivity Intent 还原出来的原版 Intent 被继续交给原生的 AM:
// 将 Host Stub Activity Intent 替换为原版 IntentActivityThread.ActivityClientRecord.intent.set(r, intent);// 同上ActivityThread.ActivityClientRecord.activityInfo.set(r, info);
最后,最后一个 Hook 点在 Instrumentation.callActivityOnCreate:
因为 AMS 实际上启动的是 StubActivity 的关系,真正的 Activity 的一些信息还不是其真正的信息,比如主题之类的,所以需要在这个时机修复一下,选择这个时间修复的原因也是因为 Activity 已经被 new 出来了,而且资源已经准备完毕。
public void callActivityOnCreate(Activity activity, Bundle icicle) { VirtualCore.get().getComponentDelegate().beforeActivityCreate(activity); IBinder token = mirror.android.app.Activity.mToken.get(activity); ActivityClientRecord r = VActivityManager.get().getActivityRecord(token); // 替换 Activity 对象 if (r != null) { r.activity = activity; } ContextFixer.fixContext(activity); ActivityFixer.fixActivity(activity); ActivityInfo info = null; if (r != null) { info = r.info; } // 设置主题和屏幕纵横控制 if (info != null) { if (info.theme != 0) { activity.setTheme(info.theme); } if (activity.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED && info.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { activity.setRequestedOrientation(info.screenOrientation); } } super.callActivityOnCreate(activity, icicle); VirtualCore.get().getComponentDelegate().afterActivityCreate(activity); }
下一章介绍 Service 的代理。 Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service
更多相关文章
- android 横竖屏
- Android中外部程序调用方法总结
- android:configChanges属性
- Android之Bluetooth
- Android应用程序启动过程源代码分析
- Android四大基本组件介绍与生命周期
- Android深入浅出之Binder机制
- 打包下载Android源码
- Android(安卓)Scroller、VelocityTracker