Skip to content

Commit f2edae2

Browse files
committed
fix(YouTube - Spoof signature): Fix tracking such as history or watch time
1 parent c153979 commit f2edae2

File tree

3 files changed

+88
-20
lines changed

3 files changed

+88
-20
lines changed

src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofSignaturePatch.kt

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
1616
import app.revanced.patches.youtube.misc.settings.SettingsPatch
1717
import app.revanced.patches.youtube.video.information.VideoInformationPatch
1818
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
19-
import app.revanced.util.exception
19+
import app.revanced.util.*
20+
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
2021
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
2122
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
2223

@@ -28,8 +29,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
2829
PlayerResponseMethodHookPatch::class,
2930
VideoInformationPatch::class,
3031
SpoofSignatureResourcePatch::class,
31-
AddResourcesPatch::class
32-
]
32+
AddResourcesPatch::class,
33+
],
3334
)
3435
object SpoofSignaturePatch : BytecodePatch(
3536
setOf(
@@ -41,7 +42,9 @@ object SpoofSignaturePatch : BytecodePatch(
4142
StoryboardRendererDecoderRecommendedLevelFingerprint,
4243
StoryboardThumbnailParentFingerprint,
4344
ScrubbedPreviewLayoutFingerprint,
44-
)
45+
StatsQueryParameterFingerprint,
46+
ParamsMapPutFingerprint,
47+
),
4548
) {
4649
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
4750
"Lapp/revanced/integrations/youtube/patches/spoof/SpoofSignaturePatch;"
@@ -55,14 +58,14 @@ object SpoofSignaturePatch : BytecodePatch(
5558
preferences = setOf(
5659
SwitchPreference("revanced_spoof_signature_verification_enabled"),
5760
SwitchPreference("revanced_spoof_signature_in_feed_enabled"),
58-
SwitchPreference("revanced_spoof_storyboard")
61+
SwitchPreference("revanced_spoof_storyboard"),
5962
),
60-
)
63+
),
6164
)
6265

6366
// Hook the player parameters.
6467
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameter(
65-
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Z)Ljava/lang/String;"
68+
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Z)Ljava/lang/String;",
6669
)
6770

6871
// Force the seekbar time and chapters to always show up.
@@ -72,7 +75,7 @@ object SpoofSignaturePatch : BytecodePatch(
7275
StoryboardThumbnailFingerprint.also {
7376
it.resolve(
7477
context,
75-
classDef
78+
classDef,
7679
)
7780
}.result?.let {
7881
val endIndex = it.scanResult.patternScanResult!!.endIndex
@@ -84,15 +87,15 @@ object SpoofSignaturePatch : BytecodePatch(
8487
endIndex,
8588
"""
8689
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->getSeekbarThumbnailOverrideValue()Z
87-
"""
90+
""",
8891
)
8992
// Since this is end of the method must replace one line then add the rest.
9093
it.mutableMethod.addInstructions(
9194
endIndex + 1,
9295
"""
9396
move-result v0
9497
return v0
95-
"""
98+
""",
9699
)
97100
} ?: throw StoryboardThumbnailFingerprint.exception
98101
}
@@ -107,7 +110,7 @@ object SpoofSignaturePatch : BytecodePatch(
107110
"""
108111
iget-object v0, p0, $imageViewFieldName # copy imageview field to a register
109112
invoke-static {v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->seekbarImageViewCreated(Landroid/widget/ImageView;)V
110-
"""
113+
""",
111114
)
112115
}
113116
} ?: throw ScrubbedPreviewLayoutFingerprint.exception
@@ -117,7 +120,7 @@ object SpoofSignaturePatch : BytecodePatch(
117120
*/
118121
arrayOf(
119122
PlayerResponseModelImplGeneralFingerprint,
120-
PlayerResponseModelImplLiveStreamFingerprint
123+
PlayerResponseModelImplLiveStreamFingerprint,
121124
).forEach { fingerprint ->
122125
fingerprint.result?.let {
123126
it.mutableMethod.apply {
@@ -130,7 +133,7 @@ object SpoofSignaturePatch : BytecodePatch(
130133
"""
131134
invoke-static { v$getStoryBoardRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String;
132135
move-result-object v$getStoryBoardRegister
133-
"""
136+
""",
134137
)
135138
}
136139
} ?: throw fingerprint.exception
@@ -143,10 +146,11 @@ object SpoofSignaturePatch : BytecodePatch(
143146
.getInstruction<OneRegisterInstruction>(moveOriginalRecommendedValueIndex).registerA
144147

145148
it.mutableMethod.addInstructions(
146-
moveOriginalRecommendedValueIndex + 1, """
149+
moveOriginalRecommendedValueIndex + 1,
150+
"""
147151
invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I
148152
move-result v$originalValueRegister
149-
"""
153+
""",
150154
)
151155
} ?: throw StoryboardRendererDecoderRecommendedLevelFingerprint.exception
152156

@@ -158,10 +162,11 @@ object SpoofSignaturePatch : BytecodePatch(
158162
getInstruction<OneRegisterInstruction>(moveOriginalRecommendedValueIndex).registerA
159163

160164
addInstructions(
161-
moveOriginalRecommendedValueIndex, """
165+
moveOriginalRecommendedValueIndex,
166+
"""
162167
invoke-static { v$originalValueRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getRecommendedLevel(I)I
163168
move-result v$originalValueRegister
164-
"""
169+
""",
165170
)
166171
}
167172
} ?: throw PlayerResponseModelImplRecommendedLevelFingerprint.exception
@@ -177,7 +182,7 @@ object SpoofSignaturePatch : BytecodePatch(
177182
invoke-static { p$storyBoardUrlParams }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardRendererSpec(Ljava/lang/String;)Ljava/lang/String;
178183
move-result-object p$storyBoardUrlParams
179184
""",
180-
ExternalLabel("ignore", getInstruction(0))
185+
ExternalLabel("ignore", getInstruction(0)),
181186
)
182187
}
183188
} ?: throw StoryboardRendererSpecFingerprint.exception
@@ -189,11 +194,43 @@ object SpoofSignaturePatch : BytecodePatch(
189194
it.mutableMethod.getInstruction<OneRegisterInstruction>(storyBoardUrlIndex).registerA
190195

191196
it.mutableMethod.addInstructions(
192-
storyBoardUrlIndex + 1, """
197+
storyBoardUrlIndex + 1,
198+
"""
193199
invoke-static { v$storyboardUrlRegister }, $INTEGRATIONS_CLASS_DESCRIPTOR->getStoryboardDecoderRendererSpec(Ljava/lang/String;)Ljava/lang/String;
194200
move-result-object v$storyboardUrlRegister
195-
"""
201+
""",
196202
)
197203
} ?: throw StoryboardRendererDecoderSpecFingerprint.exception
204+
205+
// Fix stats not being tracked.
206+
// Due to signature spoofing "adformat" is present in query parameters made for /stats requests,
207+
// even though, for regular videos, it should not be.
208+
// This breaks stats tracking.
209+
// Replace the ad parameter with the video parameter in the query parameters.
210+
StatsQueryParameterFingerprint.result?.let {
211+
val putMethod = ParamsMapPutFingerprint.result?.method?.toString()
212+
?: throw ParamsMapPutFingerprint.exception
213+
214+
it.mutableMethod.apply {
215+
val adParamIndex = it.scanResult.stringsScanResult!!.matches.first().index
216+
val videoParamIndex = adParamIndex + 3
217+
218+
// Replace the ad parameter with the video parameter.
219+
replaceInstruction(adParamIndex, getInstruction(videoParamIndex))
220+
221+
// Call paramsMap.put instead of paramsMap.putIfNotExist
222+
// because the key is already present in the map.
223+
val putAdParamIndex = adParamIndex + 1
224+
val putIfKeyNotExistsInstruction = getInstruction<FiveRegisterInstruction>(putAdParamIndex)
225+
replaceInstruction(
226+
putAdParamIndex,
227+
"invoke-virtual { " +
228+
"v${putIfKeyNotExistsInstruction.registerC}, " +
229+
"v${putIfKeyNotExistsInstruction.registerD}, " +
230+
"v${putIfKeyNotExistsInstruction.registerE} }, " +
231+
putMethod,
232+
)
233+
}
234+
} ?: throw StatsQueryParameterFingerprint.exception
198235
}
199236
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
2+
3+
import app.revanced.patcher.extensions.or
4+
import app.revanced.patcher.fingerprint.MethodFingerprint
5+
import com.android.tools.smali.dexlib2.AccessFlags
6+
import com.android.tools.smali.dexlib2.Opcode
7+
8+
internal object ParamsMapPutFingerprint : MethodFingerprint(
9+
returnType = "V",
10+
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
11+
parameters = listOf(
12+
"Ljava/lang/String;",
13+
"Ljava/lang/String;",
14+
),
15+
opcodes = listOf(
16+
Opcode.CONST_4,
17+
Opcode.CONST_4,
18+
Opcode.CONST_4,
19+
Opcode.MOVE_OBJECT,
20+
Opcode.MOVE_OBJECT,
21+
Opcode.MOVE_OBJECT,
22+
Opcode.INVOKE_DIRECT_RANGE,
23+
),
24+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package app.revanced.patches.youtube.misc.fix.playback.fingerprints
2+
3+
import app.revanced.patcher.fingerprint.MethodFingerprint
4+
5+
internal object StatsQueryParameterFingerprint : MethodFingerprint(
6+
strings = listOf("adunit"),
7+
)

0 commit comments

Comments
 (0)