diff --git a/src/main/kotlin/app/revanced/patches/music/misc/microg/annotations/MusicMicroGPatchCompatibility.kt b/src/main/kotlin/app/revanced/patches/music/misc/microg/annotations/MusicMicroGPatchCompatibility.kt new file mode 100644 index 000000000..aaa361e67 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/music/misc/microg/annotations/MusicMicroGPatchCompatibility.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.music.misc.microg.annotations + +import app.revanced.patcher.annotation.Compatibility +import app.revanced.patcher.annotation.Package + +@Compatibility( + [Package( + "com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51") + )] +) +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +internal annotation class MusicMicroGPatchCompatibility \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/misc/microg/fingerprints/GooglePlayUtilityFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/misc/microg/fingerprints/GooglePlayUtilityFingerprint.kt new file mode 100644 index 000000000..c39c436e3 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/music/misc/microg/fingerprints/GooglePlayUtilityFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.music.misc.microg.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility +import org.jf.dexlib2.AccessFlags + +@Name("google-play-utility-fingerprint") +@MatchingMethod( + "Lnuv;", "b" +) +@DirectPatternScanMethod +@MusicMicroGPatchCompatibility +@Version("0.0.1") +object GooglePlayUtilityFingerprint : MethodFingerprint( + "I", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "I"), null, listOf("This should never happen.", "MetadataValueReader", "GooglePlayServicesUtil", "com.android.vending", "android.hardware.type.embedded") +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/misc/microg/fingerprints/PrimeFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/misc/microg/fingerprints/PrimeFingerprint.kt new file mode 100644 index 000000000..550b1d836 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/music/misc/microg/fingerprints/PrimeFingerprint.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.music.misc.microg.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility + +@Name("google-play-prime-fingerprint") +@MatchingMethod( + "Lrwi;", "a" +) +@DirectPatternScanMethod +@MusicMicroGPatchCompatibility +@Version("0.0.1") +object PrimeFingerprint : MethodFingerprint( + null, null, null, null, listOf("com.google.android.GoogleCamera", "com.android.vending") +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/misc/microg/fingerprints/ServiceCheckFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/misc/microg/fingerprints/ServiceCheckFingerprint.kt new file mode 100644 index 000000000..c4d65a7c4 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/music/misc/microg/fingerprints/ServiceCheckFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.music.misc.microg.fingerprints + +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod +import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod +import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility +import org.jf.dexlib2.AccessFlags + +@Name("google-play-service-checker-fingerprint") +@MatchingMethod( + "Lnuv;", "d" +) +@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value. +@MusicMicroGPatchCompatibility +@Version("0.0.1") +object ServiceCheckFingerprint : MethodFingerprint( + "V", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "I"), null, listOf("Google Play Services not available") +) diff --git a/src/main/kotlin/app/revanced/patches/music/misc/microg/patch/bytecode/MusicMicroGBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/music/misc/microg/patch/bytecode/MusicMicroGBytecodePatch.kt new file mode 100644 index 000000000..79abfeaf8 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/music/misc/microg/patch/bytecode/MusicMicroGBytecodePatch.kt @@ -0,0 +1,160 @@ +package app.revanced.patches.music.misc.microg.patch.bytecode + +import app.revanced.extensions.equalsAny +import app.revanced.patcher.annotation.Description +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.data.impl.BytecodeData +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.replaceInstruction +import app.revanced.patcher.patch.annotations.Dependencies +import app.revanced.patcher.patch.annotations.Patch +import app.revanced.patcher.patch.impl.BytecodePatch +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.util.proxy.mutableTypes.MutableClass +import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility +import app.revanced.patches.music.misc.microg.patch.resource.MusicMicroGResourcePatch +import app.revanced.patches.youtube.misc.microg.patch.resource.enum.StringReplaceMode +import app.revanced.patches.music.misc.microg.shared.Constants.BASE_MICROG_PACKAGE_NAME +import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME +import app.revanced.patches.music.misc.microg.fingerprints.* +import org.jf.dexlib2.Opcode +import org.jf.dexlib2.builder.MutableMethodImplementation +import org.jf.dexlib2.builder.instruction.BuilderInstruction21c +import org.jf.dexlib2.iface.instruction.formats.Instruction21c +import org.jf.dexlib2.iface.reference.StringReference +import org.jf.dexlib2.immutable.reference.ImmutableStringReference + +@Patch +@Dependencies([MusicMicroGResourcePatch::class]) +@Name("music-microg-support") +@Description("Patch to allow YouTube Music ReVanced to run without root and under a different package name.") +@MusicMicroGPatchCompatibility +@Version("0.0.1") +class MusicMicroGBytecodePatch : BytecodePatch( + listOf( + ServiceCheckFingerprint, + GooglePlayUtilityFingerprint, + PrimeFingerprint, + ) +) { + override fun execute(data: BytecodeData): PatchResult { + disablePlayServiceChecks() + data.classes.forEach { classDef -> + var proxiedClass: MutableClass? = null + + classDef.methods.forEach methodLoop@{ method -> + val implementation = method.implementation ?: return@methodLoop + + var proxiedImplementation: MutableMethodImplementation? = null + + implementation.instructions.forEachIndexed { i, instruction -> + if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed + + val stringValue = ((instruction as Instruction21c).reference as StringReference).string + + val replaceMode = if (stringValue.equalsAny( + "com.google.android.gms", + "com.google.android.gms.chimera", + "com.google.android.c2dm.intent.REGISTER", + "com.google.android.c2dm.permission.SEND", + "com.google.iid.TOKEN_REQUEST", + "com.google", + "com.google.android.gms.chimera.GmsIntentOperationService", + "com.google.android.gms.phenotype.internal.IPhenotypeCallbacks", + "com.google.android.gms.phenotype.internal.IPhenotypeService", + "com.google.android.gms.phenotype.service.START", + "com.google.android.gms.phenotype.PACKAGE_NAME", + "com.google.android.gms.phenotype.UPDATE", + "com.google.android.gms.phenotype", + "com.google.android.gms.auth.accounts", + "com.google.android.c2dm.intent.REGISTRATION", + "com.google.android.gsf.action.GET_GLS", + "com.google.android.gsf.login", + "content://com.google.settings/partner", + "content://com.google.android.gms.phenotype/", + "content://com.google.android.gsf.gservices", + "content://com.google.android.gsf.gservices/prefix", + "com.google.android.c2dm.intent.RECEIVE" + ) + ) { + StringReplaceMode.REPLACE_WITH_MICROG + } else if (stringValue.equalsAny( + "com.google.android.apps.youtube.music.SuggestionsProvider", "com.google.android.apps.youtube.music.fileprovider" + ) + ) { + StringReplaceMode.REPLACE_WITH_REVANCED + } else { + StringReplaceMode.DO_NOT_REPLACE + } + + if (replaceMode != StringReplaceMode.DO_NOT_REPLACE) { + if (proxiedClass == null) { + proxiedClass = data.proxy(classDef).resolve() + } + + if (proxiedImplementation == null) { + proxiedImplementation = proxiedClass!!.methods.first { + it.name == method.name && it.parameterTypes.containsAll(method.parameterTypes) + }.implementation!! + } + + val newString = if (replaceMode == StringReplaceMode.REPLACE_WITH_REVANCED) stringValue.replace( + "com.google.android.apps.youtube.music", REVANCED_MUSIC_PACKAGE_NAME + ) + else stringValue.replace("com.google", BASE_MICROG_PACKAGE_NAME) + + proxiedImplementation!!.replaceInstruction( + i, BuilderInstruction21c( + Opcode.CONST_STRING, instruction.registerA, ImmutableStringReference(newString) + ) + ) + } + } + } + } + + return PatchResultSuccess() + } + + private fun disablePlayServiceChecks() { + listOf( + ServiceCheckFingerprint, + GooglePlayUtilityFingerprint, + ).forEach { fingerprint -> + val result = fingerprint.result!! + val stringInstructions = when (result.method.returnType.first()) { + 'V' -> "return-void" + 'I' -> """ + const/4 v0, 0x0 + return v0 + """ + + else -> throw Exception("This case should never happen.") + } + result.mutableMethod.addInstructions( + 0, stringInstructions + ) + } + + val primeMethod = PrimeFingerprint.result!!.mutableMethod + val implementation = primeMethod.implementation!! + + var register = 2 + val index = implementation.instructions.indexOfFirst { + if (it.opcode != Opcode.CONST_STRING) return@indexOfFirst false + + val instructionString = ((it as Instruction21c).reference as StringReference).string + if (instructionString != "com.google.android.apps.youtube.music") return@indexOfFirst false + + register = it.registerA + return@indexOfFirst true + } + + primeMethod.replaceInstruction( + index, "const-string v$register, \"$REVANCED_MUSIC_PACKAGE_NAME\"" + ) + + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/misc/microg/patch/resource/MusicMicroGResourcePatch.kt b/src/main/kotlin/app/revanced/patches/music/misc/microg/patch/resource/MusicMicroGResourcePatch.kt new file mode 100644 index 000000000..824f570ab --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/music/misc/microg/patch/resource/MusicMicroGResourcePatch.kt @@ -0,0 +1,44 @@ +package app.revanced.patches.music.misc.microg.patch.resource + +import app.revanced.patcher.annotation.Description +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.data.impl.ResourceData +import app.revanced.patcher.patch.PatchResult +import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.patch.impl.ResourcePatch +import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility +import app.revanced.patches.music.misc.microg.shared.Constants.BASE_MICROG_PACKAGE_NAME +import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_APP_NAME +import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME + +@Name("music-microg-resource-patch") +@Description("Resource patch to allow YouTube Music ReVanced to run without root and under a different package name.") +@MusicMicroGPatchCompatibility +@Version("0.0.1") +class MusicMicroGResourcePatch : ResourcePatch() { + override fun execute(data: ResourceData): PatchResult { + + val manifest = data["AndroidManifest.xml"].readText() + + data["AndroidManifest.xml"].writeText( + manifest.replace( + "package=\"com.google.android.apps.youtube.music", "package=\"$REVANCED_MUSIC_PACKAGE_NAME" + ).replace( + "android:label=\"@string/app_name", "android:label=\"$REVANCED_MUSIC_APP_NAME" + ).replace( + "android:label=\"@string/app_launcher_name", "android:label=\"$REVANCED_MUSIC_APP_NAME" + ).replace( + "android:authorities=\"com.google.android.apps.youtube.music", "android:authorities=\"$REVANCED_MUSIC_PACKAGE_NAME" + ).replace( + "com.google.android.apps.youtube.music.permission.C2D_MESSAGE", "$REVANCED_MUSIC_PACKAGE_NAME.permission.C2D_MESSAGE" + ).replace( + "com.google.android.c2dm", "$BASE_MICROG_PACKAGE_NAME.android.c2dm" + ).replace( + "", "" + ) + ) + + return PatchResultSuccess() + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/misc/microg/shared/Constants.kt b/src/main/kotlin/app/revanced/patches/music/misc/microg/shared/Constants.kt new file mode 100644 index 000000000..54a3e285f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/music/misc/microg/shared/Constants.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.music.misc.microg.shared + +object Constants { + internal const val BASE_MICROG_PACKAGE_NAME = "com.mgoogle" + internal const val REVANCED_MUSIC_APP_NAME = "YouTube Music ReVanced" + internal const val REVANCED_MUSIC_PACKAGE_NAME = "app.revanced.android.apps.youtube.music" +} \ No newline at end of file