mirror of
https://github.com/revanced/revanced-patches
synced 2025-02-12 13:16:49 +01:00
refactor(microg-support): share code between music-microg-support
patch
This commit is contained in:
parent
50b5ff0864
commit
0db4f4079b
@ -1,37 +1,27 @@
|
|||||||
package app.revanced.patches.music.misc.microg.patch.bytecode
|
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.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.impl.BytecodeData
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.extensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patcher.patch.impl.BytecodePatch
|
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.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 app.revanced.patches.music.misc.microg.fingerprints.*
|
||||||
import org.jf.dexlib2.Opcode
|
import app.revanced.patches.music.misc.microg.patch.resource.MusicMicroGResourcePatch
|
||||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
|
||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
|
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
|
||||||
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
|
import app.revanced.patches.youtube.misc.microg.shared.Constants
|
||||||
import org.jf.dexlib2.iface.reference.StringReference
|
import app.revanced.util.microg.MicroGBytecodeHelper
|
||||||
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
|
|
||||||
|
|
||||||
@Patch
|
@Patch
|
||||||
@DependsOn([MusicMicroGResourcePatch::class])
|
@DependsOn([MusicMicroGResourcePatch::class])
|
||||||
@Name("music-microg-support")
|
@Name("music-microg-support")
|
||||||
@Description("Allows YouTube Music ReVanced to run without root and under a different package name.")
|
@Description("Allows YouTube Music ReVanced to run without root and under a different package name.")
|
||||||
@MusicMicroGPatchCompatibility
|
@MusicMicroGPatchCompatibility
|
||||||
@Version("0.0.1")
|
@Version("0.0.2")
|
||||||
class MusicMicroGBytecodePatch : BytecodePatch(
|
class MusicMicroGBytecodePatch : BytecodePatch(
|
||||||
listOf(
|
listOf(
|
||||||
ServiceCheckFingerprint,
|
ServiceCheckFingerprint,
|
||||||
@ -42,130 +32,34 @@ class MusicMicroGBytecodePatch : BytecodePatch(
|
|||||||
PrimeFingerprint,
|
PrimeFingerprint,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
// NOTE: the previous patch also replaced the following strings, but it seems like they are not needed:
|
||||||
disablePlayServiceChecks()
|
// - "com.google.android.gms.chimera.GmsIntentOperationService",
|
||||||
data.classes.forEach { classDef ->
|
// - "com.google.android.gms.phenotype.internal.IPhenotypeCallbacks",
|
||||||
var proxiedClass: MutableClass? = null
|
// - "com.google.android.gms.phenotype.internal.IPhenotypeService",
|
||||||
|
// - "com.google.android.gms.phenotype.PACKAGE_NAME",
|
||||||
classDef.methods.forEach methodLoop@{ method ->
|
// - "com.google.android.gms.phenotype.UPDATE",
|
||||||
val implementation = method.implementation ?: return@methodLoop
|
// - "com.google.android.gms.phenotype",
|
||||||
|
override fun execute(data: BytecodeData) =
|
||||||
var proxiedImplementation: MutableMethodImplementation? = null
|
// apply common microG patch
|
||||||
|
MicroGBytecodeHelper.patchBytecode(
|
||||||
implementation.instructions.forEachIndexed { i, instruction ->
|
data,
|
||||||
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed
|
arrayOf(
|
||||||
|
MicroGBytecodeHelper.packageNameTransform(
|
||||||
val stringValue = ((instruction as Instruction21c).reference as StringReference).string
|
Constants.PACKAGE_NAME,
|
||||||
|
Constants.REVANCED_PACKAGE_NAME
|
||||||
val replaceMode = if (stringValue.equalsAny(
|
)
|
||||||
"com.google.android.gms",
|
),
|
||||||
"com.google.android.gms.chimera",
|
MicroGBytecodeHelper.PrimeMethodTransformationData(
|
||||||
"com.google.android.c2dm.intent.REGISTER",
|
PrimeFingerprint,
|
||||||
"com.google.android.c2dm.permission.SEND",
|
MUSIC_PACKAGE_NAME,
|
||||||
"com.google.iid.TOKEN_REQUEST",
|
REVANCED_MUSIC_PACKAGE_NAME
|
||||||
"com.google",
|
),
|
||||||
"com.google.android.gms.chimera.GmsIntentOperationService",
|
listOf(
|
||||||
"com.google.android.gms.phenotype.internal.IPhenotypeCallbacks",
|
ServiceCheckFingerprint,
|
||||||
"com.google.android.gms.phenotype.internal.IPhenotypeService",
|
GooglePlayUtilityFingerprint,
|
||||||
"com.google.android.gms.phenotype.service.START",
|
CastDynamiteModuleFingerprint,
|
||||||
"com.google.android.gms.phenotype.PACKAGE_NAME",
|
CastDynamiteModuleV2Fingerprint,
|
||||||
"com.google.android.gms.phenotype.UPDATE",
|
CastContextFetchFingerprint
|
||||||
"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,
|
|
||||||
CastDynamiteModuleFingerprint,
|
|
||||||
CastDynamiteModuleV2Fingerprint,
|
|
||||||
CastContextFetchFingerprint,
|
|
||||||
).forEach { fingerprint ->
|
|
||||||
val result = fingerprint.result!!
|
|
||||||
val stringInstructions = when (result.method.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.mutableMethod.addInstructions(
|
|
||||||
0, stringInstructions
|
|
||||||
)
|
)
|
||||||
}
|
).let { PatchResultSuccess() }
|
||||||
|
|
||||||
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\""
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,41 @@
|
|||||||
package app.revanced.patches.music.misc.microg.patch.resource
|
package app.revanced.patches.music.misc.microg.patch.resource
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.Description
|
import app.revanced.patcher.annotation.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.impl.ResourceData
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.impl.ResourcePatch
|
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||||
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
|
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.MUSIC_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_APP_NAME
|
||||||
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
|
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
|
||||||
|
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_NAME
|
||||||
@Name("music-microg-resource-patch")
|
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_SIGNATURE
|
||||||
@Description("Resource patch to allow YouTube Music ReVanced to run without root and under a different package name.")
|
import app.revanced.util.microg.MicroGManifestHelper
|
||||||
@MusicMicroGPatchCompatibility
|
import app.revanced.util.microg.MicroGResourceHelper
|
||||||
@Version("0.0.1")
|
|
||||||
class MusicMicroGResourcePatch : ResourcePatch() {
|
@Name("music-microg-resource-patch")
|
||||||
override fun execute(data: ResourceData): PatchResult {
|
@Description("Resource patch to allow YouTube Music ReVanced to run without root and under a different package name.")
|
||||||
|
@MusicMicroGPatchCompatibility
|
||||||
val manifest = data["AndroidManifest.xml"].readText()
|
@Version("0.0.2")
|
||||||
|
class MusicMicroGResourcePatch : ResourcePatch() {
|
||||||
data["AndroidManifest.xml"].writeText(
|
override fun execute(data: ResourceData): PatchResult {
|
||||||
manifest.replace(
|
// update manifest
|
||||||
"package=\"com.google.android.apps.youtube.music", "package=\"$REVANCED_MUSIC_PACKAGE_NAME"
|
MicroGResourceHelper.patchManifest(
|
||||||
).replace(
|
data,
|
||||||
"android:label=\"@string/app_name", "android:label=\"$REVANCED_MUSIC_APP_NAME"
|
MUSIC_PACKAGE_NAME,
|
||||||
).replace(
|
REVANCED_MUSIC_PACKAGE_NAME,
|
||||||
"android:label=\"@string/app_launcher_name", "android:label=\"$REVANCED_MUSIC_APP_NAME"
|
REVANCED_MUSIC_APP_NAME
|
||||||
).replace(
|
)
|
||||||
"android:authorities=\"com.google.android.apps.youtube.music", "android:authorities=\"$REVANCED_MUSIC_PACKAGE_NAME"
|
|
||||||
).replace(
|
// add metadata to the manifest
|
||||||
"com.google.android.apps.youtube.music.permission.C2D_MESSAGE", "$REVANCED_MUSIC_PACKAGE_NAME.permission.C2D_MESSAGE"
|
MicroGManifestHelper.addSpoofingMetadata(
|
||||||
).replace(
|
data,
|
||||||
"com.google.android.c2dm", "$BASE_MICROG_PACKAGE_NAME.android.c2dm"
|
SPOOFED_PACKAGE_NAME,
|
||||||
).replace(
|
SPOOFED_PACKAGE_SIGNATURE
|
||||||
"</queries>", "<package android:name=\"$BASE_MICROG_PACKAGE_NAME.android.gms\"/></queries>"
|
)
|
||||||
)
|
return PatchResultSuccess()
|
||||||
)
|
}
|
||||||
|
|
||||||
return PatchResultSuccess()
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,7 +1,9 @@
|
|||||||
package app.revanced.patches.music.misc.microg.shared
|
package app.revanced.patches.music.misc.microg.shared
|
||||||
|
|
||||||
object Constants {
|
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_APP_NAME = "YouTube Music ReVanced"
|
||||||
internal const val REVANCED_MUSIC_PACKAGE_NAME = "app.revanced.android.apps.youtube.music"
|
internal const val REVANCED_MUSIC_PACKAGE_NAME = "app.revanced.android.apps.youtube.music"
|
||||||
|
internal const val MUSIC_PACKAGE_NAME = "com.google.android.apps.youtube.music"
|
||||||
|
internal const val SPOOFED_PACKAGE_NAME = MUSIC_PACKAGE_NAME
|
||||||
|
internal const val SPOOFED_PACKAGE_SIGNATURE = "afb0fed5eeaebdd86f56a97742f4b6b33ef59875"
|
||||||
}
|
}
|
@ -1,32 +1,21 @@
|
|||||||
package app.revanced.patches.youtube.misc.microg.patch.bytecode
|
package app.revanced.patches.youtube.misc.microg.patch.bytecode
|
||||||
|
|
||||||
import app.revanced.extensions.equalsAny
|
|
||||||
import app.revanced.patcher.annotation.Description
|
import app.revanced.patcher.annotation.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.impl.BytecodeData
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.patch.PatchResult
|
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
import app.revanced.patcher.patch.impl.BytecodePatch
|
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
|
||||||
import app.revanced.patches.youtube.layout.castbutton.patch.HideCastButtonPatch
|
import app.revanced.patches.youtube.layout.castbutton.patch.HideCastButtonPatch
|
||||||
import app.revanced.patches.youtube.misc.clientspoof.patch.ClientSpoofPatch
|
import app.revanced.patches.youtube.misc.clientspoof.patch.ClientSpoofPatch
|
||||||
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
|
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
|
||||||
import app.revanced.patches.youtube.misc.microg.fingerprints.*
|
import app.revanced.patches.youtube.misc.microg.fingerprints.*
|
||||||
import app.revanced.patches.youtube.misc.microg.patch.resource.MicroGResourcePatch
|
import app.revanced.patches.youtube.misc.microg.patch.resource.MicroGResourcePatch
|
||||||
import app.revanced.patches.youtube.misc.microg.patch.resource.enum.StringReplaceMode
|
import app.revanced.patches.youtube.misc.microg.shared.Constants.PACKAGE_NAME
|
||||||
import app.revanced.patches.youtube.misc.microg.shared.Constants.BASE_MICROG_PACKAGE_NAME
|
|
||||||
import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_PACKAGE_NAME
|
import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_PACKAGE_NAME
|
||||||
import org.jf.dexlib2.Opcode
|
import app.revanced.util.microg.MicroGBytecodeHelper
|
||||||
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
|
@Patch
|
||||||
@DependsOn(
|
@DependsOn(
|
||||||
@ -51,121 +40,27 @@ class MicroGBytecodePatch : BytecodePatch(
|
|||||||
PrimeFingerprint,
|
PrimeFingerprint,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
override fun execute(data: BytecodeData): PatchResult {
|
override fun execute(data: BytecodeData) =
|
||||||
disablePlayServiceChecksAndFixCastIssues()
|
// apply common microG patch
|
||||||
data.classes.forEach { classDef ->
|
MicroGBytecodeHelper.patchBytecode(
|
||||||
var proxiedClass: MutableClass? = null
|
data, arrayOf(
|
||||||
|
MicroGBytecodeHelper.packageNameTransform(
|
||||||
classDef.methods.forEach methodLoop@{ method ->
|
PACKAGE_NAME,
|
||||||
val implementation = method.implementation ?: return@methodLoop
|
REVANCED_PACKAGE_NAME
|
||||||
|
)
|
||||||
var proxiedImplementation: MutableMethodImplementation? = null
|
),
|
||||||
|
MicroGBytecodeHelper.PrimeMethodTransformationData(
|
||||||
implementation.instructions.forEachIndexed { i, instruction ->
|
PrimeFingerprint,
|
||||||
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed
|
PACKAGE_NAME,
|
||||||
|
REVANCED_PACKAGE_NAME
|
||||||
val stringValue = ((instruction as Instruction21c).reference as StringReference).string
|
),
|
||||||
|
listOf(
|
||||||
val replaceMode = if (stringValue.equalsAny(
|
IntegrityCheckFingerprint,
|
||||||
"com.google.android.gms",
|
ServiceCheckFingerprint,
|
||||||
"com.google.android.c2dm.intent.REGISTER",
|
GooglePlayUtilityFingerprint,
|
||||||
"com.google.android.c2dm.permission.SEND",
|
CastDynamiteModuleFingerprint,
|
||||||
"com.google.iid.TOKEN_REQUEST",
|
CastDynamiteModuleV2Fingerprint,
|
||||||
"com.google",
|
CastContextFetchFingerprint
|
||||||
"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.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.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", REVANCED_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 disablePlayServiceChecksAndFixCastIssues() {
|
|
||||||
listOf(
|
|
||||||
IntegrityCheckFingerprint,
|
|
||||||
ServiceCheckFingerprint,
|
|
||||||
GooglePlayUtilityFingerprint,
|
|
||||||
CastDynamiteModuleFingerprint,
|
|
||||||
CastDynamiteModuleV2Fingerprint,
|
|
||||||
CastContextFetchFingerprint
|
|
||||||
).forEach { fingerprint ->
|
|
||||||
val result = fingerprint.result!!
|
|
||||||
val stringInstructions = when (result.method.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.mutableMethod.addInstructions(
|
|
||||||
0, stringInstructions
|
|
||||||
)
|
)
|
||||||
}
|
).let { PatchResultSuccess() }
|
||||||
|
|
||||||
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.youtube") return@indexOfFirst false
|
|
||||||
|
|
||||||
register = it.registerA
|
|
||||||
return@indexOfFirst true
|
|
||||||
}
|
|
||||||
|
|
||||||
primeMethod.replaceInstruction(
|
|
||||||
index, "const-string v$register, \"$REVANCED_PACKAGE_NAME\""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,21 @@ import app.revanced.patcher.patch.PatchResult
|
|||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
import app.revanced.patcher.patch.impl.ResourcePatch
|
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||||
|
import app.revanced.patches.music.misc.microg.shared.Constants
|
||||||
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
|
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
|
||||||
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
|
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
|
||||||
import app.revanced.patches.youtube.misc.microg.shared.Constants.BASE_MICROG_PACKAGE_NAME
|
import app.revanced.patches.youtube.misc.microg.shared.Constants.PACKAGE_NAME
|
||||||
|
import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_APP_NAME
|
||||||
import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_PACKAGE_NAME
|
import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_PACKAGE_NAME
|
||||||
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch
|
import app.revanced.patches.youtube.misc.microg.shared.Constants.SPOOFED_PACKAGE_NAME
|
||||||
|
import app.revanced.patches.youtube.misc.microg.shared.Constants.SPOOFED_PACKAGE_SIGNATURE
|
||||||
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference
|
import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference
|
||||||
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
|
||||||
|
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch
|
||||||
|
import app.revanced.util.microg.Constants.MICROG_VENDOR
|
||||||
|
import app.revanced.util.microg.MicroGManifestHelper
|
||||||
|
import app.revanced.util.microg.MicroGResourceHelper
|
||||||
|
|
||||||
@Name("microg-resource-patch")
|
@Name("microg-resource-patch")
|
||||||
@DependsOn([FixLocaleConfigErrorPatch::class, SettingsResourcePatch::class])
|
@DependsOn([FixLocaleConfigErrorPatch::class, SettingsResourcePatch::class])
|
||||||
@ -27,32 +34,26 @@ class MicroGResourcePatch : ResourcePatch() {
|
|||||||
SettingsPatch.addPreference(
|
SettingsPatch.addPreference(
|
||||||
Preference(
|
Preference(
|
||||||
StringResource("microg_settings", "MicroG Settings"),
|
StringResource("microg_settings", "MicroG Settings"),
|
||||||
Preference.Intent("$BASE_MICROG_PACKAGE_NAME.android.gms", "", "org.microg.gms.ui.SettingsActivity"),
|
Preference.Intent("$MICROG_VENDOR.android.gms", "", "org.microg.gms.ui.SettingsActivity"),
|
||||||
StringResource("microg_settings_summary", "Settings for MicroG"),
|
StringResource("microg_settings_summary", "Settings for MicroG"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
SettingsPatch.renameIntentsTargetPackage(REVANCED_PACKAGE_NAME)
|
SettingsPatch.renameIntentsTargetPackage(REVANCED_PACKAGE_NAME)
|
||||||
|
|
||||||
val manifest = data["AndroidManifest.xml"]
|
// update manifest
|
||||||
manifest.writeText(
|
MicroGResourceHelper.patchManifest(
|
||||||
manifest.readText()
|
data,
|
||||||
.replace(
|
PACKAGE_NAME,
|
||||||
"package=\"com.google.android.youtube", "package=\"$REVANCED_PACKAGE_NAME"
|
REVANCED_PACKAGE_NAME,
|
||||||
).replace(
|
REVANCED_APP_NAME
|
||||||
"android:authorities=\"com.google.android.youtube", "android:authorities=\"$REVANCED_PACKAGE_NAME"
|
|
||||||
).replace(
|
|
||||||
"com.google.android.youtube.permission.C2D_MESSAGE", "$REVANCED_PACKAGE_NAME.permission.C2D_MESSAGE"
|
|
||||||
).replace( // might not be needed
|
|
||||||
"com.google.android.youtube.lifecycle-trojan", "$REVANCED_PACKAGE_NAME.lifecycle-trojan"
|
|
||||||
).replace( // might not be needed
|
|
||||||
"com.google.android.youtube.photopicker_images", "$REVANCED_PACKAGE_NAME.photopicker_images"
|
|
||||||
).replace(
|
|
||||||
"com.google.android.c2dm", "$BASE_MICROG_PACKAGE_NAME.android.c2dm"
|
|
||||||
).replace(
|
|
||||||
"</queries>", "<package android:name=\"$BASE_MICROG_PACKAGE_NAME.android.gms\"/></queries>"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// add metadata to manifest
|
||||||
|
MicroGManifestHelper.addSpoofingMetadata(
|
||||||
|
data,
|
||||||
|
SPOOFED_PACKAGE_NAME,
|
||||||
|
SPOOFED_PACKAGE_SIGNATURE
|
||||||
|
)
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.misc.microg.patch.resource.enum
|
|
||||||
|
|
||||||
enum class StringReplaceMode {
|
|
||||||
REPLACE_WITH_MICROG, REPLACE_WITH_REVANCED, DO_NOT_REPLACE
|
|
||||||
}
|
|
@ -1,6 +1,9 @@
|
|||||||
package app.revanced.patches.youtube.misc.microg.shared
|
package app.revanced.patches.youtube.misc.microg.shared
|
||||||
|
|
||||||
object Constants {
|
object Constants {
|
||||||
internal const val BASE_MICROG_PACKAGE_NAME = "com.mgoogle"
|
internal const val REVANCED_APP_NAME = "YouTube ReVanced"
|
||||||
internal const val REVANCED_PACKAGE_NAME = "app.revanced.android.youtube"
|
internal const val REVANCED_PACKAGE_NAME = "app.revanced.android.youtube"
|
||||||
|
internal const val PACKAGE_NAME = "com.google.android.youtube"
|
||||||
|
internal const val SPOOFED_PACKAGE_NAME = PACKAGE_NAME
|
||||||
|
internal const val SPOOFED_PACKAGE_SIGNATURE = "24bb24c05e47e0aefa68a58a766179d9b613a600"
|
||||||
}
|
}
|
130
src/main/kotlin/app/revanced/util/microg/Constants.kt
Normal file
130
src/main/kotlin/app/revanced/util/microg/Constants.kt
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package app.revanced.util.microg
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constants for microG builds with signature spoofing
|
||||||
|
*/
|
||||||
|
internal object Constants {
|
||||||
|
/**
|
||||||
|
* microG vendor name
|
||||||
|
* aka. package prefix / package base
|
||||||
|
*/
|
||||||
|
const val MICROG_VENDOR = "com.mgoogle"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta-data for microG package name spoofing on patched builds
|
||||||
|
*/
|
||||||
|
const val META_SPOOFED_PACKAGE_NAME = "$MICROG_VENDOR.android.gms.SPOOFED_PACKAGE_NAME"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta-data for microG package signature spoofing on patched builds
|
||||||
|
*/
|
||||||
|
const val META_SPOOFED_PACKAGE_SIGNATURE = "$MICROG_VENDOR.android.gms.SPOOFED_PACKAGE_SIGNATURE"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* meta-data for microG package detection
|
||||||
|
*/
|
||||||
|
const val META_GMS_PACKAGE_NAME = "app.revanced.MICROG_PACKAGE_NAME"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a list of all permissions in microG
|
||||||
|
*/
|
||||||
|
val PERMISSIONS = listOf(
|
||||||
|
// C2DM / GCM
|
||||||
|
"com.google.android.c2dm.permission.RECEIVE",
|
||||||
|
"com.google.android.c2dm.permission.SEND",
|
||||||
|
"com.google.android.gtalkservice.permission.GTALK_SERVICE",
|
||||||
|
|
||||||
|
// GAuth
|
||||||
|
"com.google.android.googleapps.permission.GOOGLE_AUTH",
|
||||||
|
"com.google.android.googleapps.permission.GOOGLE_AUTH.cp",
|
||||||
|
"com.google.android.googleapps.permission.GOOGLE_AUTH.local",
|
||||||
|
"com.google.android.googleapps.permission.GOOGLE_AUTH.mail",
|
||||||
|
"com.google.android.googleapps.permission.GOOGLE_AUTH.writely",
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a list of all (intent) actions in microG
|
||||||
|
*/
|
||||||
|
val ACTIONS = listOf(
|
||||||
|
// location
|
||||||
|
"com.google.android.gms.location.places.ui.PICK_PLACE",
|
||||||
|
"com.google.android.gms.location.places.GeoDataApi",
|
||||||
|
"com.google.android.gms.location.places.PlacesApi",
|
||||||
|
"com.google.android.gms.location.places.PlaceDetectionApi",
|
||||||
|
"com.google.android.gms.wearable.MESSAGE_RECEIVED",
|
||||||
|
|
||||||
|
// C2DM / GCM
|
||||||
|
"com.google.android.c2dm.intent.REGISTER",
|
||||||
|
"com.google.android.c2dm.intent.REGISTRATION",
|
||||||
|
"com.google.android.c2dm.intent.UNREGISTER",
|
||||||
|
"com.google.android.c2dm.intent.RECEIVE",
|
||||||
|
"com.google.iid.TOKEN_REQUEST",
|
||||||
|
"com.google.android.gcm.intent.SEND",
|
||||||
|
|
||||||
|
// car
|
||||||
|
"com.google.android.gms.car.service.START",
|
||||||
|
|
||||||
|
// people
|
||||||
|
"com.google.android.gms.people.service.START",
|
||||||
|
|
||||||
|
// wearable
|
||||||
|
"com.google.android.gms.wearable.BIND",
|
||||||
|
|
||||||
|
// auth
|
||||||
|
"com.google.android.gsf.login",
|
||||||
|
"com.google.android.gsf.action.GET_GLS",
|
||||||
|
"com.google.android.gms.common.account.CHOOSE_ACCOUNT",
|
||||||
|
"com.google.android.gms.auth.login.LOGIN",
|
||||||
|
"com.google.android.gms.auth.api.credentials.PICKER",
|
||||||
|
"com.google.android.gms.auth.api.credentials.service.START",
|
||||||
|
"com.google.android.gms.auth.service.START",
|
||||||
|
"com.google.firebase.auth.api.gms.service.START",
|
||||||
|
"com.google.android.gms.auth.be.appcert.AppCertService",
|
||||||
|
|
||||||
|
// fido
|
||||||
|
"com.google.android.gms.fido.fido2.privileged.START",
|
||||||
|
|
||||||
|
// games
|
||||||
|
"com.google.android.gms.games.service.START",
|
||||||
|
"com.google.android.gms.games.PLAY_GAMES_UPGRADE",
|
||||||
|
|
||||||
|
// chimera
|
||||||
|
"com.google.android.gms.chimera",
|
||||||
|
|
||||||
|
// fonts
|
||||||
|
"com.google.android.gms.fonts",
|
||||||
|
|
||||||
|
// phenotype
|
||||||
|
"com.google.android.gms.phenotype.service.START",
|
||||||
|
|
||||||
|
// location
|
||||||
|
"com.google.android.gms.location.reporting.service.START",
|
||||||
|
|
||||||
|
// misc
|
||||||
|
"com.google.android.gms.gmscompliance.service.START",
|
||||||
|
"com.google.android.gms.oss.licenses.service.START",
|
||||||
|
"com.google.android.gms.safetynet.service.START",
|
||||||
|
"com.google.android.gms.tapandpay.service.BIND"
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a list of all content provider authorities in microG
|
||||||
|
*/
|
||||||
|
val AUTHORITIES = listOf(
|
||||||
|
// gsf
|
||||||
|
"com.google.android.gsf.gservices",
|
||||||
|
"com.google.settings",
|
||||||
|
|
||||||
|
// auth
|
||||||
|
"com.google.android.gms.auth.accounts",
|
||||||
|
|
||||||
|
// chimera
|
||||||
|
"com.google.android.gms.chimera",
|
||||||
|
|
||||||
|
// fonts
|
||||||
|
"com.google.android.gms.fonts",
|
||||||
|
|
||||||
|
// phenotype
|
||||||
|
"com.google.android.gms.phenotype"
|
||||||
|
)
|
||||||
|
}
|
229
src/main/kotlin/app/revanced/util/microg/MicroGBytecodeHelper.kt
Normal file
229
src/main/kotlin/app/revanced/util/microg/MicroGBytecodeHelper.kt
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
package app.revanced.util.microg
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.impl.BytecodeData
|
||||||
|
import app.revanced.patcher.extensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.util.microg.Constants.ACTIONS
|
||||||
|
import app.revanced.util.microg.Constants.AUTHORITIES
|
||||||
|
import app.revanced.util.microg.Constants.MICROG_VENDOR
|
||||||
|
import app.revanced.util.microg.Constants.PERMISSIONS
|
||||||
|
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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for applying bytecode patches needed for the microg-support patches.
|
||||||
|
*/
|
||||||
|
internal object MicroGBytecodeHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform strings with package name out of [fromPackageName] and [toPackageName].
|
||||||
|
*
|
||||||
|
* @param fromPackageName Original package name.
|
||||||
|
* @param toPackageName The package name to accept.
|
||||||
|
**/
|
||||||
|
fun packageNameTransform(fromPackageName: String, toPackageName: String): (String) -> String? {
|
||||||
|
return { referencedString ->
|
||||||
|
when (referencedString) {
|
||||||
|
"$fromPackageName.SuggestionsProvider",
|
||||||
|
"$fromPackageName.fileprovider" -> referencedString.replace(fromPackageName, toPackageName)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prime method data class for the [MicroGBytecodeHelper] class.
|
||||||
|
*
|
||||||
|
* @param primeMethodFingerprint The prime methods [MethodFingerprint].
|
||||||
|
* @param fromPackageName Original package name.
|
||||||
|
* @param toPackageName The package name to accept.
|
||||||
|
**/
|
||||||
|
data class PrimeMethodTransformationData(
|
||||||
|
val primeMethodFingerprint: MethodFingerprint,
|
||||||
|
val fromPackageName: String,
|
||||||
|
val toPackageName: String
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Patch the prime method to accept the new package name.
|
||||||
|
*/
|
||||||
|
fun transformPrimeMethodPackageName() {
|
||||||
|
val primeMethod = primeMethodFingerprint.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 != fromPackageName) return@indexOfFirst false
|
||||||
|
|
||||||
|
register = it.registerA
|
||||||
|
return@indexOfFirst true
|
||||||
|
}
|
||||||
|
|
||||||
|
primeMethod.replaceInstruction(
|
||||||
|
index, "const-string v$register, \"$toPackageName\""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patch the bytecode to work with MicroG.
|
||||||
|
* Note: this only handles string constants to gms (intent actions, authorities, ...).
|
||||||
|
* If the app employs additional checks to validate the installed gms package, you'll have to handle those in the app- specific patch
|
||||||
|
*
|
||||||
|
* @param data Bytecode data instance.
|
||||||
|
* @param additionalStringTransforms Additional transformations applied to all const-string references.
|
||||||
|
* @param primeMethodTransformationData Data to patch the prime method.
|
||||||
|
* @param earlyReturns List of [MethodFingerprint] to return the resolved methods early.
|
||||||
|
*/
|
||||||
|
fun patchBytecode(
|
||||||
|
data: BytecodeData,
|
||||||
|
additionalStringTransforms: Array<(str: String) -> String?>,
|
||||||
|
primeMethodTransformationData: PrimeMethodTransformationData,
|
||||||
|
earlyReturns: List<MethodFingerprint>
|
||||||
|
) {
|
||||||
|
earlyReturns.returnEarly()
|
||||||
|
primeMethodTransformationData.transformPrimeMethodPackageName()
|
||||||
|
|
||||||
|
val allTransforms = arrayOf(
|
||||||
|
MicroGBytecodeHelper::commonTransform,
|
||||||
|
MicroGBytecodeHelper::contentUrisTransform,
|
||||||
|
*additionalStringTransforms
|
||||||
|
)
|
||||||
|
|
||||||
|
// transform all strings using all provided transforms, first match wins
|
||||||
|
data.transformStringReferences transform@{
|
||||||
|
for (transformFn in allTransforms) {
|
||||||
|
val s = transformFn(it)
|
||||||
|
if (s != null) return@transform s
|
||||||
|
}
|
||||||
|
|
||||||
|
return@transform null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* const-string transform function for common gms string references.
|
||||||
|
*
|
||||||
|
* @param referencedString The string to transform.
|
||||||
|
*/
|
||||||
|
private fun commonTransform(referencedString: String): String? =
|
||||||
|
when (referencedString) {
|
||||||
|
"com.google",
|
||||||
|
"com.google.android.gms",
|
||||||
|
in PERMISSIONS,
|
||||||
|
in ACTIONS,
|
||||||
|
in AUTHORITIES -> referencedString.replace("com.google", MICROG_VENDOR)
|
||||||
|
|
||||||
|
// subscribedfeeds has no vendor prefix for whatever reason...
|
||||||
|
"subscribedfeeds" -> "${MICROG_VENDOR}.subscribedfeeds"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* const-string transform function for strings containing gms content uris / authorities.
|
||||||
|
*/
|
||||||
|
private fun contentUrisTransform(str: String): String? {
|
||||||
|
// only when content:// uri
|
||||||
|
if (str.startsWith("content://")) {
|
||||||
|
// check if matches any authority
|
||||||
|
for (authority in AUTHORITIES) {
|
||||||
|
val uriPrefix = "content://$authority"
|
||||||
|
if (str.startsWith(uriPrefix)) {
|
||||||
|
return str.replace(
|
||||||
|
uriPrefix,
|
||||||
|
"content://${authority.replace("com.google", MICROG_VENDOR)}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// gms also has a 'subscribedfeeds' authority, check for that one too
|
||||||
|
val subFeedsUriPrefix = "content://subscribedfeeds"
|
||||||
|
if (str.startsWith(subFeedsUriPrefix)) {
|
||||||
|
return str.replace(subFeedsUriPrefix, "content://${MICROG_VENDOR}.subscribedfeeds")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform all constant string references using a transformation function.
|
||||||
|
*
|
||||||
|
* @param transformFn string transformation function. if null, string is not changed.
|
||||||
|
*/
|
||||||
|
private fun BytecodeData.transformStringReferences(transformFn: (str: String) -> String?) {
|
||||||
|
classes.forEach { classDef ->
|
||||||
|
var mutableClass: MutableClass? = null
|
||||||
|
|
||||||
|
// enumerate all methods
|
||||||
|
classDef.methods.forEach classLoop@{ methodDef ->
|
||||||
|
var mutableMethod: MutableMethod? = null
|
||||||
|
val implementation = methodDef.implementation ?: return@classLoop
|
||||||
|
|
||||||
|
// enumerate all instructions and find const-string
|
||||||
|
implementation.instructions.forEachIndexed implLoop@{ index, instruction ->
|
||||||
|
// skip all that are not const-string
|
||||||
|
if (instruction.opcode != Opcode.CONST_STRING) return@implLoop
|
||||||
|
val str = ((instruction as Instruction21c).reference as StringReference).string
|
||||||
|
|
||||||
|
// call transform function
|
||||||
|
val transformedStr = transformFn(str)
|
||||||
|
if (transformedStr != null) {
|
||||||
|
// make class and method mutable, if not already
|
||||||
|
mutableClass = mutableClass ?: proxy(classDef).resolve()
|
||||||
|
mutableMethod = mutableMethod ?: mutableClass!!.methods.first {
|
||||||
|
it.name == methodDef.name && it.parameterTypes.containsAll(methodDef.parameterTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace instruction with updated string
|
||||||
|
mutableMethod!!.implementation!!.replaceInstruction(
|
||||||
|
index,
|
||||||
|
BuilderInstruction21c(
|
||||||
|
Opcode.CONST_STRING,
|
||||||
|
instruction.registerA,
|
||||||
|
ImmutableStringReference(
|
||||||
|
transformedStr
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the resolved methods of a list of [MethodFingerprint] early.
|
||||||
|
*/
|
||||||
|
private fun List<MethodFingerprint>.returnEarly() {
|
||||||
|
this.forEach { fingerprint ->
|
||||||
|
val result = fingerprint.result!!
|
||||||
|
val stringInstructions = when (result.method.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.mutableMethod.addInstructions(
|
||||||
|
0, stringInstructions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
package app.revanced.util.microg
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
|
import app.revanced.util.microg.Constants.META_GMS_PACKAGE_NAME
|
||||||
|
import app.revanced.util.microg.Constants.META_SPOOFED_PACKAGE_NAME
|
||||||
|
import app.revanced.util.microg.Constants.META_SPOOFED_PACKAGE_SIGNATURE
|
||||||
|
import app.revanced.util.microg.Constants.MICROG_VENDOR
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
import org.w3c.dom.Node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper class for adding manifest metadata needed for microG builds with signature spoofing
|
||||||
|
*/
|
||||||
|
internal object MicroGManifestHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add manifest entries needed for package and signature spoofing when using MicroG.
|
||||||
|
* Note: this only adds metadata entries for signature spoofing, other changes may still be required to make a microG patch work.
|
||||||
|
*
|
||||||
|
* @param data Resource data.
|
||||||
|
* @param spoofedPackage The package to spoof.
|
||||||
|
* @param spoofedSignature The signature to spoof.
|
||||||
|
*/
|
||||||
|
fun addSpoofingMetadata(
|
||||||
|
data: ResourceData,
|
||||||
|
spoofedPackage: String,
|
||||||
|
spoofedSignature: String
|
||||||
|
) {
|
||||||
|
data.xmlEditor["AndroidManifest.xml"].use {
|
||||||
|
val applicationNode = it
|
||||||
|
.file
|
||||||
|
.getElementsByTagName("application")
|
||||||
|
.item(0)
|
||||||
|
|
||||||
|
// package spoofing
|
||||||
|
applicationNode.adoptChild("meta-data") {
|
||||||
|
setAttribute("android:name", META_SPOOFED_PACKAGE_NAME)
|
||||||
|
setAttribute("android:value", spoofedPackage)
|
||||||
|
}
|
||||||
|
applicationNode.adoptChild("meta-data") {
|
||||||
|
setAttribute("android:name", META_SPOOFED_PACKAGE_SIGNATURE)
|
||||||
|
setAttribute("android:value", spoofedSignature)
|
||||||
|
}
|
||||||
|
|
||||||
|
// microG presence detection in integrations
|
||||||
|
applicationNode.adoptChild("meta-data") {
|
||||||
|
setAttribute("android:name", META_GMS_PACKAGE_NAME)
|
||||||
|
setAttribute("android:value", "${MICROG_VENDOR}.android.gms")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Node.adoptChild(tagName: String, block: Element.() -> Unit) {
|
||||||
|
val child = ownerDocument.createElement(tagName)
|
||||||
|
child.block()
|
||||||
|
appendChild(child)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package app.revanced.util.microg
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.impl.ResourceData
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for applying resource patches needed for the microg-support patches.
|
||||||
|
*/
|
||||||
|
internal object MicroGResourceHelper {
|
||||||
|
/**
|
||||||
|
* Patch the manifest to work with MicroG.
|
||||||
|
*
|
||||||
|
* @param data Bytecode data instance.
|
||||||
|
* @param fromPackageName Original package name.
|
||||||
|
* @param toPackageName The package name to accept.
|
||||||
|
* @param toName The new name of the app.
|
||||||
|
*/
|
||||||
|
fun patchManifest(
|
||||||
|
data: ResourceData,
|
||||||
|
fromPackageName: String,
|
||||||
|
toPackageName: String,
|
||||||
|
toName: String
|
||||||
|
) {
|
||||||
|
val manifest = data["AndroidManifest.xml"].readText()
|
||||||
|
data["AndroidManifest.xml"].writeText(
|
||||||
|
manifest.replace(
|
||||||
|
"package=\"$fromPackageName",
|
||||||
|
"package=\"$toPackageName"
|
||||||
|
).replace(
|
||||||
|
"android:label=\"@string/app_name",
|
||||||
|
"android:label=\"$toName"
|
||||||
|
).replace(
|
||||||
|
"android:label=\"@string/app_launcher_name",
|
||||||
|
"android:label=\"$toName"
|
||||||
|
).replace(
|
||||||
|
"android:authorities=\"$fromPackageName",
|
||||||
|
"android:authorities=\"$toPackageName"
|
||||||
|
).replace(
|
||||||
|
"$fromPackageName.permission.C2D_MESSAGE",
|
||||||
|
"$toPackageName.permission.C2D_MESSAGE"
|
||||||
|
).replace(
|
||||||
|
"com.google.android.c2dm",
|
||||||
|
"${Constants.MICROG_VENDOR}.android.c2dm"
|
||||||
|
).replace(
|
||||||
|
"</queries>",
|
||||||
|
"<package android:name=\"${Constants.MICROG_VENDOR}.android.gms\"/></queries>"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user