Android系统进程无法加载WebView

当系统进程加载WebView时会报错:UnsupportedOperationException:
如下

java.lang.UnsupportedOperationException: For security reasons, WebView is not allowed in privileged processes
  at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:96)
  at android.webkit.WebView.getFactory(WebView.java:2194)
  at android.webkit.WebView.ensureProviderCreated(WebView.java:2189)
  at android.webkit.WebView.setOverScrollMode(WebView.java:2248)
  at android.view.View.<init>(View.java:3588)
  at android.view.View.<init>(View.java:3682)
  at android.view.ViewGroup.<init>(ViewGroup.java:497)
  at android.widget.AbsoluteLayout.<init>(AbsoluteLayout.java:55)
  at android.webkit.WebView.<init>(WebView.java:544)
  at android.webkit.WebView.<init>(WebView.java:489)
  at android.webkit.WebView.<init>(WebView.java:472)
  at android.webkit.WebView.<init>(WebView.java:459)
  at android.webkit.WebView.<init>(WebView.java:449)

通过log分析原因是:
因为安全问题,WebView禁止在系统进程中加载。这肯定是因为WebView众所周知的安全漏洞,可以通过JSBridge调Android内部函数,从而引起安全问题
有兴趣详细了解这个问题可以移步这里。WebView详解及安全问题

源码分析:

在源码中看下异常抛出的位置

WebViewFactory.jpg

代码具体位置AndroidXref 注:android 8.0
通过代码发现当WebView启动时调到这个getProvide函数,然后会判断进程的uid,如果是上面那几种就挂了。

解决办法 -- HOOK

思路-1:在使用 WebView 之前,我们先 Hook WebViewFactory,创建 sProviderInstance 对象,从而绕过系统检查。

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);
        }
    }

思路-2 : Hook进程UID
从源码中可以看出其uid的获取方式

final int uid = android.os.Process.myUid();

所以HOOK这个函数,使其返回的UID不在下面UID之列即可。
我是用Xposed HOOK的,不过应该可以按着这个思路用其他方式HOOK。

XposedBridge.hookAllMethods(Process.class, "myUid", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                Throwable t = new Throwable();
                StackTraceElement[] traces = t.getStackTrace();
                if(traces.length < 4)
                    return;
                StackTraceElement trace = traces[3];
                if(WebViewFactoryClassName.equals(trace.getClassName()) && GetProviderMethodName.equals(trace.getMethodName())){
                    param.setResult(10010);
                }
            }
        });

最后, 同学点个赞吧!!! 加个关注好么
参考文章
https://blog.csdn.net/codingnotes/article/details/79020542