android webview在系统签名文件下报错解决

Posted 不二菜菜子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android webview在系统签名文件下报错解决相关的知识,希望对你有一定的参考价值。

最近定制系统应用开发用到了系统签名,有个功能用到了WebView这个控件,

正常不添加系统签名情况下WebView是正常的,添加系统签名后Webview崩溃报错:

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

详细看了下错误信息

发现这一行:

For security reasons, WebView is not allowed in privileged processes 出于安全原因,特权进程中不允许使用WebView

换句话说就是因为安全性问题系统级应用不允许使用WebView这个控件;

那么是为什么不允许在系统级应用使用呢?那么问题就来了;java嘛,面向Google编程。出事先Google,90%以上的问题都有先辈碰到过,实在没有再去翻源码,查原因;

随即Google 百度一番,得知原因

WebView这个控件在首次运行时会先调用getProvider()方法检测uid,如果是系统进程或者root进程,就直接抛出异常
为什么会有这种安全机制呢?因为WebView允许运行js,如果用户通过js注入,那么js就可以肆无忌惮的使用系统权限,
(系统级权限的APP不管要干嘛都可以无需做任何申请提示,可谓是为所欲为)这无疑是一个大漏洞,门户大开。
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);
            
        
    

解决方法:

一:

如果是自编系统直接暴力改造系统framework层,直接干掉uid的判断;从根本上解决;

二:

从WebViewFactory这个类的GetProvider方法上看,在抛出异常之前有个判断,如果 sProviderInstance 等空就开始检测是不是在系统进程或者ROOT进程下运行

既然知道了有这一层判断,那么我们是否可以尝试通过模仿系统创建sProviderInstance一开始就手动构造个sProviderInstance 从而达到欺骗系统绕过这段检测呢?

看看系统是如何创建sProviderInstance的。这是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);
        
    

 通过查看以上源代码可以得知其实系统也是通过反射来创建的,返回值是一个WebViewFactoryProvider的类,可以看到系统会首先加载CHROMIUM_WEBVIEW_FACTORY,也就是使用Chrome内核的WebView。这个方法是静态的,那么我们就可以模仿一下

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

在使用WebView这个控件前也就是onCreate()方法调用setContentView()布局前,调用一次hookWebView()方法,手动创建个sProviderInstance对象,从而绕过系统的检测;解决系统签名下WebView不允许使用的限制,

我用的定制系统是6.0的,后续的高版本源码是否有无被修改过,暂时不得而知,后续有时间再去翻翻高版本的源代码;

来源:Android Webview在系统签名下会崩溃报错的解决方案_阿贾克斯`的博客-CSDN博客

以上是关于android webview在系统签名文件下报错解决的主要内容,如果未能解决你的问题,请参考以下文章

64位系统下报错:cannot load OCI DLL,126

shell文件管理jenkins构建过程---window环境下报错:找不到shell文件

转:vim模式下报错E37: No write since last change (add ! to override)

CI ~ CodeIgniter在nginx下报错404 Not Found ,解决办法

CI ~ CodeIgniter在nginx下报错404 Not Found ,解决办法

servlet 中web.xml在eclipse下报错?