From dd2f636a4afbc3992919a90a8fbd0358ca0c9775 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Wed, 17 Jan 2024 19:38:10 +0900 Subject: [PATCH] refactor(YouTube/Return YouTube Dislike): make patch more robust by removing opcode patterns from fingerprints --- .../general/ReturnYouTubeDislikePatch.kt | 100 ++++-------------- ...TextComponentAtomicReferenceFingerprint.kt | 19 ---- ...mponentAtomicReferenceLegacyFingerprint.kt | 18 ---- .../TextComponentTmpFingerprint.kt | 16 --- .../ReturnYouTubeDislikeRollingNumberPatch.kt | 66 ++++++------ ...ingNumberMeasureAnimatedTextFingerprint.kt | 31 ++++-- 6 files changed, 74 insertions(+), 176 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentAtomicReferenceFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentAtomicReferenceLegacyFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentTmpFingerprint.kt diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt index f48d3a96a..a1692390f 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/ReturnYouTubeDislikePatch.kt @@ -3,8 +3,6 @@ package app.revanced.patches.youtube.utils.returnyoutubedislike.general import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.annotation.CompatiblePackage @@ -16,11 +14,8 @@ import app.revanced.patches.youtube.utils.playerresponse.PlayerResponsePatch import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.DislikeFingerprint import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.LikeFingerprint import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.RemoveLikeFingerprint -import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.TextComponentAtomicReferenceFingerprint -import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.TextComponentAtomicReferenceLegacyFingerprint import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.TextComponentConstructorFingerprint import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.TextComponentContextFingerprint -import app.revanced.patches.youtube.utils.returnyoutubedislike.general.fingerprints.TextComponentTmpFingerprint import app.revanced.patches.youtube.utils.returnyoutubedislike.oldlayout.ReturnYouTubeDislikeOldLayoutPatch import app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.ReturnYouTubeDislikeRollingNumberPatch import app.revanced.patches.youtube.utils.returnyoutubedislike.shorts.ReturnYouTubeDislikeShortsPatch @@ -28,10 +23,8 @@ import app.revanced.patches.youtube.utils.settings.SettingsPatch import app.revanced.patches.youtube.utils.videoid.general.VideoIdPatch import app.revanced.util.exception import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction -import com.android.tools.smali.dexlib2.iface.reference.Reference @Patch( name = "Return YouTube Dislike", @@ -101,88 +94,34 @@ object ReturnYouTubeDislikePatch : BytecodePatch( TextComponentConstructorFingerprint.result?.let { parentResult -> // Resolves fingerprints - val parentClassDef = parentResult.classDef - TextComponentContextFingerprint.resolve(context, parentClassDef) - TextComponentTmpFingerprint.resolve(context, parentClassDef) - TextComponentAtomicReferenceFingerprint.resolve(context, parentClassDef) - TextComponentAtomicReferenceLegacyFingerprint.resolve(context, parentClassDef) + TextComponentContextFingerprint.resolve(context, parentResult.classDef) TextComponentContextFingerprint.result?.let { it.mutableMethod.apply { - val booleanIndex = it.scanResult.patternScanResult!!.endIndex + val conversionContextFieldIndex = implementation!!.instructions.indexOfFirst { instruction -> + instruction.opcode == Opcode.IGET_OBJECT + && (instruction as ReferenceInstruction).reference.toString().endsWith("Ljava/util/Map;") + } - 1 + val conversionContextFieldReference = + getInstruction(conversionContextFieldIndex).reference - for (index in booleanIndex downTo 0) { - if (getInstruction(index).opcode != Opcode.IGET_OBJECT) continue - - val targetReference = - getInstruction(index).reference.toString() - - if (targetReference.endsWith("Ljava/util/Map;")) { - conversionContextFieldReference = - getInstruction(index - 1).reference - - break - } - } - } - } ?: throw TextComponentContextFingerprint.exception - - TextComponentTmpFingerprint.result?.let { - it.mutableMethod.apply { - val startIndex = it.scanResult.patternScanResult!!.startIndex - tmpRegister = - getInstruction(startIndex).registerE - } - } ?: throw TextComponentTmpFingerprint.exception - - - val textComponentAtomicReferenceResult = - TextComponentAtomicReferenceFingerprint.result - ?: TextComponentAtomicReferenceLegacyFingerprint.result - ?: throw TextComponentAtomicReferenceLegacyFingerprint.exception - - TextComponentAtomicReferenceFingerprint.result?.let { - it.mutableMethod.apply { - val startIndex = it.scanResult.patternScanResult!!.startIndex - val originalRegisterA = - getInstruction(startIndex + 2).registerA - - replaceInstruction( - startIndex + 2, - "move-object v$originalRegisterA, v$tmpRegister" - ) - replaceInstruction( - startIndex + 1, - "move-result-object v$tmpRegister" - ) - } - } - - textComponentAtomicReferenceResult.let { - it.mutableMethod.apply { - val atomicReferenceStartIndex = it.scanResult.patternScanResult!!.startIndex - val insertIndex = it.scanResult.patternScanResult!!.endIndex - val moveCharSequenceInstruction = - getInstruction(insertIndex) - - val atomicReferenceRegister = - getInstruction(atomicReferenceStartIndex).registerC - - val charSequenceRegister = - moveCharSequenceInstruction.registerB + val charSequenceIndex = implementation!!.instructions.indexOfFirst { instruction -> + instruction.opcode == Opcode.IGET_OBJECT + && (instruction as ReferenceInstruction).reference.toString().endsWith("Ljava/util/BitSet;") + } - 1 + val charSequenceRegister = getInstruction(charSequenceIndex).registerA + val freeRegister = getInstruction(charSequenceIndex).registerB addInstructions( - insertIndex + 1, """ - move-object/from16 v$tmpRegister, p0 - iget-object v$tmpRegister, v$tmpRegister, $conversionContextFieldReference - invoke-static {v$tmpRegister, v$atomicReferenceRegister, v$charSequenceRegister}, $INTEGRATIONS_RYD_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/util/concurrent/atomic/AtomicReference;Ljava/lang/CharSequence;)Ljava/lang/CharSequence; + charSequenceIndex - 1, """ + move-object/from16 v$freeRegister, p0 + iget-object v$freeRegister, v$freeRegister, $conversionContextFieldReference + invoke-static {v$freeRegister, v$charSequenceRegister}, $INTEGRATIONS_RYD_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence; move-result-object v$charSequenceRegister - move-object v${moveCharSequenceInstruction.registerA}, v${charSequenceRegister} """ ) - removeInstruction(insertIndex) } - } + } ?: throw TextComponentContextFingerprint.exception } ?: throw TextComponentConstructorFingerprint.exception VideoIdPatch.injectCall("$INTEGRATIONS_RYD_CLASS_DESCRIPTOR->newVideoLoaded(Ljava/lang/String;)V") @@ -208,9 +147,6 @@ object ReturnYouTubeDislikePatch : BytecodePatch( private const val FILTER_CLASS_DESCRIPTOR = "$COMPONENTS_PATH/ReturnYouTubeDislikeFilterPatch;" - private lateinit var conversionContextFieldReference: Reference - private var tmpRegister: Int = 12 - private fun MethodFingerprint.toPatch(voteKind: Vote) = VotePatch(this, voteKind) private data class VotePatch(val fingerprint: MethodFingerprint, val voteKind: Vote) diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentAtomicReferenceFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentAtomicReferenceFingerprint.kt deleted file mode 100644 index 8faa2f200..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentAtomicReferenceFingerprint.kt +++ /dev/null @@ -1,19 +0,0 @@ -package app.revanced.patches.youtube.utils.returnyoutubedislike.general.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 - -object TextComponentAtomicReferenceFingerprint : MethodFingerprint( - returnType = "L", - accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL, - parameters = listOf("L"), - opcodes = listOf( - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.MOVE_OBJECT, - Opcode.CHECK_CAST, - Opcode.MOVE_OBJECT - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentAtomicReferenceLegacyFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentAtomicReferenceLegacyFingerprint.kt deleted file mode 100644 index 47ab88bfe..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentAtomicReferenceLegacyFingerprint.kt +++ /dev/null @@ -1,18 +0,0 @@ -package app.revanced.patches.youtube.utils.returnyoutubedislike.general.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 - -object TextComponentAtomicReferenceLegacyFingerprint : MethodFingerprint( - returnType = "L", - accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL, - parameters = listOf("L"), - opcodes = listOf( - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.CHECK_CAST, - Opcode.MOVE_OBJECT - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentTmpFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentTmpFingerprint.kt deleted file mode 100644 index 198e325fe..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/general/fingerprints/TextComponentTmpFingerprint.kt +++ /dev/null @@ -1,16 +0,0 @@ -package app.revanced.patches.youtube.utils.returnyoutubedislike.general.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 - -object TextComponentTmpFingerprint : MethodFingerprint( - returnType = "L", - accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL, - parameters = listOf("L"), - opcodes = listOf( - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, // last instruction of the method - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/ReturnYouTubeDislikeRollingNumberPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/ReturnYouTubeDislikeRollingNumberPatch.kt index f654e6e84..d39082fcc 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/ReturnYouTubeDislikeRollingNumberPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/ReturnYouTubeDislikeRollingNumberPatch.kt @@ -2,11 +2,13 @@ package app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.youtube.utils.fingerprints.RollingNumberTextViewAnimationUpdateFingerprint import app.revanced.patches.youtube.utils.integrations.Constants.UTILS_PATH import app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.fingerprints.RollingNumberMeasureAnimatedTextFingerprint @@ -31,6 +33,7 @@ object ReturnYouTubeDislikeRollingNumberPatch : BytecodePatch( RollingNumberSetterFingerprint, RollingNumberMeasureTextParentFingerprint, RollingNumberTextViewFingerprint, + RollingNumberMeasureAnimatedTextFingerprint, RollingNumberTextViewAnimationUpdateFingerprint ) ) { @@ -88,41 +91,42 @@ object ReturnYouTubeDislikeRollingNumberPatch : BytecodePatch( } } ?: throw RollingNumberSetterFingerprint.exception - RollingNumberMeasureTextParentFingerprint.result?.let { parentResult -> - // Rolling Number text views use the measured width of the raw string for layout. - // Modify the measure text calculation to include the left drawable separator if needed. - RollingNumberMeasureAnimatedTextFingerprint.also { - it.resolve( - context, - parentResult.classDef - ) - }.result?.let { - it.mutableMethod.apply { - val returnInstructionIndex = it.scanResult.patternScanResult!!.endIndex - val measuredTextWidthRegister = - getInstruction(returnInstructionIndex).registerA + // Rolling Number text views use the measured width of the raw string for layout. + // Modify the measure text calculation to include the left drawable separator if needed. + RollingNumberMeasureAnimatedTextFingerprint.result?.let { + it.mutableMethod.apply { + val endIndex = it.scanResult.patternScanResult!!.endIndex + val measuredTextWidthIndex = endIndex - 2 + val measuredTextWidthRegister = + getInstruction(measuredTextWidthIndex).registerA - replaceInstruction( // Replace instruction to preserve control flow label. - returnInstructionIndex, - "invoke-static {p1, v$measuredTextWidthRegister}, $INTEGRATIONS_RYD_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F" - ) - addInstructions( - returnInstructionIndex + 1, """ - move-result v$measuredTextWidthRegister - return v$measuredTextWidthRegister - """ - ) + addInstructions( + endIndex + 1, """ + invoke-static {p1, v$measuredTextWidthRegister}, $INTEGRATIONS_RYD_CLASS_DESCRIPTOR->onRollingNumberMeasured(Ljava/lang/String;F)F + move-result v$measuredTextWidthRegister + """ + ) + + val ifGeIndex = implementation!!.instructions.indexOfFirst { instruction -> + instruction.opcode == Opcode.IF_GE } - } ?: throw RollingNumberMeasureAnimatedTextFingerprint.exception + val ifGeInstruction = getInstruction(ifGeIndex) + + removeInstruction(ifGeIndex) + addInstructionsWithLabels( + ifGeIndex, """ + if-ge v${ifGeInstruction.registerA}, v${ifGeInstruction.registerB}, :jump + """, ExternalLabel("jump", getInstruction(endIndex)) + ) + } + } ?: throw RollingNumberMeasureAnimatedTextFingerprint.exception + + RollingNumberMeasureTextParentFingerprint.result?.classDef?.let { parentClassDef -> + RollingNumberMeasureStaticLabelFingerprint.resolve(context, parentClassDef) // Additional text measurement method. Used if YouTube decides not to animate the likes count // and sometimes used for initial video load. - RollingNumberMeasureStaticLabelFingerprint.also { - it.resolve( - context, - parentResult.classDef - ) - }.result?.let { + RollingNumberMeasureStaticLabelFingerprint.result?.let { it.mutableMethod.apply { val measureTextIndex = it.scanResult.patternScanResult!!.startIndex + 1 val freeRegister = getInstruction(0).registerA diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureAnimatedTextFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureAnimatedTextFingerprint.kt index 36b0d2c17..8705d27d6 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureAnimatedTextFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/returnyoutubedislike/rollingnumber/fingerprints/RollingNumberMeasureAnimatedTextFingerprint.kt @@ -1,23 +1,34 @@ package app.revanced.patches.youtube.utils.returnyoutubedislike.rollingnumber.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 +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction /** - * Resolves to class found in [RollingNumberMeasureTextParentFingerprint]. + * Compatible with YouTube v18.30.xx to v18.49.xx */ object RollingNumberMeasureAnimatedTextFingerprint : MethodFingerprint( - returnType = "F", - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - parameters = listOf("Ljava/lang/String;"), opcodes = listOf( Opcode.INVOKE_VIRTUAL, Opcode.MOVE_RESULT, - Opcode.ADD_FLOAT_2ADDR, + Opcode.ADD_FLOAT_2ADDR, // measuredTextWidth Opcode.ADD_INT_LIT8, - Opcode.GOTO, - Opcode.RETURN - ) + Opcode.GOTO + ), + customFingerprint = custom@{ methodDef, _ -> + if (methodDef.implementation == null) + return@custom false + + for (instruction in methodDef.implementation!!.instructions) { + if (instruction.opcode != Opcode.INVOKE_VIRTUAL) + continue + + val invokeInstruction = instruction as ReferenceInstruction + if (!invokeInstruction.reference.toString().endsWith("Landroid/text/TextPaint;->measureText([CII)F")) + continue + + return@custom true + } + return@custom false + } ) \ No newline at end of file