@@ -16,7 +16,8 @@ import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
1616import app.revanced.patches.youtube.misc.settings.SettingsPatch
1717import app.revanced.patches.youtube.video.information.VideoInformationPatch
1818import 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
2021import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
2122import 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)
3435object 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}
0 commit comments