본문 바로가기
개발/Android

[Android] java.lang.UnsupportedOperationException: For security reasons, WebView is not allowed in privileged processes

by blacktree 2022. 12. 2.
반응형

시스템 애플리케이션에서 WebView를 생성할 때 UnsupportedOperationException이 보고 되고 "보안상의 이유로 WebView는 권한 있는 프로세스에서 허용되지 않습니다"라는 질문이 표시됩니다. https://cs.android.com/ 에 따라 소스코드 사이트에서 WebView 관련 클래스를 던진 질문 찾아 다음과 같이 클래스에서 구체적으로 오류를 발생시키는 코드를 찾습니다.

 

 

1. 문제의 원인을 찾기 위해 WebViewFactoryProvider의 getProvider()을 확인

@UnsupportedAppUsage
static WebViewFactoryProvider getProvider() {
    synchronized (sProviderLock) {
        // For now the main purpose of this function (and the factory abstraction) is to keep
        // us honest and minimize usage of WebView internals when binding the proxy.
        if (sProviderInstance != null) return sProviderInstance;

        final int uid = android.os.Process.myUid();
        if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
                || uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
                || uid == android.os.Process.BLUETOOTH_UID) {
            throw new UnsupportedOperationException(
                    "For security reasons, WebView is not allowed in privileged processes");
        }

        if (!isWebViewSupported()) {
            // Device doesn't support WebView; don't try to load it, just throw.
            throw new UnsupportedOperationException();
        }

        if (sWebViewDisabled) {
            throw new IllegalStateException(
                    "WebView.disableWebView() was called: WebView is disabled");
        }

        Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
        try {
            Class<WebViewFactoryProvider> providerClass = getProviderClass();
            Method staticFactory = null;
            try {
                staticFactory = providerClass.getMethod(
                    CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class);
            } catch (Exception e) {
                if (DEBUG) {
                    Log.w(LOGTAG, "error instantiating provider with static factory method", e);
                }
            }

            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactoryProvider invocation");
            try {
                sProviderInstance = (WebViewFactoryProvider)
                        staticFactory.invoke(null, new WebViewDelegate());
                if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
                return sProviderInstance;
            } catch (Exception e) {
                Log.e(LOGTAG, "error instantiating provider", e);
                throw new AndroidRuntimeException(e);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
        }
    }
}

 

2. 애플리케이션 Uid가 아래와 같은 경우 UnsupportedOperationException을 발생하는 것을 확인

 final int uid = android.os.Process.myUid();
        if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID
                || uid == android.os.Process.PHONE_UID || uid == android.os.Process.NFC_UID
                || uid == android.os.Process.BLUETOOTH_UID) {
            throw new UnsupportedOperationException(
                    "For security reasons, WebView is not allowed in privileged processes");
        }

 

3. 여기서 우리는 웹뷰를 생성하기 전에 미리 sProviderInstance를 생성해 놓으면 null이 아니면 다음 코드가 진행되지 않는다는 것을 확인

 if (sProviderInstance != null) return sProviderInstance;

 

4. Hook을 통해 sProviderInstance 생성하기 

  1. Hook을 통해 sProviderInstance를 생성한 후 리플렉션을 통해 얻은 sProviderInstance의 Feild를 설정하여 객체를 설정하기만 하면 됩니다.
  2. sProviderInstance를 생성하려면 일부 클래스를 가져오려면 리플렉션이 필요합니다.
  3. 마지막으로 Application에서 HookUtils.hookWebView()를 실행합니다.
  4. sProviderInstance가 성공적으로 생성된다면 성공한 것입니다.
@SuppressLint("SoonBlockedPrivateApi")
public static void hookWebView(){
    int sdkInt = Build.VERSION.SDK_INT;
    try {
        Class<?> factoryClass = Class.forName("android.webkit.WebViewFactory");
        Field field = factoryClass.getDeclaredField("sProviderInstance");
        field.setAccessible(true);
        Object sProviderInstance = field.get(null);
        if (sProviderInstance != null) {
            Log.i("sProviderInstance isn't null");
            return;
        }

        Method getProviderClassMethod;
        if (sdkInt > 22) {
            getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
        } else if (sdkInt == 22) {
            getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
        } else {
            Log.i("Don't need to Hook WebView");
            return;
        }
        getProviderClassMethod.setAccessible(true);
        Class<?> factoryProviderClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
        Class<?> delegateClass = Class.forName("android.webkit.WebViewDelegate");
        Constructor<?> delegateConstructor = delegateClass.getDeclaredConstructor();
        delegateConstructor.setAccessible(true);
        if(sdkInt < 26){//低於Android O版本
            Constructor<?> providerConstructor = factoryProviderClass.getConstructor(delegateClass);
            if (providerConstructor != null) {
                providerConstructor.setAccessible(true);
                sProviderInstance = providerConstructor.newInstance(delegateConstructor.newInstance());
            }
        } else {
            Field chromiumMethodName = factoryClass.getDeclaredField("CHROMIUM_WEBVIEW_FACTORY_METHOD");
            chromiumMethodName.setAccessible(true);
            String chromiumMethodNameStr = (String)chromiumMethodName.get(null);
            if (chromiumMethodNameStr == null) {
                chromiumMethodNameStr = "create";
            }
            Method staticFactory = factoryProviderClass.getMethod(chromiumMethodNameStr, delegateClass);
            if (staticFactory!=null){
                sProviderInstance = staticFactory.invoke(null, delegateConstructor.newInstance());
            }
        }

        if (sProviderInstance != null){
            field.set("sProviderInstance", sProviderInstance);
            Log.i("Hook success!");
        } else {
            Log.i("Hook failed!");
        }
    } catch (Throwable e) {
        Log.w(e.getMessage());
    }
}

 

728x90
반응형

댓글