feat: spoof-wifi-connection patch (#1527)

Signed-off-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: Linus789 <Linus789@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
Linus
2023-02-01 19:52:41 +00:00
committed by GitHub
parent f8738a7292
commit adce206d66
4 changed files with 392 additions and 80 deletions

View File

@ -0,0 +1,63 @@
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.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.Instruction
internal abstract class AbstractTransformInstructionsPatch<T> : BytecodePatch() {
abstract fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int
): T?
abstract fun transform(mutableMethod: MutableMethod, entry: T)
override fun execute(context: BytecodeContext): PatchResult {
// Find all instructions
buildMap {
context.classes.forEach { classDef ->
classDef.methods.let { methods ->
buildMap methodList@{
methods.forEach methods@{ method ->
with(method.implementation?.instructions ?: return@methods) {
ArrayDeque<T>().also { patchIndices ->
this.forEachIndexed { index, instruction ->
val result = filterMap(classDef, method, instruction, index)
if (result != null) {
patchIndices.add(result)
}
}
}.also { if (it.isEmpty()) return@methods }.let { patches ->
put(method, patches)
}
}
}
}
}.also { if (it.isEmpty()) return@forEach }.let { methodPatches ->
put(classDef, methodPatches)
}
}
}.forEach { (classDef, methods) ->
// And finally transform the instructions...
with(context.proxy(classDef).mutableClass) {
methods.forEach { (method, patches) ->
val mutableMethod = findMutableMethodOf(method)
while (!patches.isEmpty()) {
transform(mutableMethod, patches.removeLast())
}
}
}
}
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,92 @@
package app.revanced.util.patch
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
import org.jf.dexlib2.iface.reference.MethodReference
internal typealias Instruction35cInfo = Triple<IMethodCall, Instruction35c, Int>
internal 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}"
)
}
}
internal 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)
}
internal 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)
}