mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-30 06:01:03 +00:00
some fixes & update to newest patcher changes
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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="())
|
||||
}
|
||||
|
||||
@@ -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>()!!
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
""",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"())
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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"())
|
||||
}
|
||||
|
||||
@@ -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\"")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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\"")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"())
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
) {
|
||||
|
||||
@@ -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"())
|
||||
|
||||
@@ -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\"",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -37,7 +37,7 @@ val hideMusicVideoAdsPatch = bytecodePatch(
|
||||
)
|
||||
|
||||
navigate(showVideoAdsParentMethodMatch.immutableMethod)
|
||||
.to(showVideoAdsParentMethodMatch.indices.first() + 1)
|
||||
.to(showVideoAdsParentMethodMatch[0] + 1)
|
||||
.stop()
|
||||
.addInstructions(
|
||||
0,
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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\"")
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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\"")
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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"())
|
||||
}
|
||||
|
||||
@@ -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\"",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
) {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.",
|
||||
) {
|
||||
|
||||
@@ -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"(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;") }
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ val unlockThemesPatch = bytecodePatch(
|
||||
compatibleWith("com.ticktick.task")
|
||||
|
||||
apply {
|
||||
checkLockedThemesFingerprint.addInstructions(
|
||||
checkLockedThemesMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x0
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
"""
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user