Shamrock/xposed/src/main/java/moe/fuqiuluo/remote/api/GenerateQSign.kt

241 lines
9.0 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package moe.fuqiuluo.remote.api
import com.tencent.mobileqq.qsec.qsecdandelionsdk.Dandelion
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.response.respond
import io.ktor.server.routing.Routing
import io.ktor.server.routing.get
import io.ktor.server.routing.post
import io.ktor.server.routing.route
import io.ktor.util.pipeline.PipelineContext
import kotlinx.serialization.Serializable
import moe.fuqiuluo.remote.entries.EmptyObject
import moe.fuqiuluo.remote.entries.Status
import moe.fuqiuluo.xposed.ipc.IQSigner
import moe.fuqiuluo.xposed.ipc.ShamrockIpc
import moe.fuqiuluo.xposed.tools.fetchGetOrNull
import moe.fuqiuluo.xposed.tools.fetchGetOrThrow
import moe.fuqiuluo.xposed.tools.fetchPostOrNull
import moe.fuqiuluo.xposed.tools.fetchPostOrThrow
import moe.fuqiuluo.xposed.tools.hex2ByteArray
import moe.fuqiuluo.xposed.tools.respond
import moe.fuqiuluo.xposed.tools.toHexString
import java.nio.ByteBuffer
fun Routing.qsign() {
route("/sign") {
get {
val uin = fetchGetOrThrow("uin")
val cmd = fetchGetOrThrow("cmd")
val seq = fetchGetOrThrow("seq").toInt()
val buffer = fetchGetOrThrow("buffer").hex2ByteArray()
requestSign(cmd, uin, seq, buffer)
}
post {
val uin = fetchPostOrThrow("uin")
val cmd = fetchPostOrThrow("cmd")
val seq = fetchPostOrThrow("seq").toInt()
val buffer = fetchPostOrThrow("buffer").hex2ByteArray()
requestSign(cmd, uin, seq, buffer)
}
}
get("/custom_energy") {
val data = fetchGetOrThrow("data")
val salt = fetchGetOrThrow("salt").hex2ByteArray()
val sign = Dandelion.getInstance().fly(data, salt)
call.respond(OldApiResult(0, "success", sign.toHexString()))
}
route("/energy") {
get {
val data = fetchGetOrThrow("data")
if(!(data.startsWith("810_") || data.startsWith("812_"))) {
call.respond(OldApiResult(-2, "data参数不合法", null))
return@get
}
var mode = fetchGetOrNull("mode")
if (mode == null) {
mode = when(data) {
"810_d", "810_a", "810_f", "810_9" -> "v2"
"810_2", "810_25", "810_7", "810_24" -> "v1"
"812_a" -> "v3"
"812_5" -> "v4"
else -> null
}
}
if (mode == null) {
call.respond(OldApiResult(-2, "无法自动决断mode请主动提供", null))
return@get
}
val salt = when (mode) {
"v1" -> {
val uin = fetchGetOrThrow("uin").toLong()
val version = fetchGetOrThrow("version")
val guid = fetchGetOrThrow("guid").hex2ByteArray()
val salt = ByteBuffer.allocate(8 + 2 + guid.size + 2 + 10 + 4)
val sub = data.substring(4).toInt(16)
salt.putLong(uin)
salt.putShort(guid.size.toShort())
salt.put(guid)
salt.putShort(version.length.toShort())
salt.put(version.toByteArray())
salt.putInt(sub)
salt.array()
}
"v2" -> {
val version = fetchGetOrThrow("version")
val guid = fetchGetOrThrow("guid").hex2ByteArray()
val sub = data.substring(4).toInt(16)
val salt = ByteBuffer.allocate(4 + 2 + guid.size + 2 + 10 + 4 + 4)
salt.putInt(0)
salt.putShort(guid.size.toShort())
salt.put(guid)
salt.putShort(version.length.toShort())
salt.put(version.toByteArray())
salt.putInt(sub)
salt.putInt(0)
salt.array()
}
"v3" -> { // 812_a
val version = fetchGetOrThrow("version")
val phone = fetchGetOrThrow("phone").toByteArray() // 86-xxx
val salt = ByteBuffer.allocate(phone.size + 2 + 2 + version.length + 2)
// 38 36 2D 31 37 33 36 30 32 32 39 31 37 32
// 00 00
// 00 06
// 38 2E 39 2E 33 38
// 00 00
// result => 0C051B17347DF3B8EFDE849FC233C88DBEA23F5277099BB313A9CD000000004B744F7A00000000
salt.put(phone)
//println(String(phone))
salt.putShort(0)
salt.putShort(version.length.toShort())
salt.put(version.toByteArray())
salt.putShort(0)
salt.array()
}
"v4" -> { // 812_5
error("Not support [v4] mode.")
}
else -> ByteArray(0)
}
val sign = Dandelion.getInstance().fly(data, salt)
call.respond(OldApiResult(0, "success", sign.toHexString()))
}
post {
val data = fetchPostOrThrow("data")
if(!(data.startsWith("810_") || data.startsWith("812_"))) {
call.respond(OldApiResult(-2, "data参数不合法", null))
return@post
}
var mode = fetchPostOrNull("mode")
if (mode == null) {
mode = when(data) {
"810_d", "810_a", "810_f", "810_9" -> "v2"
"810_2", "810_25", "810_7", "810_24" -> "v1"
"812_a" -> "v3"
"812_5" -> "v4"
else -> null
}
}
if (mode == null) {
call.respond(OldApiResult(-2, "无法自动决断mode请主动提供", null))
return@post
}
val salt = when (mode) {
"v1" -> {
val uin = fetchPostOrThrow("uin").toLong()
val version = fetchPostOrThrow("version")
val guid = fetchPostOrThrow("guid").hex2ByteArray()
val salt = ByteBuffer.allocate(8 + 2 + guid.size + 2 + 10 + 4)
val sub = data.substring(4).toInt(16)
salt.putLong(uin)
salt.putShort(guid.size.toShort())
salt.put(guid)
salt.putShort(version.length.toShort())
salt.put(version.toByteArray())
salt.putInt(sub)
salt.array()
}
"v2" -> {
val version = fetchPostOrThrow("version")
val guid = fetchPostOrThrow("guid").hex2ByteArray()
val sub = data.substring(4).toInt(16)
val salt = ByteBuffer.allocate(4 + 2 + guid.size + 2 + 10 + 4 + 4)
salt.putInt(0)
salt.putShort(guid.size.toShort())
salt.put(guid)
salt.putShort(version.length.toShort())
salt.put(version.toByteArray())
salt.putInt(sub)
salt.putInt(0)
salt.array()
}
"v3" -> { // 812_a
val version = fetchPostOrThrow("version")
val phone = fetchPostOrThrow("phone").toByteArray() // 86-xxx
val salt = ByteBuffer.allocate(phone.size + 2 + 2 + version.length + 2)
salt.put(phone)
//println(String(phone))
salt.putShort(0)
salt.putShort(version.length.toShort())
salt.put(version.toByteArray())
salt.putShort(0)
salt.array()
}
"v4" -> { // 812_5
error("Not support [v4] mode.")
}
else -> ByteArray(0)
}
val sign = Dandelion.getInstance().fly(data, salt)
call.respond(OldApiResult(0, "success", sign.toHexString()))
}
}
}
@Serializable
private data class Sign(
val token: String,
val extra: String,
val sign: String,
val o3did: String,
val requestCallback: List<Int>
)
private lateinit var signer: IQSigner
private suspend fun PipelineContext<Unit, ApplicationCall>.requestSign(
cmd: String,
uin: String,
seq: Int,
buffer: ByteArray,
) {
if (!::signer.isInitialized) {
val binder = ShamrockIpc.get(ShamrockIpc.IPC_QSIGN)
if (binder == null) {
respond(false, Status.InternalHandlerError, EmptyObject)
return
} else {
signer = IQSigner.Stub.asInterface(binder)
}
}
val sign = signer.sign(cmd, seq, uin, buffer)
call.respond(OldApiResult(0, "success", Sign(
sign.token.toHexString(),
sign.extra.toHexString(),
sign.sign.toHexString(), "", listOf()
)))
}