feat(YouTube): support version 19.02.39

This commit is contained in:
inotia00 2024-01-24 23:02:52 +09:00
parent 5e12e0fa6a
commit b83a1bdd45
24 changed files with 317 additions and 281 deletions

View File

@ -1,19 +0,0 @@
package app.revanced.patches.shared.fingerprints.litho
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
object IdentifierFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
opcodes = listOf(
Opcode.IPUT_OBJECT,
Opcode.INVOKE_INTERFACE_RANGE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.CONST_STRING
),
strings = listOf("Element missing type extension")
)

View File

@ -4,11 +4,13 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.fingerprints.litho.EmptyComponentBuilderFingerprint
import app.revanced.patches.shared.fingerprints.litho.IdentifierFingerprint
import app.revanced.util.exception
import app.revanced.util.getEmptyStringInstructionIndex
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
@ -17,10 +19,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import kotlin.properties.Delegates
object ComponentParserPatch : BytecodePatch(
setOf(
EmptyComponentBuilderFingerprint,
IdentifierFingerprint
)
setOf(EmptyComponentBuilderFingerprint)
) {
private lateinit var emptyComponentLabel: String
internal lateinit var insertMethod: MutableMethod
@ -60,15 +59,28 @@ object ComponentParserPatch : BytecodePatch(
}
}
private fun MutableMethod.getTargetIndexDownTo(
startIndex: Int,
opcode: Opcode
): Int {
for (index in startIndex downTo 0) {
if (getInstruction(index).opcode != opcode)
continue
return index
}
throw PatchException("Failed to find hook method")
}
override fun execute(context: BytecodeContext) {
/**
* Shared fingerprint
*/
EmptyComponentBuilderFingerprint.result?.let {
it.mutableMethod.apply {
EmptyComponentBuilderFingerprint.result?.let { result ->
result.mutableMethod.apply {
insertMethod = this
emptyComponentIndex = it.scanResult.patternScanResult!!.startIndex + 1
emptyComponentIndex = result.scanResult.patternScanResult!!.startIndex + 1
val builderMethodDescriptor =
getInstruction<ReferenceInstruction>(emptyComponentIndex).reference
@ -94,22 +106,19 @@ object ComponentParserPatch : BytecodePatch(
getInstruction<TwoRegisterInstruction>(stringBuilderIndex).registerA
insertIndex = stringBuilderIndex + 1
}
} ?: throw EmptyComponentBuilderFingerprint.exception
/**
* Only used in YouTube
*/
IdentifierFingerprint.result?.let {
it.mutableMethod.apply {
val identifierIndex = it.scanResult.patternScanResult!!.startIndex
val objectIndex = it.scanResult.patternScanResult!!.endIndex + 1
val emptyStringIndex = getEmptyStringInstructionIndex()
val identifierIndex = getTargetIndexDownTo(emptyStringIndex, Opcode.IPUT_OBJECT)
identifierRegister =
getInstruction<OneRegisterInstruction>(identifierIndex).registerA
getInstruction<TwoRegisterInstruction>(identifierIndex).registerA
val objectIndex = implementation!!.instructions.let {
emptyStringIndex + it.subList(emptyStringIndex, it.size - 1).indexOfFirst { instruction ->
instruction.opcode == Opcode.INVOKE_VIRTUAL
}
}
objectRegister = getInstruction<BuilderInstruction35c>(objectIndex).registerC
}
}
} ?: throw EmptyComponentBuilderFingerprint.exception
}
}

View File

@ -0,0 +1,58 @@
package app.revanced.patches.shared.patch.transformation
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.util.findMutableMethodOf
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
@Suppress("MemberVisibilityCanBePrivate")
abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch(emptySet()) {
abstract fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int
): T?
abstract fun transform(mutableMethod: MutableMethod, entry: T)
// Returns the patch indices as a Sequence, which will execute lazily.
fun findPatchIndices(classDef: ClassDef, method: Method): Sequence<T>? {
return method.implementation?.instructions?.asSequence()?.withIndex()?.mapNotNull { (index, instruction) ->
filterMap(classDef, method, instruction, index)
}
}
override fun execute(context: BytecodeContext) {
// Find all methods to patch
buildMap {
context.classes.forEach { classDef ->
val methods = buildList {
classDef.methods.forEach { method ->
// Since the Sequence executes lazily,
// using any() results in only calling
// filterMap until the first index has been found.
if (findPatchIndices(classDef, method)?.any() == true) add(method)
}
}
if (methods.isNotEmpty()) {
put(classDef, methods)
}
}
}.forEach { (classDef, methods) ->
// And finally transform the methods...
val mutableClass = context.proxy(classDef).mutableClass
methods.map(mutableClass::findMutableMethodOf).forEach methods@{ mutableMethod ->
val patchIndices = findPatchIndices(mutableClass, mutableMethod)?.toCollection(ArrayDeque())
?: return@methods
while (!patchIndices.isEmpty()) transform(mutableMethod, patchIndices.removeLast())
}
}
}
}

View File

@ -10,7 +10,7 @@ import app.revanced.patches.youtube.utils.settings.SettingsPatch
@Patch(
name = "Remove viewer discretion dialog",
description = "Removes the dialog that appears when you try to watch a video that has been age-restricted " +
description = "Adds an option to remove the dialog that appears when opening a video that has been age-restricted " +
"by accepting it automatically. This does not bypass the age restriction.",
dependencies = [SettingsPatch::class],
compatiblePackages = [

View File

@ -112,4 +112,4 @@ object AmbientModeSwitchPatch : BytecodePatch(
SettingsPatch.updatePatchStatus("Ambient mode switch")
}
}
}

View File

@ -2,18 +2,18 @@ package app.revanced.patches.youtube.misc.externalbrowser
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.misc.externalbrowser.fingerprints.ExternalBrowserPrimaryFingerprint
import app.revanced.patches.youtube.misc.externalbrowser.fingerprints.ExternalBrowserSecondaryFingerprint
import app.revanced.patches.youtube.misc.externalbrowser.fingerprints.ExternalBrowserTertiaryFingerprint
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.shared.patch.transformation.AbstractTransformInstructionsPatch
import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.util.exception
import app.revanced.util.getStringInstructionIndex
import com.android.tools.smali.dexlib2.iface.ClassDef
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
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.StringReference
@Patch(
name = "Enable external browser",
@ -47,35 +47,36 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
]
)
@Suppress("unused")
object ExternalBrowserPatch : BytecodePatch(
setOf(
ExternalBrowserPrimaryFingerprint,
ExternalBrowserSecondaryFingerprint,
ExternalBrowserTertiaryFingerprint
)
object ExternalBrowserPatch : AbstractTransformInstructionsPatch<Pair<Int, Int>>(
) {
override fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int
): Pair<Int, Int>? {
if (instruction !is ReferenceInstruction) return null
val reference = instruction.reference as? StringReference ?: return null
if (reference.string != "android.support.customtabs.action.CustomTabsService") return null
return instructionIndex to (instruction as OneRegisterInstruction).registerA
}
override fun transform(mutableMethod: MutableMethod, entry: Pair<Int, Int>) {
val (intentStringIndex, register) = entry
// Hook the intent string.
mutableMethod.addInstructions(
intentStringIndex + 1, """
invoke-static {v$register}, $MISC_PATH/ExternalBrowserPatch;->enableExternalBrowser(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$register
"""
)
}
override fun execute(context: BytecodeContext) {
arrayOf(
ExternalBrowserPrimaryFingerprint,
ExternalBrowserSecondaryFingerprint,
ExternalBrowserTertiaryFingerprint
).forEach { fingerprint ->
fingerprint.result?.let {
it.mutableMethod.apply {
val targetIndex =
getStringInstructionIndex("android.support.customtabs.action.CustomTabsService")
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstructions(
targetIndex + 1, """
invoke-static {v$register}, $MISC_PATH/ExternalBrowserPatch;->enableExternalBrowser(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$register
"""
)
}
} ?: throw fingerprint.exception
}
super.execute(context)
/**
* Add settings
@ -87,6 +88,5 @@ object ExternalBrowserPatch : BytecodePatch(
)
SettingsPatch.updatePatchStatus("Enable external browser")
}
}

View File

@ -1,18 +0,0 @@
package app.revanced.patches.youtube.misc.externalbrowser.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
object ExternalBrowserPrimaryFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
opcodes = listOf(
Opcode.CHECK_CAST,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.CONST_STRING
),
strings = listOf("android.support.customtabs.action.CustomTabsService")
)

View File

@ -1,11 +0,0 @@
package app.revanced.patches.youtube.misc.externalbrowser.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
object ExternalBrowserSecondaryFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
strings = listOf("android.support.customtabs.action.CustomTabsService")
)

View File

@ -1,18 +0,0 @@
package app.revanced.patches.youtube.misc.externalbrowser.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
object ExternalBrowserTertiaryFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
opcodes = listOf(
Opcode.CHECK_CAST,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.CONST_STRING
),
strings = listOf("android.support.customtabs.action.CustomTabsService")
)

View File

@ -54,18 +54,18 @@ object LanguageSelectorPatch : BytecodePatch(
override fun execute(context: BytecodeContext) {
val result = GeneralPrefsFingerprint.result // YouTube v18.33.xx ~
?: GeneralPrefsLegacyFingerprint.result // ~ YouTube v18.33.xx
?: GeneralPrefsLegacyFingerprint.result // ~ YouTube v18.32.xx
?: throw GeneralPrefsFingerprint.exception
result.let {
it.mutableMethod.apply {
val targetIndex = it.scanResult.patternScanResult!!.endIndex
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
val insertIndex = it.scanResult.patternScanResult!!.endIndex - 2
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructions(
targetIndex, """
insertIndex, """
invoke-static {}, $MISC_PATH/LanguageSelectorPatch;->enableLanguageSwitch()Z
move-result v$targetRegister
move-result v$insertRegister
"""
)
}

View File

@ -3,15 +3,18 @@ package app.revanced.patches.youtube.misc.language.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
/**
* Compatible with YouTube v18.33.40~
*/
object GeneralPrefsFingerprint : MethodFingerprint(
returnType = "V",
parameters = emptyList(),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.SGET,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT
),
strings = listOf("bedtime_reminder_toggle"),
customFingerprint = { methodDef, _ -> methodDef.definingClass.endsWith("/GeneralPrefsFragment;") }

View File

@ -3,6 +3,9 @@ package app.revanced.patches.youtube.misc.language.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
/**
* Compatible with ~YouTube v18.32.39
*/
object GeneralPrefsLegacyFingerprint : MethodFingerprint(
returnType = "V",
parameters = emptyList(),
@ -12,7 +15,9 @@ object GeneralPrefsLegacyFingerprint : MethodFingerprint(
Opcode.CONST_4,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ
Opcode.IF_NEZ,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT
),
strings = listOf("bedtime_reminder_toggle"),
customFingerprint = { methodDef, _ -> methodDef.definingClass.endsWith("/GeneralPrefsFragment;") }

View File

@ -19,8 +19,8 @@ import app.revanced.util.exception
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
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.WideLiteralInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
@ -83,8 +83,8 @@ object SeekMessagePatch : BytecodePatch(
/**
* Added in YouTube v18.29.xx~
*/
SeekEduUndoOverlayFingerprint.result?.let {
it.mutableMethod.apply {
SeekEduUndoOverlayFingerprint.result?.let { result ->
result.mutableMethod.apply {
val seekUndoCalls = implementation!!.instructions.withIndex()
.filter { instruction ->
(instruction.value as? WideLiteralInstruction)?.wideLiteral == SeekUndoEduOverlayStub
@ -92,38 +92,31 @@ object SeekMessagePatch : BytecodePatch(
val insertIndex = seekUndoCalls.elementAt(seekUndoCalls.size - 1).index
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
for (index in insertIndex until implementation!!.instructions.size) {
val targetInstruction = getInstruction(index)
if (targetInstruction.opcode != Opcode.INVOKE_VIRTUAL)
continue
if (((targetInstruction as Instruction35c).reference as MethodReference).name != "setOnClickListener")
continue
// Force close occurs only in YouTube v18.36.xx unless we add this.
if (SettingsPatch.is1836)
addComponent(insertIndex, index - 1)
addInstructionsWithLabels(
insertIndex, fixComponent + """
invoke-static {}, $PLAYER->hideSeekUndoMessage()Z
move-result v$insertRegister
if-nez v$insertRegister, :default
""", ExternalLabel("default", getInstruction(index + 1))
)
/**
* Add settings
*/
SettingsPatch.addPreference(
arrayOf(
"PREFERENCE: PLAYER_SETTINGS",
"SETTINGS: HIDE_SEEK_UNDO_MESSAGE"
)
)
break
val jumpIndex = implementation!!.instructions.let {
insertIndex + it.subList(insertIndex, it.size - 1).indexOfFirst { instruction ->
instruction.opcode == Opcode.INVOKE_VIRTUAL
&& ((instruction as? ReferenceInstruction)?.reference as? MethodReference)?.name == "setOnClickListener"
}
}
val constComponent = getConstComponent(insertIndex, jumpIndex - 1)
addInstructionsWithLabels(
insertIndex, constComponent + """
invoke-static {}, $PLAYER->hideSeekUndoMessage()Z
move-result v$insertRegister
if-nez v$insertRegister, :default
""", ExternalLabel("default", getInstruction(jumpIndex + 1))
)
/**
* Add settings
*/
SettingsPatch.addPreference(
arrayOf(
"PREFERENCE: PLAYER_SETTINGS",
"SETTINGS: HIDE_SEEK_UNDO_MESSAGE"
)
)
}
}
@ -141,30 +134,24 @@ object SeekMessagePatch : BytecodePatch(
}
private var fixComponent: String = ""
private fun MutableMethod.addComponent(
private fun MutableMethod.getConstComponent(
startIndex: Int,
endIndex: Int
) {
val fixRegister =
): String {
val constRegister =
getInstruction<FiveRegisterInstruction>(endIndex).registerE
for (index in endIndex downTo startIndex) {
val opcode = getInstruction(index).opcode
if (opcode != Opcode.CONST_16)
if (getInstruction(index).opcode != Opcode.CONST_16)
continue
val register = getInstruction<OneRegisterInstruction>(index).registerA
if (register != fixRegister)
if (getInstruction<OneRegisterInstruction>(index).registerA != constRegister)
continue
val fixValue = getInstruction<WideLiteralInstruction>(index).wideLiteral.toInt()
val constValue = getInstruction<WideLiteralInstruction>(index).wideLiteral.toInt()
fixComponent = "const/16 v$fixRegister, $fixValue"
break
return "const/16 v$constRegister, $constValue"
}
return ""
}
}

View File

@ -1,11 +1,9 @@
package app.revanced.patches.youtube.seekbar.append
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.utils.fingerprints.TotalTimeFingerprint
@ -13,11 +11,16 @@ import app.revanced.patches.youtube.utils.integrations.Constants.SEEKBAR
import app.revanced.patches.youtube.utils.overridequality.OverrideQualityHookPatch
import app.revanced.patches.youtube.utils.overridespeed.OverrideSpeedHookPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.TotalTime
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.util.exception
import app.revanced.util.getReference
import app.revanced.util.getWideLiteralInstructionIndex
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
name = "Append time stamps information",
@ -60,36 +63,28 @@ object AppendTimeStampInformationPatch : BytecodePatch(
setOf(TotalTimeFingerprint)
) {
override fun execute(context: BytecodeContext) {
TotalTimeFingerprint.result?.let {
it.mutableMethod.apply {
var setTextIndex = -1
for ((textViewIndex, textViewInstruction) in implementation!!.instructions.withIndex()) {
if (textViewInstruction.opcode != Opcode.INVOKE_VIRTUAL) continue
if (getInstruction<ReferenceInstruction>(textViewIndex).reference.toString() ==
"Landroid/widget/TextView;->getText()Ljava/lang/CharSequence;"
) {
setTextIndex = textViewIndex + 2
val setTextRegister = getInstruction<Instruction35c>(setTextIndex).registerC
val textViewRegister =
getInstruction<Instruction35c>(textViewIndex).registerC
addInstructions(
setTextIndex, """
invoke-static {v$setTextRegister}, $SEEKBAR->appendTimeStampInformation(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$setTextRegister
"""
)
addInstruction(
textViewIndex,
"invoke-static {v$textViewRegister}, $SEEKBAR->setContainerClickListener(Landroid/view/View;)V"
)
break
TotalTimeFingerprint.result?.let { result ->
result.mutableMethod.apply {
val constIndex = getWideLiteralInstructionIndex(TotalTime)
val charSequenceIndex = implementation!!.instructions.let {
constIndex + it.subList(constIndex, it.size - 1).indexOfFirst { instruction ->
instruction.opcode == Opcode.MOVE_RESULT_OBJECT
}
}
if (setTextIndex == -1)
throw PatchException("target Instruction not found!")
val charSequenceRegister = getInstruction<OneRegisterInstruction>(charSequenceIndex).registerA
val textViewIndex = indexOfFirstInstruction {
getReference<MethodReference>()?.name == "getText"
}
val textViewRegister =
getInstruction<Instruction35c>(textViewIndex).registerC
addInstructions(
textViewIndex, """
invoke-static {v$textViewRegister}, $SEEKBAR->setContainerClickListener(Landroid/view/View;)V
invoke-static {v$charSequenceRegister}, $SEEKBAR->appendTimeStampInformation(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$charSequenceRegister
"""
)
}
} ?: throw TotalTimeFingerprint.exception

View File

@ -11,7 +11,6 @@ import app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints.UserW
import app.revanced.patches.youtube.utils.integrations.Constants.SHORTS
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.util.exception
import app.revanced.util.getWideLiteralInstructionIndex
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
@ -53,7 +52,7 @@ object DisableShortsOnStartupPatch : BytecodePatch(
UserWasInShortsFingerprint.result?.let {
it.mutableMethod.apply {
val insertIndex = getWideLiteralInstructionIndex(45381394)
val insertIndex = it.scanResult.patternScanResult!!.startIndex
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructionsWithLabels(

View File

@ -1,13 +1,14 @@
package app.revanced.patches.youtube.shorts.startupshortsreset.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.util.fingerprint.LiteralValueFingerprint
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
object UserWasInShortsFingerprint : LiteralValueFingerprint(
object UserWasInShortsFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Ljava/lang/Object;"),
strings = listOf("Failed to read user_was_in_shorts proto after successful warmup"),
literalSupplier = { 45381394 }
opcodes = listOf(Opcode.CONST_WIDE_32),
strings = listOf("Failed to read user_was_in_shorts proto after successful warmup")
)

View File

@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.patch.litho.ComponentParserPatch
import app.revanced.patches.youtube.utils.browseid.fingerprints.BrowseIdClassFingerprint
@ -13,6 +14,7 @@ import app.revanced.patches.youtube.utils.integrations.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.litho.LithoFilterPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.util.exception
import app.revanced.util.getStringInstructionIndex
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@ -38,22 +40,29 @@ object BrowseIdHookPatch : BytecodePatch(
* This class handles BrowseId.
* Pass an instance of this class to integrations to use Java Reflection.
*/
BrowseIdClassFingerprint.result
?.mutableClass?.methods?.find { method -> method.name == "<init>" }
?.apply {
val browseIdFieldIndex = implementation!!.instructions.indexOfFirst { instruction ->
instruction.opcode == Opcode.IPUT_OBJECT
}
val browseIdFieldName =
(getInstruction<ReferenceInstruction>(browseIdFieldIndex).reference as FieldReference).name
BrowseIdClassFingerprint.result?.let {
it.mutableMethod.apply {
val targetIndex = getStringInstructionIndex("VL") - 1
val targetReference = getInstruction<ReferenceInstruction>(targetIndex).reference
val targetClass = context.findClass((targetReference as FieldReference).definingClass)!!.mutableClass
addInstructions(
1, """
const-string v0, "$browseIdFieldName"
invoke-static {p0, v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->initialize(Ljava/lang/Object;Ljava/lang/String;)V
"""
)
} ?: throw BrowseIdClassFingerprint.exception
targetClass.methods.find { method -> method.name == "<init>" }
?.apply {
val browseIdFieldIndex = implementation!!.instructions.indexOfFirst { instruction ->
instruction.opcode == Opcode.IPUT_OBJECT
}
val browseIdFieldName =
(getInstruction<ReferenceInstruction>(browseIdFieldIndex).reference as FieldReference).name
addInstructions(
1, """
const-string v0, "$browseIdFieldName"
invoke-static {p0, v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->initialize(Ljava/lang/Object;Ljava/lang/String;)V
"""
)
} ?: throw PatchException("BrowseIdClass not found!")
}
} ?: throw BrowseIdClassFingerprint.exception
/**
* Set BrowseId to integrations.

View File

@ -1,7 +1,12 @@
package app.revanced.patches.youtube.utils.browseid.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
object BrowseIdClassFingerprint : MethodFingerprint(
strings = listOf("\u0001\t\u0000\u0001\u0002\u0010\t\u0000\u0000\u0001\u0002\u1008\u0000\u0003\u1008\u0002\u0005\u1008\u0003\u0006\u1409\u0005\u0007\u1007\u0004\u0008\u1009\u0006\u000c\u1008\n\u000e\u180c\u000b\u0010\u1007\r")
returnType = "Ljava/lang/Object;",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.SYNTHETIC,
parameters = listOf("Ljava/lang/Object;", "L"),
strings = listOf("VL")
)

View File

@ -26,6 +26,5 @@ object GeneralByteBufferFingerprint : MethodFingerprint(
Opcode.IPUT,
Opcode.IPUT,
Opcode.GOTO
),
customFingerprint = { methodDef, _ -> methodDef.name == "f" }
)
)

View File

@ -12,29 +12,63 @@ import java.io.Closeable
object PlayerResponsePatch : BytecodePatch(
setOf(PlayerParameterBuilderFingerprint)
), Closeable, MutableSet<PlayerResponsePatch.Hook> by mutableSetOf() {
private const val VIDEO_ID_PARAMETER = 1
private const val PLAYER_PARAMETER = 3
private const val IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11
private var VIDEO_ID_PARAMETER = 1
private var PLAYER_PARAMETER = 3
private var IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11
private var freeRegister = 0
private var shouldApplyNewMethod = false
private lateinit var playerResponseMethod: MutableMethod
override fun execute(context: BytecodeContext) {
playerResponseMethod = PlayerParameterBuilderFingerprint.result?.mutableMethod
?: throw PlayerParameterBuilderFingerprint.exception
playerResponseMethod.apply {
freeRegister = implementation!!.registerCount - parameters.size - 2
shouldApplyNewMethod = freeRegister > 2
if (shouldApplyNewMethod) {
IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = freeRegister
PLAYER_PARAMETER = freeRegister - 1
VIDEO_ID_PARAMETER = freeRegister - 2
}
}
}
override fun close() {
fun hookVideoId(hook: Hook) = playerResponseMethod.addInstruction(
0,
"invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
)
fun hookVideoId(hook: Hook) {
playerResponseMethod.apply {
val instruction =
if (shouldApplyNewMethod)
"invoke-static {v$VIDEO_ID_PARAMETER, v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
else
"invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
addInstruction(
0,
instruction
)
}
}
fun hookPlayerParameter(hook: Hook) = playerResponseMethod.addInstructions(
0, """
invoke-static {p$VIDEO_ID_PARAMETER, p$PLAYER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
move-result-object p$PLAYER_PARAMETER
"""
)
fun hookPlayerParameter(hook: Hook) {
playerResponseMethod.apply {
val instruction =
if (shouldApplyNewMethod)
"""
invoke-static {v$VIDEO_ID_PARAMETER, v$PLAYER_PARAMETER, v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
move-result-object p3
"""
else
"""
invoke-static {p$VIDEO_ID_PARAMETER, p$PLAYER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
move-result-object p$PLAYER_PARAMETER
"""
addInstructions(
0,
instruction
)
}
}
// Reverse the order in order to preserve insertion order of the hooks.
val beforeVideoIdHooks = filterIsInstance<Hook.PlayerBeforeVideoId>().asReversed()
@ -45,6 +79,16 @@ object PlayerResponsePatch : BytecodePatch(
afterVideoIdHooks.forEach(::hookPlayerParameter)
videoIdHooks.forEach(::hookVideoId)
beforeVideoIdHooks.forEach(::hookPlayerParameter)
if (shouldApplyNewMethod) {
playerResponseMethod.addInstructions(
0, """
move-object v$VIDEO_ID_PARAMETER, p1
move-object v$PLAYER_PARAMETER, p3
move/from16 v$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER, p11
"""
)
}
}
internal abstract class Hook(private val methodDescriptor: String) {

View File

@ -22,9 +22,11 @@ import app.revanced.patches.youtube.utils.returnyoutubedislike.shorts.ReturnYouT
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch
import app.revanced.util.exception
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@Patch(
name = "Return YouTube Dislike",
@ -100,14 +102,14 @@ object ReturnYouTubeDislikePatch : BytecodePatch(
it.mutableMethod.apply {
val conversionContextFieldIndex = implementation!!.instructions.indexOfFirst { instruction ->
instruction.opcode == Opcode.IGET_OBJECT
&& (instruction as ReferenceInstruction).reference.toString().endsWith("Ljava/util/Map;")
&& instruction.getReference<FieldReference>()?.type == "Ljava/util/Map;"
} - 1
val conversionContextFieldReference =
getInstruction<ReferenceInstruction>(conversionContextFieldIndex).reference
val charSequenceIndex = implementation!!.instructions.indexOfFirst { instruction ->
instruction.opcode == Opcode.IGET_OBJECT
&& (instruction as ReferenceInstruction).reference.toString().endsWith("Ljava/util/BitSet;")
&& instruction.getReference<FieldReference>()?.type == "Ljava/util/BitSet;"
} - 1
val charSequenceRegister = getInstruction<TwoRegisterInstruction>(charSequenceIndex).registerA
val freeRegister = getInstruction<TwoRegisterInstruction>(charSequenceIndex).registerB

View File

@ -86,7 +86,6 @@ object SettingsPatch : AbstractSettingsResourcePatch(
val playServicesVersion = node.textContent.toInt()
is1836 = playServicesVersion in 233700000..233801999
upward1828 = 232900000 <= playServicesVersion
upward1831 = 233200000 <= playServicesVersion
upward1834 = 233502000 <= playServicesVersion
@ -156,7 +155,6 @@ object SettingsPatch : AbstractSettingsResourcePatch(
private val threadPoolExecutor = Executors.newFixedThreadPool(THREAD_COUNT)
internal lateinit var contexts: ResourceContext
internal var is1836: Boolean = false
internal var upward1828: Boolean = false
internal var upward1831: Boolean = false
internal var upward1834: Boolean = false

View File

@ -10,31 +10,6 @@ object VideoIdWithoutShortsFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.DECLARED_SYNCHRONIZED,
parameters = listOf("L"),
opcodes = listOf(
Opcode.MONITOR_ENTER,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_4,
Opcode.NEW_ARRAY,
Opcode.SGET_OBJECT,
Opcode.CONST_4,
Opcode.APUT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.CONST_4,
Opcode.IPUT_OBJECT,
Opcode.MONITOR_EXIT,
Opcode.RETURN_VOID,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.NEW_ARRAY,
Opcode.SGET_OBJECT,
Opcode.APUT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
@ -42,10 +17,7 @@ object VideoIdWithoutShortsFingerprint : MethodFingerprint(
Opcode.MONITOR_EXIT,
Opcode.RETURN_VOID,
Opcode.MONITOR_EXIT,
Opcode.RETURN_VOID,
Opcode.MOVE_EXCEPTION,
Opcode.MONITOR_EXIT,
Opcode.THROW
Opcode.RETURN_VOID
),
customFingerprint = { methodDef, classDef ->
methodDef.name == "l" && classDef.methods.count() == 17

View File

@ -87,6 +87,13 @@ fun Method.getWideLiteralInstructionIndex(literal: Long) = implementation?.let {
}
} ?: -1
fun Method.getEmptyStringInstructionIndex() = implementation?.let {
it.instructions.indexOfFirst { instruction ->
instruction.opcode == Opcode.CONST_STRING
&& (instruction as? BuilderInstruction21c)?.reference.toString().isEmpty()
}
} ?: -1
fun Method.getStringInstructionIndex(value: String) = implementation?.let {
it.instructions.indexOfFirst { instruction ->
instruction.opcode == Opcode.CONST_STRING
@ -138,6 +145,15 @@ inline fun <reified T : Reference> Instruction.getReference() =
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) =
this.implementation!!.instructions.indexOfFirst(predicate)
/**
* Get the index of the last [Instruction] that matches the predicate.
*
* @param predicate The predicate to match.
* @return The index of the first [Instruction] that matches the predicate.
*/
fun Method.indexOfLastInstruction(predicate: Instruction.() -> Boolean) =
this.implementation!!.instructions.indexOfFirst(predicate)
/**
* Return the resolved methods of [MethodFingerprint]s early.
*/