From 37a8707746678e8eb3586e1d4c9e29f4a9e4856a Mon Sep 17 00:00:00 2001 From: 1fexd Date: Thu, 2 May 2024 08:50:00 +0200 Subject: [PATCH] feat: Add existing ID Austria patches --- api/revanced-patches-template.api | 24 ++- build.gradle.kts | 34 ++-- .../revanced/patches/example/ExamplePatch.kt | 20 --- .../detection/root/RootDetectionPatch.kt | 26 +++ .../AttestationSupportedCheckFingerprint.kt | 13 ++ .../BootloaderCheckFingerprint.kt | 13 ++ .../root/fingerprints/RootCheckFingerprint.kt | 13 ++ .../signature/SpoofSignaturePatch.kt | 41 +++++ .../fingerprints/SpoofSignatureFingerprint.kt | 13 ++ .../kotlin/app/revanced/util/BytecodeUtils.kt | 155 ++++++++++++++++++ 10 files changed, 313 insertions(+), 39 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/patches/example/ExamplePatch.kt create mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt create mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/AttestationSupportedCheckFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/BootloaderCheckFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/RootCheckFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt create mode 100644 src/main/kotlin/app/revanced/patches/idaustria/detection/signature/fingerprints/SpoofSignatureFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/util/BytecodeUtils.kt diff --git a/api/revanced-patches-template.api b/api/revanced-patches-template.api index 8b4a865..a01a7ae 100644 --- a/api/revanced-patches-template.api +++ b/api/revanced-patches-template.api @@ -1,6 +1,26 @@ -public final class app/revanced/patches/example/ExamplePatch : app/revanced/patcher/patch/BytecodePatch { - public static final field INSTANCE Lapp/revanced/patches/example/ExamplePatch; +public final class app/revanced/patches/idaustria/detection/root/RootDetectionPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/idaustria/detection/root/RootDetectionPatch; 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/idaustria/detection/signature/SpoofSignaturePatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V +} + +public final class app/revanced/util/BytecodeUtilsKt { + public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z + public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod; + public static final fun getException (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/patch/PatchException; + public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I + public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I + public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V + public static final fun resultOrThrow (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/fingerprint/MethodFingerprintResult; + public static final fun returnEarly (Ljava/util/List;Z)V + public static synthetic fun returnEarly$default (Ljava/util/List;ZILjava/lang/Object;)V + public static final fun transformMethods (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V + public static final fun traverseClassHierarchy (Lapp/revanced/patcher/data/BytecodeContext;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lkotlin/jvm/functions/Function1;)V +} + diff --git a/build.gradle.kts b/build.gradle.kts index 267b63b..1212fda 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { signing } -group = "app.revanced" +group = "fe.revanced.patches" repositories { mavenCentral() @@ -35,14 +35,14 @@ kotlin { tasks { withType(Jar::class) { manifest { - attributes["Name"] = "ReVanced Patches template" - attributes["Description"] = "Patches template for ReVanced." + attributes["Name"] = "eGovPatchesAT ReVanced patches" + attributes["Description"] = "eGovPatchesAT ReVanced patches" attributes["Version"] = version attributes["Timestamp"] = System.currentTimeMillis().toString() - attributes["Source"] = "git@github.com:revanced/revanced-patches-template.git" - attributes["Author"] = "ReVanced" - attributes["Contact"] = "contact@revanced.app" - attributes["Origin"] = "https://revanced.app" + attributes["Source"] = "git@github.com:eGovPatchesAT/revanced-patches.git" + attributes["Author"] = "eGovPatchesAT" + attributes["Contact"] = "1fexd@420blaze.it" + attributes["Origin"] = "https://github.com/eGovPatchesAT" attributes["License"] = "GNU General Public License v3.0" } } @@ -83,7 +83,7 @@ publishing { repositories { maven { name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/revanced/revanced-patches-template") + url = uri("https://maven.pkg.github.com/eGovPatchesAT/revanced-patches") credentials { username = System.getenv("GITHUB_ACTOR") password = System.getenv("GITHUB_TOKEN") @@ -96,9 +96,9 @@ publishing { from(components["java"]) pom { - name = "ReVanced Patches template" - description = "Patches template for ReVanced." - url = "https://revanced.app" + name = "eGovPatchesAT ReVanced patches" + description = "eGovPatchesAT ReVanced patches" + url = "https://github.com/eGovPatchesAT" licenses { license { @@ -108,15 +108,15 @@ publishing { } developers { developer { - id = "ReVanced" - name = "ReVanced" - email = "contact@revanced.app" + id = "eGovPatchesAT" + name = "eGovPatchesAT" + email = "eGovPatchesAT@420blaze.it" } } scm { - connection = "scm:git:git://github.com/revanced/revanced-patches-template.git" - developerConnection = "scm:git:git@github.com:revanced/revanced-patches-template.git" - url = "https://github.com/revanced/revanced-patches-template" + connection = "scm:git:git://github.com/eGovPatchesAT/revanced-patches.git" + developerConnection = "scm:git:git@github.com:eGovPatchesAT/revanced-patches.git" + url = "https://github.com/eGovPatchesAT/revanced-patches" } } } diff --git a/src/main/kotlin/app/revanced/patches/example/ExamplePatch.kt b/src/main/kotlin/app/revanced/patches/example/ExamplePatch.kt deleted file mode 100644 index b4d00d7..0000000 --- a/src/main/kotlin/app/revanced/patches/example/ExamplePatch.kt +++ /dev/null @@ -1,20 +0,0 @@ -package app.revanced.patches.example - -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch - -@Patch( - name = "Example Patch", - description = "This is an example patch to start with.", - compatiblePackages = [ - CompatiblePackage("com.example.app", ["1.0.0"]), - ], -) -@Suppress("unused") -object ExamplePatch : BytecodePatch(emptySet()) { - override fun execute(context: BytecodeContext) { - // TODO("Not yet implemented") - } -} diff --git a/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt b/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt new file mode 100644 index 0000000..2b720d3 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/idaustria/detection/root/RootDetectionPatch.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.idaustria.detection.root + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.idaustria.detection.root.fingerprints.AttestationSupportedCheckFingerprint +import app.revanced.patches.idaustria.detection.root.fingerprints.BootloaderCheckFingerprint +import app.revanced.patches.idaustria.detection.root.fingerprints.RootCheckFingerprint +import app.revanced.util.returnEarly + +@Patch( + name = "Remove root detection", + description = "Removes the check for root permissions and unlocked bootloader.", + compatiblePackages = [CompatiblePackage("at.gv.oe.app")] +) +@Suppress("unused") +object RootDetectionPatch : BytecodePatch( + setOf(AttestationSupportedCheckFingerprint, BootloaderCheckFingerprint, RootCheckFingerprint) +) { + override fun execute(context: BytecodeContext) = listOf( + AttestationSupportedCheckFingerprint, + BootloaderCheckFingerprint, + RootCheckFingerprint + ).returnEarly(true) +} diff --git a/src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/AttestationSupportedCheckFingerprint.kt b/src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/AttestationSupportedCheckFingerprint.kt new file mode 100644 index 0000000..a03115d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/AttestationSupportedCheckFingerprint.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.idaustria.detection.root.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object AttestationSupportedCheckFingerprint : MethodFingerprint( + "V", + accessFlags = AccessFlags.PUBLIC.value, + customFingerprint = { methodDef, _ -> + methodDef.name == "attestationSupportCheck" && + methodDef.definingClass.endsWith("/DeviceIntegrityCheck;") + } +) diff --git a/src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/BootloaderCheckFingerprint.kt b/src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/BootloaderCheckFingerprint.kt new file mode 100644 index 0000000..f400d85 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/BootloaderCheckFingerprint.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.idaustria.detection.root.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object BootloaderCheckFingerprint : MethodFingerprint( + "Z", + accessFlags = AccessFlags.PUBLIC.value, + customFingerprint = { methodDef, _ -> + methodDef.name == "bootloaderCheck" && + methodDef.definingClass.endsWith("/DeviceIntegrityCheck;") + } +) diff --git a/src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/RootCheckFingerprint.kt b/src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/RootCheckFingerprint.kt new file mode 100644 index 0000000..5a36bcb --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/idaustria/detection/root/fingerprints/RootCheckFingerprint.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.idaustria.detection.root.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object RootCheckFingerprint : MethodFingerprint( + "V", + accessFlags = AccessFlags.PUBLIC.value, + customFingerprint = { methodDef, _ -> + methodDef.name == "rootCheck" && + methodDef.definingClass.endsWith("/DeviceIntegrityCheck;") + } +) diff --git a/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt b/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt new file mode 100644 index 0000000..ae427b7 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/SpoofSignaturePatch.kt @@ -0,0 +1,41 @@ +package app.revanced.patches.idaustria.detection.signature + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.idaustria.detection.signature.fingerprints.SpoofSignatureFingerprint + +@Patch( + name = "Spoof signature", + description = "Spoofs the signature of the app.", + compatiblePackages = [CompatiblePackage("at.gv.oe.app")] +) +@Suppress("unused") +object SpoofSignaturePatch : BytecodePatch( + setOf(SpoofSignatureFingerprint) +) { + private const val EXPECTED_SIGNATURE = + "OpenSSLRSAPublicKey{modulus=ac3e6fd6050aa7e0d6010ae58190404cd89a56935b44f6fee" + + "067c149768320026e10b24799a1339e414605e448e3f264444a327b9ae292be2b62ad567dd1800dbed4a88f718a33dc6db6b" + + "f5178aa41aa0efff8a3409f5ca95dbfccd92c7b4298966df806ea7a0204a00f0e745f6d9f13bdf24f3df715d7b62c1600906" + + "15de1c8a956b9286764985a3b3c060963c435fb9481a5543aaf0671fc2dba6c5c2b17d1ef1d85137f14dc9bbdf3490288087" + + "324cd48341cce64fabf6a9b55d1a7bf23b2fcdff451fd85bf0c7feb0a5e884d7c5c078e413149566a12a686e6efa70ae5161" + + "a0201307692834cda336c55157fef125e67c01c1359886f94742105596b42a790404bbcda5dad6a65f115aaff5e45ef3c28b" + + "2316ff6cef07aa49a45aa58c07bf258051b13ef449ccb37a3679afd5cfb9132f70bb9d931a937897544f90c3bcc80ed012e9" + + "f6ba020b8cdc23f8c29ac092b88f0e370ff9434e4f0f359e614ae0868dc526fa41e4b7596533e8d10279b66e923ecd9f0b20" + + "0def55be2c1f6f9c72c92fb45d7e0a9ac571cb38f0a9a37bb33ea06f223fde8c7a92e8c47769e386f9799776e8f110c21df2" + + "77ef1be61b2c01ebdabddcbf53cc4b6fd9a3c445606ee77b3758162c80ad8f8137b3c6864e92db904807dcb2be9d7717dd21" + + "bf42c121d620ddfb7914f7a95c713d9e1c1b7bdb4a03d618e40cf7e9e235c0b5687e03b7ab3,publicExponent=10001}" + + override fun execute(context: BytecodeContext) { + SpoofSignatureFingerprint.result!!.mutableMethod.addInstructions( + 0, + """ + const-string v0, "$EXPECTED_SIGNATURE" + return-object v0 + """ + ) + } +} diff --git a/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/fingerprints/SpoofSignatureFingerprint.kt b/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/fingerprints/SpoofSignatureFingerprint.kt new file mode 100644 index 0000000..13c5b1f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/idaustria/detection/signature/fingerprints/SpoofSignatureFingerprint.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.idaustria.detection.signature.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object SpoofSignatureFingerprint : MethodFingerprint( + "L", + parameters = listOf("L"), + accessFlags = AccessFlags.PRIVATE.value, + customFingerprint = { methodDef, _ -> + methodDef.definingClass.endsWith("/SL2Step1Task;") && methodDef.name == "getPubKey" + } +) diff --git a/src/main/kotlin/app/revanced/util/BytecodeUtils.kt b/src/main/kotlin/app/revanced/util/BytecodeUtils.kt new file mode 100644 index 0000000..6c06688 --- /dev/null +++ b/src/main/kotlin/app/revanced/util/BytecodeUtils.kt @@ -0,0 +1,155 @@ +package app.revanced.util + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.util.proxy.mutableTypes.MutableClass +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +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.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction +import com.android.tools.smali.dexlib2.iface.reference.Reference +import com.android.tools.smali.dexlib2.util.MethodUtil + + +fun MethodFingerprint.resultOrThrow() = result ?: throw exception + +/** + * The [PatchException] of failing to resolve a [MethodFingerprint]. + * + * @return The [PatchException]. + */ +val MethodFingerprint.exception + get() = PatchException("Failed to resolve ${this.javaClass.simpleName}") + +/** + * Find the [MutableMethod] from a given [Method] in a [MutableClass]. + * + * @param method The [Method] to find. + * @return The [MutableMethod]. + */ +fun MutableClass.findMutableMethodOf(method: Method) = this.methods.first { + MethodUtil.methodSignaturesMatch(it, method) +} + +/** + * Apply a transform to all methods of the class. + * + * @param transform The transformation function. Accepts a [MutableMethod] and returns a transformed [MutableMethod]. + */ +fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod) { + val transformedMethods = methods.map { it.transform() } + methods.clear() + methods.addAll(transformedMethods) +} + +/** + * Inject a call to a method that hides a view. + * + * @param insertIndex The index to insert the call at. + * @param viewRegister The register of the view to hide. + * @param classDescriptor The descriptor of the class that contains the method. + * @param targetMethod The name of the method to call. + */ +fun MutableMethod.injectHideViewCall( + insertIndex: Int, + viewRegister: Int, + classDescriptor: String, + targetMethod: String +) = addInstruction( + insertIndex, + "invoke-static { v$viewRegister }, $classDescriptor->$targetMethod(Landroid/view/View;)V" +) + +/** + * Find the index of the first instruction with the id of the given resource name. + * + * @param resourceName the name of the resource to find the id for. + * @return the index of the first instruction with the id of the given resource name, or -1 if not found. + */ +//fun Method.findIndexForIdResource(resourceName: String): Int { +// fun getIdResourceId(resourceName: String) = ResourceMappingPatch.resourceMappings.single { +// it.type == "id" && it.name == resourceName +// }.id +// +// return indexOfFirstWideLiteralInstructionValue(getIdResourceId(resourceName)) +//} + +/** + * Find the index of the first wide literal instruction with the given value. + * + * @return the first literal instruction with the value, or -1 if not found. + */ +fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementation?.let { + it.instructions.indexOfFirst { instruction -> + (instruction as? WideLiteralInstruction)?.wideLiteral == literal + } +} ?: -1 + +/** + * Check if the method contains a literal with the given value. + * + * @return if the method contains a literal with the given value. + */ +fun Method.containsWideLiteralInstructionValue(literal: Long) = + indexOfFirstWideLiteralInstructionValue(literal) >= 0 + +/** + * Traverse the class hierarchy starting from the given root class. + * + * @param targetClass the class to start traversing the class hierarchy from. + * @param callback function that is called for every class in the hierarchy. + */ +fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) { + callback(targetClass) + this.findClass(targetClass.superclass ?: return)?.mutableClass?.let { + traverseClassHierarchy(it, callback) + } +} + +/** + * Get the [Reference] of an [Instruction] as [T]. + * + * @param T The type of [Reference] to cast to. + * @return The [Reference] as [T] or null + * if the [Instruction] is not a [ReferenceInstruction] or the [Reference] is not of type [T]. + * @see ReferenceInstruction + */ +inline fun Instruction.getReference() = (this as? ReferenceInstruction)?.reference as? T + +/** + * Get the index of the first [Instruction] that matches the predicate. + * + * @param predicate The predicate to match. + * @return The index of the first [Instruction] that matches the predicate. + */ +fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = + this.implementation!!.instructions.indexOfFirst(predicate) + + /** + * Return the resolved methods of [MethodFingerprint]s early. + */ + fun List.returnEarly(bool: Boolean = false) { + val const = if (bool) "0x1" else "0x0" + this.forEach { fingerprint -> + fingerprint.result?.let { result -> + val stringInstructions = when (result.method.returnType.first()) { + 'L' -> """ + const/4 v0, $const + return-object v0 + """ + 'V' -> "return-void" + 'I', 'Z' -> """ + const/4 v0, $const + return v0 + """ + else -> throw Exception("This case should never happen.") + } + + result.mutableMethod.addInstructions(0, stringInstructions) + } ?: throw fingerprint.exception + } + }