mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-29 01:01:19 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
870234a007
@ -29,7 +29,6 @@ import net.mamoe.mirai.qqandroid.utils.toRichTextElems
|
|||||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||||
import net.mamoe.mirai.utils.cryptor.contentToString
|
import net.mamoe.mirai.utils.cryptor.contentToString
|
||||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||||
import net.mamoe.mirai.utils.io.hexToBytes
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -228,11 +227,15 @@ internal class MessageSvc {
|
|||||||
elems = message.toRichTextElems()
|
elems = message.toRichTextElems()
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
//.apply { add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(
|
||||||
|
// pbReserve = "78 00 F8 01 00 C8 02 00".hexToBytes()
|
||||||
|
// ))) }
|
||||||
msgSeq = seq,
|
msgSeq = seq,
|
||||||
msgRand = Random.nextInt().absoluteValue,
|
msgRand = Random.nextInt().absoluteValue//,
|
||||||
syncCookie = "08 A0 C2 C4 F1 05 10 A0 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 E4 C2 B1 95 03 48 A1 9F E0 C7 08 58 D3 C2 8F A0 09 60 1D 68 A0 C2 C4 F1 05 70 00".hexToBytes()
|
// syncCookie = ByteArray(0)
|
||||||
?: SyncCookie(time = currentTimeSeconds + client.timeDifference).toByteArray(SyncCookie.serializer()),
|
// ?: SyncCookie(time = currentTimeSeconds + client.timeDifference).toByteArray(SyncCookie.serializer()),
|
||||||
msgVia = 0
|
// msgVia = 1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,64 @@ internal fun NotOnlineImageFromFile.toJceData(): ImMsgBody.NotOnlineImage {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun CustomFaceFromFile.toJceData(): ImMsgBody.CustomFace {
|
||||||
|
return ImMsgBody.CustomFace(
|
||||||
|
filePath = this.filepath,
|
||||||
|
fileId = this.fileId,
|
||||||
|
serverIp = this.serverIp,
|
||||||
|
serverPort = this.serverPort,
|
||||||
|
fileType = this.fileType,
|
||||||
|
signature = this.signature,
|
||||||
|
useful = this.useful,
|
||||||
|
md5 = this.md5,
|
||||||
|
bizType = this.bizType,
|
||||||
|
imageType = this.imageType,
|
||||||
|
width = this.width,
|
||||||
|
height = this.height,
|
||||||
|
source = this.source,
|
||||||
|
size = this.size,
|
||||||
|
pbReserve = this.pbReserve
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
customFace=CustomFace#2050019814 {
|
||||||
|
guid=<Empty ByteArray>
|
||||||
|
filePath=5F6C522DEAC4F36C0ED8EF362660EFD6.png
|
||||||
|
shortcut=
|
||||||
|
buffer=<Empty ByteArray>
|
||||||
|
flag=<Empty ByteArray>
|
||||||
|
oldData=<Empty ByteArray>
|
||||||
|
fileId=0xB40AF10E(-1274351346)
|
||||||
|
serverIp=0xB703E13A(-1224482502)
|
||||||
|
serverPort=0x00000050(80)
|
||||||
|
fileType=0x00000042(66)
|
||||||
|
signature=6B 44 61 76 72 79 68 79 57 67 70 52 41 45 78 49
|
||||||
|
useful=0x00000001(1)
|
||||||
|
md5=5F 6C 52 2D EA C4 F3 6C 0E D8 EF 36 26 60 EF D6
|
||||||
|
thumbUrl=
|
||||||
|
bigUrl=
|
||||||
|
origUrl=
|
||||||
|
bizType=0x00000005(5)
|
||||||
|
repeatIndex=0x00000000(0)
|
||||||
|
repeatImage=0x00000000(0)
|
||||||
|
imageType=0x000003E9(1001)
|
||||||
|
index=0x00000000(0)
|
||||||
|
width=0x0000005F(95)
|
||||||
|
height=0x00000054(84)
|
||||||
|
source=0x00000067(103)
|
||||||
|
size=0x000006E2(1762)
|
||||||
|
origin=0x00000000(0)
|
||||||
|
thumbWidth=0x00000000(0)
|
||||||
|
thumbHeight=0x00000000(0)
|
||||||
|
showLen=0x00000000(0)
|
||||||
|
downloadLen=0x00000000(0)
|
||||||
|
_400Url=
|
||||||
|
_400Width=0x00000000(0)
|
||||||
|
_400Height=0x00000000(0)
|
||||||
|
pbReserve=08 01 10 00 32 00 4A 0E 5B E5 8A A8 E7 94 BB E8 A1 A8 E6 83 85 5D 50 00 78 05
|
||||||
|
}
|
||||||
|
|
||||||
notOnlineImage=NotOnlineImage#2050019814 {
|
notOnlineImage=NotOnlineImage#2050019814 {
|
||||||
filePath=41AEF2D4B5BD24CF3791EFC5FEB67D60.jpg
|
filePath=41AEF2D4B5BD24CF3791EFC5FEB67D60.jpg
|
||||||
fileLen=0x00000350(848)
|
fileLen=0x00000350(848)
|
||||||
@ -57,26 +114,39 @@ internal fun MessageChain.toRichTextElems(): MutableList<ImMsgBody.Elem> {
|
|||||||
|
|
||||||
this.forEach {
|
this.forEach {
|
||||||
when (it) {
|
when (it) {
|
||||||
is PlainText -> {
|
is PlainText -> elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue)))
|
||||||
elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue)))
|
|
||||||
}
|
|
||||||
is At -> {
|
is At -> {
|
||||||
|
|
||||||
}
|
}
|
||||||
is NotOnlineImageFromServer -> {
|
is CustomFaceFromFile -> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
|
||||||
elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
|
is CustomFaceFromServer -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
|
||||||
// elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".hexToBytes())))
|
is NotOnlineImageFromServer -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
|
||||||
}
|
is NotOnlineImageFromFile -> elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData()))
|
||||||
is NotOnlineImageFromFile -> {
|
|
||||||
elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData()))
|
|
||||||
// elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".hexToBytes())))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements
|
return elements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class CustomFaceFromServer(
|
||||||
|
internal val delegate: ImMsgBody.CustomFace
|
||||||
|
) : CustomFace() {
|
||||||
|
override val filepath: String get() = delegate.filePath
|
||||||
|
override val fileId: Int get() = delegate.fileId
|
||||||
|
override val serverIp: Int get() = delegate.serverIp
|
||||||
|
override val serverPort: Int get() = delegate.serverPort
|
||||||
|
override val fileType: Int get() = delegate.fileType
|
||||||
|
override val signature: ByteArray get() = delegate.signature
|
||||||
|
override val useful: Int get() = delegate.useful
|
||||||
|
override val md5: ByteArray get() = delegate.md5
|
||||||
|
override val bizType: Int get() = delegate.bizType
|
||||||
|
override val imageType: Int get() = delegate.imageType
|
||||||
|
override val width: Int get() = delegate.width
|
||||||
|
override val height: Int get() = delegate.height
|
||||||
|
override val source: Int get() = delegate.source
|
||||||
|
override val size: Int get() = delegate.size
|
||||||
|
override val pbReserve: ByteArray get() = delegate.pbReserve
|
||||||
|
}
|
||||||
|
|
||||||
internal class NotOnlineImageFromServer(
|
internal class NotOnlineImageFromServer(
|
||||||
internal val delegate: ImMsgBody.NotOnlineImage
|
internal val delegate: ImMsgBody.NotOnlineImage
|
||||||
@ -107,22 +177,8 @@ internal fun ImMsgBody.RichText.toMessageChain(): MessageChain {
|
|||||||
|
|
||||||
elems.forEach {
|
elems.forEach {
|
||||||
when {
|
when {
|
||||||
it.notOnlineImage != null -> message.add(
|
it.notOnlineImage != null -> message.add(NotOnlineImageFromServer(it.notOnlineImage))
|
||||||
NotOnlineImageFromServer(it.notOnlineImage)
|
it.customFace != null -> message.add(CustomFaceFromServer(it.customFace))
|
||||||
)
|
|
||||||
it.customFace != null -> message.add(
|
|
||||||
NotOnlineImageFromFile(
|
|
||||||
it.customFace.filePath,
|
|
||||||
it.customFace.md5,
|
|
||||||
it.customFace.origUrl,
|
|
||||||
it.customFace.downloadLen,
|
|
||||||
it.customFace.height,
|
|
||||||
it.customFace.width,
|
|
||||||
it.customFace.bizType,
|
|
||||||
it.customFace.imageType,
|
|
||||||
it.customFace.filePath
|
|
||||||
)
|
|
||||||
)
|
|
||||||
it.text != null -> message.add(it.text.str.toMessage())
|
it.text != null -> message.add(it.text.str.toMessage())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,10 +68,12 @@ internal val t163 = "2C 7A 7B 23 4E 24 3F 24 24 47 62 6B 69 2E 47 50".hexToBytes
|
|||||||
var ecdhPrivateKeyS = "97a52992cb7a2110413629af94a3c249c68a3b731510caa8"
|
var ecdhPrivateKeyS = "97a52992cb7a2110413629af94a3c249c68a3b731510caa8"
|
||||||
|
|
||||||
internal val shareKeyCalculatedByConstPubKey
|
internal val shareKeyCalculatedByConstPubKey
|
||||||
get() = ECDH.calculateShareKey(
|
by lazy {
|
||||||
loadPrivateKey(ecdhPrivateKeyS),
|
ECDH.calculateShareKey(
|
||||||
initialPublicKey
|
loadPrivateKey(ecdhPrivateKeyS),
|
||||||
)
|
initialPublicKey
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var passwordMd5: ByteArray = byteArrayOf()
|
var passwordMd5: ByteArray = byteArrayOf()
|
||||||
var uin: Long = 0L
|
var uin: Long = 0L
|
||||||
@ -137,132 +139,141 @@ fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed
|
|||||||
println("uin=" + readString(readInt() - 4))
|
println("uin=" + readString(readInt() - 4))
|
||||||
|
|
||||||
println("// 解密 body")
|
println("// 解密 body")
|
||||||
readRemainingBytes().tryDecrypt().toReadPacket().debugPrintThis("outer body decrypted").apply {
|
val encrypted = readRemainingBytes()
|
||||||
when (flag1) {
|
|
||||||
0x0A -> decodeSso()
|
|
||||||
0x0B -> decodeUni()
|
|
||||||
else -> error("unknown flag1: $flag1")
|
|
||||||
}
|
|
||||||
|
|
||||||
when (flag2) {
|
val decrypted = encrypted.tryDecryptOrNull()
|
||||||
|
if (decrypted == null) {
|
||||||
|
println("cannot decrypt: ${encrypted.toUHexString()}")
|
||||||
|
error("cannot decrypt: ${encrypted.toUHexString()}")
|
||||||
|
} else {
|
||||||
|
decrypted.toReadPacket().debugPrintThis("outer body decrypted").apply {
|
||||||
|
when (flag1) {
|
||||||
|
0x0A -> decodeSso()
|
||||||
|
0x0B -> decodeUni()
|
||||||
|
else -> error("unknown flag1: $flag1")
|
||||||
|
}
|
||||||
|
|
||||||
2 -> {
|
when (flag2) {
|
||||||
|
|
||||||
this.debugPrintThis("Oicq Request").apply {
|
2 -> {
|
||||||
/*
|
|
||||||
byte 2 // head flag
|
|
||||||
short 27 + 2 + remaining.length
|
|
||||||
ushort client.protocolVersion // const 8001
|
|
||||||
ushort 0x0001 // const0
|
|
||||||
uint client.uin
|
|
||||||
byte 3 // const1
|
|
||||||
ubyte encryptMethod.value // [EncryptMethod]
|
|
||||||
byte 0 // const2
|
|
||||||
int 2 // const3
|
|
||||||
int client.appClientVersion
|
|
||||||
int 0 // const4
|
|
||||||
*/
|
|
||||||
discardExact(3)
|
|
||||||
readShort().toInt().takeIf { it != 8001 }?.let {
|
|
||||||
println("这个包不是 oicqRequest")
|
|
||||||
return@debugIfFail this
|
|
||||||
println(" got new protocolVersion=$it")
|
|
||||||
}
|
|
||||||
val commandId = readUShort().toInt()
|
|
||||||
println(" commandId=0x${commandId.toShort().toUHexString()}")
|
|
||||||
readUShort().toInt().takeIf { it != 1 }?.let {
|
|
||||||
println(" got new const0=$it")
|
|
||||||
}
|
|
||||||
println(" uin=${readUInt()}")
|
|
||||||
readByte().toInt().takeIf { it != 3 }?.let {
|
|
||||||
println(" got new const1=$it")
|
|
||||||
}
|
|
||||||
val encryptionMethod = readUByte().toInt()
|
|
||||||
readByte().toInt().takeIf { it != 0 }?.let {
|
|
||||||
println(" got new const2=$it")
|
|
||||||
}
|
|
||||||
readInt().takeIf { it != 2 }?.let {
|
|
||||||
println(" got new const3=$it")
|
|
||||||
}
|
|
||||||
readInt().takeIf { it != 0 }?.let {
|
|
||||||
println(" got new appClientVersion=$it")
|
|
||||||
}
|
|
||||||
readInt().takeIf { it != 0 }?.let {
|
|
||||||
println(" got new const4=$it")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.debugPrintThis("Oicq Request").apply {
|
||||||
discardExact(1)
|
/*
|
||||||
discardExact(1)
|
byte 2 // head flag
|
||||||
val randomKey = readBytes(16)
|
short 27 + 2 + remaining.length
|
||||||
println("randomKey= ${randomKey.toUHexString()}")
|
ushort client.protocolVersion // const 8001
|
||||||
readUShort().toInt().takeIf { it != 258 }?.let {
|
ushort 0x0001 // const0
|
||||||
println(" got new const in ECDH head(originally=258)=$it")
|
uint client.uin
|
||||||
}
|
byte 3 // const1
|
||||||
val publicKey = readBytes(readShort().toInt())
|
ubyte encryptMethod.value // [EncryptMethod]
|
||||||
println("ecdh publicKey=" + publicKey.toUHexString())
|
byte 0 // const2
|
||||||
|
int 2 // const3
|
||||||
|
int client.appClientVersion
|
||||||
val encrypt = when (encryptionMethod) {
|
int 0 // const4
|
||||||
135, 7 -> {
|
*/
|
||||||
ECDH.calculateShareKey(
|
discardExact(3)
|
||||||
loadPrivateKey(ecdhPrivateKeyS),
|
readShort().toInt().takeIf { it != 8001 }?.let {
|
||||||
//"04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7".chunkedHexToBytes().adjustToPublicKey() // QQ: 04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7
|
println("这个包不是 oicqRequest")
|
||||||
ECDH.constructPublicKey("30 46 30 10 06 07 2A 86 48 CE 3D 02 01 06 05 2B 81 04 00 1F 03 32 00".hexToBytes() + publicKey)
|
return@debugIfFail this
|
||||||
)
|
println(" got new protocolVersion=$it")
|
||||||
|
}
|
||||||
|
val commandId = readUShort().toInt()
|
||||||
|
println(" commandId=0x${commandId.toShort().toUHexString()}")
|
||||||
|
readUShort().toInt().takeIf { it != 1 }?.let {
|
||||||
|
println(" got new const0=$it")
|
||||||
|
}
|
||||||
|
println(" uin=${readUInt()}")
|
||||||
|
readByte().toInt().takeIf { it != 3 }?.let {
|
||||||
|
println(" got new const1=$it")
|
||||||
|
}
|
||||||
|
val encryptionMethod = readUByte().toInt()
|
||||||
|
readByte().toInt().takeIf { it != 0 }?.let {
|
||||||
|
println(" got new const2=$it")
|
||||||
|
}
|
||||||
|
readInt().takeIf { it != 2 }?.let {
|
||||||
|
println(" got new const3=$it")
|
||||||
|
}
|
||||||
|
readInt().takeIf { it != 0 }?.let {
|
||||||
|
println(" got new appClientVersion=$it")
|
||||||
|
}
|
||||||
|
readInt().takeIf { it != 0 }?.let {
|
||||||
|
println(" got new const4=$it")
|
||||||
}
|
}
|
||||||
|
|
||||||
69 -> {
|
|
||||||
error("encryptionMethod 69")
|
discardExact(1)
|
||||||
|
discardExact(1)
|
||||||
|
val randomKey = readBytes(16)
|
||||||
|
println("randomKey= ${randomKey.toUHexString()}")
|
||||||
|
readUShort().toInt().takeIf { it != 258 }?.let {
|
||||||
|
println(" got new const in ECDH head(originally=258)=$it")
|
||||||
}
|
}
|
||||||
else -> error("unknown encryptionMethod=$encryptionMethod")
|
val publicKey = readBytes(readShort().toInt())
|
||||||
}
|
println("ecdh publicKey=" + publicKey.toUHexString())
|
||||||
|
|
||||||
val encryptedBody = readBytes((remaining - 1).toInt())
|
|
||||||
|
|
||||||
val decrypted = kotlin.runCatching {
|
val encrypt = when (encryptionMethod) {
|
||||||
encryptedBody.decryptBy(encrypt).also { println("first by calculatedShareKey or sessionKey(method=7)") }
|
135, 7 -> {
|
||||||
}.getOrElse {
|
ECDH.calculateShareKey(
|
||||||
encryptedBody.decryptBy(shareKeyCalculatedByConstPubKey).also { println("first by shareKeyCalculatedByConstPubKey") }
|
loadPrivateKey(ecdhPrivateKeyS),
|
||||||
}.let { firstDecrypted ->
|
//"04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7".chunkedHexToBytes().adjustToPublicKey() // QQ: 04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7
|
||||||
runCatching {
|
ECDH.constructPublicKey("30 46 30 10 06 07 2A 86 48 CE 3D 02 01 06 05 2B 81 04 00 1F 03 32 00".hexToBytes() + publicKey)
|
||||||
firstDecrypted.decryptBy(encrypt).also { println("second by calculatedShareKey") }
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
69 -> {
|
||||||
|
error("encryptionMethod 69")
|
||||||
|
}
|
||||||
|
else -> error("unknown encryptionMethod=$encryptionMethod")
|
||||||
|
}
|
||||||
|
|
||||||
|
val encryptedBody = readBytes((remaining - 1).toInt())
|
||||||
|
|
||||||
|
val decrypted = kotlin.runCatching {
|
||||||
|
encryptedBody.decryptBy(encrypt).also { println("first by calculatedShareKey or sessionKey(method=7)") }
|
||||||
}.getOrElse {
|
}.getOrElse {
|
||||||
kotlin.runCatching {
|
encryptedBody.decryptBy(shareKeyCalculatedByConstPubKey).also { println("first by shareKeyCalculatedByConstPubKey") }
|
||||||
firstDecrypted.decryptBy(shareKeyCalculatedByConstPubKey)
|
}.let { firstDecrypted ->
|
||||||
}.getOrDefault(firstDecrypted)
|
runCatching {
|
||||||
}
|
firstDecrypted.decryptBy(encrypt).also { println("second by calculatedShareKey") }
|
||||||
}
|
}.getOrElse {
|
||||||
|
kotlin.runCatching {
|
||||||
PacketLogger.info("Real body=" + decrypted.toUHexString())
|
firstDecrypted.decryptBy(shareKeyCalculatedByConstPubKey)
|
||||||
decrypted.toReadPacket().apply {
|
}.getOrDefault(firstDecrypted)
|
||||||
if (commandId == 0x0810) {
|
|
||||||
DebugLogger.info("发送 login!! 正在获取 tgtgtKey")
|
|
||||||
try {
|
|
||||||
discardExact(4)
|
|
||||||
val tlvMap = readTLVMap()
|
|
||||||
tlvMap.printTLVMap()
|
|
||||||
tlvMap[0x106]
|
|
||||||
?.also { DebugLogger.info("找到了 0x106") }
|
|
||||||
?.decryptBy(md5(passwordMd5 + ByteArray(4) + uin.toInt().toByteArray()))
|
|
||||||
?.read {
|
|
||||||
discardExact(2 + 4 * 4 + 8 + 4 + 4 + 1 + 16)
|
|
||||||
tgtgtKey = readBytes(16)
|
|
||||||
DebugLogger.info("获取 tgtgtKey=${tgtgtKey.toUHexString()}")
|
|
||||||
} ?: DebugLogger.info("找不到 0x106")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PacketLogger.info("Real body=" + decrypted.toUHexString())
|
||||||
|
decrypted.toReadPacket().apply {
|
||||||
|
if (commandId == 0x0810) {
|
||||||
|
DebugLogger.info("发送 login!! 正在获取 tgtgtKey")
|
||||||
|
try {
|
||||||
|
discardExact(4)
|
||||||
|
val tlvMap = readTLVMap()
|
||||||
|
tlvMap.printTLVMap()
|
||||||
|
tlvMap[0x106]
|
||||||
|
?.also { DebugLogger.info("找到了 0x106") }
|
||||||
|
?.decryptBy(md5(passwordMd5 + ByteArray(4) + uin.toInt().toByteArray()))
|
||||||
|
?.read {
|
||||||
|
discardExact(2 + 4 * 4 + 8 + 4 + 4 + 1 + 16)
|
||||||
|
tgtgtKey = readBytes(16)
|
||||||
|
DebugLogger.info("获取 tgtgtKey=${tgtgtKey.toUHexString()}")
|
||||||
|
} ?: DebugLogger.info("找不到 0x106")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else -> {
|
||||||
else -> {
|
this.debugPrintThis("uni packet")
|
||||||
this.debugPrintThis("uni packet")
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ByteReadPacket.decodeUni() {
|
fun ByteReadPacket.decodeUni() {
|
||||||
@ -354,8 +365,8 @@ val keys: Map<String, ByteArray>
|
|||||||
"shareKeyCalculatedByConstPubKey" to shareKeyCalculatedByConstPubKey,
|
"shareKeyCalculatedByConstPubKey" to shareKeyCalculatedByConstPubKey,
|
||||||
"t108" to t108,
|
"t108" to t108,
|
||||||
"t10c" to t10c,
|
"t10c" to t10c,
|
||||||
"t163" to t163
|
"t163" to t163
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ByteArray.tryDecrypt(): ByteArray {
|
fun ByteArray.tryDecrypt(): ByteArray {
|
||||||
return this.tryDecryptOrNull() ?: error("Cannot decrypt. Encrypted data=" + this.toUHexString())
|
return this.tryDecryptOrNull() ?: error("Cannot decrypt. Encrypted data=" + this.toUHexString())
|
||||||
|
@ -2,19 +2,110 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.message.data
|
package net.mamoe.mirai.message.data
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
sealed class Image : Message {
|
sealed class Image : Message {
|
||||||
abstract val resourceId: String
|
abstract val md5: ByteArray
|
||||||
|
|
||||||
abstract override fun toString(): String
|
abstract override fun toString(): String
|
||||||
|
|
||||||
final companion object Key : Message.Key<Image>
|
companion object Key : Message.Key<Image>
|
||||||
|
|
||||||
abstract override fun eq(other: Message): Boolean
|
abstract override fun eq(other: Message): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class CustomFace : Image() {
|
||||||
|
abstract val filepath: String
|
||||||
|
abstract val fileId: Int
|
||||||
|
abstract val serverIp: Int
|
||||||
|
abstract val serverPort: Int
|
||||||
|
abstract val fileType: Int
|
||||||
|
abstract val signature: ByteArray
|
||||||
|
abstract val useful: Int
|
||||||
|
abstract override val md5: ByteArray
|
||||||
|
abstract val bizType: Int
|
||||||
|
abstract val imageType: Int
|
||||||
|
abstract val width: Int
|
||||||
|
abstract val height: Int
|
||||||
|
abstract val source: Int
|
||||||
|
abstract val size:Int
|
||||||
|
abstract val pbReserve: ByteArray
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "[CustomFace]"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun eq(other: Message): Boolean {
|
||||||
|
return this.toString() == other.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CustomFaceFromFile(
|
||||||
|
override val filepath: String,
|
||||||
|
override val fileId: Int,
|
||||||
|
override val serverIp: Int,
|
||||||
|
override val serverPort: Int,
|
||||||
|
override val fileType: Int,
|
||||||
|
override val signature: ByteArray,
|
||||||
|
override val useful: Int,
|
||||||
|
override val md5: ByteArray,
|
||||||
|
override val bizType: Int,
|
||||||
|
override val imageType: Int,
|
||||||
|
override val width: Int,
|
||||||
|
override val height: Int,
|
||||||
|
override val source: Int,
|
||||||
|
override val size: Int,
|
||||||
|
override val pbReserve: ByteArray
|
||||||
|
) : CustomFace() {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other == null || this::class != other::class) return false
|
||||||
|
|
||||||
|
other as CustomFaceFromFile
|
||||||
|
|
||||||
|
if (filepath != other.filepath) return false
|
||||||
|
if (fileId != other.fileId) return false
|
||||||
|
if (serverIp != other.serverIp) return false
|
||||||
|
if (serverPort != other.serverPort) return false
|
||||||
|
if (fileType != other.fileType) return false
|
||||||
|
if (!signature.contentEquals(other.signature)) return false
|
||||||
|
if (useful != other.useful) return false
|
||||||
|
if (!md5.contentEquals(other.md5)) return false
|
||||||
|
if (bizType != other.bizType) return false
|
||||||
|
if (imageType != other.imageType) return false
|
||||||
|
if (width != other.width) return false
|
||||||
|
if (height != other.height) return false
|
||||||
|
if (source != other.source) return false
|
||||||
|
if (size != other.size) return false
|
||||||
|
if (!pbReserve.contentEquals(other.pbReserve)) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = filepath.hashCode()
|
||||||
|
result = 31 * result + fileId
|
||||||
|
result = 31 * result + serverIp
|
||||||
|
result = 31 * result + serverPort
|
||||||
|
result = 31 * result + fileType
|
||||||
|
result = 31 * result + signature.contentHashCode()
|
||||||
|
result = 31 * result + useful
|
||||||
|
result = 31 * result + md5.contentHashCode()
|
||||||
|
result = 31 * result + bizType
|
||||||
|
result = 31 * result + imageType
|
||||||
|
result = 31 * result + width
|
||||||
|
result = 31 * result + height
|
||||||
|
result = 31 * result + source
|
||||||
|
result = 31 * result + size
|
||||||
|
result = 31 * result + pbReserve.contentHashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract class NotOnlineImage : Image() {
|
abstract class NotOnlineImage : Image() {
|
||||||
abstract override val resourceId: String
|
abstract val resourceId: String
|
||||||
abstract val md5: ByteArray
|
abstract override val md5: ByteArray
|
||||||
abstract val filepath: String
|
abstract val filepath: String
|
||||||
abstract val fileLength: Int
|
abstract val fileLength: Int
|
||||||
abstract val height: Int
|
abstract val height: Int
|
||||||
@ -24,7 +115,7 @@ abstract class NotOnlineImage : Image() {
|
|||||||
open val downloadPath: String get() = resourceId
|
open val downloadPath: String get() = resourceId
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "[$resourceId]"
|
return "[NotOnlineImage $resourceId]"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun eq(other: Message): Boolean {
|
override fun eq(other: Message): Boolean {
|
||||||
@ -32,7 +123,7 @@ abstract class NotOnlineImage : Image() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class NotOnlineImageFromFile(
|
data class NotOnlineImageFromFile(
|
||||||
override val resourceId: String,
|
override val resourceId: String,
|
||||||
override val md5: ByteArray,
|
override val md5: ByteArray,
|
||||||
override val filepath: String,
|
override val filepath: String,
|
||||||
@ -42,4 +133,36 @@ open class NotOnlineImageFromFile(
|
|||||||
override val bizType: Int = 0,
|
override val bizType: Int = 0,
|
||||||
override val imageType: Int = 1000,
|
override val imageType: Int = 1000,
|
||||||
override val downloadPath: String = resourceId
|
override val downloadPath: String = resourceId
|
||||||
) : NotOnlineImage()
|
) : NotOnlineImage() {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other == null || this::class != other::class) return false
|
||||||
|
|
||||||
|
other as NotOnlineImageFromFile
|
||||||
|
|
||||||
|
if (resourceId != other.resourceId) return false
|
||||||
|
if (!md5.contentEquals(other.md5)) return false
|
||||||
|
if (filepath != other.filepath) return false
|
||||||
|
if (fileLength != other.fileLength) return false
|
||||||
|
if (height != other.height) return false
|
||||||
|
if (width != other.width) return false
|
||||||
|
if (bizType != other.bizType) return false
|
||||||
|
if (imageType != other.imageType) return false
|
||||||
|
if (downloadPath != other.downloadPath) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = resourceId.hashCode()
|
||||||
|
result = 31 * result + md5.contentHashCode()
|
||||||
|
result = 31 * result + filepath.hashCode()
|
||||||
|
result = 31 * result + fileLength
|
||||||
|
result = 31 * result + height
|
||||||
|
result = 31 * result + width
|
||||||
|
result = 31 * result + bizType
|
||||||
|
result = 31 * result + imageType
|
||||||
|
result = 31 * result + downloadPath.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package net.mamoe.mirai.utils.cryptor
|
package net.mamoe.mirai.utils.cryptor
|
||||||
|
|
||||||
import net.mamoe.mirai.utils.io.chunkedHexToBytes
|
import net.mamoe.mirai.utils.io.chunkedHexToBytes
|
||||||
import net.mamoe.mirai.utils.io.toUHexString
|
|
||||||
|
|
||||||
expect interface ECDHPrivateKey {
|
expect interface ECDHPrivateKey {
|
||||||
fun getEncoded(): ByteArray
|
fun getEncoded(): ByteArray
|
||||||
@ -65,8 +64,11 @@ private val commonHeadForNot02 = "3046301006072A8648CE3D020106052B8104001F033200
|
|||||||
private const val constantHead = "3046301006072A8648CE3D020106052B8104001F03320004"
|
private const val constantHead = "3046301006072A8648CE3D020106052B8104001F03320004"
|
||||||
private val byteArray_04 = byteArrayOf(0x04)
|
private val byteArray_04 = byteArrayOf(0x04)
|
||||||
|
|
||||||
fun ByteArray.adjustToPublicKey(): ECDHPublicKey {
|
|
||||||
val head = if(this.size<30) "302E301006072A8648CE3D020106052B8104001F031A00" else "3046301006072A8648CE3D020106052B8104001F03320004"
|
|
||||||
|
|
||||||
return ECDH.constructPublicKey((head + this.toUHexString("")).chunkedHexToBytes())
|
private val head1 = "302E301006072A8648CE3D020106052B8104001F031A00".chunkedHexToBytes()
|
||||||
|
private val head2 = "3046301006072A8648CE3D020106052B8104001F03320004".chunkedHexToBytes()
|
||||||
|
fun ByteArray.adjustToPublicKey(): ECDHPublicKey {
|
||||||
|
val head = if (this.size < 30) head1 else head2
|
||||||
|
|
||||||
|
return ECDH.constructPublicKey(head + this)
|
||||||
}
|
}
|
@ -23,6 +23,7 @@ import net.mamoe.mirai.message.data.buildXMLMessage
|
|||||||
import net.mamoe.mirai.message.data.getValue
|
import net.mamoe.mirai.message.data.getValue
|
||||||
import net.mamoe.mirai.message.sendAsImageTo
|
import net.mamoe.mirai.message.sendAsImageTo
|
||||||
import net.mamoe.mirai.utils.ContextImpl
|
import net.mamoe.mirai.utils.ContextImpl
|
||||||
|
import net.mamoe.mirai.utils.io.toUHexString
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.swing.filechooser.FileSystemView
|
import javax.swing.filechooser.FileSystemView
|
||||||
@ -130,7 +131,7 @@ suspend fun main() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
image.downloadTo(newTestTempFile(suffix = ".png").also { reply("Temp file: ${it.absolutePath}") })
|
image.downloadTo(newTestTempFile(suffix = ".png").also { reply("Temp file: ${it.absolutePath}") })
|
||||||
reply(image.resourceId + " downloaded")
|
reply(image.md5.toUHexString() + " downloaded")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
reply(e.message ?: e::class.java.simpleName)
|
reply(e.message ?: e::class.java.simpleName)
|
||||||
|
Loading…
Reference in New Issue
Block a user