mirror of
https://github.com/revanced/revanced-patches
synced 2024-11-19 00:09:24 +01:00
fix(YouTube - Spoof signature): Fix tracking such as history or watch time
This commit is contained in:
parent
c22604a726
commit
bcd8b48e70
@ -16,7 +16,8 @@ import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
|
|||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
||||||
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
|
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.*
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
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.ReferenceInstruction
|
||||||
|
|
||||||
@ -28,8 +29,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
|||||||
PlayerResponseMethodHookPatch::class,
|
PlayerResponseMethodHookPatch::class,
|
||||||
VideoInformationPatch::class,
|
VideoInformationPatch::class,
|
||||||
SpoofSignatureResourcePatch::class,
|
SpoofSignatureResourcePatch::class,
|
||||||
AddResourcesPatch::class
|
AddResourcesPatch::class,
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
object SpoofSignaturePatch : BytecodePatch(
|
object SpoofSignaturePatch : BytecodePatch(
|
||||||
setOf(
|
setOf(
|
||||||
@ -41,7 +42,9 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
StoryboardRendererDecoderRecommendedLevelFingerprint,
|
StoryboardRendererDecoderRecommendedLevelFingerprint,
|
||||||
StoryboardThumbnailParentFingerprint,
|
StoryboardThumbnailParentFingerprint,
|
||||||
ScrubbedPreviewLayoutFingerprint,
|
ScrubbedPreviewLayoutFingerprint,
|
||||||
)
|
StatsQueryParameterFingerprint,
|
||||||
|
ParamsMapPutFingerprint,
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/integrations/youtube/patches/spoof/SpoofSignaturePatch;"
|
"Lapp/revanced/integrations/youtube/patches/spoof/SpoofSignaturePatch;"
|
||||||
@ -55,14 +58,14 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
preferences = setOf(
|
preferences = setOf(
|
||||||
SwitchPreference("revanced_spoof_signature_verification_enabled"),
|
SwitchPreference("revanced_spoof_signature_verification_enabled"),
|
||||||
SwitchPreference("revanced_spoof_signature_in_feed_enabled"),
|
SwitchPreference("revanced_spoof_signature_in_feed_enabled"),
|
||||||
SwitchPreference("revanced_spoof_storyboard")
|
SwitchPreference("revanced_spoof_storyboard"),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hook the player parameters.
|
// Hook the player parameters.
|
||||||
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameter(
|
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameter(
|
||||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Z)Ljava/lang/String;"
|
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Z)Ljava/lang/String;",
|
||||||
)
|
)
|
||||||
|
|
||||||
// Force the seekbar time and chapters to always show up.
|
// Force the seekbar time and chapters to always show up.
|
||||||
@ -72,7 +75,7 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
StoryboardThumbnailFingerprint.also {
|
StoryboardThumbnailFingerprint.also {
|
||||||
it.resolve(
|
it.resolve(
|
||||||
context,
|
context,
|
||||||
classDef
|
classDef,
|
||||||
)
|
)
|
||||||
}.result?.let {
|
}.result?.let {
|
||||||
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
val endIndex = it.scanResult.patternScanResult!!.endIndex
|
||||||
@ -84,7 +87,7 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
endIndex,
|
endIndex,
|
||||||
"""
|
"""
|
||||||
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarThumbnailOverrideValue()Z
|
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarThumbnailOverrideValue()Z
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
// Since this is end of the method must replace one line then add the rest.
|
// Since this is end of the method must replace one line then add the rest.
|
||||||
it.mutableMethod.addInstructions(
|
it.mutableMethod.addInstructions(
|
||||||
@ -92,7 +95,7 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
"""
|
"""
|
||||||
move-result v0
|
move-result v0
|
||||||
return v0
|
return v0
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
} ?: throw StoryboardThumbnailFingerprint.exception
|
} ?: throw StoryboardThumbnailFingerprint.exception
|
||||||
}
|
}
|
||||||
@ -107,7 +110,7 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
"""
|
"""
|
||||||
iget-object v0, p0, $imageViewFieldName # copy imageview field to a register
|
iget-object v0, p0, $imageViewFieldName # copy imageview field to a register
|
||||||
invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->seekbarImageViewCreated(Landroid/widget/ImageView;)V
|
invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->seekbarImageViewCreated(Landroid/widget/ImageView;)V
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: throw ScrubbedPreviewLayoutFingerprint.exception
|
} ?: throw ScrubbedPreviewLayoutFingerprint.exception
|
||||||
@ -117,7 +120,7 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
*/
|
*/
|
||||||
arrayOf(
|
arrayOf(
|
||||||
PlayerResponseModelImplGeneralFingerprint,
|
PlayerResponseModelImplGeneralFingerprint,
|
||||||
PlayerResponseModelImplLiveStreamFingerprint
|
PlayerResponseModelImplLiveStreamFingerprint,
|
||||||
).forEach { fingerprint ->
|
).forEach { fingerprint ->
|
||||||
fingerprint.result?.let {
|
fingerprint.result?.let {
|
||||||
it.mutableMethod.apply {
|
it.mutableMethod.apply {
|
||||||
@ -130,7 +133,7 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
"""
|
"""
|
||||||
invoke-static { v$getStoryBoardRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String;
|
invoke-static { v$getStoryBoardRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String;
|
||||||
move-result-object v$getStoryBoardRegister
|
move-result-object v$getStoryBoardRegister
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: throw fingerprint.exception
|
} ?: throw fingerprint.exception
|
||||||
@ -143,10 +146,11 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
.getInstruction<OneRegisterInstruction>(moveOriginalRecommendedValueIndex).registerA
|
.getInstruction<OneRegisterInstruction>(moveOriginalRecommendedValueIndex).registerA
|
||||||
|
|
||||||
it.mutableMethod.addInstructions(
|
it.mutableMethod.addInstructions(
|
||||||
moveOriginalRecommendedValueIndex + 1, """
|
moveOriginalRecommendedValueIndex + 1,
|
||||||
|
"""
|
||||||
invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I
|
invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I
|
||||||
move-result v$originalValueRegister
|
move-result v$originalValueRegister
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
} ?: throw StoryboardRendererDecoderRecommendedLevelFingerprint.exception
|
} ?: throw StoryboardRendererDecoderRecommendedLevelFingerprint.exception
|
||||||
|
|
||||||
@ -158,10 +162,11 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
getInstruction<OneRegisterInstruction>(moveOriginalRecommendedValueIndex).registerA
|
getInstruction<OneRegisterInstruction>(moveOriginalRecommendedValueIndex).registerA
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
moveOriginalRecommendedValueIndex, """
|
moveOriginalRecommendedValueIndex,
|
||||||
|
"""
|
||||||
invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I
|
invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I
|
||||||
move-result v$originalValueRegister
|
move-result v$originalValueRegister
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: throw PlayerResponseModelImplRecommendedLevelFingerprint.exception
|
} ?: throw PlayerResponseModelImplRecommendedLevelFingerprint.exception
|
||||||
@ -177,7 +182,7 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
invoke-static { p$storyBoardUrlParams }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String;
|
invoke-static { p$storyBoardUrlParams }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String;
|
||||||
move-result-object p$storyBoardUrlParams
|
move-result-object p$storyBoardUrlParams
|
||||||
""",
|
""",
|
||||||
ExternalLabel("ignore", getInstruction(0))
|
ExternalLabel("ignore", getInstruction(0)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: throw StoryboardRendererSpecFingerprint.exception
|
} ?: throw StoryboardRendererSpecFingerprint.exception
|
||||||
@ -189,11 +194,43 @@ object SpoofSignaturePatch : BytecodePatch(
|
|||||||
it.mutableMethod.getInstruction<OneRegisterInstruction>(storyBoardUrlIndex).registerA
|
it.mutableMethod.getInstruction<OneRegisterInstruction>(storyBoardUrlIndex).registerA
|
||||||
|
|
||||||
it.mutableMethod.addInstructions(
|
it.mutableMethod.addInstructions(
|
||||||
storyBoardUrlIndex + 1, """
|
storyBoardUrlIndex + 1,
|
||||||
|
"""
|
||||||
invoke-static { v$storyboardUrlRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardDecoderRendererSpec(Ljava/lang/String;)Ljava/lang/String;
|
invoke-static { v$storyboardUrlRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardDecoderRendererSpec(Ljava/lang/String;)Ljava/lang/String;
|
||||||
move-result-object v$storyboardUrlRegister
|
move-result-object v$storyboardUrlRegister
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
} ?: throw StoryboardRendererDecoderSpecFingerprint.exception
|
} ?: throw StoryboardRendererDecoderSpecFingerprint.exception
|
||||||
|
|
||||||
|
// Fix stats not being tracked.
|
||||||
|
// Due to signature spoofing "adformat" is present in query parameters made for /stats requests,
|
||||||
|
// even though, for regular videos, it should not be.
|
||||||
|
// This breaks stats tracking.
|
||||||
|
// Replace the ad parameter with the video parameter in the query parameters.
|
||||||
|
StatsQueryParameterFingerprint.result?.let {
|
||||||
|
val putMethod = ParamsMapPutFingerprint.result?.method?.toString()
|
||||||
|
?: throw ParamsMapPutFingerprint.exception
|
||||||
|
|
||||||
|
it.mutableMethod.apply {
|
||||||
|
val adParamIndex = it.scanResult.stringsScanResult!!.matches.first().index
|
||||||
|
val videoParamIndex = adParamIndex + 3
|
||||||
|
|
||||||
|
// Replace the ad parameter with the video parameter.
|
||||||
|
replaceInstruction(adParamIndex, getInstruction(videoParamIndex))
|
||||||
|
|
||||||
|
// Call paramsMap.put instead of paramsMap.putIfNotExist
|
||||||
|
// because the key is already present in the map.
|
||||||
|
val putAdParamIndex = adParamIndex + 1
|
||||||
|
val putIfKeyNotExistsInstruction = getInstruction<FiveRegisterInstruction>(putAdParamIndex)
|
||||||
|
replaceInstruction(
|
||||||
|
putAdParamIndex,
|
||||||
|
"invoke-virtual { " +
|
||||||
|
"v${putIfKeyNotExistsInstruction.registerC}, " +
|
||||||
|
"v${putIfKeyNotExistsInstruction.registerD}, " +
|
||||||
|
"v${putIfKeyNotExistsInstruction.registerE} }, " +
|
||||||
|
putMethod,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} ?: throw StatsQueryParameterFingerprint.exception
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.fix.playback.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 ParamsMapPutFingerprint : MethodFingerprint(
|
||||||
|
returnType = "V",
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
parameters = listOf(
|
||||||
|
"Ljava/lang/String;",
|
||||||
|
"Ljava/lang/String;",
|
||||||
|
),
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.CONST_4,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.MOVE_OBJECT,
|
||||||
|
Opcode.INVOKE_DIRECT_RANGE,
|
||||||
|
),
|
||||||
|
)
|
@ -0,0 +1,7 @@
|
|||||||
|
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
|
||||||
|
internal object StatsQueryParameterFingerprint : MethodFingerprint(
|
||||||
|
strings = listOf("adunit"),
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user