From a64f33e3859de3a36ce745241f6fd77239c84486 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 18 May 2023 02:57:50 +0200 Subject: [PATCH] refactor(youtube/settings): increase fingerprint robustness --- ...mFingerprint.kt => SetThemeFingerprint.kt} | 14 ++-- .../fingerprints/ThemeSetterAppFingerprint.kt | 25 ------ .../settings/bytecode/patch/SettingsPatch.kt | 77 +++++++------------ .../resource/patch/SettingsResourcePatch.kt | 23 ++---- 4 files changed, 46 insertions(+), 93 deletions(-) rename src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/{ThemeSetterSystemFingerprint.kt => SetThemeFingerprint.kt} (52%) delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/ThemeSetterAppFingerprint.kt diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/ThemeSetterSystemFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/SetThemeFingerprint.kt similarity index 52% rename from src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/ThemeSetterSystemFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/SetThemeFingerprint.kt index b54d1e8a6..468e8e244 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/ThemeSetterSystemFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/SetThemeFingerprint.kt @@ -5,12 +5,16 @@ import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourc import org.jf.dexlib2.Opcode import org.jf.dexlib2.iface.instruction.WideLiteralInstruction -object ThemeSetterSystemFingerprint : MethodFingerprint( - "L", +object SetThemeFingerprint : MethodFingerprint( + returnType = "L", opcodes = listOf(Opcode.RETURN_OBJECT), customFingerprint = { methodDef, _ -> - methodDef.implementation?.instructions?.any { - it.opcode.ordinal == Opcode.CONST.ordinal && (it as WideLiteralInstruction).wideLiteral == SettingsResourcePatch.appearanceStringId - } == true + methodDef.implementation?.instructions?.any { instruction -> + if (instruction.opcode != Opcode.CONST) return@any false + + val wideLiteral = (instruction as WideLiteralInstruction).wideLiteral + + SettingsResourcePatch.appearanceStringId == wideLiteral + } ?: false } ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/ThemeSetterAppFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/ThemeSetterAppFingerprint.kt deleted file mode 100644 index 1fceecd38..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/fingerprints/ThemeSetterAppFingerprint.kt +++ /dev/null @@ -1,25 +0,0 @@ -package app.revanced.patches.youtube.misc.settings.bytecode.fingerprints - -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint -import org.jf.dexlib2.AccessFlags -import org.jf.dexlib2.Opcode - -object ThemeSetterAppFingerprint : MethodFingerprint( - "L", - AccessFlags.PUBLIC or AccessFlags.STATIC, - parameters = listOf("L", "L", "L", "L"), - opcodes = listOf( - Opcode.CONST, // target reference - Opcode.GOTO, - Opcode.CONST, // target reference - Opcode.INVOKE_DIRECT, - Opcode.RETURN_OBJECT, - Opcode.NEW_INSTANCE, - null, // changed from invoke interface to invoke virtual - Opcode.MOVE_RESULT_OBJECT, - Opcode.SGET_OBJECT, - Opcode.IF_NE, - Opcode.CONST, // target reference - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/patch/SettingsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/patch/SettingsPatch.kt index dd694ab64..c46053a08 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/patch/SettingsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/settings/bytecode/patch/SettingsPatch.kt @@ -7,6 +7,8 @@ import app.revanced.patcher.annotation.Version import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.instruction +import app.revanced.patcher.extensions.replaceInstruction import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResultSuccess @@ -15,69 +17,48 @@ import app.revanced.patches.shared.settings.preference.impl.Preference import app.revanced.patches.shared.settings.util.AbstractPreferenceScreen import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.LicenseActivityFingerprint -import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSetterAppFingerprint -import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSetterSystemFingerprint +import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.SetThemeFingerprint import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch +import org.jf.dexlib2.Opcode +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction import org.jf.dexlib2.util.MethodUtil -@DependsOn( - [ - IntegrationsPatch::class, - SettingsResourcePatch::class, - ] -) +@DependsOn([IntegrationsPatch::class, SettingsResourcePatch::class, ]) @Name("settings") @Description("Adds settings for ReVanced to YouTube.") @Version("0.0.1") class SettingsPatch : BytecodePatch( - listOf(LicenseActivityFingerprint, ThemeSetterSystemFingerprint, ThemeSetterAppFingerprint) + listOf(LicenseActivityFingerprint, SetThemeFingerprint) ) { override fun execute(context: BytecodeContext): PatchResult { - fun buildInvokeInstructionsString( + // TODO: Remove this when it is only required at one place. + fun getSetThemeInstructionString( registers: String = "v0", classDescriptor: String = THEME_HELPER_DESCRIPTOR, methodName: String = SET_THEME_METHOD_NAME, parameters: String = "Ljava/lang/Object;" - ) = "invoke-static {$registers}, $classDescriptor->$methodName($parameters)V" + ) = "invoke-static { $registers }, $classDescriptor->$methodName($parameters)V" - // apply the current theme of the settings page - ThemeSetterSystemFingerprint.result!!.let { result -> - val call = buildInvokeInstructionsString() - result.mutableMethod.apply { - // TODO: The label is pointing to the instruction below, but should instead point to the new instruction. - addInstruction( - result.scanResult.patternScanResult!!.startIndex, call - ) - addInstructions( - implementation!!.instructions.size - 1, call - ) - } - } + SetThemeFingerprint.result?.mutableMethod?.let { setThemeMethod -> + setThemeMethod.implementation!!.instructions.mapIndexedNotNull { i, instruction -> + if (instruction.opcode == Opcode.RETURN_OBJECT) i else null + } + .asReversed() // Prevent index shifting. + .forEach { returnIndex -> + // The following strategy is to replace the return instruction with the setTheme instruction, + // then add a return instruction after the setTheme instruction. + // This is done because the return instruction is a target of another instruction. - // set the theme based on the preference of the app - ThemeSetterAppFingerprint.result?.apply { - fun buildInstructionsString(theme: Int) = """ - const/4 v0, 0x$theme - ${buildInvokeInstructionsString(parameters = "I")} - """ + setThemeMethod.apply { + // This register is returned by the setTheme method. + val register = instruction(returnIndex).registerA - val patternScanResult = scanResult.patternScanResult!! - - mutableMethod.apply { - addInstructions( - patternScanResult.endIndex + 1, buildInstructionsString(1) - ) - addInstructions( - patternScanResult.endIndex - 7, buildInstructionsString(0) - ) - addInstructions( - patternScanResult.endIndex - 9, buildInstructionsString(1) - ) - addInstructions( - implementation!!.instructions.size - 2, buildInstructionsString(0) - ) - } - } ?: return ThemeSetterAppFingerprint.toErrorResult() + val setThemeInstruction = getSetThemeInstructionString("v$register") + replaceInstruction(returnIndex, setThemeInstruction) + addInstruction(returnIndex + 1, "return-object v0") + } + } + } ?: return SetThemeFingerprint.toErrorResult() // set the theme based on the preference of the device LicenseActivityFingerprint.result!!.apply licenseActivity@{ @@ -87,7 +68,7 @@ class SettingsPatch : BytecodePatch( classDescriptor: String = SETTINGS_ACTIVITY_DESCRIPTOR, methodName: String = "initializeSettings", parameters: String = this@licenseActivity.mutableClass.type - ) = buildInvokeInstructionsString(registers, classDescriptor, methodName, parameters) + ) = getSetThemeInstructionString(registers, classDescriptor, methodName, parameters) // initialize the settings addInstructions( diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/settings/resource/patch/SettingsResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/settings/resource/patch/SettingsResourcePatch.kt index dbf4ccfcc..7ed6f2ee0 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/settings/resource/patch/SettingsResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/settings/resource/patch/SettingsResourcePatch.kt @@ -29,21 +29,17 @@ class SettingsResourcePatch : AbstractSettingsResourcePatch( override fun execute(context: ResourceContext): PatchResult { super.execute(context) - /* - * used by a fingerprint of SettingsPatch - */ + // Used for a fingerprint from SettingsPatch. appearanceStringId = ResourceMappingPatch.resourceMappings.find { it.type == "string" && it.name == "app_theme_appearance_dark" }!!.id - /* - * create missing directory for the resources - */ + + // Create missing directory for the resources. context["res/drawable-ldrtl-xxxhdpi"].mkdirs() - /* - * copy layout resources - */ + + // Copy layout resources. arrayOf( ResourceUtils.ResourceGroup( "layout", @@ -63,7 +59,7 @@ class SettingsResourcePatch : AbstractSettingsResourcePatch( preferencesEditor = context.xmlEditor["res/xml/settings_fragment.xml"] - // Add the ReVanced settings to the YouTube settings + // Add the ReVanced settings to the YouTube settings. val youtubePackage = "com.google.android.youtube" SettingsPatch.addPreference( Preference( @@ -92,11 +88,8 @@ class SettingsResourcePatch : AbstractSettingsResourcePatch( internal companion object { - // Used by a fingerprint of SettingsPatch - // this field is located in the SettingsResourcePatch - // because if it were to be defined in the SettingsPatch companion object, - // the companion object could be initialized before ResourceMappingResourcePatch has executed. - internal var appearanceStringId: Long = -1 + // Used for a fingerprint from SettingsPatch. + internal var appearanceStringId = -1L // if this is not null, all intents will be renamed to this var overrideIntentsTargetPackage: String? = null