Android webview reports an error under the system signature file

Recently, the custom system application development uses the system signature, and there is a function that uses the WebView control.

WebView is normal when the system signature is not added. After adding the system signature, Webview crashes and reports an error:

Binary XML file line #46: Binary XML file line #46: Error inflating class android.webkit.WebView

Read the error message in detail

found this line:

For security reasons, WebView is not allowed in privileged  processes

In other words, system-level applications are not allowed to use the WebView control because of security issues;

So why is it not allowed to be used in system-level applications? Then the question arises; java, for Google programming. Before Google happened, more than 90% of the problems had been encountered by predecessors. In fact, I didn't go to the source code to find out the reason;

Immediately after Google Baidu, I learned the reason

When the WebView control is run for the first time, it will call the getProvider() method to detect the uid. If it is a system process or a root process, it will directly throw an exception.
Why is there such a security mechanism? Because WebView allows js to run, if the user injects through js, then js can use system permissions unscrupulously,
(Apps with system-level permissions don’t need to make any application prompts no matter what they want to do. It can be said that they can do whatever they want.) This is undoubtedly a big loophole and the door is wide open.
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) {
                throw new UnsupportedOperationException(
                        "For security reasons, WebView is not allowed in privileged processes");
            }
 
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
            try {
                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
                loadNativeLibrary();
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
 
                Class<WebViewFactoryProvider> providerClass;
                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getFactoryClass()");
                try {
                    providerClass = getFactoryClass();
                } catch (ClassNotFoundException e) {
                    Log.e(LOGTAG, "error loading provider", e);
                    throw new AndroidRuntimeException(e);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
                }
 
                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
                Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()");
                try {
                    try {
                        sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
                                .newInstance(new WebViewDelegate());
                    } catch (Exception e) {
                        sProviderInstance = providerClass.newInstance();
                    }
                    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);
                    StrictMode.setThreadPolicy(oldPolicy);
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
        }
    }

Solution:

one:

If it is a self-made system that directly violently transforms the system framework layer, it directly kills the judgment of uid; it is fundamentally solved;

two:

From the GetProvider method of the WebViewFactory class, there is a judgment before throwing an exception. If sProviderInstance is empty, it will start to detect whether it is running under the system process or the ROOT process.

Now that we know that there is this level of judgment, can we try to create a sProviderInstance by imitating the system and manually construct a sProviderInstance from the beginning so as to deceive the system to bypass this detection?

See how the system creates sProviderInstance. This is the source code of getFactoryClass

private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException {
        Application initialApplication = AppGlobals.getInitialApplication();
        try {
            // First fetch the package info so we can log the webview package version.
            String packageName = getWebViewPackageName();
            sPackageInfo = initialApplication.getPackageManager().getPackageInfo(packageName, 0);
            Log.i(LOGTAG, "Loading " + packageName + " version " + sPackageInfo.versionName +
                          " (code " + sPackageInfo.versionCode + ")");
 
            // Construct a package context to load the Java code into the current app.
            Context webViewContext = initialApplication.createPackageContext(packageName,
                    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
            initialApplication.getAssets().addAssetPath(
                    webViewContext.getApplicationInfo().sourceDir);
            ClassLoader clazzLoader = webViewContext.getClassLoader();
            Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
            try {
                return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, true,
                                                                     clazzLoader);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
            }
        } catch (PackageManager.NameNotFoundException e) {
            // If the package doesn't exist, then try loading the null WebView instead.
            // If that succeeds, then this is a device without WebView support; if it fails then
            // swallow the failure, complain that the real WebView is missing and rethrow the
            // original exception.
            try {
                return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
            } catch (ClassNotFoundException e2) {
                // Ignore.
            }
            Log.e(LOGTAG, "Chromium WebView package does not exist", e);
            throw new AndroidRuntimeException(e);
        }
    }

 By viewing the above source code, we can know that the system is actually created by reflection, and the return value is a WebViewFactoryProvider class. You can see that the system will first load CHROMIUM_WEBVIEW_FACTORY, which is the WebView using the Chrome kernel. This method is static, so we can imitate it

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.debug("sProviderInstance isn't null");
                return;
            }
            Method getProviderClassMethod;
            if (sdkInt > 22) {
                getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
            } else if (sdkInt == 22) {
                getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
            } else {
                log.info("Don't need to Hook WebView");
                return;
            }
            getProviderClassMethod.setAccessible(true);
            Class<?> providerClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
            Class<?> delegateClass = Class.forName("android.webkit.WebViewDelegate");
            Constructor<?> providerConstructor = providerClass.getConstructor(delegateClass);
            if (providerConstructor != null) {
                providerConstructor.setAccessible(true);
                Constructor<?> declaredConstructor = delegateClass.getDeclaredConstructor();
                declaredConstructor.setAccessible(true);
                sProviderInstance = providerConstructor.newInstance(declaredConstructor.newInstance());
                log.debug("sProviderInstance:{}", sProviderInstance);
                field.set("sProviderInstance", sProviderInstance);
            }
            log.debug("Hook done!");
        } catch (Throwable e) {
            log.error(e);
        }
    }

Before using the WebView control, that is, before the onCreate() method calls the setContentView() layout, call the hookWebView() method once to manually create an sProviderInstance object, thereby bypassing the system's detection; to solve the restrictions that WebView is not allowed to use under the system signature,

The customization system I use is 6.0. Whether the source code of the subsequent higher version has been modified is temporarily unknown, and I will look through the source code of the higher version later when I have time;

Source: Android Webview will crash and report an error solution under the system signature_Ajax` Blog-CSDN Blog

Guess you like

Origin blog.csdn.net/guliang28/article/details/126971097