Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 97 additions & 13 deletions app/src/main/java/me/iacn/biliroaming/BiliBiliPackage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
}

Expand Down
67 changes: 39 additions & 28 deletions app/src/main/java/me/iacn/biliroaming/hook/EnvHook.kt
Original file line number Diff line number Diff line change
@@ -1,44 +1,55 @@
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

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<String, String?>
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<String, String?>
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)
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/me/iacn/biliroaming/hook/SubtitleHook.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/jni/dex_builder
12 changes: 12 additions & 0 deletions app/src/main/proto/me/iacn/biliroaming/configs.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}