feat: Publish as a library (#3356)

This commit is contained in:
oSumAtrIX 2023-12-02 22:35:13 +01:00 committed by GitHub
parent 1f1cae12e4
commit 4b878eeeda
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
500 changed files with 2369 additions and 719 deletions

View file

@ -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())
}
}
}
}
}

View file

@ -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,

View file

@ -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)
}