mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2024-11-10 01:02:22 +01:00
chore: merge branch dev
to main
(#187)
This commit is contained in:
commit
bb9a73e53b
12 changed files with 738 additions and 310 deletions
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -1,3 +1,24 @@
|
||||||
|
# [11.0.0-dev.2](https://github.com/revanced/revanced-patcher/compare/v11.0.0-dev.1...v11.0.0-dev.2) (2023-06-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add imports to fix failing tests ([43d6868](https://github.com/revanced/revanced-patcher/commit/43d6868d1f59922f9812f3e4a2a890f3b331def6))
|
||||||
|
|
||||||
|
# [11.0.0-dev.1](https://github.com/revanced/revanced-patcher/compare/v10.0.0...v11.0.0-dev.1) (2023-06-07)
|
||||||
|
|
||||||
|
|
||||||
|
* refactor!: move extension functions to their corresponding classes ([a12fe7d](https://github.com/revanced/revanced-patcher/commit/a12fe7dd9e976c38a0a82fe35e6650f58f815de4))
|
||||||
|
* refactor!: use proper extension function names ([efdd01a](https://github.com/revanced/revanced-patcher/commit/efdd01a9886b6f06af213731824621964367b2a3))
|
||||||
|
* fix!: implement extension functions consistently ([aacf900](https://github.com/revanced/revanced-patcher/commit/aacf9007647b1cc11bc40053625802573efda6ef))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* This changes the import paths for extension functions.
|
||||||
|
* This changes the names of extension functions
|
||||||
|
* This changes the name of functions
|
||||||
|
|
||||||
# [10.0.0](https://github.com/revanced/revanced-patcher/compare/v9.0.0...v10.0.0) (2023-06-07)
|
# [10.0.0](https://github.com/revanced/revanced-patcher/compare/v9.0.0...v10.0.0) (2023-06-07)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 10.0.0
|
version = 11.0.0-dev.2
|
||||||
|
|
|
@ -4,7 +4,6 @@ import app.revanced.patcher.data.Context
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.dependencies
|
import app.revanced.patcher.extensions.PatchExtensions.dependencies
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.requiresIntegrations
|
import app.revanced.patcher.extensions.PatchExtensions.requiresIntegrations
|
||||||
import app.revanced.patcher.extensions.nullOutputStream
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
|
||||||
import app.revanced.patcher.patch.*
|
import app.revanced.patcher.patch.*
|
||||||
import app.revanced.patcher.util.VersionReader
|
import app.revanced.patcher.util.VersionReader
|
||||||
|
@ -25,6 +24,7 @@ import org.jf.dexlib2.Opcodes
|
||||||
import org.jf.dexlib2.iface.DexFile
|
import org.jf.dexlib2.iface.DexFile
|
||||||
import org.jf.dexlib2.writer.io.MemoryDataStore
|
import org.jf.dexlib2.writer.io.MemoryDataStore
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.OutputStream
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
|
||||||
internal val NAMER = BasicDexFileNamer()
|
internal val NAMER = BasicDexFileNamer()
|
||||||
|
@ -247,7 +247,7 @@ class Patcher(private val options: PatcherOptions) {
|
||||||
XmlPullStreamDecoder(
|
XmlPullStreamDecoder(
|
||||||
axmlParser, AndrolibResources().resXmlSerializer
|
axmlParser, AndrolibResources().resXmlSerializer
|
||||||
).decodeManifest(
|
).decodeManifest(
|
||||||
extInputFile.directory.getFileInput("AndroidManifest.xml"), nullOutputStream
|
extInputFile.directory.getFileInput("AndroidManifest.xml"), OutputStream.nullOutputStream()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,83 +1,33 @@
|
||||||
package app.revanced.patcher.extensions
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
import app.revanced.patcher.annotation.*
|
|
||||||
import app.revanced.patcher.data.Context
|
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
|
||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
|
||||||
import app.revanced.patcher.patch.OptionsContainer
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.PatchOptions
|
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
|
||||||
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KVisibility
|
|
||||||
import kotlin.reflect.full.companionObject
|
|
||||||
import kotlin.reflect.full.companionObjectInstance
|
|
||||||
|
|
||||||
/**
|
internal object AnnotationExtensions {
|
||||||
* Recursively find a given annotation on a class.
|
/**
|
||||||
* @param targetAnnotation The annotation to find.
|
* Recursively find a given annotation on a class.
|
||||||
* @return The annotation.
|
*
|
||||||
*/
|
* @param targetAnnotation The annotation to find.
|
||||||
private fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: KClass<T>) =
|
* @return The annotation.
|
||||||
this.findAnnotationRecursively(targetAnnotation.java, mutableSetOf())
|
*/
|
||||||
|
fun <T : Annotation> Class<*>.findAnnotationRecursively(targetAnnotation: KClass<T>): T? {
|
||||||
|
fun <T : Annotation> Class<*>.findAnnotationRecursively(
|
||||||
|
targetAnnotation: Class<T>, traversed: MutableSet<Annotation>
|
||||||
|
): T? {
|
||||||
|
val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name }
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST") if (found != null) return found as T
|
||||||
|
|
||||||
private fun <T : Annotation> Class<*>.findAnnotationRecursively(
|
for (annotation in this.annotations) {
|
||||||
targetAnnotation: Class<T>, traversed: MutableSet<Annotation>
|
if (traversed.contains(annotation)) continue
|
||||||
): T? {
|
traversed.add(annotation)
|
||||||
val found = this.annotations.firstOrNull { it.annotationClass.java.name == targetAnnotation.name }
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST") if (found != null) return found as T
|
return (annotation.annotationClass.java.findAnnotationRecursively(targetAnnotation, traversed))
|
||||||
|
?: continue
|
||||||
for (annotation in this.annotations) {
|
|
||||||
if (traversed.contains(annotation)) continue
|
|
||||||
traversed.add(annotation)
|
|
||||||
|
|
||||||
return (annotation.annotationClass.java.findAnnotationRecursively(targetAnnotation, traversed)) ?: continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
object PatchExtensions {
|
|
||||||
val Class<out Patch<Context>>.patchName: String
|
|
||||||
get() = findAnnotationRecursively(Name::class)?.name ?: this.simpleName
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.version
|
|
||||||
get() = findAnnotationRecursively(Version::class)?.version
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.include
|
|
||||||
get() = findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class)!!.include
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.description
|
|
||||||
get() = findAnnotationRecursively(Description::class)?.description
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.dependencies
|
|
||||||
get() = findAnnotationRecursively(DependsOn::class)?.dependencies
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.compatiblePackages
|
|
||||||
get() = findAnnotationRecursively(Compatibility::class)?.compatiblePackages
|
|
||||||
|
|
||||||
internal val Class<out Patch<Context>>.requiresIntegrations
|
|
||||||
get() = findAnnotationRecursively(RequiresIntegrations::class) != null
|
|
||||||
|
|
||||||
val Class<out Patch<Context>>.options: PatchOptions?
|
|
||||||
get() = kotlin.companionObject?.let { cl ->
|
|
||||||
if (cl.visibility != KVisibility.PUBLIC) return null
|
|
||||||
kotlin.companionObjectInstance?.let {
|
|
||||||
(it as? OptionsContainer)?.options
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
object MethodFingerprintExtensions {
|
return this.findAnnotationRecursively(targetAnnotation.java, mutableSetOf())
|
||||||
val MethodFingerprint.name: String
|
}
|
||||||
get() = this.javaClass.simpleName
|
|
||||||
|
|
||||||
val MethodFingerprint.fuzzyPatternScanMethod
|
|
||||||
get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class)
|
|
||||||
|
|
||||||
val MethodFingerprint.fuzzyScanThreshold
|
|
||||||
get() = fuzzyPatternScanMethod?.threshold ?: 0
|
|
||||||
}
|
}
|
|
@ -1,236 +1,33 @@
|
||||||
package app.revanced.patcher.extensions
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
|
||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
|
||||||
import app.revanced.patcher.util.smali.toInstruction
|
|
||||||
import app.revanced.patcher.util.smali.toInstructions
|
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.builder.BuilderInstruction
|
|
||||||
import org.jf.dexlib2.builder.BuilderOffsetInstruction
|
|
||||||
import org.jf.dexlib2.builder.Label
|
|
||||||
import org.jf.dexlib2.builder.MutableMethodImplementation
|
|
||||||
import org.jf.dexlib2.builder.instruction.*
|
|
||||||
import org.jf.dexlib2.iface.Method
|
|
||||||
import org.jf.dexlib2.iface.instruction.Instruction
|
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethod
|
|
||||||
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
|
|
||||||
import java.io.OutputStream
|
|
||||||
|
|
||||||
infix fun AccessFlags.or(other: AccessFlags) = this.value or other.value
|
|
||||||
infix fun Int.or(other: AccessFlags) = this or other.value
|
|
||||||
|
|
||||||
fun MutableMethodImplementation.addInstructions(index: Int, instructions: List<BuilderInstruction>) {
|
|
||||||
for (i in instructions.lastIndex downTo 0) {
|
|
||||||
this.addInstruction(index, instructions[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MutableMethodImplementation.addInstructions(instructions: List<BuilderInstruction>) {
|
|
||||||
for (instruction in instructions) {
|
|
||||||
this.addInstruction(instruction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MutableMethodImplementation.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) {
|
|
||||||
for (i in instructions.lastIndex downTo 0) {
|
|
||||||
this.replaceInstruction(index + i, instructions[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) {
|
|
||||||
for (i in count - 1 downTo 0) {
|
|
||||||
this.removeInstruction(index + i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clones the method.
|
* Create a label for the instruction at given index.
|
||||||
* @param registerCount This parameter allows you to change the register count of the method.
|
*
|
||||||
* This may be a positive or negative number.
|
|
||||||
* @return The **immutable** cloned method. Call [toMutable] or [cloneMutable] to get a **mutable** copy.
|
|
||||||
*/
|
|
||||||
internal fun Method.clone(registerCount: Int = 0): ImmutableMethod {
|
|
||||||
val clonedImplementation = implementation?.let {
|
|
||||||
ImmutableMethodImplementation(
|
|
||||||
it.registerCount + registerCount,
|
|
||||||
it.instructions,
|
|
||||||
it.tryBlocks,
|
|
||||||
it.debugItems,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return ImmutableMethod(
|
|
||||||
returnType, name, parameters, returnType, accessFlags, annotations, hiddenApiRestrictions, clonedImplementation
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
* @param instruction The smali instruction to add.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.addInstruction(index: Int, instruction: String) =
|
|
||||||
this.implementation!!.addInstruction(index, instruction.toInstruction(this))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace a smali instruction within the method.
|
|
||||||
* @param index The index to replace the instruction at.
|
|
||||||
* @param instruction The smali instruction to place.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.replaceInstruction(index: Int, instruction: String) =
|
|
||||||
this.implementation!!.replaceInstruction(index, instruction.toInstruction(this))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a smali instruction within the method.
|
|
||||||
* @param index The index to delete the instruction at.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.removeInstruction(index: Int) = this.implementation!!.removeInstruction(index)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a label for the instruction at given index in the method's implementation.
|
|
||||||
* @param index The index to create the label for the instruction at.
|
* @param index The index to create the label for the instruction at.
|
||||||
* @return The label.
|
* @return The label.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.label(index: Int) = this.implementation!!.newLabelForIndex(index)
|
fun MutableMethod.newLabel(index: Int) = implementation!!.newLabelForIndex(index)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an instruction at the given index in the method's implementation.
|
* Perform a bitwise OR operation between two [AccessFlags].
|
||||||
* @param index The index to get the instruction at.
|
*
|
||||||
* @return The instruction.
|
* @param other The other [AccessFlags] to perform the operation with.
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.instruction(index: Int): BuilderInstruction = this.implementation!!.instructions[index]
|
infix fun AccessFlags.or(other: AccessFlags) = value or other.value
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an instruction at the given index in the method's implementation.
|
* Perform a bitwise OR operation between an [AccessFlags] and an [Int].
|
||||||
* @param index The index to get the instruction at.
|
*
|
||||||
* @param T The type of instruction to return.
|
* @param other The [Int] to perform the operation with.
|
||||||
* @return The instruction.
|
|
||||||
*/
|
*/
|
||||||
fun <T> MutableMethod.instruction(index: Int): T = instruction(index) as T
|
infix fun Int.or(other: AccessFlags) = this or other.value
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add smali instructions to the method.
|
* Perform a bitwise OR operation between an [Int] and an [AccessFlags].
|
||||||
* @param index The index to insert the instructions at.
|
*
|
||||||
* @param smali The smali instructions to add.
|
* @param other The [AccessFlags] to perform the operation with.
|
||||||
* @param externalLabels A list of [ExternalLabel] representing a list of labels for instructions which are not in the method to compile.
|
|
||||||
*/
|
*/
|
||||||
|
infix fun AccessFlags.or(other: Int) = value or other
|
||||||
fun MutableMethod.addInstructions(index: Int, smali: String, externalLabels: List<ExternalLabel> = emptyList()) {
|
|
||||||
// Create reference dummy instructions for the instructions.
|
|
||||||
val nopedSmali = StringBuilder(smali).also { builder ->
|
|
||||||
externalLabels.forEach { (name, _) ->
|
|
||||||
builder.append("\n:$name\nnop")
|
|
||||||
}
|
|
||||||
}.toString()
|
|
||||||
|
|
||||||
// Compile the instructions with the dummy labels
|
|
||||||
val compiledInstructions = nopedSmali.toInstructions(this)
|
|
||||||
|
|
||||||
// Add the compiled list of instructions to the method.
|
|
||||||
val methodImplementation = this.implementation!!
|
|
||||||
methodImplementation.addInstructions(index, compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size))
|
|
||||||
|
|
||||||
val methodInstructions = methodImplementation.instructions
|
|
||||||
methodInstructions.subList(index, index + compiledInstructions.size - externalLabels.size)
|
|
||||||
.forEachIndexed { compiledInstructionIndex, compiledInstruction ->
|
|
||||||
// If the compiled instruction is not an offset instruction, skip it.
|
|
||||||
if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new label for the instruction and replaces it with the label of the [compiledInstruction] at [compiledInstructionIndex].
|
|
||||||
*/
|
|
||||||
fun Instruction.makeNewLabel() {
|
|
||||||
// Create the final label.
|
|
||||||
val label = methodImplementation.newLabelForIndex(methodInstructions.indexOf(this))
|
|
||||||
// Create the final instruction with the new label.
|
|
||||||
val newInstruction = replaceOffset(
|
|
||||||
compiledInstruction, label
|
|
||||||
)
|
|
||||||
// Replace the instruction pointing to the dummy label with the new instruction pointing to the real instruction.
|
|
||||||
methodImplementation.replaceInstruction(index + compiledInstructionIndex, newInstruction)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the compiled instruction targets its own instruction,
|
|
||||||
// which means it points to some of its own, simply an offset has to be applied.
|
|
||||||
val labelIndex = compiledInstruction.target.location.index
|
|
||||||
if (labelIndex < compiledInstructions.size - externalLabels.size) {
|
|
||||||
// Get the targets index (insertion index + the index of the dummy instruction).
|
|
||||||
methodInstructions[index + labelIndex].makeNewLabel()
|
|
||||||
return@forEachIndexed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since the compiled instruction points to a dummy instruction,
|
|
||||||
// we can find the real instruction which it was created for by calculation.
|
|
||||||
|
|
||||||
// Get the index of the instruction in the externalLabels list which the dummy instruction was created for.
|
|
||||||
// this line works because we created the dummy instructions in the same order as the externalLabels list.
|
|
||||||
val (_, instruction) = externalLabels[(compiledInstructions.size - 1) - labelIndex]
|
|
||||||
instruction.makeNewLabel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add smali instructions to the end of the method.
|
|
||||||
* @param instructions The smali instructions to add.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.addInstructions(instructions: String, labels: List<ExternalLabel> = emptyList()) =
|
|
||||||
this.addInstructions(this.implementation!!.instructions.size, instructions, labels)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace smali instructions within the method.
|
|
||||||
* @param index The index to replace the instructions at.
|
|
||||||
* @param instructions The smali instructions to place.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.replaceInstructions(index: Int, instructions: String) =
|
|
||||||
this.implementation!!.replaceInstructions(index, instructions.toInstructions(this))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove smali instructions from the method.
|
|
||||||
* @param index The index to remove the instructions at.
|
|
||||||
* @param count The amount of instructions to remove.
|
|
||||||
*/
|
|
||||||
fun MutableMethod.removeInstructions(index: Int, count: Int) = this.implementation!!.removeInstructions(index, count)
|
|
||||||
|
|
||||||
private fun replaceOffset(
|
|
||||||
i: BuilderOffsetInstruction, label: Label
|
|
||||||
): BuilderOffsetInstruction {
|
|
||||||
return when (i) {
|
|
||||||
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
|
|
||||||
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
|
|
||||||
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
|
|
||||||
is BuilderInstruction22t -> BuilderInstruction22t(i.opcode, i.registerA, i.registerB, label)
|
|
||||||
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
|
|
||||||
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
|
|
||||||
else -> throw IllegalStateException("A non-offset instruction was given, this should never happen!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clones the method.
|
|
||||||
* @param registerCount This parameter allows you to change the register count of the method.
|
|
||||||
* This may be a positive or negative number.
|
|
||||||
* @return The **mutable** cloned method. Call [clone] to get an **immutable** copy.
|
|
||||||
*/
|
|
||||||
internal fun Method.cloneMutable(registerCount: Int = 0) = clone(registerCount).toMutable()
|
|
||||||
|
|
||||||
internal fun parametersEqual(
|
|
||||||
parameters1: Iterable<CharSequence>, parameters2: Iterable<CharSequence>
|
|
||||||
): Boolean {
|
|
||||||
if (parameters1.count() != parameters2.count()) return false
|
|
||||||
val iterator1 = parameters1.iterator()
|
|
||||||
parameters2.forEach {
|
|
||||||
if (!it.startsWith(iterator1.next())) return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val nullOutputStream = object : OutputStream() {
|
|
||||||
override fun write(b: Int) {}
|
|
||||||
}
|
|
|
@ -0,0 +1,326 @@
|
||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
|
import app.revanced.patcher.util.smali.toInstruction
|
||||||
|
import app.revanced.patcher.util.smali.toInstructions
|
||||||
|
import org.jf.dexlib2.builder.BuilderInstruction
|
||||||
|
import org.jf.dexlib2.builder.BuilderOffsetInstruction
|
||||||
|
import org.jf.dexlib2.builder.Label
|
||||||
|
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||||
|
import org.jf.dexlib2.builder.instruction.*
|
||||||
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
|
|
||||||
|
object InstructionExtensions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add instructions to a method at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to add the instructions at.
|
||||||
|
* @param instructions The instructions to add.
|
||||||
|
*/
|
||||||
|
fun MutableMethodImplementation.addInstructions(
|
||||||
|
index: Int,
|
||||||
|
instructions: List<BuilderInstruction>
|
||||||
|
) =
|
||||||
|
instructions.asReversed().forEach { addInstruction(index, it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add instructions to a method.
|
||||||
|
* The instructions will be added at the end of the method.
|
||||||
|
*
|
||||||
|
* @param instructions The instructions to add.
|
||||||
|
*/
|
||||||
|
fun MutableMethodImplementation.addInstructions(instructions: List<BuilderInstruction>) =
|
||||||
|
instructions.forEach { this.addInstruction(it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove instructions from a method at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to remove the instructions at.
|
||||||
|
* @param count The amount of instructions to remove.
|
||||||
|
*/
|
||||||
|
fun MutableMethodImplementation.removeInstructions(index: Int, count: Int) = repeat(count) {
|
||||||
|
removeInstruction(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the first instructions from a method.
|
||||||
|
*
|
||||||
|
* @param count The amount of instructions to remove.
|
||||||
|
*/
|
||||||
|
fun MutableMethodImplementation.removeInstructions(count: Int) = removeInstructions(0, count)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace instructions at the given index with the given instructions.
|
||||||
|
* The amount of instructions to replace is the amount of instructions in the given list.
|
||||||
|
*
|
||||||
|
* @param index The index to replace the instructions at.
|
||||||
|
* @param instructions The instructions to replace the instructions with.
|
||||||
|
*/
|
||||||
|
fun MutableMethodImplementation.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) {
|
||||||
|
// Remove the instructions at the given index.
|
||||||
|
removeInstructions(index, instructions.size)
|
||||||
|
|
||||||
|
// Add the instructions at the given index.
|
||||||
|
addInstructions(index, instructions)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an instruction to a method at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to add the instruction at.
|
||||||
|
* @param instruction The instruction to add.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.addInstruction(index: Int, instruction: BuilderInstruction) =
|
||||||
|
implementation!!.addInstruction(index, instruction)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an instruction to a method.
|
||||||
|
*
|
||||||
|
* @param instruction The instructions to add.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.addInstruction(instruction: BuilderInstruction) =
|
||||||
|
implementation!!.addInstruction(instruction)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an instruction to a method at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to add the instruction at.
|
||||||
|
* @param smaliInstructions The instruction to add.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.addInstruction(index: Int, smaliInstructions: String) =
|
||||||
|
implementation!!.addInstruction(index, smaliInstructions.toInstruction(this))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an instruction to a method.
|
||||||
|
*
|
||||||
|
* @param smaliInstructions The instruction to add.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.addInstruction(smaliInstructions: String) =
|
||||||
|
implementation!!.addInstruction(smaliInstructions.toInstruction(this))
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add instructions to a method at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to add the instructions at.
|
||||||
|
* @param instructions The instructions to add.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.addInstructions(index: Int, instructions: List<BuilderInstruction>) =
|
||||||
|
implementation!!.addInstructions(index, instructions)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add instructions to a method.
|
||||||
|
*
|
||||||
|
* @param instructions The instructions to add.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.addInstructions(instructions: List<BuilderInstruction>) =
|
||||||
|
implementation!!.addInstructions(instructions)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add instructions to a method.
|
||||||
|
*
|
||||||
|
* @param smaliInstructions The instructions to add.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.addInstructions(index: Int, smaliInstructions: String) =
|
||||||
|
implementation!!.addInstructions(index, smaliInstructions.toInstructions(this))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add instructions to a method.
|
||||||
|
*
|
||||||
|
* @param smaliInstructions The instructions to add.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.addInstructions(smaliInstructions: String) =
|
||||||
|
implementation!!.addInstructions(smaliInstructions.toInstructions(this))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add instructions to a method at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to add the instructions at.
|
||||||
|
* @param smaliInstructions The instructions to add.
|
||||||
|
* @param externalLabels A list of [ExternalLabel] for instructions outside of [smaliInstructions].
|
||||||
|
*/
|
||||||
|
// Special function for adding instructions with external labels.
|
||||||
|
fun MutableMethod.addInstructionsWithLabels(
|
||||||
|
index: Int,
|
||||||
|
smaliInstructions: String,
|
||||||
|
vararg externalLabels: ExternalLabel
|
||||||
|
) {
|
||||||
|
// Create reference dummy instructions for the instructions.
|
||||||
|
val nopSmali = StringBuilder(smaliInstructions).also { builder ->
|
||||||
|
externalLabels.forEach { (name, _) ->
|
||||||
|
builder.append("\n:$name\nnop")
|
||||||
|
}
|
||||||
|
}.toString()
|
||||||
|
|
||||||
|
// Compile the instructions with the dummy labels
|
||||||
|
val compiledInstructions = nopSmali.toInstructions(this)
|
||||||
|
|
||||||
|
// Add the compiled list of instructions to the method.
|
||||||
|
addInstructions(
|
||||||
|
index,
|
||||||
|
compiledInstructions.subList(0, compiledInstructions.size - externalLabels.size)
|
||||||
|
)
|
||||||
|
|
||||||
|
implementation!!.apply {
|
||||||
|
this@apply.instructions.subList(index, index + compiledInstructions.size - externalLabels.size)
|
||||||
|
.forEachIndexed { compiledInstructionIndex, compiledInstruction ->
|
||||||
|
// If the compiled instruction is not an offset instruction, skip it.
|
||||||
|
if (compiledInstruction !is BuilderOffsetInstruction) return@forEachIndexed
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new label for the instruction
|
||||||
|
* and replaces it with the label of the [compiledInstruction] at [compiledInstructionIndex].
|
||||||
|
*/
|
||||||
|
fun Instruction.makeNewLabel() {
|
||||||
|
fun replaceOffset(
|
||||||
|
i: BuilderOffsetInstruction, label: Label
|
||||||
|
): BuilderOffsetInstruction {
|
||||||
|
return when (i) {
|
||||||
|
is BuilderInstruction10t -> BuilderInstruction10t(i.opcode, label)
|
||||||
|
is BuilderInstruction20t -> BuilderInstruction20t(i.opcode, label)
|
||||||
|
is BuilderInstruction21t -> BuilderInstruction21t(i.opcode, i.registerA, label)
|
||||||
|
is BuilderInstruction22t -> BuilderInstruction22t(
|
||||||
|
i.opcode,
|
||||||
|
i.registerA,
|
||||||
|
i.registerB,
|
||||||
|
label
|
||||||
|
)
|
||||||
|
is BuilderInstruction30t -> BuilderInstruction30t(i.opcode, label)
|
||||||
|
is BuilderInstruction31t -> BuilderInstruction31t(i.opcode, i.registerA, label)
|
||||||
|
else -> throw IllegalStateException(
|
||||||
|
"A non-offset instruction was given, this should never happen!"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the final label.
|
||||||
|
val label = newLabelForIndex(this@apply.instructions.indexOf(this))
|
||||||
|
|
||||||
|
// Create the final instruction with the new label.
|
||||||
|
val newInstruction = replaceOffset(
|
||||||
|
compiledInstruction, label
|
||||||
|
)
|
||||||
|
|
||||||
|
// Replace the instruction pointing to the dummy label
|
||||||
|
// with the new instruction pointing to the real instruction.
|
||||||
|
replaceInstruction(index + compiledInstructionIndex, newInstruction)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the compiled instruction targets its own instruction,
|
||||||
|
// which means it points to some of its own, simply an offset has to be applied.
|
||||||
|
val labelIndex = compiledInstruction.target.location.index
|
||||||
|
if (labelIndex < compiledInstructions.size - externalLabels.size) {
|
||||||
|
// Get the targets index (insertion index + the index of the dummy instruction).
|
||||||
|
this.instructions[index + labelIndex].makeNewLabel()
|
||||||
|
return@forEachIndexed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the compiled instruction points to a dummy instruction,
|
||||||
|
// we can find the real instruction which it was created for by calculation.
|
||||||
|
|
||||||
|
// Get the index of the instruction in the externalLabels list
|
||||||
|
// which the dummy instruction was created for.
|
||||||
|
// This works because we created the dummy instructions in the same order as the externalLabels list.
|
||||||
|
val (_, instruction) = externalLabels[(compiledInstructions.size - 1) - labelIndex]
|
||||||
|
instruction.makeNewLabel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an instruction at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to remove the instruction at.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.removeInstruction(index: Int) =
|
||||||
|
implementation!!.removeInstruction(index)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove instructions at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to remove the instructions at.
|
||||||
|
* @param count The amount of instructions to remove.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.removeInstructions(index: Int, count: Int) =
|
||||||
|
implementation!!.removeInstructions(index, count)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove instructions at the given index.
|
||||||
|
*
|
||||||
|
* @param count The amount of instructions to remove.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.removeInstructions(count: Int) =
|
||||||
|
implementation!!.removeInstructions(count)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace an instruction at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to replace the instruction at.
|
||||||
|
* @param instruction The instruction to replace the instruction with.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.replaceInstruction(index: Int, instruction: BuilderInstruction) =
|
||||||
|
implementation!!.replaceInstruction(index, instruction)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace an instruction at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to replace the instruction at.
|
||||||
|
* @param smaliInstruction The smali instruction to replace the instruction with.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.replaceInstruction(index: Int, smaliInstruction: String) =
|
||||||
|
implementation!!.replaceInstruction(index, smaliInstruction.toInstruction(this))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace instructions at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to replace the instructions at.
|
||||||
|
* @param instructions The instructions to replace the instructions with.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.replaceInstructions(index: Int, instructions: List<BuilderInstruction>) =
|
||||||
|
implementation!!.replaceInstructions(index, instructions)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace instructions at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to replace the instructions at.
|
||||||
|
* @param smaliInstructions The smali instructions to replace the instructions with.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.replaceInstructions(index: Int, smaliInstructions: String) =
|
||||||
|
implementation!!.replaceInstructions(index, smaliInstructions.toInstructions(this))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an instruction at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to get the instruction at.
|
||||||
|
* @return The instruction.
|
||||||
|
*/
|
||||||
|
fun MutableMethodImplementation.getInstruction(index: Int): BuilderInstruction = instructions[index]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an instruction at the given index.
|
||||||
|
*
|
||||||
|
* @param index The index to get the instruction at.
|
||||||
|
* @param T The type of instruction to return.
|
||||||
|
* @return The instruction.
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T> MutableMethodImplementation.getInstruction(index: Int): T = getInstruction(index) as T
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an instruction at the given index.
|
||||||
|
* @param index The index to get the instruction at.
|
||||||
|
* @return The instruction.
|
||||||
|
*/
|
||||||
|
fun MutableMethod.getInstruction(index: Int): BuilderInstruction = implementation!!.getInstruction(index)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an instruction at the given index.
|
||||||
|
* @param index The index to get the instruction at.
|
||||||
|
* @param T The type of instruction to return.
|
||||||
|
* @return The instruction.
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T> MutableMethod.getInstruction(index: Int): T = implementation!!.getInstruction<T>(index)
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
|
||||||
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
|
||||||
|
object MethodFingerprintExtensions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of a [MethodFingerprint].
|
||||||
|
*/
|
||||||
|
val MethodFingerprint.name: String
|
||||||
|
get() = this.javaClass.simpleName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [FuzzyPatternScanMethod] annotation of a [MethodFingerprint].
|
||||||
|
*/
|
||||||
|
val MethodFingerprint.fuzzyPatternScanMethod
|
||||||
|
get() = javaClass.findAnnotationRecursively(FuzzyPatternScanMethod::class)
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
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.Context
|
||||||
|
import app.revanced.patcher.extensions.AnnotationExtensions.findAnnotationRecursively
|
||||||
|
import app.revanced.patcher.patch.OptionsContainer
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import app.revanced.patcher.patch.PatchOptions
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.RequiresIntegrations
|
||||||
|
import kotlin.reflect.KVisibility
|
||||||
|
import kotlin.reflect.full.companionObject
|
||||||
|
import kotlin.reflect.full.companionObjectInstance
|
||||||
|
|
||||||
|
object PatchExtensions {
|
||||||
|
/**
|
||||||
|
* The name of a [Patch].
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.patchName: String
|
||||||
|
get() = findAnnotationRecursively(Name::class)?.name ?: this.simpleName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of a [Patch].
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.version
|
||||||
|
get() = findAnnotationRecursively(Version::class)?.version
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather or not a [Patch] should be included.
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.include
|
||||||
|
get() = findAnnotationRecursively(app.revanced.patcher.patch.annotations.Patch::class)!!.include
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The description of a [Patch].
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.description
|
||||||
|
get() = findAnnotationRecursively(Description::class)?.description
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dependencies of a [Patch].
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.dependencies
|
||||||
|
get() = findAnnotationRecursively(DependsOn::class)?.dependencies
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The packages a [Patch] is compatible with.
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.compatiblePackages
|
||||||
|
get() = findAnnotationRecursively(Compatibility::class)?.compatiblePackages
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather or not a [Patch] requires integrations.
|
||||||
|
*/
|
||||||
|
internal val Class<out Patch<Context>>.requiresIntegrations
|
||||||
|
get() = findAnnotationRecursively(RequiresIntegrations::class) != null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The options of a [Patch].
|
||||||
|
*/
|
||||||
|
val Class<out Patch<Context>>.options: PatchOptions?
|
||||||
|
get() = kotlin.companionObject?.let { cl ->
|
||||||
|
if (cl.visibility != KVisibility.PUBLIC) return null
|
||||||
|
kotlin.companionObjectInstance?.let {
|
||||||
|
(it as? OptionsContainer)?.options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,6 @@ package app.revanced.patcher.fingerprint.method.impl
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.MethodFingerprintExtensions.fuzzyPatternScanMethod
|
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.Fingerprint
|
import app.revanced.patcher.fingerprint.Fingerprint
|
||||||
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
|
||||||
import app.revanced.patcher.util.proxy.ClassProxy
|
import app.revanced.patcher.util.proxy.ClassProxy
|
||||||
|
@ -90,6 +88,17 @@ abstract class MethodFingerprint(
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
|
fun parametersEqual(
|
||||||
|
parameters1: Iterable<CharSequence>, parameters2: Iterable<CharSequence>
|
||||||
|
): Boolean {
|
||||||
|
if (parameters1.count() != parameters2.count()) return false
|
||||||
|
val iterator1 = parameters1.iterator()
|
||||||
|
parameters2.forEach {
|
||||||
|
if (!it.startsWith(iterator1.next())) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if (methodFingerprint.parameters != null && !parametersEqual(
|
if (methodFingerprint.parameters != null && !parametersEqual(
|
||||||
methodFingerprint.parameters, // TODO: parseParameters()
|
methodFingerprint.parameters, // TODO: parseParameters()
|
||||||
method.parameterTypes
|
method.parameterTypes
|
||||||
|
@ -155,7 +164,7 @@ abstract class MethodFingerprint(
|
||||||
fingerprint: MethodFingerprint
|
fingerprint: MethodFingerprint
|
||||||
): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? {
|
): MethodFingerprintResult.MethodFingerprintScanResult.PatternScanResult? {
|
||||||
val instructions = this.implementation!!.instructions
|
val instructions = this.implementation!!.instructions
|
||||||
val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyScanThreshold
|
val fingerprintFuzzyPatternScanThreshold = fingerprint.fuzzyPatternScanMethod?.threshold ?: 0
|
||||||
|
|
||||||
val pattern = fingerprint.opcodes!!
|
val pattern = fingerprint.opcodes!!
|
||||||
val instructionLength = instructions.count()
|
val instructionLength = instructions.count()
|
||||||
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
package app.revanced.patcher.extensions
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.builder.BuilderOffsetInstruction
|
||||||
|
import org.jf.dexlib2.builder.MutableMethodImplementation
|
||||||
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction21s
|
||||||
|
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import org.jf.dexlib2.immutable.ImmutableMethod
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
private object InstructionExtensionsTest {
|
||||||
|
private lateinit var testMethod: MutableMethod
|
||||||
|
private lateinit var testMethodImplementation: MutableMethodImplementation
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun createTestMethod() = ImmutableMethod(
|
||||||
|
"TestClass;",
|
||||||
|
"testMethod",
|
||||||
|
null,
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC.value,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
MutableMethodImplementation(16).also { testMethodImplementation = it }.apply {
|
||||||
|
repeat(10) { i -> this.addInstruction(TestInstruction(i)) }
|
||||||
|
},
|
||||||
|
).let { testMethod = it.toMutable() }
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionsToImplementationIndexed() = applyToImplementation {
|
||||||
|
addInstructions(5, getTestInstructions(5..6)).also {
|
||||||
|
assertRegisterIs(5, 5)
|
||||||
|
assertRegisterIs(6, 6)
|
||||||
|
|
||||||
|
assertRegisterIs(5, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionsToImplementation() = applyToImplementation {
|
||||||
|
addInstructions(getTestInstructions(10..11)).also {
|
||||||
|
assertRegisterIs(10, 10)
|
||||||
|
assertRegisterIs(11, 11)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeInstructionsFromImplementationIndexed() = applyToImplementation {
|
||||||
|
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeInstructionsFromImplementation() = applyToImplementation {
|
||||||
|
removeInstructions(0).also { assertRegisterIs(9, 9) }
|
||||||
|
removeInstructions(1).also { assertRegisterIs(1, 0) }
|
||||||
|
removeInstructions(2).also { assertRegisterIs(3, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun replaceInstructionsInImplementationIndexed() = applyToImplementation {
|
||||||
|
replaceInstructions(5, getTestInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
assertRegisterIs(7, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionToMethodIndexed() = applyToMethod {
|
||||||
|
addInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionToMethod() = applyToMethod {
|
||||||
|
addInstruction(TestInstruction(0)).also { assertRegisterIs(0, 10) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addSmaliInstructionToMethodIndexed() = applyToMethod {
|
||||||
|
addInstruction(5, getTestSmaliInstruction(0)).also { assertRegisterIs(0, 5) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addSmaliInstructionToMethod() = applyToMethod {
|
||||||
|
addInstruction(getTestSmaliInstruction(0)).also { assertRegisterIs(0, 10) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionsToMethodIndexed() = applyToMethod {
|
||||||
|
addInstructions(5, getTestInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
|
||||||
|
assertRegisterIs(5, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addInstructionsToMethod() = applyToMethod {
|
||||||
|
addInstructions(getTestInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 10)
|
||||||
|
assertRegisterIs(1, 11)
|
||||||
|
|
||||||
|
assertRegisterIs(9, 9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addSmaliInstructionsToMethodIndexed() = applyToMethod {
|
||||||
|
addInstructionsWithLabels(5, getTestSmaliInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
|
||||||
|
assertRegisterIs(5, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addSmaliInstructionsToMethod() = applyToMethod {
|
||||||
|
addInstructions(getTestSmaliInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 10)
|
||||||
|
assertRegisterIs(1, 11)
|
||||||
|
|
||||||
|
assertRegisterIs(9, 9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addSmaliInstructionsWithExternalLabelToMethodIndexed() = applyToMethod {
|
||||||
|
val label = ExternalLabel("testLabel", getInstruction(5))
|
||||||
|
|
||||||
|
addInstructionsWithLabels(
|
||||||
|
5,
|
||||||
|
getTestSmaliInstructions(0..1).plus("\n").plus("goto :${label.name}"),
|
||||||
|
label
|
||||||
|
).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
assertRegisterIs(5, 8)
|
||||||
|
|
||||||
|
val gotoTarget = getInstruction<BuilderOffsetInstruction>(7)
|
||||||
|
.target.location.instruction as OneRegisterInstruction
|
||||||
|
|
||||||
|
assertEquals(5, gotoTarget.registerA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeInstructionFromMethodIndexed() = applyToMethod {
|
||||||
|
removeInstruction(5).also {
|
||||||
|
assertRegisterIs(4, 4)
|
||||||
|
assertRegisterIs(6, 5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeInstructionsFromMethodIndexed() = applyToMethod {
|
||||||
|
removeInstructions(5, 5).also { assertRegisterIs(4, 4) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun removeInstructionsFromMethod() = applyToMethod {
|
||||||
|
removeInstructions(0).also { assertRegisterIs(9, 9) }
|
||||||
|
removeInstructions(1).also { assertRegisterIs(1, 0) }
|
||||||
|
removeInstructions(2).also { assertRegisterIs(3, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun replaceInstructionInMethodIndexed() = applyToMethod {
|
||||||
|
replaceInstruction(5, TestInstruction(0)).also { assertRegisterIs(0, 5) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun replaceInstructionsInMethodIndexed() = applyToMethod {
|
||||||
|
replaceInstructions(5, getTestInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
assertRegisterIs(7, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun replaceSmaliInstructionsInMethodIndexed() = applyToMethod {
|
||||||
|
replaceInstructions(5, getTestSmaliInstructions(0..1)).also {
|
||||||
|
assertRegisterIs(0, 5)
|
||||||
|
assertRegisterIs(1, 6)
|
||||||
|
assertRegisterIs(7, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Helper methods
|
||||||
|
|
||||||
|
private fun applyToImplementation(block: MutableMethodImplementation.() -> Unit) {
|
||||||
|
testMethodImplementation.apply(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyToMethod(block: MutableMethod.() -> Unit) {
|
||||||
|
testMethod.apply(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableMethodImplementation.assertRegisterIs(register: Int, atIndex: Int) = assertEquals(
|
||||||
|
register, getInstruction<OneRegisterInstruction>(atIndex).registerA
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun MutableMethod.assertRegisterIs(register: Int, atIndex: Int) =
|
||||||
|
implementation!!.assertRegisterIs(register, atIndex)
|
||||||
|
|
||||||
|
private fun getTestInstructions(range: IntRange) = range.map { TestInstruction(it) }
|
||||||
|
|
||||||
|
private fun getTestSmaliInstruction(register: Int) = "const/16 v$register, 0"
|
||||||
|
|
||||||
|
private fun getTestSmaliInstructions(range: IntRange) = range.joinToString("\n") {
|
||||||
|
getTestSmaliInstruction(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
private class TestInstruction(register: Int) : BuilderInstruction21s(Opcode.CONST_16, register, 0)
|
||||||
|
}
|
|
@ -4,9 +4,9 @@ import app.revanced.patcher.annotation.Description
|
||||||
import app.revanced.patcher.annotation.Name
|
import app.revanced.patcher.annotation.Name
|
||||||
import app.revanced.patcher.annotation.Version
|
import app.revanced.patcher.annotation.Version
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.extensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patcher.patch.*
|
import app.revanced.patcher.patch.*
|
||||||
import app.revanced.patcher.patch.annotations.DependsOn
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
import app.revanced.patcher.patch.annotations.Patch
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
@ -119,7 +119,7 @@ class ExampleBytecodePatch : BytecodePatch(listOf(ExampleFingerprint)) {
|
||||||
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
|
// For this sake of example I reuse the TestClass field dummyField inside the virtual register 0.
|
||||||
//
|
//
|
||||||
// Control flow instructions are not supported as of now.
|
// Control flow instructions are not supported as of now.
|
||||||
method.addInstructions(
|
method.addInstructionsWithLabels(
|
||||||
startIndex + 2,
|
startIndex + 2,
|
||||||
"""
|
"""
|
||||||
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
invoke-static { }, LTestClass;->returnHello()Ljava/lang/String;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package app.revanced.patcher.util.smali
|
package app.revanced.patcher.util.smali
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.instruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
import app.revanced.patcher.extensions.label
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.newLabel
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
|
@ -35,7 +36,7 @@ internal class InlineSmaliCompilerTest {
|
||||||
method.addInstructions(arrayOfNulls<String>(insnAmount).also {
|
method.addInstructions(arrayOfNulls<String>(insnAmount).also {
|
||||||
Arrays.fill(it, "const/4 v0, 0x0")
|
Arrays.fill(it, "const/4 v0, 0x0")
|
||||||
}.joinToString("\n"))
|
}.joinToString("\n"))
|
||||||
method.addInstructions(
|
method.addInstructionsWithLabels(
|
||||||
targetIndex,
|
targetIndex,
|
||||||
"""
|
"""
|
||||||
:test
|
:test
|
||||||
|
@ -44,7 +45,7 @@ internal class InlineSmaliCompilerTest {
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
val insn = method.instruction<BuilderInstruction21t>(insnIndex)
|
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
|
||||||
assertEquals(targetIndex, insn.target.location.index)
|
assertEquals(targetIndex, insn.target.location.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,19 +62,19 @@ internal class InlineSmaliCompilerTest {
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(labelIndex, method.label(labelIndex).location.index)
|
assertEquals(labelIndex, method.newLabel(labelIndex).location.index)
|
||||||
|
|
||||||
method.addInstructions(
|
method.addInstructionsWithLabels(
|
||||||
|
method.implementation!!.instructions.size,
|
||||||
"""
|
"""
|
||||||
const/4 v0, 0x1
|
const/4 v0, 0x1
|
||||||
if-eqz v0, :test
|
if-eqz v0, :test
|
||||||
return-void
|
return-void
|
||||||
""", listOf(
|
""",
|
||||||
ExternalLabel("test",method.instruction(1))
|
ExternalLabel("test", method.getInstruction(1))
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val insn = method.instruction<BuilderInstruction21t>(insnIndex)
|
val insn = method.getInstruction<BuilderInstruction21t>(insnIndex)
|
||||||
assertTrue(insn.target.isPlaced, "Label was not placed")
|
assertTrue(insn.target.isPlaced, "Label was not placed")
|
||||||
assertEquals(labelIndex, insn.target.location.index)
|
assertEquals(labelIndex, insn.target.location.index)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue