feat(YouTube): remove Spoof format stream data patch

This commit is contained in:
inotia00 2024-05-23 01:10:09 +09:00
parent 60c05cb193
commit 65bb867bd2
7 changed files with 0 additions and 341 deletions

View File

@ -1,234 +0,0 @@
package app.revanced.patches.youtube.utils.fix.formatstream
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.patch.PatchException
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.utils.compatibility.Constants
import app.revanced.patches.youtube.utils.fix.formatstream.fingerprints.EndpointUrlBuilderFingerprint
import app.revanced.patches.youtube.utils.fix.formatstream.fingerprints.FormatStreamModelConstructorFingerprint
import app.revanced.patches.youtube.utils.fix.formatstream.fingerprints.PlaybackStartConstructorFingerprint
import app.revanced.patches.youtube.utils.fix.formatstream.fingerprints.PlaybackStartParentFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.MISC_PATH
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.util.getReference
import app.revanced.util.getStringInstructionIndex
import app.revanced.util.getTargetIndex
import app.revanced.util.getWalkerMethod
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
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.iface.reference.Reference
@Suppress("unused")
object SpoofFormatStreamDataPatch : BaseBytecodePatch(
name = "Spoof format stream data",
description = "Adds an option to spoof the format stream data to prevent playback issues.",
dependencies = setOf(
SettingsPatch::class,
VideoInformationPatch::class,
),
compatiblePackages = Constants.COMPATIBLE_PACKAGE,
fingerprints = setOf(
EndpointUrlBuilderFingerprint,
FormatStreamModelConstructorFingerprint,
PlaybackStartParentFingerprint
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"$MISC_PATH/SpoofFormatStreamDataPatch;"
private const val INTEGRATIONS_METHOD_DESCRIPTOR =
"hookStreamData"
private const val INTEGRATIONS_METHOD_CALL =
INTEGRATIONS_CLASS_DESCRIPTOR +
"->" +
INTEGRATIONS_METHOD_DESCRIPTOR +
"(Ljava/lang/Object;)V"
private const val STREAMING_DATA_OUTER_CLASS =
"Lcom/google/protos/youtube/api/innertube/StreamingDataOuterClass\$StreamingData;"
private lateinit var hookMethod: MutableMethod
private lateinit var uriMethod: MutableMethod
private fun MutableMethod.replaceFieldName(
index: Int,
replaceFieldString: String
) {
val reference = getInstruction<ReferenceInstruction>(index).reference
val fieldName = (reference as FieldReference).name
hookMethod.apply {
val stringIndex = getStringInstructionIndex(replaceFieldString)
val stringRegister = getInstruction<OneRegisterInstruction>(stringIndex).registerA
replaceInstruction(
stringIndex,
"const-string v$stringRegister, \"$fieldName\""
)
}
}
override fun execute(context: BytecodeContext) {
// region set field name
hookMethod = context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)!!
.mutableClass.methods.find { method -> method.name == INTEGRATIONS_METHOD_DESCRIPTOR }
?: throw PatchException("SpoofFormatStreamDataPatch not found")
FormatStreamModelConstructorFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
// Find the field name that will be used for reflection.
val urlIndex = it.scanResult.patternScanResult!!.startIndex
val itagIndex = getTargetIndex(urlIndex + 1, Opcode.IGET)
replaceFieldName(urlIndex, "replaceMeWithUrlFieldName")
replaceFieldName(itagIndex, "replaceMeWithITagFieldName")
}
it.mutableClass.methods.find { method ->
method.parameters == listOf("Ljava/lang/String;")
&& method.returnType == "Landroid/net/Uri;"
}?.apply {
val walkerIndex = indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL
&& getReference<MethodReference>()?.returnType == "Landroid/net/Uri;"
}
uriMethod = getWalkerMethod(context, walkerIndex)
} ?: throw PatchException("Uri method not found")
}
// endregion
// region set protobufList reference
lateinit var nonDashProtobufListReference: Reference
lateinit var dashProtobufListReference: Reference
val streamingDataOutClassConstructorMethod = context.findClass(STREAMING_DATA_OUTER_CLASS)!!
.mutableClass.methods.find { method -> method.name == "<init>" }
?: throw PatchException("StreamingDataOutClass not found")
streamingDataOutClassConstructorMethod.apply {
val protobufListIndex = indexOfFirstInstruction {
opcode == Opcode.INVOKE_STATIC
&& getReference<MethodReference>()?.definingClass == STREAMING_DATA_OUTER_CLASS
&& getReference<MethodReference>()?.name == "emptyProtobufList"
}
if (protobufListIndex <= 0)
throw PatchException("ProtobufList index not found")
val protobufListReference = getInstruction<ReferenceInstruction>(protobufListIndex).reference
val protobufListClass = (protobufListReference as MethodReference).returnType
val protobufListCalls = implementation!!.instructions.withIndex()
.filter { instruction ->
((instruction.value as? ReferenceInstruction)?.reference as? FieldReference)?.type == protobufListClass
}
nonDashProtobufListReference =
getInstruction<ReferenceInstruction>(protobufListCalls.elementAt(0).index).reference
dashProtobufListReference =
getInstruction<ReferenceInstruction>(protobufListCalls.elementAt(1).index).reference
}
// endregion
// region hook stream data
PlaybackStartConstructorFingerprint.resolve(
context,
PlaybackStartParentFingerprint.resultOrThrow().classDef
)
PlaybackStartConstructorFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val streamingDataOuterClassIndex = it.scanResult.patternScanResult!!.startIndex + 1
val streamingDataOuterClassReference = getInstruction<ReferenceInstruction>(streamingDataOuterClassIndex).reference
if (!streamingDataOuterClassReference.toString().endsWith(STREAMING_DATA_OUTER_CLASS))
throw PatchException("Type does not match: $streamingDataOuterClassReference")
val insertIndex = streamingDataOuterClassIndex + 1
val streamingDataOuterClassRegister = getInstruction<TwoRegisterInstruction>(streamingDataOuterClassIndex).registerA
val freeRegister = implementation!!.registerCount - parameters.size - 2
addInstructionsWithLabels(
insertIndex,
"""
if-eqz v$streamingDataOuterClassRegister, :ignore
iget-object v$freeRegister, v$streamingDataOuterClassRegister, $nonDashProtobufListReference
invoke-static { v$freeRegister }, $INTEGRATIONS_METHOD_CALL
iget-object v$freeRegister, v$streamingDataOuterClassRegister, $dashProtobufListReference
invoke-static { v$freeRegister }, $INTEGRATIONS_METHOD_CALL
""", ExternalLabel("ignore", getInstruction(insertIndex))
)
}
}
uriMethod.apply {
val insertIndex = implementation!!.instructions.size - 1
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructions(
insertIndex, """
invoke-static { v$insertRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->hookUri(Landroid/net/Uri;)Landroid/net/Uri;
move-result-object v$insertRegister
"""
)
}
// endregion
// region hook endpoint url
EndpointUrlBuilderFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val uriIndex = indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL
&& getReference<MethodReference>()?.definingClass == "Landroid/net/Uri;"
&& getReference<MethodReference>()?.name == "toString"
}
val uriStringIndex = getTargetIndex(uriIndex, Opcode.IPUT_OBJECT)
val uriStringReference = getInstruction<ReferenceInstruction>(uriStringIndex).reference
it.mutableClass.methods.find { method ->
method.parameters == listOf("Lcom/google/protobuf/MessageLite;")
&& method.returnType == "V"
}?.addInstructions(
0,
"""
iget-object v0, p0, $uriStringReference
invoke-static { v0 }, $INTEGRATIONS_CLASS_DESCRIPTOR->newEndpointUrlResponse(Ljava/lang/String;)V
"""
) ?: throw PatchException("PlaybackStart method not found")
}
}
// endregion
/**
* Add settings
*/
SettingsPatch.addPreference(
arrayOf(
"PREFERENCE_CATEGORY: MISC_EXPERIMENTAL_FLAGS",
"SETTINGS: SPOOF_FORMAT_STREAM_DATA"
)
)
SettingsPatch.updatePatchStatus(this)
}
}

View File

@ -1,35 +0,0 @@
package app.revanced.patches.youtube.utils.fix.formatstream.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.fix.formatstream.fingerprints.EndpointUrlBuilderFingerprint.indexOfToStringInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
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 object EndpointUrlBuilderFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.DECLARED_SYNCHRONIZED,
returnType = "Ljava/lang/String;",
parameters = emptyList(),
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL, // Uri.toString()
Opcode.MOVE_RESULT_OBJECT,
),
strings = listOf("asig"),
customFingerprint = { methodDef, _ ->
indexOfToStringInstruction(methodDef) >= 0
}
) {
fun indexOfToStringInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
opcode == Opcode.INVOKE_VIRTUAL
&& getReference<MethodReference>()?.definingClass == "Landroid/net/Uri;"
&& getReference<MethodReference>()?.name == "toString"
}
}

View File

@ -1,30 +0,0 @@
package app.revanced.patches.youtube.utils.fix.formatstream.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.utils.fix.formatstream.fingerprints.FormatStreamModelConstructorFingerprint.indexOfParseInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
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 object FormatStreamModelConstructorFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
returnType = "V",
opcodes = listOf(
Opcode.IGET_OBJECT, // get formatStreamData
Opcode.INVOKE_STATIC // Uri.parse(String formatStreamData)
),
customFingerprint = { methodDef, classDef ->
classDef.type == "Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;" &&
indexOfParseInstruction(methodDef) >= 0
}
) {
fun indexOfParseInstruction(methodDef: Method) =
methodDef.indexOfFirstInstruction {
opcode == Opcode.INVOKE_STATIC && getReference<MethodReference>()?.name == "parse"
}
}

View File

@ -1,18 +0,0 @@
package app.revanced.patches.youtube.utils.fix.formatstream.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
internal object PlaybackStartConstructorFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("L"),
opcodes = listOf(
Opcode.IPUT,
Opcode.IGET_OBJECT, // type: Lcom/google/protos/youtube/api/innertube/StreamingDataOuterClass$StreamingData;
Opcode.IF_NEZ
),
)

View File

@ -1,13 +0,0 @@
package app.revanced.patches.youtube.utils.fix.formatstream.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object PlaybackStartParentFingerprint : MethodFingerprint(
returnType = "Lcom/google/android/libraries/youtube/innertube/model/media/VideoStreamingData;",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = emptyList(),
strings = listOf("Invalid playback type; streaming data is not playable"),
)

View File

@ -1396,13 +1396,6 @@ Tap on the continue button and disable battery optimizations."</string>
<string name="revanced_sanitize_sharing_links_summary">Removes tracking query parameters from the URLs when sharing links.</string>
<string name="revanced_disable_quic_protocol_title">Disable QUIC protocol</string>
<string name="revanced_disable_quic_protocol_summary">"Disable CronetEngine's QUIC protocol."</string>
<string name="revanced_spoof_format_stream_data_title">Spoof format stream data</string>
<string name="revanced_spoof_format_stream_data_summary">"Spoofs the format stream data to prevent playback issues.
Limitations:
• There may be about 5 seconds of buffering when videos start.
• If buffering takes too long, you may need to close and reopen the video.
• Since this is still under development, there may be other unknown issues."</string>
<string name="revanced_spoof_player_parameter_title">Spoof player parameter</string>
<string name="revanced_spoof_player_parameter_summary">"Spoofs player parameters to prevent playback issues.

View File

@ -562,9 +562,6 @@
<!-- PREFERENCE_CATEGORY: MISC_EXPERIMENTAL_FLAGS
<PreferenceCategory android:title="@string/revanced_preference_category_experimental_flag" android:layout="@layout/revanced_settings_preferences_category"/>PREFERENCE_CATEGORY: MISC_EXPERIMENTAL_FLAGS -->
<!-- SETTINGS: SPOOF_FORMAT_STREAM_DATA
<SwitchPreference android:title="@string/revanced_spoof_format_stream_data_title" android:key="revanced_spoof_format_stream_data" android:defaultValue="false" android:summary="@string/revanced_spoof_format_stream_data_summary" />SETTINGS: SPOOF_FORMAT_STREAM_DATA -->
<!-- SETTINGS: SPOOF_PLAYER_PARAMETER
<SwitchPreference android:title="@string/revanced_spoof_player_parameter_title" android:key="revanced_spoof_player_parameter" android:defaultValue="false" android:summary="@string/revanced_spoof_player_parameter_summary" />
<SwitchPreference android:title="@string/revanced_spoof_player_parameter_in_feed_title" android:key="revanced_spoof_player_parameter_in_feed" android:defaultValue="false" android:summaryOn="@string/revanced_spoof_player_parameter_in_feed_summary_on" android:summaryOff="@string/revanced_spoof_player_parameter_in_feed_summary_off" android:dependency="revanced_spoof_player_parameter" />SETTINGS: SPOOF_PLAYER_PARAMETER -->
@ -644,7 +641,6 @@
<Preference android:title="Enable minimized playback" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
<Preference android:title="Enable open links directly" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
<Preference android:title="Sanitize sharing links" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
<Preference android:title="Spoof format stream data" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
<Preference android:title="Spoof test client" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
</PreferenceCategory>