mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-21 18:03:56 +00:00
Compare commits
14 Commits
v5.48.0-de
...
v5.48.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3f7609fe3 | ||
|
|
d25dcfe49a | ||
|
|
1cc2cb9cb2 | ||
|
|
f5cbb31724 | ||
|
|
b42ae27ce6 | ||
|
|
43ab29d03d | ||
|
|
789f0a5628 | ||
|
|
da836b667c | ||
|
|
44e7dbcf4d | ||
|
|
195c239000 | ||
|
|
c47beae213 | ||
|
|
cebcfab86a | ||
|
|
71c6cb569e | ||
|
|
6bb6281149 |
48
CHANGELOG.md
48
CHANGELOG.md
@@ -1,3 +1,51 @@
|
|||||||
|
# [5.48.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.7...v5.48.0-dev.8) (2026-01-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Letterboxd:** Add `Unlock app icons` patch ([#6415](https://github.com/ReVanced/revanced-patches/issues/6415)) ([d25dcfe](https://github.com/ReVanced/revanced-patches/commit/d25dcfe49ac331c9b3dca739ba0be95dbab669cc))
|
||||||
|
|
||||||
|
# [5.48.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.6...v5.48.0-dev.7) (2026-01-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Strava:** Add `Disable Quick Edit` patch ([#6452](https://github.com/ReVanced/revanced-patches/issues/6452)) ([f5cbb31](https://github.com/ReVanced/revanced-patches/commit/f5cbb31724d15f7e939b96ee0186fd0a108f9fdc))
|
||||||
|
* **Strava:** Add `Overwrite media upload parameters` patch ([#6410](https://github.com/ReVanced/revanced-patches/issues/6410)) ([b42ae27](https://github.com/ReVanced/revanced-patches/commit/b42ae27ce66ebad9e9cfc5b70fc121df5bad7567))
|
||||||
|
|
||||||
|
# [5.48.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.5...v5.48.0-dev.6) (2026-01-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Fix build error introduced in `4046bee` ([#6417](https://github.com/ReVanced/revanced-patches/issues/6417)) ([789f0a5](https://github.com/ReVanced/revanced-patches/commit/789f0a562861825065633d172445ebf35a1ba8d8))
|
||||||
|
|
||||||
|
# [5.48.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.4...v5.48.0-dev.5) (2025-12-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Disney+ - Skip ads:** Remove unsupported package names ([#6422](https://github.com/ReVanced/revanced-patches/issues/6422)) ([44e7dbc](https://github.com/ReVanced/revanced-patches/commit/44e7dbcf4d7eaf94dd0164baba847d3e19250154))
|
||||||
|
|
||||||
|
# [5.48.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.3...v5.48.0-dev.4) (2025-12-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Strava:** Add `Block Snowplow tracking` patch ([#6413](https://github.com/ReVanced/revanced-patches/issues/6413)) ([c47beae](https://github.com/ReVanced/revanced-patches/commit/c47beae21376dd17ab8bc09afe73e9094481bde9))
|
||||||
|
|
||||||
|
# [5.48.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.2...v5.48.0-dev.3) (2025-12-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Fix compilation error introduced in `6bb6281` ([#6409](https://github.com/ReVanced/revanced-patches/issues/6409)) ([71c6cb5](https://github.com/ReVanced/revanced-patches/commit/71c6cb569ebf7b93cf73ee391839e5220557ce7c))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Instagram - Hides navigation buttons:** Add more buttons to hide ([#6390](https://github.com/ReVanced/revanced-patches/issues/6390)) ([6bb6281](https://github.com/ReVanced/revanced-patches/commit/6bb62811493da04812cc3e392e68d874f95cbef9))
|
||||||
|
|
||||||
# [5.48.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.1...v5.48.0-dev.2) (2025-12-27)
|
# [5.48.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.1...v5.48.0-dev.2) (2025-12-27)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
android.useAndroidX = true
|
android.useAndroidX = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 5.48.0-dev.2
|
version = 5.48.0-dev.8
|
||||||
|
|||||||
@@ -352,6 +352,10 @@ public final class app/revanced/patches/letterboxd/ads/HideAdsPatchKt {
|
|||||||
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/letterboxd/unlock/unlockAppIcons/UnlockAppIconsPatchKt {
|
||||||
|
public static final fun getUnlockAppIconsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt {
|
public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt {
|
||||||
public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1188,10 +1192,22 @@ public final class app/revanced/patches/stocard/layout/HideStoryBubblesPatchKt {
|
|||||||
public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/strava/mediaupload/OverwriteMediaUploadParametersPatchKt {
|
||||||
|
public static final fun getOverwriteMediaUploadParametersPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/password/EnablePasswordLoginPatchKt {
|
public final class app/revanced/patches/strava/password/EnablePasswordLoginPatchKt {
|
||||||
public static final fun getEnablePasswordLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getEnablePasswordLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/strava/privacy/BlockSnowplowTrackingPatchKt {
|
||||||
|
public static final fun getBlockSnowplowTrackingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/strava/quickedit/DisableQuickEditPatchKt {
|
||||||
|
public static final fun getDisableQuickEditPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatchKt {
|
public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatchKt {
|
||||||
public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1967,6 +1983,7 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||||
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
||||||
public static final fun literal (Lapp/revanced/patcher/FingerprintBuilder;Lkotlin/jvm/functions/Function0;)V
|
public static final fun literal (Lapp/revanced/patcher/FingerprintBuilder;Lkotlin/jvm/functions/Function0;)V
|
||||||
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
||||||
@@ -1976,7 +1993,6 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V
|
||||||
public static synthetic fun returnEarly$default (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ZILjava/lang/Object;)V
|
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
||||||
|
|||||||
@@ -8,11 +8,7 @@ val skipAdsPatch = bytecodePatch(
|
|||||||
name = "Skip ads",
|
name = "Skip ads",
|
||||||
description = "Automatically skips ads.",
|
description = "Automatically skips ads.",
|
||||||
) {
|
) {
|
||||||
compatibleWith(
|
compatibleWith("com.disney.disneyplus")
|
||||||
"com.disney.disneyplus",
|
|
||||||
"in.startv.hotstar",
|
|
||||||
"in.startv.hotstaronly",
|
|
||||||
)
|
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
arrayOf(insertionGetPointsFingerprint, insertionGetRangesFingerprint).forEach {
|
arrayOf(insertionGetPointsFingerprint, insertionGetRangesFingerprint).forEach {
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
|
|
||||||
dependsOn(sharedExtensionPatch)
|
dependsOn(sharedExtensionPatch)
|
||||||
|
|
||||||
|
val hideHome by booleanOption(
|
||||||
|
key = "hideHome",
|
||||||
|
default = false,
|
||||||
|
title = "Hide Home",
|
||||||
|
description = "Permanently hides the Home button. App starts at next available tab." // On the "homecoming" / current instagram layout.
|
||||||
|
)
|
||||||
|
|
||||||
val hideReels by booleanOption(
|
val hideReels by booleanOption(
|
||||||
key = "hideReels",
|
key = "hideReels",
|
||||||
default = true,
|
default = true,
|
||||||
@@ -35,6 +42,27 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
description = "Permanently hides the Reels button."
|
description = "Permanently hides the Reels button."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val hideDirect by booleanOption(
|
||||||
|
key = "hideDirect",
|
||||||
|
default = false,
|
||||||
|
title = "Hide Direct",
|
||||||
|
description = "Permanently hides the Direct button."
|
||||||
|
)
|
||||||
|
|
||||||
|
val hideSearch by booleanOption(
|
||||||
|
key = "hideSearch",
|
||||||
|
default = false,
|
||||||
|
title = "Hide Search",
|
||||||
|
description = "Permanently hides the Search button."
|
||||||
|
)
|
||||||
|
|
||||||
|
val hideProfile by booleanOption(
|
||||||
|
key = "hideProfile",
|
||||||
|
default = false,
|
||||||
|
title = "Hide Profile",
|
||||||
|
description = "Permanently hides the Profile button."
|
||||||
|
)
|
||||||
|
|
||||||
val hideCreate by booleanOption(
|
val hideCreate by booleanOption(
|
||||||
key = "hideCreate",
|
key = "hideCreate",
|
||||||
default = true,
|
default = true,
|
||||||
@@ -43,7 +71,7 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
if (!hideReels!! && !hideCreate!!) {
|
if (!hideHome!! &&!hideReels!! && !hideDirect!! && !hideSearch!! && !hideProfile!! && !hideCreate!!) {
|
||||||
return@execute Logger.getLogger(this::class.java.name).warning(
|
return@execute Logger.getLogger(this::class.java.name).warning(
|
||||||
"No hide navigation buttons options are enabled. No changes made."
|
"No hide navigation buttons options are enabled. No changes made."
|
||||||
)
|
)
|
||||||
@@ -76,6 +104,13 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hideHome!!) {
|
||||||
|
addInstructionsAtControlFlowLabel(
|
||||||
|
returnIndex,
|
||||||
|
instructionsRemoveButtonByName("fragment_feed")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (hideReels!!) {
|
if (hideReels!!) {
|
||||||
addInstructionsAtControlFlowLabel(
|
addInstructionsAtControlFlowLabel(
|
||||||
returnIndex,
|
returnIndex,
|
||||||
@@ -83,12 +118,33 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hideDirect!!) {
|
||||||
|
addInstructionsAtControlFlowLabel(
|
||||||
|
returnIndex,
|
||||||
|
instructionsRemoveButtonByName("fragment_direct_tab")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (hideSearch!!) {
|
||||||
|
addInstructionsAtControlFlowLabel(
|
||||||
|
returnIndex,
|
||||||
|
instructionsRemoveButtonByName("fragment_search")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (hideCreate!!) {
|
if (hideCreate!!) {
|
||||||
addInstructionsAtControlFlowLabel(
|
addInstructionsAtControlFlowLabel(
|
||||||
returnIndex,
|
returnIndex,
|
||||||
instructionsRemoveButtonByName("fragment_share")
|
instructionsRemoveButtonByName("fragment_share")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hideProfile!!) {
|
||||||
|
addInstructionsAtControlFlowLabel(
|
||||||
|
returnIndex,
|
||||||
|
instructionsRemoveButtonByName("fragment_profile")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.letterboxd.unlock.unlockAppIcons
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
|
internal val getCanChangeAppIconFingerprint = fingerprint {
|
||||||
|
custom { method, classDef ->
|
||||||
|
method.name == "getCanChangeAppIcon" && classDef.type.endsWith("SettingsAppIconFragment;")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
package app.revanced.patches.letterboxd.unlock.unlockAppIcons
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val unlockAppIconsPatch = bytecodePatch(
|
||||||
|
name = "Unlock app icons",
|
||||||
|
) {
|
||||||
|
compatibleWith("com.letterboxd.letterboxd")
|
||||||
|
|
||||||
|
execute {
|
||||||
|
getCanChangeAppIconFingerprint.method.returnEarly(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package app.revanced.patches.strava.mediaupload
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
|
internal val getCompressionQualityFingerprint = fingerprint {
|
||||||
|
custom { method, _ ->
|
||||||
|
method.name == "getCompressionQuality"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val getMaxDurationFingerprint = fingerprint {
|
||||||
|
custom { method, _ ->
|
||||||
|
method.name == "getMaxDuration"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val getMaxSizeFingerprint = fingerprint {
|
||||||
|
custom { method, _ ->
|
||||||
|
method.name == "getMaxSize"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package app.revanced.patches.strava.mediaupload
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patcher.patch.intOption
|
||||||
|
import app.revanced.patcher.patch.longOption
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val overwriteMediaUploadParametersPatch = bytecodePatch(
|
||||||
|
name = "Overwrite media upload parameters",
|
||||||
|
description = "Overwrites the compression, resize and trim media (images and videos) parameters returned by Strava's server before upload.",
|
||||||
|
) {
|
||||||
|
compatibleWith("com.strava")
|
||||||
|
|
||||||
|
val compressionQuality by intOption(
|
||||||
|
key = "compressionQuality",
|
||||||
|
title = "Compression quality (percent)",
|
||||||
|
description = "This is used as the JPEG quality setting (≤ 100).",
|
||||||
|
) { it == null || it in 1..100 }
|
||||||
|
|
||||||
|
val maxDuration by longOption(
|
||||||
|
key = "maxDuration",
|
||||||
|
title = "Max duration (seconds)",
|
||||||
|
description = "The maximum length (≤ ${60 * 60}) of a video before it gets trimmed.",
|
||||||
|
) { it == null || it in 1..60 * 60 }
|
||||||
|
|
||||||
|
val maxSize by intOption(
|
||||||
|
key = "maxSize",
|
||||||
|
title = "Max size (pixels)",
|
||||||
|
description = "The image gets resized so that the smaller dimension (width/height) does not exceed this value (≤ 10000).",
|
||||||
|
) { it == null || it in 1..10000 }
|
||||||
|
|
||||||
|
execute {
|
||||||
|
val mediaUploadParametersClass = classes.single { it.endsWith("/MediaUploadParameters;") }
|
||||||
|
|
||||||
|
compressionQuality?.let { compressionQuality ->
|
||||||
|
getCompressionQualityFingerprint.match(mediaUploadParametersClass).method.returnEarly(compressionQuality / 100f)
|
||||||
|
}
|
||||||
|
|
||||||
|
maxDuration?.let { maxDuration ->
|
||||||
|
getMaxDurationFingerprint.match(mediaUploadParametersClass).method.returnEarly(maxDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
maxSize?.let {
|
||||||
|
getMaxSizeFingerprint.match(mediaUploadParametersClass).method.returnEarly(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package app.revanced.patches.strava.password
|
package app.revanced.patches.strava.password
|
||||||
|
|
||||||
import app.revanced.patcher.Fingerprint
|
import app.revanced.patcher.Fingerprint
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val enablePasswordLoginPatch = bytecodePatch(
|
val enablePasswordLoginPatch = bytecodePatch(
|
||||||
@@ -12,10 +12,9 @@ val enablePasswordLoginPatch = bytecodePatch(
|
|||||||
compatibleWith("com.strava")
|
compatibleWith("com.strava")
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
fun Fingerprint.loadTrueInsteadOfField() =
|
fun Fingerprint.returnTrue() = method.returnEarly(true)
|
||||||
method.replaceInstruction(patternMatch!!.startIndex, "const/4 v0, 0x1")
|
|
||||||
|
|
||||||
logInGetUsePasswordFingerprint.loadTrueInsteadOfField()
|
logInGetUsePasswordFingerprint.returnTrue()
|
||||||
emailChangeGetUsePasswordFingerprint.loadTrueInsteadOfField()
|
emailChangeGetUsePasswordFingerprint.returnTrue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
package app.revanced.patches.strava.password
|
package app.revanced.patches.strava.password
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
internal val logInGetUsePasswordFingerprint = fingerprint {
|
internal val logInGetUsePasswordFingerprint = fingerprint {
|
||||||
opcodes(Opcode.IGET_BOOLEAN)
|
|
||||||
custom { method, classDef ->
|
custom { method, classDef ->
|
||||||
method.name == "getUsePassword" && classDef.endsWith("/RequestOtpLogInNetworkResponse;")
|
method.name == "getUsePassword" && classDef.endsWith("/RequestOtpLogInNetworkResponse;")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val emailChangeGetUsePasswordFingerprint = fingerprint {
|
internal val emailChangeGetUsePasswordFingerprint = fingerprint {
|
||||||
opcodes(Opcode.IGET_BOOLEAN)
|
|
||||||
custom { method, classDef ->
|
custom { method, classDef ->
|
||||||
method.name == "getUsePassword" && classDef.endsWith("/RequestEmailChangeWithOtpOrPasswordResponse;")
|
method.name == "getUsePassword" && classDef.endsWith("/RequestEmailChangeWithOtpOrPasswordResponse;")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package app.revanced.patches.strava.privacy
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val blockSnowplowTrackingPatch = bytecodePatch(
|
||||||
|
name = "Block Snowplow tracking",
|
||||||
|
description = "Blocks Snowplow analytics. See https://snowplow.io for more information.",
|
||||||
|
) {
|
||||||
|
compatibleWith("com.strava")
|
||||||
|
|
||||||
|
execute {
|
||||||
|
// Keep events list empty, otherwise sent to https://c.strava.com/com.snowplowanalytics.snowplow/tp2.
|
||||||
|
insertEventFingerprint.method.returnEarly()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.strava.privacy
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
|
// https://github.com/snowplow/snowplow-android-tracker/blob/2.2.0/snowplow-tracker/src/main/java/com/snowplowanalytics/snowplow/internal/emitter/storage/SQLiteEventStore.java#L130
|
||||||
|
// Not the exact same code (e.g. returns void instead of long), even though the version number matches.
|
||||||
|
internal val insertEventFingerprint = fingerprint {
|
||||||
|
strings("Added event to database: %s")
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.strava.quickedit
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val disableQuickEditPatch = bytecodePatch(
|
||||||
|
name = "Disable Quick Edit",
|
||||||
|
description = "Prevents the Quick Edit prompt from popping up.",
|
||||||
|
) {
|
||||||
|
compatibleWith("com.strava")
|
||||||
|
|
||||||
|
execute {
|
||||||
|
getHasAccessToQuickEditFingerprint.method.returnEarly()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.patches.strava.quickedit
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
|
internal val getHasAccessToQuickEditFingerprint = fingerprint {
|
||||||
|
returns("Z")
|
||||||
|
custom { method, _ ->
|
||||||
|
method.name == "getHasAccessToQuickEdit"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
package app.revanced.patches.strava.subscription
|
package app.revanced.patches.strava.subscription
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
internal val getSubscribedFingerprint = fingerprint {
|
internal val getSubscribedFingerprint = fingerprint {
|
||||||
opcodes(Opcode.IGET_BOOLEAN)
|
|
||||||
custom { method, classDef ->
|
custom { method, classDef ->
|
||||||
classDef.endsWith("/SubscriptionDetailResponse;") && method.name == "getSubscribed"
|
method.name == "getSubscribed" && classDef.endsWith("/SubscriptionDetailResponse;")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.strava.subscription
|
package app.revanced.patches.strava.subscription
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val unlockSubscriptionPatch = bytecodePatch(
|
val unlockSubscriptionPatch = bytecodePatch(
|
||||||
@@ -11,9 +11,6 @@ val unlockSubscriptionPatch = bytecodePatch(
|
|||||||
compatibleWith("com.strava")
|
compatibleWith("com.strava")
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
getSubscribedFingerprint.method.replaceInstruction(
|
getSubscribedFingerprint.method.returnEarly(true)
|
||||||
getSubscribedFingerprint.patternMatch!!.startIndex,
|
|
||||||
"const/4 v0, 0x1",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,22 +23,19 @@ import app.revanced.util.InstructionUtils.Companion.writeOpcodes
|
|||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.Opcode.*
|
import com.android.tools.smali.dexlib2.Opcode.*
|
||||||
|
import com.android.tools.smali.dexlib2.analysis.reflection.util.ReflectionUtils
|
||||||
|
import com.android.tools.smali.dexlib2.formatter.DexFormatter
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.*
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.Reference
|
import com.android.tools.smali.dexlib2.iface.reference.Reference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.value.*
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.value.*
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
import java.util.EnumSet
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starting from and including the instruction at index [startIndex],
|
* Starting from and including the instruction at index [startIndex],
|
||||||
@@ -180,7 +177,7 @@ internal val Instruction.isReturnInstruction: Boolean
|
|||||||
*
|
*
|
||||||
* @param fieldName The name of the field to find. Partial matches are allowed.
|
* @param fieldName The name of the field to find. Partial matches are allowed.
|
||||||
*/
|
*/
|
||||||
private fun Method.findInstructionIndexFromToString(fieldName: String) : Int {
|
private fun Method.findInstructionIndexFromToString(fieldName: String): Int {
|
||||||
val stringIndex = indexOfFirstInstruction {
|
val stringIndex = indexOfFirstInstruction {
|
||||||
val reference = getReference<StringReference>()
|
val reference = getReference<StringReference>()
|
||||||
reference?.string?.contains(fieldName) == true
|
reference?.string?.contains(fieldName) == true
|
||||||
@@ -233,7 +230,7 @@ private fun Method.findInstructionIndexFromToString(fieldName: String) : Int {
|
|||||||
* @param fieldName The name of the field to find. Partial matches are allowed.
|
* @param fieldName The name of the field to find. Partial matches are allowed.
|
||||||
*/
|
*/
|
||||||
context(BytecodePatchContext)
|
context(BytecodePatchContext)
|
||||||
internal fun Method.findMethodFromToString(fieldName: String) : MutableMethod {
|
internal fun Method.findMethodFromToString(fieldName: String): MutableMethod {
|
||||||
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
|
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
|
||||||
return navigate(this).to(methodUsageIndex).stop()
|
return navigate(this).to(methodUsageIndex).stop()
|
||||||
}
|
}
|
||||||
@@ -243,7 +240,7 @@ internal fun Method.findMethodFromToString(fieldName: String) : MutableMethod {
|
|||||||
*
|
*
|
||||||
* @param fieldName The name of the field to find. Partial matches are allowed.
|
* @param fieldName The name of the field to find. Partial matches are allowed.
|
||||||
*/
|
*/
|
||||||
internal fun Method.findFieldFromToString(fieldName: String) : FieldReference {
|
internal fun Method.findFieldFromToString(fieldName: String): FieldReference {
|
||||||
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
|
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
|
||||||
return getInstruction<ReferenceInstruction>(methodUsageIndex).getReference<FieldReference>()!!
|
return getInstruction<ReferenceInstruction>(methodUsageIndex).getReference<FieldReference>()!!
|
||||||
}
|
}
|
||||||
@@ -838,23 +835,59 @@ fun BytecodePatchContext.forEachLiteralValueInstruction(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val RETURN_TYPE_MISMATCH = "Mismatch between override type and Method return type"
|
private fun MutableMethod.checkReturnType(expectedTypes: Iterable<Class<*>>) {
|
||||||
|
val returnTypeJava = ReflectionUtils.dexToJavaName(returnType)
|
||||||
|
check(expectedTypes.any { returnTypeJava == it.name }) {
|
||||||
|
"Actual return type $returnTypeJava is not contained in expected types: $expectedTypes"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides the first instruction of a method with a constant `Boolean` return value.
|
* Overrides the first instruction of a method with returning the default value for the type (or `void`).
|
||||||
* None of the method code will ever execute.
|
* None of the method code will ever execute.
|
||||||
*
|
*
|
||||||
* For methods that return an object or any array type, calling this method with `false`
|
|
||||||
* will force the method to return a `null` value.
|
|
||||||
*
|
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Boolean = false) {
|
fun MutableMethod.returnEarly() {
|
||||||
val returnType = returnType.first()
|
val value = when (returnType) {
|
||||||
check(returnType == 'Z' || (!value && (returnType == 'V' || returnType == 'L' || returnType != '['))) {
|
"V" -> null
|
||||||
RETURN_TYPE_MISMATCH
|
"Z" -> ImmutableBooleanEncodedValue.FALSE_VALUE
|
||||||
|
"B" -> ImmutableByteEncodedValue(0)
|
||||||
|
"S" -> ImmutableShortEncodedValue(0)
|
||||||
|
"C" -> ImmutableCharEncodedValue(Char.MIN_VALUE)
|
||||||
|
"I" -> ImmutableIntEncodedValue(0)
|
||||||
|
"F" -> ImmutableFloatEncodedValue(0f)
|
||||||
|
"J" -> ImmutableLongEncodedValue(0)
|
||||||
|
"D" -> ImmutableDoubleEncodedValue(0.0)
|
||||||
|
else -> ImmutableNullEncodedValue.INSTANCE
|
||||||
}
|
}
|
||||||
overrideReturnValue(value.toHexString(), false)
|
overrideReturnValue(value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableMethod.returnString(value: String, late: Boolean) {
|
||||||
|
checkReturnType(String::class.java.allAssignableTypes())
|
||||||
|
overrideReturnValue(ImmutableStringEncodedValue(value), late)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the first instruction of a method with a constant `String` return value.
|
||||||
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnEarly(value: String) = returnString(value, false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant `String` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: String) = returnString(value, true)
|
||||||
|
|
||||||
|
private fun MutableMethod.returnByte(value: Byte, late: Boolean) {
|
||||||
|
checkReturnType(Byte::class.javaObjectType.allAssignableTypes() + Byte::class.javaPrimitiveType!!)
|
||||||
|
overrideReturnValue(ImmutableByteEncodedValue(value), late)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -863,9 +896,40 @@ fun MutableMethod.returnEarly(value: Boolean = false) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Byte) {
|
fun MutableMethod.returnEarly(value: Byte) = returnByte(value, false)
|
||||||
check(returnType.first() == 'B') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), false)
|
/**
|
||||||
|
* Overrides all return statements with a constant `Byte` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Byte) = returnByte(value, true)
|
||||||
|
|
||||||
|
private fun MutableMethod.returnBoolean(value: Boolean, late: Boolean) {
|
||||||
|
checkReturnType(Boolean::class.javaObjectType.allAssignableTypes() + Boolean::class.javaPrimitiveType!!)
|
||||||
|
overrideReturnValue(ImmutableBooleanEncodedValue.forBoolean(value), late)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the first instruction of a method with a constant `Boolean` return value.
|
||||||
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnEarly(value: Boolean) = returnBoolean(value, false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant `Boolean` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Boolean) = returnBoolean(value, true)
|
||||||
|
|
||||||
|
private fun MutableMethod.returnShort(value: Short, late: Boolean) {
|
||||||
|
checkReturnType(Short::class.javaObjectType.allAssignableTypes() + Short::class.javaPrimitiveType!!)
|
||||||
|
overrideReturnValue(ImmutableShortEncodedValue(value), late)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -874,9 +938,19 @@ fun MutableMethod.returnEarly(value: Byte) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Short) {
|
fun MutableMethod.returnEarly(value: Short) = returnShort(value, false)
|
||||||
check(returnType.first() == 'S') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), false)
|
/**
|
||||||
|
* Overrides all return statements with a constant `Short` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Short) = returnShort(value, true)
|
||||||
|
|
||||||
|
private fun MutableMethod.returnChar(value: Char, late: Boolean) {
|
||||||
|
checkReturnType(Char::class.javaObjectType.allAssignableTypes() + Char::class.javaPrimitiveType!!)
|
||||||
|
overrideReturnValue(ImmutableCharEncodedValue(value), late)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -885,9 +959,19 @@ fun MutableMethod.returnEarly(value: Short) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Char) {
|
fun MutableMethod.returnEarly(value: Char) = returnChar(value, false)
|
||||||
check(returnType.first() == 'C') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.code.toString(), false)
|
/**
|
||||||
|
* Overrides all return statements with a constant `Char` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Char) = returnChar(value, true)
|
||||||
|
|
||||||
|
private fun MutableMethod.returnInt(value: Int, late: Boolean) {
|
||||||
|
checkReturnType(Int::class.javaObjectType.allAssignableTypes() + Int::class.javaPrimitiveType!!)
|
||||||
|
overrideReturnValue(ImmutableIntEncodedValue(value), late)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -896,20 +980,19 @@ fun MutableMethod.returnEarly(value: Char) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Int) {
|
fun MutableMethod.returnEarly(value: Int) = returnInt(value, false)
|
||||||
check(returnType.first() == 'I') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides the first instruction of a method with a constant `Long` return value.
|
* Overrides all return statements with a constant `Int` value.
|
||||||
* None of the method code will ever execute.
|
* All method code is executed the same as unpatched.
|
||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnEarly
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Long) {
|
fun MutableMethod.returnLate(value: Int) = returnInt(value, true)
|
||||||
check(returnType.first() == 'J') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), false)
|
private fun MutableMethod.returnFloat(value: Float, late: Boolean) {
|
||||||
|
checkReturnType(Float::class.javaObjectType.allAssignableTypes() + Float::class.javaPrimitiveType!!)
|
||||||
|
overrideReturnValue(ImmutableFloatEncodedValue(value), late)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -918,9 +1001,40 @@ fun MutableMethod.returnEarly(value: Long) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Float) {
|
fun MutableMethod.returnEarly(value: Float) = returnFloat(value, false)
|
||||||
check(returnType.first() == 'F') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), false)
|
/**
|
||||||
|
* Overrides all return statements with a constant `Float` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Float) = returnFloat(value, true)
|
||||||
|
|
||||||
|
private fun MutableMethod.returnLong(value: Long, late: Boolean) {
|
||||||
|
checkReturnType(Long::class.javaObjectType.allAssignableTypes() + Long::class.javaPrimitiveType!!)
|
||||||
|
overrideReturnValue(ImmutableLongEncodedValue(value), late)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the first instruction of a method with a constant `Long` return value.
|
||||||
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnEarly(value: Long) = returnLong(value, false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant `Long` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Long) = returnLong(value, true)
|
||||||
|
|
||||||
|
private fun MutableMethod.returnDouble(value: Double, late: Boolean) {
|
||||||
|
checkReturnType(Double::class.javaObjectType.allAssignableTypes() + Double::class.javaPrimitiveType!!)
|
||||||
|
overrideReturnValue(ImmutableDoubleEncodedValue(value), late)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -929,113 +1043,7 @@ fun MutableMethod.returnEarly(value: Float) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Double) {
|
fun MutableMethod.returnEarly(value: Double) = returnDouble(value, false)
|
||||||
check(returnType.first() == 'J') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides the first instruction of a method with a constant String return value.
|
|
||||||
* None of the method code will ever execute.
|
|
||||||
*
|
|
||||||
* Target method must have return type
|
|
||||||
* Ljava/lang/String; or Ljava/lang/CharSequence;
|
|
||||||
*
|
|
||||||
* @see returnLate
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnEarly(value: String) {
|
|
||||||
check(returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;") {
|
|
||||||
RETURN_TYPE_MISMATCH
|
|
||||||
}
|
|
||||||
overrideReturnValue(value, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all return statements with a constant `Boolean` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* For methods that return an object or any array type, calling this method with `false`
|
|
||||||
* will force the method to return a `null` value.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Boolean) {
|
|
||||||
val returnType = returnType.first()
|
|
||||||
if (returnType == 'V') {
|
|
||||||
error("Cannot return late for Method of void type")
|
|
||||||
}
|
|
||||||
check(returnType == 'Z' || (!value && (returnType == 'L' || returnType == '['))) {
|
|
||||||
RETURN_TYPE_MISMATCH
|
|
||||||
}
|
|
||||||
|
|
||||||
overrideReturnValue(value.toHexString(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all return statements with a constant `Byte` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Byte) {
|
|
||||||
check(returnType.first() == 'B') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all return statements with a constant `Short` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Short) {
|
|
||||||
check(returnType.first() == 'S') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all return statements with a constant `Char` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Char) {
|
|
||||||
check(returnType.first() == 'C') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.code.toString(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all return statements with a constant `Int` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Int) {
|
|
||||||
check(returnType.first() == 'I') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all return statements with a constant `Long` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Long) {
|
|
||||||
check(returnType.first() == 'J') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all return statements with a constant `Float` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Float) {
|
|
||||||
check(returnType.first() == 'F') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides all return statements with a constant `Double` value.
|
* Overrides all return statements with a constant `Double` value.
|
||||||
@@ -1043,75 +1051,164 @@ fun MutableMethod.returnLate(value: Float) {
|
|||||||
*
|
*
|
||||||
* @see returnEarly
|
* @see returnEarly
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnLate(value: Double) {
|
fun MutableMethod.returnLate(value: Double) = returnDouble(value, true)
|
||||||
check(returnType.first() == 'D') { RETURN_TYPE_MISMATCH }
|
|
||||||
overrideReturnValue(value.toString(), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private fun MutableMethod.overrideReturnValue(value: EncodedValue?, returnLate: Boolean) {
|
||||||
* Overrides all return statements with a constant String value.
|
val instructions = if (value == null) {
|
||||||
* All method code is executed the same as unpatched.
|
require(!returnLate) {
|
||||||
*
|
"Cannot return late for method of void type"
|
||||||
* Target method must have return type
|
}
|
||||||
* Ljava/lang/String; or Ljava/lang/CharSequence;
|
"return-void"
|
||||||
*
|
} else {
|
||||||
* @see returnEarly
|
val encodedValue = DexFormatter.INSTANCE.getEncodedValue(value)
|
||||||
*/
|
when (value) {
|
||||||
fun MutableMethod.returnLate(value: String) {
|
is NullEncodedValue -> {
|
||||||
check(returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;") {
|
"""
|
||||||
RETURN_TYPE_MISMATCH
|
|
||||||
}
|
|
||||||
overrideReturnValue(value, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun MutableMethod.overrideReturnValue(value: String, returnLate: Boolean) {
|
|
||||||
val instructions = if (returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;" ) {
|
|
||||||
"""
|
|
||||||
const-string v0, "$value"
|
|
||||||
return-object v0
|
|
||||||
"""
|
|
||||||
} else when (returnType.first()) {
|
|
||||||
// If return type is an object, always return null.
|
|
||||||
'L', '[' -> {
|
|
||||||
"""
|
|
||||||
const/4 v0, 0x0
|
const/4 v0, 0x0
|
||||||
return-object v0
|
return-object v0
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
'V' -> {
|
is StringEncodedValue -> {
|
||||||
"return-void"
|
"""
|
||||||
}
|
const-string v0, $encodedValue
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
'B', 'Z' -> {
|
is ByteEncodedValue -> {
|
||||||
"""
|
if (returnType == "B") {
|
||||||
const/4 v0, $value
|
"""
|
||||||
return v0
|
const/4 v0, $encodedValue
|
||||||
"""
|
return v0
|
||||||
}
|
"""
|
||||||
|
} else {
|
||||||
|
"""
|
||||||
|
const/4 v0, $encodedValue
|
||||||
|
invoke-static { v0 }, Ljava/lang/Byte;->valueOf(B)Ljava/lang/Byte;
|
||||||
|
move-result-object v0
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
'S', 'C' -> {
|
is BooleanEncodedValue -> {
|
||||||
"""
|
val encodedValue = value.value.toHexString()
|
||||||
const/16 v0, $value
|
if (returnType == "Z") {
|
||||||
return v0
|
"""
|
||||||
"""
|
const/4 v0, $encodedValue
|
||||||
}
|
return v0
|
||||||
|
"""
|
||||||
|
} else {
|
||||||
|
"""
|
||||||
|
const/4 v0, $encodedValue
|
||||||
|
invoke-static { v0 }, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;
|
||||||
|
move-result-object v0
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
'I', 'F' -> {
|
is ShortEncodedValue -> {
|
||||||
"""
|
if (returnType == "S") {
|
||||||
const v0, $value
|
"""
|
||||||
return v0
|
const/16 v0, $encodedValue
|
||||||
"""
|
return v0
|
||||||
}
|
"""
|
||||||
|
} else {
|
||||||
|
"""
|
||||||
|
const/16 v0, $encodedValue
|
||||||
|
invoke-static { v0 }, Ljava/lang/Short;->valueOf(S)Ljava/lang/Short;
|
||||||
|
move-result-object v0
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
'J', 'D' -> {
|
is CharEncodedValue -> {
|
||||||
"""
|
if (returnType == "C") {
|
||||||
const-wide v0, $value
|
"""
|
||||||
return-wide v0
|
const/16 v0, $encodedValue
|
||||||
"""
|
return v0
|
||||||
}
|
"""
|
||||||
|
} else {
|
||||||
|
"""
|
||||||
|
const/16 v0, $encodedValue
|
||||||
|
invoke-static { v0 }, Ljava/lang/Character;->valueOf(C)Ljava/lang/Character;
|
||||||
|
move-result-object v0
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> throw Exception("Return type is not supported: $this")
|
is IntEncodedValue -> {
|
||||||
|
if (returnType == "I") {
|
||||||
|
"""
|
||||||
|
const v0, $encodedValue
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
} else {
|
||||||
|
"""
|
||||||
|
const v0, $encodedValue
|
||||||
|
invoke-static { v0 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
|
||||||
|
move-result-object v0
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is FloatEncodedValue -> {
|
||||||
|
val encodedValue = "${encodedValue}f"
|
||||||
|
if (returnType == "F") {
|
||||||
|
"""
|
||||||
|
const v0, $encodedValue
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
} else {
|
||||||
|
"""
|
||||||
|
const v0, $encodedValue
|
||||||
|
invoke-static { v0 }, Ljava/lang/Float;->valueOf(F)Ljava/lang/Float;
|
||||||
|
move-result-object v0
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongEncodedValue -> {
|
||||||
|
val encodedValue = "${encodedValue}L"
|
||||||
|
if (returnType == "J") {
|
||||||
|
"""
|
||||||
|
const-wide v0, $encodedValue
|
||||||
|
return-wide v0
|
||||||
|
"""
|
||||||
|
} else {
|
||||||
|
"""
|
||||||
|
const-wide v0, $encodedValue
|
||||||
|
invoke-static { v0 }, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;
|
||||||
|
move-result-object v0
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is DoubleEncodedValue -> {
|
||||||
|
if (returnType == "D") {
|
||||||
|
"""
|
||||||
|
const-wide v0, $encodedValue
|
||||||
|
return-wide v0
|
||||||
|
"""
|
||||||
|
} else {
|
||||||
|
"""
|
||||||
|
const-wide v0, $encodedValue
|
||||||
|
invoke-static { v0 }, Ljava/lang/Double;->valueOf(D)Ljava/lang/Double;
|
||||||
|
move-result-object v0
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw IllegalArgumentException("Value $value cannot be returned from $this")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnLate) {
|
if (returnLate) {
|
||||||
|
|||||||
@@ -7,4 +7,21 @@ internal object Utils {
|
|||||||
.trimIndent() // Remove the leading newline.
|
.trimIndent() // Remove the leading newline.
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Boolean.toHexString(): String = if (this) "0x1" else "0x0"
|
internal fun Boolean.toHexString(): String = if (this) "0x1" else "0x0"
|
||||||
|
|
||||||
|
internal fun Class<*>.allAssignableTypes(): Set<Class<*>> {
|
||||||
|
val result = mutableSetOf<Class<*>>()
|
||||||
|
|
||||||
|
fun visit(child: Class<*>?) {
|
||||||
|
if (child == null || !result.add(child)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
child.interfaces.forEach(::visit)
|
||||||
|
visit(child.superclass)
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(this)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user