内容简介:,最近在布局开发中使用了 Webview,然而程序在运行时直接 carsh 了,查看 log,报错信息如下:原来是 Android 官方为了安全考虑,不允许特权进程(即系统进程)中使用 Webview。可是我们确实需要使用 Webview 该怎么办呢?跟踪源码发现在
AndroidManifest.xml
中配置了
android:sharedUserId="android.uid.system"
,最近在布局开发中使用了 Webview,然而程序在运行时直接 carsh 了,查看 log,报错信息如下:
Caused by: java.lang.UnsupportedOperationException: For security reasons, WebView is not allowed in privileged processes at android.webkit.WebViewFactory.getProvider(WebViewFactory.java:155) at android.webkit.CookieManager.getInstance(CookieManager.java:42) 复制代码
原来是 Android 官方为了安全考虑,不允许特权进程(即系统进程)中使用 Webview。可是我们确实需要使用 Webview 该怎么办呢?
跟踪源码发现在 WebViewFactory
类中的 getProvider()
方法中有如下代码(API版本:26):
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"); } 复制代码
可以看到第一次使用时,系统会检查 sProviderInstance
是否为空,不为空的话直接返回创建过的实例,否则就判断当前 uid,如果是 Root/System/Phone/NFC/Bluetooth 的话就会抛出异常。 sProviderInstance
是 WebViewFactoryProvider
的对象,那么,我们可以考虑提前创建 sProviderInstance
实例,这样就可以绕过系统检查,从而避免异常的抛出。
这时候就需要用到我们的 Hook 思想了,首先我们需要找到一个合适的 Hook 点。 sProviderInstance
是 static 变量,恰恰是一个非常合适的 Hook 点。这里需要用到反射的方法,代码如下:
/** * 避免系统检查抛出异常 */ public static void checkWebView() { int sdkInt = Build.VERSION.SDK_INT; try { //拿到 WebViewFactory 类 Class<?> factoryClass = Class.forName("android.webkit.WebViewFactory"); //拿到类对应的 field Field field = factoryClass.getDeclaredField("sProviderInstance"); //field为private,设置为可访问的 field.setAccessible(true); //拿到 WebViewFactory 的 sProviderInstance 实例 //sProviderInstance 是 static 类型,不需要传入具体对象 Object sProviderInstance = field.get(null); if (sProviderInstance != null) { return; } Method getProviderClassMethod; if (sdkInt > 22) { getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass"); } else if (sdkInt == 22) { getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass"); } else { 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 sProviderInstance = providerConstructor.newInstance(declaredConstructor.newInstance()); //完成 sProviderInstance 赋值 field.set("sProviderInstance", sProviderInstance); } } catch (Throwable e) { e.printStackTrace(); } } 复制代码
这样我们就完成了 checkWebView()
方法,在使用WebView前只需要调用一次 checkWebView()
方法就可以成功绕过系统的检查,顺利在系统进程里使用WebView。
总结:在解决这次问题的过程中最大的收获是了解并使用了 Hook , Hook 功能十分强大,远非三言两语可以讲清楚,大家有兴趣可以进一步学习了解。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。