refactor(youtube/settings): increase fingerprint robustness
This commit is contained in:
		
							parent
							
								
									758b300591
								
							
						
					
					
						commit
						a64f33e385
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
    }
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
    )
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
 | 
			
		||||
                    setThemeMethod.apply {
 | 
			
		||||
                        // This register is returned by the setTheme method.
 | 
			
		||||
                        val register = instruction<OneRegisterInstruction>(returnIndex).registerA
 | 
			
		||||
 | 
			
		||||
                        val setThemeInstruction = getSetThemeInstructionString("v$register")
 | 
			
		||||
                        replaceInstruction(returnIndex, setThemeInstruction)
 | 
			
		||||
                        addInstruction(returnIndex + 1, "return-object v0")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        // 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")}
 | 
			
		||||
                """
 | 
			
		||||
 | 
			
		||||
            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()
 | 
			
		||||
        } ?: 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(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue