上一章: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 启动

更多相关文章

  1. Android/Bluetooth 初始化流程
  2. Android 进程保活手段分析
  3. execlp启动android进程命令窗口通过adb shell 进入android 的Lin
  4. Android中应用多进程的整理总结
  5. Android进程系列第八篇---LowmemoryKiller机制分析(下)

随机推荐

  1. 高效方法 | Jupyter Notebook 比你想象中
  2. 金州勇士4年3冠的成功秘诀!数据可视化分析
  3. 分布式爬虫的部署之Scrapyd对接Docker
  4. Scrapy框架的使用之Scrapyrt的使用
  5. OpenCV:模板匹配。
  6. OpenCV:人脸检测。
  7. 分布式爬虫的部署之Scrapyd批量部署
  8. 【机器学习笔记】:解读正则化,LASSO回归,岭
  9. 手把手教你用Bokeh进行可视化数据分析(附
  10. 分布式爬虫的部署之Gerapy分布式管理