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<VPackage> CREATOR = new Creator<VPackage>() {
        @Override
        public VPackage createFromParcel(Parcel source) {
            return new VPackage(source);
        }

        @Override
        public 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 preferences
    public ArrayList<ConfigurationInfo> configPreferences = null;
    // Applications requested features
    public 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。

下一章分析 App 的启动 : Android 双开沙箱 VirtualApp 源码分析(三)App 启动

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/ganyao939543405/article/details/76150725
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞