refactor(youtube/settings): increase fingerprint robustness

This commit is contained in:
oSumAtrIX 2023-05-18 02:57:50 +02:00
parent 758b300591
commit a64f33e385
No known key found for this signature in database
GPG key ID: A9B3094ACDB604B4
4 changed files with 46 additions and 93 deletions

View file

@ -5,12 +5,16 @@ import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourc
import org.jf.dexlib2.Opcode import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
object ThemeSetterSystemFingerprint : MethodFingerprint( object SetThemeFingerprint : MethodFingerprint(
"L", returnType = "L",
opcodes = listOf(Opcode.RETURN_OBJECT), opcodes = listOf(Opcode.RETURN_OBJECT),
customFingerprint = { methodDef, _ -> customFingerprint = { methodDef, _ ->
methodDef.implementation?.instructions?.any { methodDef.implementation?.instructions?.any { instruction ->
it.opcode.ordinal == Opcode.CONST.ordinal && (it as WideLiteralInstruction).wideLiteral == SettingsResourcePatch.appearanceStringId if (instruction.opcode != Opcode.CONST) return@any false
} == true
val wideLiteral = (instruction as WideLiteralInstruction).wideLiteral
SettingsResourcePatch.appearanceStringId == wideLiteral
} ?: false
} }
) )

View file

@ -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
)
)

View file

@ -7,6 +7,8 @@ import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.addInstructions 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.BytecodePatch
import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess 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.shared.settings.util.AbstractPreferenceScreen
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch 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.LicenseActivityFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSetterAppFingerprint import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.SetThemeFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ThemeSetterSystemFingerprint
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch 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 import org.jf.dexlib2.util.MethodUtil
@DependsOn( @DependsOn([IntegrationsPatch::class, SettingsResourcePatch::class, ])
[
IntegrationsPatch::class,
SettingsResourcePatch::class,
]
)
@Name("settings") @Name("settings")
@Description("Adds settings for ReVanced to YouTube.") @Description("Adds settings for ReVanced to YouTube.")
@Version("0.0.1") @Version("0.0.1")
class SettingsPatch : BytecodePatch( class SettingsPatch : BytecodePatch(
listOf(LicenseActivityFingerprint, ThemeSetterSystemFingerprint, ThemeSetterAppFingerprint) listOf(LicenseActivityFingerprint, SetThemeFingerprint)
) { ) {
override fun execute(context: BytecodeContext): PatchResult { override fun execute(context: BytecodeContext): PatchResult {
fun buildInvokeInstructionsString( // TODO: Remove this when it is only required at one place.
fun getSetThemeInstructionString(
registers: String = "v0", registers: String = "v0",
classDescriptor: String = THEME_HELPER_DESCRIPTOR, classDescriptor: String = THEME_HELPER_DESCRIPTOR,
methodName: String = SET_THEME_METHOD_NAME, methodName: String = SET_THEME_METHOD_NAME,
parameters: String = "Ljava/lang/Object;" 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 SetThemeFingerprint.result?.mutableMethod?.let { setThemeMethod ->
ThemeSetterSystemFingerprint.result!!.let { result -> setThemeMethod.implementation!!.instructions.mapIndexedNotNull { i, instruction ->
val call = buildInvokeInstructionsString() if (instruction.opcode == Opcode.RETURN_OBJECT) i else null
result.mutableMethod.apply { }
// TODO: The label is pointing to the instruction below, but should instead point to the new instruction. .asReversed() // Prevent index shifting.
addInstruction( .forEach { returnIndex ->
result.scanResult.patternScanResult!!.startIndex, call // The following strategy is to replace the return instruction with the setTheme instruction,
) // then add a return instruction after the setTheme instruction.
addInstructions( // This is done because the return instruction is a target of another instruction.
implementation!!.instructions.size - 1, call
) 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")
} }
} }
} ?: return SetThemeFingerprint.toErrorResult()
// 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()
// set the theme based on the preference of the device // set the theme based on the preference of the device
LicenseActivityFingerprint.result!!.apply licenseActivity@{ LicenseActivityFingerprint.result!!.apply licenseActivity@{
@ -87,7 +68,7 @@ class SettingsPatch : BytecodePatch(
classDescriptor: String = SETTINGS_ACTIVITY_DESCRIPTOR, classDescriptor: String = SETTINGS_ACTIVITY_DESCRIPTOR,
methodName: String = "initializeSettings", methodName: String = "initializeSettings",
parameters: String = this@licenseActivity.mutableClass.type parameters: String = this@licenseActivity.mutableClass.type
) = buildInvokeInstructionsString(registers, classDescriptor, methodName, parameters) ) = getSetThemeInstructionString(registers, classDescriptor, methodName, parameters)
// initialize the settings // initialize the settings
addInstructions( addInstructions(

View file

@ -29,21 +29,17 @@ class SettingsResourcePatch : AbstractSettingsResourcePatch(
override fun execute(context: ResourceContext): PatchResult { override fun execute(context: ResourceContext): PatchResult {
super.execute(context) super.execute(context)
/* // Used for a fingerprint from SettingsPatch.
* used by a fingerprint of SettingsPatch
*/
appearanceStringId = ResourceMappingPatch.resourceMappings.find { appearanceStringId = ResourceMappingPatch.resourceMappings.find {
it.type == "string" && it.name == "app_theme_appearance_dark" it.type == "string" && it.name == "app_theme_appearance_dark"
}!!.id }!!.id
/*
* create missing directory for the resources // Create missing directory for the resources.
*/
context["res/drawable-ldrtl-xxxhdpi"].mkdirs() context["res/drawable-ldrtl-xxxhdpi"].mkdirs()
/*
* copy layout resources // Copy layout resources.
*/
arrayOf( arrayOf(
ResourceUtils.ResourceGroup( ResourceUtils.ResourceGroup(
"layout", "layout",
@ -63,7 +59,7 @@ class SettingsResourcePatch : AbstractSettingsResourcePatch(
preferencesEditor = context.xmlEditor["res/xml/settings_fragment.xml"] 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" val youtubePackage = "com.google.android.youtube"
SettingsPatch.addPreference( SettingsPatch.addPreference(
Preference( Preference(
@ -92,11 +88,8 @@ class SettingsResourcePatch : AbstractSettingsResourcePatch(
internal companion object { internal companion object {
// Used by a fingerprint of SettingsPatch // Used for a fingerprint from SettingsPatch.
// this field is located in the SettingsResourcePatch internal var appearanceStringId = -1L
// 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
// if this is not null, all intents will be renamed to this // if this is not null, all intents will be renamed to this
var overrideIntentsTargetPackage: String? = null var overrideIntentsTargetPackage: String? = null