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 new file mode 100644 index 0000000000..0246694ee7 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/ScrubbedPreviewLayoutFingerprint.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.misc.fix.playback.patch.SpoofSignatureVerificationResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import org.jf.dexlib2.AccessFlags +import org.jf.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. + literal = SpoofSignatureVerificationResourcePatch.scrubbedPreviewThumbnailResourceId +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailFingerprint.kt new file mode 100644 index 0000000000..51e824d0d5 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailFingerprint.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode + +/** + * Resolves using the class found in [StoryboardThumbnailParentFingerprint]. + */ +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/misc/fix/playback/fingerprints/StoryboardThumbnailParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailParentFingerprint.kt new file mode 100644 index 0000000000..ae50aae996 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/StoryboardThumbnailParentFingerprint.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.youtube.misc.fix.playback.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.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`) + */ +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 diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/UserAgentHeaderBuilderFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/UserAgentHeaderBuilderFingerprint.kt index 5428c2a4d2..ab71b605fc 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/UserAgentHeaderBuilderFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/UserAgentHeaderBuilderFingerprint.kt @@ -1,6 +1,5 @@ package app.revanced.patches.youtube.misc.fix.playback.fingerprints - import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import org.jf.dexlib2.Opcode diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/patch/SpoofSignatureVerificationPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/patch/SpoofSignatureVerificationPatch.kt index de1a4b3187..00b41556fd 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/patch/SpoofSignatureVerificationPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/patch/SpoofSignatureVerificationPatch.kt @@ -7,47 +7,35 @@ import app.revanced.patcher.annotation.Version import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.toMethodWalker import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +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.PatchResult import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import app.revanced.patches.shared.settings.preference.impl.StringResource -import app.revanced.patches.shared.settings.preference.impl.SwitchPreference -import app.revanced.patches.youtube.misc.fix.playback.fingerprints.ProtobufParameterBuilderFingerprint +import app.revanced.patches.youtube.misc.fix.playback.fingerprints.* import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.playertype.patch.PlayerTypeHookPatch -import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch +import org.jf.dexlib2.iface.instruction.ReferenceInstruction @Name("Spoof signature verification") @Description("Spoofs a patched client to prevent playback issues.") @DependsOn([ + SpoofSignatureVerificationResourcePatch::class, IntegrationsPatch::class, - SettingsPatch::class, - PlayerTypeHookPatch::class, + PlayerTypeHookPatch::class ]) @Version("0.0.1") class SpoofSignatureVerificationPatch : BytecodePatch( listOf( ProtobufParameterBuilderFingerprint, + StoryboardThumbnailParentFingerprint, + ScrubbedPreviewLayoutFingerprint, ) ) { override fun execute(context: BytecodeContext): PatchResult { - SettingsPatch.PreferenceScreen.MISC.addPreferences( - SwitchPreference( - "revanced_spoof_signature_verification", - StringResource("revanced_spoof_signature_verification_title", "Spoof app signature"), - StringResource("revanced_spoof_signature_verification_summary_on", - "App signature spoofed\\n\\n" - + "Side effects include:\\n" - + "• Ambient mode may not work\\n" - + "• Seekbar thumbnails are hidden\\n" - + "• Downloading videos may not work"), - StringResource("revanced_spoof_signature_verification_summary_off", "App signature not spoofed"), - StringResource("revanced_spoof_signature_verification_user_dialog_message", - "Turning off this setting may cause playback issues.") - ) - ) // hook parameter ProtobufParameterBuilderFingerprint.result?.let { @@ -68,6 +56,53 @@ class SpoofSignatureVerificationPatch : BytecodePatch( } } ?: return ProtobufParameterBuilderFingerprint.toErrorResult() + + // 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 ?: return StoryboardThumbnailParentFingerprint.toErrorResult() + 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 + """ + ) + } ?: return StoryboardThumbnailFingerprint.toErrorResult() + + + // 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, + """ + iget-object v0, p0, $imageViewFieldName # copy imageview field to a register + invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->seekbarImageViewCreated(Landroid/widget/ImageView;)V + """ + ) + } + } ?: return ScrubbedPreviewLayoutFingerprint.toErrorResult() + return PatchResultSuccess() } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/patch/SpoofSignatureVerificationResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/patch/SpoofSignatureVerificationResourcePatch.kt new file mode 100644 index 0000000000..b82dcfd956 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/patch/SpoofSignatureVerificationResourcePatch.kt @@ -0,0 +1,44 @@ +package app.revanced.patches.youtube.misc.fix.playback.patch + +import app.revanced.patcher.data.ResourceContext +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.patch.ResourcePatch +import app.revanced.patcher.patch.annotations.DependsOn +import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch +import app.revanced.patches.shared.settings.preference.impl.StringResource +import app.revanced.patches.shared.settings.preference.impl.SwitchPreference +import app.revanced.patches.youtube.misc.fix.playback.fingerprints.* +import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch + +@DependsOn([SettingsPatch::class, ResourceMappingPatch::class]) +class SpoofSignatureVerificationResourcePatch : ResourcePatch { + + override fun execute(context: ResourceContext): PatchResult { + SettingsPatch.PreferenceScreen.MISC.addPreferences( + SwitchPreference( + "revanced_spoof_signature_verification", + StringResource("revanced_spoof_signature_verification_title", "Spoof app signature"), + StringResource("revanced_spoof_signature_verification_summary_on", + "App signature spoofed\\n\\n" + + "Side effects include:\\n" + + "• Ambient mode may not work\\n" + + "• Seekbar thumbnails are hidden\\n" + + "• Downloading videos may not work"), + StringResource("revanced_spoof_signature_verification_summary_off", "App signature not spoofed"), + StringResource("revanced_spoof_signature_verification_user_dialog_message", + "Turning off this setting may cause playback issues.") + ) + ) + + scrubbedPreviewThumbnailResourceId = ResourceMappingPatch.resourceMappings.single { + it.type == "id" && it.name == "thumbnail" + }.id + + return PatchResultSuccess() + } + + companion object { + var scrubbedPreviewThumbnailResourceId: Long = -1 + } +}