feat(YouTube Music): add support versions 7.01.53 ~ 7.02.51

This commit is contained in:
inotia00 2024-05-26 16:01:24 +09:00
parent 904f1d6357
commit f15a5d5259
10 changed files with 119 additions and 30 deletions

View File

@ -10,6 +10,7 @@ import app.revanced.patches.music.misc.minimizedplayback.fingerprints.MinimizedP
import app.revanced.patches.music.misc.minimizedplayback.fingerprints.MusicBrowserServiceFingerprint
import app.revanced.patches.music.misc.minimizedplayback.fingerprints.PodCastConfigFingerprint
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.util.getStartsWithStringInstructionIndex
import app.revanced.util.getStringInstructionIndex
import app.revanced.util.getWalkerMethod
import app.revanced.util.patch.BaseBytecodePatch
@ -51,7 +52,7 @@ object MinimizedPlaybackPatch : BaseBytecodePatch(
MusicBrowserServiceFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val targetIndex =
getStringInstructionIndex("MBS: Return empty root for client: %s, isFullMediaBrowserEnabled: %b, is client browsable: %b, isRedAccount: %b")
getStartsWithStringInstructionIndex("MBS: Return empty root for client: %s")
for (index in targetIndex downTo 0) {
if (getInstruction(index).opcode != Opcode.INVOKE_VIRTUAL) continue

View File

@ -2,14 +2,17 @@ package app.revanced.patches.music.misc.minimizedplayback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.getStartsWithStringInstructionIndex
import com.android.tools.smali.dexlib2.AccessFlags
internal object MusicBrowserServiceFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Ljava/lang/String;", "Landroid/os/Bundle;"),
strings = listOf("MBS: Return empty root for client: %s, isFullMediaBrowserEnabled: %b, is client browsable: %b, isRedAccount: %b"),
customFingerprint = { methodDef, _ ->
methodDef.definingClass.endsWith("/MusicBrowserService;")
customFingerprint = custom@{ methodDef, _ ->
if (!methodDef.definingClass.endsWith("/MusicBrowserService;"))
return@custom false
methodDef.getStartsWithStringInstructionIndex("MBS: Return empty root for client: %s") > 0
}
)

View File

@ -10,8 +10,9 @@ object Constants {
"6.29.58",
"6.31.55",
"6.33.52",
"6.50.53",
"6.51.53"
"6.51.53",
"7.01.53",
"7.02.51",
)
)
)

View File

@ -16,7 +16,7 @@ import app.revanced.patches.music.utils.settings.fingerprints.GoogleApiActivityF
import app.revanced.patches.music.utils.settings.fingerprints.PreferenceFingerprint
import app.revanced.patches.music.utils.settings.fingerprints.SettingsHeadersFragmentFingerprint
import app.revanced.patches.shared.integrations.Constants.INTEGRATIONS_UTILS_CLASS_DESCRIPTOR
import app.revanced.patches.shared.settings.fingerprints.SharedSettingFingerprint
import app.revanced.patches.shared.fingerprints.SharedSettingFingerprint
import app.revanced.util.getTargetIndex
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode

View File

@ -14,7 +14,7 @@ import app.revanced.patches.reddit.utils.resourceid.SharedResourceIdPatch.LabelA
import app.revanced.patches.reddit.utils.settings.fingerprints.AcknowledgementsLabelBuilderFingerprint
import app.revanced.patches.reddit.utils.settings.fingerprints.OssLicensesMenuActivityOnCreateFingerprint
import app.revanced.patches.reddit.utils.settings.fingerprints.SettingsStatusLoadFingerprint
import app.revanced.patches.shared.settings.fingerprints.SharedSettingFingerprint
import app.revanced.patches.shared.fingerprints.SharedSettingFingerprint
import app.revanced.util.getTargetIndex
import app.revanced.util.getWideLiteralInstructionIndex
import app.revanced.util.resultOrThrow

View File

@ -1,4 +1,4 @@
package app.revanced.patches.shared.settings.fingerprints
package app.revanced.patches.shared.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.shared.integrations.Constants.INTEGRATIONS_SETTING_CLASS_DESCRIPTOR

View File

@ -7,8 +7,10 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.integrations.Constants.COMPONENTS_PATH
import app.revanced.patches.shared.litho.fingerprints.EmptyComponentsFingerprint
import app.revanced.patches.shared.litho.fingerprints.LithoFilterPatchConstructorFingerprint
import app.revanced.patches.shared.litho.fingerprints.PathBuilderFingerprint
import app.revanced.patches.shared.litho.fingerprints.SetByteBufferFingerprint
@ -19,6 +21,7 @@ import app.revanced.util.getTargetIndexWithFieldReferenceType
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import java.io.Closeable
@ -26,8 +29,8 @@ import java.io.Closeable
@Suppress("SpellCheckingInspection", "unused")
object LithoFilterPatch : BytecodePatch(
setOf(
EmptyComponentsFingerprint,
LithoFilterPatchConstructorFingerprint,
PathBuilderFingerprint,
SetByteBufferFingerprint
)
), Closeable {
@ -40,6 +43,13 @@ object LithoFilterPatch : BytecodePatch(
internal lateinit var addFilter: (String) -> Unit
private set
private lateinit var emptyComponentMethod: MutableMethod
private lateinit var emptyComponentLabel: String
private lateinit var emptyComponentMethodName: String
private lateinit var pathBuilderMethodCall: String
private var filterCount = 0
override fun execute(context: BytecodeContext) {
@ -55,14 +65,75 @@ object LithoFilterPatch : BytecodePatch(
}
}
PathBuilderFingerprint.resultOrThrow().let {
EmptyComponentsFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
// resolves fingerprint.
PathBuilderFingerprint.resolve(context, it.classDef)
emptyComponentMethod = this
emptyComponentMethodName = name
val emptyComponentMethodIndex = it.scanResult.patternScanResult!!.startIndex + 1
val emptyComponentMethodReference =
getInstruction<ReferenceInstruction>(emptyComponentMethodIndex).reference
val emptyComponentFieldReference =
getInstruction<ReferenceInstruction>(emptyComponentMethodIndex + 2).reference
emptyComponentLabel = """
move-object/from16 v0, p1
invoke-static {v0}, $emptyComponentMethodReference
move-result-object v0
iget-object v0, v0, $emptyComponentFieldReference
return-object v0
"""
}
}
PathBuilderFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
// If the EmptyComponents Method and the PathBuilder Method are different,
// new inject way is required.
// TODO: Refactor LithoFilter patch when support for YouTube 18.29.38 ~ 19.17.41 and YT Music 6.29.58 ~ 6.51.53 is dropped.
if (emptyComponentMethodName != name) {
// In this case, the access modifier of the method that handles PathBuilder is 'AccessFlags.PRIVATE or AccessFlags.FINAL.
// Methods that handle PathBuilder are invoked by methods that handle EmptyComponents.
// 'pathBuilderMethodCall' is a reference that invokes the PathBuilder Method.
pathBuilderMethodCall = "$definingClass->$name("
for (i in 0 until parameters.size) {
pathBuilderMethodCall += parameterTypes[i]
}
pathBuilderMethodCall += ")$returnType"
emptyComponentMethod.apply {
// If the return value of the PathBuilder Method is null,
// it means that pathBuilder has been filtered by the LithoFilterPatch.
// (Refer comments below.)
// Returns emptyComponents.
for (index in implementation!!.instructions.size - 1 downTo 0) {
val instruction = getInstruction(index)
if ((instruction as? ReferenceInstruction)?.reference.toString() != pathBuilderMethodCall)
continue
val insertRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA
val insertIndex = index + 2
addInstructionsWithLabels(
insertIndex, """
if-nez v$insertRegister, :ignore
""" + emptyComponentLabel,
ExternalLabel("ignore", getInstruction(insertIndex))
)
}
}
// If the EmptyComponents Method and the PathBuilder Method are different,
// PathBuilder Method's returnType cannot cast emptyComponents.
// So just returns null value.
emptyComponentLabel = """
const/4 v0, 0x0
return-object v0
"""
}
val stringBuilderIndex = getTargetIndexWithFieldReferenceType("Ljava/lang/StringBuilder;")
val stringBuilderRegister = getInstruction<TwoRegisterInstruction>(stringBuilderIndex).registerA
@ -81,12 +152,8 @@ object LithoFilterPatch : BytecodePatch(
invoke-static {v$stringBuilderRegister, v$identifierRegister, v$objectRegister}, $INTEGRATIONS_LITHO_FILER_CLASS_DESCRIPTOR->filter(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/lang/Object;)Z
move-result v$stringBuilderRegister
if-eqz v$stringBuilderRegister, :filter
move-object/from16 v0, p1
invoke-static {v0}, $emptyComponentMethodReference
move-result-object v0
iget-object v0, v0, $emptyComponentFieldReference
return-object v0
""", ExternalLabel("filter", getInstruction(insertIndex))
""" + emptyComponentLabel,
ExternalLabel("filter", getInstruction(insertIndex))
)
}
}

View File

@ -0,0 +1,18 @@
package app.revanced.patches.shared.litho.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object EmptyComponentsFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
opcodes = listOf(
Opcode.INVOKE_INTERFACE,
Opcode.INVOKE_STATIC_RANGE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT
),
strings = listOf("Error while converting %s")
)

View File

@ -1,18 +1,11 @@
package app.revanced.patches.shared.litho.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* Since YouTube v19.18.41 and YT Music 7.01.53, pathBuilder is being handled by a different Method.
*/
internal object PathBuilderFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
opcodes = listOf(
Opcode.INVOKE_INTERFACE,
Opcode.INVOKE_STATIC_RANGE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT
),
strings = listOf("Error while converting %s")
strings = listOf("Number of bits must be positive")
)

View File

@ -18,7 +18,6 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMut
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -212,7 +211,14 @@ fun Method.getEmptyStringInstructionIndex()
fun Method.getStringInstructionIndex(value: String) = implementation?.let {
it.instructions.indexOfFirst { instruction ->
instruction.opcode == Opcode.CONST_STRING
&& (instruction as? BuilderInstruction21c)?.reference.toString() == value
&& (instruction as? ReferenceInstruction)?.reference.toString() == value
}
} ?: -1
fun Method.getStartsWithStringInstructionIndex(value: String) = implementation?.let {
it.instructions.indexOfFirst { instruction ->
instruction.opcode == Opcode.CONST_STRING
&& (instruction as? ReferenceInstruction)?.reference.toString().startsWith(value)
}
} ?: -1