feat: Publish as a library (#3356)
This commit is contained in:
parent
1f1cae12e4
commit
4b878eeeda
500 changed files with 2369 additions and 719 deletions
|
|
@ -1,64 +0,0 @@
|
|||
package app.revanced.util.patch
|
||||
|
||||
import app.revanced.extensions.findMutableMethodOf
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
|
||||
abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch() {
|
||||
|
||||
abstract fun filterMap(
|
||||
classDef: ClassDef,
|
||||
method: Method,
|
||||
instruction: Instruction,
|
||||
instructionIndex: Int
|
||||
): T?
|
||||
|
||||
abstract fun transform(mutableMethod: MutableMethod, entry: T)
|
||||
|
||||
// Returns the patch indices as a Sequence, which will execute lazily.
|
||||
private fun findPatchIndices(classDef: ClassDef, method: Method): Sequence<T>? {
|
||||
return method.implementation?.instructions?.asSequence()?.withIndex()?.mapNotNull { (index, instruction) ->
|
||||
filterMap(classDef, method, instruction, index)
|
||||
}
|
||||
}
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// Find all methods to patch
|
||||
buildMap {
|
||||
context.classes.forEach { classDef ->
|
||||
val methods = buildList {
|
||||
classDef.methods.forEach { method ->
|
||||
// Since the Sequence executes lazily,
|
||||
// using any() results in only calling
|
||||
// filterMap until the first index has been found.
|
||||
val patchIndices = findPatchIndices(classDef, method)
|
||||
|
||||
if (patchIndices?.any() == true) {
|
||||
add(method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (methods.isNotEmpty()) {
|
||||
put(classDef, methods)
|
||||
}
|
||||
}
|
||||
}.forEach { (classDef, methods) ->
|
||||
// And finally transform the methods...
|
||||
val mutableClass = context.proxy(classDef).mutableClass
|
||||
|
||||
methods.map(mutableClass::findMutableMethodOf).forEach methods@{ mutableMethod ->
|
||||
val patchIndices = findPatchIndices(mutableClass, mutableMethod)?.toCollection(ArrayDeque())
|
||||
?: return@methods
|
||||
|
||||
while (!patchIndices.isEmpty()) {
|
||||
transform(mutableMethod, patchIndices.removeLast())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,19 @@
|
|||
package app.revanced.util.patch
|
||||
|
||||
import app.revanced.extensions.containsWideLiteralInstructionValue
|
||||
import app.revanced.util.containsWideLiteralInstructionValue
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* A fingerprint to resolve methods that contain a specific literal value.
|
||||
*
|
||||
* @param returnType The method's return type compared using String.startsWith.
|
||||
* @param accessFlags The method's exact access flags using values of AccessFlags.
|
||||
* @param parameters The parameters of the method. Partial matches allowed and follow the same rules as returnType.
|
||||
* @param opcodes An opcode pattern of the method's instructions. Wildcard or unknown opcodes can be specified by null.
|
||||
* @param strings A list of the method's strings compared each using String.contains.
|
||||
* @param literalSupplier A supplier for the literal value to check for.
|
||||
*/
|
||||
abstract class LiteralValueFingerprint(
|
||||
returnType: String? = null,
|
||||
accessFlags: Int? = null,
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
package app.revanced.util.patch
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.ClassDef
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
typealias Instruction35cInfo = Triple<IMethodCall, Instruction35c, Int>
|
||||
|
||||
interface IMethodCall {
|
||||
val definedClassName: String
|
||||
val methodName: String
|
||||
val methodParams: Array<String>
|
||||
val returnType: String
|
||||
|
||||
/**
|
||||
* Replaces an invoke-virtual instruction with an invoke-static instruction,
|
||||
* which calls a static replacement method in the respective integrations class.
|
||||
* The method definition in the integrations class is expected to be the same,
|
||||
* except that the method should be static and take as a first parameter
|
||||
* an instance of the class, in which the original method was defined in.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* original method: Window#setFlags(int, int)
|
||||
*
|
||||
* replacement method: Integrations#setFlags(Window, int, int)
|
||||
*/
|
||||
fun replaceInvokeVirtualWithIntegrations(
|
||||
definingClassDescriptor: String,
|
||||
method: MutableMethod,
|
||||
instruction: Instruction35c,
|
||||
instructionIndex: Int
|
||||
) {
|
||||
val registers = arrayOf(
|
||||
instruction.registerC,
|
||||
instruction.registerD,
|
||||
instruction.registerE,
|
||||
instruction.registerF,
|
||||
instruction.registerG
|
||||
)
|
||||
val argsNum = methodParams.size + 1 // + 1 for instance of definedClassName
|
||||
if (argsNum > registers.size) {
|
||||
// should never happen, but just to be sure (also for the future) a safety check
|
||||
throw RuntimeException(
|
||||
"Not enough registers for ${definedClassName}#${methodName}: " +
|
||||
"Required $argsNum registers, but only got ${registers.size}."
|
||||
)
|
||||
}
|
||||
|
||||
val args = registers.take(argsNum).joinToString(separator = ", ") { reg -> "v${reg}" }
|
||||
val replacementMethodDefinition =
|
||||
"${methodName}(${definedClassName}${methodParams.joinToString(separator = "")})${returnType}"
|
||||
|
||||
method.replaceInstruction(
|
||||
instructionIndex,
|
||||
"invoke-static { $args }, ${definingClassDescriptor}->${replacementMethodDefinition}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified E> fromMethodReference(methodReference: MethodReference)
|
||||
where E : Enum<E>, E : IMethodCall = enumValues<E>().firstOrNull { search ->
|
||||
search.definedClassName == methodReference.definingClass
|
||||
&& search.methodName == methodReference.name
|
||||
&& methodReference.parameterTypes.toTypedArray().contentEquals(search.methodParams)
|
||||
&& search.returnType == methodReference.returnType
|
||||
}
|
||||
|
||||
inline fun <reified E> filterMapInstruction35c(
|
||||
integrationsClassDescriptorPrefix: String,
|
||||
classDef: ClassDef,
|
||||
instruction: Instruction,
|
||||
instructionIndex: Int
|
||||
): Instruction35cInfo? where E : Enum<E>, E : IMethodCall {
|
||||
if (classDef.type.startsWith(integrationsClassDescriptorPrefix)) {
|
||||
// avoid infinite recursion
|
||||
return null
|
||||
}
|
||||
|
||||
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) {
|
||||
return null
|
||||
}
|
||||
|
||||
val invokeInstruction = instruction as Instruction35c
|
||||
val methodRef = invokeInstruction.reference as MethodReference
|
||||
val methodCall = fromMethodReference<E>(methodRef) ?: return null
|
||||
|
||||
return Instruction35cInfo(methodCall, invokeInstruction, instructionIndex)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue