From 91474ba07376c13e7a71685dfd8b6e6913ed5ee9 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 19 Apr 2022 02:00:48 +0200 Subject: [PATCH 1/4] feat: begin `MicroG Patch` Signed-off-by: oSumAtrIX --- src/main/kotlin/app/revanced/patches/Index.kt | 2 + .../app/revanced/patches/misc/MicroGPatch.kt | 540 ++++++++++++++++++ 2 files changed, 542 insertions(+) create mode 100644 src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt diff --git a/src/main/kotlin/app/revanced/patches/Index.kt b/src/main/kotlin/app/revanced/patches/Index.kt index 61635533f..821a86840 100644 --- a/src/main/kotlin/app/revanced/patches/Index.kt +++ b/src/main/kotlin/app/revanced/patches/Index.kt @@ -10,6 +10,7 @@ import app.revanced.patches.layout.HideReelsPatch import app.revanced.patches.layout.MinimizedPlaybackPatch import app.revanced.patches.layout.OldQualityLayoutPatch import app.revanced.patches.misc.IntegrationsPatch +import app.revanced.patches.misc.MicroGPatch /** * Index contains all the patches. @@ -22,6 +23,7 @@ object Index { */ val patches: List<() -> Patch> = listOf( ::IntegrationsPatch, + ::MicroGPatch, ::HomeAdsPatch, ::VideoAdsPatch, ::HomePromoPatch, diff --git a/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt b/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt new file mode 100644 index 000000000..478218943 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt @@ -0,0 +1,540 @@ +package app.revanced.patches.misc + +import app.revanced.patcher.PatcherData +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.or +import app.revanced.patcher.patch.* +import app.revanced.patcher.proxy +import app.revanced.patcher.signature.MethodMetadata +import app.revanced.patcher.signature.MethodSignature +import app.revanced.patcher.signature.MethodSignatureMetadata +import app.revanced.patcher.signature.PatternScanMethod +import app.revanced.patcher.smali.toInstruction +import app.revanced.patcher.smali.toInstructions +import org.jf.dexlib2.AccessFlags +import org.jf.dexlib2.Opcode +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 + +private const val BASE_MICROG_PACKAGE_NAME = "com.mgoogle" +private const val BASE_REVANCED_PACKAGE_NAME = "app.revanced.youtube" + +private val compatiblePackages = listOf( + PackageMetadata( + "com.google.android.youtube", listOf("17.14.35") + ) +) + +private val metadata = PatchMetadata( + "microg", + "MicroG Patch", + "Patch to allow YouTube ReVanced to run without root and under a different package name.", + compatiblePackages, + "0.0.1" +) + +private val description = "Signature required for ${metadata.name}." + +class MicroGPatch : Patch( + metadata, listOf( + MethodSignature( + MethodSignatureMetadata( + "google-play-sig-check-method", + MethodMetadata("Ldsf;", "d"), + PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value. + compatiblePackages, + description, + "0.0.1" + ), "L", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "L"), listOf( + Opcode.MOVE_OBJECT_FROM16, + Opcode.CONST_STRING, + Opcode.CONST_STRING, + Opcode.NEW_INSTANCE, + Opcode.MOVE_OBJECT_FROM16, + Opcode.INVOKE_DIRECT, + Opcode.CONST_4, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_EQ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.SGET, + Opcode.IGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_WIDE, + Opcode.CONST_WIDE, + Opcode.CONST_STRING, + Opcode.CONST_4, + Opcode.CONST_STRING, + Opcode.CONST_4, + Opcode.CMP_LONG, + Opcode.IF_GEZ, + Opcode.CONST_16, + Opcode.GOTO_16, + Opcode.CONST_STRING, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.GOTO, + Opcode.MOVE_EXCEPTION, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CONST_4, + Opcode.IGET_OBJECT, + Opcode.IF_EQZ, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.CONST, + Opcode.IF_NE, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.SGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_NEZ, + Opcode.CONST_STRING, + Opcode.GOTO, + Opcode.NEW_ARRAY + ), listOf("This should never happen.", "GooglePlayServicesUtil", "Google Play Store signature invalid.") + ), MethodSignature( + MethodSignatureMetadata( + "google-play-service-checker-method", + MethodMetadata("Llpe;", "d"), + PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value. + compatiblePackages, + description, + "0.0.1" + ), "V", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "I"), listOf( + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.SGET_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.IF_NEZ, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.THROW, + Opcode.NEW_INSTANCE, + Opcode.CONST_STRING, + Opcode.INVOKE_DIRECT, + Opcode.THROW, + Opcode.RETURN_VOID + ), listOf("Google Play Services not available") + ), MethodSignature( + MethodSignatureMetadata( + "google-play-utility-method", + MethodMetadata("Llpe;", "b"), + PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value. + compatiblePackages, + description, + "0.0.1" + ), "I", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "L"), listOf( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.GOTO, + Opcode.CONST_STRING, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.CONST_4, + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.GOTO, + Opcode.SGET_OBJECT, + Opcode.MONITOR_ENTER, + Opcode.SGET_BOOLEAN, + Opcode.IF_EQZ, + Opcode.MONITOR_EXIT, + Opcode.GOTO, + Opcode.SPUT_BOOLEAN, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + Opcode.MONITOR_EXIT, + Opcode.GOTO, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.SPUT, + Opcode.GOTO, + Opcode.MOVE_EXCEPTION, + Opcode.CONST_STRING, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MONITOR_EXIT, + Opcode.SGET, + Opcode.IF_EQZ, + Opcode.CONST, + Opcode.IF_NE, + Opcode.GOTO, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.THROW, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.THROW, + Opcode.MOVE_EXCEPTION, + Opcode.MONITOR_EXIT, + Opcode.THROW, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.CONST_4, + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.IF_NEZ, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.SPUT_OBJECT, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.INVOKE_STATIC, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_16, + Opcode.IF_EQZ, + Opcode.CONST_STRING, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.GOTO, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.CONST_16, + Opcode.GOTO_16, + Opcode.CONST_4, + Opcode.CONST_STRING, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.GOTO, + Opcode.IF_EQZ, + Opcode.INVOKE_STATIC, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.GOTO, + Opcode.IF_EQZ, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.AGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.AGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.GOTO, + Opcode.IGET, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_GE, + Opcode.IGET, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.GOTO, + Opcode.MOVE_EXCEPTION, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.GOTO, + Opcode.IGET_BOOLEAN, + Opcode.IF_NEZ, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.RETURN, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.RETURN + ), listOf("This should never happen.", "MetadataValueReader", "com.google.android.gms") + ), MethodSignature( + MethodSignatureMetadata( + "google-play-prime-method", + MethodMetadata("Louy;", "e"), + PatternScanMethod.Direct(), + compatiblePackages, + description, + "0.0.1" + ), null, null, null, null, listOf("com.google.android.GoogleCamera", "com.android.vending") + ) + ) +) { + override fun execute(patcherData: PatcherData): PatchResult { + // smali patches + disablePlayServiceChecks() + for (classDef in patcherData.classes) { + methodLoop@ for (method in classDef.methods) { + val implementation = method.implementation ?: continue@methodLoop + + implementation.instructions.forEachIndexed { i, instruction -> + if (instruction.opcode == Opcode.CONST_STRING) { + + val reference = ((instruction as Instruction21c).reference as StringReference).string + + val newString = + if (reference == "com.google") { + BASE_MICROG_PACKAGE_NAME + } else if ( + // https://github.com/TeamVanced/VancedMicroG/pull/139/file + !reference.startsWith("com.google.android.gms.chimera.container") && + reference.startsWith("com.google.iid") || + reference.startsWith("com.google.android.gms.chimera") || + reference.startsWith("com.google.android.c2dm") || + //reference.startsWith("com.google.android.gms.phenotype") || TODO: implement when we replace references below + reference.startsWith("com.google.android.gms.auth.accounts") || + reference.startsWith("com.google.android.c2dm") || + reference.startsWith("com.google.android.gsf") || + reference.startsWith("com.google.android.c2dm") || + reference.startsWith("content://com.google.settings") + ) { + reference.replace("com.google", BASE_MICROG_PACKAGE_NAME) + } else if ( + reference.startsWith("com.google.android.youtube.SuggestionsProvider") || // TODO: also in resources & manifest + reference.startsWith("com.google.android.youtube.fileprovider") // TODO: also in resources & manifest + ) { + reference.replace("com.google.android.youtube", BASE_REVANCED_PACKAGE_NAME) + } else { + reference + } + patcherData.proxy(classDef) + .resolve().methods.first { it.name == method.name }.implementation!!.replaceInstruction( + i, BuilderInstruction21c( + Opcode.CONST_STRING, instruction.registerA, ImmutableStringReference(newString) + ) + ) + + } + + // TODO: phenotype reference -> microg reference + //if (instruction is ReferenceInstruction) { + // val proxy = patcherData.proxy(classDef).resolve() + // val implementation = proxy.methods.first { it.name == method.name }.implementation!! + // when (instruction.referenceType) { + // ReferenceType.METHOD -> { + // val reference = instruction.reference as MethodReference + // if (!reference.name.startsWith("com.google.android.gms.phenotype")) return@forEachIndexed + + // val modifiedReference = ImmutableMethodReference( + // reference.definingClass.replace("com.google", BASE_MICROG_PACKAGE_NAME), + // reference.name, + // reference.parameterTypes.map { it.toString().replace("com.google", BASE_MICROG_PACKAGE_NAME) }, + // reference.returnType.replace("com.google", BASE_MICROG_PACKAGE_NAME), + // ); + + // val newInstruction = when (instruction.opcode.format) { + // Format.Format35c -> { + // val instruction35c = instruction as Instruction35c + // BuilderInstruction35c( + // instruction.opcode, + // instruction35c.registerCount, + // instruction35c.registerC, + // instruction35c.registerD, + // instruction35c.registerE, + // instruction35c.registerF, + // instruction35c.registerG, + // modifiedReference + // ) + // } + // Format.Format3rc -> + // BuilderInstruction3rc( + // instruction.opcode, + // ) + // Format.Format45cc -> + // BuilderInstruction45cc( + // instruction.opcode, + // ) + // Format.Format4rcc -> + // BuilderInstruction4rcc( + // instruction.opcode, + // ) + // } + // implementation.replaceInstruction( + // i, + + // ) + // } + // ReferenceType.METHOD_PROTO -> { + + // } + // ReferenceType.TYPE -> { + + // } + // ReferenceType.CALL_SITE -> { + + // } + // ReferenceType.METHOD_HANDLE -> { + + // } + // ReferenceType.FIELD -> { + + // } + // ReferenceType.NONE -> { + + // } + // } + //} + } + } + } + + return PatchResultSuccess() + } + + private fun disablePlayServiceChecks() { + for (i in 0 until signatures.count() - 1) { + val result = signatures.elementAt(i).result!! + val stringInstructions = when (result.immutableMethod.returnType.first()) { + 'L' -> """ + const/4 v0, 0x0 + return-object + """ + 'V' -> "return-void" + 'I' -> """ + const/4 v0, 0x0 + return v0 + """ + else -> "" + } + result.method.implementation!!.addInstructions( + 0, stringInstructions.trimMargin().toInstructions() + ) + } + + val implementation = signatures.last().result!!.method.implementation!! + var register = 2 + val index = implementation.instructions.indexOfFirst { + return@indexOfFirst if (it.opcode == Opcode.CONST_STRING && ((it as Instruction21c).reference as StringReference).string == "com.google.android.youtube") { + register = it.registerA + true + } else false + + } + + signatures.last().result!!.method.implementation!!.replaceInstruction( + index, "const-string v$register, \"$BASE_REVANCED_PACKAGE_NAME\"".toInstruction() + ) + } +} \ No newline at end of file From af0b16482f110dd16e644693712548d898f2559c Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 19 Apr 2022 20:00:50 +0200 Subject: [PATCH 2/4] refactor: `MicroGPatch` Signed-off-by: oSumAtrIX --- .../app/revanced/extensions/Extensions.kt | 8 ++ .../app/revanced/patches/misc/MicroGPatch.kt | 107 ++++++++++-------- 2 files changed, 70 insertions(+), 45 deletions(-) diff --git a/src/main/kotlin/app/revanced/extensions/Extensions.kt b/src/main/kotlin/app/revanced/extensions/Extensions.kt index 16242c940..b42f47a8f 100644 --- a/src/main/kotlin/app/revanced/extensions/Extensions.kt +++ b/src/main/kotlin/app/revanced/extensions/Extensions.kt @@ -11,4 +11,12 @@ internal fun MutableMethodImplementation.injectHideCall( index, "invoke-static { v$register }, Lfi/razerman/youtube/XAdRemover;->HideView(Landroid/view/View;)V".toInstruction() ) +} + +internal fun String.startsWithAny(vararg prefix: String): Boolean { + for (_prefix in prefix) + if (this.startsWith(_prefix)) + return true + + return false } \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt b/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt index 478218943..fb085762e 100644 --- a/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt +++ b/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt @@ -1,5 +1,6 @@ package app.revanced.patches.misc +import app.revanced.extensions.startsWithAny import app.revanced.patcher.PatcherData import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.or @@ -385,47 +386,27 @@ class MicroGPatch : Patch( override fun execute(patcherData: PatcherData): PatchResult { // smali patches disablePlayServiceChecks() - for (classDef in patcherData.classes) { - methodLoop@ for (method in classDef.methods) { - val implementation = method.implementation ?: continue@methodLoop + patcherData.classes.forEach { classDef -> + classDef.methods.forEach methodLoop@{ method -> + val implementation = method.implementation ?: return@methodLoop implementation.instructions.forEachIndexed { i, instruction -> if (instruction.opcode == Opcode.CONST_STRING) { - - val reference = ((instruction as Instruction21c).reference as StringReference).string - - val newString = - if (reference == "com.google") { - BASE_MICROG_PACKAGE_NAME - } else if ( - // https://github.com/TeamVanced/VancedMicroG/pull/139/file - !reference.startsWith("com.google.android.gms.chimera.container") && - reference.startsWith("com.google.iid") || - reference.startsWith("com.google.android.gms.chimera") || - reference.startsWith("com.google.android.c2dm") || - //reference.startsWith("com.google.android.gms.phenotype") || TODO: implement when we replace references below - reference.startsWith("com.google.android.gms.auth.accounts") || - reference.startsWith("com.google.android.c2dm") || - reference.startsWith("com.google.android.gsf") || - reference.startsWith("com.google.android.c2dm") || - reference.startsWith("content://com.google.settings") - ) { - reference.replace("com.google", BASE_MICROG_PACKAGE_NAME) - } else if ( - reference.startsWith("com.google.android.youtube.SuggestionsProvider") || // TODO: also in resources & manifest - reference.startsWith("com.google.android.youtube.fileprovider") // TODO: also in resources & manifest - ) { - reference.replace("com.google.android.youtube", BASE_REVANCED_PACKAGE_NAME) - } else { - reference - } - patcherData.proxy(classDef) - .resolve().methods.first { it.name == method.name }.implementation!!.replaceInstruction( - i, BuilderInstruction21c( - Opcode.CONST_STRING, instruction.registerA, ImmutableStringReference(newString) + val instructionString = ((instruction as Instruction21c).reference as StringReference).string + patcherData + .proxy(classDef) + .resolve() + .methods + .first { it.name == method.name } + .implementation!! + .replaceInstruction( + i, + BuilderInstruction21c( + Opcode.CONST_STRING, + instruction.registerA, + ImmutableStringReference(instructionString.replacePackageName()) ) ) - } // TODO: phenotype reference -> microg reference @@ -500,9 +481,35 @@ class MicroGPatch : Patch( } } + // allow GC to clean unused/ replaced immutable class definitions + patcherData.classes.applyProxies() + + // TODO: resource patches return PatchResultSuccess() } + private fun String.replacePackageName(): String { + return if (this == "com.google") BASE_MICROG_PACKAGE_NAME + else if (!this.startsWith("com.google.android.gms.chimera.container") && // https://github.com/TeamVanced/VancedMicroG/pull/139/file + this.startsWithAny( + "com.google.android.gms.auth.accounts", + "com.google.android.gms.chimera", + "com.google.android.c2dm", + "com.google.android.c2dm", + "com.google.android.gsf", + "com.google.android.c2dm", + "com.google.iid", + "content://com.google.settings", + // "com.google.android.gms.phenotype" TODO: implement when we replace references below + ) + ) this.replace("com.google", BASE_MICROG_PACKAGE_NAME) + else if (this.startsWithAny( + "com.google.android.youtube.SuggestionsProvider", "com.google.android.youtube.fileprovider" + ) + ) this.replace("com.google.android.youtube", BASE_REVANCED_PACKAGE_NAME) + else this + } + private fun disablePlayServiceChecks() { for (i in 0 until signatures.count() - 1) { val result = signatures.elementAt(i).result!! @@ -523,18 +530,28 @@ class MicroGPatch : Patch( ) } - val implementation = signatures.last().result!!.method.implementation!! + val implementation = signatures + .last() + .result!! + .method + .implementation!! + var register = 2 - val index = implementation.instructions.indexOfFirst { - return@indexOfFirst if (it.opcode == Opcode.CONST_STRING && ((it as Instruction21c).reference as StringReference).string == "com.google.android.youtube") { + 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.youtube") return@indexOfFirst false + register = it.registerA - true - } else false + return@indexOfFirst true + } - } - - signatures.last().result!!.method.implementation!!.replaceInstruction( - index, "const-string v$register, \"$BASE_REVANCED_PACKAGE_NAME\"".toInstruction() + implementation.replaceInstruction( + index, + "const-string v$register, \"$BASE_REVANCED_PACKAGE_NAME\"".toInstruction() ) } } \ No newline at end of file From 6f1cabe9728772232d23dfacdd604b856e73f3a8 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 20 Apr 2022 02:44:56 +0200 Subject: [PATCH 3/4] refactor: furthermore implement logic for `MicroGPatch` Signed-off-by: oSumAtrIX --- .../app/revanced/patches/misc/MicroGPatch.kt | 289 ++++++++++-------- 1 file changed, 165 insertions(+), 124 deletions(-) diff --git a/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt b/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt index fb085762e..c5e3baacf 100644 --- a/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt +++ b/src/main/kotlin/app/revanced/patches/misc/MicroGPatch.kt @@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.or import app.revanced.patcher.patch.* import app.revanced.patcher.proxy +import app.revanced.patcher.proxy.mutableTypes.MutableClass import app.revanced.patcher.signature.MethodMetadata import app.revanced.patcher.signature.MethodSignature import app.revanced.patcher.signature.MethodSignatureMetadata @@ -14,13 +15,14 @@ import app.revanced.patcher.smali.toInstruction import app.revanced.patcher.smali.toInstructions import org.jf.dexlib2.AccessFlags 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 private const val BASE_MICROG_PACKAGE_NAME = "com.mgoogle" -private const val BASE_REVANCED_PACKAGE_NAME = "app.revanced.youtube" +private const val BASE_REVANCED_PACKAGE_NAME = "app.revanced.android.youtube" private val compatiblePackages = listOf( PackageMetadata( @@ -38,6 +40,10 @@ private val metadata = PatchMetadata( private val description = "Signature required for ${metadata.name}." +enum class StringReplaceMode { + REPLACE_WITH_MICROG, REPLACE_WITH_REVANCED, DO_NOT_REPLACE +} + class MicroGPatch : Patch( metadata, listOf( MethodSignature( @@ -387,127 +393,169 @@ class MicroGPatch : Patch( // smali patches disablePlayServiceChecks() patcherData.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) { - val instructionString = ((instruction as Instruction21c).reference as StringReference).string - patcherData - .proxy(classDef) - .resolve() - .methods - .first { it.name == method.name } - .implementation!! - .replaceInstruction( - i, - BuilderInstruction21c( - Opcode.CONST_STRING, - instruction.registerA, - ImmutableStringReference(instructionString.replacePackageName()) + val stringValue = ((instruction as Instruction21c).reference as StringReference).string + + val replaceMode = + if (stringValue.startsWith("com.google.android.gms.chimera.container")) // https://github.com/TeamVanced/VancedMicroG/pull/139/file + StringReplaceMode.DO_NOT_REPLACE + else if (stringValue == "com.google" || stringValue.startsWithAny( + "com.google.android.gms.auth.accounts", + "com.google.android.gms.chimera", + "com.google.android.c2dm", + "com.google.android.c2dm", + "com.google.android.gsf", + "com.google.android.c2dm", + "com.google.iid", + "content://com.google.settings" + ) + ) { + StringReplaceMode.REPLACE_WITH_MICROG + } else if (stringValue.startsWithAny( + "com.google.android.youtube.SuggestionsProvider", + "com.google.android.youtube.fileprovider" + ) + ) { + StringReplaceMode.REPLACE_WITH_REVANCED + } else { + StringReplaceMode.DO_NOT_REPLACE + } + + + if (replaceMode != StringReplaceMode.DO_NOT_REPLACE) { + if (proxiedClass == null) { + proxiedClass = patcherData.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.youtube", BASE_REVANCED_PACKAGE_NAME + ) + else stringValue.replace("com.google", BASE_MICROG_PACKAGE_NAME) + + proxiedImplementation!!.replaceInstruction( + i, BuilderInstruction21c( + Opcode.CONST_STRING, instruction.registerA, ImmutableStringReference(newString) ) ) + } + + // TODO: phenotype reference -> microg reference + //if (instruction is ReferenceInstruction) { + // val proxy = patcherData.proxy(classDef).resolve() + // val implementation = proxy.methods.first { it.name == method.name }.implementation!! + // when (instruction.referenceType) { + // ReferenceType.METHOD -> { + // val reference = instruction.reference as MethodReference + // if (!reference.name.startsWith("com.google.android.gms.phenotype")) return@forEachIndexed + + // val modifiedReference = ImmutableMethodReference( + // reference.definingClass.replace("com.google", BASE_MICROG_PACKAGE_NAME), + // reference.name, + // reference.parameterTypes.map { + // it.toString().replace("com.google", BASE_MICROG_PACKAGE_NAME) + // }, + // reference.returnType.replace("com.google", BASE_MICROG_PACKAGE_NAME), + // ); + + // val newInstruction = when (instruction.opcode.format) { + // Format.Format35c -> { + // val instruction35c = instruction as Instruction35c + // BuilderInstruction35c( + // instruction.opcode, + // instruction35c.registerCount, + // instruction35c.registerC, + // instruction35c.registerD, + // instruction35c.registerE, + // instruction35c.registerF, + // instruction35c.registerG, + // modifiedReference + // ) + // } + // Format.Format3rc -> + // BuilderInstruction3rc( + // instruction.opcode, + // ) + // Format.Format45cc -> + // BuilderInstruction45cc( + // instruction.opcode, + // ) + // Format.Format4rcc -> + // BuilderInstruction4rcc( + // instruction.opcode, + // ) + // } + // implementation.replaceInstruction( + // i, + + // ) + // } + // ReferenceType.METHOD_PROTO -> { + + // } + // ReferenceType.TYPE -> { + + // } + // ReferenceType.CALL_SITE -> { + + // } + // ReferenceType.METHOD_HANDLE -> { + + // } + // ReferenceType.FIELD -> { + + // } + // ReferenceType.NONE -> { + + // } + // } + //} + } - - // TODO: phenotype reference -> microg reference - //if (instruction is ReferenceInstruction) { - // val proxy = patcherData.proxy(classDef).resolve() - // val implementation = proxy.methods.first { it.name == method.name }.implementation!! - // when (instruction.referenceType) { - // ReferenceType.METHOD -> { - // val reference = instruction.reference as MethodReference - // if (!reference.name.startsWith("com.google.android.gms.phenotype")) return@forEachIndexed - - // val modifiedReference = ImmutableMethodReference( - // reference.definingClass.replace("com.google", BASE_MICROG_PACKAGE_NAME), - // reference.name, - // reference.parameterTypes.map { it.toString().replace("com.google", BASE_MICROG_PACKAGE_NAME) }, - // reference.returnType.replace("com.google", BASE_MICROG_PACKAGE_NAME), - // ); - - // val newInstruction = when (instruction.opcode.format) { - // Format.Format35c -> { - // val instruction35c = instruction as Instruction35c - // BuilderInstruction35c( - // instruction.opcode, - // instruction35c.registerCount, - // instruction35c.registerC, - // instruction35c.registerD, - // instruction35c.registerE, - // instruction35c.registerF, - // instruction35c.registerG, - // modifiedReference - // ) - // } - // Format.Format3rc -> - // BuilderInstruction3rc( - // instruction.opcode, - // ) - // Format.Format45cc -> - // BuilderInstruction45cc( - // instruction.opcode, - // ) - // Format.Format4rcc -> - // BuilderInstruction4rcc( - // instruction.opcode, - // ) - // } - // implementation.replaceInstruction( - // i, - - // ) - // } - // ReferenceType.METHOD_PROTO -> { - - // } - // ReferenceType.TYPE -> { - - // } - // ReferenceType.CALL_SITE -> { - - // } - // ReferenceType.METHOD_HANDLE -> { - - // } - // ReferenceType.FIELD -> { - - // } - // ReferenceType.NONE -> { - - // } - // } - //} } } } - // allow GC to clean unused/ replaced immutable class definitions + // replace string back + val implementation = signatures.elementAt(2).result!!.findParentMethod( + MethodSignature( + MethodSignatureMetadata( + "do-not-replace-method", + MethodMetadata("Llpe;", "c"), + PatternScanMethod.Direct(), + compatiblePackages, + description, + "0.0.1" + ), "L", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L"), null, listOf("com.google.android.gms") + ) + )!!.method.implementation!! + + implementation.replaceInstruction( + implementation.instructions.indexOfFirst { it.opcode == Opcode.CONST_STRING }, + "const-string v0, \"com.google.android.gms\"".toInstruction() + ) + + // allow GC to clean unused/ replaced immutable class definitions after this call patcherData.classes.applyProxies() // TODO: resource patches return PatchResultSuccess() - } - private fun String.replacePackageName(): String { - return if (this == "com.google") BASE_MICROG_PACKAGE_NAME - else if (!this.startsWith("com.google.android.gms.chimera.container") && // https://github.com/TeamVanced/VancedMicroG/pull/139/file - this.startsWithAny( - "com.google.android.gms.auth.accounts", - "com.google.android.gms.chimera", - "com.google.android.c2dm", - "com.google.android.c2dm", - "com.google.android.gsf", - "com.google.android.c2dm", - "com.google.iid", - "content://com.google.settings", - // "com.google.android.gms.phenotype" TODO: implement when we replace references below - ) - ) this.replace("com.google", BASE_MICROG_PACKAGE_NAME) - else if (this.startsWithAny( - "com.google.android.youtube.SuggestionsProvider", "com.google.android.youtube.fileprovider" - ) - ) this.replace("com.google.android.youtube", BASE_REVANCED_PACKAGE_NAME) - else this } private fun disablePlayServiceChecks() { @@ -516,42 +564,35 @@ class MicroGPatch : Patch( val stringInstructions = when (result.immutableMethod.returnType.first()) { 'L' -> """ const/4 v0, 0x0 - return-object + return-object v0 """ 'V' -> "return-void" 'I' -> """ const/4 v0, 0x0 return v0 """ - else -> "" + else -> throw Exception("This case should never happen.") } result.method.implementation!!.addInstructions( - 0, stringInstructions.trimMargin().toInstructions() + 0, stringInstructions.trimIndent().toInstructions() ) } - val implementation = signatures - .last() - .result!! - .method - .implementation!! + val implementation = signatures.last().result!!.method.implementation!! var register = 2 - val index = implementation - .instructions - .indexOfFirst { - if (it.opcode != Opcode.CONST_STRING) return@indexOfFirst false + 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.youtube") return@indexOfFirst false + val instructionString = ((it as Instruction21c).reference as StringReference).string + if (instructionString != "com.google.android.youtube") return@indexOfFirst false - register = it.registerA - return@indexOfFirst true - } + register = it.registerA + return@indexOfFirst true + } implementation.replaceInstruction( - index, - "const-string v$register, \"$BASE_REVANCED_PACKAGE_NAME\"".toInstruction() + index, "const-string v$register, \"$BASE_REVANCED_PACKAGE_NAME\"".toInstruction() ) } } \ No newline at end of file From cbb9e2cd1fa829e1d1dd92dbd40131b11ae6a05b Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Thu, 5 May 2022 00:04:14 +0200 Subject: [PATCH 4/4] fix: breaking patcher changes Signed-off-by: oSumAtrIX --- src/main/kotlin/app/revanced/patches/Index.kt | 2 +- .../patches/youtube/misc/MicroGPatch.kt | 602 ++++++++++++++++++ 2 files changed, 603 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/patches/Index.kt b/src/main/kotlin/app/revanced/patches/Index.kt index bcb2c0be0..df4823355 100644 --- a/src/main/kotlin/app/revanced/patches/Index.kt +++ b/src/main/kotlin/app/revanced/patches/Index.kt @@ -14,7 +14,7 @@ import app.revanced.patches.youtube.interaction.EnableSeekbarTappingPatch import app.revanced.patches.youtube.layout.* import app.revanced.patches.youtube.misc.FixLocaleConfigErrorPatch import app.revanced.patches.youtube.misc.IntegrationsPatch -import app.revanced.patches.music.misc.MicroGPatch +import app.revanced.patches.youtube.misc.MicroGPatch /** * Index contains all the patches. diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/MicroGPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/MicroGPatch.kt index e69de29bb..cfe93b4a9 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/MicroGPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/MicroGPatch.kt @@ -0,0 +1,602 @@ +package app.revanced.patches.youtube.misc + +import app.revanced.extensions.startsWithAny +import app.revanced.patcher.data.implementation.BytecodeData +import app.revanced.patcher.data.implementation.proxy +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.or +import app.revanced.patcher.patch.implementation.BytecodePatch +import app.revanced.patcher.patch.implementation.metadata.PackageMetadata +import app.revanced.patcher.patch.implementation.metadata.PatchMetadata +import app.revanced.patcher.patch.implementation.misc.PatchResult +import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess +import app.revanced.patcher.proxy.mutableTypes.MutableClass +import app.revanced.patcher.signature.MethodMetadata +import app.revanced.patcher.signature.MethodSignature +import app.revanced.patcher.signature.MethodSignatureMetadata +import app.revanced.patcher.signature.PatternScanMethod +import app.revanced.patcher.smali.toInstruction +import app.revanced.patcher.smali.toInstructions +import org.jf.dexlib2.AccessFlags +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 + +private const val BASE_MICROG_PACKAGE_NAME = "com.mgoogle" +private const val BASE_REVANCED_PACKAGE_NAME = "app.revanced.android.youtube" + +private val compatiblePackages = listOf( + PackageMetadata( + "com.google.android.youtube", listOf("17.14.35") + ) +) + +private val metadata = PatchMetadata( + "microg", + "MicroG Patch", + "Patch to allow YouTube ReVanced to run without root and under a different package name.", + compatiblePackages, + "0.0.1" +) + +private val description = "Signature required for ${metadata.name}." + +enum class StringReplaceMode { + REPLACE_WITH_MICROG, REPLACE_WITH_REVANCED, DO_NOT_REPLACE +} + +class MicroGPatch : BytecodePatch( + metadata, listOf( + MethodSignature( + MethodSignatureMetadata( + "google-play-sig-check-method", + MethodMetadata("Ldsf;", "d"), + PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value. + compatiblePackages, + description, + "0.0.1" + ), "L", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "L"), listOf( + Opcode.MOVE_OBJECT_FROM16, + Opcode.CONST_STRING, + Opcode.CONST_STRING, + Opcode.NEW_INSTANCE, + Opcode.MOVE_OBJECT_FROM16, + Opcode.INVOKE_DIRECT, + Opcode.CONST_4, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_EQ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.SGET, + Opcode.IGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_WIDE, + Opcode.CONST_WIDE, + Opcode.CONST_STRING, + Opcode.CONST_4, + Opcode.CONST_STRING, + Opcode.CONST_4, + Opcode.CMP_LONG, + Opcode.IF_GEZ, + Opcode.CONST_16, + Opcode.GOTO_16, + Opcode.CONST_STRING, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.GOTO, + Opcode.MOVE_EXCEPTION, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CONST_4, + Opcode.IGET_OBJECT, + Opcode.IF_EQZ, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.CONST, + Opcode.IF_NE, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.SGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IF_NEZ, + Opcode.CONST_STRING, + Opcode.GOTO, + Opcode.NEW_ARRAY + ), listOf("This should never happen.", "GooglePlayServicesUtil", "Google Play Store signature invalid.") + ), MethodSignature( + MethodSignatureMetadata( + "google-play-service-checker-method", + MethodMetadata("Llpe;", "d"), + PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value. + compatiblePackages, + description, + "0.0.1" + ), "V", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "I"), listOf( + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.SGET_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.IF_NEZ, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.THROW, + Opcode.NEW_INSTANCE, + Opcode.CONST_STRING, + Opcode.INVOKE_DIRECT, + Opcode.THROW, + Opcode.RETURN_VOID + ), listOf("Google Play Services not available") + ), MethodSignature( + MethodSignatureMetadata( + "google-play-utility-method", + MethodMetadata("Llpe;", "b"), + PatternScanMethod.Fuzzy(2), // FIXME: Test this threshold and find the best value. + compatiblePackages, + description, + "0.0.1" + ), "I", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L", "L"), listOf( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST, + Opcode.INVOKE_VIRTUAL, + Opcode.GOTO, + Opcode.CONST_STRING, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.CONST_4, + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.GOTO, + Opcode.SGET_OBJECT, + Opcode.MONITOR_ENTER, + Opcode.SGET_BOOLEAN, + Opcode.IF_EQZ, + Opcode.MONITOR_EXIT, + Opcode.GOTO, + Opcode.SPUT_BOOLEAN, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + Opcode.MONITOR_EXIT, + Opcode.GOTO, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.SPUT, + Opcode.GOTO, + Opcode.MOVE_EXCEPTION, + Opcode.CONST_STRING, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MONITOR_EXIT, + Opcode.SGET, + Opcode.IF_EQZ, + Opcode.CONST, + Opcode.IF_NE, + Opcode.GOTO, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.THROW, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.THROW, + Opcode.MOVE_EXCEPTION, + Opcode.MONITOR_EXIT, + Opcode.THROW, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.CONST_4, + Opcode.IF_NEZ, + Opcode.SGET_OBJECT, + Opcode.IF_NEZ, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.SPUT_OBJECT, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.CONST_4, + Opcode.INVOKE_STATIC, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_16, + Opcode.IF_EQZ, + Opcode.CONST_STRING, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.GOTO, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.CONST_16, + Opcode.GOTO_16, + Opcode.CONST_4, + Opcode.CONST_STRING, + Opcode.CONST_16, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.GOTO, + Opcode.IF_EQZ, + Opcode.INVOKE_STATIC, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.GOTO, + Opcode.IF_EQZ, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.AGET_OBJECT, + Opcode.IGET_OBJECT, + Opcode.AGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.GOTO, + Opcode.IGET, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_GE, + Opcode.IGET, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.IGET_OBJECT, + Opcode.IF_NEZ, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.GOTO, + Opcode.MOVE_EXCEPTION, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.GOTO, + Opcode.IGET_BOOLEAN, + Opcode.IF_NEZ, + Opcode.CONST_4, + Opcode.GOTO, + Opcode.RETURN, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_STRING, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.RETURN + ), listOf("This should never happen.", "MetadataValueReader", "com.google.android.gms") + ), MethodSignature( + MethodSignatureMetadata( + "google-play-prime-method", + MethodMetadata("Louy;", "e"), + PatternScanMethod.Direct(), + compatiblePackages, + description, + "0.0.1" + ), null, null, null, null, listOf("com.google.android.GoogleCamera", "com.android.vending") + ) + ) +) { + override fun execute(data: BytecodeData): PatchResult { + // smali patches + 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) { + val stringValue = ((instruction as Instruction21c).reference as StringReference).string + + val replaceMode = + if (stringValue.startsWith("com.google.android.gms.chimera.container")) // https://github.com/TeamVanced/VancedMicroG/pull/139/file + StringReplaceMode.DO_NOT_REPLACE + else if (stringValue == "com.google" || stringValue.startsWithAny( + "com.google.android.gms.auth.accounts", + "com.google.android.gms.chimera", + "com.google.android.c2dm", + "com.google.android.c2dm", + "com.google.android.gsf", + "com.google.android.c2dm", + "com.google.iid", + "content://com.google.settings" + ) + ) { + StringReplaceMode.REPLACE_WITH_MICROG + } else if (stringValue.startsWithAny( + "com.google.android.youtube.SuggestionsProvider", + "com.google.android.youtube.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.youtube", BASE_REVANCED_PACKAGE_NAME + ) + else stringValue.replace("com.google", BASE_MICROG_PACKAGE_NAME) + + proxiedImplementation!!.replaceInstruction( + i, BuilderInstruction21c( + Opcode.CONST_STRING, instruction.registerA, ImmutableStringReference(newString) + ) + ) + } + + // TODO: phenotype reference -> microg reference + //if (instruction is ReferenceInstruction) { + // val proxy = patcherData.proxy(classDef).resolve() + // val implementation = proxy.methods.first { it.name == method.name }.implementation!! + // when (instruction.referenceType) { + // ReferenceType.METHOD -> { + // val reference = instruction.reference as MethodReference + // if (!reference.name.startsWith("com.google.android.gms.phenotype")) return@forEachIndexed + + // val modifiedReference = ImmutableMethodReference( + // reference.definingClass.replace("com.google", BASE_MICROG_PACKAGE_NAME), + // reference.name, + // reference.parameterTypes.map { + // it.toString().replace("com.google", BASE_MICROG_PACKAGE_NAME) + // }, + // reference.returnType.replace("com.google", BASE_MICROG_PACKAGE_NAME), + // ); + + // val newInstruction = when (instruction.opcode.format) { + // Format.Format35c -> { + // val instruction35c = instruction as Instruction35c + // BuilderInstruction35c( + // instruction.opcode, + // instruction35c.registerCount, + // instruction35c.registerC, + // instruction35c.registerD, + // instruction35c.registerE, + // instruction35c.registerF, + // instruction35c.registerG, + // modifiedReference + // ) + // } + // Format.Format3rc -> + // BuilderInstruction3rc( + // instruction.opcode, + // ) + // Format.Format45cc -> + // BuilderInstruction45cc( + // instruction.opcode, + // ) + // Format.Format4rcc -> + // BuilderInstruction4rcc( + // instruction.opcode, + // ) + // } + // implementation.replaceInstruction( + // i, + + // ) + // } + // ReferenceType.METHOD_PROTO -> { + + // } + // ReferenceType.TYPE -> { + + // } + // ReferenceType.CALL_SITE -> { + + // } + // ReferenceType.METHOD_HANDLE -> { + + // } + // ReferenceType.FIELD -> { + + // } + // ReferenceType.NONE -> { + + // } + // } + //} + + } + } + } + } + + // replace string back + val implementation = signatures.elementAt(2).result!!.findParentMethod( + MethodSignature( + MethodSignatureMetadata( + "do-not-replace-method", + MethodMetadata("Llpe;", "c"), + PatternScanMethod.Direct(), + compatiblePackages, + description, + "0.0.1" + ), "L", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L"), null, listOf("com.google.android.gms") + ) + )!!.method.implementation!! + + implementation.replaceInstruction( + implementation.instructions.indexOfFirst { it.opcode == Opcode.CONST_STRING }, + "const-string v0, \"com.google.android.gms\"".toInstruction() + ) + + // allow GC to clean unused/ replaced immutable class definitions after this call + data.classes.applyProxies() + + // TODO: resource patches + return PatchResultSuccess() + + } + + private fun disablePlayServiceChecks() { + for (i in 0 until signatures.count() - 1) { + val result = signatures.elementAt(i).result!! + val stringInstructions = when (result.immutableMethod.returnType.first()) { + 'L' -> """ + const/4 v0, 0x0 + return-object v0 + """ + 'V' -> "return-void" + 'I' -> """ + const/4 v0, 0x0 + return v0 + """ + else -> throw Exception("This case should never happen.") + } + result.method.implementation!!.addInstructions( + 0, stringInstructions.trimIndent().toInstructions() + ) + } + + val implementation = signatures.last().result!!.method.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.youtube") return@indexOfFirst false + + register = it.registerA + return@indexOfFirst true + } + + implementation.replaceInstruction( + index, "const-string v$register, \"$BASE_REVANCED_PACKAGE_NAME\"".toInstruction() + ) + } +} \ No newline at end of file