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:
parent
f8738a7292
commit
adce206d66
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
92
src/main/kotlin/app/revanced/util/patch/MethodCall.kt
Normal file
92
src/main/kotlin/app/revanced/util/patch/MethodCall.kt
Normal 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)
|
||||
}
|
Loading…
Reference in a new issue