some fixes & update to newest patcher changes

This commit is contained in:
oSumAtrIX
2026-01-29 23:17:33 +01:00
parent e928128a94
commit acac6e960c
194 changed files with 654 additions and 600 deletions

View File

@@ -366,7 +366,7 @@ val addResourcesPatch = resourcePatch(
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

@@ -12,7 +12,7 @@ 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 BytecodePatchContext.getDailyMeasurementCountMethod by gettingFirstMutableMethodDeclaratively("dailyMeasurementCount") {
accessFlags(AccessFlags.PRIVATE)

View File

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

View File

@@ -19,7 +19,7 @@ val hideAdsPatch = bytecodePatch("Hide ads") {
apply {
// Get obfuscated "enableAds" field from toString method.
val enableAdsField = videoUrlReadyToStringMethodMatch.method.apply {
val stringIndex = videoUrlReadyToStringMethodMatch.indices.last()
val stringIndex = videoUrlReadyToStringMethodMatch[-1]
val fieldIndex = indexOfFirstInstruction(stringIndex, Opcode.IGET_BOOLEAN)
getInstruction<ReferenceInstruction>(fieldIndex).getReference<FieldReference>()!!

View File

@@ -3,6 +3,7 @@ package app.revanced.patches.duolingo.ad
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
@@ -19,16 +20,16 @@ val disableAdsPatch = bytecodePatch("Disable ads") {
// 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.
initializeMonetizationDebugSettingsMethodMatch.match(
monetizationDebugSettingsToStringMethod.classDef,
).method.apply {
val insertIndex = initializeMonetizationDebugSettingsMethodMatch.indices.first()
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
monetizationDebugSettingsToStringMethod.immutableClassDef.initializeMonetizationDebugSettingsMethodMatch.let {
it.method.apply {
val insertIndex = it[0]
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
addInstructions(
insertIndex,
"const/4 v$register, 0x1",
)
addInstructions(
insertIndex,
"const/4 v$register, 0x1",
)
}
}
}
}

View File

@@ -4,11 +4,12 @@ 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 initializeMonetizationDebugSettingsMethodMatch = firstMethodComposite {
internal val ClassDef.initializeMonetizationDebugSettingsMethodMatch by ClassDefComposing.composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
returnType("V")
// Parameters have not been reliable for fingerprinting between versions.
// Parameters have not been reliable for matching between versions.
opcodes(Opcode.IPUT_BOOLEAN)
}

View File

@@ -3,6 +3,7 @@ package app.revanced.patches.duolingo.debug
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
@@ -19,10 +20,8 @@ val enableDebugMenuPatch = bytecodePatch(
debugCategoryAllowOnReleaseBuildsMethod.returnEarly(true)
// Change build config debug build flag.
buildConfigProviderConstructorMethodMatch.match(
buildConfigProviderToStringMethod.classDef,
).let {
val index = it.indices.first()
buildConfigProviderToStringMethod.immutableClassDef.buildConfigProviderConstructorMethodMatch.let {
val index = it[0]
val register = it.method.getInstruction<OneRegisterInstruction>(index).registerA
it.method.addInstruction(index + 1, "const/4 v$register, 0x1")

View File

@@ -4,6 +4,7 @@ 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 BytecodePatchContext.debugCategoryAllowOnReleaseBuildsMethod by gettingFirstMutableMethodDeclaratively {
name("getAllowOnReleaseBuilds")
@@ -12,8 +13,7 @@ internal val BytecodePatchContext.debugCategoryAllowOnReleaseBuildsMethod by get
parameterTypes()
}
internal val buildConfigProviderConstructorMethodMatch = firstMethodComposite {
internal val ClassDef.buildConfigProviderConstructorMethodMatch by ClassDefComposing.composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameterTypes()
opcodes(Opcode.CONST_4)

View File

@@ -4,8 +4,9 @@ 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 initializeEnergyConfigMethodMatch = firstMethodComposite {
internal val ClassDef.initializeEnergyConfigMethodMatch by ClassDefComposing.composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
opcodes(Opcode.RETURN_VOID)
}

View File

@@ -2,6 +2,7 @@ package app.revanced.patches.duolingo.energy
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
@@ -13,17 +14,19 @@ val skipEnergyRechargeAdsPatch = bytecodePatch(
compatibleWith("com.duolingo")
apply {
initializeEnergyConfigMethodMatch.match(energyConfigToStringMethod.classDef).method.apply {
val energyField = energyConfigToStringMethod.findFieldFromToString("energy=")
val insertIndex = initializeEnergyConfigMethodMatch.indices.first() // TODO
energyConfigToStringMethod.immutableClassDef.initializeEnergyConfigMethodMatch.let {
it.method.apply {
val energyField = energyConfigToStringMethod.findFieldFromToString("energy=")
val insertIndex = it[0]
addInstructions(
insertIndex,
"""
addInstructions(
insertIndex,
"""
const/16 v0, 99
iput v0, p0, $energyField
""",
)
)
}
}
}
}

View File

@@ -30,7 +30,7 @@ internal val BytecodePatchContext.getSponsoredDataModelTemplateMethod by getting
Opcode.RETURN_OBJECT,
)
}
internal val getStoryVisibilityMethodMatch = firstMethodComposite("This should not be called for base class object") {
internal val BytecodePatchContext.getStoryVisibilityMethodMatch by composingFirstMethod("This should not be called for base class object") {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returnType("Ljava/lang/String;")
opcodes(

View File

@@ -64,7 +64,7 @@ val hideSponsoredStoriesPatch = bytecodePatch("Hide 'Sponsored Stories'") {
// 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.
getStoryVisibilityMethodMatch.method.addInstructionsWithLabels(
getStoryVisibilityMethodMatch.indices.first(),
getStoryVisibilityMethodMatch[0],
"""
instance-of v0, p0, $graphQlStoryClassDescriptor
if-eqz v0, :resume_normal

View File

@@ -14,7 +14,7 @@ val enableCustomTabsPatch = bytecodePatch(
apply {
launchCustomTabMethodMatch.method.apply {
val checkIndex = launchCustomTabMethodMatch.indices.last() + 1
val checkIndex = launchCustomTabMethodMatch[-1] + 1
val register = getInstruction<OneRegisterInstruction>(checkIndex).registerA
replaceInstruction(checkIndex, "const/4 v$register, 0x1")

View File

@@ -1,13 +1,14 @@
package app.revanced.patches.googlenews.customtabs
import app.revanced.patcher.accessFlags
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.definingClass
import app.revanced.patcher.firstMethodComposite
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 launchCustomTabMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.launchCustomTabMethodMatch by composingFirstMethod {
definingClass { endsWith("CustomTabsArticleLauncher;") }
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
opcodes(

View File

@@ -1,8 +1,9 @@
package app.revanced.patches.googlerecorder.restrictions
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
internal val onApplicationCreateMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.onApplicationCreateMethodMatch by composingFirstMethod {
name("onCreate")
definingClass { endsWith("RecorderApplication") }
instructions("com.google.android.feature.PIXEL_2017_EXPERIENCE"())

View File

@@ -14,7 +14,7 @@ val removeDeviceRestrictionsPatch = bytecodePatch(
compatibleWith("com.google.android.apps.recorder")
apply {
val featureStringIndex = onApplicationCreateMethodMatch.indices.first()
val featureStringIndex = onApplicationCreateMethodMatch[0]
onApplicationCreateMethodMatch.method.apply {
// Remove check for device restrictions.

View File

@@ -1,11 +1,12 @@
package app.revanced.patches.instagram.hide.explore
import app.revanced.patcher.firstMethodComposite
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 val exploreResponseJsonParserMethodMatch = firstMethodComposite("ExploreTopicalFeedResponse") {
internal val BytecodePatchContext.exploreResponseJsonParserMethodMatch by composingFirstMethod("ExploreTopicalFeedResponse") {
name("parseFromJson")
instructions("sectional_items"())
}

View File

@@ -15,7 +15,7 @@ val hideExploreFeedPatch = bytecodePatch(
apply {
exploreResponseJsonParserMethodMatch.method.apply {
val targetStringIndex = exploreResponseJsonParserMethodMatch.indices.first()
val targetStringIndex = exploreResponseJsonParserMethodMatch[0]
val targetStringRegister = getInstruction<OneRegisterInstruction>(targetStringIndex).registerA
replaceInstruction(targetStringIndex, "const-string v$targetStringRegister, \"BOGUS\"")

View File

@@ -1,9 +1,10 @@
package app.revanced.patches.instagram.hide.stories
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val getOrCreateAvatarViewMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.getOrCreateAvatarViewMethodMatch by composingFirstMethod {
definingClass("Lcom/instagram/reels/ui/views/reelavatar/RecyclerReelAvatarView;")
parameterTypes()
returnType("L")

View File

@@ -13,7 +13,7 @@ val hideStoriesFromHomePatch = bytecodePatch(
apply {
getOrCreateAvatarViewMethodMatch.let {
val addStoryEndIndex = it.indices.last()
val addStoryEndIndex = it[-1]
// Remove addView of Story.
it.method.removeInstruction(addStoryEndIndex)

View File

@@ -1,8 +1,9 @@
package app.revanced.patches.instagram.hide.suggestions
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.unorderedAllOf
internal val FEED_ITEM_KEYS_TO_BE_HIDDEN = arrayOf(
@@ -15,7 +16,7 @@ internal val FEED_ITEM_KEYS_TO_BE_HIDDEN = arrayOf(
"suggested_users",
)
internal val feedItemParseFromJsonMethodMatch = firstMethodComposite("FeedItem") {
internal val BytecodePatchContext.feedItemParseFromJsonMethodMatch by composingFirstMethod("FeedItem") {
instructions(
predicates = unorderedAllOf(
predicates =

View File

@@ -15,7 +15,7 @@ val hideSuggestedContentPatch = bytecodePatch(
apply {
feedItemParseFromJsonMethodMatch.method.apply {
feedItemParseFromJsonMethodMatch.indices.forEach { targetStringIndex ->
feedItemParseFromJsonMethodMatch.indices[0].forEach { targetStringIndex ->
val targetStringRegister = getInstruction<OneRegisterInstruction>(targetStringIndex).registerA
replaceInstruction(targetStringIndex, "const-string v$targetStringRegister, \"BOGUS\"")

View File

@@ -21,7 +21,7 @@ val enableDeveloperMenuPatch = bytecodePatch(
apply {
clearNotificationReceiverMethodMatch.let {
val stringIndex = it.indices.first()
val stringIndex = it[0]
it.immutableMethod.indexOfFirstInstructionReversedOrThrow(stringIndex) {
val reference = methodReference

View File

@@ -1,8 +1,9 @@
package app.revanced.patches.instagram.misc.devmenu
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
internal val clearNotificationReceiverMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.clearNotificationReceiverMethodMatch by composingFirstMethod {
name("onReceive")
definingClass("Lcom/instagram/notifications/push/ClearNotificationReceiver;")
instructions("NOTIFICATION_DISMISSED"())

View File

@@ -1,13 +1,14 @@
package app.revanced.patches.instagram.misc.links
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
internal const val TARGET_STRING = "Tracking.ARG_CLICK_SOURCE"
internal val inAppBrowserFunctionMethodMatch = firstMethodComposite("TrackingInfo.ARG_MODULE_NAME") {
internal val BytecodePatchContext.inAppBrowserFunctionMethodMatch by composingFirstMethod("TrackingInfo.ARG_MODULE_NAME") {
instructions(TARGET_STRING())
returnType("Z")
}

View File

@@ -23,7 +23,7 @@ val openLinksExternallyPatch = bytecodePatch(
apply {
inAppBrowserFunctionMethodMatch.let {
val stringMatchIndex = it.indices.first()
val stringMatchIndex = it[0]
it.method.apply {
val urlResultObjIndex = indexOfFirstInstructionOrThrow(

View File

@@ -18,7 +18,7 @@ internal fun BytecodePatchContext.editShareLinksPatch(block: MutableMethod.(inde
for (match in methodsToPatch) {
match.method.apply {
val putSharingUrlIndex = indexOfFirstInstruction(
match.indices.first(),
match[0],
Opcode.IPUT_OBJECT,
)

View File

@@ -1,32 +1,33 @@
package app.revanced.patches.instagram.misc.share
import app.revanced.patcher.firstMethodComposite
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 val permalinkResponseJsonParserMethodMatch = firstMethodComposite(
internal val BytecodePatchContext.permalinkResponseJsonParserMethodMatch by composingFirstMethod(
"PermalinkResponse",
) {
name("parseFromJson")
instructions("permalink"())
}
internal val storyUrlResponseJsonParserMethodMatch = firstMethodComposite(
internal val BytecodePatchContext.storyUrlResponseJsonParserMethodMatch by composingFirstMethod(
"StoryItemUrlResponse",
) {
name("parseFromJson")
instructions("story_item_to_share_url"())
}
internal val profileUrlResponseJsonParserMethodMatch = firstMethodComposite(
internal val BytecodePatchContext.profileUrlResponseJsonParserMethodMatch by composingFirstMethod(
"ProfileUrlResponse",
) {
name("parseFromJson")
instructions("profile_to_share_url"())
}
internal val liveUrlResponseJsonParserMethodMatch = firstMethodComposite(
internal val BytecodePatchContext.liveUrlResponseJsonParserMethodMatch by composingFirstMethod(
"LiveItemLinkUrlResponse",
) {
name("parseFromJson")

View File

@@ -11,7 +11,7 @@ val disableVersionCheckPatch = bytecodePatch(
compatibleWith("com.adobe.lrmobile"("9.3.0"))
apply {
val igetIndex = refreshRemoteConfigurationMethodMatch.indices.last()
val igetIndex = refreshRemoteConfigurationMethodMatch[-1]
// This value represents the server command to clear all version restrictions.
val statusForceReset = "-0x2"

View File

@@ -1,12 +1,13 @@
package app.revanced.patches.lightroom.misc.version
import app.revanced.patcher.accessFlags
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.composingFirstMethod
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 refreshRemoteConfigurationMethodMatch = firstMethodComposite(
internal val BytecodePatchContext.refreshRemoteConfigurationMethodMatch by composingFirstMethod(
"com.adobe.lrmobile.denylisted_version_set_key",
"com.adobe.lrmobile.app_min_version_key",
) {

View File

@@ -1,20 +1,21 @@
package app.revanced.patches.messenger.metaai
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val getMobileConfigBoolMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.getMobileConfigBoolMethodMatch by composingFirstMethod {
parameterTypes("J")
returnType("Z")
opcodes(Opcode.RETURN)
custom { "Lcom/facebook/mobileconfig/factory/MobileConfigUnsafeContext;" in immutableClassDef.interfaces }
}
internal val metaAIKillSwitchCheckMethodMatch = firstMethodComposite("SearchAiagentImplementationsKillSwitch") {
internal val BytecodePatchContext.metaAIKillSwitchCheckMethodMatch by composingFirstMethod("SearchAiagentImplementationsKillSwitch") {
opcodes(Opcode.CONST_WIDE)
}
internal val extensionMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.extensionMethodMatch by composingFirstMethod {
name(EXTENSION_METHOD_NAME)
definingClass(EXTENSION_CLASS_DESCRIPTOR)
instructions("REPLACED_BY_PATCH"())

View File

@@ -22,7 +22,7 @@ val removeMetaAIPatch = bytecodePatch(
apply {
getMobileConfigBoolMethodMatch.let {
val returnIndex = it.indices.first()
val returnIndex = it[0]
val returnRegister = it.method.getInstruction<OneRegisterInstruction>(returnIndex).registerA
it.method.addInstructions(
@@ -36,13 +36,13 @@ val removeMetaAIPatch = bytecodePatch(
// Extract the common starting digits of Meta AI flag IDs from a flag found in code.
val relevantDigits = metaAIKillSwitchCheckMethodMatch.let {
it.method.getInstruction<WideLiteralInstruction>(it.indices.first()).wideLiteral
it.method.getInstruction<WideLiteralInstruction>(it[0]).wideLiteral
}.toString().substring(0, 7)
// Replace placeholder in the extension method.
extensionMethodMatch.let {
it.method.replaceInstruction(
it.indices.first(),
it[0],
"const-string v1, \"$relevantDigits\"",
)
}

View File

@@ -1,9 +1,10 @@
package app.revanced.patches.mifitness.misc.locale
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val syncBluetoothLanguageMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.syncBluetoothLanguageMethodMatch by composingFirstMethod {
name("syncBluetoothLanguage")
definingClass("Lcom/xiaomi/fitness/devicesettings/DeviceSettingsSyncer")
opcodes(Opcode.MOVE_RESULT_OBJECT)

View File

@@ -17,7 +17,7 @@ val forceEnglishLocalePatch = bytecodePatch(
apply {
syncBluetoothLanguageMethodMatch.let {
val resolvePhoneLocaleInstruction = it.indices.first()
val resolvePhoneLocaleInstruction = it[0]
val registerIndexToUpdate = it.method.getInstruction<OneRegisterInstruction>(resolvePhoneLocaleInstruction).registerA
it.method.replaceInstruction(

View File

@@ -1,10 +1,11 @@
package app.revanced.patches.music.ad.video
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.opcodes
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val showVideoAdsParentMethodMatch = firstMethodComposite(
internal val BytecodePatchContext.showVideoAdsParentMethodMatch by composingFirstMethod(
"maybeRegenerateCpnAndStatsClient called unexpectedly, but no error.",
) {
opcodes(

View File

@@ -37,7 +37,7 @@ val hideMusicVideoAdsPatch = bytecodePatch(
)
navigate(showVideoAdsParentMethodMatch.immutableMethod)
.to(showVideoAdsParentMethodMatch.indices.first() + 1)
.to(showVideoAdsParentMethodMatch[0] + 1)
.stop()
.addInstructions(
0,

View File

@@ -1,10 +1,11 @@
package app.revanced.patches.music.interaction.permanentrepeat
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 repeatTrackMethodMatch = firstMethodComposite("w_st") {
internal val BytecodePatchContext.repeatTrackMethodMatch by composingFirstMethod("w_st") {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes("L", "L")
@@ -14,6 +15,6 @@ internal val repeatTrackMethodMatch = firstMethodComposite("w_st") {
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_NEZ
Opcode.IF_NEZ,
)
}

View File

@@ -40,7 +40,7 @@ val permanentRepeatPatch = bytecodePatch(
)
repeatTrackMethodMatch.method.apply {
val startIndex = repeatTrackMethodMatch.indices.last()
val startIndex = repeatTrackMethodMatch[-1]
val repeatIndex = startIndex + 1
// Start index is at a branch, but the same

View File

@@ -18,7 +18,7 @@ internal val BytecodePatchContext.playerOverlayChipMethod by gettingFirstMutable
instructions(playerOverlayChip())
}
internal val historyMenuItemMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.historyMenuItemMethodMatch by composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes("Landroid/view/Menu;")
@@ -32,7 +32,7 @@ internal val historyMenuItemMethodMatch = firstMethodComposite {
}
}
internal val historyMenuItemOfflineTabMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.historyMenuItemOfflineTabMethodMatch by composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes("Landroid/view/Menu;")
@@ -40,9 +40,9 @@ internal val historyMenuItemOfflineTabMethodMatch = firstMethodComposite {
Opcode.INVOKE_INTERFACE,
Opcode.RETURN_VOID,
)
custom {
instructions.matchIndexed("literals", items = unorderedAllOf(historyMenuItem(), offlineSettingsMenuItem()))
}
val match = indexedMatcher(items = unorderedAllOf(historyMenuItem(), offlineSettingsMenuItem()))
custom { match(instructions) }
}
internal val BytecodePatchContext.searchActionViewMethod by gettingFirstMutableMethodDeclaratively {

View File

@@ -74,7 +74,7 @@ val hideButtonsPatch = bytecodePatch(
historyMenuItemOfflineTabMethodMatch,
).forEach { match ->
match.method.apply {
val targetIndex = match.indices.first()
val targetIndex = match[0]
val targetRegister = getInstruction<FiveRegisterInstruction>(targetIndex).registerD
addInstructions(

View File

@@ -1,10 +1,11 @@
package app.revanced.patches.music.layout.compactheader
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.Opcode
internal val chipCloudMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.chipCloudMethodMatch by composingFirstMethod {
returnType("V")
opcodes(
Opcode.CONST,

View File

@@ -45,7 +45,7 @@ val hideCategoryBarPatch = bytecodePatch(
chipCloud = ResourceType.LAYOUT["chip_cloud"]
chipCloudMethodMatch.let {
val targetIndex = it.indices.last()
val targetIndex = it[-1]
val targetRegister = it.method.getInstruction<OneRegisterInstruction>(targetIndex).registerA
it.method.addInstruction(

View File

@@ -50,8 +50,8 @@ val changeMiniplayerColorPatch = bytecodePatch(
SwitchPreference("revanced_music_change_miniplayer_color"),
)
switchToggleColorMethodMatch.match(miniPlayerConstructorMethodMatch.immutableClassDef).let {
val relativeIndex = it.indices.last() + 1
miniPlayerConstructorMethodMatch.immutableClassDef.switchToggleColorMethodMatch.let {
val relativeIndex = it[-1] + 1
val invokeVirtualIndex = it.method.indexOfFirstInstructionOrThrow(
relativeIndex,

View File

@@ -7,7 +7,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.ClassDef
internal val miniPlayerConstructorMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.miniPlayerConstructorMethodMatch by composingFirstMethod {
returnType("V")
instructions(
ResourceType.ID("mpp_player_bottom_sheet"),
@@ -18,7 +18,7 @@ internal val miniPlayerConstructorMethodMatch = firstMethodComposite {
/**
* Matches to the class found in [miniPlayerConstructorMethodMatch].
*/
internal val switchToggleColorMethodMatch = firstMethodComposite {
internal val ClassDef.switchToggleColorMethodMatch by ClassDefComposing.composingFirstMethod {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returnType("V")
parameterTypes("L", "J")

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.music.layout.navigationbar
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
@@ -9,7 +10,7 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val tabLayoutTextMethodMatch = firstMethodComposite("FEmusic_search") {
internal val BytecodePatchContext.tabLayoutTextMethodMatch by composingFirstMethod("FEmusic_search") {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")
parameterTypes("L")

View File

@@ -100,7 +100,7 @@ val navigationBarPatch = bytecodePatch(
)
// Set navigation enum and hide navigation buttons.
val enumIndex = tabLayoutTextMethodMatch.indices.first() + 3
val enumIndex = tabLayoutTextMethodMatch[0] + 3
val enumRegister = getInstruction<OneRegisterInstruction>(enumIndex).registerA
val insertEnumIndex = indexOfFirstInstructionOrThrow(Opcode.AND_INT_LIT8) - 2

View File

@@ -5,9 +5,9 @@ import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val hideGetPremiumMethodMatch = firstMethodComposite(
internal val BytecodePatchContext.hideGetPremiumMethodMatch by composingFirstMethod(
"FEmusic_history",
"FEmusic_offline"
"FEmusic_offline",
) {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("V")

View File

@@ -41,7 +41,7 @@ val hideGetMusicPremiumPatch = bytecodePatch(
)
hideGetPremiumMethodMatch.let {
val insertIndex = it.indices.last()
val insertIndex = it[-1]
it.method.apply {
val setVisibilityInstruction = getInstruction<FiveRegisterInstruction>(insertIndex)

View File

@@ -14,7 +14,7 @@ import com.android.tools.smali.dexlib2.Opcode val BytecodePatchContext.jwPlayerC
accessFlags(AccessFlags.PUBLIC)
}
internal val screenMapperMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.screenMapperMethodMatch by composingFirstMethod {
name("map")
definingClass("Lnl/nu/android/bff/data/mappers/ScreenMapper;")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
@@ -27,7 +27,7 @@ internal val screenMapperMethodMatch = firstMethodComposite {
)
}
internal val nextPageRepositoryImplMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.nextPageRepositoryImplMethodMatch by composingFirstMethod {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returnType("Lnl/nu/android/bff/domain/models/Page;")
parameterTypes("Lnl/nu/performance/api/client/PacResponse;", "Ljava/lang/String;")

View File

@@ -27,7 +27,7 @@ val hideAdsPatch = bytecodePatch(
// Filter injected content from API calls out of lists.
arrayOf(screenMapperMethodMatch, nextPageRepositoryImplMethodMatch).forEach { match ->
// Index of instruction moving result of BlockPage;->getBlocks(...).
val moveGetBlocksResultObjectIndex = match.indices.first()
val moveGetBlocksResultObjectIndex = match[0]
val moveInstruction = match.method.getInstruction<OneRegisterInstruction>(moveGetBlocksResultObjectIndex)
val listRegister = moveInstruction.registerA

View File

@@ -1,11 +1,12 @@
package app.revanced.patches.photomath.detection.signature
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val checkSignatureMethodMatch = firstMethodComposite("SHA") {
internal val BytecodePatchContext.checkSignatureMethodMatch by composingFirstMethod("SHA") {
instructions(
Opcode.CONST_STRING(),
Opcode.INVOKE_STATIC(),

View File

@@ -10,7 +10,7 @@ val signatureDetectionPatch = bytecodePatch(
description = "Disables detection of incorrect signature.",
) {
apply {
val replacementIndex = checkSignatureMethodMatch.indices.last()
val replacementIndex = checkSignatureMethodMatch[-1]
val checkRegister = checkSignatureMethodMatch.method.getInstruction<OneRegisterInstruction>(replacementIndex)
.registerA

View File

@@ -4,14 +4,14 @@ import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val enableSplitTunnelingUiMethodMatch = firstMethodComposite("currentModeAppNames") {
internal val BytecodePatchContext.enableSplitTunnelingUiMethodMatch by composingFirstMethod("currentModeAppNames") {
opcodes(
Opcode.MOVE_OBJECT,
Opcode.MOVE_FROM16,
Opcode.INVOKE_DIRECT_RANGE
Opcode.INVOKE_DIRECT_RANGE,
)
}
internal val BytecodePatchContext.initializeSplitTunnelingSettingsUIMethod by gettingFirstMutableMethodDeclaratively {
name("applyRestrictions")
}
}

View File

@@ -15,7 +15,7 @@ val unlockSplitTunnelingPatch = bytecodePatch("Unlock split tunneling") {
apply {
enableSplitTunnelingUiMethodMatch.let {
val registerIndex = it.indices.last() - 1
val registerIndex = it[-1] - 1
val register = it.method.getInstruction<OneRegisterInstruction>(registerIndex).registerA
it.method.replaceInstruction(registerIndex, "const/4 v$register, 0x0")
}

View File

@@ -15,7 +15,7 @@ fun spoofClientPatch(
redirectUri: String,
block: BytecodePatchBuilder.(Option<String>) -> Unit = {},
) = bytecodePatch(
name = "Spoof client", // TODO
name = "Spoof client",
description = "Restores functionality of the app by using custom client ID.",
) {
block(
@@ -23,9 +23,9 @@ fun spoofClientPatch(
name = "OAuth client ID",
values = null,
description = "The Reddit OAuth client ID. " +
"You can get your client ID from https://www.reddit.com/prefs/apps. " +
"The application type has to be \"Installed app\" " +
"and the redirect URI has to be set to \"$redirectUri\".",
"You can get your client ID from https://www.reddit.com/prefs/apps. " +
"The application type has to be \"Installed app\" " +
"and the redirect URI has to be set to \"$redirectUri\".",
required = true,
),
)

View File

@@ -1,16 +1,17 @@
package app.revanced.patches.reddit.customclients.baconreader.api
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.patch.BytecodePatchContext
internal val getAuthorizationUrlMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.getAuthorizationUrlMethodMatch by composingFirstMethod {
instructions("client_id=zACVn0dSFGdWqQ"())
}
internal val requestTokenMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.requestTokenMethodMatch by composingFirstMethod {
instructions(
"zACVn0dSFGdWqQ"(),
"kDm2tYpu9DqyWFFyPlNcXGEni4k"(String::contains)
"kDm2tYpu9DqyWFFyPlNcXGEni4k"(String::contains),
)
}
}

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.reddit.customclients.baconreader.api
import app.revanced.patcher.MatchBuilder
import app.revanced.patcher.Match
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patches.reddit.customclients.spoofClientPatch
@@ -11,7 +11,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://baconreader.com/au
dependsOn(
// Redirects from SSL to WWW domain are bugged causing auth problems.
// Manually rewrite the URLs to fix this.
replaceStringPatch("ssl.reddit.com", "www.reddit.com")
replaceStringPatch("ssl.reddit.com", "www.reddit.com"),
)
compatibleWith(
@@ -22,8 +22,8 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://baconreader.com/au
val clientId by clientIdOption
apply {
fun MatchBuilder.patch(replacementString: String) {
val clientIdIndex = indices.first()
fun Match.patch(replacementString: String) {
val clientIdIndex = get(0)
val clientIdRegister = method.getInstruction<OneRegisterInstruction>(clientIdIndex).registerA
method.replaceInstruction(

View File

@@ -1,12 +1,13 @@
package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.patch.BytecodePatchContext
internal val downloadAudioMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.downloadAudioMethodMatch by composingFirstMethod {
instructions(
"/DASH_audio.mp4"(),
"/audio"()
"/audio"(),
)
}

View File

@@ -18,7 +18,7 @@ val fixMissingAudioInVideoDownloadsPatch = bytecodePatch(
"/DASH_AUDIO_64.mp4",
)
downloadAudioMethodMatch.indices.forEachIndexed { index, i ->
downloadAudioMethodMatch.indices[0].forEachIndexed { index, i ->
val replacement = endpointReplacements[i]
val register = downloadAudioMethodMatch.method.getInstruction<OneRegisterInstruction>(index).registerA

View File

@@ -6,17 +6,17 @@ import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val basicAuthorizationMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.basicAuthorizationMethodMatch by composingFirstMethod {
instructions(
"yyOCBp.RHJhDKd"(),
"fJOxVwBUyo*=f:<OoejWs:AqmIJ"() // Encrypted basic authorization string.
"fJOxVwBUyo*=f:<OoejWs:AqmIJ"(), // Encrypted basic authorization string.
)
}
internal val buildAuthorizationStringMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.buildAuthorizationStringMethodMatch by composingFirstMethod {
instructions(
"yyOCBp.RHJhDKd"(),
"client_id"()
"client_id"(),
)
}

View File

@@ -1,7 +1,6 @@
package app.revanced.patches.reddit.customclients.redditisfun.api
import app.revanced.patcher.Match
import app.revanced.patcher.MatchBuilder
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patches.reddit.customclients.spoofClientPatch
@@ -31,11 +30,11 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "redditisfun://auth") { cl
* @param getReplacementIndex A function that returns the index of the instruction to replace
* using the [Match.indices] list from the [Match].
*/
fun MatchBuilder.replaceWith(
fun Match.replaceWith(
string: String,
getReplacementIndex: List<Int>.() -> Int,
) = method.apply {
val replacementIndex = indices.getReplacementIndex()
val replacementIndex = indices[0].getReplacementIndex()
val clientIdRegister = getInstruction<OneRegisterInstruction>(replacementIndex).registerA
replaceInstruction(replacementIndex, "const-string v$clientIdRegister, \"$string\"")

View File

@@ -1,28 +1,28 @@
package app.revanced.patches.reddit.customclients.relayforreddit.api
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
import org.stringtemplate.v4.compiler.Bytecode
internal fun baseClientIdMethod(string: String) = firstMethodComposite {
internal fun baseClientIdMethod(string: String) = composingFirstMethod {
instructions(
"dj-xCIZQYiLbEg"(),
string()
string(),
)
}
internal val getLoggedInBearerTokenMethodMatch = baseClientIdMethod("authorization_code")
internal val BytecodePatchContext.getLoggedInBearerTokenMethodMatch by baseClientIdMethod("authorization_code")
internal val getLoggedOutBearerTokenMethodMatch = baseClientIdMethod("https://oauth.reddit.com/grants/installed_client")
internal val BytecodePatchContext.getLoggedOutBearerTokenMethodMatch by baseClientIdMethod("https://oauth.reddit.com/grants/installed_client")
internal val getRefreshTokenMethodMatch = baseClientIdMethod("refresh_token")
internal val BytecodePatchContext.getRefreshTokenMethodMatch by baseClientIdMethod("refresh_token")
internal val loginActivityClientIdMethodMatch = baseClientIdMethod("&duration=permanent")
internal val BytecodePatchContext.loginActivityClientIdMethodMatch by baseClientIdMethod("&duration=permanent")
internal val BytecodePatchContext.redditCheckDisableAPIMethod by gettingFirstMutableMethodDeclaratively("Reddit Disabled") {
instructions(Opcode.IF_EQZ())

View File

@@ -28,7 +28,7 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "dbrady://relay") { client
getLoggedOutBearerTokenMethodMatch,
getRefreshTokenMethodMatch,
).forEach { match ->
val clientIdIndex = match.indices.first()
val clientIdIndex = match[0]
val clientIdRegister = match.method.getInstruction<OneRegisterInstruction>(clientIdIndex).registerA
match.method.replaceInstruction(clientIdIndex, "const-string v$clientIdRegister, \"$clientId\"")

View File

@@ -8,7 +8,7 @@ val disablePiracyDetectionPatch = bytecodePatch(
) {
apply {
// Do not throw an error if the fingerprint is not resolved.
// Do not throw an error if the method can't be found.
// This is fine because new versions of the target app do not need this patch.
detectPiracyMethodOrNull?.returnEarly()
}

View File

@@ -1,17 +1,19 @@
package app.revanced.patches.reddit.customclients.sync.syncforreddit.api
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.ClassDefComposing
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.gettingFirstMutableMethod
import app.revanced.patcher.instructions
import app.revanced.patcher.invoke
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.string
import com.android.tools.smali.dexlib2.iface.ClassDef
internal val getAuthorizationStringMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.getAuthorizationStringMethodMatch by composingFirstMethod {
instructions(string { startsWith("authorize.compact?client_id") })
}
internal val getBearerTokenMethodMatch = firstMethodComposite {
internal val ClassDef.getBearerTokenMethodMatch by ClassDefComposing.composingFirstMethod {
instructions(string { startsWith("Basic") })
}
@@ -19,6 +21,6 @@ internal val BytecodePatchContext.getUserAgentMethod by gettingFirstMutableMetho
"android:com.laurencedawson.reddit_sync",
)
internal val imgurImageAPIMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.imgurImageAPIMethodMatch by composingFirstMethod {
instructions("https://imgur-apiv3.p.rapidapi.com/3/image"())
}

View File

@@ -18,7 +18,7 @@ val spoofClientPatch = spoofClientPatch(
disablePiracyDetectionPatch,
// Redirects from SSL to WWW domain are bugged causing auth problems.
// Manually rewrite the URLs to fix this.
replaceStringPatch("ssl.reddit.com", "www.reddit.com")
replaceStringPatch("ssl.reddit.com", "www.reddit.com"),
)
compatibleWith(
@@ -32,11 +32,11 @@ val spoofClientPatch = spoofClientPatch(
apply {
// region Patch client id.
getBearerTokenMethodMatch.match(getAuthorizationStringMethodMatch.immutableClassDef).method.apply {
getAuthorizationStringMethodMatch.immutableClassDef.getBearerTokenMethodMatch.method.apply {
val auth = Base64.getEncoder().encodeToString("$clientId:".toByteArray(Charsets.UTF_8))
returnEarly("Basic $auth")
val occurrenceIndex = getAuthorizationStringMethodMatch.indices.first()
val occurrenceIndex = getAuthorizationStringMethodMatch[0]
getAuthorizationStringMethodMatch.method.apply {
val authorizationStringInstruction = getInstruction<OneRegisterInstruction>(occurrenceIndex)
@@ -68,7 +68,7 @@ val spoofClientPatch = spoofClientPatch(
// region Patch Imgur API URL.
val apiUrlIndex = imgurImageAPIMethodMatch.indices.first()
val apiUrlIndex = imgurImageAPIMethodMatch[0]
imgurImageAPIMethodMatch.method.replaceInstruction(
apiUrlIndex,
"const-string v1, \"https://api.imgur.com/3/image\"",

View File

@@ -15,7 +15,7 @@ internal val BytecodePatchContext.createOkHttpClientMethod by gettingFirstMutabl
returnType("V")
parameterTypes()
custom {
// There are four functions (each creating a client) defined in this file with very similar fingerprints.
// There are four functions (each creating a client) defined in this file with very similar methods.
// We're looking for the one that only creates one object (the builder) and sets client options true
// (thus never reloading the register with a 0).
immutableClassDef.sourceFile == "OkHttpHelper.java" && instructions.count {

View File

@@ -1,39 +1,40 @@
package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.user
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
internal fun userEndpointMethodMatch(
source: String,
accessFlags: Set<AccessFlags>? = null
) = firstMethodComposite {
accessFlags: Set<AccessFlags>? = null,
) = composingFirstMethod {
instructions("u/"(String::contains))
custom { immutableClassDef.sourceFile == source }
accessFlags(*accessFlags?.toTypedArray() ?: return@firstMethodComposite)
accessFlags(*accessFlags?.toTypedArray() ?: return@composingFirstMethod)
}
internal val oAuthFriendRequestMethodMatch = userEndpointMethodMatch(
internal val BytecodePatchContext.oAuthFriendRequestMethodMatch by userEndpointMethodMatch(
"OAuthFriendRequest.java",
)
internal val oAuthUnfriendRequestMethodMatch = userEndpointMethodMatch(
internal val BytecodePatchContext.oAuthUnfriendRequestMethodMatch by userEndpointMethodMatch(
"OAuthUnfriendRequest.java",
)
internal val oAuthUserIdRequestMethodMatch = userEndpointMethodMatch(
internal val BytecodePatchContext.oAuthUserIdRequestMethodMatch by userEndpointMethodMatch(
"OAuthUserIdRequest.java",
)
internal val oAuthUserInfoRequestMethodMatch = userEndpointMethodMatch(
internal val BytecodePatchContext.oAuthUserInfoRequestMethodMatch by userEndpointMethodMatch(
"OAuthUserInfoRequest.java",
)
internal val oAuthSubredditInfoRequestConstructorMethodMatch = userEndpointMethodMatch(
internal val BytecodePatchContext.oAuthSubredditInfoRequestConstructorMethodMatch by userEndpointMethodMatch(
"OAuthSubredditInfoRequest.java",
setOf(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR),
)
internal val oAuthSubredditInfoRequestHelperMethodMatch = userEndpointMethodMatch(
internal val BytecodePatchContext.oAuthSubredditInfoRequestHelperMethodMatch by userEndpointMethodMatch(
"OAuthSubredditInfoRequest.java",
setOf(AccessFlags.PRIVATE, AccessFlags.STATIC),
)

View File

@@ -28,7 +28,7 @@ val useUserEndpointPatch = bytecodePatch(
oAuthUserIdRequestMethodMatch,
oAuthUserInfoRequestMethodMatch,
).map { match ->
match.indices.first() to match.method
match[0] to match.method
}.forEach { (userPathStringIndex, method) ->
val userPathStringInstruction = method.getInstruction<OneRegisterInstruction>(userPathStringIndex)

View File

@@ -1,9 +1,10 @@
package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.video
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val parseRedditVideoNetworkResponseMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.parseRedditVideoNetworkResponseMethodMatch by composingFirstMethod {
name("parseNetworkResponse")
opcodes(
Opcode.NEW_INSTANCE,

View File

@@ -24,7 +24,7 @@ val fixVideoDownloadsPatch = bytecodePatch(
)
apply {
val scanResult = parseRedditVideoNetworkResponseMethodMatch.indices
val scanResult = parseRedditVideoNetworkResponseMethodMatch.indices[0]
val newInstanceIndex = scanResult.first()
val invokeDirectIndex = scanResult.last() - 1

View File

@@ -12,8 +12,8 @@ internal val BytecodePatchContext.numberOfPresetAppNamesExtensionMethod by getti
parameterTypes()
}
// A much simpler fingerprint exists that can set the small icon (contains string "414843287017"),
// but that has limited usage and this fingerprint allows changing any part of the notification.
// A much simpler method exists that can set the small icon (contains string "414843287017"),
// but that has limited usage and this one allows changing any part of the notification.
internal val BytecodePatchContext.notificationMethod by gettingFirstMutableMethodDeclaratively(
"key_action_priority",
) {

View File

@@ -1,10 +1,11 @@
package app.revanced.patches.shared.layout.theme
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 lithoOnBoundsChangeMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.lithoOnBoundsChangeMethodMatch by composingFirstMethod {
name("onBoundsChange")
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
returnType("V")

View File

@@ -11,7 +11,7 @@ val lithoColorHookPatch = bytecodePatch(
) {
apply {
var insertionIndex = lithoOnBoundsChangeMethodMatch.indices.last() - 1
var insertionIndex = lithoOnBoundsChangeMethodMatch[-1] - 1
lithoColorOverrideHook = { targetMethodClass, targetMethodName ->
lithoOnBoundsChangeMethodMatch.method.addInstructions(

View File

@@ -1,18 +1,19 @@
package app.revanced.patches.shared.misc.audio
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
internal val formatStreamModelToStringMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.formatStreamModelToStringMethodMatch by composingFirstMethod {
name("toString")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("Ljava/lang/String;")
instructions(
"isDefaultAudioTrack="(String::contains),
"audioTrackId="(String::contains)
"audioTrackId="(String::contains),
)
}
internal val selectAudioStreamMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.selectAudioStreamMethodMatch by composingFirstMethod {
instructions(45666189L())
}

View File

@@ -39,7 +39,7 @@ internal fun forceOriginalAudioPatch(
subclassExtensionClassDescriptor: String,
preferenceScreen: BasePreferenceScreen.Screen,
) = bytecodePatch(
name = "Force original audio", // TODO
name = "Force original audio",
description = "Adds an option to always use the original audio track.",
) {
block()
@@ -65,7 +65,7 @@ internal fun forceOriginalAudioPatch(
// and instead overrides to the user region language.
if (fixUseLocalizedAudioTrackFlag()) {
selectAudioStreamMethodMatch.method.insertLiteralOverride(
selectAudioStreamMethodMatch.indices.first(),
selectAudioStreamMethodMatch[0],
"$EXTENSION_CLASS_DESCRIPTOR->ignoreDefaultAudioStream(Z)Z",
)
}

View File

@@ -26,12 +26,11 @@ internal fun enableDebuggingPatch(
executeBlock: BytecodePatchContext.() -> Unit = {},
hookStringFeatureFlag: Boolean,
preferenceScreen: BasePreferenceScreen.Screen,
additionalDebugPreferences: List<BasePreference> = emptyList()
additionalDebugPreferences: List<BasePreference> = emptyList(),
) = bytecodePatch(
name = "Enable debugging", // TODO
name = "Enable debugging",
description = "Adds options for debugging and exporting ReVanced logs to the clipboard.",
) {
dependsOn(
addResourcesPatch,
resourcePatch {
@@ -48,11 +47,11 @@ internal fun enableDebuggingPatch(
"revanced_settings_arrow_left_double.xml",
"revanced_settings_arrow_left_one.xml",
"revanced_settings_arrow_right_double.xml",
"revanced_settings_arrow_right_one.xml"
)
"revanced_settings_arrow_right_one.xml",
),
)
}
}
},
)
block()
@@ -75,19 +74,19 @@ internal fun enableDebuggingPatch(
NonInteractivePreference(
"revanced_debug_export_logs_to_clipboard",
tag = "app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference",
selectable = true
selectable = true,
),
NonInteractivePreference(
"revanced_debug_logs_clear_buffer",
tag = "app.revanced.extension.shared.settings.preference.ClearLogBufferPreference",
selectable = true
selectable = true,
),
NonInteractivePreference(
"revanced_debug_feature_flags_manager",
tag = "app.revanced.extension.shared.settings.preference.FeatureFlagsManagerPreference",
selectable = true
)
)
selectable = true,
),
),
)
preferenceScreen.addPreferences(
@@ -95,7 +94,7 @@ internal fun enableDebuggingPatch(
key = "revanced_debug_screen",
sorting = Sorting.UNSORTED,
preferences = preferences,
)
),
)
// Hook the methods that look up if a feature flag is active.
@@ -108,7 +107,7 @@ internal fun enableDebuggingPatch(
"""
invoke-static { v$register, p1 }, $EXTENSION_CLASS_DESCRIPTOR->isBooleanFeatureFlagEnabled(ZLjava/lang/Long;)Z
move-result v$register
"""
""",
)
}
}
@@ -123,7 +122,7 @@ internal fun enableDebuggingPatch(
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isDoubleFeatureFlagEnabled(DJD)D
move-result-wide v0
return-wide v0
"""
""",
)
}
@@ -137,11 +136,11 @@ internal fun enableDebuggingPatch(
invoke-static/range { v0 .. v5 }, $EXTENSION_CLASS_DESCRIPTOR->isLongFeatureFlagEnabled(JJJ)J
move-result-wide v0
return-wide v0
"""
""",
)
}
if (hookStringFeatureFlag)
if (hookStringFeatureFlag) {
experimentalFeatureFlagParentMethod.immutableClassDef.getExperimentalStringFeatureFlagMethod().apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
@@ -152,9 +151,10 @@ internal fun enableDebuggingPatch(
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
"""
""",
)
}
}
// There exists other experimental accessor methods for byte[]
// and wrappers for obfuscated classes, but currently none of those are hooked.

View File

@@ -18,7 +18,7 @@ internal fun checkWatchHistoryDomainNameResolutionPatch(
executeBlock: BytecodePatchContext.() -> Unit = {},
getMainActivityMethod: BytecodePatchContext.() -> MutableMethod,
) = bytecodePatch(
name = "Check watch history domain name resolution", // TODO
name = "Check watch history domain name resolution",
description = "Checks if the device DNS server is preventing user watch history from being saved.",
) {
block()

View File

@@ -1,9 +1,8 @@
package app.revanced.patches.shared.misc.extension
import app.revanced.patcher.*
import app.revanced.patcher.firstMutableClassDef
import app.revanced.patcher.firstMutableMethodDeclaratively
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.firstMutableClassDef
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@@ -85,18 +84,18 @@ fun sharedExtensionPatch(
class ExtensionHook internal constructor(
private val getInsertIndex: Method.() -> Int,
private val getContextRegister: Method.() -> String,
private val predicate: DeclarativePredicate<Method>,
private val build: context(MutableList<String>) MutablePredicateList<Method>.() -> Unit,
) {
context(_: BytecodePatchContext)
context(context: BytecodePatchContext)
operator fun invoke(extensionClassDescriptor: String) {
val method = firstMutableMethodDeclaratively(predicate = predicate)
val method = context.firstMutableMethodDeclaratively(build = build)
val insertIndex = method.getInsertIndex()
val contextRegister = method.getContextRegister()
method.addInstruction(
insertIndex,
"invoke-static/range { $contextRegister .. $contextRegister }, " +
"$extensionClassDescriptor->setContext(Landroid/content/Context;)V",
"$extensionClassDescriptor->setContext(Landroid/content/Context;)V",
)
}
}
@@ -104,8 +103,8 @@ class ExtensionHook internal constructor(
fun extensionHook(
getInsertIndex: Method.() -> Int = { 0 },
getContextRegister: Method.() -> String = { "p0" },
predicate: DeclarativePredicate<Method>,
) = ExtensionHook(getInsertIndex, getContextRegister, predicate)
build: context(MutableList<String>) MutablePredicateList<Method>.() -> Unit,
) = ExtensionHook(getInsertIndex, getContextRegister, build)
/**
* Creates an extension hook from a non-obfuscated activity, which typically is the main activity
@@ -124,8 +123,11 @@ fun activityOnCreateExtensionHook(activityClassType: String): ExtensionHook {
return extensionHook {
name("onCreate")
if (fullClassType) definingClass(activityClassType)
else definingClass { endsWith(activityClassType) }
if (fullClassType) {
definingClass(activityClassType)
} else {
definingClass { endsWith(activityClassType) }
}
returnType("V")
parameterTypes("Landroid/os/Bundle;")

View File

@@ -1,10 +1,11 @@
package app.revanced.patches.shared.misc.fix.verticalscroll
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 canScrollVerticallyMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.canScrollVerticallyMethodMatch by composingFirstMethod {
definingClass { endsWith("SwipeRefreshLayout;") }
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("Z")

View File

@@ -12,7 +12,7 @@ val verticalScrollPatch = bytecodePatch(
apply {
canScrollVerticallyMethodMatch.let {
it.method.apply {
val moveResultIndex = it.indices.last()
val moveResultIndex = it[-1]
val moveResultRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
val insertIndex = moveResultIndex + 1

View File

@@ -54,7 +54,7 @@ fun gmsCoreSupportPatch(
executeBlock: BytecodePatchContext.() -> Unit = {},
block: BytecodePatchBuilder.() -> Unit = {},
) = bytecodePatch(
name = "GmsCore support", // TODO
name = "GmsCore support",
description = "Allows the app to work without root by using a different package name when patched " +
"using a GmsCore instead of Google Play Services.",
) {

View File

@@ -1,9 +1,10 @@
package app.revanced.patches.shared.misc.privacy
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val youTubeCopyTextFingerprintMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.youTubeCopyTextMethodMatch by composingFirstMethod {
returnType("V")
parameterTypes("L", "Ljava/util/Map;")
instructions(
@@ -11,22 +12,22 @@ internal val youTubeCopyTextFingerprintMethodMatch = firstMethodComposite {
after(0..2, "text/plain"()),
after(0..2, method("newPlainText")),
after(0..2, Opcode.MOVE_RESULT_OBJECT()),
after(0..2, method("setPrimaryClip"))
after(0..2, method("setPrimaryClip")),
)
}
internal val youTubeSystemShareSheetMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.youTubeSystemShareSheetMethodMatch by composingFirstMethod {
returnType("V")
parameterTypes("L", "Ljava/util/Map;")
instructions(
method("setClassName"),
after(0..4, method("iterator")),
after(0..15, allOf(Opcode.IGET_OBJECT(), type("Ljava/lang/String;"))),
after(0..15, method("putExtra"))
after(0..15, method("putExtra")),
)
}
internal val youTubeShareSheetMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.youTubeShareSheetMethodMatch by composingFirstMethod {
returnType("V")
parameterTypes("L", "Ljava/util/Map;")
instructions(
@@ -34,6 +35,6 @@ internal val youTubeShareSheetMethodMatch = firstMethodComposite {
after(allOf(Opcode.CHECK_CAST(), type("Ljava/lang/String;"))),
after(Opcode.GOTO()),
method("putExtra"),
"YTShare_Logging_Share_Intent_Endpoint_Byte_Array"()
"YTShare_Logging_Share_Intent_Endpoint_Byte_Array"(),
)
}

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.shared.misc.privacy
import app.revanced.patcher.MatchBuilder
import app.revanced.patcher.Match
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.getInstruction
import app.revanced.patcher.patch.BytecodePatchBuilder
@@ -28,7 +28,7 @@ internal fun sanitizeSharingLinksPatch(
preferenceScreen: BasePreferenceScreen.Screen,
replaceMusicLinksWithYouTube: Boolean = false,
) = bytecodePatch(
name = "Sanitize sharing links", // TODO
name = "Sanitize sharing links",
description = "Removes the tracking query parameters from shared links.",
) {
block()
@@ -58,8 +58,9 @@ internal fun sanitizeSharingLinksPatch(
},
)
fun MatchBuilder.hookUrlString(matchIndex: Int) {
val index = indices[matchIndex]
fun Match.hookUrlString(matchIndex: Int) {
val index = get(matchIndex)
val urlRegister = method.getInstruction<OneRegisterInstruction>(index).registerA
method.addInstructions(
@@ -71,8 +72,8 @@ internal fun sanitizeSharingLinksPatch(
)
}
fun MatchBuilder.hookIntentPutExtra(matchIndex: Int) {
val index = indices[matchIndex]
fun Match.hookIntentPutExtra(matchIndex: Int) {
val index = get(matchIndex)
val urlRegister = method.getInstruction<FiveRegisterInstruction>(index).registerE
method.addInstructionsAtControlFlowLabel(
@@ -85,7 +86,7 @@ internal fun sanitizeSharingLinksPatch(
}
// YouTube share sheet copy link.
youTubeCopyTextFingerprintMethodMatch.hookUrlString(0)
youTubeCopyTextMethodMatch.hookUrlString(0)
// YouTube share sheet other apps.
youTubeShareSheetMethodMatch.hookIntentPutExtra(3)

View File

@@ -1,10 +1,10 @@
package app.revanced.patches.shared.misc.spoof
import app.revanced.patcher.accessFlags
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.custom
import app.revanced.patcher.definingClass
import app.revanced.patcher.extensions.methodReference
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.gettingFirstMethodDeclaratively
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.immutableClassDef
@@ -21,15 +21,15 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
internal val buildInitPlaybackRequestMatch = firstMethodComposite("Content-Type", "Range") {
returnType("Lorg/chromium/net/UrlRequest\$Builder;")
internal val BytecodePatchContext.buildInitPlaybackRequestMethodMatch by composingFirstMethod("Content-Type", "Range") {
returnType($$"Lorg/chromium/net/UrlRequest$Builder;")
instructions(
Opcode.MOVE_RESULT_OBJECT(),
Opcode.IGET_OBJECT(), // Moves the request URI string to a register to build the request with.
)
}
internal val buildPlayerRequestURIMethodMatch = firstMethodComposite("key", "asig") {
internal val BytecodePatchContext.buildPlayerRequestURIMethodMatch by composingFirstMethod("key", "asig") {
returnType("Ljava/lang/String;")
instructions(
Opcode.INVOKE_VIRTUAL(), // Register holds player request URI.
@@ -41,7 +41,7 @@ internal val buildPlayerRequestURIMethodMatch = firstMethodComposite("key", "asi
)
}
internal val buildRequestMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.buildRequestMethodMatch by composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returnType("Lorg/chromium/net/UrlRequest") // UrlRequest; or UrlRequest$Builder;
instructions(
@@ -80,8 +80,8 @@ internal val buildRequestMethodMatch = firstMethodComposite {
val parameterTypes = parameterTypes
val parameterTypesSize = parameterTypes.size
(parameterTypesSize == 6 || parameterTypesSize == 7 || parameterTypesSize == 8) &&
parameterTypes[1] == "Ljava/util/Map;" && // URL headers.
indexOfNewUrlRequestBuilderInstruction(this) >= 0
parameterTypes[1] == "Ljava/util/Map;" && // URL headers.
indexOfNewUrlRequestBuilderInstruction(this) >= 0
}
}
@@ -98,7 +98,7 @@ internal val BytecodePatchContext.protobufClassParseByteBufferMethod by gettingF
)
}
internal val createStreamingDataMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.createStreamingDataMethodMatch by composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameterTypes("L")
instructions(
@@ -131,7 +131,7 @@ internal val BytecodePatchContext.buildMediaDataSourceMethod by gettingFirstMuta
)
}
internal val hlsCurrentTimeMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.hlsCurrentTimeMethodMatch by composingFirstMethod {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameterTypes("Z", "L")
instructions(
@@ -141,7 +141,7 @@ internal val hlsCurrentTimeMethodMatch = firstMethodComposite {
internal const val DISABLED_BY_SABR_STREAMING_URI_STRING = "DISABLED_BY_SABR_STREAMING_URI"
internal val mediaFetchEnumConstructorMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.mediaFetchEnumConstructorMethodMatch by composingFirstMethod {
returnType("V")
instructions(
"ENABLED"(),
@@ -170,13 +170,13 @@ internal val BytecodePatchContext.patchIncludedExtensionMethodMethod by gettingF
// This code appears to replace the player config after the streams are loaded.
// Flag is present in YouTube 19.34, but is missing Platypus stream replacement code until 19.43.
// Flag and Platypus code is also present in newer versions of YouTube Music.
internal val mediaFetchHotConfigMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.mediaFetchHotConfigMethodMatch by composingFirstMethod {
instructions(45645570L())
}
// YT 20.10+, YT Music 8.11 - 8.14.
// Flag is missing in YT Music 8.15+, and it is not known if a replacement flag/feature exists.
internal val mediaFetchHotConfigAlternativeMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.mediaFetchHotConfigAlternativeMethodMatch by composingFirstMethod {
instructions(45683169L())
}
@@ -184,7 +184,7 @@ internal val mediaFetchHotConfigAlternativeMethodMatch = firstMethodComposite {
// but its exact purpose is not known. If this flag is enabled while stream spoofing
// then videos will never start playback and load forever.
// Flag does not seem to affect playback if spoofing is off.
internal val playbackStartDescriptorFeatureFlagMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.playbackStartDescriptorFeatureFlagMethodMatch by composingFirstMethod {
parameterTypes()
returnType("Z")
instructions(45665455L())
@@ -194,9 +194,9 @@ internal fun indexOfNewUrlRequestBuilderInstruction(method: Method) = method.ind
val reference = methodReference ?: return@indexOfFirstInstruction false
opcode == Opcode.INVOKE_VIRTUAL && reference.definingClass == "Lorg/chromium/net/CronetEngine;" &&
reference.name == "newUrlRequestBuilder" &&
reference.parameterTypes.size == 3 &&
reference.parameterTypes[0] == "Ljava/lang/String;" &&
reference.parameterTypes[1] == "Lorg/chromium/net/UrlRequest\$Callback;" &&
reference.parameterTypes[2] == "Ljava/util/concurrent/Executor;"
reference.name == "newUrlRequestBuilder" &&
reference.parameterTypes.size == 3 &&
reference.parameterTypes[0] == "Ljava/lang/String;" &&
reference.parameterTypes[1] == "Lorg/chromium/net/UrlRequest\$Callback;" &&
reference.parameterTypes[2] == "Ljava/util/concurrent/Executor;"
}

View File

@@ -39,7 +39,7 @@ internal fun spoofVideoStreamsPatch(
block: BytecodePatchBuilder.() -> Unit,
executeBlock: BytecodePatchContext.() -> Unit = {},
) = bytecodePatch(
name = "Spoof video streams", // TODO
name = "Spoof video streams",
description = "Adds options to spoof the client video streams to fix playback.",
) {
block()
@@ -64,8 +64,8 @@ internal fun spoofVideoStreamsPatch(
// region Block /initplayback requests to fall back to /get_watch requests.
buildInitPlaybackRequestMatch.method.apply {
val moveUriStringIndex = buildInitPlaybackRequestMatch.indices.first()
buildInitPlaybackRequestMethodMatch.method.apply {
val moveUriStringIndex = buildInitPlaybackRequestMethodMatch[0]
val targetRegister = getInstruction<OneRegisterInstruction>(moveUriStringIndex).registerA
addInstructions(
@@ -82,7 +82,7 @@ internal fun spoofVideoStreamsPatch(
// region Block /get_watch requests to fall back to /player requests.
buildPlayerRequestURIMethodMatch.method.apply {
val invokeToStringIndex = buildPlayerRequestURIMethodMatch.indices.first()
val invokeToStringIndex = buildPlayerRequestURIMethodMatch[0]
val uriRegister = getInstruction<FiveRegisterInstruction>(invokeToStringIndex).registerC
addInstructions(
@@ -101,7 +101,7 @@ internal fun spoofVideoStreamsPatch(
buildRequestMethodMatch.method.apply {
buildRequestMethod = this
val newRequestBuilderIndex = buildRequestMethodMatch.indices.first()
val newRequestBuilderIndex = buildRequestMethodMatch[0]
buildRequestMethodUrlRegister = getInstruction<FiveRegisterInstruction>(newRequestBuilderIndex).registerD
val freeRegister = findFreeRegister(newRequestBuilderIndex, buildRequestMethodUrlRegister)
@@ -121,7 +121,7 @@ internal fun spoofVideoStreamsPatch(
createStreamingDataMethodMatch.method.apply {
val setStreamDataMethodName = "patch_setStreamingData"
val resultMethodType = createStreamingDataMethodMatch.classDef.type
val videoDetailsIndex = createStreamingDataMethodMatch.indices.last()
val videoDetailsIndex = createStreamingDataMethodMatch[-1]
val videoDetailsRegister = getInstruction<TwoRegisterInstruction>(videoDetailsIndex).registerA
val videoDetailsClass = getInstruction(videoDetailsIndex).getReference<FieldReference>()!!.type
@@ -132,7 +132,7 @@ internal fun spoofVideoStreamsPatch(
)
val protobufClass = protobufClassParseByteBufferMethod.definingClass
val setStreamingDataIndex = createStreamingDataMethodMatch.indices.first()
val setStreamingDataIndex = createStreamingDataMethodMatch[0]
val playerProtoClass = getInstruction(setStreamingDataIndex + 1)
.getReference<FieldReference>()!!.definingClass
@@ -260,7 +260,7 @@ internal fun spoofVideoStreamsPatch(
// region Fix iOS livestream current time.
hlsCurrentTimeMethodMatch.method.insertLiteralOverride(
hlsCurrentTimeMethodMatch.indices.first(),
hlsCurrentTimeMethodMatch[0],
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z",
)
@@ -270,7 +270,7 @@ internal fun spoofVideoStreamsPatch(
// If SABR is disabled, it seems 'MediaFetchHotConfig' may no longer need an override (not confirmed).
val (mediaFetchEnumClass, sabrFieldReference) = with(mediaFetchEnumConstructorMethodMatch.method) {
val disabledBySABRStreamingUrlString = mediaFetchEnumConstructorMethodMatch.indices.last()
val disabledBySABRStreamingUrlString = mediaFetchEnumConstructorMethodMatch[-1]
val mediaFetchEnumClass = definingClass
val sabrFieldIndex = indexOfFirstInstructionOrThrow(disabledBySABRStreamingUrlString) {
@@ -311,21 +311,21 @@ internal fun spoofVideoStreamsPatch(
if (fixMediaFetchHotConfig()) {
mediaFetchHotConfigMethodMatch.method.insertLiteralOverride(
mediaFetchHotConfigMethodMatch.indices.first(),
mediaFetchHotConfigMethodMatch[0],
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z",
)
}
if (fixMediaFetchHotConfigAlternative()) {
mediaFetchHotConfigAlternativeMethodMatch.method.insertLiteralOverride(
mediaFetchHotConfigAlternativeMethodMatch.indices.first(),
mediaFetchHotConfigAlternativeMethodMatch[0],
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z",
)
}
if (fixParsePlaybackResponseFeatureFlag()) {
playbackStartDescriptorFeatureFlagMethodMatch.method.insertLiteralOverride(
playbackStartDescriptorFeatureFlagMethodMatch.indices.first(),
playbackStartDescriptorFeatureFlagMethodMatch[0],
"$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z",
)
}

View File

@@ -1,12 +1,13 @@
package app.revanced.patches.solidexplorer2.functionality.filesize
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.definingClass
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.name
import app.revanced.patcher.opcodes
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val onReadyMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.onReadyMethodMatch by composingFirstMethod {
name("onReady")
definingClass("Lpl/solidexplorer/plugins/texteditor/TextEditor;")
opcodes(

View File

@@ -14,7 +14,7 @@ val removeFileSizeLimitPatch = bytecodePatch(
apply {
onReadyMethodMatch.let {
val cmpIndex = it.indices.first() + 1
val cmpIndex = it[0] + 1
val cmpResultRegister = it.method.getInstruction<ThreeRegisterInstruction>(cmpIndex).registerA
it.method.replaceInstruction(cmpIndex, "const/4 v$cmpResultRegister, 0x0")

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val interceptMethodMatch = firstMethodComposite("SC-Mob-UserPlan", "Configuration") {
internal val BytecodePatchContext.interceptMethodMatch by composingFirstMethod("SC-Mob-UserPlan", "Configuration") {
accessFlags(AccessFlags.PUBLIC)
returnType("L")
parameterTypes("L")

View File

@@ -48,7 +48,7 @@ val hideAdsPatch = bytecodePatch("Hide ads") {
// Prevent verification of an HTTP header containing the user's current plan, which would contradict the previous patch.
val conditionIndex = interceptMethodMatch.indices.last() + 1 // TODO
val conditionIndex = interceptMethodMatch[-1] + 1
interceptMethodMatch.method.addInstruction(
conditionIndex,
"return-object p1",

View File

@@ -1,10 +1,11 @@
package app.revanced.patches.spotify.misc.extension
import app.revanced.patcher.*
import app.revanced.patcher.patch.BytecodePatchContext
internal val loadOrbitLibraryMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.loadOrbitLibraryMethodMatch by composingFirstMethod {
instructions(
"orbit_library_load"(),
"orbit-jni-spotify"()
"orbit-jni-spotify"(),
)
}

View File

@@ -50,7 +50,7 @@ val disableSubscriptionSuggestionsPatch = bytecodePatch("Disable subscription su
},
)
val getModulesIndex = getModulesMethodMatch.indices.first()
val getModulesIndex = getModulesMethodMatch[0]
immutableMethod.removeInstruction(getModulesIndex)
immutableMethod.addInstructions(
getModulesIndex,

View File

@@ -1,12 +1,13 @@
package app.revanced.patches.strava.upselling
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.definingClass
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.name
import app.revanced.patcher.opcodes
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val getModulesMethodMatch = firstMethodComposite {
internal val BytecodePatchContext.getModulesMethodMatch by composingFirstMethod {
name("getModules")
definingClass { endsWith("/GenericLayoutEntry;") }
opcodes(Opcode.IGET_OBJECT)

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.name
import app.revanced.patcher.patch.BytecodePatchContext
internal val BytecodePatchContext.checkLockedThemesFingerprint by gettingFirstMutableMethodDeclaratively {
internal val BytecodePatchContext.checkLockedThemesMethod by gettingFirstMutableMethodDeclaratively {
name("isLockedTheme")
definingClass { endsWith("Theme;") }
}

View File

@@ -12,7 +12,7 @@ val unlockThemesPatch = bytecodePatch(
compatibleWith("com.ticktick.task")
apply {
checkLockedThemesFingerprint.addInstructions(
checkLockedThemesMethod.addInstructions(
0,
"""
const/4 v0, 0x0

View File

@@ -1,14 +1,15 @@
package app.revanced.patches.tumblr.featureflags
import app.revanced.patcher.accessFlags
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.opcodes
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.returnType
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
// This fingerprint targets the method to get the value of a Feature in the class "com.tumblr.configuration.Feature".
// This targets the method to get the value of a Feature in the class "com.tumblr.configuration.Feature".
// Features seem to be Tumblr's A/B testing program.
// Feature states are loaded from the server in the "api-http2.tumblr.com/v2/config" request on (first) startup.
// A lot of features are returned there, but most of them do not seem to do anything (anymore).
@@ -17,7 +18,7 @@ import com.android.tools.smali.dexlib2.Opcode
// Some features seem to be very old and never removed, though, such as Google Login.
// The startIndex of the opcode pattern is at the start of the function after the arg null check.
// we want to insert our instructions there.
internal val getFeatureValueMethodMatch = firstMethodComposite("feature") {
internal val BytecodePatchContext.getFeatureValueMethodMatch by composingFirstMethod("feature") {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returnType("Ljava/lang/String;")
parameterTypes("L", "Z")

View File

@@ -70,7 +70,7 @@ val overrideFeatureFlagsPatch = bytecodePatch(
// This is equivalent to
// String forcedValue = getValueOverride(feature)
// if (forcedValue != null) return forcedValue
val getFeatureIndex = match.indices.first()
val getFeatureIndex = match[0]
match.method.addInstructionsWithLabels(
getFeatureIndex,
"""

View File

@@ -1,23 +1,23 @@
package app.revanced.patches.tumblr.fixes
import app.revanced.patcher.firstMethodComposite
import app.revanced.patcher.composingFirstMethod
import app.revanced.patcher.gettingFirstMutableMethodDeclaratively
import app.revanced.patcher.opcodes
import app.revanced.patcher.parameterTypes
import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
// Fingerprint for the addQueryParam method from retrofit2:
// Matches the addQueryParam method from retrofit2:
// https://github.com/square/retrofit/blob/trunk/retrofit/src/main/java/retrofit2/RequestBuilder.java#L186.
// Injecting here allows modifying dynamically set query parameters.
internal val BytecodePatchContext.addQueryParamMethod by gettingFirstMutableMethodDeclaratively("Malformed URL. Base: ", ", Relative: ") {
parameterTypes("Ljava/lang/String;", "Ljava/lang/String;", "Z")
}
// Fingerprint for the parseHttpMethodAndPath method from retrofit2:
// Matches the parseHttpMethodAndPath method from retrofit2:
// https://github.com/square/retrofit/blob/ebf87b10997e2136af4d335276fa950221852c64/retrofit/src/main/java/retrofit2/RequestFactory.java#L270-L302
// Injecting here allows modifying the path/query params of API endpoints defined via annotations.
internal val httpPathParserMethodMatch = firstMethodComposite("Only one HTTP method is allowed. Found: %s and %s.") {
internal val BytecodePatchContext.httpPathParserMethodMatch by composingFirstMethod("Only one HTTP method is allowed. Found: %s and %s.") {
opcodes(
Opcode.IPUT_OBJECT,
Opcode.IPUT_BOOLEAN,

View File

@@ -21,7 +21,7 @@ val fixOldVersionsPatch = bytecodePatch(
// Remove the live query parameters from the path when it's specified via a @METHOD annotation.
for (liveQueryParameter in liveQueryParameters) {
httpPathParserMethodMatch.method.addInstructions(
httpPathParserMethodMatch.indices.last() + 1,
httpPathParserMethodMatch[-1] + 1,
"""
# urlPath = urlPath.replace(liveQueryParameter, "")
const-string p1, "$liveQueryParameter"

View File

@@ -24,7 +24,7 @@ val filterTimelineObjectsPatch = bytecodePatch(
dependsOn(sharedExtensionPatch)
apply {
val filterInsertIndex = timelineFilterExtensionMethodMatch.indices.first()
val filterInsertIndex = timelineFilterExtensionMethodMatch[0]
timelineFilterExtensionMethodMatch.method.apply {
val addInstruction = getInstruction<BuilderInstruction35c>(filterInsertIndex + 1)

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