mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-16 06:17:15 +02:00
fix(YouTube/Spoof format stream data): incorrect url is used
This commit is contained in:
parent
43ba5ad56a
commit
1f744e083d
@ -1,19 +1,21 @@
|
||||
package app.revanced.patches.youtube.utils.fix.formatstream
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
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.PlaybackStartFingerprint
|
||||
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.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
|
||||
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.getStringInstructionIndex
|
||||
import app.revanced.util.getTargetIndex
|
||||
@ -26,21 +28,21 @@ 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 options to spoof format stream data to prevent playback issues.",
|
||||
dependencies = setOf(
|
||||
PlayerResponseMethodHookPatch::class,
|
||||
SettingsPatch::class,
|
||||
VideoIdPatch::class,
|
||||
VideoInformationPatch::class,
|
||||
),
|
||||
compatiblePackages = Constants.COMPATIBLE_PACKAGE,
|
||||
fingerprints = setOf(
|
||||
EndpointUrlBuilderFingerprint,
|
||||
FormatStreamModelConstructorFingerprint,
|
||||
PlaybackStartFingerprint
|
||||
PlaybackStartParentFingerprint
|
||||
)
|
||||
) {
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
@ -80,8 +82,7 @@ object SpoofFormatStreamDataPatch : BaseBytecodePatch(
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
|
||||
// Hook player response video id, to start loading format stream data sooner in the background.
|
||||
VideoIdPatch.hookPlayerResponseVideoId("$INTEGRATIONS_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
|
||||
// region set field name
|
||||
|
||||
hookMethod = context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)!!
|
||||
.mutableClass.methods.find { method -> method.name == INTEGRATIONS_METHOD_DESCRIPTOR }
|
||||
@ -93,18 +94,24 @@ object SpoofFormatStreamDataPatch : BaseBytecodePatch(
|
||||
// Find the field name that will be used for reflection.
|
||||
val urlIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
val itagIndex = getTargetIndex(urlIndex + 1, Opcode.IGET)
|
||||
val audioCodecParameterIndex = getTargetIndex(urlIndex + 1, Opcode.IGET_OBJECT)
|
||||
|
||||
replaceFieldName(urlIndex, "replaceMeWithUrlFieldName")
|
||||
replaceFieldName(itagIndex, "replaceMeWithITagFieldName")
|
||||
replaceFieldName(audioCodecParameterIndex, "replaceMeWithAudioCodecParameterFieldName")
|
||||
}
|
||||
}
|
||||
|
||||
PlaybackStartFingerprint.resultOrThrow().mutableMethod.apply {
|
||||
// endregion
|
||||
|
||||
// Type of object being invoked is protobufList.
|
||||
// Find the class name of protobufList.
|
||||
// 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
|
||||
@ -116,35 +123,78 @@ object SpoofFormatStreamDataPatch : BaseBytecodePatch(
|
||||
val protobufListReference = getInstruction<ReferenceInstruction>(protobufListIndex).reference
|
||||
val protobufListClass = (protobufListReference as MethodReference).returnType
|
||||
|
||||
// Hooks all instructions that load or save protobufList.
|
||||
for (index in implementation!!.instructions.size - 1 downTo 0) {
|
||||
val instruction = getInstruction(index)
|
||||
val protobufListCalls = implementation!!.instructions.withIndex()
|
||||
.filter { instruction ->
|
||||
((instruction.value as? ReferenceInstruction)?.reference as? FieldReference)?.type == protobufListClass
|
||||
}
|
||||
|
||||
if (instruction.opcode != Opcode.IGET_OBJECT
|
||||
&& instruction.opcode != Opcode.IPUT_OBJECT)
|
||||
continue
|
||||
nonDashProtobufListReference =
|
||||
getInstruction<ReferenceInstruction>(protobufListCalls.elementAt(0).index).reference
|
||||
dashProtobufListReference =
|
||||
getInstruction<ReferenceInstruction>(protobufListCalls.elementAt(1).index).reference
|
||||
}
|
||||
|
||||
val fieldReference = instruction.getReference<FieldReference>()
|
||||
if (fieldReference?.definingClass != STREAMING_DATA_OUTER_CLASS)
|
||||
continue
|
||||
if (fieldReference.type != protobufListClass)
|
||||
continue
|
||||
// endregion
|
||||
|
||||
val insertRegister = getInstruction<TwoRegisterInstruction>(index).registerA
|
||||
val insertIndex =
|
||||
if (instruction.opcode == Opcode.IPUT_OBJECT)
|
||||
index
|
||||
else
|
||||
index + 1
|
||||
// region hook stream data
|
||||
|
||||
addInstruction(
|
||||
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,
|
||||
"invoke-static { v$insertRegister }, " +
|
||||
INTEGRATIONS_METHOD_CALL
|
||||
"""
|
||||
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))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
*/
|
||||
|
@ -0,0 +1,35 @@
|
||||
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(String formatStreamData)
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
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
|
||||
),
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object PlaybackStartFingerprint : MethodFingerprint(
|
||||
internal object PlaybackStartParentFingerprint : MethodFingerprint(
|
||||
returnType = "Lcom/google/android/libraries/youtube/innertube/model/media/VideoStreamingData;",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = emptyList(),
|
Loading…
x
Reference in New Issue
Block a user