Skip to content

Commit ce37cfd

Browse files
authored
Improve spoofing of X-Requested-With header to support newer WebView versions (#3) (#517)
* Override getPackageName for WebView request spoofing Override getPackageName to spoof package name in WebView requests based on call stack. * Update minimum WebView version and add constants
1 parent e2916f2 commit ce37cfd

File tree

2 files changed

+59
-1
lines changed
  • app/src/main/java/eu/kanade/tachiyomi
  • core/main/src/androidMain/kotlin/eu/kanade/tachiyomi/util/system

2 files changed

+59
-1
lines changed

app/src/main/java/eu/kanade/tachiyomi/App.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import android.content.Intent
1111
import android.content.IntentFilter
1212
import android.content.pm.PackageManager
1313
import android.os.Build
14+
import android.os.Looper
1415
import android.webkit.WebView
1516
import androidx.appcompat.app.AppCompatDelegate
1617
import androidx.core.app.ActivityCompat
@@ -59,6 +60,7 @@ import eu.kanade.tachiyomi.util.system.launchIO
5960
import eu.kanade.tachiyomi.util.system.localeContext
6061
import eu.kanade.tachiyomi.util.system.notification
6162
import eu.kanade.tachiyomi.util.system.setToDefault
63+
import eu.kanade.tachiyomi.util.system.WebViewUtil
6264
import java.security.Security
6365
import kotlinx.coroutines.Dispatchers
6466
import kotlinx.coroutines.flow.launchIn
@@ -221,6 +223,22 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
221223
MultiDex.install(this)
222224
}
223225

226+
override fun getPackageName(): String {
227+
try {
228+
// Override the value passed as X-Requested-With in WebView requests
229+
val stackTrace = Looper.getMainLooper().thread.stackTrace
230+
val isChromiumCall = stackTrace.any { trace ->
231+
trace.className.lowercase() in setOf("org.chromium.base.buildinfo", "org.chromium.base.apkinfo") &&
232+
trace.methodName.lowercase() in setOf("getall", "getpackagename", "<init>")
233+
}
234+
235+
if (isChromiumCall) return WebViewUtil.spoofedPackageName(applicationContext)
236+
} catch (_: Exception) {
237+
}
238+
239+
return super.getPackageName()
240+
}
241+
224242
override fun onLowMemory() {
225243
super.onLowMemory()
226244
LibraryPresenter.onLowMemory()

core/main/src/androidMain/kotlin/eu/kanade/tachiyomi/util/system/WebViewUtil.kt

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,35 @@ import kotlin.coroutines.resume
1111
import kotlinx.coroutines.suspendCancellableCoroutine
1212

1313
object WebViewUtil {
14-
const val MINIMUM_WEBVIEW_VERSION = 114
14+
private const val CHROME_PACKAGE = "com.android.chrome"
15+
private const val SYSTEM_SETTINGS_PACKAGE = "com.android.settings"
16+
17+
const val MINIMUM_WEBVIEW_VERSION = 118
18+
19+
/**
20+
* Uses the WebView's user agent string to create something similar to what Chrome on Android
21+
* would return.
22+
*
23+
* Example of WebView user agent string:
24+
* Mozilla/5.0 (Linux; Android 13; Pixel 7 Build/TQ3A.230901.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36
25+
*
26+
* Example of Chrome on Android:
27+
* Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.3
28+
*/
29+
fun getInferredUserAgent(context: Context): String {
30+
return WebView(context)
31+
.getDefaultUserAgentString()
32+
.replace("; Android .*?\\)".toRegex(), "; Android 10; K)")
33+
.replace("Version/.* Chrome/".toRegex(), "Chrome/")
34+
}
35+
36+
fun getVersion(context: Context): String {
37+
val webView = WebView.getCurrentWebViewPackage() ?: return "how did you get here?"
38+
val pm = context.packageManager
39+
val label = webView.applicationInfo!!.loadLabel(pm)
40+
val version = webView.versionName
41+
return "$label $version"
42+
}
1543

1644
fun supportsWebView(context: Context): Boolean {
1745
try {
@@ -25,6 +53,16 @@ object WebViewUtil {
2553

2654
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)
2755
}
56+
57+
fun spoofedPackageName(context: Context): String {
58+
return try {
59+
context.packageManager.getPackageInfo(CHROME_PACKAGE, PackageManager.GET_META_DATA)
60+
61+
CHROME_PACKAGE
62+
} catch (_: PackageManager.NameNotFoundException) {
63+
SYSTEM_SETTINGS_PACKAGE
64+
}
65+
}
2866
}
2967

3068
fun WebView.isOutdated(): Boolean {
@@ -43,6 +81,8 @@ fun WebView.setDefaultSettings() {
4381
displayZoomControls = false
4482
cacheMode = WebSettings.LOAD_DEFAULT
4583
}
84+
85+
CookieManager.getInstance().acceptThirdPartyCookies(this)
4686
}
4787

4888
private fun WebView.getWebViewMajorVersion(): Int {

0 commit comments

Comments
 (0)