From 1b3ebe1a5806af4a09b07ef0ab5abcd72689a674 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Fri, 14 Mar 2025 18:33:31 +0900 Subject: [PATCH] feat(YouTube): Replace with a fingerprint that supports a wider range of versions (..20.10) --- .../youtube/patches/general/GeneralPatch.java | 4 ++ .../feed/components/FeedComponentsPatch.kt | 8 +--- .../youtube/feed/components/Fingerprints.kt | 16 +++++++- .../youtube/general/toolbar/Fingerprints.kt | 20 +++++++++- .../general/toolbar/ToolBarComponentsPatch.kt | 38 ++++++++++--------- .../patches/youtube/utils/Fingerprints.kt | 3 +- .../utils/fix/streamingdata/Fingerprints.kt | 6 +++ .../streamingdata/SpoofStreamingDataPatch.kt | 8 ++++ .../utils/playservice/VersionCheckPatch.kt | 6 +++ .../video/playerresponse/Fingerprints.kt | 28 +++++++++++--- .../PlayerResponseMethodHookPatch.kt | 11 +++++- .../util/fingerprint/LegacyFingerprint.kt | 14 +++---- 12 files changed, 121 insertions(+), 41 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java index 531af491c..b5652471b 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/GeneralPatch.java @@ -478,6 +478,10 @@ public class GeneralPatch { return Settings.HIDE_SEARCH_TERM_THUMBNAIL.get(); } + public static boolean hideSearchTermThumbnail(boolean original) { + return !hideSearchTermThumbnail() && original; + } + private static final boolean hideImageSearchButton = Settings.HIDE_IMAGE_SEARCH_BUTTON.get(); private static final boolean hideVoiceSearchButton = Settings.HIDE_VOICE_SEARCH_BUTTON.get(); diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt index ff76bc9f7..655606d54 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt @@ -259,13 +259,7 @@ val feedComponentsPatch = bytecodePatch( elementParserFingerprint.matchOrThrow(elementParserParentFingerprint).let { it.method.apply { val freeRegister = implementation!!.registerCount - parameters.size - 2 - val insertIndex = indexOfFirstInstructionOrThrow { - val reference = getReference() - - reference?.parameterTypes?.size == 1 && - reference.parameterTypes.first() == "[B" && - reference.returnType.startsWith("L") - } + val insertIndex = indexOfBufferParserInstruction(this) if (is_19_46_or_greater) { val objectIndex = indexOfFirstInstructionReversedOrThrow(insertIndex, Opcode.IGET_OBJECT) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/Fingerprints.kt index 9ee3d8d9d..1e7609914 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/Fingerprints.kt @@ -11,9 +11,13 @@ import app.revanced.patches.youtube.utils.resourceid.filterBarHeight import app.revanced.patches.youtube.utils.resourceid.horizontalCardList import app.revanced.patches.youtube.utils.resourceid.relatedChipCloudMargin import app.revanced.util.fingerprint.legacyFingerprint +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction import app.revanced.util.or import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.reference.MethodReference internal val breakingNewsFingerprint = legacyFingerprint( name = "breakingNewsFingerprint", @@ -90,9 +94,19 @@ internal val elementParserFingerprint = legacyFingerprint( Opcode.MOVE_RESULT_OBJECT, Opcode.IGET_OBJECT, Opcode.RETURN_OBJECT - ) + ), + customFingerprint = { method, _ -> + indexOfBufferParserInstruction(method) >= 0 + } ) +internal fun indexOfBufferParserInstruction(method: Method) = + method.indexOfFirstInstruction { + val reference = getReference() + reference?.parameterTypes?.firstOrNull() == "[B" && + reference.returnType.startsWith("L") + } + internal val elementParserParentFingerprint = legacyFingerprint( name = "elementParserParentFingerprint", returnType = "L", diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/Fingerprints.kt index 6440b9e6e..163bc173a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/Fingerprints.kt @@ -91,7 +91,25 @@ internal val createSearchSuggestionsFingerprint = legacyFingerprint( returnType = "Landroid/view/View;", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, parameters = listOf("I", "Landroid/view/View;", "Landroid/view/ViewGroup;"), - strings = listOf("ss_rds") + strings = listOf("ss_rds"), + customFingerprint = { method, _ -> + indexOfIteratorInstruction(method) >= 0 + } +) + +internal fun indexOfIteratorInstruction(method: Method) = + method.indexOfFirstInstructionReversed { + opcode == Opcode.INVOKE_INTERFACE && + getReference()?.toString() == "Ljava/util/Iterator;->next()Ljava/lang/Object;" + } + +// Flag is present in YouTube 19.16, but was not used until YouTube 19.43. +// Related issue: https://github.com/inotia00/ReVanced_Extended/issues/2784 +internal const val SEARCH_FRAGMENT_FEATURE_FLAG = 45353159L + +internal val searchFragmentFeatureFlagFingerprint = legacyFingerprint( + name = "searchFragmentFeatureFlagFingerprint", + literals = listOf(SEARCH_FRAGMENT_FEATURE_FLAG), ) internal val drawerContentViewConstructorFingerprint = legacyFingerprint( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/ToolBarComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/ToolBarComponentsPatch.kt index 2d50bbb25..f928fd94d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/ToolBarComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/toolbar/ToolBarComponentsPatch.kt @@ -15,7 +15,7 @@ import app.revanced.patches.youtube.utils.castbutton.hookToolBarCastButton import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.patch.PatchList.TOOLBAR_COMPONENTS -import app.revanced.patches.youtube.utils.playservice.is_19_46_or_greater +import app.revanced.patches.youtube.utils.playservice.is_19_16_or_greater import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.patches.youtube.utils.resourceid.actionBarRingoBackground import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch @@ -31,6 +31,7 @@ import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT import app.revanced.util.doRecursively import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.findMethodOrThrow +import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall import app.revanced.util.fingerprint.matchOrThrow import app.revanced.util.fingerprint.methodCall import app.revanced.util.fingerprint.methodOrThrow @@ -38,7 +39,6 @@ import app.revanced.util.fingerprint.mutableClassOrThrow import app.revanced.util.getReference import app.revanced.util.getWalkerMethod import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import app.revanced.util.replaceLiteralInstructionCall import com.android.tools.smali.dexlib2.Opcode @@ -46,6 +46,7 @@ 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.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.util.MethodUtil import org.w3c.dom.Element @@ -266,36 +267,39 @@ val toolBarComponentsPatch = bytecodePatch( // region patch for hide search term thumbnail createSearchSuggestionsFingerprint.methodOrThrow().apply { - val literal = if (is_19_46_or_greater) - 32L - else - 40L - val relativeIndex = indexOfFirstLiteralInstructionOrThrow(literal) - val replaceIndex = indexOfFirstInstructionReversedOrThrow(relativeIndex) { - opcode == Opcode.INVOKE_VIRTUAL && - getReference()?.toString() == "Landroid/widget/ImageView;->setVisibility(I)V" - } - 1 - - val jumpIndex = indexOfFirstInstructionOrThrow(relativeIndex) { + val iteratorIndex = indexOfIteratorInstruction(this) + val replaceIndex = indexOfFirstInstructionOrThrow(iteratorIndex) { + opcode == Opcode.IGET_OBJECT && + getReference()?.type == "Landroid/widget/ImageView;" + } + val jumpIndex = indexOfFirstInstructionOrThrow(replaceIndex) { opcode == Opcode.INVOKE_STATIC && getReference()?.toString() == "Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;" } + 4 - val replaceIndexInstruction = getInstruction(replaceIndex) + val freeRegister = replaceIndexInstruction.registerA + val classRegister = replaceIndexInstruction.registerB val replaceIndexReference = getInstruction(replaceIndex).reference addInstructionsWithLabels( replaceIndex + 1, """ invoke-static { }, $GENERAL_CLASS_DESCRIPTOR->hideSearchTermThumbnail()Z - move-result v${replaceIndexInstruction.registerA} - if-nez v${replaceIndexInstruction.registerA}, :hidden - iget-object v${replaceIndexInstruction.registerA}, v${replaceIndexInstruction.registerB}, $replaceIndexReference + move-result v$freeRegister + if-nez v$freeRegister, :hidden + iget-object v$freeRegister, v$classRegister, $replaceIndexReference """, ExternalLabel("hidden", getInstruction(jumpIndex)) ) removeInstruction(replaceIndex) } + if (is_19_16_or_greater) { + searchFragmentFeatureFlagFingerprint.injectLiteralInstructionBooleanCall( + SEARCH_FRAGMENT_FEATURE_FLAG, + "$GENERAL_CLASS_DESCRIPTOR->hideSearchTermThumbnail(Z)Z" + ) + } + // endregion /* diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/Fingerprints.kt index 3a579a948..7488b3d1e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/Fingerprints.kt @@ -234,7 +234,8 @@ internal val youtubeControlsOverlayFingerprint = legacyFingerprint( accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, parameters = emptyList(), literals = listOf( - fadeDurationFast, + // Removed in YouTube 20.09.40+ + // fadeDurationFast, insetOverlayViewLayout, scrimOverlay, // Removed in YouTube 20.02.38+ diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/Fingerprints.kt index 978104994..fa1662872 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/Fingerprints.kt @@ -146,4 +146,10 @@ internal val onesieEncryptionFeatureFlagFingerprint = legacyFingerprint( literals = listOf(ONESIE_ENCRYPTION_FEATURE_FLAG), ) +// YouTube 20.10 ~ +internal const val ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG = 45683169L +internal val onesieEncryptionAlternativeFeatureFlagFingerprint = legacyFingerprint( + name = "onesieEncryptionAlternativeFeatureFlagFingerprint", + literals = listOf(ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG), +) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt index 3b573061f..0befb7493 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/fix/streamingdata/SpoofStreamingDataPatch.kt @@ -19,6 +19,7 @@ import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PAC import app.revanced.patches.youtube.utils.compatibility.Constants.YOUTUBE_PACKAGE_NAME import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_STREAMING_DATA import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater +import app.revanced.patches.youtube.utils.playservice.is_20_10_or_greater import app.revanced.patches.youtube.utils.playservice.versionCheckPatch import app.revanced.patches.youtube.utils.request.buildRequestPatch import app.revanced.patches.youtube.utils.request.hookBuildRequest @@ -332,6 +333,13 @@ val spoofStreamingDataPatch = bytecodePatch( "$EXTENSION_CLASS_DESCRIPTOR->skipResponseEncryption(Z)Z" ) + if (is_20_10_or_greater) { + onesieEncryptionAlternativeFeatureFlagFingerprint.injectLiteralInstructionBooleanCall( + ONESIE_ENCRYPTION_ALTERNATIVE_FEATURE_FLAG, + "$EXTENSION_CLASS_DESCRIPTOR->skipResponseEncryption(Z)Z" + ) + } + settingArray += "SETTINGS: SKIP_RESPONSE_ENCRYPTION" } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playservice/VersionCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playservice/VersionCheckPatch.kt index 0015e98d5..ef0ca18fa 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playservice/VersionCheckPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/playservice/VersionCheckPatch.kt @@ -23,6 +23,8 @@ var is_19_09_or_greater = false private set var is_19_15_or_greater = false private set +var is_19_16_or_greater = false + private set var is_19_17_or_greater = false private set var is_19_23_or_greater = false @@ -59,6 +61,8 @@ var is_20_03_or_greater = false private set var is_20_05_or_greater = false private set +var is_20_10_or_greater = false + private set val versionCheckPatch = resourcePatch( description = "versionCheckPatch", @@ -83,6 +87,7 @@ val versionCheckPatch = resourcePatch( is_19_04_or_greater = 240502000 <= playStoreServicesVersion is_19_09_or_greater = 241002000 <= playStoreServicesVersion is_19_15_or_greater = 241602000 <= playStoreServicesVersion + is_19_16_or_greater = 241702000 <= playStoreServicesVersion is_19_17_or_greater = 241802000 <= playStoreServicesVersion is_19_23_or_greater = 242402000 <= playStoreServicesVersion is_19_25_or_greater = 242599000 <= playStoreServicesVersion @@ -101,5 +106,6 @@ val versionCheckPatch = resourcePatch( is_20_02_or_greater = 250299000 <= playStoreServicesVersion is_20_03_or_greater = 250405000 <= playStoreServicesVersion is_20_05_or_greater = 250605000 <= playStoreServicesVersion + is_20_10_or_greater = 251105000 <= playStoreServicesVersion } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt index 822f8e6aa..61b78022b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/Fingerprints.kt @@ -10,7 +10,6 @@ private val PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST = listOf( "[B", "Ljava/lang/String;", // Player parameters proto buffer. "Ljava/lang/String;", // PlaylistId. - "I", "I" ) @@ -19,7 +18,7 @@ internal val playerParameterBuilderFingerprint = legacyFingerprint( accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, returnType = "L", strings = listOf("psps"), - // 19.22 and earlier parameters are: + // parameters in 18.29 ~ 19.22 : // "Ljava/lang/String;", // VideoId. // "[B", // "Ljava/lang/String;", // Player parameters proto buffer. @@ -34,20 +33,39 @@ internal val playerParameterBuilderFingerprint = legacyFingerprint( // "Z", // "Z" - // 19.23+ parameters are: + // parameters in 19.23 ~ 20.09 : // "Ljava/lang/String;", // VideoId. // "[B", // "Ljava/lang/String;", // Player parameters proto buffer. // "Ljava/lang/String;", // PlaylistId. // "I", // "I", - // "L", + // "L", // New parameters added in 19.25. // "Ljava/util/Set;", // "Ljava/lang/String;", // "Ljava/lang/String;", // "L", // "Z", // Appears to indicate if the video id is being opened or is currently playing. // "Z", + // "Z", + // "Z" + + // parameters in 20.10 ~ : + // "Ljava/lang/String;", // VideoId. + // "[B", + // "Ljava/lang/String;", // Player parameters proto buffer. + // "Ljava/lang/String;", // PlaylistId. + // "I", + // "Z", // New parameters added in 20.10. + // "I", + // "L", // New parameters added in 19.25. + // "Ljava/util/Set;", + // "Ljava/lang/String;", + // "Ljava/lang/String;", + // "L", + // "Z", // Appears to indicate if the video id is being opened or is currently playing. + // "Z", + // "Z", // "Z" customFingerprint = custom@{ method, _ -> val parameterTypes = method.parameterTypes @@ -56,7 +74,7 @@ internal val playerParameterBuilderFingerprint = legacyFingerprint( return@custom false } - val startsWithMethodParameterList = parameterTypes.slice(0..5) + val startsWithMethodParameterList = parameterTypes.slice(0..4) parametersEqual( PLAYER_PARAMETER_STARTS_WITH_PARAMETER_LIST, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt index cf1eb8951..9e8d23aeb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/video/playerresponse/PlayerResponseMethodHookPatch.kt @@ -37,11 +37,18 @@ val playerResponseMethodHookPatch = bytecodePatch( ?: playerParameterBuilderLegacyFingerprint.methodOrThrow() playerResponseMethod.apply { - parameterIsShortAndOpeningOrPlaying = parameterTypes.indexOfFirst { it == "Z" } + 1 + val setIndex = parameterTypes.indexOfFirst { it == "Ljava/util/Set;" } + val parameterSize = parameterTypes.size + val relativeIndex = parameterTypes.subList(setIndex, parameterSize - 1).indexOfFirst { it == "Z" } + + // YouTube 18.29 ~ 19.22 : p11 + // YouTube 19.23 ~ 20.09 : p12 + // YouTube 20.10 ~ : p13 + parameterIsShortAndOpeningOrPlaying = setIndex + relativeIndex + 1 // On some app targets the method has too many registers pushing the parameters past v15. // If needed, move the parameters to 4-bit registers so they can be passed to extension. playerResponseMethodCopyRegisters = implementation!!.registerCount - - parameterTypes.size + parameterIsShortAndOpeningOrPlaying > 15 + parameterSize + parameterIsShortAndOpeningOrPlaying > 15 } if (playerResponseMethodCopyRegisters) { diff --git a/patches/src/main/kotlin/app/revanced/util/fingerprint/LegacyFingerprint.kt b/patches/src/main/kotlin/app/revanced/util/fingerprint/LegacyFingerprint.kt index 36dc8bdb2..87d432cf0 100644 --- a/patches/src/main/kotlin/app/revanced/util/fingerprint/LegacyFingerprint.kt +++ b/patches/src/main/kotlin/app/revanced/util/fingerprint/LegacyFingerprint.kt @@ -100,24 +100,24 @@ fun Pair.injectLiteralInstructionBooleanCall( ) { methodOrThrow().apply { val literalIndex = indexOfFirstLiteralInstruction(literal) - val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) - val targetRegister = getInstruction(targetIndex).registerA + val index = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) + val register = getInstruction(index).registerA val smaliInstruction = if (descriptor.startsWith("0x")) """ - const/16 v$targetRegister, $descriptor + const/16 v$register, $descriptor """ else if (descriptor.endsWith("(Z)Z")) """ - invoke-static {v$targetRegister}, $descriptor - move-result v$targetRegister + invoke-static/range { v$register .. v$register }, $descriptor + move-result v$register """ else """ invoke-static {}, $descriptor - move-result v$targetRegister + move-result v$register """ addInstructions( - targetIndex + 1, + index + 1, smaliInstruction ) }