build(Needs bump)!: Update to ReVanced Patcher v22 (#6542)

Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: Pun Butrach <pun.butrach@gmail.com>
Co-authored-by: Ushie <ushiekane@gmail.com>
Co-authored-by: ILoveOpenSourceApplications <ILoveOpenSourceApplications@users.noreply.github.com>
Co-authored-by: rospino74 <34315725+rospino74@users.noreply.github.com>
Co-authored-by: drobotk <pawwwll@gmail.com>
Co-authored-by: Sayanth <13906889+SayanthD@users.noreply.github.com>
Co-authored-by: kitadai31 <90122968+kitadai31@users.noreply.github.com>

BREAKING CHANGE: Deprecated APIs have been removed, and various APIs now use the updated ReVanced Patcher v22 APIs.
This commit is contained in:
oSumAtrIX 2026-02-27 02:39:17 +01:00 committed by GitHub
parent 376f2af8d8
commit ab2ac36e30
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
842 changed files with 12691 additions and 13203 deletions

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,10 @@ dependencies {
kotlin {
compilerOptions {
freeCompilerArgs = listOf("-Xcontext-receivers")
freeCompilerArgs.addAll(
"-Xexplicit-backing-fields",
"-Xcontext-parameters"
)
}
}

View file

@ -8,7 +8,7 @@ val exportAllActivitiesPatch = resourcePatch(
description = "Makes all app activities exportable.",
use = false,
) {
execute {
apply {
val exportedFlag = "android:exported"
document("AndroidManifest.xml").use { document ->

View file

@ -1,6 +1,6 @@
package app.revanced.patches.all.misc.adb
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
@ -13,27 +13,26 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/all/misc/hide/adb/HideAdbPatch;"
private val SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE = ImmutableMethodReference(
"Landroid/provider/Settings\$Global;",
$$"Landroid/provider/Settings$Global;",
"getInt",
listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;"),
"I"
"I",
)
private val SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE = ImmutableMethodReference(
"Landroid/provider/Settings\$Global;",
$$"Landroid/provider/Settings$Global;",
"getInt",
listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;", "I"),
"I"
"I",
)
private fun MethodReference.anyMethodSignatureMatches(vararg anyOf: MethodReference): Boolean {
return anyOf.any {
MethodUtil.methodSignaturesMatch(it, this)
}
}
private val getIntMethodReferences = listOf(
SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE,
SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE,
)
@Suppress("unused")
val hideAdbStatusPatch = bytecodePatch(
val hideADBStatusPatch = bytecodePatch(
name = "Hide ADB status",
description = "Hides enabled development settings and/or ADB.",
use = false,
@ -46,11 +45,8 @@ val hideAdbStatusPatch = bytecodePatch(
val reference = instruction
.takeIf { it.opcode == Opcode.INVOKE_STATIC }
?.getReference<MethodReference>()
?.takeIf {
it.anyMethodSignatureMatches(
SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE,
SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE
)
?.takeIf { reference ->
getIntMethodReferences.any { MethodUtil.methodSignaturesMatch(it, reference) }
}
?: return@filterMap null
@ -67,9 +63,9 @@ val hideAdbStatusPatch = bytecodePatch(
method.replaceInstruction(
index,
"invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->getInt($parameterString)I"
"invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->getInt($parameterString)I",
)
}
)
},
),
)
}

View file

@ -3,8 +3,8 @@ package app.revanced.patches.all.misc.appicon
import app.revanced.patcher.patch.resourcePatch
import app.revanced.util.asSequence
import app.revanced.util.childElementsSequence
import java.util.logging.Logger
import org.w3c.dom.Element
import java.util.logging.Logger
@Suppress("unused")
val hideAppIconPatch = resourcePatch(
@ -12,7 +12,7 @@ val hideAppIconPatch = resourcePatch(
description = "Hides the app icon from the Android launcher.",
use = false,
) {
execute {
apply {
document("AndroidManifest.xml").use { document ->
var changed = false
@ -26,6 +26,7 @@ val hideAppIconPatch = resourcePatch(
"action" -> if (child.getAttribute("android:name") == "android.intent.action.MAIN") {
hasMainAction = true
}
"category" -> if (child.getAttribute("android:name") == "android.intent.category.LAUNCHER") {
launcherCategory = child
}
@ -45,4 +46,3 @@ val hideAppIconPatch = resourcePatch(
}
}
}

View file

@ -1,7 +1,7 @@
package app.revanced.patches.all.misc.build
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference

View file

@ -11,172 +11,148 @@ val spoofBuildInfoPatch = bytecodePatch(
use = false,
) {
val board by stringOption(
key = "board",
default = null,
title = "Board",
name = "Board",
description = "The name of the underlying board, like \"goldfish\".",
)
val bootloader by stringOption(
key = "bootloader",
default = null,
title = "Bootloader",
name = "Bootloader",
description = "The system bootloader version number.",
)
val brand by stringOption(
key = "brand",
default = null,
title = "Brand",
name = "Brand",
description = "The consumer-visible brand with which the product/hardware will be associated, if any.",
)
val cpuAbi by stringOption(
key = "cpu-abi",
default = null,
title = "CPU ABI",
name = "CPU ABI",
description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead.",
)
val cpuAbi2 by stringOption(
key = "cpu-abi-2",
default = null,
title = "CPU ABI 2",
name = "CPU ABI 2",
description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead.",
)
val device by stringOption(
key = "device",
default = null,
title = "Device",
name = "Device",
description = "The name of the industrial design.",
)
val display by stringOption(
key = "display",
default = null,
title = "Display",
name = "Display",
description = "A build ID string meant for displaying to the user.",
)
val fingerprint by stringOption(
key = "fingerprint",
default = null,
title = "Fingerprint",
name = "Fingerprint",
description = "A string that uniquely identifies this build.",
)
val hardware by stringOption(
key = "hardware",
default = null,
title = "Hardware",
name = "Hardware",
description = "The name of the hardware (from the kernel command line or /proc).",
)
val host by stringOption(
key = "host",
default = null,
title = "Host",
name = "Host",
description = "The host.",
)
val id by stringOption(
key = "id",
default = null,
title = "ID",
name = "ID",
description = "Either a changelist number, or a label like \"M4-rc20\".",
)
val manufacturer by stringOption(
key = "manufacturer",
default = null,
title = "Manufacturer",
name = "Manufacturer",
description = "The manufacturer of the product/hardware.",
)
val model by stringOption(
key = "model",
default = null,
title = "Model",
name = "Model",
description = "The end-user-visible name for the end product.",
)
val odmSku by stringOption(
key = "odm-sku",
default = null,
title = "ODM SKU",
name = "ODM SKU",
description = "The SKU of the device as set by the original design manufacturer (ODM).",
)
val product by stringOption(
key = "product",
default = null,
title = "Product",
name = "Product",
description = "The name of the overall product.",
)
val radio by stringOption(
key = "radio",
default = null,
title = "Radio",
name = "Radio",
description = "This field was deprecated in API level 15. " +
"The radio firmware version is frequently not available when this class is initialized, " +
"leading to a blank or \"unknown\" value for this string. Use getRadioVersion() instead.",
)
val serial by stringOption(
key = "serial",
default = null,
title = "Serial",
name = "Serial",
description = "This field was deprecated in API level 26. Use getSerial() instead.",
)
val sku by stringOption(
key = "sku",
default = null,
title = "SKU",
name = "SKU",
description = "The SKU of the hardware (from the kernel command line).",
)
val socManufacturer by stringOption(
key = "soc-manufacturer",
default = null,
title = "SOC manufacturer",
name = "SOC manufacturer",
description = "The manufacturer of the device's primary system-on-chip.",
)
val socModel by stringOption(
key = "soc-model",
default = null,
title = "SOC model",
name = "SOC model",
description = "The model name of the device's primary system-on-chip.",
)
val tags by stringOption(
key = "tags",
default = null,
title = "Tags",
name = "Tags",
description = "Comma-separated tags describing the build, like \"unsigned,debug\".",
)
val time by longOption(
key = "time",
default = null,
title = "Time",
name = "Time",
description = "The time at which the build was produced, given in milliseconds since the UNIX epoch.",
)
val type by stringOption(
key = "type",
default = null,
title = "Type",
name = "Type",
description = "The type of build, like \"user\" or \"eng\".",
)
val user by stringOption(
key = "user",
default = null,
title = "User",
name = "User",
description = "The user.",
)
@ -209,6 +185,5 @@ val spoofBuildInfoPatch = bytecodePatch(
user,
)
},
)
}

View file

@ -1,8 +1,6 @@
@file:Suppress("unused")
package app.revanced.patches.all.misc.connectivity.location.hide
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.fromMethodReference

View file

@ -1,9 +0,0 @@
package app.revanced.patches.all.misc.connectivity.telephony.sim.spoof
import app.revanced.patcher.patch.bytecodePatch
@Deprecated("Patch was renamed", ReplaceWith("spoofSimProviderPatch"))
@Suppress("unused")
val spoofSimCountryPatch = bytecodePatch {
dependsOn(spoofSimProviderPatch)
}

View file

@ -1,21 +1,21 @@
package app.revanced.patches.all.misc.connectivity.telephony.sim.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.intOption
import app.revanced.patcher.patch.stringOption
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
import java.util.Locale
import java.util.*
@Suppress("unused")
val spoofSimProviderPatch = bytecodePatch(
val spoofSIMProviderPatch = bytecodePatch(
name = "Spoof SIM provider",
description = "Spoofs information about the SIM card provider.",
use = false,
@ -23,13 +23,11 @@ val spoofSimProviderPatch = bytecodePatch(
val countries = Locale.getISOCountries().associateBy { Locale("", it).displayCountry }
fun isoCountryPatchOption(
key: String,
title: String,
name: String,
) = stringOption(
key,
name,
null,
countries,
title,
"ISO-3166-1 alpha-2 country code equivalent for the SIM provider's country code.",
false,
validator = { it: String? -> it == null || it.uppercase() in countries.values },
@ -37,39 +35,29 @@ val spoofSimProviderPatch = bytecodePatch(
fun isMccMncValid(it: Int?): Boolean = it == null || (it >= 10000 && it <= 999999)
val networkCountryIso by isoCountryPatchOption(
"networkCountryIso",
"Network ISO country code",
)
val networkCountryIso by isoCountryPatchOption("Network ISO country code")
val networkOperator by intOption(
key = "networkOperator",
title = "MCC+MNC network operator code",
name = "MCC+MNC network operator code",
description = "The 5 or 6 digits MCC+MNC (Mobile Country Code + Mobile Network Code) of the network operator.",
validator = { isMccMncValid(it) }
validator = { isMccMncValid(it) },
)
val networkOperatorName by stringOption(
key = "networkOperatorName",
title = "Network operator name",
name = "Network operator name",
description = "The full name of the network operator.",
)
val simCountryIso by isoCountryPatchOption(
"simCountryIso",
"SIM ISO country code",
)
val simCountryIso by isoCountryPatchOption("SIM ISO country code")
val simOperator by intOption(
key = "simOperator",
title = "MCC+MNC SIM operator code",
name = "MCC+MNC SIM operator code",
description = "The 5 or 6 digits MCC+MNC (Mobile Country Code + Mobile Network Code) of the SIM operator.",
validator = { isMccMncValid(it) }
validator = { isMccMncValid(it) },
)
val simOperatorName by stringOption(
key = "simOperatorName",
title = "SIM operator name",
name = "SIM operator name",
description = "The full name of the SIM operator.",
)

View file

@ -11,7 +11,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
@Suppress("unused")
val spoofWifiPatch = bytecodePatch(
val spoofWiFiConnectionPatch = bytecodePatch(
name = "Spoof Wi-Fi connection",
description = "Spoofs an existing Wi-Fi connection.",
use = false,

View file

@ -9,34 +9,29 @@ import app.revanced.util.getNode
import org.w3c.dom.Element
import java.io.File
@Suppress("unused")
val customNetworkSecurityPatch = resourcePatch(
name = "Custom network security",
description = "Allows trusting custom certificate authorities for a specific domain.",
use = false
use = false,
) {
val targetDomains by stringsOption(
key = "targetDomains",
title = "Target domains",
name = "Target domains",
description = "List of domains to which the custom trust configuration will be applied (one domain per entry).",
default = listOf("example.com"),
required = true
required = true,
)
val includeSubdomains by booleanOption(
key = "includeSubdomains",
title = "Include subdomains",
name = "Include subdomains",
description = "Applies the configuration to all subdomains of the target domains.",
default = false,
required = true
required = true,
)
val customCAFilePaths by stringsOption(
key = "customCAFilePaths",
title = "Custom CA file paths",
name = "Custom CA file paths",
description = """
List of paths to files in PEM or DER format (one file path per entry).
@ -47,43 +42,35 @@ val customNetworkSecurityPatch = resourcePatch(
CA files will be bundled in res/raw/ of resulting APK
""".trimIndentMultiline(),
default = null,
required = false
required = false,
)
val allowUserCerts by booleanOption(
key = "allowUserCerts",
title = "Trust user added CAs",
name = "Trust user added CAs",
description = "Makes an app trust certificates from the Android user store for the specified domains, and if the option \"Include Subdomains\" is enabled then also the subdomains.",
default = false,
required = true
required = true,
)
val allowSystemCerts by booleanOption(
key = "allowSystemCerts",
title = "Trust system CAs",
name = "Trust system CAs",
description = "Makes an app trust certificates from the Android system store for the specified domains, and and if the option \"Include Subdomains\" is enabled then also the subdomains.",
default = true,
required = true
required = true,
)
val allowCleartextTraffic by booleanOption(
key = "allowCleartextTraffic",
title = "Allow cleartext traffic (HTTP)",
name = "Allow cleartext traffic (HTTP)",
description = "Allows unencrypted HTTP traffic for the specified domains, and if \"Include Subdomains\" is enabled then also the subdomains.",
default = false,
required = true
required = true,
)
val overridePins by booleanOption(
key = "overridePins",
title = "Override certificate pinning",
name = "Override certificate pinning",
description = "Overrides certificate pinning for the specified domains and their subdomains if the option \"Include Subdomains\" is enabled to allow inspecting app traffic via a proxy.",
default = false,
required = true
required = true,
)
fun generateNetworkSecurityConfig(): String {
@ -128,55 +115,47 @@ ${trustAnchorsXML.trimEnd()}
</trust-anchors>
</domain-config>
</network-security-config>
""".trimIndent()
""".trimIndent()
}
execute {
apply {
val nscFileNameBare = "network_security_config"
val resXmlDir = "res/xml"
val resRawDir = "res/raw"
val nscFileNameWithSuffix = "$nscFileNameBare.xml"
document("AndroidManifest.xml").use { document ->
val applicationNode = document.getNode("application") as Element
applicationNode.setAttribute("android:networkSecurityConfig", "@xml/$nscFileNameBare")
}
File(get(resXmlDir), nscFileNameWithSuffix).apply {
writeText(generateNetworkSecurityConfig())
}
for (customCAFilePath in customCAFilePaths ?: emptyList()) {
val file = File(customCAFilePath)
if (!file.exists()) {
throw PatchException(
"The custom CA file path cannot be found: " +
file.absolutePath
file.absolutePath,
)
}
if (!file.isFile) {
throw PatchException(
"The custom CA file path must be a file: "
+ file.absolutePath
"The custom CA file path must be a file: " +
file.absolutePath,
)
}
val caFileNameWithoutSuffix = customCAFilePath.substringAfterLast('/').substringBefore('.')
val caFile = File(customCAFilePath)
File(
get(resRawDir),
caFileNameWithoutSuffix
caFileNameWithoutSuffix,
).writeText(
caFile.readText()
caFile.readText(),
)
}
}
}

View file

@ -3,12 +3,13 @@ package app.revanced.patches.all.misc.debugging
import app.revanced.patcher.patch.resourcePatch
import org.w3c.dom.Element
@Suppress("unused")
val enableAndroidDebuggingPatch = resourcePatch(
name = "Enable Android debugging",
description = "Enables Android debugging capabilities. This can slow down the app.",
use = false,
) {
execute {
apply {
document("AndroidManifest.xml").use { document ->
val applicationNode =
document

View file

@ -1,19 +0,0 @@
package app.revanced.patches.all.misc.directory
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.directory.documentsprovider.exportInternalDataDocumentsProviderPatch
@Suppress("unused")
@Deprecated(
"Superseded by internalDataDocumentsProviderPatch",
ReplaceWith("internalDataDocumentsProviderPatch"),
)
val changeDataDirectoryLocationPatch = bytecodePatch(
// name = "Change data directory location",
description = "Changes the data directory in the application from " +
"the app internal storage directory to /sdcard/android/data accessible by root-less devices." +
"Using this patch can cause unexpected issues with some apps.",
use = false,
) {
dependsOn(exportInternalDataDocumentsProviderPatch)
}

View file

@ -18,7 +18,7 @@ val exportInternalDataDocumentsProviderPatch = resourcePatch(
},
)
execute {
apply {
val documentsProviderClass =
"app.revanced.extension.all.misc.directory.documentsprovider.InternalDataDocumentsProvider"
@ -28,7 +28,7 @@ val exportInternalDataDocumentsProviderPatch = resourcePatch(
.asSequence()
.any { it.attributes.getNamedItem("android:name")?.nodeValue == documentsProviderClass }
) {
return@execute
return@apply
}
val authority =

View file

@ -3,19 +3,16 @@ package app.revanced.patches.all.misc.hex
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.rawResourcePatch
import app.revanced.patcher.patch.stringsOption
import app.revanced.patches.shared.misc.hex.HexPatchBuilder
import app.revanced.patches.shared.misc.hex.hexPatch
import app.revanced.util.Utils.trimIndentMultiline
@Suppress("unused")
val hexPatch = rawResourcePatch(
name = "Hex",
val Hex = rawResourcePatch(
description = "Replaces a hexadecimal patterns of bytes of files in an APK.",
use = false,
) {
val replacements by stringsOption(
key = "replacements",
title = "Replacements",
name = "Replacements",
description = """
Hexadecimal patterns to search for and replace with another in a target file.
@ -34,10 +31,14 @@ val hexPatch = rawResourcePatch(
dependsOn(
hexPatch(
block = fun HexPatchBuilder.() {
block = {
replacements!!.forEach { replacement ->
try {
val (pattern, replacementPattern, targetFilePath) = replacement.split("|", limit = 3)
val (pattern, replacementPattern, targetFilePath) = replacement.split(
"|",
limit = 3
)
pattern asPatternTo replacementPattern inFile targetFilePath
} catch (e: Exception) {
throw PatchException(
@ -49,6 +50,6 @@ val hexPatch = rawResourcePatch(
}
}
},
)
),
)
}

View file

@ -8,7 +8,7 @@ val predictiveBackGesturePatch = resourcePatch(
description = "Enables the predictive back gesture introduced on Android 13.",
use = false,
) {
execute {
apply {
val flag = "android:enableOnBackInvokedCallback"
document("AndroidManifest.xml").use { document ->

View file

@ -3,6 +3,7 @@ package app.revanced.patches.all.misc.network
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.debugging.enableAndroidDebuggingPatch
import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.util.getNode
import org.w3c.dom.Element
import java.io.File
@ -14,16 +15,20 @@ val overrideCertificatePinningPatch = resourcePatch(
) {
dependsOn(enableAndroidDebuggingPatch)
execute {
apply {
val resXmlDirectory = get("res/xml")
// Add android:networkSecurityConfig="@xml/network_security_config" and the "networkSecurityConfig" attribute if it does not exist.
document("AndroidManifest.xml").use { document ->
val applicationNode = document.getElementsByTagName("application").item(0) as Element
if (!applicationNode.hasAttribute("networkSecurityConfig")) {
document.createAttribute("android:networkSecurityConfig")
.apply { value = "@xml/network_security_config" }.let(applicationNode.attributes::setNamedItem)
val applicationNode = document.getNode("application") as Element
applicationNode.apply {
if (!hasAttribute("networkSecurityConfig")) {
attributes.setNamedItem(
document.createAttribute("android:networkSecurityConfig").apply {
value = "@xml/network_security_config"
}
)
}
}
}

View file

@ -6,7 +6,15 @@ import app.revanced.util.getNode
import org.w3c.dom.Element
import java.util.logging.Logger
lateinit var packageNameOption: Option<String>
private val packageNameOption = stringOption(
default = "Default",
values = mapOf("Default" to "Default"),
name = "Package name",
description = "The name of the package to rename the app to.",
required = true,
) {
it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$"))
}
/**
* Set the package name to use.
@ -26,40 +34,30 @@ fun setOrGetFallbackPackageName(fallbackPackageName: String): String {
}
}
@Suppress("ObjectPropertyName")
val changePackageNamePatch = resourcePatch(
name = "Change package name",
description = "Appends \".revanced\" to the package name by default. " +
"Changing the package name of the app can lead to unexpected issues.",
use = false,
) {
packageNameOption = stringOption(
key = "packageName",
default = "Default",
values = mapOf("Default" to "Default"),
title = "Package name",
description = "The name of the package to rename the app to.",
required = true,
) {
it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$"))
}
packageNameOption()
val updatePermissions by booleanOption(
key = "updatePermissions",
default = false,
title = "Update permissions",
name = "Update permissions",
description = "Update compatibility receiver permissions. " +
"Enabling this can fix installation errors, but this can also break features in certain apps.",
)
val updateProviders by booleanOption(
key = "updateProviders",
default = false,
title = "Update providers",
name = "Update providers",
description = "Update provider names declared by the app. " +
"Enabling this can fix installation errors, but this can also break features in certain apps.",
)
finalize {
afterDependents {
/**
* Apps that are confirmed to not work correctly with this patch.
* This is not an exhaustive list, and is only the apps with
@ -80,7 +78,7 @@ val changePackageNamePatch = resourcePatch(
val packageName = manifest.getAttribute("package")
if (incompatibleAppPackages.contains(packageName)) {
return@finalize Logger.getLogger(this::class.java.name).severe(
return@afterDependents Logger.getLogger(this::class.java.name).severe(
"'$packageName' does not work correctly with \"Change package name\"",
)
}

View file

@ -1,10 +1,9 @@
package app.revanced.patches.all.misc.playintegrity
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
@ -16,10 +15,9 @@ private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference(
"Landroid/content/Context;",
"bindService",
listOf("Landroid/content/Intent;", "Landroid/content/ServiceConnection;", "I"),
"Z"
"Z",
)
@Suppress("unused")
val disablePlayIntegrityPatch = bytecodePatch(
name = "Disable Play Integrity",
@ -43,13 +41,14 @@ val disablePlayIntegrityPatch = bytecodePatch(
transform = { method, entry ->
val (instruction, index, parameterTypes) = entry
val parameterString = parameterTypes.joinToString(separator = "")
val registerString = "v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}, v${instruction.registerF}"
val registerString =
"v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}, v${instruction.registerF}"
method.replaceInstruction(
index,
"invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->bindService(Landroid/content/Context;$parameterString)Z"
"invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->bindService(Landroid/content/Context;$parameterString)Z",
)
}
)
},
),
)
}

View file

@ -215,8 +215,8 @@ fun addResources(
* @see addResourcesPatch
*/
fun addResources(
patch: Patch<*>,
parseIds: (Patch<*>) -> Map<AppId, Set<PatchId>> = {
patch: Patch,
parseIds: (Patch) -> Map<AppId, Set<PatchId>> = {
val patchId = patch.name ?: throw PatchException("Patch has no name")
val packages = patch.compatiblePackages ?: throw PatchException("Patch has no compatible packages")
@ -273,9 +273,9 @@ val addResourcesPatch = resourcePatch(
from the temporary map to addResourcesPatch.
After all patches that depend on addResourcesPatch have been executed,
addResourcesPatch#finalize is finally called to add all staged resources to the app.
addResourcesPatch#afterDependents is finally called to add all staged resources to the app.
*/
execute {
apply {
stagedResources = buildMap {
/**
* Puts resources under `/resources/addresources/<value>/<resourceKind>.xml` into the map.
@ -324,7 +324,7 @@ val addResourcesPatch = resourcePatch(
// Stage all resources to a temporary map.
// Staged resources consumed by addResourcesPatch#invoke(Patch)
// are later used in addResourcesPatch#finalize.
// are later used in addResourcesPatch#afterDependents.
try {
val addStringResources = { source: Value, dest: Value ->
addResources(source, dest, "strings", StringResource::fromNode)
@ -343,7 +343,7 @@ val addResourcesPatch = resourcePatch(
* Adds all resources staged in [addResourcesPatch] to the app.
* This is called after all patches that depend on [addResourcesPatch] have been executed.
*/
finalize {
afterDependents {
operator fun MutableMap<String, Pair<Document, Node>>.invoke(
value: Value,
resource: BaseResource,
@ -359,14 +359,14 @@ val addResourcesPatch = resourcePatch(
}
getOrPut(resourceFileName) {
this@finalize["res/$value/$resourceFileName.xml"].also {
this@afterDependents["res/$value/$resourceFileName.xml"].also {
it.parentFile?.mkdirs()
if (it.createNewFile()) {
it.writeText("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>")
}
}
document("res/$value/$resourceFileName.xml").let { document ->
// Save the target node here as well

View file

@ -10,7 +10,7 @@ import org.w3c.dom.Element
private val removeCaptureRestrictionResourcePatch = resourcePatch(
description = "Sets allowAudioPlaybackCapture in manifest to true.",
) {
execute {
apply {
document("AndroidManifest.xml").use { document ->
// Get the application node.
val applicationNode =

View file

@ -1,6 +1,6 @@
package app.revanced.patches.all.misc.screenshot
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
@ -16,7 +16,7 @@ private val registerScreenCaptureCallbackMethodReference = ImmutableMethodRefere
"Ljava/util/concurrent/Executor;",
"Landroid/app/Activity\$ScreenCaptureCallback;",
),
"V"
"V",
)
private val unregisterScreenCaptureCallbackMethodReference = ImmutableMethodReference(
@ -25,28 +25,30 @@ private val unregisterScreenCaptureCallbackMethodReference = ImmutableMethodRefe
listOf(
"Landroid/app/Activity\$ScreenCaptureCallback;",
),
"V"
"V",
)
@Suppress("unused")
val preventScreenshotDetectionPatch = bytecodePatch(
name = "Prevent screenshot detection",
description = "Removes the registration of all screen capture callbacks. This prevents the app from detecting screenshots.",
use = false
use = false,
) {
dependsOn(transformInstructionsPatch(
filterMap = { _, _, instruction, instructionIndex ->
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@transformInstructionsPatch null
val reference = instruction.getReference<MethodReference>() ?: return@transformInstructionsPatch null
dependsOn(
transformInstructionsPatch(
filterMap = { _, _, instruction, instructionIndex ->
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@transformInstructionsPatch null
instructionIndex.takeIf {
MethodUtil.methodSignaturesMatch(reference, registerScreenCaptureCallbackMethodReference) ||
MethodUtil.methodSignaturesMatch(reference, unregisterScreenCaptureCallbackMethodReference)
}
},
transform = { mutableMethod, instructionIndex ->
mutableMethod.removeInstruction(instructionIndex)
}
))
val reference = instruction.getReference<MethodReference>() ?: return@transformInstructionsPatch null
instructionIndex.takeIf {
MethodUtil.methodSignaturesMatch(reference, registerScreenCaptureCallbackMethodReference) ||
MethodUtil.methodSignaturesMatch(reference, unregisterScreenCaptureCallbackMethodReference)
}
},
transform = { mutableMethod, instructionIndex ->
mutableMethod.removeInstruction(instructionIndex)
},
),
)
}

View file

@ -1,6 +1,6 @@
package app.revanced.patches.all.misc.screenshot
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c

View file

@ -13,12 +13,13 @@ val removeShareTargetsPatch = resourcePatch(
description = "Removes share targets like directly sharing to a frequent contact.",
use = false,
) {
execute {
apply {
try {
document("res/xml/shortcuts.xml")
} catch (_: FileNotFoundException) {
return@execute Logger.getLogger(this::class.java.name).warning(
"The app has no shortcuts. No changes applied.")
return@apply Logger.getLogger(this::class.java.name).warning(
"The app has no shortcuts. No changes applied.",
)
}.use { document ->
val rootNode = document.getNode("shortcuts") as? Element ?: return@use

View file

@ -5,7 +5,6 @@ import app.revanced.patcher.patch.stringOption
import app.revanced.util.getNode
import com.android.apksig.ApkVerifier
import com.android.apksig.apk.ApkFormatException
import org.w3c.dom.Element
import java.io.File
import java.io.IOException
import java.nio.file.InvalidPathException
@ -15,15 +14,14 @@ import java.security.cert.CertificateFactory
import java.util.*
@Suppress("unused")
val enableRomSignatureSpoofing = resourcePatch(
val enableROMSignatureSpoofingPatch = resourcePatch(
name = "Enable ROM signature spoofing",
description = "Spoofs the signature via the manifest meta-data \"fake-signature\". " +
"This patch only works with ROMs that support signature spoofing.",
"This patch only works with ROMs that support signature spoofing.",
use = false,
) {
val signatureOrPath by stringOption(
key = "signatureOrApkFilePath",
title = "Signature or APK file path",
name = "Signature or APK file path",
validator = validator@{ signature ->
signature ?: return@validator false
@ -32,14 +30,13 @@ val enableRomSignatureSpoofing = resourcePatch(
description = "The hex-encoded signature or path to an APK file with the desired signature.",
required = true,
)
execute {
apply {
document("AndroidManifest.xml").use { document ->
val permission = document.createElement("uses-permission").apply {
setAttribute("android:name", "android.permission.FAKE_PACKAGE_SIGNATURE")
}
val manifest = document.getNode("manifest").appendChild(permission)
val fakeSignatureMetadata = document.createElement("meta-data").apply {
setAttribute("android:name", "fake-signature")
setAttribute("android:value", parseSignature(signatureOrPath!!))
@ -70,15 +67,17 @@ private fun parseSignature(optionValue: String): String? {
val hexFormat = HexFormat.of()
val signature = (if (result.isVerifiedUsingV3Scheme) {
result.v3SchemeSigners[0].certificate
} else if (result.isVerifiedUsingV2Scheme) {
result.v2SchemeSigners[0].certificate
} else if (result.isVerifiedUsingV1Scheme) {
result.v1SchemeSigners[0].certificate
} else {
return null
}).encoded
val signature = (
if (result.isVerifiedUsingV3Scheme) {
result.v3SchemeSigners[0].certificate
} else if (result.isVerifiedUsingV2Scheme) {
result.v2SchemeSigners[0].certificate
} else if (result.isVerifiedUsingV1Scheme) {
result.v1SchemeSigners[0].certificate
} else {
return null
}
).encoded
return hexFormat.formatHex(signature)
} catch (_: IOException) {
@ -89,4 +88,4 @@ private fun parseSignature(optionValue: String): String? {
}
return null
}
}

View file

@ -6,13 +6,13 @@ import org.w3c.dom.Element
import java.util.logging.Logger
@Suppress("unused")
val setTargetSdkVersion34 = resourcePatch(
val setTargetSDKVersion34Patch = resourcePatch(
name = "Set target SDK version 34",
description = "Changes the target SDK to version 34 (Android 14). " +
"For devices running Android 15+, this will disable edge-to-edge display.",
"For devices running Android 15+, this will disable edge-to-edge display.",
use = false,
) {
execute {
apply {
val targetSdkOverride = 34 // Android 14.
document("AndroidManifest.xml").use { document ->
@ -24,12 +24,12 @@ val setTargetSdkVersion34 = resourcePatch(
try {
val manifestElement = document.getNode("manifest") as Element
val compileSdkVersion = Integer.parseInt(
manifestElement.getAttribute("android:compileSdkVersion")
manifestElement.getAttribute("android:compileSdkVersion"),
)
if (compileSdkVersion <= targetSdkOverride) {
getLogger().warning(
"This app does not appear to use a target SDK above $targetSdkOverride: " +
"(compileSdkVersion: $compileSdkVersion)"
"(compileSdkVersion: $compileSdkVersion)",
)
}
} catch (_: Exception) {

View file

@ -1,7 +1,7 @@
package app.revanced.patches.all.misc.transformation
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod
import app.revanced.patcher.extensions.replaceInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.instruction.Instruction

View file

@ -1,49 +1,22 @@
package app.revanced.patches.all.misc.transformation
import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.util.findMutableMethodOf
import app.revanced.util.forEachInstructionAsSequence
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
@Deprecated(
"Use forEachInstructionAsSequence directly within a bytecodePatch", ReplaceWith(
"bytecodePatch { apply { forEachInstructionAsSequence(filterMap, transform) } }",
"app.revanced.util.forEachInstructionAsSequence",
"app.revanced.patcher.patch.bytecodePatch",
)
)
fun <T> transformInstructionsPatch(
filterMap: (ClassDef, Method, Instruction, Int) -> T?,
transform: (MutableMethod, T) -> Unit,
) = bytecodePatch {
// Returns the patch indices as a Sequence, which will execute lazily.
fun findPatchIndices(classDef: ClassDef, method: Method): Sequence<T>? =
method.implementation?.instructions?.asSequence()?.withIndex()?.mapNotNull { (index, instruction) ->
filterMap(classDef, method, instruction, index)
}
execute {
// Find all methods to patch
buildMap {
classes.forEach { classDef ->
val methods = buildList {
classDef.methods.forEach { method ->
// Since the Sequence executes lazily,
// using any() results in only calling
// filterMap until the first index has been found.
if (findPatchIndices(classDef, method)?.any() == true) add(method)
}
}
if (methods.isNotEmpty()) {
put(classDef, methods)
}
}
}.forEach { (classDef, methods) ->
// And finally transform the methods...
val mutableClass = proxy(classDef).mutableClass
methods.map(mutableClass::findMutableMethodOf).forEach methods@{ mutableMethod ->
val patchIndices = findPatchIndices(mutableClass, mutableMethod)?.toCollection(ArrayDeque())
?: return@methods
while (!patchIndices.isEmpty()) transform(mutableMethod, patchIndices.removeLast())
}
}
}
apply { forEachInstructionAsSequence(filterMap, transform) }
}

View file

@ -9,23 +9,22 @@ import org.w3c.dom.Element
val changeVersionCodePatch = resourcePatch(
name = "Change version code",
description = "Changes the version code of the app. This will turn off app store updates " +
"and allows downgrading an existing app install to an older app version.",
"and allows downgrading an existing app install to an older app version.",
use = false,
) {
val versionCode by intOption(
key = "versionCode",
default = Int.MAX_VALUE,
values = mapOf(
"Lowest" to 1,
"Highest" to Int.MAX_VALUE,
),
title = "Version code",
name = "Version code",
description = "The version code to use. Using the highest value turns off app store " +
"updates and allows downgrading an existing app install to an older app version.",
"updates and allows downgrading an existing app install to an older app version.",
required = true,
) { versionCode -> versionCode!! >= 1 }
execute {
apply {
document("AndroidManifest.xml").use { document ->
val manifestElement = document.getNode("manifest") as Element
manifestElement.setAttribute("android:versionCode", "$versionCode")

View file

@ -1,22 +1,16 @@
package app.revanced.patches.amazon
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val deepLinkingPatch = bytecodePatch(
val alwaysAllowDeepLinkingPatch = bytecodePatch(
name = "Always allow deep-linking",
description = "Open Amazon links, even if the app is not set to handle Amazon links.",
) {
compatibleWith("com.amazon.mShop.android.shopping")
execute {
deepLinkingFingerprint.method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""",
)
apply {
deepLinkingMethod.returnEarly(true)
}
}

View file

@ -1,11 +1,17 @@
package app.revanced.patches.amazon
import app.revanced.patcher.fingerprint
import app.revanced.patcher.accessFlags
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import com.android.tools.smali.dexlib2.AccessFlags
internal val deepLinkingFingerprint = fingerprint {
internal val BytecodePatchContext.deepLinkingMethod by gettingFirstMethodDeclaratively(
"https://www.",
"android.intent.action.VIEW"
) {
accessFlags(AccessFlags.PRIVATE)
returns("Z")
parameters("L")
strings("https://www.", "android.intent.action.VIEW")
returnType("Z")
parameterTypes("L")
}

View file

@ -1,6 +1,9 @@
package app.revanced.patches.angulus.ads
import app.revanced.patcher.fingerprint
import app.revanced.patcher.accessFlags
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import com.android.tools.smali.dexlib2.AccessFlags
// Keywords to search for in case the method name changes:
@ -9,10 +12,9 @@ import com.android.tools.smali.dexlib2.AccessFlags
// dailyAdResetCount
// MeasurementPrefs
// This fingerprint targets a method that returns the daily measurement count.
// This targets a method that returns the daily measurement count.
// This method is used to determine if the user has reached the daily limit of measurements.
internal val getDailyMeasurementCountFingerprint = fingerprint {
internal val BytecodePatchContext.getDailyMeasurementCountMethod by gettingFirstMethodDeclaratively("dailyMeasurementCount") {
accessFlags(AccessFlags.PRIVATE)
returns("I")
strings("dailyMeasurementCount")
returnType("I")
}

View file

@ -1,17 +1,17 @@
package app.revanced.patches.angulus.ads
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.misc.pairip.license.disableLicenseCheckPatch
import app.revanced.patches.shared.misc.pairip.license.disablePairipLicenseCheckPatch
import app.revanced.util.returnEarly
@Suppress("unused")
val angulusPatch = bytecodePatch(name = "Hide ads") {
val hideAdsPatch = bytecodePatch("Hide ads") {
compatibleWith("com.drinkplusplus.angulus")
dependsOn(disableLicenseCheckPatch)
dependsOn(disablePairipLicenseCheckPatch)
execute {
apply {
// Always return 0 as the daily measurement count.
getDailyMeasurementCountFingerprint.method.returnEarly(0)
getDailyMeasurementCountMethod.returnEarly(0)
}
}

View file

@ -1,19 +0,0 @@
package app.revanced.patches.backdrops.misc.pro
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.Opcode
@Deprecated("Fingerprint no longer resolves and will soon be deleted.")
internal val proUnlockFingerprint = fingerprint {
opcodes(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
)
custom { method, _ ->
method.name == "lambda\$existPurchase\$0" &&
method.definingClass == "Lcom/backdrops/wallpapers/data/local/DatabaseHandlerIAB;"
}
}

View file

@ -1,24 +0,0 @@
package app.revanced.patches.backdrops.misc.pro
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
@Deprecated("This patch no longer works and will soon be deleted.")
val proUnlockPatch = bytecodePatch{
compatibleWith("com.backdrops.wallpapers")
execute {
val registerIndex = proUnlockFingerprint.patternMatch!!.endIndex - 1
proUnlockFingerprint.method.apply {
val register = getInstruction<OneRegisterInstruction>(registerIndex).registerA
addInstruction(
proUnlockFingerprint.patternMatch!!.endIndex,
"const/4 v$register, 0x1",
)
}
}
}

View file

@ -1,7 +1,9 @@
package app.revanced.patches.bandcamp.limitations
import app.revanced.patcher.fingerprint
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.patch.BytecodePatchContext
internal val handlePlaybackLimitsFingerprint = fingerprint {
strings("track_id", "play_count")
}
internal val BytecodePatchContext.handlePlaybackLimitsMethod by gettingFirstMethodDeclaratively(
"track_id",
"play_count"
)

View file

@ -10,7 +10,7 @@ val removePlayLimitsPatch = bytecodePatch(
) {
compatibleWith("com.bandcamp.android")
execute {
handlePlaybackLimitsFingerprint.method.returnEarly()
apply {
handlePlaybackLimitsMethod.returnEarly()
}
}

View file

@ -1,7 +1,7 @@
package app.revanced.patches.cieid.restrictions.root
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val bypassRootChecksPatch = bytecodePatch(
@ -10,7 +10,7 @@ val bypassRootChecksPatch = bytecodePatch(
) {
compatibleWith("it.ipzs.cieid")
execute {
checkRootFingerprint.method.addInstruction(1, "return-void")
apply {
checkRootMethod.returnEarly()
}
}

View file

@ -1,9 +1,11 @@
package app.revanced.patches.cieid.restrictions.root
import app.revanced.patcher.fingerprint
import app.revanced.patcher.definingClass
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
internal val checkRootFingerprint = fingerprint {
custom { method, _ ->
method.name == "onResume" && method.definingClass == "Lit/ipzs/cieid/BaseActivity;"
}
internal val BytecodePatchContext.checkRootMethod by gettingFirstMethodDeclaratively {
name("onResume")
definingClass("Lit/ipzs/cieid/BaseActivity;")
}

View file

@ -1,28 +1,25 @@
package app.revanced.patches.com.sbs.ondemand.tv
import app.revanced.patcher.fingerprint
import app.revanced.patcher.definingClass
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
internal val shouldShowAdvertisingTVFingerprint = fingerprint {
returns("Z")
custom { method, classDef ->
method.name == "getShouldShowAdvertisingTV" &&
classDef.type == "Lcom/sbs/ondemand/common/InMemoryStorage;"
}
val BytecodePatchContext.shouldShowAdvertisingTVMethod by gettingFirstMethodDeclaratively {
name("getShouldShowAdvertisingTV")
definingClass("Lcom/sbs/ondemand/common/InMemoryStorage;")
returnType("Z")
}
internal val shouldShowPauseAdFingerprint = fingerprint {
returns("Z")
custom { method, classDef ->
method.name == "shouldShowPauseAd" &&
classDef.type == "Lcom/sbs/ondemand/player/viewmodels/PauseAdController;"
}
internal val BytecodePatchContext.shouldShowPauseAdMethod by gettingFirstMethodDeclaratively {
name("shouldShowPauseAd")
definingClass("Lcom/sbs/ondemand/player/viewmodels/PauseAdController;")
returnType("Z")
}
internal val requestAdStreamFingerprint = fingerprint {
returns("V")
custom { method, classDef ->
method.name == "requestAdStream\$player_googleStoreTvRelease" &&
classDef.type == "Lcom/sbs/ondemand/player/viewmodels/AdsController;"
}
internal val BytecodePatchContext.requestAdStreamMethod by gettingFirstMethodDeclaratively {
name("requestAdStream\$player_googleStoreTvRelease")
definingClass("Lcom/sbs/ondemand/player/viewmodels/AdsController;")
returnType("V")
}

View file

@ -1,8 +1,8 @@
package app.revanced.patches.com.sbs.ondemand.tv
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.misc.pairip.license.disableLicenseCheckPatch
import app.revanced.patches.shared.misc.pairip.license.disablePairipLicenseCheckPatch
import app.revanced.util.returnEarly
@Suppress("unused")
@ -12,11 +12,11 @@ val removeAdsPatch = bytecodePatch(
) {
compatibleWith("com.sbs.ondemand.tv")
dependsOn(disableLicenseCheckPatch)
dependsOn(disablePairipLicenseCheckPatch)
execute {
shouldShowAdvertisingTVFingerprint.method.returnEarly(true)
shouldShowPauseAdFingerprint.method.returnEarly(false)
apply {
shouldShowAdvertisingTVMethod.returnEarly(true)
shouldShowPauseAdMethod.returnEarly(false)
// Remove on-demand pre-roll advertisements using exception handling.
// Exception handling is used instead of returnEarly() because:
@ -24,14 +24,14 @@ val removeAdsPatch = bytecodePatch(
// 2. SBS app has built-in exception handling in handleProviderFailure().
// 3. Exception triggers fallbackToAkamaiProvider() which loads actual content.
// 4. This preserves the intended app flow: first try ads, then fail gracefully, then load content.
requestAdStreamFingerprint.method.addInstructions(
0,
requestAdStreamMethod.addInstructions(
0,
"""
new-instance v0, Ljava/lang/RuntimeException;
const-string v1, "Ad stream disabled"
invoke-direct {v0, v1}, Ljava/lang/RuntimeException;-><init>(Ljava/lang/String;)V
throw v0
"""
""",
)
}
}

View file

@ -1,7 +1,7 @@
package app.revanced.patches.cricbuzz.ads
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.cricbuzz.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
@ -15,26 +15,25 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/cricbuzz/ads/HideAdsPatch;"
@Suppress("unused")
val disableAdsPatch = bytecodePatch (
name = "Hide ads",
) {
val hideAdsPatch = bytecodePatch("Hide ads") {
compatibleWith("com.cricbuzz.android"("6.24.01"))
dependsOn(sharedExtensionPatch)
execute {
userStateSwitchFingerprint.method.returnEarly(true)
apply {
userStateSwitchMethod.returnEarly(true)
// Remove region-specific Cricbuzz11 elements.
cb11ConstructorFingerprint.method.addInstruction(0, "const/4 p7, 0x0")
getBottomBarFingerprint.method.apply {
val getIndex = indexOfFirstInstructionOrThrow() {
cb11ConstructorMethod.addInstruction(0, "const/4 p7, 0x0")
getBottomBarMethod.apply {
val getIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IGET_OBJECT && getReference<FieldReference>()?.name == "bottomBar"
}
val getRegister = getInstruction<TwoRegisterInstruction>(getIndex).registerA
addInstruction(getIndex + 1,
"invoke-static { v$getRegister }, $EXTENSION_CLASS_DESCRIPTOR->filterCb11(Ljava/util/List;)V"
addInstruction(
getIndex + 1,
"invoke-static { v$getRegister }, $EXTENSION_CLASS_DESCRIPTOR->filterCb11(Ljava/util/List;)V",
)
}
}

View file

@ -1,15 +1,16 @@
package app.revanced.patches.cricbuzz.ads
import app.revanced.patcher.fingerprint
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val userStateSwitchFingerprint = fingerprint {
internal val BytecodePatchContext.userStateSwitchMethod by gettingFirstMethodDeclaratively("key.user.state", "NA") {
opcodes(Opcode.SPARSE_SWITCH)
strings("key.user.state", "NA")
}
internal val cb11ConstructorFingerprint = fingerprint {
parameters(
internal val BytecodePatchContext.cb11ConstructorMethod by gettingFirstMethodDeclaratively {
definingClass("CB11Details;")
parameterTypes(
"Ljava/lang/String;",
"Ljava/lang/String;",
"Ljava/lang/String;",
@ -19,15 +20,11 @@ internal val cb11ConstructorFingerprint = fingerprint {
"Z",
"Ljava/lang/String;",
"Ljava/lang/String;",
"L"
"L",
)
custom { _, classDef ->
classDef.endsWith("CB11Details;")
}
}
internal val getBottomBarFingerprint = fingerprint {
custom { method, classDef ->
method.name == "getBottomBar" && classDef.endsWith("HomeMenu;")
}
}
internal val BytecodePatchContext.getBottomBarMethod by gettingFirstMethodDeclaratively {
name("getBottomBar")
definingClass("HomeMenu;")
}

View file

@ -1,9 +1,5 @@
package app.revanced.patches.cricbuzz.misc.extension
import app.revanced.patches.shared.misc.extension.extensionHook
import app.revanced.patches.shared.misc.extension.activityOnCreateExtensionHook
internal val applicationInitHook = extensionHook {
custom { method, classDef ->
method.name == "onCreate" && classDef.endsWith("/NyitoActivity;")
}
}
internal val applicationInitHook = activityOnCreateExtensionHook("/NyitoActivity;")

View file

@ -1,7 +1,10 @@
package app.revanced.patches.crunchyroll.ads
import app.revanced.patcher.fingerprint
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.patch.BytecodePatchContext
internal val videoUrlReadyToStringFingerprint = fingerprint {
strings("VideoUrlReady(url=", ", enableAds=")
internal val BytecodePatchContext.videoUrlReadyToStringMethodMatch by composingFirstMethod {
instructions("VideoUrlReady(url="(), ", enableAds="())
}

View file

@ -1,8 +1,8 @@
package app.revanced.patches.crunchyroll.ads
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
@ -13,34 +13,35 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@Suppress("unused")
val hideAdsPatch = bytecodePatch(
name = "Hide ads"
) {
val hideAdsPatch = bytecodePatch("Hide ads") {
compatibleWith("com.crunchyroll.crunchyroid")
execute {
apply {
// Get obfuscated "enableAds" field from toString method.
val enableAdsField = videoUrlReadyToStringFingerprint.let {
val strIndex = videoUrlReadyToStringFingerprint.stringMatches!!.last().index
val fieldIndex = it.method.indexOfFirstInstruction(strIndex, Opcode.IGET_BOOLEAN)
it.method.getInstruction<ReferenceInstruction>(fieldIndex).getReference<FieldReference>()!!
val enableAdsField = videoUrlReadyToStringMethodMatch.method.let {
val stringIndex = videoUrlReadyToStringMethodMatch[-1]
val fieldIndex = it.indexOfFirstInstruction(stringIndex, Opcode.IGET_BOOLEAN)
it.getInstruction<ReferenceInstruction>(fieldIndex).getReference<FieldReference>()!!
}
// Remove final access flag on field.
videoUrlReadyToStringFingerprint.classDef.fields
videoUrlReadyToStringMethodMatch.classDef.fields
.first { it.name == enableAdsField.name }
.removeFlags(AccessFlags.FINAL)
// Override enableAds field in non-default constructor.
val constructor = videoUrlReadyToStringFingerprint.classDef.methods.first {
val constructor = videoUrlReadyToStringMethodMatch.classDef.methods.first {
AccessFlags.CONSTRUCTOR.isSet(it.accessFlags) && it.parameters.isNotEmpty()
}
constructor.addInstructions(
constructor.instructions.count() - 1,
"""
move-object/from16 v0, p0
const/4 v1, 0x0
iput-boolean v1, v0, $enableAdsField
""")
""",
)
}
}
}

View file

@ -1,19 +1,19 @@
package app.revanced.patches.disneyplus.ads
package app.revanced.patches.disneyplus
import app.revanced.patcher.fingerprint
import app.revanced.patcher.definingClass
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
internal val insertionGetPointsFingerprint = fingerprint {
returns("Ljava/util/List")
custom { method, _ ->
method.name == "getPoints" &&
method.definingClass == "Lcom/dss/sdk/internal/media/Insertion;"
}
internal val BytecodePatchContext.insertionGetPointsMethod by gettingFirstMethodDeclaratively {
name("getPoints")
definingClass("Lcom/dss/sdk/internal/media/Insertion;")
returnType("Ljava/util/List")
}
internal val insertionGetRangesFingerprint = fingerprint {
returns("Ljava/util/List")
custom { method, _ ->
method.name == "getRanges" &&
method.definingClass == "Lcom/dss/sdk/internal/media/Insertion;"
}
internal val BytecodePatchContext.insertionGetRangesMethod by gettingFirstMethodDeclaratively {
name("getRanges")
definingClass("Lcom/dss/sdk/internal/media/Insertion;")
returnType("Ljava/util/List")
}

View file

@ -1,6 +1,6 @@
package app.revanced.patches.disneyplus.ads
package app.revanced.patches.disneyplus
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused")
@ -10,11 +10,11 @@ val skipAdsPatch = bytecodePatch(
) {
compatibleWith("com.disney.disneyplus")
execute {
arrayOf(insertionGetPointsFingerprint, insertionGetRangesFingerprint).forEach {
it.method.addInstructions(
0,
"""
apply {
arrayOf(insertionGetPointsMethod, insertionGetRangesMethod).forEach {
it.addInstructions(
0,
"""
new-instance v0, Ljava/util/ArrayList;
invoke-direct {v0}, Ljava/util/ArrayList;-><init>()V
return-object v0

View file

@ -1,18 +1,18 @@
package app.revanced.patches.duolingo.ad
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.classDef
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.bytecodePatch
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
@Suppress("unused")
val disableAdsPatch = bytecodePatch(
"Disable ads",
) {
val disableAdsPatch = bytecodePatch("Disable ads") {
// 6.55.3 and higher can show ads after each exercise.
compatibleWith("com.duolingo"("6.54.5"))
execute {
apply {
// Couple approaches to remove ads exist:
//
// MonetizationDebugSettings has a boolean value for "disableAds".
@ -20,10 +20,9 @@ val disableAdsPatch = bytecodePatch(
// SharedPreferences has a debug boolean value with key "disable_ads", which maps to "DebugCategory.DISABLE_ADS".
//
// MonetizationDebugSettings seems to be the most general setting to work fine.
initializeMonetizationDebugSettingsFingerprint
.match(monetizationDebugSettingsToStringFingerprint.classDef)
.method.apply {
val insertIndex = initializeMonetizationDebugSettingsFingerprint.patternMatch!!.startIndex
monetizationDebugSettingsToStringMethod.immutableClassDef.initializeMonetizationDebugSettingsMethodMatch.let {
it.method.apply {
val insertIndex = it[0]
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
addInstructions(
@ -31,5 +30,6 @@ val disableAdsPatch = bytecodePatch(
"const/4 v$register, 0x1",
)
}
}
}
}

View file

@ -1,17 +1,19 @@
package app.revanced.patches.duolingo.ad
import app.revanced.patcher.fingerprint
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.ClassDef
internal val initializeMonetizationDebugSettingsFingerprint = fingerprint {
internal val ClassDef.initializeMonetizationDebugSettingsMethodMatch by ClassDefComposing.composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
returns("V")
// Parameters have not been reliable for fingerprinting between versions.
returnType("V")
// Parameters have not been reliable for matching between versions.
opcodes(Opcode.IPUT_BOOLEAN)
}
internal val monetizationDebugSettingsToStringFingerprint = fingerprint {
strings("MonetizationDebugSettings(") // Partial string match.
custom { method, _ -> method.name == "toString" }
}
internal val BytecodePatchContext.monetizationDebugSettingsToStringMethod by gettingFirstMethodDeclaratively {
name("toString")
instructions(string("MonetizationDebugSettings(", String::contains))
}

View file

@ -1,7 +1,9 @@
package app.revanced.patches.duolingo.debug
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.classDef
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -9,27 +11,20 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val enableDebugMenuPatch = bytecodePatch(
name = "Enable debug menu",
use = false
use = false,
) {
compatibleWith("com.duolingo")
execute {
apply {
// It seems all categories are allowed on release. Force this on anyway.
debugCategoryAllowOnReleaseBuildsFingerprint.method.returnEarly(true)
debugCategoryAllowOnReleaseBuildsMethod.returnEarly(true)
// Change build config debug build flag.
buildConfigProviderConstructorFingerprint.match(
buildConfigProviderToStringFingerprint.classDef
).let {
val index = it.patternMatch!!.startIndex
buildConfigProviderToStringMethod.immutableClassDef.buildConfigProviderConstructorMethodMatch.let {
val index = it[0]
it.method.apply {
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstruction(
index + 1,
"const/4 v$register, 0x1"
)
}
val register = it.method.getInstruction<OneRegisterInstruction>(index).registerA
it.method.addInstruction(index + 1, "const/4 v$register, 0x1")
}
}
}

View file

@ -1,28 +1,27 @@
package app.revanced.patches.duolingo.debug
import app.revanced.patcher.fingerprint
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.ClassDef
internal val debugCategoryAllowOnReleaseBuildsFingerprint = fingerprint {
returns("Z")
parameters()
custom { method, classDef ->
method.name == "getAllowOnReleaseBuilds" && classDef.type == "Lcom/duolingo/debug/DebugCategory;"
}
internal val BytecodePatchContext.debugCategoryAllowOnReleaseBuildsMethod by gettingFirstMethodDeclaratively {
name("getAllowOnReleaseBuilds")
definingClass("Lcom/duolingo/debug/DebugCategory;")
returnType("Z")
parameterTypes()
}
internal val buildConfigProviderConstructorFingerprint = fingerprint {
internal val ClassDef.buildConfigProviderConstructorMethodMatch by ClassDefComposing.composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters()
parameterTypes()
opcodes(Opcode.CONST_4)
}
internal val buildConfigProviderToStringFingerprint = fingerprint {
parameters()
returns("Ljava/lang/String;")
strings("BuildConfigProvider(") // Partial string match.
custom { method, _ ->
method.name == "toString"
}
internal val BytecodePatchContext.buildConfigProviderToStringMethod by gettingFirstMethodDeclaratively {
name("toString")
parameterTypes()
returnType("Ljava/lang/String;")
instructions(string("BuildConfigProvider(", String::contains))
}

View file

@ -1,21 +1,25 @@
package app.revanced.patches.duolingo.energy
import app.revanced.patcher.fingerprint
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.ClassDef
/**
* Matches the class found in [energyConfigToStringFingerprint].
*/
internal val initializeEnergyConfigFingerprint = fingerprint {
internal val ClassDef.initializeEnergyConfigMethodMatch by ClassDefComposing.composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
opcodes(Opcode.RETURN_VOID)
}
// Class name currently is not obfuscated but it may be in the future.
internal val energyConfigToStringFingerprint = fingerprint {
parameters()
returns("Ljava/lang/String;")
strings("EnergyConfig(", "maxEnergy=") // Partial string matches.
custom { method, _ -> method.name == "toString" }
// Class name currently is not obfuscated, but it may be in the future.
internal val BytecodePatchContext.energyConfigToStringMethod by gettingFirstMethodDeclaratively {
name("toString")
parameterTypes()
returnType("Ljava/lang/String;")
instructions(
predicates=unorderedAllOf(
"EnergyConfig("(String::contains),
"maxEnergy="(String::contains),
)
)
}

View file

@ -1,31 +1,32 @@
package app.revanced.patches.duolingo.energy
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.classDef
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.findFieldFromToString
@Suppress("unused")
val skipEnergyRechargeAdsPatch = bytecodePatch(
name = "Skip energy recharge ads",
description = "Skips watching ads to recharge energy."
description = "Skips watching ads to recharge energy.",
) {
compatibleWith("com.duolingo")
execute {
initializeEnergyConfigFingerprint
.match(energyConfigToStringFingerprint.classDef)
.method.apply {
val energyField = energyConfigToStringFingerprint.method
.findFieldFromToString("energy=")
val insertIndex = initializeEnergyConfigFingerprint.patternMatch!!.startIndex
apply {
energyConfigToStringMethod.immutableClassDef.initializeEnergyConfigMethodMatch.let {
it.method.apply {
val energyField = energyConfigToStringMethod.findFieldFromToString("energy=")
val insertIndex = it[0]
addInstructions(
insertIndex,
"""
const/16 v0, 99
iput v0, p0, $energyField
"""
const/16 v0, 99
iput v0, p0, $energyField
""",
)
}
}
}
}

View file

@ -1,13 +1,14 @@
package app.revanced.patches.facebook.ads.mainfeed
import app.revanced.patcher.fingerprint
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val baseModelMapperFingerprint = fingerprint {
internal val BytecodePatchContext.baseModelMapperMethod by gettingFirstImmutableMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Lcom/facebook/graphql/modelutil/BaseModelWithTree;")
parameters("Ljava/lang/Class", "I", "I")
returnType("Lcom/facebook/graphql/modelutil/BaseModelWithTree;")
parameterTypes("Ljava/lang/Class", "I", "I")
opcodes(
Opcode.SGET_OBJECT,
Opcode.INVOKE_STATIC,
@ -16,11 +17,11 @@ internal val baseModelMapperFingerprint = fingerprint {
Opcode.IF_EQ,
)
}
internal val getSponsoredDataModelTemplateFingerprint = fingerprint {
internal val BytecodePatchContext.getSponsoredDataModelTemplateMethod by gettingFirstImmutableMethodDeclaratively {
definingClass("Lcom/facebook/graphql/model/GraphQLFBMultiAdsFeedUnit;")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
parameters()
returnType("L")
parameterTypes()
opcodes(
Opcode.CONST,
Opcode.CONST,
@ -28,14 +29,10 @@ internal val getSponsoredDataModelTemplateFingerprint = fingerprint {
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT,
)
custom { _, classDef ->
classDef.type == "Lcom/facebook/graphql/model/GraphQLFBMultiAdsFeedUnit;"
}
}
internal val getStoryVisibilityFingerprint = fingerprint {
internal val BytecodePatchContext.getStoryVisibilityMethodMatch by composingFirstMethod("This should not be called for base class object") {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Ljava/lang/String;")
returnType("Ljava/lang/String;")
opcodes(
Opcode.INSTANCE_OF,
Opcode.IF_NEZ,
@ -45,5 +42,4 @@ internal val getStoryVisibilityFingerprint = fingerprint {
Opcode.IF_NEZ,
Opcode.CONST,
)
strings("This should not be called for base class object")
}

View file

@ -1,9 +1,9 @@
package app.revanced.patches.facebook.ads.mainfeed
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod.Companion.toMutable
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.addInstructionsWithLabels
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
@ -11,14 +11,11 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
@Suppress("unused")
val hideSponsoredStoriesPatch = bytecodePatch(
name = "Hide 'Sponsored Stories'",
) {
val hideSponsoredStoriesPatch = bytecodePatch("Hide 'Sponsored Stories'") {
compatibleWith("com.facebook.katana"("490.0.0.63.82"))
execute {
val sponsoredDataModelTemplateMethod = getSponsoredDataModelTemplateFingerprint.originalMethod
val baseModelMapperMethod = baseModelMapperFingerprint.originalMethod
apply {
val sponsoredDataModelTemplateMethod = getSponsoredDataModelTemplateMethod
val baseModelWithTreeType = baseModelMapperMethod.returnType
val graphQlStoryClassDescriptor = "Lcom/facebook/graphql/model/GraphQLStory;"
@ -28,7 +25,7 @@ val hideSponsoredStoriesPatch = bytecodePatch(
// could change in future version, we need to extract them and call the base implementation directly.
val getSponsoredDataHelperMethod = ImmutableMethod(
getStoryVisibilityFingerprint.originalClassDef.type,
getStoryVisibilityMethodMatch.immutableClassDef.type,
"getSponsoredData",
listOf(ImmutableMethodParameter(graphQlStoryClassDescriptor, null, null)),
baseModelWithTreeType,
@ -62,12 +59,12 @@ val hideSponsoredStoriesPatch = bytecodePatch(
)
}
getStoryVisibilityFingerprint.classDef.methods.add(getSponsoredDataHelperMethod)
getStoryVisibilityMethodMatch.classDef.methods.add(getSponsoredDataHelperMethod)
// Check if the parameter type is GraphQLStory and if sponsoredDataModelGetter returns a non-null value.
// If so, hide the story by setting the visibility to StoryVisibility.GONE.
getStoryVisibilityFingerprint.method.addInstructionsWithLabels(
getStoryVisibilityFingerprint.patternMatch!!.startIndex,
getStoryVisibilityMethodMatch.method.addInstructionsWithLabels(
getStoryVisibilityMethodMatch[0],
"""
instance-of v0, p0, $graphQlStoryClassDescriptor
if-eqz v0, :resume_normal

View file

@ -1,24 +1,24 @@
package app.revanced.patches.facebook.ads.story
import app.revanced.patcher.fingerprint
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue
internal val adsInsertionFingerprint = fieldFingerprint(
fieldValue = "AdBucketDataSourceUtil\$attemptAdsInsertion\$1",
internal val BytecodePatchContext.adsInsertionMethod by runMethod(
fieldValue = $$"AdBucketDataSourceUtil$attemptAdsInsertion$1",
)
internal val fetchMoreAdsFingerprint = fieldFingerprint(
fieldValue = "AdBucketDataSourceUtil\$attemptFetchMoreAds\$1",
internal val BytecodePatchContext.fetchMoreAdsMethod by runMethod(
fieldValue = $$"AdBucketDataSourceUtil$attemptFetchMoreAds$1",
)
internal fun fieldFingerprint(fieldValue: String) = fingerprint {
returns("V")
parameters()
custom { method, classDef ->
method.name == "run" &&
classDef.fields.any any@{ field ->
if (field.name != "__redex_internal_original_name") return@any false
(field.initialValue as? StringEncodedValue)?.value == fieldValue
}
internal fun runMethod(fieldValue: String) = gettingFirstMethodDeclaratively {
name("run")
returnType("V")
parameterTypes()
custom {
immutableClassDef.anyField {
name == "__redex_internal_original_name" && (initialValue as? StringEncodedValue)?.value == fieldValue
}
}
}

View file

@ -1,7 +1,8 @@
package app.revanced.patches.facebook.ads.story
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val hideStoryAdsPatch = bytecodePatch(
@ -10,12 +11,7 @@ val hideStoryAdsPatch = bytecodePatch(
) {
compatibleWith("com.facebook.katana")
execute {
setOf(
fetchMoreAdsFingerprint,
adsInsertionFingerprint,
).forEach { fingerprint ->
fingerprint.method.replaceInstruction(0, "return-void")
}
apply {
setOf(fetchMoreAdsMethod, adsInsertionMethod).forEach(MutableMethod::returnEarly)
}
}

View file

@ -1,24 +1,18 @@
package app.revanced.patches.finanzonline.detection.bootloader
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val bootloaderDetectionPatch = bytecodePatch(
val removeBootloaderDetectionPatch = bytecodePatch(
name = "Remove bootloader detection",
description = "Removes the check for an unlocked bootloader.",
) {
compatibleWith("at.gv.bmf.bmf2go")
execute {
setOf(createKeyFingerprint, bootStateFingerprint).forEach { fingerprint ->
fingerprint.method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""",
)
apply {
setOf(createKeyMethod, bootStateMethod).forEach { method ->
method.returnEarly(true)
}
}
}

View file

@ -1,13 +1,17 @@
package app.revanced.patches.finanzonline.detection.bootloader
import com.android.tools.smali.dexlib2.Opcode
import app.revanced.patcher.accessFlags
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.opcodes
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.Opcode
// Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.AttestationHelper#isBootStateOk (3.0.1)
internal val bootStateFingerprint = fingerprint {
internal val BytecodePatchContext.bootStateMethod by gettingFirstMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC)
returns("Z")
returnType("Z")
opcodes(
Opcode.INVOKE_DIRECT,
Opcode.MOVE_RESULT_OBJECT,
@ -25,13 +29,18 @@ internal val bootStateFingerprint = fingerprint {
Opcode.IF_NE,
Opcode.GOTO,
Opcode.MOVE,
Opcode.RETURN
Opcode.RETURN,
)
}
// Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.AttestationHelper#createKey (3.0.1)
internal val createKeyFingerprint = fingerprint {
internal val BytecodePatchContext.createKeyMethod by gettingFirstMethodDeclaratively(
"attestation",
"SHA-256",
"random",
"EC",
"AndroidKeyStore",
) {
accessFlags(AccessFlags.PUBLIC)
returns("Z")
strings("attestation", "SHA-256", "random", "EC", "AndroidKeyStore")
}
returnType("Z")
}

View file

@ -1,14 +1,15 @@
package app.revanced.patches.finanzonline.detection.root
import com.android.tools.smali.dexlib2.Opcode
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.Opcode
// Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.RootDetection#isRooted (3.0.1)
internal val rootDetectionFingerprint = fingerprint {
internal val BytecodePatchContext.rootDetectionMethod by gettingFirstMethodDeclaratively {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("L")
parameters("L")
returnType("L")
parameterTypes("L")
opcodes(
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,

View file

@ -1,19 +1,17 @@
package app.revanced.patches.finanzonline.detection.root
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.PATCH_DESCRIPTION_REMOVE_ROOT_DETECTION
import app.revanced.patches.shared.PATCH_NAME_REMOVE_ROOT_DETECTION
@Suppress("unused")
val rootDetectionPatch = bytecodePatch(
name = PATCH_NAME_REMOVE_ROOT_DETECTION,
description = PATCH_DESCRIPTION_REMOVE_ROOT_DETECTION,
val removeRootDetectionPatch = bytecodePatch(
name = "Remove root detection",
description = "Removes the check for root permissions and unlocked bootloader.",
) {
compatibleWith("at.gv.bmf.bmf2go")
execute {
rootDetectionFingerprint.method.addInstructions(
apply {
rootDetectionMethod.addInstructions(
0,
"""
sget-object v0, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;

View file

@ -1,12 +1,17 @@
package app.revanced.patches.fotmob.ads
import app.revanced.patcher.accessFlags
import app.revanced.patcher.definingClass
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
internal val shouldDisplayAdsMethod = fingerprint {
internal val BytecodePatchContext.shouldDisplayAdsMethod by gettingFirstMethodDeclaratively {
name("shouldDisplayAds")
definingClass("AdsService;")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
custom { method, classDef ->
method.name == "shouldDisplayAds" && classDef.type.endsWith("AdsService;")
}
returnType("Z")
}

View file

@ -9,7 +9,7 @@ val hideAdsPatch = bytecodePatch(
) {
compatibleWith("com.mobilefootie.wc2010")
execute {
shouldDisplayAdsMethod.method.returnEarly(false)
apply {
shouldDisplayAdsMethod.returnEarly(false)
}
}
}

View file

@ -1,7 +1,7 @@
package app.revanced.patches.googlenews.customtabs
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -12,9 +12,9 @@ val enableCustomTabsPatch = bytecodePatch(
) {
compatibleWith("com.google.android.apps.magazines")
execute {
launchCustomTabFingerprint.method.apply {
val checkIndex = launchCustomTabFingerprint.patternMatch!!.endIndex + 1
apply {
launchCustomTabMethodMatch.method.apply {
val checkIndex = launchCustomTabMethodMatch[-1] + 1
val register = getInstruction<OneRegisterInstruction>(checkIndex).registerA
replaceInstruction(checkIndex, "const/4 v$register, 0x1")

View file

@ -1,10 +1,15 @@
package app.revanced.patches.googlenews.customtabs
import app.revanced.patcher.fingerprint
import app.revanced.patcher.accessFlags
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.definingClass
import app.revanced.patcher.opcodes
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val launchCustomTabFingerprint = fingerprint {
internal val BytecodePatchContext.launchCustomTabMethodMatch by composingFirstMethod {
definingClass("CustomTabsArticleLauncher;")
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
opcodes(
Opcode.IPUT_OBJECT,
@ -13,5 +18,4 @@ internal val launchCustomTabFingerprint = fingerprint {
Opcode.CONST_4,
Opcode.IPUT_BOOLEAN,
)
custom { _, classDef -> classDef.endsWith("CustomTabsArticleLauncher;") }
}

View file

@ -1,5 +1,11 @@
package app.revanced.patches.googlenews.misc.extension.hooks
import app.revanced.patcher.definingClass
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.name
import app.revanced.patcher.opcodes
import app.revanced.patches.shared.misc.extension.extensionHook
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
@ -10,19 +16,20 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private var getApplicationContextIndex = -1
internal val startActivityInitHook = extensionHook(
insertIndexResolver = { method ->
getApplicationContextIndex = method.indexOfFirstInstructionOrThrow {
getInsertIndex = {
getApplicationContextIndex = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "getApplicationContext"
}
getApplicationContextIndex + 2 // Below the move-result-object instruction.
},
contextRegisterResolver = { method ->
val moveResultInstruction = method.implementation!!.instructions.elementAt(getApplicationContextIndex + 1)
as OneRegisterInstruction
getContextRegister = {
val moveResultInstruction = instructions.elementAt(getApplicationContextIndex + 1) as OneRegisterInstruction
"v${moveResultInstruction.registerA}"
},
) {
name("onCreate")
definingClass("/StartActivity;")
opcodes(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
@ -35,7 +42,4 @@ internal val startActivityInitHook = extensionHook(
Opcode.INVOKE_VIRTUAL, // Calls startActivity.getApplicationContext().
Opcode.MOVE_RESULT_OBJECT,
)
custom { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;")
}
}

View file

@ -1,9 +1,11 @@
package app.revanced.patches.googlenews.misc.gms
import app.revanced.patcher.fingerprint
import app.revanced.patcher.definingClass
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
internal val magazinesActivityOnCreateFingerprint = fingerprint {
custom { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;")
}
internal val BytecodePatchContext.magazinesActivityOnCreateMethod by gettingFirstMethodDeclaratively {
name("onCreate")
definingClass("/StartActivity;")
}

View file

@ -1,23 +1,23 @@
package app.revanced.patches.googlenews.misc.gms
import app.revanced.patcher.extensions.methodReference
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.Option
import app.revanced.patches.googlenews.misc.extension.extensionPatch
import app.revanced.patches.googlenews.misc.gms.Constants.MAGAZINES_PACKAGE_NAME
import app.revanced.patches.googlenews.misc.gms.Constants.REVANCED_MAGAZINES_PACKAGE_NAME
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
import app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Suppress("unused")
val gmsCoreSupportPatch = gmsCoreSupportPatch(
fromPackageName = MAGAZINES_PACKAGE_NAME,
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
mainActivityOnCreateFingerprintToInsertIndex = magazinesActivityOnCreateFingerprint to {
getMainActivityOnCreateMethodToGetInsertIndex = BytecodePatchContext::magazinesActivityOnCreateMethod::get to {
val getApplicationContextIndex =
magazinesActivityOnCreateFingerprint.method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "getApplicationContext"
magazinesActivityOnCreateMethod.indexOfFirstInstructionOrThrow {
methodReference?.name == "getApplicationContext"
}
getApplicationContextIndex + 2 // Below the move-result-object instruction.

View file

@ -7,12 +7,12 @@ import app.revanced.util.returnEarly
val enableDCIMFoldersBackupControlPatch = bytecodePatch(
name = "Enable DCIM folders backup control",
description = "Disables always on backup for the Camera and other DCIM folders, allowing you to control backup " +
"for each folder individually. This will make the app default to having no folders backed up.",
"for each folder individually. This will make the app default to having no folders backed up.",
use = false,
) {
compatibleWith("com.google.android.apps.photos")
execute {
isDCIMFolderBackupControlDisabled.method.returnEarly(false)
apply {
isDCIMFolderBackupControlMethod.returnEarly(false)
}
}

View file

@ -1,8 +1,9 @@
package app.revanced.patches.googlephotos.misc.backup
import app.revanced.patcher.fingerprint
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
internal val isDCIMFolderBackupControlDisabled = fingerprint {
returns("Z")
strings("/dcim", "/mars_files/")
internal val BytecodePatchContext.isDCIMFolderBackupControlMethod by gettingFirstMethodDeclaratively("/dcim", "/mars_files/") {
returnType("Z")
}

View file

@ -1,5 +1,11 @@
package app.revanced.patches.googlephotos.misc.extension
import app.revanced.patcher.definingClass
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.name
import app.revanced.patcher.opcodes
import app.revanced.patches.shared.misc.extension.extensionHook
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
@ -10,19 +16,20 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private var getApplicationContextIndex = -1
internal val homeActivityInitHook = extensionHook(
insertIndexResolver = { method ->
getApplicationContextIndex = method.indexOfFirstInstructionOrThrow {
getInsertIndex = {
getApplicationContextIndex = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "getApplicationContext"
}
getApplicationContextIndex + 2 // Below the move-result-object instruction.
},
contextRegisterResolver = { method ->
val moveResultInstruction = method.implementation!!.instructions.elementAt(getApplicationContextIndex + 1)
as OneRegisterInstruction
getContextRegister = {
val moveResultInstruction = instructions.elementAt(getApplicationContextIndex + 1) as OneRegisterInstruction
"v${moveResultInstruction.registerA}"
},
) {
name("onCreate")
definingClass("/HomeActivity;")
opcodes(
Opcode.CONST_STRING,
Opcode.INVOKE_STATIC,
@ -31,7 +38,4 @@ internal val homeActivityInitHook = extensionHook(
Opcode.INVOKE_VIRTUAL, // Calls getApplicationContext().
Opcode.MOVE_RESULT_OBJECT,
)
custom { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;")
}
}

View file

@ -1,7 +1,6 @@
package app.revanced.patches.googlephotos.misc.features
import app.revanced.patcher.fingerprint
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.patch.BytecodePatchContext
internal val initializeFeaturesEnumFingerprint = fingerprint {
strings("com.google.android.apps.photos.NEXUS_PRELOAD")
}
internal val BytecodePatchContext.initializeFeaturesEnumMethod by gettingFirstMethodDeclaratively("com.google.android.apps.photos.NEXUS_PRELOAD")

View file

@ -1,7 +1,7 @@
package app.revanced.patches.googlephotos.misc.features
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.instructions
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.stringsOption
import app.revanced.util.getReference
@ -19,18 +19,16 @@ val spoofFeaturesPatch = bytecodePatch(
dependsOn(spoofBuildInfoPatch)
val featuresToEnable by stringsOption(
key = "featuresToEnable",
default = listOf(
"com.google.android.apps.photos.NEXUS_PRELOAD",
"com.google.android.apps.photos.nexus_preload",
),
title = "Features to enable",
name = "Features to enable",
description = "Google Pixel exclusive features to enable. Features up to Pixel XL enable the unlimited storage feature.",
required = true,
)
val featuresToDisable by stringsOption(
key = "featuresToDisable",
default = listOf(
"com.google.android.apps.photos.PIXEL_2017_PRELOAD",
"com.google.android.apps.photos.PIXEL_2018_PRELOAD",
@ -49,20 +47,20 @@ val spoofFeaturesPatch = bytecodePatch(
"com.google.android.feature.PIXEL_2025_MIDYEAR_EXPERIENCE",
"com.google.android.feature.PIXEL_2025_EXPERIENCE",
),
title = "Features to disable",
name = "Features to disable",
description = "Google Pixel exclusive features to disable." +
"Features after Pixel XL may have to be disabled for unlimited storage depending on the device.",
required = true,
)
execute {
apply {
@Suppress("NAME_SHADOWING")
val featuresToEnable = featuresToEnable!!.toSet()
@Suppress("NAME_SHADOWING")
val featuresToDisable = featuresToDisable!!.toSet()
initializeFeaturesEnumFingerprint.method.apply {
initializeFeaturesEnumMethod.apply {
instructions.filter { it.opcode == Opcode.CONST_STRING }.forEach {
val feature = it.getReference<StringReference>()!!.string

View file

@ -1,9 +1,11 @@
package app.revanced.patches.googlephotos.misc.gms
import app.revanced.patcher.fingerprint
import app.revanced.patcher.definingClass
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
internal val homeActivityOnCreateFingerprint = fingerprint {
custom { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;")
}
internal val BytecodePatchContext.homeActivityOnCreateMethod by gettingFirstMethodDeclaratively {
name("onCreate")
definingClass("/HomeActivity;")
}

View file

@ -1,5 +1,7 @@
package app.revanced.patches.googlephotos.misc.gms
import app.revanced.patcher.extensions.methodReference
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.Option
import app.revanced.patches.googlephotos.misc.extension.extensionPatch
import app.revanced.patches.googlephotos.misc.gms.Constants.PHOTOS_PACKAGE_NAME
@ -8,14 +10,16 @@ import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
@Suppress("unused")
val gmsCoreSupportPatch = gmsCoreSupportPatch(
fromPackageName = PHOTOS_PACKAGE_NAME,
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
mainActivityOnCreateFingerprintToInsertIndex = homeActivityOnCreateFingerprint to {
val index = homeActivityOnCreateFingerprint.method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "getApplicationContext"
getMainActivityOnCreateMethodToGetInsertIndex = BytecodePatchContext::homeActivityOnCreateMethod::get to {
val index = homeActivityOnCreateMethod.indexOfFirstInstructionOrThrow {
methodReference?.name == "getApplicationContext"
}
// Below the move-result-object instruction,

View file

@ -1,8 +0,0 @@
package app.revanced.patches.googlephotos.misc.preferences
import app.revanced.patcher.fingerprint
internal val backupPreferencesFingerprint = fingerprint {
returns("Lcom/google/android/apps/photos/backup/data/BackupPreferences;")
strings("backup_prefs_had_backup_only_when_charging_enabled")
}

View file

@ -1,30 +0,0 @@
package app.revanced.patches.googlephotos.misc.preferences
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Deprecated("This patch no longer works and this code will soon be deleted")
@Suppress("unused")
val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch(
description = "Restores a hidden toggle to only run backups when the device is charging."
) {
compatibleWith("com.google.android.apps.photos"("7.11.0.705590205"))
execute {
// Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true.
backupPreferencesFingerprint.let {
it.method.apply {
val index = indexOfFirstInstructionOrThrow(
it.stringMatches!!.first().index,
Opcode.MOVE_RESULT
)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstruction(index + 1, "const/4 v$register, 0x1")
}
}
}
}

View file

@ -1,12 +1,10 @@
package app.revanced.patches.googlerecorder.restrictions
import app.revanced.patcher.fingerprint
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
internal val onApplicationCreateFingerprint = fingerprint {
strings("com.google.android.feature.PIXEL_2017_EXPERIENCE")
custom { method, classDef ->
if (method.name != "onCreate") return@custom false
classDef.endsWith("RecorderApplication;")
}
internal val BytecodePatchContext.onApplicationCreateMethodMatch by composingFirstMethod {
name("onCreate")
definingClass("RecorderApplication;")
instructions("com.google.android.feature.PIXEL_2017_EXPERIENCE"())
}

View file

@ -1,8 +1,8 @@
package app.revanced.patches.googlerecorder.restrictions
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.removeInstructions
import app.revanced.patcher.patch.bytecodePatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -13,10 +13,10 @@ val removeDeviceRestrictionsPatch = bytecodePatch(
) {
compatibleWith("com.google.android.apps.recorder")
execute {
val featureStringIndex = onApplicationCreateFingerprint.stringMatches!!.first().index
apply {
val featureStringIndex = onApplicationCreateMethodMatch[0]
onApplicationCreateFingerprint.method.apply {
onApplicationCreateMethodMatch.method.apply {
// Remove check for device restrictions.
removeInstructions(featureStringIndex - 2, 5)

View file

@ -1,21 +1,13 @@
package app.revanced.patches.hexeditor.ad
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val disableAdsPatch = bytecodePatch(
name = "Disable ads",
) {
val disableAdsPatch = bytecodePatch("Disable ads") {
compatibleWith("com.myprog.hexedit")
execute {
primaryAdsFingerprint.method.replaceInstructions(
0,
"""
const/4 v0, 0x1
return v0
""",
)
apply {
primaryAdsMethod.returnEarly(true)
}
}

View file

@ -1,9 +1,11 @@
package app.revanced.patches.hexeditor.ad
import app.revanced.patcher.fingerprint
import app.revanced.patcher.definingClass
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
internal val primaryAdsFingerprint = fingerprint {
custom { method, classDef ->
classDef.endsWith("PreferencesHelper;") && method.name == "isAdsDisabled"
}
internal val BytecodePatchContext.primaryAdsMethod by gettingFirstMethodDeclaratively {
name("isAdsDisabled")
definingClass("PreferencesHelper;")
}

View file

@ -1,8 +1,11 @@
package app.revanced.patches.iconpackstudio.misc.pro
import app.revanced.patcher.fingerprint
import app.revanced.patcher.definingClass
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
internal val checkProFingerprint = fingerprint {
returns("Z")
custom { _, classDef -> classDef.endsWith("IPSPurchaseRepository;") }
}
internal val BytecodePatchContext.checkProMethod by gettingFirstMethodDeclaratively {
definingClass("IPSPurchaseRepository;")
returnType("Z")
}

View file

@ -1,21 +1,13 @@
package app.revanced.patches.iconpackstudio.misc.pro
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val unlockProPatch = bytecodePatch(
name = "Unlock pro",
) {
val unlockProPatch = bytecodePatch("Unlock pro") {
compatibleWith("ginlemon.iconpackstudio"("2.2 build 016"))
execute {
checkProFingerprint.method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""",
)
apply {
checkProMethod.returnEarly(true)
}
}

View file

@ -1,22 +1,19 @@
package app.revanced.patches.idaustria.detection.deviceintegrity
import app.revanced.patcher.fingerprint
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
internal val isDeviceBootloaderOpenFingerprint = fingerprint {
internal val BytecodePatchContext.isDeviceBootloaderOpenMethod by gettingFirstMethodDeclaratively {
name("isDeviceBootloaderOpen")
definingClass("/DeviceIntegrityCheckProviderImpl;")
accessFlags(AccessFlags.PUBLIC)
returns("Ljava/lang/Object;")
custom { method, classDef ->
method.name == "isDeviceBootloaderOpen" &&
classDef.endsWith("/DeviceIntegrityCheckProviderImpl;")
}
returnType("Ljava/lang/Object;")
}
internal val isDeviceRootedFingerprint = fingerprint {
internal val BytecodePatchContext.isDeviceRootedMethod by gettingFirstMethodDeclaratively {
name("isDeviceRooted")
definingClass("/DeviceIntegrityCheckProviderImpl;")
accessFlags(AccessFlags.PUBLIC)
returns("Z")
custom { method, classDef ->
method.name == "isDeviceRooted" &&
classDef.endsWith("/DeviceIntegrityCheckProviderImpl;")
}
returnType("Z")
}

View file

@ -1,10 +1,9 @@
package app.revanced.patches.idaustria.detection.deviceintegrity
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val removeDeviceIntegrityChecksPatch = bytecodePatch(
name = "Remove device integrity checks",
@ -12,19 +11,17 @@ val removeDeviceIntegrityChecksPatch = bytecodePatch(
) {
compatibleWith("at.gv.oe.app")
execute {
isDeviceRootedFingerprint.method.returnEarly(false)
apply {
isDeviceRootedMethod.returnEarly(false)
isDeviceBootloaderOpenFingerprint.method.apply {
addInstructions(
0,
"""
const/4 v0, 0x0
invoke-static { v0 }, Lkotlin/coroutines/jvm/internal/Boxing;->boxBoolean(Z)Ljava/lang/Boolean;
move-result-object v0
return-object v0
"""
)
}
isDeviceBootloaderOpenMethod.addInstructions(
0,
"""
const/4 v0, 0x0
invoke-static { v0 }, Lkotlin/coroutines/jvm/internal/Boxing;->boxBoolean(Z)Ljava/lang/Boolean;
move-result-object v0
return-object v0
""",
)
}
}

View file

@ -1,10 +0,0 @@
package app.revanced.patches.idaustria.detection.root
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.idaustria.detection.deviceintegrity.removeDeviceIntegrityChecksPatch
@Deprecated("Patch was superseded", ReplaceWith("removeDeviceIntegrityChecksPatch"))
@Suppress("unused")
val rootDetectionPatch = bytecodePatch {
dependsOn(removeDeviceIntegrityChecksPatch)
}

View file

@ -1,13 +1,13 @@
package app.revanced.patches.idaustria.detection.signature
import app.revanced.patcher.fingerprint
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
internal val spoofSignatureFingerprint = fingerprint {
internal val BytecodePatchContext.spoofSignatureMethod by gettingFirstMethodDeclaratively {
name("getPubKey")
definingClass("/SL2Step1Task;")
accessFlags(AccessFlags.PRIVATE)
returns("L")
parameters("L")
custom { method, classDef ->
classDef.endsWith("/SL2Step1Task;") && method.name == "getPubKey"
}
returnType("L")
parameterTypes("L")
}

View file

@ -10,7 +10,7 @@ val spoofSignaturePatch = bytecodePatch(
) {
compatibleWith("at.gv.oe.app")
execute {
apply {
val expectedSignature =
"OpenSSLRSAPublicKey{modulus=ac3e6fd6050aa7e0d6010ae58190404cd89a56935b44f6fee" +
"067c149768320026e10b24799a1339e414605e448e3f264444a327b9ae292be2b62ad567dd1800dbed4a88f718a33dc6db6b" +
@ -24,6 +24,6 @@ val spoofSignaturePatch = bytecodePatch(
"77ef1be61b2c01ebdabddcbf53cc4b6fd9a3c445606ee77b3758162c80ad8f8137b3c6864e92db904807dcb2be9d7717dd21" +
"bf42c121d620ddfb7914f7a95c713d9e1c1b7bdb4a03d618e40cf7e9e235c0b5687e03b7ab3,publicExponent=10001}"
spoofSignatureFingerprint.method.returnEarly(expectedSignature)
spoofSignatureMethod.returnEarly(expectedSignature)
}
}

View file

@ -1,8 +1,11 @@
package app.revanced.patches.inshorts.ad
import app.revanced.patcher.fingerprint
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
internal val inshortsAdsFingerprint = fingerprint {
returns("V")
strings("GoogleAdLoader", "exception in requestAd")
internal val BytecodePatchContext.inshortsAdsMethod by gettingFirstMethodDeclaratively(
"GoogleAdLoader", "exception in requestAd"
) {
returnType("V")
}

View file

@ -1,20 +1,13 @@
package app.revanced.patches.inshorts.ad
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val hideAdsPatch = bytecodePatch(
name = "Hide ads",
) {
val hideAdsPatch = bytecodePatch("Hide ads") {
compatibleWith("com.nis.app")
execute {
inshortsAdsFingerprint.method.addInstruction(
0,
"""
return-void
""",
)
apply {
inshortsAdsMethod.returnEarly()
}
}

View file

@ -1,16 +1,14 @@
package app.revanced.patches.instagram.ads
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.meta.ads.adInjectorFingerprint
import app.revanced.patches.meta.ads.adInjectorMethod
import app.revanced.util.returnEarly
@Suppress("unused")
val hideAdsPatch = bytecodePatch(
name = "Hide ads",
) {
val hideAdsPatch = bytecodePatch("Hide ads") {
compatibleWith("com.instagram.android")
execute {
adInjectorFingerprint.method.returnEarly(false)
apply {
adInjectorMethod.returnEarly(false)
}
}

View file

@ -1,20 +1,12 @@
package app.revanced.patches.instagram.feed
import app.revanced.patcher.fingerprint
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.patch.BytecodePatchContext
internal val mainFeedRequestClassFingerprint = fingerprint {
strings("Request{mReason=", ", mInstanceNumber=")
}
internal val BytecodePatchContext.mainFeedRequestClassMethod by gettingFirstMethodDeclaratively(
"Request{mReason=", ", mInstanceNumber="
)
context(BytecodePatchContext)
internal val initMainFeedRequestFingerprint get() = fingerprint {
custom { method, classDef ->
method.name == "<init>" &&
classDef == mainFeedRequestClassFingerprint.classDef
}
}
internal val mainFeedHeaderMapFinderFingerprint = fingerprint {
strings("pagination_source", "FEED_REQUEST_SENT")
}
internal val BytecodePatchContext.mainFeedHeaderMapFinderMethod by gettingFirstMethodDeclaratively(
"pagination_source", "FEED_REQUEST_SENT"
)

View file

@ -1,7 +1,12 @@
package app.revanced.patches.instagram.feed
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.classDef
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.fieldReference
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.firstMethodDeclaratively
import app.revanced.patcher.immutableClassDef
import app.revanced.patcher.name
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.instagram.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
@ -12,16 +17,16 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/instagram/feed/LimitFeedToFollowedProfiles;"
@Suppress("unused")
val limitFeedToFollowedProfiles = bytecodePatch(
val limitFeedToFollowedProfilesPatch = bytecodePatch(
name = "Limit feed to followed profiles",
description = "Filters the home feed to display only content from profiles you follow.",
use = false
use = false,
) {
compatibleWith("com.instagram.android")
dependsOn(sharedExtensionPatch)
execute {
apply {
/**
* Since the header field is obfuscated and there is no easy way to identify it among all the class fields,
* an additional method is fingerprinted.
@ -29,19 +34,19 @@ val limitFeedToFollowedProfiles = bytecodePatch(
*/
val mainFeedRequestHeaderFieldName: String
with(mainFeedHeaderMapFinderFingerprint.method) {
mainFeedHeaderMapFinderMethod.apply {
mainFeedRequestHeaderFieldName = indexOfFirstInstructionOrThrow {
getReference<FieldReference>().let { ref ->
ref?.type == "Ljava/util/Map;" &&
ref.definingClass == mainFeedRequestClassFingerprint.classDef.toString()
}
val reference = fieldReference
reference?.type == "Ljava/util/Map;" &&
reference.definingClass == mainFeedRequestClassMethod.classDef.type
}.let { instructionIndex ->
getInstruction(instructionIndex).getReference<FieldReference>()!!.name
}
}
initMainFeedRequestFingerprint.method.apply {
mainFeedRequestClassMethod.immutableClassDef.firstMethodDeclaratively {
name("<init>")
}.apply {
// Finds the instruction where the map is being initialized in the constructor
val getHeaderIndex = indexOfFirstInstructionOrThrow {
getReference<FieldReference>().let {
@ -57,7 +62,7 @@ val limitFeedToFollowedProfiles = bytecodePatch(
"""
invoke-static { v$paramHeaderRegister }, $EXTENSION_CLASS_DESCRIPTOR->setFollowingHeader(Ljava/util/Map;)Ljava/util/Map;
move-result-object v$paramHeaderRegister
"""
""",
)
}
}

View file

@ -12,12 +12,12 @@ val anonymousStoryViewingPatch = bytecodePatch(
Your view will not appear in the story viewers list.
Note: Since no data is sent, a story you have already viewed may appear as new on another device.
""".trimIndentMultiline(),
use = false
use = false,
) {
compatibleWith("com.instagram.android")
execute {
apply {
// Prevent the hashmap of the seen media to be filled
setMediaSeenHashmapFingerprint.method.returnEarly()
setMediaSeenHashmapMethod.returnEarly()
}
}

View file

@ -1,9 +1,11 @@
package app.revanced.patches.instagram.ghost.story
import app.revanced.patcher.fingerprint
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
internal val setMediaSeenHashmapFingerprint = fingerprint {
parameters()
returns("V")
strings("media/seen/")
internal val BytecodePatchContext.setMediaSeenHashmapMethod by gettingFirstMethodDeclaratively("media/seen/") {
parameterTypes()
returnType("V")
}

View file

@ -1,11 +1,12 @@
package app.revanced.patches.instagram.hide.explore
import app.revanced.patcher.fingerprint
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
internal const val EXPLORE_KEY_TO_BE_HIDDEN = "sectional_items"
internal val exploreResponseJsonParserFingerprint = fingerprint {
strings(EXPLORE_KEY_TO_BE_HIDDEN, "ExploreTopicalFeedResponse")
custom { method, _ -> method.name == "parseFromJson" }
internal val BytecodePatchContext.exploreResponseJsonParserMethodMatch by composingFirstMethod("ExploreTopicalFeedResponse") {
name("parseFromJson")
instructions("sectional_items"())
}

View file

@ -1,7 +1,9 @@
package app.revanced.patches.instagram.hide.explore
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.instagram.shared.replaceStringWithBogus
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val hideExploreFeedPatch = bytecodePatch(
@ -11,7 +13,12 @@ val hideExploreFeedPatch = bytecodePatch(
) {
compatibleWith("com.instagram.android")
execute {
exploreResponseJsonParserFingerprint.replaceStringWithBogus(EXPLORE_KEY_TO_BE_HIDDEN)
apply {
exploreResponseJsonParserMethodMatch.method.apply {
val targetStringIndex = exploreResponseJsonParserMethodMatch[0]
val targetStringRegister = getInstruction<OneRegisterInstruction>(targetStringIndex).registerA
replaceInstruction(targetStringIndex, "const-string v$targetStringRegister, \"BOGUS\"")
}
}
}

View file

@ -1,9 +1,12 @@
package app.revanced.patches.instagram.hide.highlightsTray
import app.revanced.patcher.fingerprint
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.patch.BytecodePatchContext
internal const val TARGET_STRING = "highlights_tray"
internal val highlightsUrlBuilderFingerprint = fingerprint {
strings(TARGET_STRING,"X-IG-Accept-Hint")
internal val BytecodePatchContext.highlightsUrlBuilderMethodMatch by composingFirstMethod("X-IG-Accept-Hint") {
instructions(TARGET_STRING())
}

Some files were not shown because too many files have changed in this diff Show more