package com.lody.virtual.client; import android.annotation.SuppressLint; import android.app.Application; import android.app.Instrumentation; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.os.Binder; import android.os.Build; import android.os.ConditionVariable; import android.os.Handler; import android.os.IBinder; import android.os.IInterface; import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.StrictMode; import com.lody.virtual.IOHook; import com.lody.virtual.client.core.PatchManager; import com.lody.virtual.client.core.VirtualCore; import com.lody.virtual.client.env.SpecialComponentList; import com.lody.virtual.client.env.VirtualRuntime; import com.lody.virtual.client.fixer.ContextFixer; import com.lody.virtual.client.hook.delegate.AppInstrumentation; import com.lody.virtual.client.hook.delegate.IORedirectDelegate; import com.lody.virtual.client.hook.patchs.am.HCallbackHook; import com.lody.virtual.client.hook.providers.ProviderHook; import com.lody.virtual.client.hook.secondary.ProxyServiceFactory; import com.lody.virtual.client.ipc.VActivityManager; import com.lody.virtual.client.ipc.VPackageManager; import com.lody.virtual.client.stub.StubManifest; import com.lody.virtual.helper.proto.PendingResultData; import com.lody.virtual.helper.utils.VLog; import com.lody.virtual.os.VUserHandle; import com.lody.virtual.server.secondary.FakeIdentityBinder; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import mirror.android.app.ActivityThread; import mirror.android.app.ActivityThreadNMR1; import mirror.android.app.ContextImpl; import mirror.android.app.IActivityManager; import mirror.android.app.LoadedApk; import mirror.android.providers.Settings; import mirror.android.renderscript.RenderScriptCacheDir; import mirror.android.view.HardwareRenderer; import mirror.android.view.RenderScript; import mirror.android.view.ThreadedRenderer; import mirror.com.android.internal.content.ReferrerIntent; import mirror.dalvik.system.VMRuntime; import mirror.java.lang.ThreadGroupN; import static com.lody.virtual.os.VUserHandle.getUserId; /** * @author Lody */ public final class VClientImpl extends IVClient.Stub { private static final int NEW_INTENT = 11; private static final int RECEIVER = 12; private static final String TAG = VClientImpl.class.getSimpleName(); @SuppressLint("StaticFieldLeak") private static final VClientImpl gClient = new VClientImpl(); private final H mH = new H(); private ConditionVariable mTempLock; private Instrumentation mInstrumentation = AppInstrumentation.getDefault(); private IBinder token; private int vuid; private AppBindData mBoundApplication; private Application mInitialApplication; public static VClientImpl get() { return gClient; } public boolean isBound() { return mBoundApplication != null; } public Application getCurrentApplication() { return mInitialApplication; } public String getCurrentPackage() { return mBoundApplication != null ? mBoundApplication.appInfo.packageName : null; } public int getVUid() { return vuid; } public int getBaseVUid() { return VUserHandle.getAppId(vuid); } public ClassLoader getClassLoader(ApplicationInfo appInfo) { Context context = createPackageContext(appInfo.packageName); return context.getClassLoader(); } private void sendMessage(int what, Object obj) { Message msg = Message.obtain(); msg.what = what; msg.obj = obj; mH.sendMessage(msg); } @Override public IBinder getAppThread() { Binder appThread = ActivityThread.getApplicationThread.call(VirtualCore.mainThread()); return new FakeIdentityBinder(appThread) { @Override protected int getFakeUid() { return Process.SYSTEM_UID; } }; } @Override public IBinder getToken() { return token; } public void initProcess(IBinder token, int vuid) { if (this.token != null) { throw new IllegalStateException("Token is exist!"); } this.token = token; this.vuid = vuid; } private void handleNewIntent(NewIntentData data) { Intent intent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { intent = ReferrerIntent.ctor.newInstance(data.intent, data.creator); } else { intent = data.intent; } if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) { ActivityThread.performNewIntents.call( VirtualCore.mainThread(), data.token, Collections.singletonList(intent) ); } else { ActivityThreadNMR1.performNewIntents.call( VirtualCore.mainThread(), data.token, Collections.singletonList(intent), true ); } } public void bindApplication(final String packageName, final String processName) { if (Looper.getMainLooper() == Looper.myLooper()) { bindApplicationNoCheck(packageName, processName, new ConditionVariable()); } else { final ConditionVariable lock = new ConditionVariable(); VirtualRuntime.getUIHandler().post(new Runnable() { @Override public void run() { bindApplicationNoCheck(packageName, processName, lock); lock.open(); } }); lock.block(); } } private void bindApplicationNoCheck(String packageName, String processName, ConditionVariable lock) { mTempLock = lock; try { setupUncaughtHandler(); } catch (Throwable e) { e.printStackTrace(); } try { fixInstalledProviders(); } catch (Throwable e) { e.printStackTrace(); } ActivityThread.mInitialApplication.set( VirtualCore.mainThread(), null ); AppBindData data = new AppBindData(); data.appInfo = VPackageManager.get().getApplicationInfo(packageName, 0, getUserId(vuid)); data.processName = processName; data.providers = VPackageManager.get().queryContentProviders(processName, getVUid(), PackageManager.GET_META_DATA); mBoundApplication = data; VirtualRuntime.setupRuntime(data.processName, data.appInfo); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public synchronized void start() { Throwable t = new Exception(); t.printStackTrace(); if (VirtualCore.get().uncheckedExceptionDelegate != null) VirtualCore.get().uncheckedExceptionDelegate.onShutdown(t); super.start(); } }); 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 (StubManifest.ENABLE_IO_REDIRECT) { startIOUniformer(); } IOHook.hookNative(); Object mainThread = VirtualCore.mainThread(); IOHook.startDexOverride(); Context context = createPackageContext(data.appInfo.packageName); System.setProperty("java.io.tmpdir", context.getCacheDir().getAbsolutePath()); File codeCacheDir; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { codeCacheDir = context.getCodeCacheDir(); } else { codeCacheDir = context.getCacheDir(); } 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); } } Object boundApp = fixBoundApp(mBoundApplication); mBoundApplication.info = ContextImpl.mPackageInfo.get(context); mirror.android.app.ActivityThread.AppBindData.info.set(boundApp, data.info); VMRuntime.setTargetSdkVersion.call(VMRuntime.getRuntime.call(), data.appInfo.targetSdkVersion); boolean conflict = SpecialComponentList.isConflictingInstrumentation(packageName); if (!conflict) { PatchManager.getInstance().checkEnv(AppInstrumentation.class); } mInitialApplication = LoadedApk.makeApplication.call(data.info, false, null); VLog.d(TAG, "app: %s", mInitialApplication); mirror.android.app.ActivityThread.mInitialApplication.set(mainThread, mInitialApplication); ContextFixer.fixContext(mInitialApplication); List<ProviderInfo> providers = VPackageManager.get().queryContentProviders(data.processName, vuid, PackageManager.GET_META_DATA); if (providers != null) { installContentProviders(mInitialApplication, providers); } if (lock != null) { lock.open(); mTempLock = null; } try { mInstrumentation.callApplicationOnCreate(mInitialApplication); PatchManager.getInstance().checkEnv(HCallbackHook.class); if (conflict) { PatchManager.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 == null ? "NULL" : 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) { List<ThreadGroup> groups = mirror.java.lang.ThreadGroup.groups.get(root); 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 { ThreadGroup[] groups = ThreadGroupN.groups.get(root); 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); } } } } @SuppressLint("SdCardPath") private void startIOUniformer() { ApplicationInfo info = mBoundApplication.appInfo; IOHook.redirect("/data/data/" + info.packageName + "/", info.dataDir + "/"); IOHook.redirect("/data/user/0/" + info.packageName + "/", info.dataDir + "/"); IOHook.redirect(info.dataDir + "/lib", info.nativeLibraryDir); IORedirectDelegate delegate = VirtualCore.get().ioRedirectDelegate; if (delegate != null) { Map<String, String> ioRedirect = delegate.getIORedirect(); for (Map.Entry<String, String> entry : ioRedirect.entrySet()) IOHook.redirect(entry.getKey(), entry.getValue()); Map<String, String> reversedRedirect = delegate.getIOReversedRedirect(); for (Map.Entry<String, String> entry : reversedRedirect.entrySet()) IOHook.reversed(entry.getKey(), entry.getValue()); } IOHook.hook(); } private Context createPackageContext(String packageName) { try { Context hostContext = VirtualCore.get().getContext(); return hostContext.createPackageContext(packageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException(e); } } private Object fixBoundApp(AppBindData data) { // TODO: Using Native VM Hook to fix the `Camera` and `AudioRecord`. Object thread = VirtualCore.mainThread(); Object boundApp = mirror.android.app.ActivityThread.mBoundApplication.get(thread); mirror.android.app.ActivityThread.AppBindData.appInfo.set(boundApp, data.appInfo); mirror.android.app.ActivityThread.AppBindData.processName.set(boundApp, data.processName); mirror.android.app.ActivityThread.AppBindData.instrumentationName.set(boundApp, new ComponentName(data.appInfo.packageName, Instrumentation.class.getName())); return boundApp; } private void installContentProviders(Context app, List<ProviderInfo> providers) { long origId = Binder.clearCallingIdentity(); Object mainThread = VirtualCore.mainThread(); try { for (ProviderInfo cpi : providers) { if (cpi.enabled) { ActivityThread.installProvider(mainThread, app, cpi, null); } } } finally { Binder.restoreCallingIdentity(origId); } } @Override public IBinder acquireProviderClient(ProviderInfo info) { if (mTempLock != null) { mTempLock.block(); } if (!VClientImpl.get().isBound()) { VClientImpl.get().bindApplication(info.packageName, info.processName); } IInterface provider = null; String[] authorities = info.authority.split(";"); String authority = authorities.length == 0 ? info.authority : authorities[0]; ContentResolver resolver = VirtualCore.get().getContext().getContentResolver(); ContentProviderClient client = null; try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { client = resolver.acquireUnstableContentProviderClient(authority); } else { client = resolver.acquireContentProviderClient(authority); } } catch (Throwable e) { e.printStackTrace(); } if (client != null) { provider = mirror.android.content.ContentProviderClient.mContentProvider.get(client); client.release(); } return provider != null ? provider.asBinder() : null; } private void fixInstalledProviders() { clearSettingProvider(); Map clientMap = ActivityThread.mProviderMap.get(VirtualCore.mainThread()); boolean highApi = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; for (Object clientRecord : clientMap.values()) { if (highApi) { IInterface provider = ActivityThread.ProviderClientRecordJB.mProvider.get(clientRecord); Object holder = ActivityThread.ProviderClientRecordJB.mHolder.get(clientRecord); ProviderInfo info = IActivityManager.ContentProviderHolder.info.get(holder); if (holder != null && !info.authority.startsWith(StubManifest.STUB_CP_AUTHORITY)) { provider = ProviderHook.createProxy(true, info.authority, provider); ActivityThread.ProviderClientRecordJB.mProvider.set(clientRecord, provider); IActivityManager.ContentProviderHolder.provider.set(holder, provider); } } else { String authority = ActivityThread.ProviderClientRecord.mName.get(clientRecord); IInterface provider = ActivityThread.ProviderClientRecord.mProvider.get(clientRecord); if (provider != null && !authority.startsWith(StubManifest.STUB_CP_AUTHORITY)) { provider = ProviderHook.createProxy(true, authority, provider); ActivityThread.ProviderClientRecord.mProvider.set(clientRecord, provider); } } } } private void clearSettingProvider() { Object cache; if (Settings.System.TYPE != null) { cache = Settings.System.sNameValueCache.get(); if (cache != null) { Settings.NameValueCache.mContentProvider.set(cache, null); } } if (Settings.Secure.TYPE != null) { cache = Settings.Secure.sNameValueCache.get(); if (cache != null) { Settings.NameValueCache.mContentProvider.set(cache, null); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && Settings.Global.TYPE != null) { cache = Settings.Global.sNameValueCache.get(); if (cache != null) { Settings.NameValueCache.mContentProvider.set(cache, null); } } } @Override public void finishActivity(IBinder token) { VActivityManager.get().finishActivity(token); } @Override public void scheduleNewIntent(String creator, IBinder token, Intent intent) { NewIntentData data = new NewIntentData(); data.creator = creator; data.token = token; data.intent = intent; sendMessage(NEW_INTENT, data); } @Override public void scheduleReceiver(ComponentName component, Intent intent, PendingResultData resultData) { ReceiverData receiverData = new ReceiverData(); receiverData.resultData = resultData; receiverData.intent = intent; receiverData.component = component; sendMessage(RECEIVER, receiverData); } private void handleReceiver(ReceiverData data) { BroadcastReceiver.PendingResult result = data.resultData.build(); try { Context context = createPackageContext(data.component.getPackageName()); Context receiverContext = ContextImpl.getReceiverRestrictedContext.call(context); String className = data.component.getClassName(); BroadcastReceiver receiver = (BroadcastReceiver) context.getClassLoader().loadClass(className).newInstance(); mirror.android.content.BroadcastReceiver.setPendingResult.call(receiver, result); data.intent.setExtrasClassLoader(context.getClassLoader()); receiver.onReceive(receiverContext, data.intent); if (mirror.android.content.BroadcastReceiver.getPendingResult.call(receiver) != null) { result.finish(); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException( "Unable to start receiver " + data.component + ": " + e.toString(), e); } VActivityManager.get().broadcastFinish(data.resultData); } @Override public IBinder createProxyService(ComponentName component, IBinder binder) { return ProxyServiceFactory.getProxyService(getCurrentApplication(), component, binder); } @Override public String getDebugInfo() { return "process : " + VirtualRuntime.getProcessName() + "\n" + "initialPkg : " + VirtualRuntime.getInitialPackageName() + "\n" + "vuid : " + vuid; } private static class RootThreadGroup extends ThreadGroup { public RootThreadGroup(ThreadGroup parent) { super(parent, "VA-Root"); } @Override public void uncaughtException(Thread t, Throwable e) { VLog.e("uncaught", e); if (VirtualCore.get().uncheckedExceptionDelegate != null) VirtualCore.get().uncheckedExceptionDelegate.onThreadGroupUncaughtException(t, e); System.exit(0); } } private final class NewIntentData { String creator; IBinder token; Intent intent; } private final class AppBindData { String processName; ApplicationInfo appInfo; List<ProviderInfo> providers; Object info; } private final class ReceiverData { PendingResultData resultData; Intent intent; ComponentName component; } private class H extends Handler { private H() { super(Looper.getMainLooper()); } @Override public void handleMessage(Message msg) { switch (msg.what) { case NEW_INTENT: { handleNewIntent((NewIntentData) msg.obj); } break; case RECEIVER: { handleReceiver((ReceiverData) msg.obj); } } } } }