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
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 392 additions and 80 deletions

View file

@ -0,0 +1,206 @@
package app.revanced.patches.all.connectivity.wifi.spoof.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.util.patch.*
import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.Instruction
import java.util.*
@Patch(false)
@Name("spoof-wifi-connection")
@Description("Spoofs an existing Wi-Fi connection.")
@Version("0.0.1")
internal class SpoofWifiPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
private companion object {
const val INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX = "Lapp/revanced/all/connectivity/wifi/spoof/SpoofWifiPatch"
const val INTEGRATIONS_CLASS_DESCRIPTOR = "${INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX};"
}
// Information about method calls we want to replace
enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String,
): IMethodCall {
GetSystemService1(
"Landroid/content/Context;",
"getSystemService",
arrayOf("Ljava/lang/String;"),
"Ljava/lang/Object;",
),
GetSystemService2(
"Landroid/content/Context;",
"getSystemService",
arrayOf("Ljava/lang/Class;"),
"Ljava/lang/Object;",
),
GetActiveNetworkInfo(
"Landroid/net/ConnectivityManager;",
"getActiveNetworkInfo",
arrayOf(),
"Landroid/net/NetworkInfo;",
),
IsConnected(
"Landroid/net/NetworkInfo;",
"isConnected",
arrayOf(),
"Z",
),
IsConnectedOrConnecting(
"Landroid/net/NetworkInfo;",
"isConnectedOrConnecting",
arrayOf(),
"Z",
),
IsAvailable(
"Landroid/net/NetworkInfo;",
"isAvailable",
arrayOf(),
"Z",
),
GetState(
"Landroid/net/NetworkInfo;",
"getState",
arrayOf(),
"Landroid/net/NetworkInfo\$State;",
),
GetDetailedState(
"Landroid/net/NetworkInfo;",
"getDetailedState",
arrayOf(),
"Landroid/net/NetworkInfo\$DetailedState;",
),
IsActiveNetworkMetered(
"Landroid/net/ConnectivityManager;",
"isActiveNetworkMetered",
arrayOf(),
"Z",
),
GetActiveNetwork(
"Landroid/net/ConnectivityManager;",
"getActiveNetwork",
arrayOf(),
"Landroid/net/Network;",
),
GetNetworkInfo(
"Landroid/net/ConnectivityManager;",
"getNetworkInfo",
arrayOf("Landroid/net/Network;"),
"Landroid/net/NetworkInfo;",
),
HasTransport(
"Landroid/net/NetworkCapabilities;",
"hasTransport",
arrayOf("I"),
"Z",
),
HasCapability(
"Landroid/net/NetworkCapabilities;",
"hasCapability",
arrayOf("I"),
"Z",
),
RegisterBestMatchingNetworkCallback(
"Landroid/net/ConnectivityManager;",
"registerBestMatchingNetworkCallback",
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;"),
"V",
),
RegisterDefaultNetworkCallback1(
"Landroid/net/ConnectivityManager;",
"registerDefaultNetworkCallback",
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
RegisterDefaultNetworkCallback2(
"Landroid/net/ConnectivityManager;",
"registerDefaultNetworkCallback",
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;"),
"V",
),
RegisterNetworkCallback1(
"Landroid/net/ConnectivityManager;",
"registerNetworkCallback",
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
RegisterNetworkCallback2(
"Landroid/net/ConnectivityManager;",
"registerNetworkCallback",
arrayOf("Landroid/net/NetworkRequest;", "Landroid/app/PendingIntent;"),
"V",
),
RegisterNetworkCallback3(
"Landroid/net/ConnectivityManager;",
"registerNetworkCallback",
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;"),
"V",
),
RequestNetwork1(
"Landroid/net/ConnectivityManager;",
"requestNetwork",
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
RequestNetwork2(
"Landroid/net/ConnectivityManager;",
"requestNetwork",
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "I"),
"V",
),
RequestNetwork3(
"Landroid/net/ConnectivityManager;",
"requestNetwork",
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;"),
"V",
),
RequestNetwork4(
"Landroid/net/ConnectivityManager;",
"requestNetwork",
arrayOf("Landroid/net/NetworkRequest;", "Landroid/app/PendingIntent;"),
"V",
),
RequestNetwork5(
"Landroid/net/ConnectivityManager;",
"requestNetwork",
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;", "I"),
"V",
),
UnregisterNetworkCallback1(
"Landroid/net/ConnectivityManager;",
"unregisterNetworkCallback",
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
UnregisterNetworkCallback2(
"Landroid/net/ConnectivityManager;",
"unregisterNetworkCallback",
arrayOf("Landroid/app/PendingIntent;"),
"V",
);
}
override fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int
) = filterMapInstruction35c<MethodCall>(
INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX,
classDef,
instruction,
instructionIndex
)
override fun transform(mutableMethod: MutableMethod, entry: Instruction35cInfo) {
val (methodType, instruction, instructionIndex) = entry
methodType.replaceInvokeVirtualWithIntegrations(INTEGRATIONS_CLASS_DESCRIPTOR, mutableMethod, instruction, instructionIndex)
}
}

View file

@ -1,106 +1,57 @@
package app.revanced.patches.all.screenshot.removerestriction.patch
import app.revanced.extensions.findMutableMethodOf
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
import org.jf.dexlib2.iface.reference.MethodReference
import app.revanced.util.patch.*
import org.jf.dexlib2.iface.ClassDef
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.Instruction
import java.util.*
@Patch(false)
@Name("remove-screenshot-restriction")
@Description("Removes the restriction of taking screenshots in apps that normally wouldn't allow it.")
@Version("0.0.1")
class RemoveScreenshotRestrictionPatch : BytecodePatch() {
internal class RemoveScreenshotRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
private companion object {
const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch;"
const val INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch"
const val INTEGRATIONS_CLASS_DESCRIPTOR = "$INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX;"
}
// Information about method calls we want to replace
private enum class MethodCall(
val definedClassName: String,
val methodName: String,
val replacementMethodDefinition: String
) {
enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String
): IMethodCall {
SetFlags(
"Landroid/view/Window;",
"setFlags",
"setFlags(Landroid/view/Window;II)V",
arrayOf("I", "I"),
"V",
);
}
fun replaceInstruction(method: MutableMethod, instruction: Instruction35c, instructionIndex: Int) {
when (this) {
SetFlags -> {
method.replaceInstruction(
instructionIndex,
"invoke-static { v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE} }, ${INTEGRATIONS_CLASS_DESCRIPTOR}->${replacementMethodDefinition}"
override fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int
) = filterMapInstruction35c<MethodCall>(
INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX,
classDef,
instruction,
instructionIndex
)
}
}
}
companion object {
fun fromMethodReference(methodReference: MethodReference) = values().firstOrNull { search ->
search.definedClassName == methodReference.definingClass && search.methodName == methodReference.name
}
}
}
override fun execute(context: BytecodeContext): PatchResult {
// Find all instructions where one of the methods is called
buildMap {
context.classes.forEach { classDef ->
if (classDef.type == INTEGRATIONS_CLASS_DESCRIPTOR) {
// avoid infinite recursion
return@forEach
}
classDef.methods.let { methods ->
buildMap methodList@{
methods.forEach methods@{ method ->
with(method.implementation?.instructions ?: return@methods) {
ArrayDeque<Triple<MethodCall, Instruction35c, Int>>().also { patchIndices ->
this.forEachIndexed { index, instruction ->
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@forEachIndexed
val invokeInstruction = instruction as Instruction35c
val methodRef = invokeInstruction.reference as MethodReference
val methodCall = MethodCall.fromMethodReference(methodRef) ?: return@forEachIndexed
patchIndices.add(Triple(methodCall, invokeInstruction, index))
}
}.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 replace the instructions...
with(context.proxy(classDef).mutableClass) {
methods.forEach { (method, patches) ->
val mutableMethod = findMutableMethodOf(method)
while (!patches.isEmpty()) {
val (methodType, instruction, instructionIndex) = patches.removeLast()
methodType.replaceInstruction(mutableMethod, instruction, instructionIndex)
}
}
}
}
return PatchResultSuccess()
override fun transform(mutableMethod: MutableMethod, entry: Instruction35cInfo) {
val (methodType, instruction, instructionIndex) = entry
methodType.replaceInvokeVirtualWithIntegrations(INTEGRATIONS_CLASS_DESCRIPTOR, mutableMethod, instruction, instructionIndex)
}
}

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