반응형
시스템 애플리케이션에서 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 생성하기
- Hook을 통해 sProviderInstance를 생성한 후 리플렉션을 통해 얻은 sProviderInstance의 Feild를 설정하여 객체를 설정하기만 하면 됩니다.
- sProviderInstance를 생성하려면 일부 클래스를 가져오려면 리플렉션이 필요합니다.
- 마지막으로 Application에서 HookUtils.hookWebView()를 실행합니다.
- 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
반응형
'개발 > Android' 카테고리의 다른 글
[Android] Android Studio 테마 변경하는 방법 - 1 (0) | 2022.12.09 |
---|---|
[Android] google location - 현재 위치 가져오기 (0) | 2022.12.08 |
[Android] Glide 캐시 기능 사용 안하기 (0) | 2022.12.01 |
[Android] view layout width, height 변경 방법 (0) | 2022.11.29 |
[Android] android studio 설치 방법 (0) | 2022.11.26 |
댓글