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

转载 : https://blog.csdn.net/ganyao939543405/article/details/76150725

VA 初始化

VirtualCore.startup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 会运行在四种进程, 分别是前面提到的 VAService 进程、Client App 进程、 VA 自身的 App 主进程、子进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Process type
*/
private enum ProcessType {
/**
* Server process
*/
Server,
/**
* Virtual app process
*/
VAppClient,
/**
* Main process
*/
Main,
/**
* Child process
*/
CHILD
}

这样的话, Application 就会被初始化多次,所以要在初始化的时候根据进程类型有选择的做对应的初始化工作。

InvocationStubManager.injectInternal

主要完成对 JavaframeworkHook ,将其定位到 VA 伪造 VA framework 上去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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());
addInjector(new NotificationManagerStub());
addInjector(new LocationManagerStub());
addInjector(new WindowManagerStub());
addInjector(new ClipBoardStub());
addInjector(new MountServiceStub());
...

VA 初始化主要就是这些。

Client App 的安装

VirtualCore.installPackage

首先调用远程 VAService

1
2
3
4
5
6
7
8
public InstallResult installPackage(String apkPath, int flags) {
try {
// 调用远程 VAService
return getService().installPackage(apkPath, flags);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}

最终调用 VAServcie 中的 VAppManagerService.installPackage():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// 安装 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. 准备 AppVA 沙箱环境中的私有空间,并且复制一些必要的 apkso libs
  3. 最后通知前台安装完成。

PackageParserEx.parsePackage

解析 apk menifest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 解析包结构
public static VPackage parsePackage(File packageFile) throws Throwable {
PackageParser parser = PackageParserCompat.createParser(packageFile);
// 调用对应系统版本的 parsePackage 方法
PackageParser.Package p = PackageParserCompat.parsePackage(parser, packageFile, 0);
// 包含此信息代表其是 debug 签名或者其他签名
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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;

public VPackage() {
}
...

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

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