Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Google News): Add Enable CustomTabs and GmsCore support patch #3111

Merged
merged 9 commits into from
Jul 20, 2024
28 changes: 28 additions & 0 deletions api/revanced-patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,34 @@ public final class app/revanced/patches/lightroom/misc/premium/UnlockPremiumPatc
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/magazines/misc/customtabs/UseCustomTabs : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/magazines/misc/customtabs/UseCustomTabs;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/magazines/misc/gms/ClientSpoofPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch {
public static final field INSTANCE Lapp/revanced/patches/magazines/misc/gms/ClientSpoofPatch;
public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object;
public fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Lkotlin/Triple;
public synthetic fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/Object;)V
public fun transform (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lkotlin/Triple;)V
}

public final class app/revanced/patches/magazines/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportPatch {
public static final field INSTANCE Lapp/revanced/patches/magazines/misc/gms/GmsCoreSupportPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}

public final class app/revanced/patches/magazines/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/BaseGmsCoreSupportResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/magazines/misc/gms/GmsCoreSupportResourcePatch;
}

public final class app/revanced/patches/magazines/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch {
public static final field INSTANCE Lapp/revanced/patches/magazines/misc/integrations/IntegrationsPatch;
}

public final class app/revanced/patches/memegenerator/detection/license/LicenseValidationPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/memegenerator/detection/license/LicenseValidationPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package app.revanced.patches.magazines.misc.customtabs
oSumAtrIX marked this conversation as resolved.
Show resolved Hide resolved

import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.magazines.misc.gms.customtabs.fingerprints.UseCustomTabsFingerprint
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction

@Patch(
name = "Enable CustomTabs",
description = "Enables CustomTabs which allows the Articles to be opened in your set Default Browser instead of Chrome.",
dependencies = [],
compatiblePackages = [CompatiblePackage("com.google.android.apps.magazines")]
)
@Suppress("unused")
object UseCustomTabs : BytecodePatch(setOf(UseCustomTabsFingerprint)) {
override fun execute(context: BytecodeContext) = UseCustomTabsFingerprint.result?.let { result ->
val cmpIndex = result.scanResult.patternScanResult!!.endIndex + 1
val cmpResultRegister = result.mutableMethod.getInstruction<OneRegisterInstruction>(cmpIndex).registerA
result.mutableMethod.replaceInstruction(cmpIndex, "const/4 v${cmpResultRegister}, 0x1")
} ?: throw UseCustomTabsFingerprint.exception
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package app.revanced.patches.magazines.misc.gms.customtabs.fingerprints

import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
import app.revanced.patcher.extensions.or
import com.android.tools.smali.dexlib2.AccessFlags

benjy3gg marked this conversation as resolved.
Show resolved Hide resolved


internal object UseCustomTabsFingerprint : MethodFingerprint(
customFingerprint = { methodDef, _ ->
benjy3gg marked this conversation as resolved.
Show resolved Hide resolved
methodDef.definingClass == "Lcom/google/apps/dots/android/modules/reading/customtabs/CustomTabsArticleLauncher;" && methodDef.accessFlags == AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR
},
opcodes = listOf(
Opcode.IPUT_OBJECT,
Opcode.CONST_4,
Opcode.IPUT,
Opcode.CONST_4,
Opcode.IPUT_BOOLEAN
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package app.revanced.patches.magazines.misc.gms

import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.Instruction35cInfo
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.magazines.misc.gms.Constants.MAGAZINES_PACKAGE_NAME
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference

@Patch(
name = "Client spoof",
description = "Spoofs the client to allow video playback.",
benjy3gg marked this conversation as resolved.
Show resolved Hide resolved
compatiblePackages = [
CompatiblePackage(MAGAZINES_PACKAGE_NAME),
benjy3gg marked this conversation as resolved.
Show resolved Hide resolved
],
use = true
benjy3gg marked this conversation as resolved.
Show resolved Hide resolved
)
object ClientSpoofPatch : BaseTransformInstructionsPatch<Instruction35cInfo>() {
private const val ORIGINAL_PACKAGE_NAME = MAGAZINES_PACKAGE_NAME
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"

override fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int,
) = filterMapInstruction35c<MethodCall>(
"Lapp/revanced/integrations",
classDef,
instruction,
instructionIndex,
)

override fun transform(mutableMethod: MutableMethod, entry: Instruction35cInfo) {
val (_, _, instructionIndex) = entry

// Replace the result of context.getPackageName(), if it is used in a user agent string.
mutableMethod.apply {
// After context.getPackageName() the result is moved to a register.
val targetRegister = (
getInstruction(instructionIndex + 1)
as? OneRegisterInstruction ?: return
).registerA

// IndexOutOfBoundsException is not possible here,
// but no such occurrences are present in the app.
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()

// This can technically also match non-user agent string builder append methods,
// but no such occurrences are present in the app.
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
return
}

// Overwrite the result of context.getPackageName() with the original package name.
replaceInstruction(
instructionIndex + 1,
"const-string v$targetRegister, \"${ORIGINAL_PACKAGE_NAME}\"",
)
}
}

@Suppress("unused")
private enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String,
) : IMethodCall {
GetPackageName(
"Landroid/content/Context;",
"getPackageName",
emptyArray(),
"Ljava/lang/String;",
),
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package app.revanced.patches.magazines.misc.gms

internal object Constants {
const val MAGAZINES_PACKAGE_NAME = "com.google.android.apps.magazines"
const val REVANCED_MAGAZINES_PACKAGE_NAME = "app.revanced.android.magazines"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package app.revanced.patches.magazines.misc.gms

import app.revanced.patcher.data.BytecodeContext
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch
import app.revanced.patches.magazines.misc.gms.Constants.MAGAZINES_PACKAGE_NAME
import app.revanced.patches.magazines.misc.gms.Constants.REVANCED_MAGAZINES_PACKAGE_NAME
import app.revanced.patches.magazines.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
import app.revanced.patches.magazines.misc.integrations.IntegrationsPatch
import app.revanced.patches.magazines.misc.gms.fingerprints.MagazinesActivityOnCreateFingerprint
import app.revanced.patches.magazines.misc.gms.fingerprints.GooglePlayUtilityFingerprint
import app.revanced.patches.magazines.misc.gms.fingerprints.ServiceCheckFingerprint

@Suppress("unused")
object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
fromPackageName = MAGAZINES_PACKAGE_NAME,
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
primeMethodFingerprint = null,
earlyReturnFingerprints = setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
),
mainActivityOnCreateFingerprint = MagazinesActivityOnCreateFingerprint,
integrationsPatchDependency = IntegrationsPatch::class,
dependencies = setOf(
),
benjy3gg marked this conversation as resolved.
Show resolved Hide resolved
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
compatiblePackages = setOf(
CompatiblePackage(
MAGAZINES_PACKAGE_NAME,
),
),
fingerprints = setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
),
) {
override val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption

override fun execute(context: BytecodeContext) {
super.execute(context)
}
benjy3gg marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package app.revanced.patches.magazines.misc.gms

import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportResourcePatch
import app.revanced.patches.magazines.misc.gms.Constants.MAGAZINES_PACKAGE_NAME
import app.revanced.patches.magazines.misc.gms.Constants.REVANCED_MAGAZINES_PACKAGE_NAME

object GmsCoreSupportResourcePatch : BaseGmsCoreSupportResourcePatch(
fromPackageName = MAGAZINES_PACKAGE_NAME,
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app.revanced.patches.magazines.misc.gms.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags

internal object GooglePlayUtilityFingerprint : MethodFingerprint(
oSumAtrIX marked this conversation as resolved.
Show resolved Hide resolved
returnType = "I",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("L", "I"),
strings = listOf("This should never happen.", "MetadataValueReader", "com.google.android.gms")
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package app.revanced.patches.magazines.misc.gms.fingerprints

import app.revanced.patcher.fingerprint.MethodFingerprint

internal object MagazinesActivityOnCreateFingerprint : MethodFingerprint(
customFingerprint = { methodDef, classDef ->
classDef.type.endsWith("/StartActivity;") && methodDef.name == "onCreate"
}
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package app.revanced.patches.magazines.misc.gms.fingerprints


import app.revanced.patcher.fingerprint.MethodFingerprint

internal object PrimeMethodFingerprint : MethodFingerprint(
strings = listOf("com.google.android.GoogleCamera", "com.android.vending")
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app.revanced.patches.magazines.misc.gms.fingerprints

import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags

internal object ServiceCheckFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("L", "I"),
strings = listOf("Google Play Services not available", "GooglePlayServices not available due to error ")
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package app.revanced.patches.magazines.misc.integrations

import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.magazines.misc.integrations.fingerprints.StartActivityInitFingerprint
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch

@Patch(requiresIntegrations = true)
object IntegrationsPatch : BaseIntegrationsPatch(
setOf(
StartActivityInitFingerprint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any other exported activities that can be launched? Because if so, they should be added here because if they are launched, context would not be hooked.

),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package app.revanced.patches.magazines.misc.integrations.fingerprints

import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction11x
import com.android.tools.smali.dexlib2.iface.reference.MethodReference

internal object StartActivityInitFingerprint : IntegrationsFingerprint(
opcodes = listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.IPUT_OBJECT,
Opcode.IPUT_BOOLEAN,
Opcode.INVOKE_VIRTUAL, // invoke-virtual {p0}, Lcom/google/apps/dots/android/newsstand/activity/StartActivity;->getApplicationContext()Landroid/content/Context;
Opcode.MOVE_RESULT_OBJECT, // move-result-object v2
),
insertIndexResolver = { method ->
method.implementation!!.instructions.indexOfFirst { it.getReference<MethodReference>().toString().contains("getApplicationContext") } + 2 // put this below move-result-object v2
},
contextRegisterResolver = { method ->
((method.implementation!!.instructions.toList()[method.implementation!!.instructions.indexOfFirst { it.getReference<MethodReference>().toString().contains("getApplicationContext") } + 1] as BuilderInstruction11x).registerA) // get from move-result-object v2
},
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
customFingerprint = { methodDef, _ -> methodDef.definingClass == "Lcom/google/apps/dots/android/newsstand/activity/StartActivity;" && methodDef.name == "onCreate" },
)
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
abstract class BaseGmsCoreSupportPatch(
private val fromPackageName: String,
private val toPackageName: String,
private val primeMethodFingerprint: MethodFingerprint,
private val primeMethodFingerprint: MethodFingerprint?,
private val earlyReturnFingerprints: Set<MethodFingerprint>,
private val mainActivityOnCreateFingerprint: MethodFingerprint,
private val integrationsPatchDependency: PatchClass,
Expand Down Expand Up @@ -91,7 +91,7 @@ abstract class BaseGmsCoreSupportPatch(
}

// Specific method that needs to be patched.
transformPrimeMethod(packageName)
primeMethodFingerprint?.let { transformPrimeMethod(packageName) }

// Return these methods early to prevent the app from crashing.
earlyReturnFingerprints.toList().returnEarly()
Expand Down Expand Up @@ -192,7 +192,7 @@ abstract class BaseGmsCoreSupportPatch(
}

private fun transformPrimeMethod(packageName: String) {
primeMethodFingerprint.result?.mutableMethod?.apply {
primeMethodFingerprint!!.result?.mutableMethod?.apply {
var register = 2

val index = getInstructions().indexOfFirst {
Expand Down Expand Up @@ -312,6 +312,9 @@ abstract class BaseGmsCoreSupportPatch(
// droidguard/ safetynet
"com.google.android.gms.droidguard.service.START",
"com.google.android.gms.safetynet.service.START",

// google news
benjy3gg marked this conversation as resolved.
Show resolved Hide resolved
"com.google.android.gms.accountsettings.action.VIEW_SETTINGS"
)

/**
Expand Down