VirtualApp 소스 분석 – (1)VA 시작

24442 단어 android 소스 학습
VirtualApp(이하VA라고 약칭)은 원본을 개발한 안드로이드 앱 가상화 엔진으로 다른 응용 프로그램이 이 가상 공간에서 실행할 수 있도록 가상 공간을 제공하고 응용 멀티플렉스(중복 설치 가능)를 실현한다.
VA에서 host 응용 프로그램(VA 자체)과 client 응용 프로그램(VA를 통해 설치된 응용 프로그램)은 같은 uid를 가지고 있다.실행할 때는 일반적으로 세 개의 프로세스가 포함됩니다.
  • 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, MultiDex Application에서 계승된 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()는 우선 중점 내용인 정적 Virtual Core 대상을 되돌려준 다음에 이 대상의 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의 getPackage Manager를 호출하는 방법은 패키지 관리자 unHookPackage Manager(뒤에 이 대상에 대해 hook를 할 것), 패키지 관리자의 getPackage Info 방법은 패키지 정보 Package Info 대상인hostPkgInfo를 획득한다.step 3. 다음 호출detectProcessType()은 주로 패키지 이름, 메인 프로세스 이름과 현재 프로세스 이름을 얻고 비교를 통해 프로세스 유형을 판단한다. 주로 네 가지가 있는데 그것이 바로 메인 프로세스, 서비스 프로세스(x로 끝), 클라이언트 프로세스(Vactivity Manager의isAppProcess() 방법)와 하위 프로세스이다.step 4. Invocation StubManager 클래스의 대상인 invocation StubManager를 만들고 이 대상의 init()injectAll() 방법을 각각 호출하여 Invocation StubManager의 초기화와 실행 시스템의 주입을 순서대로 완성한다.step 5. 일부 상하문의 복구와 검사를 완성하다.
    그리고 네 번째 단계의 주입 과정을 자세히 살펴보자. 먼저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: 직접 반환,
  • ServerProcess:Activity Manager Stub와 Package Manager Stub 객체 추가;
  • VAppProcess:LibCoreStub,ActivityManagerStub,PackageManagerStub 등을 추가한다.

  • 설명: 여기에 주입하는 것은 사실 자바층의 hook 조작을 완성하는 것이다. 자신의 앱은 당연히 hook이 필요하지 않고 서비스 프로세스는 응용 프로그램을 관리해야 하기 때문에 hook AMS와 PMS가 필요하다. 응용 프로세스에 대해서는 hook 전체 프레임워크 프레임워크가 필요하고 모든 호출이 VA의 프레임워크로 바뀌도록 보장한다.
    추가 작업:
        private void addInjector(IInjector IInjector) {
            mInjectors.put(IInjector.getClass(), IInjector);
        }

    Map에 새 Stub 객체 추가
        public ActivityManagerStub() {
            super(new MethodInvocationStub<>(ActivityManagerNative.getDefault.call()));
        }

    이 구조 함수에서 먼저 Method Invocation Stub 대상이 새로 만들어졌습니다. 파라미터는Activity Manager NativegetDefault()를 반사적으로 호출해서 얻은 인터페이스 대상입니다.Method Invocation Stub의 구조 함수를 보면 최종적으로 이 함수로 바뀔 것입니다.
        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());
            }
        }

    먼저 첫 번째 인자, 즉 앞에서 전송된Activity ManagerNative 실례 대상을 저장한 다음에 이 실례 대상이 Class 대상에 대응하는 모든 인터페이스proxy Interfaces를 가져와 Proxy를 통과합니다.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()입니다. 마지막으로 Instrumentation이라는 클래스에 대한 실행은 게으름 주입입니다. 왜냐하면 이 클래스의 디자인 응용 프로그램과activity 생명주기 추적 테스트이기 때문입니다.Activity ManagerStub의 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);
        }

    먼저 ActivityManagerNative가 있습니다.gDefault에서 AMN의 실례 대상을 가져오고 getInvocationStub ().getProxyInterface()는 프록시 객체의 인터페이스 객체를 가져와 설정합니다.다음 getInvocationStub ().BaseInterface () 에서 가져온 원시 인터페이스 대상은 매개 변수로 전송되며, BinderInvocation Stub 대상을 새로 만듭니다. 이 안에는method 에이전트의 추가가 완료되었습니다. 마지막으로 BinderInvocation Stub 대상인 hookAMBinder의 copyMethodProxies() 방법을 호출하여 모든 방법 에이전트를 MethodInvocation Stub의 맵 테이블에 추가합니다.
    이로써 Invocation StubManager의 작업은 모두 끝났고(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.get()를 통해 VirtualCore 정적 대상virtualCore를 가져오고 VirtualCore.VirtualInitializer()를 호출하여 네 가지 프로세스 유형을 초기화하는 경우:
  • 메인 프로세스가 먼저 Once.initialise(VApp.this)를 실행하여 초기화 작업을 진행한다.다음 문장에서 Flurry SDK를 초기화하고 로그를 사용하고build 방법을 호출합니다. 두 파라미터는 각각 응용 프로그램과 API 입니다.Key, 애플리케이션의 사용을 통계하는 데 사용됩니다. (무시할 수 있습니다.)
  • 클라이언트 프로세스는 주로 가상 코어의 setComponentDelegate, setPhoneInfoDelegate와 setTaskDescriptionDelegate 방법을 호출했는데 주로 자신의 에이전트를 설정하기 위해서이다. 예를 들어 구성된 생명주기 전후에 자신의 조작을 추가하고 장치 정보를 위조하는 등이다.
  • 서비스 프로세스는 우선virtualCore의setappRequestListener 방법을 호출하여 자신의 응용 프로그램 요청 감청기를 위조했다.그리고 가상 코어의addVisibleOutsidePackage 방법을 호출하여 패키지가 외부에 보일 수 있도록 설정합니다.

  • 이로써 VA의 Application 시작 프로세스가 분석됩니다.

    좋은 웹페이지 즐겨찾기