From b8151ebccb5b27dd9e06fa63235cf9baeef1c0ee Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 14 Jun 2023 01:43:19 +0200 Subject: [PATCH 1/9] fix: only close succeeded patches --- src/main/kotlin/app/revanced/patcher/Patcher.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 06836b1..665f609 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -364,7 +364,7 @@ class Patcher(private val options: PatcherOptions) { if (stopOnError && patchResult.isError()) return@sequence } } finally { - executedPatches.values.reversed().forEach { (patch, _) -> + executedPatches.values.filter { it.success }.reversed().forEach { (patch, _) -> patch.close() } } From d5d6f85084c03ed9c776632823ca12394a716167 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 14 Jun 2023 02:14:37 +0200 Subject: [PATCH 2/9] fix: catch exceptions from closing patches --- .../kotlin/app/revanced/patcher/Patcher.kt | 47 +++++++++++++------ .../app/revanced/patcher/patch/Patch.kt | 9 +--- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 665f609..2448592 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -23,6 +23,7 @@ import lanchon.multidexlib2.MultiDexIO import org.jf.dexlib2.Opcodes import org.jf.dexlib2.iface.DexFile import org.jf.dexlib2.writer.io.MemoryDataStore +import java.io.Closeable import java.io.File import java.io.OutputStream import java.nio.file.Files @@ -350,24 +351,42 @@ class Patcher(private val options: PatcherOptions) { val executedPatches = LinkedHashMap() // first is name - try { - context.patches.forEach { patch -> - val patchResult = executePatch(patch, executedPatches) - val result = if (patchResult.isSuccess()) { - Result.success(patchResult.success()!!) - } else { - Result.failure(patchResult.error()!!) - } + context.patches.forEach { patch -> + val patchResult = executePatch(patch, executedPatches) - yield(patch.patchName to result) - if (stopOnError && patchResult.isError()) return@sequence - } - } finally { - executedPatches.values.filter { it.success }.reversed().forEach { (patch, _) -> - patch.close() + val result = if (patchResult.isSuccess()) { + Result.success(patchResult.success()!!) + } else { + Result.failure(patchResult.error()!!) } + + // TODO: This prints before the patch really finishes in case it is a Closeable + // because the Closeable is closed after all patches are executed. + yield(patch.patchName to result) + + if (stopOnError && patchResult.isError()) return@sequence } + + executedPatches.values + .filter(ExecutedPatch::success) + .map(ExecutedPatch::patchInstance) + .filterIsInstance(Closeable::class.java) + .asReversed().forEach { + try { + it.close() + } catch (exception: Exception) { + val patchName = (it as Patch).javaClass.patchName + + logger.error("Failed to close '$patchName': ${exception.stackTraceToString()}") + + yield(patchName to Result.failure(exception)) + + // This is not failsafe. If a patch throws an exception while closing, + // the other patches that depend on it may fail. + if (stopOnError) return@sequence + } + } } } diff --git a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt index 8a194ed..3f982b8 100644 --- a/src/main/kotlin/app/revanced/patcher/patch/Patch.kt +++ b/src/main/kotlin/app/revanced/patcher/patch/Patch.kt @@ -12,7 +12,7 @@ import java.io.Closeable * If it implements [Closeable], it will be closed after all patches have been executed. * Closing will be done in reverse execution order. */ -sealed interface Patch : Closeable { +sealed interface Patch { /** * The main function of the [Patch] which the patcher will call. * @@ -20,13 +20,6 @@ sealed interface Patch : Closeable { * @return The result of executing the patch. */ fun execute(context: @UnsafeVariance T): PatchResult - - /** - * The closing function for this patch. - * - * This can be treated like popping the patch from the current patch stack. - */ - override fun close() {} } /** From f3c9e28a629c246b85651af2edc5031238b7b099 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 14 Jun 2023 00:16:26 +0000 Subject: [PATCH 3/9] chore(release): 11.0.2-dev.1 [skip ci] ## [11.0.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.1...v11.0.2-dev.1) (2023-06-14) ### Bug Fixes * catch exceptions from closing patches ([d5d6f85](https://github.com/revanced/revanced-patcher/commit/d5d6f85084c03ed9c776632823ca12394a716167)) * only close succeeded patches ([b8151eb](https://github.com/revanced/revanced-patcher/commit/b8151ebccb5b27dd9e06fa63235cf9baeef1c0ee)) --- CHANGELOG.md | 8 ++++++++ gradle.properties | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 579cc9f..d36f200 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [11.0.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.1...v11.0.2-dev.1) (2023-06-14) + + +### Bug Fixes + +* catch exceptions from closing patches ([d5d6f85](https://github.com/revanced/revanced-patcher/commit/d5d6f85084c03ed9c776632823ca12394a716167)) +* only close succeeded patches ([b8151eb](https://github.com/revanced/revanced-patcher/commit/b8151ebccb5b27dd9e06fa63235cf9baeef1c0ee)) + ## [11.0.1](https://github.com/revanced/revanced-patcher/compare/v11.0.0...v11.0.1) (2023-06-12) diff --git a/gradle.properties b/gradle.properties index 5a5dbc5..fdc87f6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ kotlin.code.style = official -version = 11.0.1 +version = 11.0.2-dev.1 From 6e1b6479b677657c226693e9cc6b63f4ef2ee060 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Sun, 18 Jun 2023 16:39:49 +0200 Subject: [PATCH 4/9] fix: use `versionCode` if `versionName` is unavailable --- src/main/kotlin/app/revanced/patcher/Patcher.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 2448592..388190c 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -249,6 +249,7 @@ class Patcher(private val options: PatcherOptions) { axmlParser, AndrolibResources().resXmlSerializer ).decodeManifest( extInputFile.directory.getFileInput("AndroidManifest.xml"), + // Older Android versions do not support OutputStream.nullOutputStream() object : OutputStream() { override fun write(b: Int) { // do nothing @@ -261,7 +262,7 @@ class Patcher(private val options: PatcherOptions) { // read of the resourceTable which is created by reading the manifest file context.packageMetadata.let { metadata -> metadata.packageName = resourceTable.currentResPackage.name - metadata.packageVersion = resourceTable.versionInfo.versionName + metadata.packageVersion = resourceTable.versionInfo.versionName ?: resourceTable.versionInfo.versionCode metadata.metaInfo.versionInfo = resourceTable.versionInfo metadata.metaInfo.sdkInfo = resourceTable.sdkInfo } From 5e681ed381bc0932ef0a8ce610caed7f7d098ae7 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 18 Jun 2023 14:41:34 +0000 Subject: [PATCH 5/9] chore(release): 11.0.2-dev.2 [skip ci] ## [11.0.2-dev.2](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.1...v11.0.2-dev.2) (2023-06-18) ### Bug Fixes * use `versionCode` if `versionName` is unavailable ([6e1b647](https://github.com/revanced/revanced-patcher/commit/6e1b6479b677657c226693e9cc6b63f4ef2ee060)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d36f200..e859c44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [11.0.2-dev.2](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.1...v11.0.2-dev.2) (2023-06-18) + + +### Bug Fixes + +* use `versionCode` if `versionName` is unavailable ([6e1b647](https://github.com/revanced/revanced-patcher/commit/6e1b6479b677657c226693e9cc6b63f4ef2ee060)) + ## [11.0.2-dev.1](https://github.com/revanced/revanced-patcher/compare/v11.0.1...v11.0.2-dev.1) (2023-06-14) diff --git a/gradle.properties b/gradle.properties index fdc87f6..a264c39 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ kotlin.code.style = official -version = 11.0.2-dev.1 +version = 11.0.2-dev.2 From d718134ab26423e02708e01eba711737f9260ba0 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Tue, 27 Jun 2023 06:06:51 +0400 Subject: [PATCH 6/9] perf: resolve fingerprints using method maps (#185) Co-authored-by: oSumAtrIX --- .../kotlin/app/revanced/patcher/Patcher.kt | 15 +- .../method/impl/MethodFingerprint.kt | 302 ++++++++++++++---- 2 files changed, 254 insertions(+), 63 deletions(-) diff --git a/src/main/kotlin/app/revanced/patcher/Patcher.kt b/src/main/kotlin/app/revanced/patcher/Patcher.kt index 388190c..c774235 100644 --- a/src/main/kotlin/app/revanced/patcher/Patcher.kt +++ b/src/main/kotlin/app/revanced/patcher/Patcher.kt @@ -4,7 +4,8 @@ import app.revanced.patcher.data.Context import app.revanced.patcher.extensions.PatchExtensions.dependencies import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.extensions.PatchExtensions.requiresIntegrations -import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolveUsingLookupMap import app.revanced.patcher.patch.* import app.revanced.patcher.util.VersionReader import brut.androlib.Androlib @@ -322,10 +323,7 @@ class Patcher(private val options: PatcherOptions) { context.resourceContext } else { context.bytecodeContext.also { context -> - (patchInstance as BytecodePatch).fingerprints?.resolve( - context, - context.classes.classes - ) + (patchInstance as BytecodePatch).fingerprints?.resolveUsingLookupMap(context) } } @@ -345,14 +343,17 @@ class Patcher(private val options: PatcherOptions) { return sequence { if (mergeIntegrations) context.integrations.merge(logger) + logger.trace("Initialize lookup maps for method MethodFingerprint resolution") + + MethodFingerprint.initializeFingerprintResolutionLookupMaps(context.bytecodeContext) + // prevent from decoding the manifest twice if it is not needed if (resourceDecodingMode == ResourceDecodingMode.FULL) decodeResources(ResourceDecodingMode.FULL) - logger.trace("Executing all patches") + logger.info("Executing patches") val executedPatches = LinkedHashMap() // first is name - context.patches.forEach { patch -> val patchResult = executePatch(patch, executedPatches) diff --git a/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt b/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt index 1a7bbf3..d749baa 100644 --- a/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt +++ b/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt @@ -4,7 +4,9 @@ import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod import app.revanced.patcher.fingerprint.Fingerprint import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod +import app.revanced.patcher.patch.PatchResultError import app.revanced.patcher.util.proxy.ClassProxy +import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.Opcode import org.jf.dexlib2.iface.ClassDef import org.jf.dexlib2.iface.Method @@ -12,19 +14,21 @@ import org.jf.dexlib2.iface.instruction.Instruction import org.jf.dexlib2.iface.instruction.ReferenceInstruction import org.jf.dexlib2.iface.reference.StringReference import org.jf.dexlib2.util.MethodUtil +import java.util.* private typealias StringMatch = MethodFingerprintResult.MethodFingerprintScanResult.StringsScanResult.StringMatch private typealias StringsScanResult = MethodFingerprintResult.MethodFingerprintScanResult.StringsScanResult +private typealias MethodClassPair = Pair /** - * Represents the [MethodFingerprint] for a method. - * @param returnType The return type of the method. - * @param accessFlags The access flags of the method. - * @param parameters The parameters of the method. - * @param opcodes The list of opcodes of the method. - * @param strings A list of strings which a method contains. + * A fingerprint to resolve methods. + * + * @param returnType The method's return type compared using [String.startsWith]. + * @param accessFlags The method's exact access flags using values of [AccessFlags]. + * @param parameters The parameters of the method. Partial matches allowed and follow the same rules as [returnType]. + * @param opcodes An opcode pattern of the method's instructions. Wildcard or unknown opcodes can be specified by `null`. + * @param strings A list of the method's strings compared each using [String.contains]. * @param customFingerprint A custom condition for this fingerprint. - * A `null` opcode is equals to an unknown opcode. */ abstract class MethodFingerprint( internal val returnType: String? = null, @@ -40,6 +44,192 @@ abstract class MethodFingerprint( var result: MethodFingerprintResult? = null companion object { + /** + * A list of methods and the class they were found in. + */ + private val methods = mutableListOf() + + /** + * Lookup map for methods keyed to the methods access flags, return type and parameter. + */ + private val methodSignatureLookupMap = mutableMapOf>() + + /** + * Lookup map for methods keyed to the strings contained in the method. + */ + private val methodStringsLookupMap = mutableMapOf>() + + /** + * Appends a string based on the parameter reference types of this method. + */ + private fun StringBuilder.appendParameters(parameters: Iterable) { + // Maximum parameters to use in the signature key. + // Some apps have methods with an incredible number of parameters (over 100 parameters have been seen). + // To keep the signature map from becoming needlessly bloated, + // group together in the same map entry all methods with the same access/return and 5 or more parameters. + // The value of 5 was chosen based on local performance testing and is not set in stone. + val maxSignatureParameters = 5 + // Must append a unique value before the parameters to distinguish this key includes the parameters. + // If this is not appended, then methods with no parameters + // will collide with different keys that specify access/return but omit the parameters. + append("p:") + parameters.forEachIndexed { index, parameter -> + if (index >= maxSignatureParameters) return + append(parameter.first()) + } + } + + /** + * Initializes lookup maps for [MethodFingerprint] resolution + * using attributes of methods such as the method signature or strings. + * + * @param context The [BytecodeContext] containing the classes to initialize the lookup maps with. + */ + internal fun initializeFingerprintResolutionLookupMaps(context: BytecodeContext) { + fun MutableMap>.add( + key: String, + methodClassPair: MethodClassPair + ) { + var methodClassPairs = this[key] + + methodClassPairs ?: run { + methodClassPairs = LinkedList().also { this[key] = it } + } + + methodClassPairs!!.add(methodClassPair) + } + + if (methods.isNotEmpty()) throw PatchResultError("Map already initialized") + + context.classes.classes.forEach { classDef -> + classDef.methods.forEach { method -> + val methodClassPair = method to classDef + + // For fingerprints with no access or return type specified. + methods += methodClassPair + + val accessFlagsReturnKey = method.accessFlags.toString() + method.returnType.first() + + // Add as the key. + methodSignatureLookupMap.add(accessFlagsReturnKey, methodClassPair) + + // Add [parameters] as the key. + methodSignatureLookupMap.add( + buildString { + append(accessFlagsReturnKey) + appendParameters(method.parameterTypes) + }, + methodClassPair + ) + + // Add strings contained in the method as the key. + method.implementation?.instructions?.forEach instructions@{ instruction -> + if (instruction.opcode != Opcode.CONST_STRING && instruction.opcode != Opcode.CONST_STRING_JUMBO) + return@instructions + + val string = ((instruction as ReferenceInstruction).reference as StringReference).string + + methodStringsLookupMap.add(string, methodClassPair) + } + + // In the future, the class type could be added to the lookup map. + // This would require MethodFingerprint to be changed to include the class type. + } + } + } + + /** + * Resolve a list of [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps]. + * + * [MethodFingerprint] resolution is fast, but if many are present they can consume a noticeable + * amount of time because they are resolved in sequence. + * + * For apps with many fingerprints, resolving performance can be improved by: + * - Slowest: Specify [opcodes] and nothing else. + * - Fast: Specify [accessFlags], [returnType]. + * - Faster: Specify [accessFlags], [returnType] and [parameters]. + * - Fastest: Specify [strings], with at least one string being an exact (non-partial) match. + */ + internal fun Iterable.resolveUsingLookupMap(context: BytecodeContext) { + if (methods.isEmpty()) throw PatchResultError("lookup map not initialized") + + for (fingerprint in this) { + fingerprint.resolveUsingLookupMap(context) + } + } + + /** + * Resolve a [MethodFingerprint] using the lookup map built by [initializeFingerprintResolutionLookupMaps]. + * + * [MethodFingerprint] resolution is fast, but if many are present they can consume a noticeable + * amount of time because they are resolved in sequence. + * + * For apps with many fingerprints, resolving performance can be improved by: + * - Slowest: Specify [opcodes] and nothing else. + * - Fast: Specify [accessFlags], [returnType]. + * - Faster: Specify [accessFlags], [returnType] and [parameters]. + * - Fastest: Specify [strings], with at least one string being an exact (non-partial) match. + */ + internal fun MethodFingerprint.resolveUsingLookupMap(context: BytecodeContext): Boolean { + /** + * Lookup [MethodClassPair]s that match the methods strings present in a [MethodFingerprint]. + * + * @return A list of [MethodClassPair]s that match the methods strings present in a [MethodFingerprint]. + */ + fun MethodFingerprint.methodStringsLookup(): List? { + strings?.forEach { + val methods = methodStringsLookupMap[it] + if (methods != null) return methods + } + return null + } + + /** + * Lookup [MethodClassPair]s that match the method signature present in a [MethodFingerprint]. + * + * @return A list of [MethodClassPair]s that match the method signature present in a [MethodFingerprint]. + */ + fun MethodFingerprint.methodSignatureLookup(): List { + if (accessFlags == null) return methods + + var returnTypeValue = returnType + if (returnTypeValue == null) { + if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) { + // Constructors always have void return type + returnTypeValue = "V" + } else { + return methods + } + } + + val key = buildString { + append(accessFlags) + append(returnTypeValue.first()) + if (parameters != null) appendParameters(parameters) + } + return methodSignatureLookupMap[key]!! + } + + /** + * Resolve a [MethodFingerprint] using a list of [MethodClassPair]. + * + * @return True if the resolution was successful, false otherwise. + */ + fun MethodFingerprint.resolveUsingMethodClassPair(classMethods: Iterable): Boolean { + classMethods.forEach { classAndMethod -> + if (resolve(context, classAndMethod.first, classAndMethod.second)) return true + } + return false + } + + val methodsWithSameStrings = methodStringsLookup() + if (methodsWithSameStrings != null) if (resolveUsingMethodClassPair(methodsWithSameStrings)) return true + + // No strings declared or none matched (partial matches are allowed). + // Use signature matching. + return resolveUsingMethodClassPair(methodSignatureLookup()) + } + /** * Resolve a list of [MethodFingerprint] against a list of [ClassDef]. * @@ -48,10 +238,10 @@ abstract class MethodFingerprint( * @return True if the resolution was successful, false otherwise. */ fun Iterable.resolve(context: BytecodeContext, classes: Iterable) { - for (fingerprint in this) // For each fingerprint - classes@ for (classDef in classes) // search through all classes for the fingerprint + for (fingerprint in this) // For each fingerprint... + classes@ for (classDef in classes) // ...search through all classes for the MethodFingerprint if (fingerprint.resolve(context, classDef)) - break@classes // if the resolution succeeded, continue with the next fingerprint + break@classes // ...if the resolution succeeded, continue with the next MethodFingerprint. } /** @@ -144,6 +334,52 @@ abstract class MethodFingerprint( val patternScanResult = if (methodFingerprint.opcodes != null) { method.implementation?.instructions ?: return false + fun Method.patternScan( + fingerprint: MethodFingerprint + ): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? { + val instructions = this.implementation!!.instructions + val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyPatternScanMethod?.threshold ?: 0 + + val pattern = fingerprint.opcodes!! + val instructionLength = instructions.count() + val patternLength = pattern.count() + + for (index in 0 until instructionLength) { + var patternIndex = 0 + var threshold = fingerprintFuzzyPatternScanThreshold + + while (index + patternIndex < instructionLength) { + val originalOpcode = instructions.elementAt(index + patternIndex).opcode + val patternOpcode = pattern.elementAt(patternIndex) + + if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) { + // reaching maximum threshold (0) means, + // the pattern does not match to the current instructions + if (threshold-- == 0) break + } + + if (patternIndex < patternLength - 1) { + // if the entire pattern has not been scanned yet + // continue the scan + patternIndex++ + continue + } + // the pattern is valid, generate warnings if fuzzyPatternScanMethod is FuzzyPatternScanMethod + val result = + MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult( + index, + index + patternIndex + ) + if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result + result.warnings = result.createWarnings(pattern, instructions) + + return result + } + } + + return null + } + method.patternScan(methodFingerprint) ?: return false } else null @@ -160,52 +396,6 @@ abstract class MethodFingerprint( return true } - private fun Method.patternScan( - fingerprint: MethodFingerprint - ): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? { - val instructions = this.implementation!!.instructions - val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyPatternScanMethod?.threshold ?: 0 - - val pattern = fingerprint.opcodes!! - val instructionLength = instructions.count() - val patternLength = pattern.count() - - for (index in 0 until instructionLength) { - var patternIndex = 0 - var threshold = fingerprintFuzzyPatternScanThreshold - - while (index + patternIndex < instructionLength) { - val originalOpcode = instructions.elementAt(index + patternIndex).opcode - val patternOpcode = pattern.elementAt(patternIndex) - - if (patternOpcode != null && patternOpcode.ordinal != originalOpcode.ordinal) { - // reaching maximum threshold (0) means, - // the pattern does not match to the current instructions - if (threshold-- == 0) break - } - - if (patternIndex < patternLength - 1) { - // if the entire pattern has not been scanned yet - // continue the scan - patternIndex++ - continue - } - // the pattern is valid, generate warnings if fuzzyPatternScanMethod is FuzzyPatternScanMethod - val result = - MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult( - index, - index + patternIndex - ) - if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result - result.warnings = result.createWarnings(pattern, instructions) - - return result - } - } - - return null - } - private fun MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult.createWarnings( pattern: Iterable, instructions: Iterable ) = buildList { From b615ed6aab42cfa8a5dffd7bb396a66e9f8ac597 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 27 Jun 2023 02:08:29 +0000 Subject: [PATCH 7/9] chore(release): 11.0.2-dev.3 [skip ci] ## [11.0.2-dev.3](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.2...v11.0.2-dev.3) (2023-06-27) ### Performance Improvements * resolve fingerprints using method maps ([#185](https://github.com/revanced/revanced-patcher/issues/185)) ([d718134](https://github.com/revanced/revanced-patcher/commit/d718134ab26423e02708e01eba711737f9260ba0)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e859c44..6846ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [11.0.2-dev.3](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.2...v11.0.2-dev.3) (2023-06-27) + + +### Performance Improvements + +* resolve fingerprints using method maps ([#185](https://github.com/revanced/revanced-patcher/issues/185)) ([d718134](https://github.com/revanced/revanced-patcher/commit/d718134ab26423e02708e01eba711737f9260ba0)) + ## [11.0.2-dev.2](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.1...v11.0.2-dev.2) (2023-06-18) diff --git a/gradle.properties b/gradle.properties index a264c39..1145e3c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ kotlin.code.style = official -version = 11.0.2-dev.2 +version = 11.0.2-dev.3 From 519359a9eb0e9dfa390c5016e9fe4a7490b8ab18 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Tue, 27 Jun 2023 04:07:24 +0200 Subject: [PATCH 8/9] fix: do not load annotations as patches --- .../patcher/util/patch/PatchBundle.kt | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/app/revanced/patcher/util/patch/PatchBundle.kt b/src/main/kotlin/app/revanced/patcher/util/patch/PatchBundle.kt index 97f165b..f334486 100644 --- a/src/main/kotlin/app/revanced/patcher/util/patch/PatchBundle.kt +++ b/src/main/kotlin/app/revanced/patcher/util/patch/PatchBundle.kt @@ -3,6 +3,7 @@ package app.revanced.patcher.util.patch import app.revanced.patcher.data.Context +import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.patch.Patch import org.jf.dexlib2.DexFileFactory @@ -19,7 +20,13 @@ sealed class PatchBundle(path: String) : File(path) { internal fun loadPatches(classLoader: ClassLoader, classNames: Iterator) = buildList { classNames.forEach { className -> val clazz = classLoader.loadClass(className) - if (!clazz.isAnnotationPresent(app.revanced.patcher.patch.annotations.Patch::class.java)) return@forEach + + // Annotations can not Patch. + if (clazz.isAnnotation) return@forEach + + clazz.findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class) + ?: return@forEach + @Suppress("UNCHECKED_CAST") this.add(clazz as Class>) } }.sortedBy { it.patchName } @@ -43,11 +50,9 @@ sealed class PatchBundle(path: String) : File(path) { ), JarFile(this) .stream() - .filter {it.name.endsWith(".class") && !it.name.contains("$")} - .map({it -> it.realName.replace('/', '.').replace(".class", "")}).iterator() + .filter { it.name.endsWith(".class") && !it.name.contains("$") } + .map { it.realName.replace('/', '.').replace(".class", "") }.iterator() ) - - } /** @@ -63,8 +68,9 @@ sealed class PatchBundle(path: String) : File(path) { * Patches will be loaded to the provided [dexClassLoader]. */ fun loadPatches() = loadPatches(dexClassLoader, - DexFileFactory.loadDexFile(path, null).classes.asSequence().map({ classDef -> + DexFileFactory.loadDexFile(path, null).classes.asSequence().map { classDef -> classDef.type.substring(1, classDef.length - 1).replace('/', '.') - }).iterator()) + }.iterator() + ) } } \ No newline at end of file From 0a8ccba33eab975d754f0819c9c0f5215700cad6 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 27 Jun 2023 02:11:33 +0000 Subject: [PATCH 9/9] chore(release): 11.0.2-dev.4 [skip ci] ## [11.0.2-dev.4](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.3...v11.0.2-dev.4) (2023-06-27) ### Bug Fixes * do not load annotations as patches ([519359a](https://github.com/revanced/revanced-patcher/commit/519359a9eb0e9dfa390c5016e9fe4a7490b8ab18)) --- CHANGELOG.md | 7 +++++++ gradle.properties | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6846ffe..4e963aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [11.0.2-dev.4](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.3...v11.0.2-dev.4) (2023-06-27) + + +### Bug Fixes + +* do not load annotations as patches ([519359a](https://github.com/revanced/revanced-patcher/commit/519359a9eb0e9dfa390c5016e9fe4a7490b8ab18)) + ## [11.0.2-dev.3](https://github.com/revanced/revanced-patcher/compare/v11.0.2-dev.2...v11.0.2-dev.3) (2023-06-27) diff --git a/gradle.properties b/gradle.properties index 1145e3c..2851995 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ kotlin.code.style = official -version = 11.0.2-dev.3 +version = 11.0.2-dev.4