fix(YouTube - Client spoof): Spoof iOS client model to fix various side effects (#3220)

Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
This commit is contained in:
oSumAtrIX 2024-05-23 22:42:11 +02:00 committed by GitHub
parent c519fdfa17
commit 9b5f4ce2b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 54 additions and 56 deletions

View file

@ -19,6 +19,7 @@ import app.revanced.patches.youtube.misc.fix.playback.fingerprints.*
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.resultOrThrow import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@ -34,7 +35,6 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
name = "Spoof client", name = "Spoof client",
description = "Spoofs the client to allow video playback.", description = "Spoofs the client to allow video playback.",
dependencies = [ dependencies = [
SpoofClientResourcePatch::class,
PlayerResponseMethodHookPatch::class, PlayerResponseMethodHookPatch::class,
SettingsPatch::class, SettingsPatch::class,
AddResourcesPatch::class, AddResourcesPatch::class,
@ -74,6 +74,7 @@ object SpoofClientPatch : BytecodePatch(
BuildPlayerRequestURIFingerprint, BuildPlayerRequestURIFingerprint,
SetPlayerRequestClientTypeFingerprint, SetPlayerRequestClientTypeFingerprint,
CreatePlayerRequestBodyFingerprint, CreatePlayerRequestBodyFingerprint,
CreatePlayerRequestBodyWithModelFingerprint,
// Storyboard spoof. // Storyboard spoof.
StoryboardRendererSpecFingerprint, StoryboardRendererSpecFingerprint,
@ -97,10 +98,9 @@ object SpoofClientPatch : BytecodePatch(
sorting = Sorting.UNSORTED, sorting = Sorting.UNSORTED,
preferences = setOf( preferences = setOf(
SwitchPreference("revanced_spoof_client"), SwitchPreference("revanced_spoof_client"),
SwitchPreference("revanced_spoof_client_use_ios"), SwitchPreference("revanced_spoof_client_use_testsuite"),
), ),
), ),
) )
// region Block /initplayback requests to fall back to /get_watch requests. // region Block /initplayback requests to fall back to /get_watch requests.
@ -168,6 +168,22 @@ object SpoofClientPatch : BytecodePatch(
Triple(clientInfoField, clientInfoClientTypeField, clientInfoClientVersionField) Triple(clientInfoField, clientInfoClientTypeField, clientInfoClientVersionField)
} }
val clientInfoClientModelField = CreatePlayerRequestBodyWithModelFingerprint.resultOrThrow().mutableMethod.let {
val instructions = it.getInstructions()
val getClientModelIndex = it.indexOfFirstInstruction {
getReference<FieldReference>().toString() == "Landroid/os/Build;->MODEL:Ljava/lang/String;"
}
// The next IPUT_OBJECT instruction after getting the client model is setting the client model field.
instructions.subList(
getClientModelIndex,
instructions.lastIndex,
).first { instruction ->
instruction.opcode == Opcode.IPUT_OBJECT
}.getReference<FieldReference>() ?: throw PatchException("Could not find clientInfoClientModelField")
}
// endregion // endregion
// region Spoof client type for /player requests. // region Spoof client type for /player requests.
@ -189,7 +205,7 @@ object SpoofClientPatch : BytecodePatch(
) )
} }
// Change requestMessage.clientInfo.clientType and requestMessage.clientInfo.clientVersion to the spoofed values. // Change client info to use the spoofed values.
// Do this in a helper method, to remove the need of picking out multiple free registers from the hooked code. // Do this in a helper method, to remove the need of picking out multiple free registers from the hooked code.
result.mutableClass.methods.add( result.mutableClass.methods.add(
ImmutableMethod( ImmutableMethod(
@ -216,12 +232,17 @@ object SpoofClientPatch : BytecodePatch(
move-result v1 move-result v1
iput v1, v0, $clientInfoClientTypeField iput v1, v0, $clientInfoClientTypeField
# Set client model to the spoofed value.
iget-object v1, v0, $clientInfoClientModelField
invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientModel(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientInfoClientModelField
# Set client version to the spoofed value. # Set client version to the spoofed value.
iget-object v1, v0, $clientInfoClientVersionField iget-object v1, v0, $clientInfoClientVersionField
invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientVersion(Ljava/lang/String;)Ljava/lang/String; invoke-static { v1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->getClientVersion(Ljava/lang/String;)Ljava/lang/String;
move-result-object v1 move-result-object v1
iput-object v1, v0, $clientInfoClientVersionField iput-object v1, v0, $clientInfoClientVersionField
:disabled :disabled
return-void return-void
""", """,

View file

@ -1,18 +0,0 @@
package app.revanced.patches.youtube.misc.fix.playback
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
@Patch(dependencies = [ResourceMappingPatch::class])
internal object SpoofClientResourcePatch : ResourcePatch() {
internal var scrubbedPreviewThumbnailResourceId: Long = -1
override fun execute(context: ResourceContext) {
scrubbedPreviewThumbnailResourceId = ResourceMappingPatch[
"id",
"thumbnail",
]
}
}

View file

@ -43,7 +43,7 @@ object SpoofSignaturePatch : BytecodePatch(
StoryboardRendererDecoderSpecFingerprint, StoryboardRendererDecoderSpecFingerprint,
StoryboardRendererDecoderRecommendedLevelFingerprint, StoryboardRendererDecoderRecommendedLevelFingerprint,
StoryboardThumbnailParentFingerprint, StoryboardThumbnailParentFingerprint,
ScrubbedPreviewLayoutFingerprint, SpoofSignaturePatchScrubbedPreviewLayoutFingerprint,
StatsQueryParameterFingerprint, StatsQueryParameterFingerprint,
ParamsMapPutFingerprint, ParamsMapPutFingerprint,
), ),

View file

@ -0,0 +1,18 @@
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal object CreatePlayerRequestBodyWithModelFingerprint : MethodFingerprint(
returnType = "L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf(),
customFingerprint = { methodDef, _ ->
methodDef.implementation!!.instructions.any {
it.getReference<FieldReference>().toString() == "Landroid/os/Build;->MODEL:Ljava/lang/String;"
}
},
)

View file

@ -5,6 +5,7 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@Deprecated("Fingerprint is obsolete and will be deleted soon")
internal object ParamsMapPutFingerprint : MethodFingerprint( internal object ParamsMapPutFingerprint : MethodFingerprint(
returnType = "V", returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,

View file

@ -6,6 +6,7 @@ import app.revanced.util.containsWideLiteralInstructionValue
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@Deprecated("Fingerprint is obsolete and will be deleted soon")
internal object PlayerResponseModelImplLiveStreamFingerprint : MethodFingerprint( internal object PlayerResponseModelImplLiveStreamFingerprint : MethodFingerprint(
returnType = "Ljava/lang/String;", returnType = "Ljava/lang/String;",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,

View file

@ -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.SpoofClientResourcePatch
import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal 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 = { SpoofClientResourcePatch.scrubbedPreviewThumbnailResourceId },
)

View file

@ -6,6 +6,7 @@ import app.revanced.util.patch.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@Deprecated("Fingerprint is obsolete and will be deleted soon")
internal object SpoofSignaturePatchScrubbedPreviewLayoutFingerprint : LiteralValueFingerprint( internal object SpoofSignaturePatchScrubbedPreviewLayoutFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
returnType = "V", returnType = "V",

View file

@ -2,6 +2,7 @@ package app.revanced.patches.youtube.misc.fix.playback.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.fingerprint.MethodFingerprint
@Deprecated("Fingerprint is obsolete and will be deleted soon")
internal object StatsQueryParameterFingerprint : MethodFingerprint( internal object StatsQueryParameterFingerprint : MethodFingerprint(
strings = listOf("adunit"), strings = listOf("adunit"),
) )

View file

@ -10,6 +10,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
* An additional change here might force the thumbnails to be created, * 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`) * or possibly a change somewhere else (maybe involving YouTube 18.23.35 class `hte`)
*/ */
@Deprecated("Fingerprint is obsolete and will be deleted soon")
internal object StoryboardThumbnailParentFingerprint : MethodFingerprint( internal object StoryboardThumbnailParentFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "Landroid/graphics/Bitmap;", returnType = "Landroid/graphics/Bitmap;",

View file

@ -1092,11 +1092,10 @@
<string name="revanced_spoof_client_summary_on">Client is spoofed</string> <string name="revanced_spoof_client_summary_on">Client is spoofed</string>
<string name="revanced_spoof_client_summary_off">Client is not spoofed\n\nVideo playback may not work</string> <string name="revanced_spoof_client_summary_off">Client is not spoofed\n\nVideo playback may not work</string>
<string name="revanced_spoof_client_user_dialog_message">Turning off this setting may cause video playback issues.</string> <string name="revanced_spoof_client_user_dialog_message">Turning off this setting may cause video playback issues.</string>
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_client_spoof_title' --> <string name="revanced_spoof_client_use_testsuite_title">Spoof client to Android Testsuite</string>
<string name="revanced_spoof_client_use_ios_title">Spoof client to iOS</string> <string name="revanced_spoof_client_use_testsuite_summary">Spoof the client to Android Testsuite</string>
<string name="revanced_spoof_client_use_ios_summary">Spoof the client to iOS instead of an Android Testsuite</string> <string name="revanced_spoof_client_use_testsuite_summary_on">Client is spoofed to an Android Testsuite client (iOS client is used for live streams)\n\nSide effects include, but are not limited to:\n• Speed flyout menu is missing\n• Captions are missing\n• Player swipe gestures may not work\n• Low quality Shorts seekbar thumbnails\n• Watch history may not work</string>
<string name="revanced_spoof_client_use_ios_summary_on">Client is spoofed to an iOS client\n\nSide effects include:\n• 60 fps video may not be available\n• No HDR video\n• Some videos may not load\n• Higher video qualities may be missing</string> <string name="revanced_spoof_client_use_testsuite_summary_off">Client is spoofed to an iOS client\n\nSide effects include:\n• No HDR video\n• Speed flyout menu is missing</string>
<string name="revanced_spoof_client_use_ios_summary_off">Client is spoofed to an Android Testsuite client (iOS client is used for live streams)\n\nSide effects include:\n• Subtitles are missing\n• Player gestures may not work\n• Low quality Shorts seekbar thumbnails</string>
<string name="revanced_spoof_client_storyboard_timeout">Spoof client thumbnails not available (API timed out)</string> <string name="revanced_spoof_client_storyboard_timeout">Spoof client thumbnails not available (API timed out)</string>
<string name="revanced_spoof_client_storyboard_io_exception">Spoof client thumbnails temporarily not available: %s</string> <string name="revanced_spoof_client_storyboard_io_exception">Spoof client thumbnails temporarily not available: %s</string>
</patch> </patch>