From b6c7b75c9d7e59ebe9ee095a1fd5579cca745516 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Tue, 12 Dec 2023 10:01:28 +0900 Subject: [PATCH] feat(YouTube/Spoof player parameters): matches official ReVanced code --- .../parameter/SpoofPlayerParameterPatch.kt | 209 ++++++++++++++++++ ...ayerResponseModelImplGeneralFingerprint.kt | 24 ++ ...rResponseModelImplLiveStreamFingerprint.kt | 24 ++ ...nseModelImplRecommendedLevelFingerprint.kt | 24 ++ ...dererDecoderRecommendedLevelFingerprint.kt | 23 ++ ...toryboardRendererDecoderSpecFingerprint.kt | 23 ++ .../StoryboardRendererSpecFingerprint.kt | 12 + .../StoryboardThumbnailFingerprint.kt | 23 ++ .../StoryboardThumbnailParentFingerprint.kt | 17 ++ 9 files changed, 379 insertions(+) create mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/SpoofPlayerParameterPatch.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplLiveStreamFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererSpecFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailParentFingerprint.kt diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/SpoofPlayerParameterPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/SpoofPlayerParameterPatch.kt new file mode 100644 index 000000000..0673846c2 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/SpoofPlayerParameterPatch.kt @@ -0,0 +1,209 @@ +package app.revanced.patches.youtube.utils.fix.parameter + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.PlayerResponseModelImplGeneralFingerprint +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.PlayerResponseModelImplLiveStreamFingerprint +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.PlayerResponseModelImplRecommendedLevelFingerprint +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardRendererDecoderRecommendedLevelFingerprint +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardRendererDecoderSpecFingerprint +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardRendererSpecFingerprint +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardThumbnailFingerprint +import app.revanced.patches.youtube.utils.fix.parameter.fingerprints.StoryboardThumbnailParentFingerprint +import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH +import app.revanced.patches.youtube.utils.playerresponse.PlayerResponsePatch +import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch +import app.revanced.patches.youtube.utils.settings.SettingsPatch +import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch +import app.revanced.util.exception +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Patch( + name = "Spoof player parameters", + description = "Spoofs player parameters to prevent playback issues.", + dependencies = [ + PlayerTypeHookPatch::class, + PlayerResponsePatch::class, + VideoIdPatch::class, + SettingsPatch::class + ], + compatiblePackages = [ + CompatiblePackage( + "com.google.android.youtube", + [ + "18.25.40", + "18.27.36", + "18.29.38", + "18.30.37", + "18.31.40", + "18.32.39", + "18.33.40", + "18.34.38", + "18.35.36", + "18.36.39", + "18.37.36", + "18.38.44", + "18.39.41", + "18.40.34", + "18.41.39", + "18.42.41", + "18.43.45", + "18.44.41", + "18.45.43" + ] + ) + ] +) +object SpoofPlayerParameterPatch : BytecodePatch( + setOf( + PlayerResponseModelImplGeneralFingerprint, + PlayerResponseModelImplLiveStreamFingerprint, + PlayerResponseModelImplRecommendedLevelFingerprint, + StoryboardRendererSpecFingerprint, + StoryboardRendererDecoderSpecFingerprint, + StoryboardRendererDecoderRecommendedLevelFingerprint, + StoryboardThumbnailParentFingerprint + ) +) { + private const val INTEGRATIONS_CLASS_DESCRIPTOR = + "$MISC_PATH/SpoofPlayerParameterPatch;" + + override fun execute(context: BytecodeContext) { + + // Hook the player parameters. + PlayerResponsePatch += PlayerResponsePatch.Hook.PlayerParameter( + "$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/String;" + ) + + // Force the seekbar time and chapters to always show up. + // This is used if the storyboard spec fetch fails, for viewing paid videos, + // or if storyboard spoofing is turned off. + StoryboardThumbnailParentFingerprint.result?.classDef?.let { classDef -> + StoryboardThumbnailFingerprint.also { + it.resolve( + context, + classDef + ) + }.result?.let { + it.mutableMethod.apply { + val targetIndex = it.scanResult.patternScanResult!!.endIndex + val targetRegister = + getInstruction(targetIndex).registerA + + // Since this is end of the method must replace one line then add the rest. + addInstructions( + targetIndex + 1, + """ + invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarThumbnailOverrideValue()Z + move-result v$targetRegister + return v$targetRegister + """ + ) + removeInstruction(targetIndex) + } + } ?: throw StoryboardThumbnailFingerprint.exception + } ?: throw StoryboardThumbnailParentFingerprint.exception + + // Hook storyboard renderer url. + arrayOf( + PlayerResponseModelImplGeneralFingerprint, + PlayerResponseModelImplLiveStreamFingerprint + ).forEach { fingerprint -> + fingerprint.result?.let { + it.mutableMethod.apply { + val getStoryboardIndex = it.scanResult.patternScanResult!!.endIndex + val getStoryboardRegister = + getInstruction(getStoryboardIndex).registerA + + addInstructions( + getStoryboardIndex, + """ + invoke-static { v$getStoryboardRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$getStoryboardRegister + """ + ) + } + } ?: throw fingerprint.exception + } + + // Hook recommended seekbar thumbnails quality level. + StoryboardRendererDecoderRecommendedLevelFingerprint.result?.let { + it.mutableMethod.apply { + val moveOriginalRecommendedValueIndex = it.scanResult.patternScanResult!!.endIndex + val originalValueRegister = + getInstruction(moveOriginalRecommendedValueIndex).registerA + + addInstructions( + moveOriginalRecommendedValueIndex + 1, """ + invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I + move-result v$originalValueRegister + """ + ) + } + } ?: throw StoryboardRendererDecoderRecommendedLevelFingerprint.exception + + // Hook the recommended precise seeking thumbnails quality level. + PlayerResponseModelImplRecommendedLevelFingerprint.result?.let { + it.mutableMethod.apply { + val moveOriginalRecommendedValueIndex = it.scanResult.patternScanResult!!.endIndex + val originalValueRegister = + getInstruction(moveOriginalRecommendedValueIndex).registerA + + addInstructions( + moveOriginalRecommendedValueIndex, """ + invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I + move-result v$originalValueRegister + """ + ) + } + } ?: throw PlayerResponseModelImplRecommendedLevelFingerprint.exception + + StoryboardRendererSpecFingerprint.result?.let { + it.mutableMethod.apply { + val storyBoardUrlParams = 0 + + addInstructionsWithLabels( + 0, """ + if-nez p$storyBoardUrlParams, :ignore + invoke-static { p$storyBoardUrlParams }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String; + move-result-object p$storyBoardUrlParams + """, ExternalLabel("ignore", getInstruction(0)) + ) + } + } ?: throw StoryboardRendererSpecFingerprint.exception + + // Hook the seekbar thumbnail decoder and use a NULL spec for live streams. + StoryboardRendererDecoderSpecFingerprint.result?.let { + val storyBoardUrlIndex = it.scanResult.patternScanResult!!.startIndex + 1 + val storyboardUrlRegister = + it.mutableMethod.getInstruction(storyBoardUrlIndex).registerA + + it.mutableMethod.addInstructions( + storyBoardUrlIndex + 1, """ + invoke-static { v$storyboardUrlRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardDecoderRendererSpec(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$storyboardUrlRegister + """ + ) + } ?: throw StoryboardRendererDecoderSpecFingerprint.exception + + /** + * Add settings + */ + SettingsPatch.addPreference( + arrayOf( + "SETTINGS: EXPERIMENTAL_FLAGS", + "SETTINGS: SPOOF_PLAYER_PARAMETER" + ) + ) + + SettingsPatch.updatePatchStatus("Spoof player parameters") + + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt new file mode 100644 index 000000000..6f93cb64f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplGeneralFingerprint.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.youtube.utils.fix.parameter.fingerprints + +import app.revanced.util.containsWideLiteralInstructionIndex +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 PlayerResponseModelImplGeneralFingerprint : MethodFingerprint( + returnType = "Ljava/lang/String;", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = emptyList(), + opcodes = listOf( + Opcode.RETURN_OBJECT, + Opcode.CONST_4, + Opcode.RETURN_OBJECT + ), + customFingerprint = handler@{ methodDef, _ -> + if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;")) + return@handler false + + methodDef.containsWideLiteralInstructionIndex(55735497) + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplLiveStreamFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplLiveStreamFingerprint.kt new file mode 100644 index 000000000..23c777f6b --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplLiveStreamFingerprint.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.youtube.utils.fix.parameter.fingerprints + +import app.revanced.util.containsWideLiteralInstructionIndex +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 PlayerResponseModelImplLiveStreamFingerprint : MethodFingerprint( + returnType = "Ljava/lang/String;", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = emptyList(), + opcodes = listOf( + Opcode.RETURN_OBJECT, + Opcode.CONST_4, + Opcode.RETURN_OBJECT + ), + customFingerprint = handler@{ methodDef, _ -> + if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;")) + return@handler false + + methodDef.containsWideLiteralInstructionIndex(70276274) + } +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt new file mode 100644 index 000000000..38a08458b --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/PlayerResponseModelImplRecommendedLevelFingerprint.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.youtube.utils.fix.parameter.fingerprints + +import app.revanced.util.containsWideLiteralInstructionIndex +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 PlayerResponseModelImplRecommendedLevelFingerprint : MethodFingerprint( + returnType = "I", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = emptyList(), + opcodes = listOf( + Opcode.SGET_OBJECT, + Opcode.IGET, + Opcode.RETURN + ), + customFingerprint = handler@{ methodDef, _ -> + if (!methodDef.definingClass.endsWith("/PlayerResponseModelImpl;")) + return@handler false + + methodDef.containsWideLiteralInstructionIndex(55735497) + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt new file mode 100644 index 000000000..89fd8aa95 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererDecoderRecommendedLevelFingerprint.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.youtube.utils.fix.parameter.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 + +/** + * Resolves to the same method as [StoryboardRendererDecoderSpecFingerprint]. + */ +internal object StoryboardRendererDecoderRecommendedLevelFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"), + opcodes = listOf( + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT + ), + strings = listOf("#-1#") +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt new file mode 100644 index 000000000..07bd89ad2 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererDecoderSpecFingerprint.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.youtube.utils.fix.parameter.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 + +/** +* Resolves to the same method as [StoryboardRendererDecoderRecommendedLevelFingerprint]. +*/ +internal object StoryboardRendererDecoderSpecFingerprint : MethodFingerprint( + returnType = "V", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;"), + opcodes = listOf( + Opcode.INVOKE_INTERFACE, // First instruction of the method. + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_4, + Opcode.CONST_4, + Opcode.IF_NEZ, + ), + strings = listOf("#-1#") +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererSpecFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererSpecFingerprint.kt new file mode 100644 index 000000000..b0ebe2c17 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardRendererSpecFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.utils.fix.parameter.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object StoryboardRendererSpecFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC, + returnType = "L", + parameters = listOf("Ljava/lang/String;", "J"), + strings = listOf("\\|"), +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailFingerprint.kt new file mode 100644 index 000000000..244c2753f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailFingerprint.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.youtube.utils.fix.parameter.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 + +/** + * Resolves using the class found in [StoryboardThumbnailParentFingerprint]. + */ +internal object StoryboardThumbnailFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Z", + parameters = listOf(), + opcodes = listOf( + Opcode.MOVE_RESULT, + Opcode.IF_GTZ, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.RETURN, + Opcode.RETURN, // Last instruction of method. + ), +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailParentFingerprint.kt new file mode 100644 index 000000000..92920a082 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/fix/parameter/fingerprints/StoryboardThumbnailParentFingerprint.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.youtube.utils.fix.parameter.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Here lies code that creates the seekbar thumbnails. + * + * An additional change here might force the thumbnails to be created, + * or possibly a change somewhere else (maybe involving YouTube 18.23.35 class `hte`) + */ +internal object StoryboardThumbnailParentFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Landroid/graphics/Bitmap;", + strings = listOf("Storyboard regionDecoder.decodeRegion exception - "), +) \ No newline at end of file