转载 : https://blog.csdn.net/ganyao939543405/article/details/76177392
在这之前,我们还是要先了解一下 VA Client Framework 和 VAService 之间的通讯方式
VAService 与通讯 VAService 首先 VAService
是指 VA
仿造 Android
原生 framework
层 Service
实现的一套副本,举例有 VActivityManagerService
,它和系统 AMS
一样,只不过他管理的是 VA
内部 Client App
的组件会话。
VAService 统一管理 首先所有 VAService
直接继承与 XXX.Stub
,也就是 Binder
,并且直接使用了一个 Map
储存在 VAService
进程空间中,并没有注册到系统 AMS
中,事实上在 VAService
进程中,每个 Service
都被当作一个普通对象 new
和初始化。
最终,他们被添加到了 ServiceCache
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class ServiceCache { private static final Map<String, IBinder> sCache = new ArrayMap<>(5); public static void addService(String name, IBinder service) { sCache.put(name, service); } public static IBinder removeService(String name) { return sCache.remove(name); } public static IBinder getService(String name) { return sCache.get(name); } }
这个 cache
很简单,就是一个 Map
。
而被添加的时机则在 BinderProvider
的 onCreate()
回调中:
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 @Override public boolean onCreate() { Context context = getContext(); // 这是一个空前台服务,目的是为了保活 VAService 进程,即 :x 进程 DaemonService.startup(context); if (!VirtualCore.get().isStartup()) { return true; } VPackageManagerService.systemReady(); addService(ServiceManagerNative.PACKAGE, VPackageManagerService.get()); VActivityManagerService.systemReady(context); addService(ServiceManagerNative.ACTIVITY, VActivityManagerService.get()); addService(ServiceManagerNative.USER, VUserManagerService.get()); VAppManagerService.systemReady(); addService(ServiceManagerNative.APP, VAppManagerService.get()); BroadcastSystem.attach(VActivityManagerService.get(), VAppManagerService.get()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { addService(ServiceManagerNative.JOB, VJobSchedulerService.get()); } VNotificationManagerService.systemReady(context); addService(ServiceManagerNative.NOTIFICATION, VNotificationManagerService.get()); VAppManagerService.get().scanApps(); VAccountManagerService.systemReady(); addService(ServiceManagerNative.ACCOUNT, VAccountManagerService.get()); addService(ServiceManagerNative.VS, VirtualStorageService.get()); addService(ServiceManagerNative.DEVICE, VDeviceManagerService.get()); return true; }
需要注意的是 DeamonService
是一个空前台服务,目的是为了保活 VAService
进程,即 :x
进程,因为现在后台服务很容易被杀,在 Android 8.0 以后后台服务只能在后台存活 5S
,而前台服务则不受影响。
ServiceFetcher VA
设计了一个单独的 ServiceFetcher
服务用于向外部暴露 VAService
中的所有服务的 IBinder
句柄,而 ServiceFetcher
本身也是 Binder
服务,也就是说, ServiceFetcher
的 Ibinder
句柄是拿到其他 VAService IBinder
的钥匙。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // ServiceFetcher 实现类 远程调用类 private class ServiceFetcher extends IServiceFetcher.Stub { @Override public IBinder getService(String name) throws RemoteException { if (name != null) { return ServiceCache.getService(name); } return null; } @Override public void addService(String name, IBinder service) throws RemoteException { if (name != null && service != null) { ServiceCache.addService(name, service); } } @Override public void removeService(String name) throws RemoteException { if (name != null) { ServiceCache.removeService(name); } } }
ServicecFetcher
自身的 IBnder
则通过 BinderProvicer
这个ContentProvider
暴露给其他进程:
1 2 3 4 5 6 7 8 9 @Override public Bundle call(String method, String arg, Bundle extras) { if ("@".equals(method)) { Bundle bundle = new Bundle(); BundleCompat.putBinder(bundle, "_VA_|_binder_", mServiceFetcher); return bundle; } return null; }
那么在 Client App
中 VA Client
就可以通过 IServiceFetcher
这个 IBinder
拿到其他服务的 IBinder
了:
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 // 通过 ContentProvider 传递一个 private static IServiceFetcher sFetcher; private static IServiceFetcher getServiceFetcher() { if (sFetcher == null || !sFetcher.asBinder().isBinderAlive()) { synchronized (ServiceManagerNative.class) { Context context = VirtualCore.get().getContext(); Bundle response = new ProviderCall.Builder(context, SERVICE_CP_AUTH).methodName("@").call(); /* SERVICE_CP_AUTH 对应着下面的 Provider <provider android:name="com.lody.virtual.server.BinderProvider" android:authorities="${applicationId}.virtual.service.BinderProvider" android:exported="false" android:process="@string/engine_process_name" /> */ if (response != null) { IBinder binder = BundleCompat.getBinder(response, "_VA_|_binder_"); linkBinderDied(binder); sFetcher = IServiceFetcher.Stub.asInterface(binder); } } } return sFetcher; } // 返回服务的 IBinder 句柄 public static IBinder getService(String name) { // 如果是本地服务,直接本地返回 if (VirtualCore.get().isServerProcess()) { return ServiceCache.getService(name); } // 通过 ServiceFetcher 的句柄找到远程 Service 的句柄 IServiceFetcher fetcher = getServiceFetcher(); if (fetcher != null) { try { return fetcher.getService(name); } catch (RemoteException e) { e.printStackTrace(); } } VLog.e(TAG, "GetService(%s) return null.", name); return null; }
启动 App 首先要了解的是 Android App
是组件化的, Apk
其实是 N 多个组件的集合,以及一些资源文件和 Assert
, App
的启动有多种情况,只要在一个新的进程中调起了 apk
中任何一个组件, App
将被初始化, Application
将被初始化。
启动 Activity 我们先看启动 Activity
的情况:
Hook startActivity(重定位 Intent 到 StubActivity) 首先在 Client App
中, startActivity
方法必须被 Hook
掉,不然 Client App
调用 startActivity
就直指外部 Activity
去了。
这部分的原理其实与 DroidPlugin
大同小异,由于插件(Client App
)中的 Activity
是没有在 AMS
中注册的, AMS
自然无法找到我们的插件 Activity
。
Hook
的目的是我们拿到用户的 Intent
,把他替换成指向 VA
在 Menifest
中站好坑的 StubActivity
的 Intent
,然后将原 Intent
当作 data
打包进新 Intent
以便日后流程再次进入 VA
时恢复。
Hook
的方法就是用我们动态代理生成的代理类对象替换系统原来的 ActiityManagerNative.geDefault
对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 //ActivityManagerStub.java @Override public void inject() throws Throwable { if (BuildCompat.isOreo()) { //Android Oreo(8.X) Object singleton = ActivityManagerOreo.IActivityManagerSingleton.get(); Singleton.mInstance.set(singleton, getInvocationStub().getProxyInterface()); } else { if (ActivityManagerNative.gDefault.type() == IActivityManager.TYPE) { ActivityManagerNative.gDefault.set(getInvocationStub().getProxyInterface()); } else if (ActivityManagerNative.gDefault.type() == Singleton.TYPE) { Object gDefault = ActivityManagerNative.gDefault.get(); Singleton.mInstance.set(gDefault, getInvocationStub().getProxyInterface()); } } BinderInvocationStub hookAMBinder = new BinderInvocationStub(getInvocationStub().getBaseInterface()); hookAMBinder.copyMethodProxies(getInvocationStub()); ServiceManager.sCache.get().put(Context.ACTIVITY_SERVICE, hookAMBinder); }
好了,下面只要调用到 startActivity
就会被 Hook
到 call
。
这个函数需要注意以下几点:
VA
有意将安装和卸载 APP
的请求重定向到了卸载 VA
内部 APK
的逻辑。
resolveActivityInfo
调用到了 VPM
的 resolveIntent
,最终会远程调用到 VPMS
的 resolveIntent
,然后 VPMS
就会去查询 VPackage
找到目标 Activity
并将信息附加在 ResolveInfo
中返回 VPM
。
最后也是最重要的一点, startActivity
会调用到 VAM.startActivity
,同样最终会远程调用到 VAMS
的 startActivity
。
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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 // Hook startActivity static class StartActivity extends MethodProxy { private static final String SCHEME_FILE = "file"; private static final String SCHEME_PACKAGE = "package"; @Override public String getMethodName() { return "startActivity"; } @Override public Object call(Object who, Method method, Object... args) throws Throwable { int intentIndex = ArrayUtils.indexOfObject(args, Intent.class, 1); if (intentIndex < 0) { return ActivityManagerCompat.START_INTENT_NOT_RESOLVED; } int resultToIndex = ArrayUtils.indexOfObject(args, IBinder.class, 2); String resolvedType = (String) args[intentIndex + 1]; Intent intent = (Intent) args[intentIndex]; intent.setDataAndType(intent.getData(), resolvedType); IBinder resultTo = resultToIndex >= 0 ? (IBinder) args[resultToIndex] : null; int userId = VUserHandle.myUserId(); if (ComponentUtils.isStubComponent(intent)) { return method.invoke(who, args); } // 请求安装和卸载界面 if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction()) || (Intent.ACTION_VIEW.equals(intent.getAction()) && "application/vnd.android.package-archive".equals(intent.getType()))) { if (handleInstallRequest(intent)) { return 0; } } else if ((Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction()) || Intent.ACTION_DELETE.equals(intent.getAction())) && "package".equals(intent.getScheme())) { if (handleUninstallRequest(intent)) { return 0; } } String resultWho = null; int requestCode = 0; Bundle options = ArrayUtils.getFirst(args, Bundle.class); if (resultTo != null) { resultWho = (String) args[resultToIndex + 1]; requestCode = (int) args[resultToIndex + 2]; } // chooser 调用选择界面 if (ChooserActivity.check(intent)) { intent.setComponent(new ComponentName(getHostContext(), ChooserActivity.class)); intent.putExtra(Constants.EXTRA_USER_HANDLE, userId); intent.putExtra(ChooserActivity.EXTRA_DATA, options); intent.putExtra(ChooserActivity.EXTRA_WHO, resultWho); intent.putExtra(ChooserActivity.EXTRA_REQUEST_CODE, requestCode); return method.invoke(who, args); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { args[intentIndex - 1] = getHostPkg(); } //解析 ActivityInfo ActivityInfo activityInfo = VirtualCore.get().resolveActivityInfo(intent, userId); if (activityInfo == null) { VLog.e("VActivityManager", "Unable to resolve activityInfo : " + intent); if (intent.getPackage() != null && isAppPkg(intent.getPackage())) { return ActivityManagerCompat.START_INTENT_NOT_RESOLVED; } return method.invoke(who, args); } // 调用远程 VAMS.startActivity int res = VActivityManager.get().startActivity(intent, activityInfo, resultTo, options, resultWho, requestCode, VUserHandle.myUserId()); if (res != 0 && resultTo != null && requestCode > 0) { VActivityManager.get().sendActivityResult(resultTo, resultWho, requestCode); } // 处理 Activity 切换动画,因为此时动画还是 Host 的 Stub Activity 默认动画,需要覆盖成子程序包的动画 if (resultTo != null) { ActivityClientRecord r = VActivityManager.get().getActivityRecord(resultTo); if (r != null && r.activity != null) { try { TypedValue out = new TypedValue(); Resources.Theme theme = r.activity.getResources().newTheme(); theme.applyStyle(activityInfo.getThemeResource(), true); if (theme.resolveAttribute(android.R.attr.windowAnimationStyle, out, true)) { TypedArray array = theme.obtainStyledAttributes(out.data, new int[]{ android.R.attr.activityOpenEnterAnimation, android.R.attr.activityOpenExitAnimation }); r.activity.overridePendingTransition(array.getResourceId(0, 0), array.getResourceId(1, 0)); array.recycle(); } } catch (Throwable e) { // Ignore } } } return res; } private boolean handleInstallRequest(Intent intent) { IAppRequestListener listener = VirtualCore.get().getAppRequestListener(); if (listener != null) { Uri packageUri = intent.getData(); if (SCHEME_FILE.equals(packageUri.getScheme())) { File sourceFile = new File(packageUri.getPath()); try { listener.onRequestInstall(sourceFile.getPath()); return true; } catch (RemoteException e) { e.printStackTrace(); } } } return false; } private boolean handleUninstallRequest(Intent intent) { IAppRequestListener listener = VirtualCore.get().getAppRequestListener(); if (listener != null) { Uri packageUri = intent.getData(); if (SCHEME_PACKAGE.equals(packageUri.getScheme())) { String pkg = packageUri.getSchemeSpecificPart(); try { listener.onRequestUninstall(pkg); return true; } catch (RemoteException e) { e.printStackTrace(); } } } return false; } }
逻辑最终走到 VAMS
后,VAMS
调用 ActivityStack
.startActivityLocked
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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 // 参考 framework 的实现 int startActivityLocked(int userId, Intent intent, ActivityInfo info, IBinder resultTo, Bundle options, String resultWho, int requestCode) { optimizeTasksLocked(); Intent destIntent; ActivityRecord sourceRecord = findActivityByToken(userId, resultTo); TaskRecord sourceTask = sourceRecord != null ? sourceRecord.task : null; ReuseTarget reuseTarget = ReuseTarget.CURRENT; ClearTarget clearTarget = ClearTarget.NOTHING; boolean clearTop = containFlags(intent, Intent.FLAG_ACTIVITY_CLEAR_TOP); boolean clearTask = containFlags(intent, Intent.FLAG_ACTIVITY_CLEAR_TASK); if (intent.getComponent() == null) { intent.setComponent(new ComponentName(info.packageName, info.name)); } if (sourceRecord != null && sourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } if (clearTop) { removeFlags(intent, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); clearTarget = ClearTarget.TOP; } if (clearTask) { if (containFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK)) { clearTarget = ClearTarget.TASK; } else { removeFlags(intent, Intent.FLAG_ACTIVITY_CLEAR_TASK); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { switch (info.documentLaunchMode) { case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING: clearTarget = ClearTarget.TASK; reuseTarget = ReuseTarget.DOCUMENT; break; case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS: reuseTarget = ReuseTarget.MULTIPLE; break; } } boolean singleTop = false; switch (info.launchMode) { case LAUNCH_SINGLE_TOP: { singleTop = true; if (containFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK)) { reuseTarget = containFlags(intent, Intent.FLAG_ACTIVITY_MULTIPLE_TASK) ? ReuseTarget.MULTIPLE : ReuseTarget.AFFINITY; } } break; case LAUNCH_SINGLE_TASK: { clearTop = false; clearTarget = ClearTarget.TOP; reuseTarget = containFlags(intent, Intent.FLAG_ACTIVITY_MULTIPLE_TASK) ? ReuseTarget.MULTIPLE : ReuseTarget.AFFINITY; } break; case LAUNCH_SINGLE_INSTANCE: { clearTop = false; clearTarget = ClearTarget.TOP; reuseTarget = ReuseTarget.AFFINITY; } break; default: { if (containFlags(intent, Intent.FLAG_ACTIVITY_SINGLE_TOP)) { singleTop = true; } } break; } if (clearTarget == ClearTarget.NOTHING) { if (containFlags(intent, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)) { clearTarget = ClearTarget.SPEC_ACTIVITY; } } if (sourceTask == null && reuseTarget == ReuseTarget.CURRENT) { reuseTarget = ReuseTarget.AFFINITY; } String affinity = ComponentUtils.getTaskAffinity(info); // 根据 Flag 寻找合适的 Task TaskRecord reuseTask = null; switch (reuseTarget) { case AFFINITY: reuseTask = findTaskByAffinityLocked(userId, affinity); break; case DOCUMENT: reuseTask = findTaskByIntentLocked(userId, intent); break; case CURRENT: reuseTask = sourceTask; break; default: break; } boolean taskMarked = false; if (reuseTask == null) { startActivityInNewTaskLocked(userId, intent, info, options); } else { boolean delivered = false; mAM.moveTaskToFront(reuseTask.taskId, 0); boolean startTaskToFront = !clearTask && !clearTop && ComponentUtils.isSameIntent(intent, reuseTask.taskRoot); if (clearTarget.deliverIntent || singleTop) { taskMarked = markTaskByClearTarget(reuseTask, clearTarget, intent.getComponent()); ActivityRecord topRecord = topActivityInTask(reuseTask); if (clearTop && !singleTop && topRecord != null && taskMarked) { topRecord.marked = true; } // Target activity is on top if (topRecord != null && !topRecord.marked && topRecord.component.equals(intent.getComponent())) { deliverNewIntentLocked(sourceRecord, topRecord, intent); delivered = true; } } if (taskMarked) { synchronized (mHistory) { scheduleFinishMarkedActivityLocked(); } } if (!startTaskToFront) { if (!delivered) { destIntent = startActivityProcess(userId, sourceRecord, intent, info); if (destIntent != null) { startActivityFromSourceTask(reuseTask, destIntent, info, resultWho, requestCode, options); } } } } return 0; }
然后 call
到了 startActivityProcess
,这就是真正替换 Intent
的地方。
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 // 使用 Host Stub Activity 的 Intent 包装原 Intent 瞒天过海 private Intent startActivityProcess(int userId, ActivityRecord sourceRecord, Intent intent, ActivityInfo info) { intent = new Intent(intent); // 获得 Activity 对应的 ProcessRecorder,如果没有则表示这是 Process 第一个打开的组件,需要初始化 Application ProcessRecord targetApp = mService.startProcessIfNeedLocked(info.processName, userId, info.packageName); if (targetApp == null) { return null; } Intent targetIntent = new Intent(); // 根据 Client App 的 PID 获取 StubActivity String stubActivityPath = fetchStubActivity(targetApp.vpid, info); Log.e("gy", "map activity:" + intent.getComponent().getClassName() + " -> " + stubActivityPath); targetIntent.setClassName(VirtualCore.get().getHostPkg(), stubActivityPath); ComponentName component = intent.getComponent(); if (component == null) { component = ComponentUtils.toComponentName(info); } targetIntent.setType(component.flattenToString()); StubActivityRecord saveInstance = new StubActivityRecord(intent, info, sourceRecord != null ? sourceRecord.component : null, userId); saveInstance.saveToIntent(targetIntent); return targetIntent; }
fetchStubActivity
会根据相同的进程 id
在 VA
的 Menifest
中找到那个提前占坑的 StubActivity
。
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 // 获取合适的 StubActivity,返回 StubActivity 全限定名 private String fetchStubActivity(int vpid, ActivityInfo targetInfo) { boolean isFloating = false; boolean isTranslucent = false; boolean showWallpaper = false; try { int[] R_Styleable_Window = R_Hide.styleable.Window.get(); int R_Styleable_Window_windowIsTranslucent = R_Hide.styleable.Window_windowIsTranslucent.get(); int R_Styleable_Window_windowIsFloating = R_Hide.styleable.Window_windowIsFloating.get(); int R_Styleable_Window_windowShowWallpaper = R_Hide.styleable.Window_windowShowWallpaper.get(); AttributeCache.Entry ent = AttributeCache.instance().get(targetInfo.packageName, targetInfo.theme, R_Styleable_Window); if (ent != null && ent.array != null) { showWallpaper = ent.array.getBoolean(R_Styleable_Window_windowShowWallpaper, false); isTranslucent = ent.array.getBoolean(R_Styleable_Window_windowIsTranslucent, false); isFloating = ent.array.getBoolean(R_Styleable_Window_windowIsFloating, false); } } catch (Throwable e) { e.printStackTrace(); } boolean isDialogStyle = isFloating || isTranslucent || showWallpaper; // 根据在 Menifest 中注册的 pid if (isDialogStyle) { return VASettings.getStubDialogName(vpid); } else { return VASettings.getStubActivityName(vpid); } }
这里需要特别注意, VA
占坑的方式和 DroidPlugin
有些小不同,VA
没有为每个 Process
注册多个 Activity
,也没有为不同的启动方式注册多个 Activity
,这里确实是有改进的。
这里根本原因是因为 VA
对 VAMS
实现的更为完整,实现了原版 AMS
的基本功能,包括完整的 Recorder
管理,Task Stack
管理等,这样的话 StubActivity
的唯一作用便是携带 Client App
真正的 Intent
交给 VAMS
处理。这套机制衍生到其他的组件也是一样的。
可以简单看一下 ActivityStack,
、 ActivityRecorder
、 ActivityRecord
。
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 /* package */ class ActivityStack { private final ActivityManager mAM; private final VActivityManagerService mService; /** * [Key] = TaskId [Value] = TaskRecord */ private final SparseArray<TaskRecord> mHistory = new SparseArray<>(); } class TaskRecord { public final List<ActivityRecord> activities = Collections.synchronizedList(new ArrayList<ActivityRecord>()); public int taskId; public int userId; public String affinity; public Intent taskRoot; } /* package */ class ActivityRecord { public TaskRecord task; public ComponentName component; public ComponentName caller; // Client App 中 Activity 的句柄 public IBinder token; public int userId; public ProcessRecord process; public int launchMode; public int flags; public boolean marked; public String affinity; }
StubActivityRecorder
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 public class StubActivityRecord { public Intent intent; public ActivityInfo info; public ComponentName caller; public int userId; public StubActivityRecord(Intent intent, ActivityInfo info, ComponentName caller, int userId) { this.intent = intent; this.info = info; this.caller = caller; this.userId = userId; } // 获取原版 Intent 和一些其他信息 public StubActivityRecord(Intent stub) { this.intent = stub.getParcelableExtra("_VA_|_intent_"); this.info = stub.getParcelableExtra("_VA_|_info_"); this.caller = stub.getParcelableExtra("_VA_|_caller_"); this.userId = stub.getIntExtra("_VA_|_user_id_", 0); } // 将原版 Intent 塞到 Stub Intent public void saveToIntent(Intent stub) { stub.putExtra("_VA_|_intent_", intent); stub.putExtra("_VA_|_info_", info); stub.putExtra("_VA_|_caller_", caller); stub.putExtra("_VA_|_user_id_", userId); } }
初始化 Application 还有一个非常重要的事情,注意到这一行 。
1 2 // 获得 Activity 对应的 ProcessRecorder,如果没有则表示这是 Process 第一个打开的组件,需要初始化 Application ProcessRecord targetApp = mService.startProcessIfNeedLocked(info.processName, userId, info.packageName);
这里会先去找对应 Client App
进程的 ProcessRecorder
, 找不到代表 Application
刚启动尚未初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private ProcessRecord performStartProcessLocked(int vuid, int vpid, ApplicationInfo info, String processName) { ProcessRecord app = new ProcessRecord(info, processName, vuid, vpid); Bundle extras = new Bundle(); BundleCompat.putBinder(extras, "_VA_|_binder_", app); extras.putInt("_VA_|_vuid_", vuid); extras.putString("_VA_|_process_", processName); extras.putString("_VA_|_pkg_", info.packageName); // 调用子程序包的 init_process 方法,并且得到子程序包 IBinder 句柄 Bundle res = ProviderCall.call(VASettings.getStubAuthority(vpid), "_VA_|_init_process_", null, extras); if (res == null) { return null; } int pid = res.getInt("_VA_|_pid_"); IBinder clientBinder = BundleCompat.getBinder(res, "_VA_|_client_"); // attach 到 Client 的 VAM attachClient(pid, clientBinder); return app; }
ProviderCall.call
向 Client App
的 StubContentProvider
发起远程调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Override public Bundle call(String method, String arg, Bundle extras) { if ("_VA_|_init_process_".equals(method)) { return initProcess(extras); } return null; } private Bundle initProcess(Bundle extras) { ConditionVariable lock = VirtualCore.get().getInitLock(); if (lock != null) { lock.block(); } IBinder token = BundleCompat.getBinder(extras,"_VA_|_binder_"); int vuid = extras.getInt("_VA_|_vuid_"); VClientImpl client = VClientImpl.get(); client.initProcess(token, vuid); Bundle res = new Bundle(); BundleCompat.putBinder(res, "_VA_|_client_", client.asBinder()); res.putInt("_VA_|_pid_", Process.myPid()); return res; }
Client App
的 IBinder
句柄(VClientImpl.asBinder
) 被打包在了 Bundle
中返回给 VAMS
。
最终 VAMS
调用原生 AM
的 startActivity
向真正的 AMS
发送替换成 StubActivity
的伪造 Intent
。
1 mirror.android.app.IActivityManager.startActivity.call(ActivityManagerNative.getDefault.call(), (Object[]) args);
恢复原 Intent 重定向到原 Activity 当 AMS
收到伪装的 Intent
后,就会找到 StubActivity
,这时流程回到 VA
里的主线程中的消息队列中。
Hook
过程就是用我们自己的 Handler
替换 android.os.Handler.mCallback
因为主线程在这里分发一些操作。
1 2 3 4 5 @Override public void inject() throws Throwable { otherCallback = getHCallback(); mirror.android.os.Handler.mCallback.set(getH(), this); }
handlerMessage
判断是 LAUNCH_ACTIVITY Action
后直接调用了 handlerLaunchActivity
方法,和原版其实很像。
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 private boolean handleLaunchActivity(Message msg) { Object r = msg.obj; Intent stubIntent = ActivityThread.ActivityClientRecord.intent.get(r); // 获取原版 Intent 信息 StubActivityRecord saveInstance = new StubActivityRecord(stubIntent); if (saveInstance.intent == null) { return true; } // 原版 Intent Intent intent = saveInstance.intent; ComponentName caller = saveInstance.caller; IBinder token = ActivityThread.ActivityClientRecord.token.get(r); ActivityInfo info = saveInstance.info; // 如果 token 还没初始化,代表 App 刚刚启动第一个组件 if (VClientImpl.get().getToken() == null) { VActivityManager.get().processRestarted(info.packageName, info.processName, saveInstance.userId); getH().sendMessageAtFrontOfQueue(Message.obtain(msg)); return false; } // AppBindData 为空,则 App 信息不明 if (!VClientImpl.get().isBound()) { // 初始化并绑定 Application VClientImpl.get().bindApplication(info.packageName, info.processName); getH().sendMessageAtFrontOfQueue(Message.obtain(msg)); return false; } // 获取 TaskId int taskId = IActivityManager.getTaskForActivity.call( ActivityManagerNative.getDefault.call(), token, false ); // 1.将 ActivityRecorder 加入 mActivities 2.通知服务端 VAMS Activity 创建完成 VActivityManager.get().onActivityCreate(ComponentUtils.toComponentName(info), caller, token, info, intent, ComponentUtils.getTaskAffinity(info), taskId, info.launchMode, info.flags); ClassLoader appClassLoader = VClientImpl.get().getClassLoader(info.applicationInfo); intent.setExtrasClassLoader(appClassLoader); // 将 Host Stub Activity Intent 替换为原版 Intent ActivityThread.ActivityClientRecord.intent.set(r, intent); // 同上 ActivityThread.ActivityClientRecord.activityInfo.set(r, info); return true; }
需要注意的是,如果这个 Activity
是这个 Apk
启动的第一个组件,则需要 bindApplication
初始化 Application
操作。
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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 private void bindApplicationNoCheck(String packageName, String processName, ConditionVariable lock) { mTempLock = lock; try { // 设置未捕获异常的 Callback setupUncaughtHandler(); } catch (Throwable e) { e.printStackTrace(); } try { // 修复 Provider 信息 fixInstalledProviders(); } catch (Throwable e) { e.printStackTrace(); } mirror.android.os.Build.SERIAL.set(deviceInfo.serial); mirror.android.os.Build.DEVICE.set(Build.DEVICE.replace(" ", "_")); ActivityThread.mInitialApplication.set( VirtualCore.mainThread(), null ); // 从 VPMS 获取 apk 信息 AppBindData data = new AppBindData(); InstalledAppInfo info = VirtualCore.get().getInstalledAppInfo(packageName, 0); if (info == null) { new Exception("App not exist!").printStackTrace(); Process.killProcess(0); System.exit(0); } // dex 优化的开关,dalvik 和 art 处理不同 if (!info.dependSystem && info.skipDexOpt) { VLog.d(TAG, "Dex opt skipped."); if (VirtualRuntime.isArt()) { ARTUtils.init(VirtualCore.get().getContext()); ARTUtils.setIsDex2oatEnabled(false); } else { DalvikUtils.init(); DalvikUtils.setDexOptMode(DalvikUtils.OPTIMIZE_MODE_NONE); } } data.appInfo = VPackageManager.get().getApplicationInfo(packageName, 0, getUserId(vuid)); data.processName = processName; data.providers = VPackageManager.get().queryContentProviders(processName, getVUid(), PackageManager.GET_META_DATA); Log.i(TAG, "Binding application " + data.appInfo.packageName + " (" + data.processName + ")"); mBoundApplication = data; // 主要设置进程的名字 VirtualRuntime.setupRuntime(data.processName, data.appInfo); int targetSdkVersion = data.appInfo.targetSdkVersion; if (targetSdkVersion < Build.VERSION_CODES.GINGERBREAD) { StrictMode.ThreadPolicy newPolicy = new StrictMode.ThreadPolicy.Builder(StrictMode.getThreadPolicy()).permitNetwork().build(); StrictMode.setThreadPolicy(newPolicy); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (mirror.android.os.StrictMode.sVmPolicyMask != null) { mirror.android.os.StrictMode.sVmPolicyMask.set(0); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) { mirror.android.os.Message.updateCheckRecycle.call(targetSdkVersion); } if (VASettings.ENABLE_IO_REDIRECT) { // IO 重定向 startIOUniformer(); } // hook native 函数 NativeEngine.hookNative(); Object mainThread = VirtualCore.mainThread(); // 准备 dex 列表 NativeEngine.startDexOverride(); // 获得子 pkg 的 Context 前提是必须在系统中安装的(疑问?) Context context = createPackageContext(data.appInfo.packageName); // 设置虚拟机系统环境 临时文件夹 codeCacheDir System.setProperty("java.io.tmpdir", context.getCacheDir().getAbsolutePath()); // oat 的 cache 目录 File codeCacheDir; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { codeCacheDir = context.getCodeCacheDir(); } else { codeCacheDir = context.getCacheDir(); } // 硬件加速的 cache 目录 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { if (HardwareRenderer.setupDiskCache != null) { HardwareRenderer.setupDiskCache.call(codeCacheDir); } } else { if (ThreadedRenderer.setupDiskCache != null) { ThreadedRenderer.setupDiskCache.call(codeCacheDir); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (RenderScriptCacheDir.setupDiskCache != null) { RenderScriptCacheDir.setupDiskCache.call(codeCacheDir); } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { if (RenderScript.setupDiskCache != null) { RenderScript.setupDiskCache.call(codeCacheDir); } } // 修复子 App 中 ActivityThread.AppBinderData 的参数,因为之前用的是在 Host 程序中注册的 Stub 的信息 Object boundApp = fixBoundApp(mBoundApplication); mBoundApplication.info = ContextImpl.mPackageInfo.get(context); mirror.android.app.ActivityThread.AppBindData.info.set(boundApp, data.info); // 同样修复 targetSdkVersion 原来也是可 Host 程序一样的 VMRuntime.setTargetSdkVersion.call(VMRuntime.getRuntime.call(), data.appInfo.targetSdkVersion); boolean conflict = SpecialComponentList.isConflictingInstrumentation(packageName); if (!conflict) { InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class); } // 开始构建子程序包的 Application 对象,并且替换原来通过 Host Stub 生成的 mInitialApplication mInitialApplication = LoadedApk.makeApplication.call(data.info, false, null); mirror.android.app.ActivityThread.mInitialApplication.set(mainThread, mInitialApplication); ContextFixer.fixContext(mInitialApplication); if (data.providers != null) { // 注册 Providers installContentProviders(mInitialApplication, data.providers); } // 初始化锁开,异步调用的初始化函数可以返回了 if (lock != null) { lock.open(); mTempLock = null; } try { // 调用 Application.onCreate mInstrumentation.callApplicationOnCreate(mInitialApplication); InvocationStubManager.getInstance().checkEnv(HCallbackStub.class); if (conflict) { InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class); } Application createdApp = ActivityThread.mInitialApplication.get(mainThread); if (createdApp != null) { mInitialApplication = createdApp; } } catch (Exception e) { if (!mInstrumentation.onException(mInitialApplication, e)) { throw new RuntimeException( "Unable to create application " + mInitialApplication.getClass().getName() + ": " + e.toString(), e); } } VActivityManager.get().appDoneExecuting(); } private void setupUncaughtHandler() { ThreadGroup root = Thread.currentThread().getThreadGroup(); while (root.getParent() != null) { root = root.getParent(); } ThreadGroup newRoot = new RootThreadGroup(root); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { final List<ThreadGroup> groups = mirror.java.lang.ThreadGroup.groups.get(root); //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (groups) { List<ThreadGroup> newGroups = new ArrayList<>(groups); newGroups.remove(newRoot); mirror.java.lang.ThreadGroup.groups.set(newRoot, newGroups); groups.clear(); groups.add(newRoot); mirror.java.lang.ThreadGroup.groups.set(root, groups); for (ThreadGroup group : newGroups) { mirror.java.lang.ThreadGroup.parent.set(group, newRoot); } } } else { final ThreadGroup[] groups = ThreadGroupN.groups.get(root); //noinspection SynchronizationOnLocalVariableOrMethodParameter synchronized (groups) { ThreadGroup[] newGroups = groups.clone(); ThreadGroupN.groups.set(newRoot, newGroups); ThreadGroupN.groups.set(root, new ThreadGroup[]{newRoot}); for (Object group : newGroups) { ThreadGroupN.parent.set(group, newRoot); } ThreadGroupN.ngroups.set(root, 1); } } }
bindApplication
主要做了以下几个事情:
从 VPMS
获取 APK
的信息,根据设置控制 Dex
优化的开关。
调用 mirror.android.os.Process.setArgV0.call(processName);
设置进程的名称,如果不设置则还是 p0 p1
这样。
做 nativeHook
主要 Hook
一些 native
的函数,主要是一些 IO
函数,包括文件访问重定向等等。
准备一些 cache
临时文件夹。
设置 AppBinderData
, AppBinderData
内部包含了 ApplicationInfo
和 provider
信息等重要的 apk
信息。可以理解为 framework
所需要的关键数据结构。
安装 ContentProvider
。
初始化用户的 Application
对象,并通过 Instrumentation
调用其 onCreate
,代表着 Client App
的生命周期正式开始。
最后成功从 StubActivity Intent
还原出来的原版 Intent
被继续交给原生的 AM
。
1 2 3 4 // 将 Host Stub Activity Intent 替换为原版 Intent ActivityThread.ActivityClientRecord.intent.set(r, intent); // 同上 ActivityThread.ActivityClientRecord.activityInfo.set(r, info);
最后,最后一个 Hook
点在 Instrumentation.callActivityOnCreate
。
因为 AMS
实际上启动的是 StubActivity
的关系,真正的 Activity
的一些信息还不是其真正的信息,比如主题之类的,所以需要在这个时机修复一下,选择这个时间修复的原因也是因为 Activity
已经被 new
出来了,而且资源已经准备完毕。
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 callActivityOnCreate(Activity activity, Bundle icicle) { VirtualCore.get().getComponentDelegate().beforeActivityCreate(activity); IBinder token = mirror.android.app.Activity.mToken.get(activity); ActivityClientRecord r = VActivityManager.get().getActivityRecord(token); // 替换 Activity 对象 if (r != null) { r.activity = activity; } ContextFixer.fixContext(activity); ActivityFixer.fixActivity(activity); ActivityInfo info = null; if (r != null) { info = r.info; } // 设置主题和屏幕纵横控制 if (info != null) { if (info.theme != 0) { activity.setTheme(info.theme); } if (activity.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED && info.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { activity.setRequestedOrientation(info.screenOrientation); } } super.callActivityOnCreate(activity, icicle); VirtualCore.get().getComponentDelegate().afterActivityCreate(activity); }
下一章介绍 Service
的代理。