diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt index ef47ee95..301681e4 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt @@ -3,40 +3,42 @@ package app.revanced.patches.youtube.misc.fix.playback import app.revanced.extensions.exception 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.replaceInstruction import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.ProtobufParameterBuilderFingerprint -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.ScrubbedPreviewLayoutFingerprint -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.StoryboardThumbnailFingerprint -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.StoryboardThumbnailParentFingerprint +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.youtube.misc.fix.playback.fingerprints.* import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch -import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import app.revanced.patches.youtube.video.information.VideoInformationPatch +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Patch( description = "Spoofs the signature to prevent playback issues.", dependencies = [ SpoofSignatureResourcePatch::class, IntegrationsPatch::class, - PlayerTypeHookPatch::class + PlayerTypeHookPatch::class, + VideoInformationPatch::class ] ) object SpoofSignaturePatch : BytecodePatch( setOf( ProtobufParameterBuilderFingerprint, StoryboardThumbnailParentFingerprint, - ScrubbedPreviewLayoutFingerprint + StoryboardRendererSpecFingerprint, + PlayerResponseModelImplFingerprint ) ) { private const val INTEGRATIONS_CLASS_DESCRIPTOR = - "Lapp/revanced/integrations/patches/SpoofSignaturePatch;" + "Lapp/revanced/integrations/patches/spoof/SpoofSignaturePatch;" override fun execute(context: BytecodeContext) { - // hook parameter + // Hook parameter. ProtobufParameterBuilderFingerprint.result?.let { val setParamMethod = context .toMethodWalker(it.method) @@ -55,51 +57,71 @@ object SpoofSignaturePatch : BytecodePatch( } } ?: throw ProtobufParameterBuilderFingerprint.exception - // When signature spoofing is enabled, the seekbar when tapped does not show // the video time, chapter names, or the video thumbnail. // Changing the value returned of this method forces all of these to show up, // except the thumbnails are blank, which is handled with the patch below. - StoryboardThumbnailParentFingerprint.result ?: throw StoryboardThumbnailParentFingerprint.exception - StoryboardThumbnailFingerprint.resolve(context, StoryboardThumbnailParentFingerprint.result!!.classDef) - StoryboardThumbnailFingerprint.result?.apply { - val endIndex = scanResult.patternScanResult!!.endIndex - // Replace existing instruction to preserve control flow label. - // The replaced return instruction always returns false - // (it is the 'no thumbnails found' control path), - // so there is no need to pass the existing return value to integrations. - mutableMethod.replaceInstruction( - endIndex, - """ - invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarThumbnailOverrideValue()Z - """ - ) - // Since this is end of the method must replace one line then add the rest. - mutableMethod.addInstructions( - endIndex + 1, - """ - move-result v0 - return v0 - """ - ) - } ?: throw StoryboardThumbnailFingerprint.exception - - - // Seekbar thumbnail now show up but are always a blank image. - // Additional changes are needed to force the client to generate the thumbnails (assuming it's possible), - // but for now hide the empty thumbnail. - ScrubbedPreviewLayoutFingerprint.result?.apply { - val endIndex = scanResult.patternScanResult!!.endIndex - mutableMethod.apply { - val imageViewFieldName = getInstruction(endIndex).reference - addInstructions( - implementation!!.instructions.lastIndex, + StoryboardThumbnailParentFingerprint.result?.classDef?.let { classDef -> + StoryboardThumbnailFingerprint.also { + it.resolve( + context, + classDef + ) + }.result?.let { + val endIndex = it.scanResult.patternScanResult!!.endIndex + // Replace existing instruction to preserve control flow label. + // The replaced return instruction always returns false + // (it is the 'no thumbnails found' control path), + // so there is no need to pass the existing return value to integrations. + it.mutableMethod.replaceInstruction( + endIndex, """ - iget-object v0, p0, $imageViewFieldName # copy imageview field to a register - invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->seekbarImageViewCreated(Landroid/widget/ImageView;)V + invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarThumbnailOverrideValue()Z """ ) - } - } ?: throw ScrubbedPreviewLayoutFingerprint.exception + // Since this is end of the method must replace one line then add the rest. + it.mutableMethod.addInstructions( + endIndex + 1, + """ + move-result v0 + return v0 + """ + ) + } ?: throw StoryboardThumbnailFingerprint.exception + + /** + * Hook StoryBoard renderer url + */ + PlayerResponseModelImplFingerprint.result?.let { + it.mutableMethod.apply { + val getStoryBoardIndex = it.scanResult.patternScanResult!!.endIndex + val getStoryBoardRegister = getInstruction(getStoryBoardIndex).registerA + + addInstructions( + getStoryBoardIndex, + """ + invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec()Ljava/lang/String; + move-result-object v$getStoryBoardRegister + """ + ) + } + } ?: throw PlayerResponseModelImplFingerprint.exception + + StoryboardRendererSpecFingerprint.result?.let { + it.mutableMethod.apply { + val storyBoardUrlParams = 0 + + addInstructionsWithLabels( + 0, + """ + if-nez p$storyBoardUrlParams, :ignore + invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec()Ljava/lang/String; + move-result-object p$storyBoardUrlParams + """, + ExternalLabel("ignore", getInstruction(0)) + ) + } + } ?: throw StoryboardRendererSpecFingerprint.exception + } } -} +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt index 67a78288..f50b8e52 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignatureResourcePatch.kt @@ -31,7 +31,7 @@ object SpoofSignatureResourcePatch : ResourcePatch() { + "Side effects include:\\n" + "• No ambient mode\\n" + "• Videos cannot be downloaded\\n" - + "• Seekbar thumbnails are hidden" + + "• Low quality seekbar thumbnails" ), StringResource( "revanced_spoof_signature_verification_enabled_summary_off", diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplFingerprint.kt new file mode 100644 index 00000000..9dd4fa35 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/PlayerResponseModelImplFingerprint.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.extensions.containsConstantInstructionValue +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +object PlayerResponseModelImplFingerprint : 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.containsConstantInstructionValue(55735497) + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ProtobufParameterBuilderFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ProtobufParameterBuilderFingerprint.kt index 46568f1f..037485d5 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ProtobufParameterBuilderFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ProtobufParameterBuilderFingerprint.kt @@ -10,4 +10,4 @@ object ProtobufParameterBuilderFingerprint : MethodFingerprint( Opcode.IPUT_OBJECT ), strings = listOf("Unexpected empty videoId.", "Prefetch request are disabled.") -) \ No newline at end of file +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ScrubbedPreviewLayoutFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ScrubbedPreviewLayoutFingerprint.kt deleted file mode 100644 index 36bce605..00000000 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ScrubbedPreviewLayoutFingerprint.kt +++ /dev/null @@ -1,27 +0,0 @@ -package app.revanced.patches.youtube.misc.fix.playback.fingerprints - -import app.revanced.patcher.extensions.or -import app.revanced.patches.youtube.misc.fix.playback.SpoofSignatureResourcePatch -import app.revanced.util.patch.LiteralValueFingerprint -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -object ScrubbedPreviewLayoutFingerprint : LiteralValueFingerprint( - accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, - returnType = "V", - parameters = listOf("Landroid/content/Context;", "Landroid/util/AttributeSet;", "I", "I"), - opcodes = listOf( - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT, - Opcode.INVOKE_VIRTUAL, - Opcode.CONST, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.CHECK_CAST, - Opcode.IPUT_OBJECT, // preview imageview - ), - // This resource is used in ~ 40 different locations, but this method has a distinct list of parameters to match to. - literalSupplier = { SpoofSignatureResourcePatch.scrubbedPreviewThumbnailResourceId } -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererSpecFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererSpecFingerprint.kt new file mode 100644 index 00000000..c6030b91 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardRendererSpecFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +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/video/quality/RememberVideoQualityPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt index 3f3c13a4..bb0ed936 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/quality/RememberVideoQualityPatch.kt @@ -122,7 +122,6 @@ object RememberVideoQualityPatch : BytecodePatch( * It also hooks the method which is called when the video quality to set is determined. * Conveniently, at this point the video quality is overridden to the remembered playback speed. */ - VideoInformationPatch.onCreateHook(INTEGRATIONS_CLASS_DESCRIPTOR, "newVideoStarted") diff --git a/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt index 683c05d6..76721143 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/video/videoid/VideoIdPatch.kt @@ -59,7 +59,7 @@ object VideoIdPatch : BytecodePatch( } - /** + /** * Adds an invoke-static instruction, called with the new id when the video changes. * * Supports all videos (regular videos, Shorts and Stories).