refactor(Spoof streaming data): Instead of replacing the entire array StreamingData.formats, replace only the approxDurationMs field

This commit is contained in:
inotia00
2024-12-20 17:35:29 +09:00
parent 3c8e61c850
commit a040b78927
7 changed files with 159 additions and 95 deletions

View File

@ -12,6 +12,7 @@ import app.revanced.patches.music.utils.settings.CategoryType
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
import app.revanced.patches.music.utils.settings.addSwitchPreference
import app.revanced.patches.music.utils.settings.settingsPatch
import app.revanced.patches.shared.formatStreamModelConstructorFingerprint
import app.revanced.util.fingerprint.matchOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction

View File

@ -5,17 +5,6 @@ import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* On YouTube, this class is 'Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;'
* On YouTube Music, class names are obfuscated.
*/
internal val formatStreamModelConstructorFingerprint = legacyFingerprint(
name = "formatStreamModelConstructorFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
literals = listOf(45374643L),
)
/**
* YouTube Music 7.13.52 ~
*/

View File

@ -35,6 +35,21 @@ private fun Method.indexOfFieldReference(string: String) = indexOfFirstInstructi
reference.toString() == string
}
/**
* On YouTube, this class is 'Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;'
* On YouTube Music, class names are obfuscated.
*/
internal val formatStreamModelConstructorFingerprint = legacyFingerprint(
name = "formatStreamModelConstructorFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
opcodes = listOf(
Opcode.IGET_WIDE,
Opcode.IPUT_WIDE,
),
literals = listOf(45374643L),
)
internal val mdxPlayerDirectorSetVideoStageFingerprint = legacyFingerprint(
name = "mdxPlayerDirectorSetVideoStageFingerprint",
strings = listOf("MdxDirector setVideoStage ad should be null when videoStage is not an Ad state ")

View File

@ -12,13 +12,13 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.shared.blockrequest.blockRequestPatch
import app.revanced.patches.shared.extension.Constants.SPOOF_PATH
import app.revanced.patches.shared.formatStreamModelConstructorFingerprint
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.fingerprint.definingClassOrThrow
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@ -81,6 +81,13 @@ fun baseSpoofStreamingDataPatch(
// region Replace the streaming data.
val approxDurationMsFieldName = formatStreamModelConstructorFingerprint.matchOrThrow().let {
with (it.method) {
val approxDurationMsFieldIndex = it.patternMatch!!.startIndex
(getInstruction<ReferenceInstruction>(approxDurationMsFieldIndex).reference as FieldReference).name
}
}
createStreamingDataFingerprint.matchOrThrow(createStreamingDataParentFingerprint)
.let { result ->
result.method.apply {
@ -134,40 +141,42 @@ fun baseSpoofStreamingDataPatch(
addInstructionsWithLabels(
0,
"""
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->isSpoofingEnabled()Z
move-result v0
if-eqz v0, :disabled
# Get video id.
iget-object v2, p1, $videoDetailsClass->c:Ljava/lang/String;
if-eqz v2, :disabled
# Get streaming data.
invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer;
move-result-object v3
if-eqz v3, :disabled
# Parse streaming data.
sget-object v4, $playerProtoClass->a:$playerProtoClass
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass
move-result-object v5
check-cast v5, $playerProtoClass
iget-object v6, v5, $getStreamingDataField
if-eqz v6, :disabled
# Get original streaming data.
iget-object v0, p0, $setStreamingDataField
# Set spoofed streaming data.
iput-object v6, p0, $setStreamingDataField
# Set original streaming data formats.
invoke-static { v2, v0, v6 }, $EXTENSION_CLASS_DESCRIPTOR->setFormats(Ljava/lang/String;$STREAMING_DATA_INTERFACE$STREAMING_DATA_INTERFACE)V
:disabled
return-void
""",
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->isSpoofingEnabled()Z
move-result v0
if-eqz v0, :disabled
# Get video id.
iget-object v2, p1, $videoDetailsClass->c:Ljava/lang/String;
if-eqz v2, :disabled
# Get streaming data.
invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer;
move-result-object v3
if-eqz v3, :disabled
# Parse streaming data.
sget-object v4, $playerProtoClass->a:$playerProtoClass
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass
move-result-object v5
check-cast v5, $playerProtoClass
iget-object v6, v5, $getStreamingDataField
if-eqz v6, :disabled
# Get original streaming data.
iget-object v0, p0, $setStreamingDataField
# Set spoofed streaming data.
iput-object v6, p0, $setStreamingDataField
# Get video length from original streaming data and save to extension.
const-string v5, "$approxDurationMsFieldName"
invoke-static { v2, v5, v0, v6 }, $EXTENSION_CLASS_DESCRIPTOR->setApproxDurationMs(Ljava/lang/String;Ljava/lang/String;$STREAMING_DATA_INTERFACE$STREAMING_DATA_INTERFACE)V
:disabled
return-void
""",
)
},
)
@ -188,24 +197,21 @@ fun baseSpoofStreamingDataPatch(
getInstruction<TwoRegisterInstruction>(videoIdIndex).registerB
val videoIdReference =
getInstruction<ReferenceInstruction>(videoIdIndex).reference
val formatsIndex = indexOfFirstInstructionReversedOrThrow(videoIdIndex) {
opcode == Opcode.IGET_OBJECT &&
getReference<FieldReference>()?.definingClass == STREAMING_DATA_INTERFACE
}
val freeRegister = getInstruction<OneRegisterInstruction>(
indexOfFirstInstructionOrThrow(formatsIndex, Opcode.CONST_WIDE)
).registerA
val audioCodecListRegister = getInstruction<TwoRegisterInstruction>(formatsIndex).registerA
val toMillisIndex = indexOfToMillisInstruction(this)
val freeRegister =
getInstruction<FiveRegisterInstruction>(toMillisIndex).registerC
val lengthMillisecondsRegister =
getInstruction<OneRegisterInstruction>(toMillisIndex + 1).registerA
addInstructions(
formatsIndex + 1, """
toMillisIndex + 2, """
# Get video id.
iget-object v$freeRegister, v$definingClassRegister, $videoIdReference
# Override streaming data formats.
invoke-static { v$freeRegister, v$audioCodecListRegister }, $EXTENSION_CLASS_DESCRIPTOR->getOriginalFormats(Ljava/lang/String;Ljava/util/List;)Ljava/util/List;
move-result-object v$audioCodecListRegister
invoke-static { v$freeRegister, v$lengthMillisecondsRegister, v${lengthMillisecondsRegister + 1} }, $EXTENSION_CLASS_DESCRIPTOR->getApproxDurationMsFromOriginalResponse(Ljava/lang/String;J)J
move-result-wide v$lengthMillisecondsRegister
"""
)
}

View File

@ -108,7 +108,8 @@ internal val videoStreamingDataConstructorFingerprint = legacyFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
returnType = "V",
customFingerprint = { method, _ ->
indexOfFormatStreamModelInitInstruction(method) >= 0
indexOfFormatStreamModelInitInstruction(method) >= 0 &&
indexOfToMillisInstruction(method) >= 0
},
)
@ -120,6 +121,17 @@ internal fun indexOfFormatStreamModelInitInstruction(method: Method) =
reference.parameterTypes.size > 1
}
internal fun indexOfToMillisInstruction(method: Method) =
method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL &&
reference?.name == "toMillis"
}
/**
* On YouTube, this class is 'Lcom/google/android/libraries/youtube/innertube/model/media/VideoStreamingData;'
* On YouTube Music, class names are obfuscated.
*/
internal val videoStreamingDataToStringFingerprint = legacyFingerprint(
name = "videoStreamingDataToStringFingerprint",
returnType = "Ljava/lang/String;",
@ -135,7 +147,6 @@ internal const val HLS_CURRENT_TIME_FEATURE_FLAG = 45355374L
internal val hlsCurrentTimeFingerprint = legacyFingerprint(
name = "hlsCurrentTimeFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Z", "L"),
literals = listOf(HLS_CURRENT_TIME_FEATURE_FLAG),
)