diff --git a/app/src/main/java/me/iacn/biliroaming/BiliBiliPackage.kt b/app/src/main/java/me/iacn/biliroaming/BiliBiliPackage.kt index 029037b755..3c72939098 100644 --- a/app/src/main/java/me/iacn/biliroaming/BiliBiliPackage.kt +++ b/app/src/main/java/me/iacn/biliroaming/BiliBiliPackage.kt @@ -174,6 +174,8 @@ class BiliBiliPackage constructor(private val mClassLoader: ClassLoader, mContex val liveRTCSourceServiceImplClass by Weak { mHookInfo.liveQuality.sourceService.class_ from mClassLoader } val defaultRequestInterceptClass by Weak { mHookInfo.liveQuality.interceptor.class_ from mClassLoader } val httpUrlClass by Weak { mHookInfo.okHttp.httpUrl.class_ from mClassLoader } + val preBuiltConfigClass by Weak { mHookInfo.preBuiltConfig.class_ from mClassLoader } + val dataSPClass by Weak { mHookInfo.dataSP.class_ from mClassLoader } // for v8.17.0+ val useNewMossFunc = instance.viewMossClass?.declaredMethods?.any { @@ -349,6 +351,11 @@ class BiliBiliPackage constructor(private val mClassLoader: ClassLoader, mContex fun httpUrlParseMethod() = mHookInfo.okHttp.httpUrl.parse.orNull + fun getPreBuiltConfigMethod() = mHookInfo.preBuiltConfig.get.orNull + + fun getDataSPMethod() = mHookInfo.dataSP.get.orNull + + private fun readHookInfo(context: Context): Configs.HookInfo { try { val hookInfoFile = File(context.cacheDir, Constant.HOOK_INFO_FILE_NAME) @@ -793,21 +800,50 @@ class BiliBiliPackage constructor(private val mClassLoader: ClassLoader, mContex }?.name ?: return@class_ } themeName = themeName { - val themeNameClassRegex = Regex("""^tv\.danmaku\.bili\.ui\.garb\.\w?$""") - val themeNameClass = classesList.filter { - it.startsWith("tv.danmaku.bili.ui.garb") && it.contains(themeNameClassRegex) - }.map { - it.findClass(classloader) - }.firstOrNull { c -> - c.declaredFields.count { - Modifier.isStatic(it.modifiers) && it.type == Map::class.java - } == 1 - } + val mainGarbClass = dexHelper.findMethodUsingString( + ".garb.GARB_CHANGE", + false, + -1, + -1, + null, + -1, + null, + null, + null, + true + ).asSequence().mapNotNull { + dexHelper.decodeMethodIndex(it)?.declaringClass + }.firstOrNull() ?: return@themeName + val id2NameIndex = dexHelper.findMethodUsingString( + "white", + false, + -1, + 1, + null, + dexHelper.encodeClassIndex(mainGarbClass), + null, + null, + null, + true + ).asSequence().firstOrNull() ?: return@themeName + val themeNameClass = dexHelper.findMethodInvoking( + id2NameIndex, + dexHelper.encodeClassIndex(Map::class.java), + 0, + null, + -1, + null, + null, + null, + true + ).asSequence().mapNotNull { + dexHelper.decodeMethodIndex(it)?.declaringClass + }.firstOrNull() ?: return@themeName class_ = class_ { - name = themeNameClass?.name ?: return@class_ + name = themeNameClass.name } field = field { - name = themeNameClass?.declaredFields?.firstOrNull { + name = themeNameClass.declaredFields.firstOrNull { it.type == Map::class.java && Modifier.isStatic(it.modifiers) }?.name ?: return@field @@ -2225,7 +2261,55 @@ class BiliBiliPackage constructor(private val mClassLoader: ClassLoader, mContex intercept = method { name = interceptMethod.name } } } - + preBuiltConfig = preBuiltConfig { + val getMap = dexHelper.findMethodUsingString( + "/config.json", + false, + -1, + -1, + null, + -1, + null, + null, + null, + true + ).asSequence().mapNotNull { dexHelper.decodeMethodIndex(it) }.firstOrNull() + ?: return@preBuiltConfig + class_ = class_ { name = getMap.declaringClass.name } + get = method { name = getMap.name } + } + dataSP = dataSP { + val spxClass = + ("com.bilibili.lib.blkv.SharedPrefX" from classloader) ?: return@dataSP + val blkvClass = ("com.bilibili.lib.blkv.BLKV" from classloader) ?: return@dataSP + val getBLSP = runCatchingOrNull { + blkvClass.getDeclaredMethod( + "getBLSharedPreferences", + Context::class.java, + File::class.java, + Boolean::class.javaPrimitiveType, + Int::class.javaPrimitiveType + ) + } ?: return@dataSP + val getDataSP = dexHelper.findMethodInvoked( + dexHelper.encodeMethodIndex(getBLSP), + dexHelper.encodeClassIndex(spxClass), + -1, + null, + -1, + null, + null, + null, + false + ).asSequence().mapNotNull { + dexHelper.decodeMethodIndex(it) + }.firstOrNull { + Log.d(it) + it.declaringClass.name.startsWith("com.bilibili.lib.blconfig.internal.TypedContext") + } ?: return@dataSP + class_ = class_ { name = getDataSP.declaringClass.name } + get = method { name = getDataSP.name } + } dexHelper.close() } diff --git a/app/src/main/java/me/iacn/biliroaming/hook/EnvHook.kt b/app/src/main/java/me/iacn/biliroaming/hook/EnvHook.kt index f7bc840e01..aafefb6626 100644 --- a/app/src/main/java/me/iacn/biliroaming/hook/EnvHook.kt +++ b/app/src/main/java/me/iacn/biliroaming/hook/EnvHook.kt @@ -1,6 +1,7 @@ package me.iacn.biliroaming.hook import android.content.SharedPreferences +import me.iacn.biliroaming.BiliBiliPackage.Companion.instance import me.iacn.biliroaming.utils.* import java.lang.reflect.Proxy import java.util.regex.Pattern @@ -8,37 +9,47 @@ import java.util.regex.Pattern class EnvHook(classLoader: ClassLoader) : BaseHook(classLoader) { override fun startHook() { Log.d("startHook: Env") - "com.bilibili.lib.blconfig.internal.EnvContext\$preBuiltConfig\$2".hookAfterMethod( - mClassLoader, - "invoke" - ) { param -> - @Suppress("UNCHECKED_CAST") - val result = param.result as MutableMap - for (config in configSet) { - (if (sPrefs.getBoolean( - config.config, - false - ) - ) config.trueValue else config.falseValue) - ?.let { result[config.key] = it } ?: result.remove(config.key) + + // EnvContext + instance.preBuiltConfigClass?.let { + val hooker: Hooker = hooker@ { param -> + @Suppress("UNCHECKED_CAST") + val result = param.result as MutableMap + for (config in configSet) { + (if (sPrefs.getBoolean( + config.config, + false + ) + ) config.trueValue else config.falseValue) + ?.let { result[config.key] = it } ?: result.remove(config.key) + } } + // v8.28.0 - ? + it.hookAfterMethod(instance.getPreBuiltConfigMethod(), hooker = hooker) + // ? - v8.48.0 .. + it.hookAfterMethod(instance.getPreBuiltConfigMethod(), it, hooker = hooker) } - "com.bilibili.lib.blconfig.internal.TypedContext\$dataSp\$2".hookAfterMethod( - mClassLoader, - "invoke" - ) { param -> - val result = param.result as SharedPreferences - // this indicates the proper instance - if (!result.contains("bv.enable_bv")) return@hookAfterMethod - for (config in configSet) { - (if (sPrefs.getBoolean( - config.config, - false - ) - ) config.trueValue else config.falseValue) - ?.let { result.edit().putString(config.key, it).apply() } - ?: result.edit().remove(config.key).apply() + + // TypedContext + instance.dataSPClass?.let { + val hooker: Hooker = hooker@ { param -> + val result = param.result as SharedPreferences + // this indicates the proper instance + if (!result.contains("bv.enable_bv")) return@hooker + for (config in configSet) { + (if (sPrefs.getBoolean( + config.config, + false + ) + ) config.trueValue else config.falseValue) + ?.let { result.edit().putString(config.key, it).apply() } + ?: result.edit().remove(config.key).apply() + } } + // v8.28.0 - ? + it.hookAfterMethod(instance.getDataSPMethod(), hooker = hooker) + // ? - v8.48.0 .. + it.hookAfterMethod(instance.getDataSPMethod(), it, hooker = hooker) } "com.bilibili.lib.blconfig.internal.OverrideConfig".findClassOrNull(mClassLoader) diff --git a/app/src/main/java/me/iacn/biliroaming/hook/SubtitleHook.kt b/app/src/main/java/me/iacn/biliroaming/hook/SubtitleHook.kt index 0298d38b6d..d78461706e 100644 --- a/app/src/main/java/me/iacn/biliroaming/hook/SubtitleHook.kt +++ b/app/src/main/java/me/iacn/biliroaming/hook/SubtitleHook.kt @@ -133,8 +133,9 @@ class SubtitleHook(classLoader: ClassLoader) : BaseHook(classLoader) { } private val offset by lazy { sPrefs.getInt("subtitle_offset", 0) } - private val closeText = + private val closeText by lazy { currentContext.getString(getResId("Player_option_subtitle_lan_doc_nodisplay", "string")) + } private var subtitleFont: Typeface? = null diff --git a/app/src/main/jni/dex_builder b/app/src/main/jni/dex_builder index cf707e414c..7a18c915af 160000 --- a/app/src/main/jni/dex_builder +++ b/app/src/main/jni/dex_builder @@ -1 +1 @@ -Subproject commit cf707e414c596b584482fb7fc5b4e891df35c148 +Subproject commit 7a18c915af1131f98106551bd5c10773c97ad87a diff --git a/app/src/main/proto/me/iacn/biliroaming/configs.proto b/app/src/main/proto/me/iacn/biliroaming/configs.proto index 88e3c0acec..ffdbcb1274 100644 --- a/app/src/main/proto/me/iacn/biliroaming/configs.proto +++ b/app/src/main/proto/me/iacn/biliroaming/configs.proto @@ -302,6 +302,16 @@ message LiveQuality { optional DefaultRequestIntercept interceptor = 3; } +message PreBuiltConfig { + optional Class class = 1; + optional Method get = 2; +} + +message DataSP { + optional Class class = 1; + optional Method get = 2; +} + message HookInfo { int64 last_update_time = 1; optional MapIds map_ids = 2; @@ -368,4 +378,6 @@ message HookInfo { optional Continuation continuation = 96; optional VipQualityTrialService vipQualityTrialService = 97; optional LiveQuality liveQuality = 98; + optional PreBuiltConfig preBuiltConfig = 99; + optional DataSP dataSP = 100; }