Android 双开沙箱 VirtualApp 源码分析(二)
上一章:Android 双开沙箱 VirtualApp 源码分析(一)
VA 初始化
先看一下代码:
VirtualCore.startup
public void startup(Context context) throws Throwable { if (!isStartUp) { // 确保 MainThread if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("VirtualCore.startup() must called in main thread."); } VASettings.STUB_CP_AUTHORITY = context.getPackageName() + "." + VASettings.STUB_DEF_AUTHORITY; ServiceManagerNative.SERVICE_CP_AUTH = context.getPackageName() + "." + ServiceManagerNative.SERVICE_DEF_AUTH; this.context = context; // 获取 ActivityThread 实例 mainThread = ActivityThread.currentActivityThread.call(); unHookPackageManager = context.getPackageManager(); hostPkgInfo = unHookPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS); detectProcessType(); // hook 系统类 InvocationStubManager invocationStubManager = InvocationStubManager.getInstance(); invocationStubManager.init(); invocationStubManager.injectAll(); // 修复权限管理 ContextFixer.fixContext(context); isStartUp = true; if (initLock != null) { initLock.open(); initLock = null; } } }
整个 VA 会运行在四种进程
private enum ProcessType { /** * Server process */ Server, /** * Virtual app process */ VAppClient, /** * Main process */ Main, /** * Child process */ CHILD }
分别是前面提到的 VAService 进程,Client App 进程,VA 自身的 App 主进程,子进程。
这样的话,Application 就会被初始化多次,所以要在初始化的时候根据进程类型有选择的做对应的初始化工作。
InvocationStubManager.injectInternal():
主要完成对 Java 层 framework 的 Hook,将其定位到 VA 伪造 VA framework 上去。
private void injectInternal() throws Throwable { // VA 自身的 App 进程不需要 Hook if (VirtualCore.get().isMainProcess()) { return; } // VAService 需要 Hook AMS 和 PMS if (VirtualCore.get().isServerProcess()) { addInjector(new ActivityManagerStub()); addInjector(new PackageManagerStub()); return; } // Client APP 需要 Hook 整个 framework,来使其调用到 VA framework if (VirtualCore.get().isVAppProcess()) { addInjector(new LibCoreStub()); addInjector(new ActivityManagerStub()); addInjector(new PackageManagerStub()); addInjector(HCallbackStub.getDefault()); addInjector(new ISmsStub()); addInjector(new ISubStub()); addInjector(new DropBoxManagerStub()); .....................
VA 初始化主要就是这些
Client App 的安装
VirtualCore.installPackage
public InstallResult installPackage(String apkPath, int flags) { try { // 调用远程 VAService return getService().installPackage(apkPath, flags); } catch (RemoteException e) { return VirtualRuntime.crash(e); } }
最终调用 VAServcie 中的 VAppManagerService.installPackage():
// 安装 apk 先于 installPackageAsUser,主要目的是生成 VPackage 结构 public synchronized InstallResult installPackage(String path, int flags, boolean notify) { long installTime = System.currentTimeMillis(); if (path == null) { return InstallResult.makeFailure("path = NULL"); } // 是否 OPT 优化(dex -> binary) boolean skipDexOpt = (flags & InstallStrategy.SKIP_DEX_OPT) != 0; // apk path File packageFile = new File(path); if (!packageFile.exists() || !packageFile.isFile()) { return InstallResult.makeFailure("Package File is not exist."); } VPackage pkg = null; try { // 进入解析包结构,该结构是可序列化的,为了持久化在磁盘上 pkg = PackageParserEx.parsePackage(packageFile); } catch (Throwable e) { e.printStackTrace(); } if (pkg == null || pkg.packageName == null) { return InstallResult.makeFailure("Unable to parse the package."); } InstallResult res = new InstallResult(); res.packageName = pkg.packageName; // PackageCache holds all packages, try to check if we need to update. VPackage existOne = PackageCacheManager.get(pkg.packageName); PackageSetting existSetting = existOne != null ? (PackageSetting) existOne.mExtras : null; if (existOne != null) { if ((flags & InstallStrategy.IGNORE_NEW_VERSION) != 0) { res.isUpdate = true; return res; } if (!canUpdate(existOne, pkg, flags)) { return InstallResult.makeFailure("Not allowed to update the package."); } res.isUpdate = true; } // 获得 app 安装文件夹 File appDir = VEnvironment.getDataAppPackageDirectory(pkg.packageName); // so 文件夹 File libDir = new File(appDir, "lib"); if (res.isUpdate) { FileUtils.deleteDir(libDir); VEnvironment.getOdexFile(pkg.packageName).delete(); VActivityManagerService.get().killAppByPkg(pkg.packageName, VUserHandle.USER_ALL); } if (!libDir.exists() && !libDir.mkdirs()) { return InstallResult.makeFailure("Unable to create lib dir."); } // 是否基于系统的 apk 加载,前提是安装过的 apk 并且 dependSystem 开关打开 boolean dependSystem = (flags & InstallStrategy.DEPEND_SYSTEM_IF_EXIST) != 0 && VirtualCore.get().isOutsideInstalled(pkg.packageName); if (existSetting != null && existSetting.dependSystem) { dependSystem = false; } // 复制 so 到 sandbox lib NativeLibraryHelperCompat.copyNativeBinaries(new File(path), libDir); // 如果不基于系统,一些必要的拷贝工作 if (!dependSystem) { File privatePackageFile = new File(appDir, "base.apk"); File parentFolder = privatePackageFile.getParentFile(); if (!parentFolder.exists() && !parentFolder.mkdirs()) { VLog.w(TAG, "Warning: unable to create folder : " + privatePackageFile.getPath()); } else if (privatePackageFile.exists() && !privatePackageFile.delete()) { VLog.w(TAG, "Warning: unable to delete file : " + privatePackageFile.getPath()); } try { FileUtils.copyFile(packageFile, privatePackageFile); } catch (IOException e) { privatePackageFile.delete(); return InstallResult.makeFailure("Unable to copy the package file."); } packageFile = privatePackageFile; } if (existOne != null) { PackageCacheManager.remove(pkg.packageName); } // 给上可执行权限,5.0 之后在 SD 卡上执行 bin 需要可执行权限 chmodPackageDictionary(packageFile); // PackageSetting 的一些配置,后面会序列化在磁盘上 PackageSetting ps; if (existSetting != null) { ps = existSetting; } else { ps = new PackageSetting(); } ps.skipDexOpt = skipDexOpt; ps.dependSystem = dependSystem; ps.apkPath = packageFile.getPath(); ps.libPath = libDir.getPath(); ps.packageName = pkg.packageName; ps.appId = VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg)); if (res.isUpdate) { ps.lastUpdateTime = installTime; } else { ps.firstInstallTime = installTime; ps.lastUpdateTime = installTime; for (int userId : VUserManagerService.get().getUserIds()) { boolean installed = userId == 0; ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed); } } //保存 VPackage Cache 到 Disk PackageParserEx.savePackageCache(pkg); //保存到 RamCache PackageCacheManager.put(pkg, ps); mPersistenceLayer.save(); BroadcastSystem.get().startApp(pkg); //发送通知 安装完成 if (notify) { notifyAppInstalled(ps, -1); } res.isSuccess = true; return res; }
APk 的安装主要完成以下几件事情:
1. 解析 menifest 拿到 apk 内部信息,包括组件信息,权限信息等。并将这些信息序列化到磁盘和内存中,以备打开时调用。
2. 准备 App 在 VA 沙箱环境中的私有空间,并且复制一些必要的 apk 和 so libs。
3. 最后通知前台安装完成。
解析 apk menifest:PackageParserEx.parsePackage():
// 解析包结构 public static VPackage parsePackage(File packageFile) throws Throwable { PackageParser parser = PackageParserCompat.createParser(packageFile); // 调用对应系统版本的 parsePackage 方法 PackageParser.Package p = PackageParserCompat.parsePackage(parser, packageFile, 0); // 包含此信息代表其是 share lib if (p.requestedPermissions.contains("android.permission.FAKE_PACKAGE_SIGNATURE") && p.mAppMetaData != null && p.mAppMetaData.containsKey("fake-signature")) { String sig = p.mAppMetaData.getString("fake-signature"); p.mSignatures = new Signature[]{new Signature(sig)}; VLog.d(TAG, "Using fake-signature feature on : " + p.packageName); } else { // 验证签名 PackageParserCompat.collectCertificates(parser, p, PackageParser.PARSE_IS_SYSTEM); } // 转换成可以序列化在磁盘上的 Cache return buildPackageCache(p); }
这里解析 Menifest 的方法其实是调用了 framework 隐藏方法 android.content.pm.PackageParser.parsePackage 来实现的,这个方法返回 android.content.pm.Package 结构,这个类型也是隐藏的,怎么办?可以从 sdk 中复制这个类到自己的项目中欺骗编译器。这就是上一章一开始提到的。
这里还有一个问题,就是 Package 类是不可序列化的,换句话说就是不能直接保存在磁盘上,我们需要将其转换成可以序列化的 VPackage 类型,这就是 buildPackageCache() 的作用。
VPackage:
public class VPackage implements Parcelable { public static final Creator CREATOR = new Creator() { @Override public VPackage createFromParcel(Parcel source) { return new VPackage(source); } @Override public VPackage[] newArray(int size) { return new VPackage[size]; } }; public ArrayList activities; public ArrayList receivers; public ArrayList providers; public ArrayList services; public ArrayList instrumentation; public ArrayList permissions; public ArrayList permissionGroups; public ArrayList requestedPermissions; public ArrayList protectedBroadcasts; public ApplicationInfo applicationInfo; public Signature[] mSignatures; public Bundle mAppMetaData; public String packageName; public int mPreferredOrder; public String mVersionName; public String mSharedUserId; public ArrayList usesLibraries; public int mVersionCode; public int mSharedUserLabel; // Applications hardware preferences public ArrayList configPreferences = null; // Applications requested features public ArrayList reqFeatures = null; public Object mExtras;..........................
可以看到 VPackage 几乎保存了 apk 中所有的关键信息,尤其是组件的数据结构会在 app 在 VA 中运行的时候给 VAMS,VPMS 这些 VAService 提供 apk 的组件信息。
关于是否 dependSystem 和 isInstallOutside,这个有关 apk 的动态加载,如果 dependSysytem 并且 apk 已经在外部环境安装了,那么 VA 会调用系统提供的 API 就可以动态加载 APK。反之 VA 需要做一些必要的复制工作然后再费劲的去加载 APK。
下一章分析 App 的启动 : Android 双开沙箱 VirtualApp 源码分析(三)App 启动
更多相关文章
- Android/Bluetooth 初始化流程
- Android 进程保活手段分析
- execlp启动android进程命令窗口通过adb shell 进入android 的Lin
- Android中应用多进程的整理总结
- Android进程系列第八篇---LowmemoryKiller机制分析(下)