Android 双开沙箱 VirtualApp 源码分析(二)

VA 初始化

先看一下代码:
VirtualCore.startup

public void startup(Context context) throws Throwable {if (!isStartUp) {// 确保 MainThreadif (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 进程不需要 Hookif (VirtualCore.get().isMainProcess()) {return;}// VAService 需要 Hook AMS 和 PMSif (VirtualCore.get().isServerProcess()) {addInjector(new ActivityManagerStub());addInjector(new PackageManagerStub());return;}// Client APP 需要 Hook 整个 framework,来使其调用到 VA frameworkif (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 {// 调用远程 VAServicereturn 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 pathFile 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 libNativeLibraryHelperCompat.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 到 DiskPackageParserEx.savePackageCache(pkg);//保存到 RamCachePackageCacheManager.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 libif (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);}// 转换成可以序列化在磁盘上的 Cachereturn 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<VPackage> CREATOR = new Creator<VPackage>() {@Overridepublic VPackage createFromParcel(Parcel source) {return new VPackage(source);}@Overridepublic VPackage[] newArray(int size) {return new VPackage[size];}};public ArrayList<ActivityComponent> activities;public ArrayList<ActivityComponent> receivers;public ArrayList<ProviderComponent> providers;public ArrayList<ServiceComponent> services;public ArrayList<InstrumentationComponent> instrumentation;public ArrayList<PermissionComponent> permissions;public ArrayList<PermissionGroupComponent> permissionGroups;public ArrayList<String> requestedPermissions;public ArrayList<String> protectedBroadcasts;public ApplicationInfo applicationInfo;public Signature[] mSignatures;public Bundle mAppMetaData;public String packageName;public int mPreferredOrder;public String mVersionName;public String mSharedUserId;public ArrayList<String> usesLibraries;public int mVersionCode;public int mSharedUserLabel;// Applications hardware preferencespublic ArrayList<ConfigurationInfo> configPreferences = null;// Applications requested featurespublic ArrayList<FeatureInfo> reqFeatures = null;public Object mExtras;

可以看到 VPackage 几乎保存了 apk 中所有的关键信息,尤其是组件的数据结构会在 app 在 VA 中运行的时候给 VAMS,VPMS 这些 VAService 提供 apk 的组件信息。

关于是否 dependSystem 和 isInstallOutside,这个有关 apk 的动态加载,如果 dependSysytem 并且 apk 已经在外部环境安装了,那么 VA 会调用系统提供的 API 就可以动态加载 APK。反之 VA 需要做一些必要的复制工作然后再费劲的去加载 APK。

作者:OSTCB
来源:CSDN
原文:https://blog.csdn.net/ganyao939543405/article/details/76150725

Android 双开沙箱 VirtualApp 源码分析(二)相关推荐

  1. Android 双开沙箱 VirtualApp 源码分析(一)

    最近发现了一个非常好的开源项目,基本实现了一个 Android 上的沙箱环境,不过应用场景最多的还是应用双开. VA github: https://github.com/asLody/Virtual ...

  2. Android 双开沙箱 VirtualApp 源码分析(六)ContentProvider

    上一章:Android 双开沙箱 VirtualApp 源码分析(五)BroadcastReceiver Provider 注册 回顾前面,Activity 启动的时候会检查 Application ...

  3. Android 双开沙箱 VirtualApp 源码分析(四)启动插件 Service

    上一章:Android 双开沙箱 VirtualApp 源码分析(三)App 启动 原生 Service 创建过程 首先有必要了解一下原生 framework 对 Service 的创建,因为在 VA ...

  4. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  5. Android Q 10.1 KeyMaster源码分析(二) - 各家方案的实现

    写在之前 这两篇文章是我2021年3月初看KeyMaster的笔记,本来打算等分析完KeyMaster和KeyStore以后再一起做成一系列贴出来,后来KeyStore的分析中断了,这一系列的文章就变 ...

  6. Android录音下————AudioRecord源码分析

    Android录音下----AudioRecord源码分析 文章目录 Android录音下----AudioRecord源码分析 一.概述 1.主要分析点 2.储备知识 二.getMinBufferS ...

  7. Android主流三方库源码分析(九、深入理解EventBus源码)

    一.EventBus使用流程概念 1.Android事件发布/订阅框架 2.事件传递既可用于Android四大组件间通信 3.EventBus的优点是代码简洁,使用简单,事件发布.订阅充分解耦 4.首 ...

  8. Android Camera 系统架构源码分析

    Android Camera 系统架构源码分析(1)---->Camera的初始化 Android Camera 系统架构源码分析(2)---->Camera的startPreview和s ...

  9. Android 系统(78)---《android framework常用api源码分析》之 app应用安装流程

    <android framework常用api源码分析>之 app应用安装流程 <android framework常用api源码分析>android生态在中国已经发展非常庞大 ...

最新文章

  1. 常用24个方法有效优化ASP.NET的性能
  2. Ubuntu 16.04安装 sogou 遗留下的问题
  3. java 泛型 接口_Java泛型(泛型接口、泛型类、泛型方法)
  4. [译]10-Spring BeanPostProcessor
  5. java模仿银行账务业务_一个银行转账业务模型分析:大魏Java记5-7
  6. shiro设置session超时时间
  7. 牛奶可乐经济学(一)
  8. Java实现FTP文件上传和下载
  9. 一文了解互联网运营核心指标(产品、运营人员必知)
  10. 【总结】1361- package.json 与 package-lock.json 的关系
  11. 电商订单系统,你该如何设计
  12. 精辟的人生格言,呵呵
  13. 【51nod】2589 快速讨伐
  14. Python每日一记42机器学习中特征重要性feature_importances_
  15. 全选、全不选、反选功能的实现
  16. LCD液晶显示屏工作原理
  17. 张勋说:简述球磨机内介质(钢球|钢棒|衬板)运动动力学(图文)
  18. 7自由度整车仿真模型建立
  19. 动态规划求解最大子段和
  20. Windows Embedded Standard 7 概述

热门文章

  1. 点击图片放大全屏加载,再次点击图片/文档回到原来位置
  2. linux删除相同用户,Linux 批量添加和删除用户
  3. 国产蓝牙耳机有哪些?最好用的国产蓝牙耳机排行榜
  4. cs231n笔记:lecture2,lecture3
  5. 触觉设备,临场感,预测控制,DOB
  6. 写在微信小程序上线之夜,我想对移动开发人员说别慌先玩玩AR压压惊!
  7. Kindle for PC 无法放大图片的困扰:使用 Windows 自带的放大镜工具
  8. python实现单机斗地主_Python编写斗地主游戏(单机版)
  9. 1、Ubuntu下安装软件报错
  10. cracking_game