Skip to content

Commit

Permalink
feat: Add 'classFingerprint' (parent fingerprint)
Browse files Browse the repository at this point in the history
  • Loading branch information
LisoUseInAIKyrios committed Jan 4, 2025
1 parent 9779e50 commit 4d38837
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 17 deletions.
3 changes: 2 additions & 1 deletion api/revanced-patcher.api
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ public final class app/revanced/patcher/Fingerprint {
public final fun match (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
public final fun matchOrNull ()Lapp/revanced/patcher/Match;
public final fun matchOrNull (Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
public final fun matchOrNull (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/Match;
public final fun matchOrNull (Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/Match;
public final fun matchOrNull (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/ClassDef;)Lapp/revanced/patcher/Match;
public final fun patchException ()Lapp/revanced/patcher/patch/PatchException;
public fun toString ()Ljava/lang/String;
}
Expand All @@ -48,6 +48,7 @@ public final class app/revanced/patcher/FingerprintBuilder {
public final fun accessFlags (I)V
public final fun accessFlags ([Lcom/android/tools/smali/dexlib2/AccessFlags;)V
public final fun build ()Lapp/revanced/patcher/Fingerprint;
public final fun classFingerprint (Lapp/revanced/patcher/Fingerprint;)V
public final fun custom (Lkotlin/jvm/functions/Function2;)V
public final fun getName ()Ljava/lang/String;
public final fun instructions ([Lapp/revanced/patcher/InstructionFilter;)V
Expand Down
35 changes: 31 additions & 4 deletions src/main/kotlin/app/revanced/patcher/Fingerprint.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ internal fun parametersStartsWith(
* ```
*
* @param name Human readable name used for [toString].
* @param classFingerprint Fingerprint that matches any method in the class this fingerprint matches to.
* @param accessFlags The exact access flags using values of [AccessFlags].
* @param returnType The return type. Compared using [String.startsWith].
* @param parameters The parameters. Partial matches allowed and follow the same rules as [returnType].
Expand All @@ -67,6 +68,7 @@ internal fun parametersStartsWith(
*/
class Fingerprint internal constructor(
internal val name: String,
internal val classFingerprint: Fingerprint? = null,
internal val accessFlags: Int?,
internal val returnType: String?,
internal val parameters: List<String>?,
Expand All @@ -86,6 +88,11 @@ class Fingerprint internal constructor(

val start = if (LOG_RESOLVING_SPEED) System.currentTimeMillis() else 0

if (classFingerprint != null) {
_matchOrNull = matchOrNull(classFingerprint.match().originalClassDef)
return _matchOrNull
}

strings?.mapNotNull {
BytecodePatchContext.lookupMaps.methodsByStrings[it]
}?.minByOrNull { it.size }?.let { methodClasses ->
Expand Down Expand Up @@ -129,7 +136,7 @@ class Fingerprint internal constructor(
if (_matchOrNull != null) return _matchOrNull

for (method in classDef.methods) {
val match = matchOrNull(method, classDef)
val match = matchOrNull(classDef, method)
if (match != null) {
_matchOrNull = match
return match
Expand All @@ -151,7 +158,10 @@ class Fingerprint internal constructor(
): Match? {
if (_matchOrNull != null) return _matchOrNull

return matchOrNull(method, BytecodePatchContext.classBy { method.definingClass == it.type }!!.immutableClass)
return matchOrNull(
BytecodePatchContext.classBy { method.definingClass == it.type }!!.immutableClass,
method
)
}

/**
Expand All @@ -162,9 +172,14 @@ class Fingerprint internal constructor(
* @return The [Match] if a match was found or if the fingerprint is already matched to a method, null otherwise.
*/
fun matchOrNull(
method: Method,
classDef: ClassDef,
method: Method,
): Match? {
if (classFingerprint != null && classFingerprint.match().classDef != classDef) {
throw PatchException("Fingerprint $this declares a class fingerprint," +
"but tried to match using a different class: $classDef ")
}

if (_matchOrNull != null) return _matchOrNull

if (returnType != null && !method.returnType.startsWith(returnType)) {
Expand Down Expand Up @@ -328,7 +343,7 @@ class Fingerprint internal constructor(
fun match(
method: Method,
classDef: ClassDef,
) = matchOrNull(method, classDef) ?: throw patchException()
) = matchOrNull(classDef, method) ?: throw patchException()

/**
* The class the matching method is a member of, or null if this fingerprint did not match.
Expand Down Expand Up @@ -533,6 +548,7 @@ class Match internal constructor(
* A builder for [Fingerprint].
*
* @property name Name of the fingerprint, and usually identical to the variable name.
* @property classFingerprint Fingerprint used to find the class this fingerprint resolves to.
* @property accessFlags The exact access flags using values of [AccessFlags].
* @property returnType The return type compared using [String.startsWith].
* @property parameters The parameters of the method. Partial matches allowed and follow the same rules as [returnType].
Expand All @@ -543,13 +559,23 @@ class Match internal constructor(
* @constructor Create a new [FingerprintBuilder].
*/
class FingerprintBuilder(val name: String) {
private var classFingerprint: Fingerprint? = null
private var accessFlags: Int? = null
private var returnType: String? = null
private var parameters: List<String>? = null
private var instructionFilters: List<InstructionFilter>? = null
private var strings: List<String>? = null
private var customBlock: ((method: Method, classDef: ClassDef) -> Boolean)? = null

/**
* Sets the class (parent) fingerprint.
*
* @param classFingerprint Fingerprint that finds any other methods in the target class.
*/
fun classFingerprint(classFingerprint: Fingerprint) {
this.classFingerprint = classFingerprint
}

/**
* Set the access flags.
*
Expand Down Expand Up @@ -660,6 +686,7 @@ class FingerprintBuilder(val name: String) {

fun build() = Fingerprint(
name,
classFingerprint,
accessFlags,
returnType,
parameters,
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/app/revanced/patcher/InstructionFilter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ class MethodFilter(
// Would be nice if this also checked all super classes,
// but doing so requires iteratively checking all superclasses
// up to the root Object class since class defs are mere Strings.
if (definingClass != "this" || referenceClass != method.definingClass) {
if (!(definingClass == "this" && referenceClass == method.definingClass)) {
return false
} // else, the method call is for 'this' class.
}
Expand Down Expand Up @@ -343,7 +343,7 @@ class FieldFilter(
val definingClass = definingClass()

if (!referenceClass.endsWith(definingClass)) {
if (definingClass != "this" || referenceClass != method.definingClass) {
if (!(definingClass === "this" && referenceClass == method.definingClass)) {
return false
} // else, the method call is for 'this' class.
}
Expand Down
20 changes: 10 additions & 10 deletions src/main/kotlin/app/revanced/patcher/patch/BytecodePatchContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ object BytecodePatchContext : PatchContext<Set<PatcherResult.PatchedDexFile>>, C
init {
classes.forEach { classDef ->
classDef.methods.forEach { method ->
val methodClassPair: MethodClassPair = method to classDef
val classMethodPair: ClassMethodPair = classDef to method

// Add strings contained in the method as the key.
method.instructionsOrNull?.forEach { instruction ->
Expand All @@ -203,7 +203,7 @@ object BytecodePatchContext : PatchContext<Set<PatcherResult.PatchedDexFile>>, C
}

val string = ((instruction as ReferenceInstruction).reference as StringReference).string
methodsByStrings[string] = methodClassPair
methodsByStrings[string] = classMethodPair
}

// In the future, the class type could be added to the lookup map.
Expand All @@ -227,22 +227,22 @@ object BytecodePatchContext : PatchContext<Set<PatcherResult.PatchedDexFile>>, C
/**
* A pair of a [Method] and the [ClassDef] it is a member of.
*/
internal typealias MethodClassPair = Pair<Method, ClassDef>
internal typealias ClassMethodPair = Pair<ClassDef, Method>

/**
* A list of [MethodClassPair]s.
* A list of [ClassMethodPair]s.
*/
internal typealias MethodClassPairs = LinkedList<MethodClassPair>
internal typealias MethodClassPairs = LinkedList<ClassMethodPair>

/**
* A lookup map for [MethodClassPairs]s.
* The key is a string and the value is a list of [MethodClassPair]s.
* The key is a string and the value is a list of [ClassMethodPair]s.
*/
internal class MethodClassPairsLookupMap : MutableMap<String, MethodClassPairs> by mutableMapOf() {
/**
* Add a [MethodClassPair] associated by any key.
* If the key does not exist, a new list is created and the [MethodClassPair] is added to it.
* Add a [ClassMethodPair] associated by any key.
* If the key does not exist, a new list is created and the [ClassMethodPair] is added to it.
*/
internal operator fun set(key: String, methodClassPair: MethodClassPair) =
apply { getOrPut(key) { MethodClassPairs() }.add(methodClassPair) }
internal operator fun set(key: String, classMethodPair: ClassMethodPair) =
apply { getOrPut(key) { MethodClassPairs() }.add(classMethodPair) }
}

0 comments on commit 4d38837

Please sign in to comment.