mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 13:12:17 +08:00
203 lines
8.2 KiB
Kotlin
203 lines
8.2 KiB
Kotlin
@file:OptIn(DelicateCoroutinesApi::class)
|
|
|
|
package moe.fuqiuluo.qqinterface.servlet
|
|
|
|
import android.os.Bundle
|
|
import com.tencent.mobileqq.app.QQAppInterface
|
|
import com.tencent.mobileqq.msf.core.MsfCore
|
|
import com.tencent.mobileqq.msf.service.MsfService
|
|
import com.tencent.mobileqq.pb.ByteStringMicro
|
|
import com.tencent.qphone.base.remote.ToServiceMsg
|
|
import com.tencent.qqnt.kernel.api.IKernelService
|
|
import io.ktor.utils.io.core.BytePacketBuilder
|
|
import io.ktor.utils.io.core.readBytes
|
|
import io.ktor.utils.io.core.writeFully
|
|
import io.ktor.utils.io.core.writeInt
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
import kotlinx.coroutines.withTimeoutOrNull
|
|
import kotlinx.serialization.encodeToByteArray
|
|
|
|
import moe.fuqiuluo.shamrock.tools.slice
|
|
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
|
import moe.fuqiuluo.shamrock.utils.PlatformUtils
|
|
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
|
import moe.fuqiuluo.shamrock.xposed.helper.internal.DynamicReceiver
|
|
import moe.fuqiuluo.shamrock.xposed.helper.internal.IPCRequest
|
|
import protobuf.oidb.TrpcOidb
|
|
import mqq.app.MobileQQ
|
|
import protobuf.auto.toByteArray
|
|
import tencent.im.oidb.oidb_sso
|
|
import kotlin.coroutines.CoroutineContext
|
|
import kotlin.coroutines.resume
|
|
|
|
internal abstract class BaseSvc {
|
|
companion object Default: CoroutineScope {
|
|
val currentUin: String
|
|
get() = app.currentAccountUin
|
|
|
|
val app: QQAppInterface
|
|
get() = AppRuntimeFetcher.appRuntime as QQAppInterface
|
|
|
|
fun createToServiceMsg(cmd: String): ToServiceMsg {
|
|
return ToServiceMsg("mobileqq.service", app.currentAccountUin, cmd)
|
|
}
|
|
|
|
suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray, trpc: Boolean = false, timeout: Long = 5000L): ByteArray? {
|
|
val seq = MsfService.getCore().nextSeq
|
|
val buffer = withTimeoutOrNull(timeout) {
|
|
suspendCancellableCoroutine { continuation ->
|
|
launch(Dispatchers.Default) {
|
|
DynamicReceiver.register(IPCRequest(cmd, seq) {
|
|
val buffer = it.getByteArrayExtra("buffer")!!
|
|
continuation.resume(buffer)
|
|
})
|
|
}
|
|
if (trpc) sendTrpcOidb(cmd, cmdId, serviceId, data, seq)
|
|
else sendOidb(cmd, cmdId, serviceId, data, seq)
|
|
}
|
|
}.also {
|
|
if (it == null)
|
|
DynamicReceiver.unregister(seq)
|
|
}?.copyOf()
|
|
try {
|
|
if (buffer != null && buffer.size >= 5 && buffer[4] == 120.toByte()) {
|
|
val builder = BytePacketBuilder()
|
|
val deBuffer = DeflateTools.uncompress(buffer.slice(4))
|
|
builder.writeInt(deBuffer.size)
|
|
builder.writeFully(deBuffer)
|
|
return builder.build().readBytes()
|
|
}
|
|
} catch (_: Exception) { }
|
|
return buffer
|
|
}
|
|
|
|
suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray, timeout: Long = 5000L): ByteArray? {
|
|
val seq = MsfService.getCore().nextSeq
|
|
val buffer = withTimeoutOrNull<ByteArray?>(timeout) {
|
|
suspendCancellableCoroutine { continuation ->
|
|
launch(Dispatchers.Default) {
|
|
DynamicReceiver.register(IPCRequest(cmd, seq) {
|
|
val buffer = it.getByteArrayExtra("buffer")!!
|
|
continuation.resume(buffer)
|
|
})
|
|
sendBuffer(cmd, isPb, data, seq)
|
|
}
|
|
}
|
|
}.also {
|
|
if (it == null)
|
|
DynamicReceiver.unregister(seq)
|
|
}?.copyOf()
|
|
try {
|
|
if (buffer != null && buffer.size >= 5 && buffer[4] == 120.toByte()) {
|
|
val builder = BytePacketBuilder()
|
|
val deBuffer = DeflateTools.uncompress(buffer.slice(4))
|
|
builder.writeInt(deBuffer.size)
|
|
builder.writeFully(deBuffer)
|
|
return builder.build().readBytes()
|
|
}
|
|
} catch (_: Exception) { }
|
|
return buffer
|
|
}
|
|
|
|
fun sendOidb(cmd: String, cmdId: Int, serviceId: Int, buffer: ByteArray, seq: Int = -1, trpc: Boolean = false) {
|
|
if (trpc) {
|
|
sendTrpcOidb(cmd, cmdId, serviceId, buffer, seq)
|
|
return
|
|
}
|
|
val to = createToServiceMsg(cmd)
|
|
val oidb = oidb_sso.OIDBSSOPkg()
|
|
oidb.uint32_command.set(cmdId)
|
|
oidb.uint32_service_type.set(serviceId)
|
|
oidb.bytes_bodybuffer.set(ByteStringMicro.copyFrom(buffer))
|
|
oidb.str_client_version.set(PlatformUtils.getClientVersion(MobileQQ.getContext()))
|
|
to.putWupBuffer(oidb.toByteArray())
|
|
to.addAttribute("req_pb_protocol_flag", true)
|
|
if (seq != -1) {
|
|
to.addAttribute("shamrock_seq", seq)
|
|
}
|
|
app.sendToService(to)
|
|
}
|
|
|
|
fun sendTrpcOidb(cmd: String, cmdId: Int, serviceId: Int, buffer: ByteArray, seq: Int = -1) {
|
|
val to = createToServiceMsg(cmd)
|
|
|
|
val oidb = TrpcOidb(
|
|
cmd = cmdId,
|
|
service = serviceId,
|
|
buffer = buffer,
|
|
flag = 1
|
|
)
|
|
to.putWupBuffer(oidb.toByteArray())
|
|
|
|
to.addAttribute("req_pb_protocol_flag", true)
|
|
if (seq != -1) {
|
|
to.addAttribute("shamrock_seq", seq)
|
|
}
|
|
app.sendToService(to)
|
|
}
|
|
|
|
fun sendBuffer(cmd: String, isPb: Boolean, buffer: ByteArray, seq: Int = MsfService.getCore().nextSeq) {
|
|
val toServiceMsg = ToServiceMsg("mobileqq.service", app.currentUin, cmd)
|
|
toServiceMsg.putWupBuffer(buffer)
|
|
toServiceMsg.addAttribute("req_pb_protocol_flag", isPb)
|
|
toServiceMsg.addAttribute("shamrock_seq", seq)
|
|
app.sendToService(toServiceMsg)
|
|
}
|
|
|
|
@OptIn(ExperimentalCoroutinesApi::class)
|
|
override val coroutineContext: CoroutineContext by lazy {
|
|
Dispatchers.IO.limitedParallelism(12)
|
|
}
|
|
}
|
|
|
|
protected fun send(toServiceMsg: ToServiceMsg) {
|
|
app.sendToService(toServiceMsg)
|
|
}
|
|
|
|
protected suspend fun sendAW(toServiceMsg: ToServiceMsg, timeout: Long = 5000L): ByteArray? {
|
|
val seq = MsfService.getCore().nextSeq
|
|
val buffer = withTimeoutOrNull<ByteArray?>(timeout) {
|
|
suspendCancellableCoroutine { continuation ->
|
|
launch(Dispatchers.Default) {
|
|
DynamicReceiver.register(IPCRequest(toServiceMsg.serviceCmd, seq) {
|
|
val buffer = it.getByteArrayExtra("buffer")!!
|
|
continuation.resume(buffer)
|
|
})
|
|
toServiceMsg.addAttribute("shamrock_seq", seq)
|
|
send(toServiceMsg)
|
|
}
|
|
}
|
|
}.also {
|
|
if (it == null) DynamicReceiver.unregister(seq)
|
|
}?.copyOf()
|
|
try {
|
|
if (buffer != null && buffer.size >= 5 && buffer[4] == 120.toByte()) {
|
|
val builder = BytePacketBuilder()
|
|
val deBuffer = DeflateTools.uncompress(buffer.slice(4))
|
|
builder.writeInt(deBuffer.size)
|
|
builder.writeFully(deBuffer)
|
|
return builder.build().readBytes()
|
|
}
|
|
} catch (_: Exception) { }
|
|
return buffer
|
|
}
|
|
|
|
protected fun sendExtra(cmd: String, builder: (Bundle) -> Unit) {
|
|
val toServiceMsg = createToServiceMsg(cmd)
|
|
builder(toServiceMsg.extraData)
|
|
app.sendToService(toServiceMsg)
|
|
}
|
|
|
|
protected fun sendPb(cmd: String, buffer: ByteArray, seq: Int) {
|
|
val toServiceMsg = createToServiceMsg(cmd)
|
|
toServiceMsg.putWupBuffer(buffer)
|
|
toServiceMsg.addAttribute("req_pb_protocol_flag", true)
|
|
toServiceMsg.addAttribute("shamrock_seq", seq)
|
|
app.sendToService(toServiceMsg)
|
|
}
|
|
} |