VirtualAppソース分析――(一)VA起動

24339 ワード

VirtualApp(以下VAと略称する)は、オープンソースのAndroid app仮想化エンジンであり、他のアプリケーションがこの仮想空間で実行され、アプリケーションのマルチオープン(再インストール可能)を実現する仮想空間を提供する.
VAではhostアプリケーション(VA自体)とclientアプリケーション(VAによってインストールされたアプリケーション)は同じuidを有する.通常、実行時には3つのプロセスが含まれます.
  • io.virtualapp、メインプロセス、VAのユーザーインタフェースと応用管理を担当する.
  • io.virtualapp:x、サービスプロセス、システムサービスを担当するエージェント;
  • io.virtual:pXXXは、clientアプリケーションを実行するためのプロセスであり、アプリケーションが起動すると、プロセス名がアプリケーションパッケージ名に更新される.

  • psコマンドで見てください.
    u0_a123   16545 256   1056344 84524 ffffffff 00000000 S io.virtualapp
    u0_a123   16607 256   909252 36140 ffffffff 00000000 S io.virtualapp:x
    u0_a123   16674 256   914872 40004 ffffffff 00000000 S com.example.test

    次に、ソースコードからVAの起動と実行の全過程を分析します.
    VAのApplicationはVAPp、パッケージパスはio.Virtualappは、MultiDexApplicationから継承されたApplicationです.このクラスはattachBaseContext()メソッドを書き換え、プログラムが起動したらまずこのメソッドを実行し、oncreate()メソッドを呼び出す.まず、このattachBaseContextメソッドを見てみましょう.
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(base);
            mPreferences = base.getSharedPreferences("va", Context.MODE_MULTI_PROCESS);
            VASettings.ENABLE_IO_REDIRECT = true;
            VASettings.ENABLE_INNER_SHORTCUT = false;
            try {
                VirtualCore.get().startup(base);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

    いくつかの属性を設定する以外に、主にこのような文があります:VirtualCore.get().startup(base); VirtualCoreはパッケージcomに定義されています.lody.virtual.client.coreのクラス、get()は、まず、重要なコンテンツの静的なVirtualCoreオブジェクトを返し、その後、オブジェクトのstartup()メソッドを呼び出して、このメソッドのコードを見て、いくつかの初期化操作を行います.
        public void startup(Context context) throws Throwable {
            if (!isStartUp) {
                // step 1
                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;
                // step 2
                this.context = context;
                mainThread = ActivityThread.currentActivityThread.call();
                unHookPackageManager = context.getPackageManager();
                hostPkgInfo = unHookPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS);
                IPCBus.initialize(new IServerCache() {
                    @Override
                    public void join(String serverName, IBinder binder) {
                        ServiceCache.addService(serverName, binder);
                    }
    
                    @Override
                    public IBinder query(String serverName) {
                        return ServiceManagerNative.getService(serverName);
                    }
                });
                // step 3
                detectProcessType();
                // step 4
                InvocationStubManager invocationStubManager = InvocationStubManager.getInstance();
                invocationStubManager.init();
                invocationStubManager.injectAll();
                // step 5
                ContextFixer.fixContext(context);
                isStartUp = true;
                if (initLock != null) {
                    initLock.open();
                    initLock = null;
                }
            }
        }

    step 1. まず、VAPpがアプリケーションのエントリとしてマルチプロセスの場合に複数回呼び出されるため、現在のプロセスがメインプロセスであるかどうかを判断します.step 2. 現在のスレッドmainThread、contextを呼び出すgetPackageManagerメソッドの順に取得パッケージマネージャunHookPackageManager(後でこのオブジェクトをhookする)、パッケージマネージャのgetPackageInfoメソッドのパケット情報PackageInfoオブジェクトhostPkgInfoを取得します.step 3. 次に、detectProcessType()を呼び出し、主にパッケージ名、メインプロセス名、および現在のプロセス名を取得し、プロセスタイプを比較して判断します.主に、メインプロセス、サービスプロセス(:xで終わる)、クライアントプロセス(VACtivityManagerのisAppProcess()メソッド)、およびサブプロセスの4つです.step 4. InvocationStubManagerクラスオブジェクトinvocationStubManagerを作成し、そのオブジェクトのinit()メソッドとinjectAll()メソッドをそれぞれ呼び出し、InvocationStubManagerの初期化と実行システムの注入を順次完了します.step 5. いくつかのコンテキストのリカバリとチェックを完了します.
    次に、第4のステップの注入プロセスを詳しく見てみましょう.まずinvocationStubManager.init()です.
        public void init() throws Throwable {
            if (isInit()) {
                throw new IllegalStateException("InvocationStubManager Has been initialized.");
            }
            injectInternal();
            sInit = true;
        }

    コードは簡単で、主にinjectInternal()という方法を呼び出して、コードを見続けます.
        private void injectInternal() throws Throwable {
            if (VirtualCore.get().isMainProcess()) {
                return;
            }
            if (VirtualCore.get().isServerProcess()) {
                addInjector(new ActivityManagerStub());
                addInjector(new PackageManagerStub());
                return;
            }
            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());
                addInjector(new BackupManagerStub());
                addInjector(new TelephonyStub());
                addInjector(new TelephonyRegistryStub());
                addInjector(new PhoneSubInfoStub());
                addInjector(new PowerManagerStub());
                addInjector(new AppWidgetManagerStub());
                addInjector(new AccountManagerStub());
                addInjector(new AudioManagerStub());
                addInjector(new SearchManagerStub());
                addInjector(new ContentServiceStub());
                addInjector(new ConnectivityStub());
    
                if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR2) {
                    addInjector(new VibratorStub());
                    addInjector(new WifiManagerStub());
                    addInjector(new BluetoothStub());
                    addInjector(new ContextHubServiceStub());
                }
                if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {
                    addInjector(new UserManagerStub());
                }
    
                if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {
                    addInjector(new DisplayStub());
                }
                if (Build.VERSION.SDK_INT >= LOLLIPOP) {
                    addInjector(new PersistentDataBlockServiceStub());
                    addInjector(new InputMethodManagerStub());
                    addInjector(new MmsStub());
                    addInjector(new SessionManagerStub());
                    addInjector(new JobServiceStub());
                    addInjector(new RestrictionStub());
                }
                if (Build.VERSION.SDK_INT >= KITKAT) {
                    addInjector(new AlarmManagerStub());
                    addInjector(new AppOpsManagerStub());
                    addInjector(new MediaRouterServiceStub());
                }
                if (Build.VERSION.SDK_INT >= LOLLIPOP_MR1) {
                    addInjector(new GraphicsStatsStub());
                    addInjector(new UsageStatsManagerStub());
                }
                if (Build.VERSION.SDK_INT >= M) {
                    addInjector(new FingerprintManagerStub());
                    addInjector(new NetworkManagementStub());
                }
                if (Build.VERSION.SDK_INT >= N) {
                    addInjector(new WifiScannerStub());
                    addInjector(new ShortcutServiceStub());
                    addInjector(new DevicePolicyManagerStub());
                }
                if (Build.VERSION.SDK_INT >= 26) {
                    addInjector(new AutoFillManagerStub());
                }
            }
        }

    簡単に見ると、現在のプロセスタイプを判断し、addInjectorメソッドでXXXStubオブジェクトを追加します.具体的には、次のようになります.
  • MainProcess:直接戻る;
  • Server Process:ActivityManagerStubオブジェクトとPackageManagerStubオブジェクトを追加します.
  • VAPppProcess:LibCoreStub、ActivityManagerStub、PackageManagerStubなどを追加する.

  • 説明:ここに注入するのは実はJava層のhook操作を完成することで、自身のappはもちろんhookを必要としないで、サービスプロセスはアプリケーションを管理する必要があるのでhook AMSとPMSを必要として、アプリケーションプロセスに対して、hook全体のframeworkフレームワークを必要として、すべての呼び出しがVAのフレームワークにリダイレクトすることを保証します.
    ここでの追加操作:
        private void addInjector(IInjector IInjector) {
            mInjectors.put(IInjector.getClass(), IInjector);
        }

    新しいStubオブジェクトをMapに追加することです
        public ActivityManagerStub() {
            super(new MethodInvocationStub<>(ActivityManagerNative.getDefault.call()));
        }

    このコンストラクション関数では、まずMethodInvocationStubオブジェクトが新規に作成され、パラメータは、ActivityManagerNativeを呼び出したgetDefault()を反射して取得されたインタフェースオブジェクトであることがわかります.MethodInvocationStubのコンストラクション関数を見ると、最終的にはこの関数に移動します.
        public MethodInvocationStub(T baseInterface, Class>... proxyInterfaces) {
            this.mBaseInterface = baseInterface;
            if (baseInterface != null) {
                if (proxyInterfaces == null) {
                    proxyInterfaces = MethodParameterUtils.getAllInterface(baseInterface.getClass());
                }
                mProxyInterface = (T) Proxy.newProxyInstance(baseInterface.getClass().getClassLoader(), proxyInterfaces, new HookInvocationHandler());
            } else {
                VLog.d(TAG, "Unable to build HookDelegate: %s.", getIdentityName());
            }
        }

    まず最初のパラメータ、すなわち前に入力ActivityManagerNativeインスタンスオブジェクトを保存し、次にこのインスタンスオブジェクトに対応するClassオブジェクトのすべてのインタフェースproxyInterfacesをProxyを介して取得する.新ProxyInstance()を使用して、Activity Managerの動的インタフェースエージェントmProxyInterfaceを構築します.ここで、HookInvocationHandlerオブジェクトはエージェントの実行ロジックを処理します.初期化の作業はここで終わり、次はinvocationStubManager.injectAll()です.
        void injectAll() throws Throwable {
            for (IInjector injector : mInjectors.values()) {
                injector.inject();
            }
            // XXX: Lazy inject the Instrumentation,
            addInjector(AppInstrumentation.getDefault());
        }

    実際には、追加された各Injectorsに対して対応するinject()が実行される遍歴であり、最後に、このクラス設計アプリケーションとactivityライフサイクルの追跡テストのため、Instrumentationというクラスに対して怠惰な注入が実行されることに注意してください.では、ActivityManagerStubのinject()の方法を見てみましょう.
        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);
        }

    まずはActivity ManagerNative.gDefaultはAMNのインスタンスオブジェクトを取得し、getInvocationStub()を取得する.getProxyInterface()エージェントオブジェクトのインタフェースオブジェクトを取得し、設定します.次はgetInvocationStub().getBaseInterface()で取得した元のインタフェースオブジェクトは、パラメータとして入力され、BinderInvocationStubオブジェクトが新規作成され、methodエージェントの追加が完了し、最後にBinderInvocationStubオブジェクトhookAMBinderのcopyMethodProxies()メソッドを呼び出し、すべてのメソッドをMethodInvocationStubのMapテーブルに代入します.
    これでInvocationStubManagerの作業はすべて終了し(to-do:hookの具体的な実装)、VAの初期化作業もすべて完了しました.次にoncreate()の方法を見てみましょう.
        public void onCreate() {
            gApp = this;
            super.onCreate();
            VirtualCore virtualCore = VirtualCore.get();
            virtualCore.initialize(new VirtualCore.VirtualInitializer() {
    
                @Override
                public void onMainProcess() {
                    Once.initialise(VApp.this);
                    new FlurryAgent.Builder()
                            .withLogEnabled(true)
                            .withListener(() -> {
                                // nothing
                            })
                            .build(VApp.this, "48RJJP7ZCZZBB6KMMWW5");
                }
    
                @Override
                public void onVirtualProcess() {
                    //listener components
                    virtualCore.setComponentDelegate(new MyComponentDelegate());
                    //fake phone imei,macAddress,BluetoothAddress
                    virtualCore.setPhoneInfoDelegate(new MyPhoneInfoDelegate());
                    //fake task description's icon and title
                    virtualCore.setTaskDescriptionDelegate(new MyTaskDescriptionDelegate());
                }
    
                @Override
                public void onServerProcess() {
                    virtualCore.setAppRequestListener(new MyAppRequestListener(VApp.this));
                    virtualCore.addVisibleOutsidePackage("com.tencent.mobileqq");
                    virtualCore.addVisibleOutsidePackage("com.tencent.mobileqqi");
                    virtualCore.addVisibleOutsidePackage("com.tencent.minihd.qq");
                    virtualCore.addVisibleOutsidePackage("com.tencent.qqlite");
                    virtualCore.addVisibleOutsidePackage("com.facebook.katana");
                    virtualCore.addVisibleOutsidePackage("com.whatsapp");
                    virtualCore.addVisibleOutsidePackage("com.tencent.mm");
                    virtualCore.addVisibleOutsidePackage("com.immomo.momo");
                }
            });
        }

    まず、VirtualCore静的オブジェクトvirtualCoreをVirtualCore.get()で取得し、VirtualCore.VirtualInitializer()を呼び出して4つのプロセスタイプを初期化します.
  • メインプロセスはまずOnce.initialise(VApp.this)を実行して初期化操作を行う.次に、Flurry SDKを初期化し、ログを有効にしてbuildメソッドを呼び出します.2つのパラメータはそれぞれアプリケーションとAPI_です.Keyは、アプリケーションの使用状況を統計するために使用されます(無視できます).
  • クライアントプロセスは主にvirtualCoreのsetComponentDelegate、setPhoneInfoDelegate、setTaskDescriptionDelegateメソッドを呼び出し、主に自分のエージェントを設定するためであり、例えば構築されたライフサイクルの前後に自分の操作を追加したり、設備の情報を偽造したりする.
  • サービスプロセスは、まずvirtualCoreのsetAppRequestListenerメソッドを呼び出し、自分のアプリケーション要求リスナーを偽造する.次にvirtualCoreのaddVisibleOutsidePackageメソッドを呼び出し、パッケージを外部に表示するように設定します.

  • これで、VAのアプリケーション起動プロセスは分析完了です.