diff --git a/build.gradle.kts b/build.gradle.kts index b98a3e14..0328d2f2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,6 +33,10 @@ dependencies { implementation("com.google.code.gson:gson:2.10.1") // Required for FlexVer-Java implementation("com.unascribed:flexver-java:1.0.2") + + // A dependency to the Android library unfortunately fails the build, + // which is why this is required for the patch change-oauth-client-id + compileOnly(project("dummy")) } kotlin { diff --git a/dummy/build.gradle.kts b/dummy/build.gradle.kts new file mode 100644 index 00000000..04b0d269 --- /dev/null +++ b/dummy/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + id("java") +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} \ No newline at end of file diff --git a/dummy/src/main/java/android/os/Environment.java b/dummy/src/main/java/android/os/Environment.java new file mode 100644 index 00000000..d79cc2ff --- /dev/null +++ b/dummy/src/main/java/android/os/Environment.java @@ -0,0 +1,7 @@ +package android.os; + +public final class Environment { + public static String getExternalStorageDirectory() { + throw new UnsupportedOperationException("Stub"); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 472c476e..45c24f8f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1,3 @@ +include("dummy") + rootProject.name = "revanced-patches" diff --git a/src/main/kotlin/app/revanced/patches/syncforreddit/ads/annotations/DisableAdsCompatibility.kt b/src/main/kotlin/app/revanced/patches/syncforreddit/ads/annotations/DisableAdsCompatibility.kt deleted file mode 100644 index c6cf4288..00000000 --- a/src/main/kotlin/app/revanced/patches/syncforreddit/ads/annotations/DisableAdsCompatibility.kt +++ /dev/null @@ -1,8 +0,0 @@ -package app.revanced.patches.syncforreddit.ads.annotations - -import app.revanced.patcher.annotation.Compatibility -import app.revanced.patcher.annotation.Package - -@Compatibility([Package("com.laurencedawson.reddit_sync")]) -@Target(AnnotationTarget.CLASS) -internal annotation class DisableAdsCompatibility diff --git a/src/main/kotlin/app/revanced/patches/syncforreddit/ads/patch/DisableAdsPatch.kt b/src/main/kotlin/app/revanced/patches/syncforreddit/ads/patch/DisableAdsPatch.kt index 648d15fb..2b15bf39 100644 --- a/src/main/kotlin/app/revanced/patches/syncforreddit/ads/patch/DisableAdsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/syncforreddit/ads/patch/DisableAdsPatch.kt @@ -1,9 +1,7 @@ package app.revanced.patches.syncforreddit.ads.patch import app.revanced.extensions.toErrorResult -import app.revanced.patcher.annotation.Description -import app.revanced.patcher.annotation.Name -import app.revanced.patcher.annotation.Version +import app.revanced.patcher.annotation.* import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.BytecodePatch @@ -11,7 +9,6 @@ import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.Patch -import app.revanced.patches.syncforreddit.ads.annotations.DisableAdsCompatibility import app.revanced.patches.syncforreddit.ads.fingerprints.IsAdsEnabledFingerprint import app.revanced.patches.syncforreddit.detection.piracy.patch.DisablePiracyDetectionPatch @@ -19,8 +16,8 @@ import app.revanced.patches.syncforreddit.detection.piracy.patch.DisablePiracyDe @Name("disable-ads") @DependsOn([DisablePiracyDetectionPatch::class]) @Description("Disables ads.") +@Compatibility([Package("com.laurencedawson.reddit_sync")]) @Version("0.0.1") -@DisableAdsCompatibility class DisableAdsPatch : BytecodePatch(listOf(IsAdsEnabledFingerprint)) { override fun execute(context: BytecodeContext): PatchResult { IsAdsEnabledFingerprint.result?.mutableMethod?.apply { diff --git a/src/main/kotlin/app/revanced/patches/syncforreddit/api/fingerprints/GetAuthorizationStringFingerprint.kt b/src/main/kotlin/app/revanced/patches/syncforreddit/api/fingerprints/GetAuthorizationStringFingerprint.kt new file mode 100644 index 00000000..8eceddfb --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/syncforreddit/api/fingerprints/GetAuthorizationStringFingerprint.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.syncforreddit.api.fingerprints + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint + +object GetAuthorizationStringFingerprint : MethodFingerprint( + strings = listOf("authorize.compact?client_id") +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/syncforreddit/api/fingerprints/GetBearerTokenFingerprint.kt b/src/main/kotlin/app/revanced/patches/syncforreddit/api/fingerprints/GetBearerTokenFingerprint.kt new file mode 100644 index 00000000..a0720872 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/syncforreddit/api/fingerprints/GetBearerTokenFingerprint.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.syncforreddit.api.fingerprints + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint + +object GetBearerTokenFingerprint : MethodFingerprint( + strings = listOf("Basic") +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/syncforreddit/api/patch/ChangeOAuthClientIdPatch.kt b/src/main/kotlin/app/revanced/patches/syncforreddit/api/patch/ChangeOAuthClientIdPatch.kt new file mode 100644 index 00000000..d3401871 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/syncforreddit/api/patch/ChangeOAuthClientIdPatch.kt @@ -0,0 +1,96 @@ +package app.revanced.patches.syncforreddit.api.patch + +import android.os.Environment +import app.revanced.patcher.annotation.* +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve +import app.revanced.patcher.patch.* +import app.revanced.patcher.patch.annotations.Patch +import app.revanced.patches.syncforreddit.api.fingerprints.GetAuthorizationStringFingerprint +import app.revanced.patches.syncforreddit.api.fingerprints.GetBearerTokenFingerprint +import org.jf.dexlib2.iface.instruction.OneRegisterInstruction +import org.jf.dexlib2.iface.instruction.ReferenceInstruction +import org.jf.dexlib2.iface.reference.StringReference +import java.io.File +import java.util.* + +@Patch +@Name("change-oauth-client-id") +@Description("Changes the OAuth client ID.") +@Compatibility([Package("com.laurencedawson.reddit_sync")]) +@Version("0.0.1") +class ChangeOAuthClientIdPatch : BytecodePatch( + listOf(GetAuthorizationStringFingerprint) +) { + override fun execute(context: BytecodeContext): PatchResult { + if (clientId == null) { + // Test if on Android + try { + Class.forName("android.os.Environment") + } catch (e: ClassNotFoundException) { + return PatchResultError("No client ID provided") + } + + File(Environment.getExternalStorageDirectory(), "reddit_client_id_revanced.txt").also { + if (it.exists()) return@also + + val error = """ + In order to use this patch, you need to provide a client ID. + You can do this by creating a file at ${it.absolutePath} with the client ID as its content. + Alternatively, you can provide the client ID using patch options. + + You can get your client ID from https://www.reddit.com/prefs/apps. + The redirect URI has to be set to "http://redditsync/auth". + """.trimIndent() + + return PatchResultError(error) + }.let { clientId = it.readText() } + } + + GetAuthorizationStringFingerprint.result?.also { result -> + GetBearerTokenFingerprint.also { it.resolve(context, result.classDef) }.result?.mutableMethod?.apply { + val auth = Base64.getEncoder().encodeToString("$clientId:".toByteArray(Charsets.UTF_8)) + addInstructions( + 0, + """ + const-string v0, "Basic $auth" + return-object v0 + """ + ) + } ?: return PatchResultError("Could not find required method to patch.") + }?.let { + val occurrenceIndex = it.scanResult.stringsScanResult!!.matches.first().index + + it.mutableMethod.apply { + val authorizationStringInstruction = getInstruction(occurrenceIndex) + val targetRegister = (authorizationStringInstruction as OneRegisterInstruction).registerA + val reference = authorizationStringInstruction.reference as StringReference + + val newAuthorizationUrl = reference.string.replace( + "client_id=.*?&".toRegex(), + "client_id=${clientId!!}&" + ) + + replaceInstruction( + occurrenceIndex, + "const-string v$targetRegister, \"$newAuthorizationUrl\"" + ) + } + } ?: return PatchResultError("Could not find required method to patch.") + return PatchResultSuccess() + } + + companion object : OptionsContainer() { + var clientId by option( + PatchOption.StringOption( + "client-id", + null, + "OAuth client ID", + "The client ID to use for OAuth." + ) + ) + } +} \ No newline at end of file