mirror of
https://github.com/revanced/revanced-patcher.git
synced 2025-05-21 20:57:06 +02:00
refactor: migrate from Signature
to Fingerprint
BREAKING CHANGE: Not backwards compatible, since a lot of classes where renamed.
This commit is contained in:
parent
c828fa2a27
commit
efa8ea1445
@ -21,7 +21,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api("app.revanced:multidexlib2:2.5.2.r2")
|
||||
implementation("app.revanced:multidexlib2:2.5.2.r2")
|
||||
|
||||
implementation("xpp3:xpp3:1.1.4c")
|
||||
implementation("org.smali:smali:2.5.2")
|
||||
@ -45,6 +45,7 @@ java {
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
if (System.getenv("GITHUB_ACTOR") != null)
|
||||
maven {
|
||||
name = "GitHubPackages"
|
||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
||||
@ -53,6 +54,8 @@ publishing {
|
||||
password = System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
else
|
||||
mavenLocal()
|
||||
}
|
||||
publications {
|
||||
register<MavenPublication>("gpr") {
|
||||
|
@ -1,19 +1,18 @@
|
||||
package app.revanced.patcher
|
||||
|
||||
import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.data.PackageMetadata
|
||||
import app.revanced.patcher.data.PatcherData
|
||||
import app.revanced.patcher.data.base.Data
|
||||
import app.revanced.patcher.data.implementation.findIndexed
|
||||
import app.revanced.patcher.data.impl.findIndexed
|
||||
import app.revanced.patcher.extensions.PatchExtensions.dependencies
|
||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||
import app.revanced.patcher.extensions.nullOutputStream
|
||||
import app.revanced.patcher.patch.base.Patch
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.ResourcePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultError
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||
import app.revanced.patcher.signature.implementation.method.resolver.MethodSignatureResolver
|
||||
import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils.resolve
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.PatchResultError
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||
import app.revanced.patcher.util.ListBackedSet
|
||||
import brut.androlib.Androlib
|
||||
import brut.androlib.meta.UsesFramework
|
||||
@ -281,10 +280,9 @@ class Patcher(private val options: PatcherOptions) {
|
||||
val data = if (isResourcePatch) {
|
||||
data.resourceData
|
||||
} else {
|
||||
MethodSignatureResolver(
|
||||
data.bytecodeData.classes.internalClasses, (patchInstance as BytecodePatch).signatures
|
||||
).resolve(data)
|
||||
data.bytecodeData
|
||||
val bytecodeData = data.bytecodeData
|
||||
(patchInstance as BytecodePatch).fingerprints.resolve(bytecodeData, bytecodeData.classes.internalClasses)
|
||||
bytecodeData
|
||||
}
|
||||
|
||||
logger.trace("Executing patch $patchName of type: ${if (isResourcePatch) "resource" else "bytecode"}")
|
||||
|
@ -1,9 +1,10 @@
|
||||
package app.revanced.patcher.data
|
||||
package app.revanced.patcher
|
||||
|
||||
import app.revanced.patcher.data.base.Data
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.data.implementation.ResourceData
|
||||
import app.revanced.patcher.patch.base.Patch
|
||||
import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.data.PackageMetadata
|
||||
import app.revanced.patcher.data.impl.BytecodeData
|
||||
import app.revanced.patcher.data.impl.ResourceData
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import org.jf.dexlib2.iface.ClassDef
|
||||
import java.io.File
|
||||
|
@ -1,7 +1,7 @@
|
||||
package app.revanced.patcher
|
||||
|
||||
import app.revanced.patcher.logging.impl.NopLogger
|
||||
import app.revanced.patcher.logging.Logger
|
||||
import app.revanced.patcher.logging.impl.NopLogger
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
|
@ -1,11 +1,11 @@
|
||||
package app.revanced.patcher.annotation
|
||||
|
||||
import app.revanced.patcher.patch.base.Patch
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patcher.patch.Patch
|
||||
|
||||
/**
|
||||
* Annotation to constrain a [Patch] or [MethodSignature] to compatible packages.
|
||||
* @param compatiblePackages A list of packages a [Patch] or [MethodSignature] is compatible with.
|
||||
* Annotation to constrain a [Patch] or [MethodFingerprint] to compatible packages.
|
||||
* @param compatiblePackages A list of packages a [Patch] or [MethodFingerprint] is compatible with.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@ -17,7 +17,7 @@ annotation class Compatibility(
|
||||
/**
|
||||
* Annotation to represent packages a patch can be compatible with.
|
||||
* @param name The package identifier name.
|
||||
* @param versions The versions of the package the [Patch] or [MethodSignature]is compatible with.
|
||||
* @param versions The versions of the package the [Patch] or [MethodFingerprint]is compatible with.
|
||||
*/
|
||||
@Target()
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
|
@ -1,11 +1,11 @@
|
||||
package app.revanced.patcher.annotation
|
||||
|
||||
import app.revanced.patcher.patch.base.Patch
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patcher.patch.Patch
|
||||
|
||||
/**
|
||||
* Annotation to name a [Patch] or [MethodSignature].
|
||||
* @param name A suggestive name for the [Patch] or [MethodSignature].
|
||||
* Annotation to name a [Patch] or [MethodFingerprint].
|
||||
* @param name A suggestive name for the [Patch] or [MethodFingerprint].
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@ -15,8 +15,8 @@ annotation class Name(
|
||||
)
|
||||
|
||||
/**
|
||||
* Annotation to describe a [Patch] or [MethodSignature].
|
||||
* @param description A description for the [Patch] or [MethodSignature].
|
||||
* Annotation to describe a [Patch] or [MethodFingerprint].
|
||||
* @param description A description for the [Patch] or [MethodFingerprint].
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@ -27,8 +27,8 @@ annotation class Description(
|
||||
|
||||
|
||||
/**
|
||||
* Annotation to version a [Patch] or [MethodSignature].
|
||||
* @param version The version of a [Patch] or [MethodSignature].
|
||||
* Annotation to version a [Patch] or [MethodFingerprint].
|
||||
* @param version The version of a [Patch] or [MethodFingerprint].
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
|
9
src/main/kotlin/app/revanced/patcher/data/Data.kt
Normal file
9
src/main/kotlin/app/revanced/patcher/data/Data.kt
Normal file
@ -0,0 +1,9 @@
|
||||
package app.revanced.patcher.data
|
||||
|
||||
import app.revanced.patcher.data.impl.BytecodeData
|
||||
import app.revanced.patcher.data.impl.ResourceData
|
||||
|
||||
/**
|
||||
* Constraint interface for [BytecodeData] and [ResourceData]
|
||||
*/
|
||||
interface Data
|
@ -1,9 +0,0 @@
|
||||
package app.revanced.patcher.data.base
|
||||
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.data.implementation.ResourceData
|
||||
|
||||
/**
|
||||
* Constraint interface for [BytecodeData] and [ResourceData]
|
||||
*/
|
||||
interface Data
|
@ -1,6 +1,6 @@
|
||||
package app.revanced.patcher.data.implementation
|
||||
package app.revanced.patcher.data.impl
|
||||
|
||||
import app.revanced.patcher.data.base.Data
|
||||
import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.util.ProxyBackedClassList
|
||||
import app.revanced.patcher.util.method.MethodWalker
|
||||
import org.jf.dexlib2.iface.ClassDef
|
@ -1,10 +1,9 @@
|
||||
package app.revanced.patcher.data.implementation
|
||||
package app.revanced.patcher.data.impl
|
||||
|
||||
import app.revanced.patcher.data.base.Data
|
||||
import app.revanced.patcher.data.Data
|
||||
import org.w3c.dom.Document
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import javax.xml.XMLConstants
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.transform.TransformerFactory
|
||||
import javax.xml.transform.dom.DOMSource
|
@ -4,11 +4,9 @@ import app.revanced.patcher.annotation.Compatibility
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.base.Data
|
||||
import app.revanced.patcher.patch.base.Patch
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||
import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
@ -47,13 +45,13 @@ object PatchExtensions {
|
||||
val Class<out Patch<Data>>.compatiblePackages get() = recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
||||
}
|
||||
|
||||
object MethodSignatureExtensions {
|
||||
val MethodSignature.name: String
|
||||
object MethodFingerprintExtensions {
|
||||
val MethodFingerprint.name: String
|
||||
get() = javaClass.recursiveAnnotation(Name::class)?.name ?: this.javaClass.simpleName
|
||||
val MethodSignature.version get() = javaClass.recursiveAnnotation(Version::class)?.version ?: "0.0.1"
|
||||
val MethodSignature.description get() = javaClass.recursiveAnnotation(Description::class)?.description
|
||||
val MethodSignature.compatiblePackages get() = javaClass.recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
||||
val MethodSignature.matchingMethod get() = javaClass.recursiveAnnotation(MatchingMethod::class)
|
||||
val MethodSignature.fuzzyPatternScanMethod get() = javaClass.recursiveAnnotation(FuzzyPatternScanMethod::class)
|
||||
val MethodSignature.fuzzyThreshold get() = fuzzyPatternScanMethod?.threshold ?: 0
|
||||
val MethodFingerprint.version get() = javaClass.recursiveAnnotation(Version::class)?.version ?: "0.0.1"
|
||||
val MethodFingerprint.description get() = javaClass.recursiveAnnotation(Description::class)?.description
|
||||
val MethodFingerprint.compatiblePackages get() = javaClass.recursiveAnnotation(Compatibility::class)?.compatiblePackages
|
||||
val MethodFingerprint.matchingMethod get() = javaClass.recursiveAnnotation(app.revanced.patcher.fingerprint.method.annotation.MatchingMethod::class)
|
||||
val MethodFingerprint.fuzzyPatternScanMethod get() = javaClass.recursiveAnnotation(app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod::class)
|
||||
val MethodFingerprint.fuzzyScanThreshold get() = fuzzyPatternScanMethod?.threshold ?: 0
|
||||
}
|
@ -77,6 +77,13 @@ internal fun Method.clone(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a smali instruction to the method.
|
||||
* @param instruction The smali instruction to add.
|
||||
*/
|
||||
fun MutableMethod.addInstruction(instruction: String) =
|
||||
this.implementation!!.addInstruction(instruction.toInstruction(this))
|
||||
|
||||
/**
|
||||
* Add a smali instruction to the method.
|
||||
* @param index The index to insert the instruction at.
|
||||
@ -152,3 +159,20 @@ internal val nullOutputStream: OutputStream =
|
||||
object : OutputStream() {
|
||||
override fun write(b: Int) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be used to parse a list of parameters represented by their first letter,
|
||||
* or in the case of arrays prefixed with an unspecified amount of '[' character.
|
||||
*/
|
||||
internal fun String.parseParameters(): List<String> {
|
||||
val parameters = mutableListOf<String>()
|
||||
var parameter = ""
|
||||
for (char in this.toCharArray()) {
|
||||
parameter += char
|
||||
if (char == '[') continue
|
||||
|
||||
parameters.add(parameter)
|
||||
parameter = ""
|
||||
}
|
||||
return parameters
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package app.revanced.patcher.fingerprint
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
|
||||
/**
|
||||
* A ReVanced fingerprint.
|
||||
* Can be a [MethodFingerprint].
|
||||
*/
|
||||
interface Fingerprint
|
@ -1,11 +1,11 @@
|
||||
package app.revanced.patcher.signature.implementation.method.annotation
|
||||
package app.revanced.patcher.fingerprint.method.annotation
|
||||
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
|
||||
/**
|
||||
* Annotations for a method which matches to a [MethodSignature].
|
||||
* Annotations for a method which matches to a [MethodFingerprint].
|
||||
* @param definingClass The defining class name of the method.
|
||||
* @param name A suggestive name for the method which the [MethodSignature] was created for.
|
||||
* @param name A suggestive name for the method which the [MethodFingerprint] was created for.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@ -15,7 +15,7 @@ annotation class MatchingMethod(
|
||||
)
|
||||
|
||||
/**
|
||||
* Annotations to scan a pattern [MethodSignature] with fuzzy algorithm.
|
||||
* Annotations to scan a pattern [MethodFingerprint] with fuzzy algorithm.
|
||||
* @param threshold if [threshold] or more of the opcodes do not match, skip.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@ -25,7 +25,7 @@ annotation class FuzzyPatternScanMethod(
|
||||
)
|
||||
|
||||
/**
|
||||
* Annotations to scan a pattern [MethodSignature] directly.
|
||||
* Annotations to scan a pattern [MethodFingerprint] directly.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
@ -0,0 +1,99 @@
|
||||
package app.revanced.patcher.fingerprint.method.impl
|
||||
|
||||
import app.revanced.patcher.data.impl.BytecodeData
|
||||
import app.revanced.patcher.data.impl.MethodNotFoundException
|
||||
import app.revanced.patcher.data.impl.proxy
|
||||
import app.revanced.patcher.extensions.MethodFingerprintExtensions.name
|
||||
import app.revanced.patcher.extensions.softCompareTo
|
||||
import app.revanced.patcher.fingerprint.Fingerprint
|
||||
import app.revanced.patcher.fingerprint.method.utils.MethodFingerprintUtils
|
||||
import app.revanced.patcher.util.proxy.ClassProxy
|
||||
import org.jf.dexlib2.Opcode
|
||||
import org.jf.dexlib2.iface.ClassDef
|
||||
import org.jf.dexlib2.iface.Method
|
||||
|
||||
/**
|
||||
* Represents the [MethodFingerprint] for a method.
|
||||
* @param returnType The return type of the method.
|
||||
* @param access 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.
|
||||
* @param customFingerprint A custom condition for this fingerprint.
|
||||
* A `null` opcode is equals to an unknown opcode.
|
||||
*/
|
||||
abstract class MethodFingerprint(
|
||||
internal val returnType: String?,
|
||||
internal val access: Int?,
|
||||
internal val parameters: Iterable<String>?,
|
||||
internal val opcodes: Iterable<Opcode?>?,
|
||||
internal val strings: Iterable<String>? = null,
|
||||
internal val customFingerprint: ((methodDef: Method) -> Boolean)? = null
|
||||
) : Fingerprint {
|
||||
/**
|
||||
* The result of the [MethodFingerprint] the [Method].
|
||||
* @throws MethodNotFoundException If the resolution of the [Method] has not happened.
|
||||
*/
|
||||
var result: MethodFingerprintResult? = null
|
||||
get() = field ?: throw MethodNotFoundException("${this.name} has not been resolved yet.")
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the result of a [MethodFingerprintUtils].
|
||||
* @param method The matching method.
|
||||
* @param classDef The [ClassDef] that contains the matching [method].
|
||||
* @param patternScanResult Opcodes pattern scan result.
|
||||
* @param data The [BytecodeData] this [MethodFingerprintResult] is attached to, to create proxies.
|
||||
*/
|
||||
data class MethodFingerprintResult(
|
||||
val method: Method,
|
||||
val classDef: ClassDef,
|
||||
val patternScanResult: PatternScanResult?,
|
||||
val data: BytecodeData
|
||||
) {
|
||||
/**
|
||||
* Returns a mutable clone of [classDef]
|
||||
*
|
||||
* Please note, this method allocates a [ClassProxy].
|
||||
* Use [classDef] where possible.
|
||||
*/
|
||||
val mutableClass by lazy { data.proxy(classDef).resolve() }
|
||||
|
||||
/**
|
||||
* Returns a mutable clone of [method]
|
||||
*
|
||||
* Please note, this method allocates a [ClassProxy].
|
||||
* Use [method] where possible.
|
||||
*/
|
||||
val mutableMethod by lazy {
|
||||
mutableClass.methods.first {
|
||||
it.softCompareTo(this.method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of a pattern scan.
|
||||
* @param startIndex The start index of the instructions where to which this pattern matches.
|
||||
* @param endIndex The end index of the instructions where to which this pattern matches.
|
||||
* @param warnings A list of warnings considering this [PatternScanResult].
|
||||
*/
|
||||
data class PatternScanResult(
|
||||
val startIndex: Int,
|
||||
val endIndex: Int,
|
||||
var warnings: List<Warning>? = null
|
||||
) {
|
||||
/**
|
||||
* Represents warnings of the pattern scan.
|
||||
* @param correctOpcode The opcode the instruction list has.
|
||||
* @param wrongOpcode The opcode the pattern list of the signature currently has.
|
||||
* @param instructionIndex The index of the opcode relative to the instruction list.
|
||||
* @param patternIndex The index of the opcode relative to the pattern list from the signature.
|
||||
*/
|
||||
data class Warning(
|
||||
val correctOpcode: Opcode,
|
||||
val wrongOpcode: Opcode,
|
||||
val instructionIndex: Int,
|
||||
val patternIndex: Int,
|
||||
)
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
package app.revanced.patcher.fingerprint.method.utils
|
||||
|
||||
import app.revanced.patcher.data.impl.BytecodeData
|
||||
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod
|
||||
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyScanThreshold
|
||||
import app.revanced.patcher.extensions.parametersEqual
|
||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprintResult
|
||||
import app.revanced.patcher.fingerprint.method.impl.PatternScanResult
|
||||
import org.jf.dexlib2.Opcode
|
||||
import org.jf.dexlib2.iface.ClassDef
|
||||
import org.jf.dexlib2.iface.Method
|
||||
import org.jf.dexlib2.iface.instruction.Instruction
|
||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import org.jf.dexlib2.iface.reference.StringReference
|
||||
|
||||
/**
|
||||
* Utility class for [MethodFingerprint]
|
||||
*/
|
||||
object MethodFingerprintUtils {
|
||||
/**
|
||||
* Resolve a list of [MethodFingerprint] against a list of [ClassDef].
|
||||
* @param context The classes on which to resolve the [MethodFingerprint].
|
||||
* @param forData The [BytecodeData] to host proxies.
|
||||
* @return True if the resolution was successful, false otherwise.
|
||||
*/
|
||||
fun Iterable<MethodFingerprint>.resolve(forData: BytecodeData, context: Iterable<ClassDef>) {
|
||||
for (fingerprint in this) // For each fingerprint
|
||||
classes@ for (classDef in context) // search through all classes for the fingerprint
|
||||
if (fingerprint.resolve(forData, classDef))
|
||||
break@classes // if the resolution succeeded, continue with the next fingerprint
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a [MethodFingerprint] against a [ClassDef].
|
||||
* @param context The class on which to resolve the [MethodFingerprint].
|
||||
* @param forData The [BytecodeData] to host proxies.
|
||||
* @return True if the resolution was successful, false otherwise.
|
||||
*/
|
||||
fun MethodFingerprint.resolve(forData: BytecodeData, context: ClassDef): Boolean {
|
||||
for (method in context.methods)
|
||||
if (this.resolve(forData, method, context))
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a [MethodFingerprint] against a [Method].
|
||||
* @param context The context on which to resolve the [MethodFingerprint].
|
||||
* @param classDef The class of the matching [Method].
|
||||
* @param forData The [BytecodeData] to host proxies.
|
||||
* @return True if the resolution was successful, false otherwise.
|
||||
*/
|
||||
fun MethodFingerprint.resolve(forData: BytecodeData, context: Method, classDef: ClassDef): Boolean {
|
||||
val methodFingerprint = this
|
||||
|
||||
if (methodFingerprint.returnType != null && !context.returnType.startsWith(methodFingerprint.returnType))
|
||||
return false
|
||||
|
||||
if (methodFingerprint.access != null && methodFingerprint.access != context.accessFlags)
|
||||
return false
|
||||
|
||||
|
||||
if (methodFingerprint.parameters != null && !parametersEqual(
|
||||
methodFingerprint.parameters, // TODO: parseParameters()
|
||||
context.parameterTypes
|
||||
)
|
||||
) return false
|
||||
|
||||
if (methodFingerprint.customFingerprint != null && methodFingerprint.customFingerprint!!(context))
|
||||
return false
|
||||
|
||||
if (methodFingerprint.strings != null) {
|
||||
val implementation = context.implementation ?: return false
|
||||
|
||||
val stringsList = methodFingerprint.strings.toMutableList()
|
||||
|
||||
implementation.instructions.forEach { instruction ->
|
||||
if (instruction.opcode.ordinal != Opcode.CONST_STRING.ordinal) return@forEach
|
||||
|
||||
val string = ((instruction as ReferenceInstruction).reference as StringReference).string
|
||||
val index = stringsList.indexOfFirst { it == string }
|
||||
if (index != -1) stringsList.removeAt(index)
|
||||
}
|
||||
|
||||
if (stringsList.isNotEmpty()) return false
|
||||
}
|
||||
|
||||
val patternScanResult = if (methodFingerprint.opcodes != null) {
|
||||
context.implementation?.instructions ?: return false
|
||||
|
||||
context.patternScan(methodFingerprint) ?: return false
|
||||
} else null
|
||||
|
||||
methodFingerprint.result = MethodFingerprintResult(context, classDef, patternScanResult, forData)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun Method.patternScan(
|
||||
fingerprint: MethodFingerprint
|
||||
): PatternScanResult? {
|
||||
val instructions = this.implementation!!.instructions
|
||||
val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyScanThreshold
|
||||
|
||||
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 = PatternScanResult(index, index + patternIndex)
|
||||
if (fingerprint.fuzzyPatternScanMethod !is FuzzyPatternScanMethod) return result
|
||||
result.warnings = result.createWarnings(pattern, instructions)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun PatternScanResult.createWarnings(
|
||||
pattern: Iterable<Opcode?>, instructions: Iterable<Instruction>
|
||||
) = buildList {
|
||||
for ((patternIndex, instructionIndex) in (this@createWarnings.startIndex until this@createWarnings.endIndex).withIndex()) {
|
||||
val originalOpcode = instructions.elementAt(instructionIndex).opcode
|
||||
val patternOpcode = pattern.elementAt(patternIndex)
|
||||
|
||||
if (patternOpcode == null || patternOpcode.ordinal == originalOpcode.ordinal) continue
|
||||
|
||||
this.add(PatternScanResult.Warning(originalOpcode, patternOpcode, instructionIndex, patternIndex))
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun ClassDef.component1() = this
|
||||
private operator fun ClassDef.component2() = this.methods
|
@ -1,9 +1,8 @@
|
||||
package app.revanced.patcher.patch.base
|
||||
package app.revanced.patcher.patch
|
||||
|
||||
import app.revanced.patcher.data.base.Data
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.ResourcePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||
|
||||
|
||||
/**
|
@ -1,4 +1,4 @@
|
||||
package app.revanced.patcher.patch.implementation.misc
|
||||
package app.revanced.patcher.patch
|
||||
|
||||
interface PatchResult {
|
||||
fun error(): PatchResultError? {
|
@ -1,7 +1,7 @@
|
||||
package app.revanced.patcher.patch.annotations
|
||||
|
||||
import app.revanced.patcher.data.base.Data
|
||||
import app.revanced.patcher.patch.base.Patch
|
||||
import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,13 @@
|
||||
package app.revanced.patcher.patch.impl
|
||||
|
||||
import app.revanced.patcher.data.impl.BytecodeData
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patcher.patch.Patch
|
||||
|
||||
/**
|
||||
* Bytecode patch for the Patcher.
|
||||
* @param fingerprints A list of [MethodFingerprint] this patch relies on.
|
||||
*/
|
||||
abstract class BytecodePatch(
|
||||
internal val fingerprints: Iterable<MethodFingerprint>
|
||||
) : Patch<BytecodeData>()
|
@ -0,0 +1,9 @@
|
||||
package app.revanced.patcher.patch.impl
|
||||
|
||||
import app.revanced.patcher.data.impl.ResourceData
|
||||
import app.revanced.patcher.patch.Patch
|
||||
|
||||
/**
|
||||
* Resource patch for the Patcher.
|
||||
*/
|
||||
abstract class ResourcePatch : Patch<ResourceData>()
|
@ -1,13 +0,0 @@
|
||||
package app.revanced.patcher.patch.implementation
|
||||
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.patch.base.Patch
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
|
||||
/**
|
||||
* Bytecode patch for the Patcher.
|
||||
* @param signatures A list of [MethodSignature] this patch relies on.
|
||||
*/
|
||||
abstract class BytecodePatch(
|
||||
internal val signatures: Iterable<MethodSignature>
|
||||
) : Patch<BytecodeData>()
|
@ -1,9 +0,0 @@
|
||||
package app.revanced.patcher.patch.implementation
|
||||
|
||||
import app.revanced.patcher.data.implementation.ResourceData
|
||||
import app.revanced.patcher.patch.base.Patch
|
||||
|
||||
/**
|
||||
* Resource patch for the Patcher.
|
||||
*/
|
||||
abstract class ResourcePatch : Patch<ResourceData>()
|
@ -1,9 +0,0 @@
|
||||
package app.revanced.patcher.signature.base
|
||||
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
|
||||
/**
|
||||
* A ReVanced signature.
|
||||
* Can be a [MethodSignature].
|
||||
*/
|
||||
interface Signature
|
@ -1,33 +0,0 @@
|
||||
package app.revanced.patcher.signature.implementation.method
|
||||
|
||||
import app.revanced.patcher.data.implementation.MethodNotFoundException
|
||||
import app.revanced.patcher.extensions.MethodSignatureExtensions.name
|
||||
import app.revanced.patcher.signature.base.Signature
|
||||
import app.revanced.patcher.signature.implementation.method.resolver.SignatureResolverResult
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* Represents the [MethodSignature] for a method.
|
||||
* @param returnType The return type of the method.
|
||||
* @param accessFlags The access flags of the method.
|
||||
* @param methodParameters 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 `null` opcode is equals to an unknown opcode.
|
||||
*/
|
||||
abstract class MethodSignature(
|
||||
internal val returnType: String?,
|
||||
internal val accessFlags: Int?,
|
||||
internal val methodParameters: Iterable<String>?,
|
||||
internal val opcodes: Iterable<Opcode?>?,
|
||||
internal val strings: Iterable<String>? = null
|
||||
) : Signature {
|
||||
/**
|
||||
* The result of the signature
|
||||
*/
|
||||
var result: SignatureResolverResult? = null
|
||||
@Throws(MethodNotFoundException::class)
|
||||
get() {
|
||||
return field ?: throw MethodNotFoundException("Could not resolve required signature ${this.name}")
|
||||
}
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
package app.revanced.patcher.signature.implementation.method.resolver
|
||||
|
||||
import app.revanced.patcher.data.PatcherData
|
||||
import app.revanced.patcher.data.implementation.proxy
|
||||
import app.revanced.patcher.extensions.MethodSignatureExtensions.fuzzyThreshold
|
||||
import app.revanced.patcher.extensions.parametersEqual
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import org.jf.dexlib2.Opcode
|
||||
import org.jf.dexlib2.iface.ClassDef
|
||||
import org.jf.dexlib2.iface.Method
|
||||
import org.jf.dexlib2.iface.instruction.Instruction
|
||||
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
|
||||
import org.jf.dexlib2.iface.reference.StringReference
|
||||
|
||||
internal class MethodSignatureResolver(
|
||||
private val classes: List<ClassDef>,
|
||||
private val methodSignatures: Iterable<MethodSignature>
|
||||
) {
|
||||
fun resolve(patcherData: PatcherData) {
|
||||
for (signature in methodSignatures) {
|
||||
for (classDef in classes) {
|
||||
for (method in classDef.methods) {
|
||||
val patternScanData = compareSignatureToMethod(signature, method) ?: continue
|
||||
|
||||
// create class proxy, in case a patch needs mutability
|
||||
val classProxy = patcherData.bytecodeData.proxy(classDef)
|
||||
signature.result = SignatureResolverResult(
|
||||
classProxy,
|
||||
patternScanData,
|
||||
method,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These functions do not require the constructor values, so they can be static.
|
||||
companion object {
|
||||
fun resolveFromProxy(
|
||||
classProxy: app.revanced.patcher.util.proxy.ClassProxy,
|
||||
signature: MethodSignature
|
||||
): SignatureResolverResult? {
|
||||
for (method in classProxy.immutableClass.methods) {
|
||||
val result = compareSignatureToMethod(signature, method) ?: continue
|
||||
return SignatureResolverResult(
|
||||
classProxy,
|
||||
result,
|
||||
method,
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun compareSignatureToMethod(
|
||||
signature: MethodSignature,
|
||||
method: Method
|
||||
): PatternScanResult? {
|
||||
signature.returnType?.let {
|
||||
if (!method.returnType.startsWith(signature.returnType)) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
signature.accessFlags?.let {
|
||||
if (signature.accessFlags != method.accessFlags) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
signature.methodParameters?.let {
|
||||
if (!parametersEqual(signature.methodParameters, method.parameterTypes)) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
signature.strings?.let { strings ->
|
||||
method.implementation ?: return null
|
||||
|
||||
val stringsList = strings.toMutableList()
|
||||
|
||||
for (instruction in method.implementation!!.instructions) {
|
||||
if (instruction.opcode != Opcode.CONST_STRING) continue
|
||||
|
||||
val string = ((instruction as Instruction21c).reference as StringReference).string
|
||||
val i = stringsList.indexOfFirst { it == string }
|
||||
if (i != -1) stringsList.removeAt(i)
|
||||
}
|
||||
|
||||
if (stringsList.isNotEmpty()) return null
|
||||
}
|
||||
|
||||
return if (signature.opcodes == null) {
|
||||
PatternScanResult(0, 0)
|
||||
} else {
|
||||
method.implementation?.instructions?.let {
|
||||
compareOpcodes(signature, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun compareOpcodes(
|
||||
signature: MethodSignature,
|
||||
instructions: Iterable<Instruction>
|
||||
): PatternScanResult? {
|
||||
val count = instructions.count()
|
||||
val pattern = signature.opcodes!!
|
||||
val size = pattern.count()
|
||||
|
||||
val threshold = signature.fuzzyThreshold
|
||||
|
||||
for (instructionIndex in 0 until count) {
|
||||
var patternIndex = 0
|
||||
var currentThreshold = threshold
|
||||
while (instructionIndex + patternIndex < count) {
|
||||
val originalOpcode = instructions.elementAt(instructionIndex + patternIndex).opcode
|
||||
val patternOpcode = pattern.elementAt(patternIndex)
|
||||
if (
|
||||
patternOpcode != null && // unknown opcode
|
||||
originalOpcode != patternOpcode &&
|
||||
currentThreshold-- == 0
|
||||
) break
|
||||
if (++patternIndex < size) continue
|
||||
patternIndex-- // fix pattern offset
|
||||
|
||||
val result = PatternScanResult(instructionIndex, instructionIndex + patternIndex)
|
||||
|
||||
result.warnings = generateWarnings(signature, instructions, result)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun generateWarnings(
|
||||
signature: MethodSignature,
|
||||
instructions: Iterable<Instruction>,
|
||||
scanResult: PatternScanResult,
|
||||
) = buildList {
|
||||
val pattern = signature.opcodes!!
|
||||
for ((patternIndex, instructionIndex) in (scanResult.startIndex until scanResult.endIndex).withIndex()) {
|
||||
val correctOpcode = instructions.elementAt(instructionIndex).opcode
|
||||
val patternOpcode = pattern.elementAt(patternIndex)
|
||||
if (
|
||||
patternOpcode != null && // unknown opcode
|
||||
correctOpcode != patternOpcode
|
||||
) {
|
||||
this.add(
|
||||
PatternScanResult.Warning(
|
||||
correctOpcode, patternOpcode,
|
||||
instructionIndex, patternIndex,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun ClassDef.component1() = this
|
||||
private operator fun ClassDef.component2() = this.methods
|
@ -1,75 +0,0 @@
|
||||
package app.revanced.patcher.signature.implementation.method.resolver
|
||||
|
||||
import app.revanced.patcher.extensions.softCompareTo
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import app.revanced.patcher.util.proxy.ClassProxy
|
||||
import org.jf.dexlib2.Opcode
|
||||
import org.jf.dexlib2.iface.Method
|
||||
|
||||
/**
|
||||
* Represents the result of a [MethodSignatureResolver].
|
||||
* @param definingClassProxy The [ClassProxy] that the matching method was found in.
|
||||
* @param resolvedMethod The actual matching method.
|
||||
* @param scanResult Opcodes pattern scan result.
|
||||
*/
|
||||
data class SignatureResolverResult(
|
||||
val definingClassProxy: ClassProxy,
|
||||
val scanResult: PatternScanResult,
|
||||
private val resolvedMethod: Method,
|
||||
) {
|
||||
/**
|
||||
* Returns the **mutable** method by the [resolvedMethod] from the [definingClassProxy].
|
||||
*
|
||||
* Please note, this method allocates a [ClassProxy].
|
||||
* Use [immutableMethod] where possible.
|
||||
*/
|
||||
val method
|
||||
get() = definingClassProxy.resolve().methods.first {
|
||||
it.softCompareTo(resolvedMethod)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the **immutable** method by the [resolvedMethod] from the [definingClassProxy].
|
||||
*
|
||||
* If you need to modify the method, use [method] instead.
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val immutableMethod: Method
|
||||
get() = definingClassProxy.immutableClass.methods.first {
|
||||
it.softCompareTo(resolvedMethod)
|
||||
}
|
||||
|
||||
fun findParentMethod(signature: MethodSignature): SignatureResolverResult? {
|
||||
return MethodSignatureResolver.resolveFromProxy(definingClassProxy, signature)
|
||||
}
|
||||
}
|
||||
|
||||
data class PatternScanResult(
|
||||
val startIndex: Int,
|
||||
val endIndex: Int
|
||||
) {
|
||||
/**
|
||||
* A list of warnings the resolver found.
|
||||
*
|
||||
* This list will be allocated when the signature has been found.
|
||||
* Meaning, if the signature was not found,
|
||||
* or the signature was not yet resolved,
|
||||
* the list will be null.
|
||||
*/
|
||||
var warnings: List<Warning>? = null
|
||||
|
||||
/**
|
||||
* Represents a resolver warning.
|
||||
* @param correctOpcode The opcode the instruction list has.
|
||||
* @param wrongOpcode The opcode the pattern list of the signature currently has.
|
||||
* @param instructionIndex The index of the opcode relative to the instruction list.
|
||||
* @param patternIndex The index of the opcode relative to the pattern list from the signature.
|
||||
*/
|
||||
data class Warning(
|
||||
val correctOpcode: Opcode,
|
||||
val wrongOpcode: Opcode,
|
||||
val instructionIndex: Int,
|
||||
val patternIndex: Int,
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package app.revanced.patcher.util.method
|
||||
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.data.implementation.MethodNotFoundException
|
||||
import app.revanced.patcher.data.impl.BytecodeData
|
||||
import app.revanced.patcher.data.impl.MethodNotFoundException
|
||||
import app.revanced.patcher.extensions.softCompareTo
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import org.jf.dexlib2.Format
|
||||
|
@ -1,7 +1,7 @@
|
||||
package app.revanced.patcher.util.patch.base
|
||||
|
||||
import app.revanced.patcher.data.base.Data
|
||||
import app.revanced.patcher.patch.base.Patch
|
||||
import app.revanced.patcher.data.Data
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
|
@ -1,16 +1,16 @@
|
||||
package app.revanced.patcher.usage.bytecode.signatures
|
||||
package app.revanced.patcher.usage.bytecode.fingerprints
|
||||
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.signature.implementation.method.MethodSignature
|
||||
import app.revanced.patcher.signature.implementation.method.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.signature.implementation.method.annotation.MatchingMethod
|
||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import app.revanced.patcher.usage.bytecode.annotation.ExampleBytecodeCompatibility
|
||||
import org.jf.dexlib2.AccessFlags
|
||||
import org.jf.dexlib2.Opcode
|
||||
|
||||
@Name("example-signature")
|
||||
@Name("example-fingerprint")
|
||||
@MatchingMethod(
|
||||
"LexampleClass;",
|
||||
"exampleMehod"
|
||||
@ -18,7 +18,7 @@ import org.jf.dexlib2.Opcode
|
||||
@FuzzyPatternScanMethod(2)
|
||||
@ExampleBytecodeCompatibility
|
||||
@Version("0.0.1")
|
||||
object ExampleSignature : MethodSignature(
|
||||
object ExampleFingerprint : MethodFingerprint(
|
||||
"V",
|
||||
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
listOf("[L"),
|
@ -3,14 +3,14 @@ package app.revanced.patcher.usage.bytecode.patch
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.BytecodeData
|
||||
import app.revanced.patcher.data.impl.BytecodeData
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.implementation.BytecodePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||
import app.revanced.patcher.usage.bytecode.signatures.ExampleSignature
|
||||
import app.revanced.patcher.patch.impl.BytecodePatch
|
||||
import app.revanced.patcher.usage.bytecode.fingerprints.ExampleFingerprint
|
||||
import app.revanced.patcher.usage.resource.annotation.ExampleResourceCompatibility
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
@ -39,34 +39,35 @@ import org.jf.dexlib2.util.Preconditions
|
||||
class ExampleBytecodePatch : BytecodePatch(
|
||||
|
||||
listOf(
|
||||
ExampleSignature
|
||||
ExampleFingerprint
|
||||
)
|
||||
) {
|
||||
// This function will be executed by the patcher.
|
||||
// You can treat it as a constructor
|
||||
override fun execute(data: BytecodeData): PatchResult {
|
||||
// Get the resolved method for the signature from the resolver cache
|
||||
val result = ExampleSignature.result!!
|
||||
// Get the resolved method by its fingerprint from the resolver cache
|
||||
val result = ExampleFingerprint.result!!
|
||||
|
||||
// Get the implementation for the resolved method
|
||||
val implementation = result.method.implementation!!
|
||||
val method = result.mutableMethod
|
||||
val implementation = method.implementation!!
|
||||
|
||||
// Let's modify it, so it prints "Hello, ReVanced! Editing bytecode."
|
||||
// Get the start index of our opcode pattern.
|
||||
// This will be the index of the instruction with the opcode CONST_STRING.
|
||||
val startIndex = result.scanResult.startIndex
|
||||
val startIndex = result.patternScanResult!!.startIndex
|
||||
|
||||
implementation.replaceStringAt(startIndex, "Hello, ReVanced! Editing bytecode.")
|
||||
|
||||
// Get the class in which the method matching our signature is defined in.
|
||||
// Get the class in which the method matching our fingerprint is defined in.
|
||||
val mainClass = data.findClass {
|
||||
it.type == result.definingClassProxy.immutableClass.type
|
||||
it.type == result.classDef.type
|
||||
}!!.resolve()
|
||||
|
||||
// Add a new method returning a string
|
||||
mainClass.methods.add(
|
||||
ImmutableMethod(
|
||||
result.definingClassProxy.immutableClass.type,
|
||||
result.classDef.type,
|
||||
"returnHello",
|
||||
null,
|
||||
"Ljava/lang/String;",
|
||||
@ -126,7 +127,7 @@ class ExampleBytecodePatch : BytecodePatch(
|
||||
move-result-object v1
|
||||
invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
|
||||
"""
|
||||
result.method.addInstructions(startIndex + 2, instructions)
|
||||
method.addInstructions(startIndex + 2, instructions)
|
||||
|
||||
// Finally, tell the patcher that this patch was a success.
|
||||
// You can also return PatchResultError with a message.
|
||||
|
@ -3,11 +3,11 @@ package app.revanced.patcher.usage.resource.patch
|
||||
import app.revanced.patcher.annotation.Description
|
||||
import app.revanced.patcher.annotation.Name
|
||||
import app.revanced.patcher.annotation.Version
|
||||
import app.revanced.patcher.data.implementation.ResourceData
|
||||
import app.revanced.patcher.data.impl.ResourceData
|
||||
import app.revanced.patcher.patch.PatchResult
|
||||
import app.revanced.patcher.patch.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.annotations.Patch
|
||||
import app.revanced.patcher.patch.implementation.ResourcePatch
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResult
|
||||
import app.revanced.patcher.patch.implementation.misc.PatchResultSuccess
|
||||
import app.revanced.patcher.patch.impl.ResourcePatch
|
||||
import app.revanced.patcher.usage.resource.annotation.ExampleResourceCompatibility
|
||||
import org.w3c.dom.Element
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user