From 76ee8a1cfbfe895d006d57afd0c56ed2373d96e6 Mon Sep 17 00:00:00 2001 From: crimera Date: Tue, 27 Feb 2024 13:51:31 +0800 Subject: [PATCH] feat(Twitter): Added mod settings --- .../downloads/ChangeDownloadFolderPatch.kt | 49 ++++++++ .../downloads}/DownloadPatch.kt | 6 +- .../fingerprints/DownloadPatchFingerprint.kt | 2 +- .../fingerprints/FIleDownloaderFingerprint.kt | 2 +- .../SetDownloadDestinationFingerprint.kt | 10 ++ .../patches/twitter/settings/SettingsPatch.kt | 105 ++++++++++++++++++ .../twitter/settings/SettingsResourcePatch.kt | 42 +++++++ .../fingerprints/InitActivityFingerprint.kt | 7 ++ .../fingerprints/SettingsFingerprint.kt | 13 +++ .../fingerprints/SettingsInitFingerprint.kt | 13 +++ 10 files changed, 244 insertions(+), 5 deletions(-) create mode 100644 src/main/kotlin/crimera/patches/twitter/interaction/downloads/ChangeDownloadFolderPatch.kt rename src/main/kotlin/crimera/patches/twitter/{download => interaction/downloads}/DownloadPatch.kt (90%) rename src/main/kotlin/crimera/patches/twitter/{download => interaction/downloads}/fingerprints/DownloadPatchFingerprint.kt (81%) rename src/main/kotlin/crimera/patches/twitter/{download => interaction/downloads}/fingerprints/FIleDownloaderFingerprint.kt (76%) create mode 100644 src/main/kotlin/crimera/patches/twitter/interaction/downloads/fingerprints/SetDownloadDestinationFingerprint.kt create mode 100644 src/main/kotlin/crimera/patches/twitter/settings/SettingsPatch.kt create mode 100644 src/main/kotlin/crimera/patches/twitter/settings/SettingsResourcePatch.kt create mode 100644 src/main/kotlin/crimera/patches/twitter/settings/fingerprints/InitActivityFingerprint.kt create mode 100644 src/main/kotlin/crimera/patches/twitter/settings/fingerprints/SettingsFingerprint.kt create mode 100644 src/main/kotlin/crimera/patches/twitter/settings/fingerprints/SettingsInitFingerprint.kt diff --git a/src/main/kotlin/crimera/patches/twitter/interaction/downloads/ChangeDownloadFolderPatch.kt b/src/main/kotlin/crimera/patches/twitter/interaction/downloads/ChangeDownloadFolderPatch.kt new file mode 100644 index 00000000..b84c7658 --- /dev/null +++ b/src/main/kotlin/crimera/patches/twitter/interaction/downloads/ChangeDownloadFolderPatch.kt @@ -0,0 +1,49 @@ +package crimera.patches.twitter.interaction.downloads + +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.getInstructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import crimera.patches.twitter.interaction.downloads.fingerprints.SetDownloadDestinationFingerprint +import crimera.patches.twitter.settings.SettingsPatch.UTILS_DESCRIPTOR + +@Patch( + name = "Change download folder", + description = "Unlocks the ability to download videos from Twitter", + requiresIntegrations = true, + compatiblePackages = [CompatiblePackage("com.twitter.android")] +) +@Suppress("unused") +object ChangeDownloadFolderPatch : BytecodePatch( + setOf(SetDownloadDestinationFingerprint) +) { + private const val GETFOLDER_DESCRIPTOR = + "invoke-static {p1}, $UTILS_DESCRIPTOR;->getVideoFolder(Ljava/lang/String;)Ljava/lang/String;" + override fun execute(context: BytecodeContext) { + val result = SetDownloadDestinationFingerprint.result + ?: throw PatchException("Could not find fingerprint") + + val method = result.mutableMethod + + val insertAt = method.getInstructions() + .first { it.opcode == Opcode.INVOKE_VIRTUAL }.location.index + + // when replacing values we should avoid hardcoding the registers + val publicFolderRegister = method.getInstruction(insertAt-1).registerA + method.replaceInstruction(insertAt-1, """ + sget-object v$publicFolderRegister, Landroid/os/Environment;->DIRECTORY_MOVIES:Ljava/lang/String; + """.trimIndent()) + + method.addInstructions(insertAt, """ + $GETFOLDER_DESCRIPTOR + move-result-object p1 + """.trimIndent()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/crimera/patches/twitter/download/DownloadPatch.kt b/src/main/kotlin/crimera/patches/twitter/interaction/downloads/DownloadPatch.kt similarity index 90% rename from src/main/kotlin/crimera/patches/twitter/download/DownloadPatch.kt rename to src/main/kotlin/crimera/patches/twitter/interaction/downloads/DownloadPatch.kt index ceb97918..5069b89f 100644 --- a/src/main/kotlin/crimera/patches/twitter/download/DownloadPatch.kt +++ b/src/main/kotlin/crimera/patches/twitter/interaction/downloads/DownloadPatch.kt @@ -1,4 +1,4 @@ -package crimera.patches.twitter.download +package crimera.patches.twitter.interaction.downloads import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions @@ -11,8 +11,8 @@ import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.util.smali.ExternalLabel import com.android.tools.smali.dexlib2.Opcode -import crimera.patches.twitter.download.fingerprints.DownloadPatchFingerprint -import crimera.patches.twitter.download.fingerprints.FIleDownloaderFingerprint +import crimera.patches.twitter.interaction.downloads.fingerprints.DownloadPatchFingerprint +import crimera.patches.twitter.interaction.downloads.fingerprints.FIleDownloaderFingerprint // Credits to @iKirby @Patch( diff --git a/src/main/kotlin/crimera/patches/twitter/download/fingerprints/DownloadPatchFingerprint.kt b/src/main/kotlin/crimera/patches/twitter/interaction/downloads/fingerprints/DownloadPatchFingerprint.kt similarity index 81% rename from src/main/kotlin/crimera/patches/twitter/download/fingerprints/DownloadPatchFingerprint.kt rename to src/main/kotlin/crimera/patches/twitter/interaction/downloads/fingerprints/DownloadPatchFingerprint.kt index 7a05a24f..dda26c7d 100644 --- a/src/main/kotlin/crimera/patches/twitter/download/fingerprints/DownloadPatchFingerprint.kt +++ b/src/main/kotlin/crimera/patches/twitter/interaction/downloads/fingerprints/DownloadPatchFingerprint.kt @@ -1,4 +1,4 @@ -package crimera.patches.twitter.download.fingerprints +package crimera.patches.twitter.interaction.downloads.fingerprints import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/kotlin/crimera/patches/twitter/download/fingerprints/FIleDownloaderFingerprint.kt b/src/main/kotlin/crimera/patches/twitter/interaction/downloads/fingerprints/FIleDownloaderFingerprint.kt similarity index 76% rename from src/main/kotlin/crimera/patches/twitter/download/fingerprints/FIleDownloaderFingerprint.kt rename to src/main/kotlin/crimera/patches/twitter/interaction/downloads/fingerprints/FIleDownloaderFingerprint.kt index b3a825ad..ec54f1b6 100644 --- a/src/main/kotlin/crimera/patches/twitter/download/fingerprints/FIleDownloaderFingerprint.kt +++ b/src/main/kotlin/crimera/patches/twitter/interaction/downloads/fingerprints/FIleDownloaderFingerprint.kt @@ -1,4 +1,4 @@ -package crimera.patches.twitter.download.fingerprints +package crimera.patches.twitter.interaction.downloads.fingerprints import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/kotlin/crimera/patches/twitter/interaction/downloads/fingerprints/SetDownloadDestinationFingerprint.kt b/src/main/kotlin/crimera/patches/twitter/interaction/downloads/fingerprints/SetDownloadDestinationFingerprint.kt new file mode 100644 index 00000000..fa742173 --- /dev/null +++ b/src/main/kotlin/crimera/patches/twitter/interaction/downloads/fingerprints/SetDownloadDestinationFingerprint.kt @@ -0,0 +1,10 @@ +package crimera.patches.twitter.interaction.downloads.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +object SetDownloadDestinationFingerprint: MethodFingerprint( + returnType = "V", + strings = listOf( + "parse(downloadData.url)" + ) +) \ No newline at end of file diff --git a/src/main/kotlin/crimera/patches/twitter/settings/SettingsPatch.kt b/src/main/kotlin/crimera/patches/twitter/settings/SettingsPatch.kt new file mode 100644 index 00000000..0e5e40c9 --- /dev/null +++ b/src/main/kotlin/crimera/patches/twitter/settings/SettingsPatch.kt @@ -0,0 +1,105 @@ +package crimera.patches.twitter.settings + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +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.getInstructions +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.util.smali.ExternalLabel +import com.android.tools.smali.dexlib2.Opcode +import crimera.patches.twitter.settings.fingerprints.InitActivityFingerprint +import crimera.patches.twitter.settings.fingerprints.SettingsFingerprint +import crimera.patches.twitter.settings.fingerprints.SettingsInitFingerprint + +@Patch( + name = "Settings", + description = "Adds settings", + requiresIntegrations = true, + dependencies = [SettingsResourcePatch::class], + compatiblePackages = [CompatiblePackage("com.twitter.android")], +) +object SettingsPatch : BytecodePatch( + setOf(SettingsFingerprint, SettingsInitFingerprint, InitActivityFingerprint) +) { + private const val INTEGRATIONS_PACKAGE = "Lapp/revanced/integrations/twitter" + const val UTILS_DESCRIPTOR = "$INTEGRATIONS_PACKAGE/settings/Utils" + private const val START_ACTIVITY_DESCRIPTOR = + "invoke-static {}, $UTILS_DESCRIPTOR;->startActivity()V" + + private const val ADD_CONTEXT_DESCRIPTOR = "invoke-static {p0}, $UTILS_DESCRIPTOR;->setCtx(Landroid/app/Application;)V" + + override fun execute(context: BytecodeContext) { + val result = SettingsInitFingerprint.result + ?: throw PatchException("Fingerprint not found") + + val initMethod = result.mutableClass.methods.first() + + val arrayCreation = initMethod.getInstructions() + .first { it.opcode == Opcode.FILLED_NEW_ARRAY_RANGE }.location.index + + initMethod.addInstructions( + arrayCreation + 2, """ + array-length v1, v0 + + add-int/lit8 v1, v1, 0x1 + + invoke-static {v0, v1}, Ljava/util/Arrays;->copyOf([Ljava/lang/Object;I)[Ljava/lang/Object; + + move-result-object v1 + + check-cast v1, [Ljava/lang/String; + + .local v1, "bigger":[Ljava/lang/String; + array-length v2, v0 + + const-string v3, "pref_mod" + + aput-object v3, v1, v2 + + move-object v0, v1 + """.trimIndent() + ) + + val h0Method = result.mutableClass.methods.first { it.returnType == "Z" } + + val igetObjectIndex = h0Method.getInstructions() + .first { it.opcode == Opcode.IGET_OBJECT }.location.index + + // startActivity + h0Method.addInstructions( + igetObjectIndex + 1, """ + const-string v0, "Working" + $START_ACTIVITY_DESCRIPTOR + const/4 v3, 0x1 + return v3 + """.trimIndent(), + ) + + // if block end + val ifBlockEnd = h0Method.getInstructions().first { it.opcode == Opcode.RETURN }.location.index+1 + + h0Method.addInstructionsWithLabels(igetObjectIndex+1, + """ + const-string v1, "pref_mod" + invoke-virtual {p1, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z + move-result v2 + + if-nez v2, :cond_idk + goto :end + """.trimIndent(), + ExternalLabel("end", h0Method.getInstructions().first { it.opcode == Opcode.CONST_STRING }), + ExternalLabel("cond_idk", h0Method.getInstruction(ifBlockEnd)), + ) + + // Add context reference + val initActivity = InitActivityFingerprint.result!!.mutableClass.methods.last{ it.name == "onCreate" } + print(initActivity.name) + + initActivity.addInstruction(initActivity.getInstructions().lastIndex, ADD_CONTEXT_DESCRIPTOR) + } +} \ No newline at end of file diff --git a/src/main/kotlin/crimera/patches/twitter/settings/SettingsResourcePatch.kt b/src/main/kotlin/crimera/patches/twitter/settings/SettingsResourcePatch.kt new file mode 100644 index 00000000..ddb83d71 --- /dev/null +++ b/src/main/kotlin/crimera/patches/twitter/settings/SettingsResourcePatch.kt @@ -0,0 +1,42 @@ +package crimera.patches.twitter.settings + +import app.revanced.patcher.data.ResourceContext +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.ResourcePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import org.w3c.dom.Element + +@Patch( + compatiblePackages = [CompatiblePackage("com.twitter.android")], +) +object SettingsResourcePatch: ResourcePatch() { + override fun execute(context: ResourceContext) { + val settingsRoot = context["res/xml/settings_root.xml"] + if (!settingsRoot.exists()) throw PatchException("settings_root not found") + + context.xmlEditor["res/xml/settings_root.xml"].use { editor -> + val parent = editor.file.getElementsByTagName("PreferenceScreen").item(0) as Element + + val prefMod = editor.file.createElement("Preference") + prefMod.setAttribute("android:icon", "@drawable/ic_vector_settings_stroke") + prefMod.setAttribute("android:title", "Mod Settings") + prefMod.setAttribute("android:key", "pref_mod") + prefMod.setAttribute("android:order", "110") + + parent.appendChild(prefMod) + } + + context.xmlEditor["AndroidManifest.xml"].use { + val applicationNode = it.file.getElementsByTagName("application").item(0) + + val modActivity = it.file.createElement("activity").apply { + setAttribute("android:label", "Mod Settings") + setAttribute("android:name", "app.revanced.integrations.twitter.settings.SettingsActivity") + setAttribute("android:excludeFromRecents", "true") + } + + applicationNode.appendChild(modActivity) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/crimera/patches/twitter/settings/fingerprints/InitActivityFingerprint.kt b/src/main/kotlin/crimera/patches/twitter/settings/fingerprints/InitActivityFingerprint.kt new file mode 100644 index 00000000..cd1c42f1 --- /dev/null +++ b/src/main/kotlin/crimera/patches/twitter/settings/fingerprints/InitActivityFingerprint.kt @@ -0,0 +1,7 @@ +package crimera.patches.twitter.settings.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +object InitActivityFingerprint: MethodFingerprint( + strings = listOf("com.twitter.util.config.ApplicationObjectGraphConfig"), +) diff --git a/src/main/kotlin/crimera/patches/twitter/settings/fingerprints/SettingsFingerprint.kt b/src/main/kotlin/crimera/patches/twitter/settings/fingerprints/SettingsFingerprint.kt new file mode 100644 index 00000000..05ba7b41 --- /dev/null +++ b/src/main/kotlin/crimera/patches/twitter/settings/fingerprints/SettingsFingerprint.kt @@ -0,0 +1,13 @@ +package crimera.patches.twitter.settings.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +object SettingsFingerprint : MethodFingerprint( + returnType = "Z", + strings = listOf( + "pref_proxy" + ), + parameters = listOf( + "Landroidx/preference/Preference;", + ) +) \ No newline at end of file diff --git a/src/main/kotlin/crimera/patches/twitter/settings/fingerprints/SettingsInitFingerprint.kt b/src/main/kotlin/crimera/patches/twitter/settings/fingerprints/SettingsInitFingerprint.kt new file mode 100644 index 00000000..63592d96 --- /dev/null +++ b/src/main/kotlin/crimera/patches/twitter/settings/fingerprints/SettingsInitFingerprint.kt @@ -0,0 +1,13 @@ +package crimera.patches.twitter.settings.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +object SettingsInitFingerprint : MethodFingerprint( + returnType = "V", + strings = listOf( + "pref_proxy" + ), + customFingerprint = { it, _ -> + it.name == "" + } +) \ No newline at end of file