mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-27 20:30:19 +02:00
feat(YouTube): remove Spoof format stream data
patch
This commit is contained in:
parent
60c05cb193
commit
65bb867bd2
@ -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)
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
),
|
||||
)
|
||||
|
@ -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"),
|
||||
)
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user