Skip to content

Commit

Permalink
http
Browse files Browse the repository at this point in the history
  • Loading branch information
MrXiaoM committed Jan 7, 2024
1 parent 3bf73a5 commit e3b897a
Show file tree
Hide file tree
Showing 17 changed files with 656 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Build Package
uses: gradle/gradle-build-action@v2
with:
arguments: deploy -Pdev.sha=${{ env.SHORT_SHA }}
arguments: deploy distZip -Pdev.sha=${{ env.SHORT_SHA }}
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
Expand Down
42 changes: 40 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,38 @@

本插件**自带**协议信息,在加载时将会应用与签名服务版本相同的设备信息文件。

# 非未来使用方式

1.3.0 起,恢复了原 http 接口。

[瑞丽斯](https://github.com/MrXiaoM/qsign/releases) 下载 `http-x.x.x.zip`,解压后文件夹结构大致情况如下
```
|-bin
|-lib
|-txlib
|-8.9.58
|-8.9.63
|-android_phone.json
|-android_pad.json
|-config.json
|-dtconfig.json
|-libfekit.so
|-libQSec.so
|-8.9.68
|-8.9.70
|-8.9.71
|-8.9.73
|-8.9.76
|-8.9.78
|-8.9.80
|-8.9.83
```
打开`cmd``终端`,执行以下命令即可启动快信号服务
```shell
java -cp lib/* MainKt --basePath=txlib/8.9.80
```
如需更换协议版本,请自行替换命令中的`8.9.80`

# 不支持特缪斯

特缪斯请使用 [bak-fpv](https://github.com/MrXiaoM/qsign/tree/bak-fpv),并在电脑上搭建签名服务,通过局域网连接签名服务。
Expand Down Expand Up @@ -158,10 +190,16 @@ dependencies {
# 编译

编译插件
```
```shell
./gradlew buildPlugin
```
打包发布 (将会在项目目录生成 `qsign-x.x.x-all.zip`)
```
```shell
./gradlew deploy
```
打包http服务 (将会在 `http/build/distributions/` 生成 `http-x.x.x.zip`)
```shell
./gradlew distZip
```

为这[一切](https://wiki.mrxiaom.top/mirai/sign.html)画上一个逗号,
87 changes: 87 additions & 0 deletions http/src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import com.tencent.mobileqq.dt.model.FEBound
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.json.Json
import moe.fuqiuluo.api.*
import moe.fuqiuluo.comm.CommonConfigExt
import moe.fuqiuluo.comm.QSignConfig
import moe.fuqiuluo.comm.checkIllegal
import moe.fuqiuluo.comm.invoke
import java.io.File

private val API_LIST = arrayOf(
Routing::index,
Routing::sign,
Routing::energy,
Routing::submit,
Routing::requestToken,
Routing::register,
)

private val json1 = Json {
encodeDefaults = true
prettyPrint = true
ignoreUnknownKeys = true
}

fun main(args: Array<String>) {
val file = File("qsign.json")
val s = if (file.exists()) file.readText() else "{}"
val cfg = json1.decodeFromString(CommonConfigExt.serializer(), s)
cfg.apply()
file.writeText(json1.encodeToString(CommonConfigExt.serializer(), cfg))

args().also {
val baseDir = File(it["basePath", "Lack of basePath."]).also {
BASE_PATH = it
}
if (!baseDir.exists() ||
!baseDir.isDirectory ||
!baseDir.resolve("libfekit.so").exists() ||
!baseDir.resolve("config.json").exists()
|| !baseDir.resolve("dtconfig.json").exists()
) {
error("The base path is invalid, perhaps it is not a directory or something is missing inside.")
} else {
val json = Json { ignoreUnknownKeys = true }
FEBound.initAssertConfig(baseDir)
println("FEBond sum = ${FEBound.checkCurrent()}")
CONFIG = json.decodeFromString<QSignConfig>(baseDir.resolve("config.json").readText())
.apply { checkIllegal() }
println("Load Package = ${CONFIG.protocol}")
}
}
CONFIG.server.also {
embeddedServer(Netty, host = it.host, port = it.port, module = Application::init)
.start(wait = true)
}

}

fun Application.init() {
install(ContentNegotiation) {
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
})
}
install(StatusPages) {
exception<Throwable> { call, cause ->
if (CONFIG.unidbg.debug) {
cause.printStackTrace()
}
call.respond(APIResult(1, cause.message ?: cause.javaClass.name, call.request.uri))
}
}
routing {
API_LIST.forEach { it(this) }
}
}
52 changes: 52 additions & 0 deletions http/src/main/kotlin/moe/fuqiuluo/api/energy.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package moe.fuqiuluo.api

import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import moe.fuqiuluo.ext.fetchGet
import moe.fuqiuluo.ext.hex2ByteArray
import moe.fuqiuluo.ext.toHexString

fun Routing.energy() {
get("/custom_energy") {
val uin = fetchGet("uin")!!.toLong()
val data = fetchGet("data")!!
val salt = fetchGet("salt")!!.hex2ByteArray()

val androidId = fetchGet("android_id", def = "")
val guid = fetchGet("guid", def = "")
val sign = kotlin.runCatching {
UnidbgFetchQSign.customEnergy(uin, data, salt, androidId, guid)
}.onFailure {
it.printStackTrace()
}.getOrNull()

if (sign == null) {
call.respond(APIResult(-1, "failed", null))
} else {
call.respond(APIResult(0, "success", sign.toHexString()))
}
}

get("/energy") {
val uin = fetchGet("uin")!!.toLong()

val data = fetchGet("data")!!
val androidId = fetchGet("android_id", def = "")
val guid = fetchGet("guid", def = null)?.hex2ByteArray()
val version = fetchGet("version", def = null)
val phone = fetchGet("phone", def = null)?.toByteArray() // 86-xxx
val receipt = fetchGet("receipt", def = null)?.toByteArray()
val sign = kotlin.runCatching {
UnidbgFetchQSign.energy(uin, data, null, version, guid, androidId, phone, receipt)
}.onFailure {
it.printStackTrace()
}.getOrNull()

if (sign == null) {
call.respond(APIResult(-1, "failed", null))
} else {
call.respond(APIResult(0, "success", sign.toHexString()))
}
}
}
9 changes: 9 additions & 0 deletions http/src/main/kotlin/moe/fuqiuluo/api/exceptions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package moe.fuqiuluo.api

object SessionNotFoundError : RuntimeException("Uin is not registered.")

object WrongKeyError : RuntimeException("Wrong API key.")

object MissingKeyError : RuntimeException("First use must be submitted with android_id and guid.")

object BlackListError : RuntimeException("Blacklist uin.")
41 changes: 41 additions & 0 deletions http/src/main/kotlin/moe/fuqiuluo/api/main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package moe.fuqiuluo.api

import CONFIG
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import moe.fuqiuluo.comm.Protocol
import top.mrxiaom.qsign.BuildConstants
import java.lang.management.ManagementFactory

@Serializable
data class APIResult<T>(
val code: Int,
val msg: String = "",
@Contextual
val data: T? = null
)

@Serializable
data class APIInfo(
val version: String,
val protocol: Protocol,
val pid: Int
)

fun Routing.index() {
get("/") {
call.respond(
APIResult(
0, "IAA 云天明 章北海 猫", APIInfo(
version = BuildConstants.VERSION,
protocol = CONFIG.protocol,
pid = runCatching { ManagementFactory.getRuntimeMXBean().name.split("@")[0].toInt() }.getOrNull()
?: -1
)
)
)
}
}
85 changes: 85 additions & 0 deletions http/src/main/kotlin/moe/fuqiuluo/api/qsign.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package moe.fuqiuluo.api

import CONFIG
import com.tencent.mobileqq.channel.SsoPacket
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.util.pipeline.*
import kotlinx.serialization.Serializable
import moe.fuqiuluo.ext.fetchGet
import moe.fuqiuluo.ext.fetchPost
import moe.fuqiuluo.ext.hex2ByteArray
import moe.fuqiuluo.ext.toHexString

fun Routing.sign() {
get("/sign") {
val uin = fetchGet("uin")!!
val qua = fetchGet("qua", CONFIG.protocol.qua)!!
val cmd = fetchGet("cmd")!!
val seq = fetchGet("seq")!!.toInt()
val buffer = fetchGet("buffer")!!.hex2ByteArray()
val qimei36 = fetchGet("qimei36", def = "")!!

val androidId = fetchGet("android_id", def = "")!!
val guid = fetchGet("guid", def = "")!!

requestSign(cmd, uin, qua, seq, buffer, qimei36, androidId, guid)
}

post("/sign") {
val param = call.receiveParameters()
val uin = fetchPost(param, "uin")!!
val qua = fetchPost(param, "qua", CONFIG.protocol.qua)!!
val cmd = fetchPost(param, "cmd")!!
val seq = fetchPost(param, "seq")!!.toInt()
val buffer = fetchPost(param, "buffer")!!.hex2ByteArray()
val qimei36 = fetchPost(param, "qimei36", def = "")!!

val androidId = param["android_id"] ?: ""
val guid = param["guid"] ?: ""

requestSign(cmd, uin, qua, seq, buffer, qimei36, androidId, guid)
}
}

@Serializable
private data class Sign(
val token: String,
val extra: String,
val sign: String,
val o3did: String,
val requestCallback: List<SsoPacket>
)

private suspend fun PipelineContext<Unit, ApplicationCall>.requestSign(
cmd: String,
uin: String,
qua: String,
seq: Int,
buffer: ByteArray,
qimei36: String,
androidId: String,
guid: String
) {
val sign = runCatching {
UnidbgFetchQSign.sign(cmd, uin.toLong(), seq, buffer, qua, qimei36, androidId, guid)
}.onFailure {
it.printStackTrace()
}.getOrNull()

if (sign == null) {
call.respond(APIResult(-1, "failed", null))
} else {
call.respond(
APIResult(
0, "success", Sign(
sign.token.toHexString(),
sign.extra.toHexString(),
sign.sign.toHexString(), sign.o3did, sign.requestCallback
)
)
)
}
}
Loading

0 comments on commit e3b897a

Please sign in to comment.