diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/Robot.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/Robot.kt index 2840cd658..264533207 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/Robot.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/Robot.kt @@ -11,15 +11,15 @@ import io.netty.channel.socket.nio.NioSocketChannel import io.netty.handler.codec.bytes.ByteArrayDecoder import io.netty.handler.codec.bytes.ByteArrayEncoder import net.mamoe.mirai.network.packet.client.ClientPacket -import net.mamoe.mirai.network.packet.client.login.ClientPasswordSubmissionPacket -import net.mamoe.mirai.network.packet.client.login.ClientServerRedirectionPacket +import net.mamoe.mirai.network.packet.client.login.* import net.mamoe.mirai.network.packet.client.writeHex import net.mamoe.mirai.network.packet.server.ServerPacket -import net.mamoe.mirai.network.packet.server.login.ServerLoginFailedResponsePacket -import net.mamoe.mirai.network.packet.server.login.ServerLoginResendResponsePacket -import net.mamoe.mirai.network.packet.server.login.ServerLoginSucceedResponsePacket -import net.mamoe.mirai.network.packet.server.login.ServerLoginVerificationCodeResponsePacket +import net.mamoe.mirai.network.packet.server.login.ServerLoginResponseFailedPacket +import net.mamoe.mirai.network.packet.server.login.ServerLoginResponseResendPacket +import net.mamoe.mirai.network.packet.server.login.ServerLoginResponseSucceedPacket +import net.mamoe.mirai.network.packet.server.login.ServerLoginResponseVerificationCodePacket import net.mamoe.mirai.network.packet.server.touch.ServerTouchResponsePacket +import net.mamoe.mirai.util.getRandomKey import net.mamoe.mirai.utils.MiraiLogger import java.net.DatagramPacket import java.net.InetSocketAddress @@ -30,11 +30,22 @@ import java.net.InetSocketAddress * @author Him188moe @ Mirai Project */ class Robot(val number: Int, private val password: String) { + private var sequence: Int = 0 + private var channel: Channel? = null + private lateinit var token00BA: ByteArray + private lateinit var token0825: ByteArray + private var loginTime: Int = 0 + private lateinit var loginIP: String + private var tgtgtKey: ByteArray? = null + + @ExperimentalUnsignedTypes + private var md5_32: ByteArray = getRandomKey(32) + @ExperimentalUnsignedTypes - internal fun onPacketReceived(packet: ServerPacket) { + private fun onPacketReceived(packet: ServerPacket) { packet.decode() when (packet) { is ServerTouchResponsePacket -> { @@ -45,6 +56,10 @@ class Robot(val number: Int, private val password: String) { qq = number )) } else {//password submission + this.loginIP = packet.loginIP + this.loginTime = packet.loginTime + this.token0825 = packet.token + this.tgtgtKey = packet.tgtgtKey sendPacket(ClientPasswordSubmissionPacket( qq = this.number, password = this.password, @@ -56,22 +71,55 @@ class Robot(val number: Int, private val password: String) { } } - is ServerLoginFailedResponsePacket -> { + is ServerLoginResponseFailedPacket -> { channel = null println("Login failed: " + packet.state.toString()) return } - is ServerLoginVerificationCodeResponsePacket -> { + is ServerLoginResponseVerificationCodePacket -> { + //[token00BA]可能来自这里 + this.token00BA = packet.token00BA + if (packet.unknownBoolean != null && packet.unknownBoolean!!) { + this.sequence = 1 + sendPacket(ClientLoginVerificationCodePacket( + qq = this.number, + token0825 = this.token0825, + token00BA = this.token00BA, + sequence = this.sequence + )) + } } - is ServerLoginSucceedResponsePacket -> { + is ServerLoginResponseSucceedPacket -> { } - is ServerLoginResendResponsePacket -> { - + //这个有可能是客户端发送验证码之后收到的回复验证码是否正确? + is ServerLoginResponseResendPacket -> { + if (packet.flag == ServerLoginResponseResendPacket.Flag.`08 36 31 03`) { + this.tgtgtKey = packet.tgtgtKey + sendPacket(ClientLoginResendPacket3104( + tgtgtKey = packet.tgtgtKey, + token00BA = packet.token, + qq = this.number, + password = this.password, + loginIP = this.loginIP, + loginTime = this.loginTime, + token0825 = this.token0825 + )) + } else { + sendPacket(ClientLoginResendPacket3106( + tgtgtKey = packet.tgtgtKey, + token00BA = packet.token, + qq = this.number, + password = this.password, + loginIP = this.loginIP, + loginTime = this.loginTime, + token0825 = this.token0825 + )) + } } else -> throw IllegalStateException() @@ -82,7 +130,7 @@ class Robot(val number: Int, private val password: String) { @ExperimentalUnsignedTypes private fun sendPacket(packet: ClientPacket) { packet.encode() - packet.writeHex(Protocol.tail); + packet.writeHex(Protocol.tail) channel!!.writeAndFlush(DatagramPacket(packet.toByteArray())) } @@ -115,7 +163,7 @@ class Robot(val number: Int, private val password: String) { Reader.init() remaining }*/ - this@Robot.onPacketReceived(ServerPacket.ofByteArray(bytes)) + this@Robot.onPacketReceived(ServerPacket.ofByteArray(bytes, tgtgtKey)) } catch (e: Exception) { MiraiLogger.catching(e) } @@ -132,42 +180,10 @@ class Robot(val number: Int, private val password: String) { } }) - channel = b.connect().sync().channel(); + channel = b.connect().sync().channel() channel!!.closeFuture().sync() } finally { group.shutdownGracefully().sync() } } - - private object Reader { - private var length: Int? = null - private lateinit var bytes: ByteArray - - fun init(bytes: ByteArray) { - this.length = bytes.size - this.bytes = bytes - } - - /** - * Reads bytes, combining them to create a packet, returning remaining bytes. - */ - fun read(bytes: ByteArray): ByteArray? { - checkNotNull(this.length) - val needSize = length!! - this.bytes.size//How many bytes we need - if (needSize == bytes.size || needSize > bytes.size) { - this.bytes += bytes - return null - } - - //We got more than we need - this.bytes += bytes.copyOfRange(0, needSize) - return bytes.copyOfRange(needSize, bytes.size - needSize)//We got remaining bytes, that is of another packet - } - - fun isPacketAvailable() = this.length == this.bytes.size - - fun toServerPacket(): ServerPacket { - return ServerPacket.ofByteArray(this.bytes) - } - } } \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientPacket.kt index ede45f97b..f8580e48c 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/ClientPacket.kt @@ -4,12 +4,10 @@ import lombok.Getter import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.packet.Packet import net.mamoe.mirai.network.packet.PacketId -import net.mamoe.mirai.util.ByteArrayDataOutputStream -import net.mamoe.mirai.util.TEACryptor -import net.mamoe.mirai.util.hexToBytes -import net.mamoe.mirai.util.toHexString +import net.mamoe.mirai.util.* import java.io.DataOutputStream import java.io.IOException +import java.net.InetAddress import java.security.MessageDigest /** @@ -76,6 +74,28 @@ fun DataOutputStream.writeHex(hex: String) { } } +@ExperimentalUnsignedTypes +fun DataOutputStream.writeVarInt(dec: UInt) { + /*.判断开始 (n < 256) + 返回 (取文本右边 (“0” + 取十六进制文本 (n), 2)) + .判断 (n ≥ 256) + hex = 取文本右边 (“0” + 取十六进制文本 (n), 4) + 返回 (取文本左边 (hex, 2) + “ ” + 取文本右边 (hex, 2)) + .默认 + 返回 (“”) + .判断结束*/ + + if (dec < 256u) { + this.writeByte(dec.toByte().toInt())//drop other bits + } + + if (dec > 256u) { + this.writeShort(dec.toShort().toInt()) + } + + throw UnsupportedOperationException() +} + @ExperimentalUnsignedTypes @Throws(IOException::class) fun DataOutputStream.writeTLV0006(qq: Int, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray) { @@ -100,6 +120,21 @@ fun DataOutputStream.writeTLV0006(qq: Int, password: String, loginTime: Int, log } } +@ExperimentalUnsignedTypes +fun DataOutputStream.writeCRC32() { + getRandomKey(16).let { + write(it)//key + writeLong(getCrc32(it))//todo may be int? check that. + } +} + +fun DataOutputStream.writeHostname() { + val hostName: String = InetAddress.getLocalHost().hostName.let { it.substring(0, it.length - 3) }; + this.writeShort(hostName.length / 2);//todo check that + this.writeShort(hostName.length); + this.writeBytes(hostName)//todo 这个对吗? +} + fun Int.toByteArray(): ByteArray = byteArrayOf(//todo 检查这方法对不对, 这其实就是从 DataInputStream copy来的 (this.ushr(24) and 0xFF).toByte(), (this.ushr(16) and 0xFF).toByte(), diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginPacket.kt new file mode 100644 index 000000000..b2c0002b8 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginPacket.kt @@ -0,0 +1,158 @@ +package net.mamoe.mirai.network.packet.client.login + +import net.mamoe.mirai.network.Protocol +import net.mamoe.mirai.network.packet.PacketId +import net.mamoe.mirai.network.packet.client.* +import net.mamoe.mirai.util.ByteArrayDataOutputStream +import net.mamoe.mirai.util.TEACryptor +import net.mamoe.mirai.util.hexToBytes + +/** + * Password submission (0836_622) + * + * @author Him188moe @ Mirai Project + */ +@PacketId("08 36 31 03") +@ExperimentalUnsignedTypes +class ClientPasswordSubmissionPacket(private val qq: Int, private val password: String, private val loginTime: Int, private val loginIP: String, private val tgtgtKey: ByteArray, private val token0825: ByteArray) : ClientPacket() { + @ExperimentalUnsignedTypes + override fun encode() { + this.writeQQ(qq) + this.writeHex(Protocol._0836_622_fix1) + this.writeHex(Protocol.publicKey) + this.writeHex("00 00 00 10") + this.writeHex(Protocol._0836key1) + + this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() { + override fun toByteArray(): ByteArray { + writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825) + writePart2() + return super.toByteArray() + } + }.toByteArray(), Protocol.shareKey.hexToBytes())) + } +} + +/** + * Dispose_0836 + * + * @author Him188moe @ Mirai Project + */ +@PacketId("08 36 31 04") +@ExperimentalUnsignedTypes +class ClientLoginResendPacket3104(qq: Int, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray) : ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA) + +/** + * Dispose_0836 + * + * @author Him188moe @ Mirai Project + */ +@PacketId("08 36 31 06") +@ExperimentalUnsignedTypes +class ClientLoginResendPacket3106(qq: Int, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray) : ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA) + +/** + * Dispose_0836 + * + * @author Him188moe @ Mirai Project + */ +@ExperimentalUnsignedTypes +open class ClientLoginResendPacket(val qq: Int, val password: String, val loginTime: Int, val loginIP: String, val tgtgtKey: ByteArray, val token0825: ByteArray, val token00BA: ByteArray) : ClientPacket() { + override fun encode() { + this.writeQQ(qq) + this.writeHex(Protocol._0836_622_fix1) + this.writeHex(Protocol.publicKey) + this.writeHex("00 00 00 10") + this.writeHex(Protocol._0836key1) + + this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() { + override fun toByteArray(): ByteArray { + writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825) + + this.writeHex("01 10") //tag + this.writeHex("00 3C")//length + this.writeHex("00 01")//tag + this.writeHex("00 38")//length + this.write(token00BA)//value + + writePart2() + return super.toByteArray() + } + }.toByteArray(), Protocol.shareKey.hexToBytes())) + } +} + +/** + * @author Him188moe @ Mirai Project + */ +@ExperimentalUnsignedTypes +private fun ClientPacket.writePart1(qq: Int, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray) { + + this.writeQQ(System.currentTimeMillis().toInt())//that's correct + this.writeHex("01 12")//tag + this.writeHex("00 38")//length + this.write(token0825)//length + this.writeHex("03 0F")//tag + this.writeHostname()//todo 务必检查这个 + /*易语言源码: PCName就是HostName + PCName = BytesToStr (Ansi转Utf8 (取主机名 ())) + PCName = 取文本左边 (PCName, 取文本长度 (PCName) - 3)*/ + + this.writeHex("00 05 00 06 00 02") + this.writeQQ(qq) + this.writeHex("00 06")//tag + this.writeHex("00 78")//length + this.writeTLV0006(qq, password, loginTime, loginIP, tgtgtKey) + //fix + this.writeHex(Protocol._0836_622_fix2) + this.writeHex("00 1A")//tag + this.writeHex("00 40")//length + this.write(TEACryptor.encrypt(Protocol._0836_622_fix2.hexToBytes(), tgtgtKey)) + this.writeHex(Protocol._0825data0) + this.writeHex(Protocol._0825data2) + this.writeQQ(qq) + this.writeZero(4) + + this.writeHex("01 03")//tag + this.writeHex("00 14")//length + + this.writeHex("00 01")//tag + this.writeHex("00 10")//length + this.writeHex("60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6")//key +} + +@ExperimentalUnsignedTypes +private fun ClientPacket.writePart2() { + + this.writeHex("03 12")//tag + this.writeHex("00 05")//length + this.writeHex("01 00 00 00 01")//value + + this.writeHex("05 08")//tag + this.writeHex("00 05")//length + this.writeHex("10 00 00 00 00")//value + + this.writeHex("03 13")//tag + this.writeHex("00 19")//length + this.writeHex("01")//value + + this.writeHex("01 02")//tag + this.writeHex("00 10")//length + this.writeHex("04 EA 78 D1 A4 FF CD CC 7C B8 D4 12 7D BB 03 AA")//key + this.writeZero(3) + this.writeByte(0)//maybe 00, 0F, 1F + + this.writeHex("01 02")//tag + this.writeHex("00 62")//length + this.writeHex("00 01")//word? + this.writeHex("04 EB B7 C1 86 F9 08 96 ED 56 84 AB 50 85 2E 48")//key + this.writeHex("00 38")//length + //value + this.writeHex("E9 AA 2B 4D 26 4C 76 18 FE 59 D5 A9 82 6A 0C 04 B4 49 50 D7 9B B1 FE 5D 97 54 8D 82 F3 22 C2 48 B9 C9 22 69 CA 78 AD 3E 2D E9 C9 DF A8 9E 7D 8C 8D 6B DF 4C D7 34 D0 D3") + + this.writeHex("00 14")//length + writeCRC32() + +} + + diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginResendPacket3104.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginResendPacket3104.kt deleted file mode 100644 index e97196684..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginResendPacket3104.kt +++ /dev/null @@ -1,17 +0,0 @@ -package net.mamoe.mirai.network.packet.client.login - -import net.mamoe.mirai.network.packet.PacketId -import net.mamoe.mirai.network.packet.client.ClientPacket - -/** - * Dispose_0836 - * - * @author Him188moe @ Mirai Project - */ -@PacketId("08 36 31 04") -@ExperimentalUnsignedTypes -class ClientLoginResendPacket3104(val tgtgtKey: ByteArray, val token00BA: ByteArray) : ClientPacket() { - override fun encode() { - - } -} \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginVerificationCodePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginVerificationCodePacket.kt new file mode 100644 index 000000000..dbfbbaf4b --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginVerificationCodePacket.kt @@ -0,0 +1,40 @@ +package net.mamoe.mirai.network.packet.client.login + +import net.mamoe.mirai.network.Protocol +import net.mamoe.mirai.network.packet.PacketId +import net.mamoe.mirai.network.packet.client.ClientPacket +import net.mamoe.mirai.network.packet.client.writeHex +import net.mamoe.mirai.network.packet.client.writeQQ +import net.mamoe.mirai.network.packet.client.writeVarInt +import net.mamoe.mirai.util.ByteArrayDataOutputStream +import net.mamoe.mirai.util.TEACryptor + +/** + * @author Him188moe @ Mirai Project + */ +@PacketId("00 BA 31 01") +@ExperimentalUnsignedTypes +class ClientLoginVerificationCodePacket(private val qq: Int, private val token0825: ByteArray, private val sequence: Int, private val token00BA: ByteArray) : ClientPacket() { + override fun encode() { + this.writeQQ(qq) + this.writeHex(Protocol.fixVer) + this.writeHex(Protocol._00BaKey) + this.write(TEACryptor.CRYPTOR_0825KEY.encrypt(object : ByteArrayDataOutputStream() { + override fun toByteArray(): ByteArray { + this.writeHex("00 02 00 00 08 04 01 E0") + this.writeHex(Protocol._0825data2) + this.writeHex("00 00 38") + this.write(token0825) + this.writeHex("01 03 00 19") + this.writeHex(Protocol.publicKey) + this.writeHex("13 00 05 00 00 00 00") + this.writeVarInt(sequence.toUInt()) + this.writeHex("00 28") + this.write(token00BA) + this.writeHex("00 10") + this.writeHex(Protocol._00BaFixKey) + return super.toByteArray() + } + }.toByteArray())) + } +} \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientPasswordSubmissionPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientPasswordSubmissionPacket.kt deleted file mode 100644 index 4e6475b85..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientPasswordSubmissionPacket.kt +++ /dev/null @@ -1,106 +0,0 @@ -package net.mamoe.mirai.network.packet.client.login - -import net.mamoe.mirai.network.Protocol -import net.mamoe.mirai.network.packet.PacketId -import net.mamoe.mirai.network.packet.client.* -import net.mamoe.mirai.util.TEACryptor -import net.mamoe.mirai.util.getCrc32 -import net.mamoe.mirai.util.getRandomKey -import net.mamoe.mirai.util.hexToBytes -import java.io.IOException -import java.net.InetAddress - -/** - * Password submission (0836_622) - * - * @author Him188moe @ Mirai Project - */ -@PacketId("08 36 31 03") -@ExperimentalUnsignedTypes -class ClientPasswordSubmissionPacket(private val qq: Int, private val password: String, private val loginTime: Int, private val loginIP: String, private val tgtgtKey: ByteArray, private val token0825: ByteArray) : ClientPacket() { - @ExperimentalUnsignedTypes - override fun encode() { - this.writeQQ(qq) - this.writeHex(Protocol._0836_622_fix1) - this.writeHex(Protocol.publicKey) - this.writeHex("00 00 00 10") - this.writeHex(Protocol._0836key1) - - //TEA 加密 - this.write(TEACryptor.encrypt(object : ClientPacket() { - @Throws(IOException::class) - override fun encode() { - val hostName: String = InetAddress.getLocalHost().hostName.let { it.substring(0, it.length - 3) }; - - this.writeQQ(System.currentTimeMillis().toInt())//that's correct - this.writeHex("01 12");//tag - this.writeHex("00 38");//length - this.write(token0825);//length - this.writeHex("03 0F");//tag - this.writeShort(hostName.length / 2);//todo check that - this.writeShort(hostName.length); - this.writeBytes(hostName)//todo 这个对吗? - /*易语言源码: PCName就是HostName - PCName = BytesToStr (Ansi转Utf8 (取主机名 ())) - PCName = 取文本左边 (PCName, 取文本长度 (PCName) - 3)*/ - - this.writeHex("00 05 00 06 00 02") - this.writeQQ(qq) - this.writeHex("00 06")//tag - this.writeHex("00 78")//length - this.writeTLV0006(qq, password, loginTime, loginIP, tgtgtKey) - //fix - this.writeHex(Protocol._0836_622_fix2) - this.writeHex("00 1A")//tag - this.writeHex("00 40")//length - this.write(TEACryptor.encrypt(Protocol._0836_622_fix2.hexToBytes(), tgtgtKey)) - this.writeHex(Protocol._0825data0) - this.writeHex(Protocol._0825data2) - this.writeQQ(qq) - this.writeZero(4) - - this.writeHex("01 03")//tag - this.writeHex("00 14")//length - - this.writeHex("00 01")//tag - this.writeHex("00 10")//length - this.writeHex("60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6")//key - - this.writeHex("03 12")//tag - this.writeHex("00 05")//length - this.writeHex("01 00 00 00 01")//value - - this.writeHex("05 08")//tag - this.writeHex("00 05")//length - this.writeHex("10 00 00 00 00")//value - - this.writeHex("03 13")//tag - this.writeHex("00 19")//length - this.writeHex("01")//value - - this.writeHex("01 02")//tag - this.writeHex("00 10")//length - this.writeHex("04 EA 78 D1 A4 FF CD CC 7C B8 D4 12 7D BB 03 AA")//key - this.writeZero(3) - this.writeByte(0)//maybe 00, 0F, 1F - - this.writeHex("01 02")//tag - this.writeHex("00 62")//length - this.writeHex("00 01")//word? - this.writeHex("04 EB B7 C1 86 F9 08 96 ED 56 84 AB 50 85 2E 48")//key - this.writeHex("00 38")//length - //value - this.writeHex("E9 AA 2B 4D 26 4C 76 18 FE 59 D5 A9 82 6A 0C 04 B4 49 50 D7 9B B1 FE 5D 97 54 8D 82 F3 22 C2 48 B9 C9 22 69 CA 78 AD 3E 2D E9 C9 DF A8 9E 7D 8C 8D 6B DF 4C D7 34 D0 D3") - - this.writeHex("00 14")//length - - getRandomKey(16).let { - write(it)//key - writeLong(getCrc32(it))//todo may be int? check that. - } - } - }.encodeToByteArray(), Protocol.shareKey.hexToBytes())) - } - - -} \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/touch/ClientTouchPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/touch/ClientTouchPacket.kt index 7d388af07..a5169fecb 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/touch/ClientTouchPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/touch/ClientTouchPacket.kt @@ -43,8 +43,8 @@ class ClientTouchPacket : ClientPacket() { this.writeHex(Protocol._0825data2) this.writeQQ(qq) this.writeHex("00 00 00 00 03 09 00 08 00 01") - this.writeIP("192.168.1.1"); - //this.writeIP(Protocol.SERVER_IP[2]); + //this.writeIP("192.168.1.1"); + this.writeIP(Protocol.SERVER_IP[2]); //this.writeIP("123456789") this.writeHex("00 02 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 02 00 19") this.writeHex(Protocol.publicKey) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/ServerPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/ServerPacket.kt index 8416f3aa7..a0dd764e9 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/ServerPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/ServerPacket.kt @@ -1,10 +1,10 @@ package net.mamoe.mirai.network.packet.server import net.mamoe.mirai.network.packet.Packet -import net.mamoe.mirai.network.packet.server.login.ServerLoginFailedResponsePacket -import net.mamoe.mirai.network.packet.server.login.ServerLoginResendResponsePacket -import net.mamoe.mirai.network.packet.server.login.ServerLoginSucceedResponsePacket -import net.mamoe.mirai.network.packet.server.login.ServerLoginVerificationCodeResponsePacket +import net.mamoe.mirai.network.packet.server.login.ServerLoginResponseFailedPacket +import net.mamoe.mirai.network.packet.server.login.ServerLoginResponseResendPacket +import net.mamoe.mirai.network.packet.server.login.ServerLoginResponseSucceedPacket +import net.mamoe.mirai.network.packet.server.login.ServerLoginResponseVerificationCodePacket import net.mamoe.mirai.network.packet.server.touch.ServerTouchResponsePacket import net.mamoe.mirai.util.toHexString import java.io.DataInputStream @@ -18,35 +18,36 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { companion object { - fun ofByteArray(bytes: ByteArray): ServerPacket { + fun ofByteArray(bytes: ByteArray, tgtgtKey: ByteArray?): ServerPacket { val stream = DataInputStream(bytes.inputStream()) stream.skipUntil(10) val idBytes = stream.readUntil(11) - return when (idBytes.joinToString("") { it.toString(16) }) { + return when (val flag = idBytes.joinToString("") { it.toString(16) }) { "08 25 31 01" -> ServerTouchResponsePacket(ServerTouchResponsePacket.Type.TYPE_08_25_31_01, stream) "08 25 31 02" -> ServerTouchResponsePacket(ServerTouchResponsePacket.Type.TYPE_08_25_31_02, stream) "08 36 31 03", "08 36 31 04", "08 36 31 05", "08 36 31 06" -> { when (bytes.size) { - 271, 207 -> { - ServerLoginResendResponsePacket(stream) - } - 871 -> return ServerLoginVerificationCodeResponsePacket(stream) + 271, 207 -> return ServerLoginResponseResendPacket(stream, when (flag) { + "08 36 31 03" -> ServerLoginResponseResendPacket.Flag.`08 36 31 03` + else -> ServerLoginResponseResendPacket.Flag.OTHER + }) + 871 -> return ServerLoginResponseVerificationCodePacket(stream) } if (bytes.size > 700) { - return ServerLoginSucceedResponsePacket(stream) + return ServerLoginResponseSucceedPacket(stream) } - return ServerLoginFailedResponsePacket(when (bytes.size) { - 319 -> ServerLoginFailedResponsePacket.State.WRONG_PASSWORD - 135 -> ServerLoginFailedResponsePacket.State.RETYPE_PASSWORD - 279 -> ServerLoginFailedResponsePacket.State.BLOCKED - 263 -> ServerLoginFailedResponsePacket.State.UNKNOWN_QQ_NUMBER - 551, 487 -> ServerLoginFailedResponsePacket.State.DEVICE_LOCK - 359 -> ServerLoginFailedResponsePacket.State.TAKEN_BACK + return ServerLoginResponseFailedPacket(when (bytes.size) { + 319 -> ServerLoginResponseFailedPacket.State.WRONG_PASSWORD + 135 -> ServerLoginResponseFailedPacket.State.RETYPE_PASSWORD + 279 -> ServerLoginResponseFailedPacket.State.BLOCKED + 263 -> ServerLoginResponseFailedPacket.State.UNKNOWN_QQ_NUMBER + 551, 487 -> ServerLoginResponseFailedPacket.State.DEVICE_LOCK + 359 -> ServerLoginResponseFailedPacket.State.TAKEN_BACK else -> throw IllegalStateException() }, stream) } @@ -77,7 +78,7 @@ fun DataInputStream.readUntil(byte: Byte): ByteArray { fun DataInputStream.readIP(): String { var buff = "" for (i in 0..3) { - val byte = readByte(); + val byte = readByte() buff += (byte.toUByte().toString()) if (i != 3) buff += "." println(byte.toHexString()) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginFailedResponsePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseFailedPacket.kt similarity index 91% rename from mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginFailedResponsePacket.kt rename to mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseFailedPacket.kt index b258b0279..7cf34dc0a 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginFailedResponsePacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseFailedPacket.kt @@ -6,7 +6,7 @@ import java.io.DataInputStream /** * @author Him188moe @ Mirai Project */ -class ServerLoginFailedResponsePacket(val state: State, input: DataInputStream) : ServerPacket(input) { +class ServerLoginResponseFailedPacket(val state: State, input: DataInputStream) : ServerPacket(input) { enum class State { WRONG_PASSWORD, // UNKNOWN,//? 要再次发送某数据包 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponsePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponsePacket.kt new file mode 100644 index 000000000..5b8895ad8 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponsePacket.kt @@ -0,0 +1,5 @@ +package net.mamoe.mirai.network.packet.server.login + +/** + * @author Him188moe @ Mirai Project + */ \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResendResponsePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseResendPacket.kt similarity index 63% rename from mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResendResponsePacket.kt rename to mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseResendPacket.kt index 3764c020a..80d084dcd 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResendResponsePacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseResendPacket.kt @@ -8,7 +8,7 @@ import java.io.DataInputStream * @author Him188moe @ Mirai Project */ @PacketId("08 36 31 03") -class ServerLoginResendResponsePacket(input: DataInputStream, private val flag: Flag) : ServerPacket(input) { +class ServerLoginResponseResendPacket(input: DataInputStream, val flag: Flag) : ServerPacket(input) { enum class Flag { `08 36 31 03`, OTHER, @@ -18,11 +18,16 @@ class ServerLoginResendResponsePacket(input: DataInputStream, private val flag: lateinit var token: ByteArray lateinit var tgtgtKey: ByteArray - override fun decode() { - _0836_tlv0006_encr = 取文本中间(data, 76, 359) + override fun decode() {//todo 检查 + this.input.skip(8) + tgtgtKey = this.input.readNBytes(13) + this.input.skip(17) + + _0836_tlv0006_encr = this.input.readNBytes(179) when (flag) { Flag.`08 36 31 03` -> { - token = 取文本中间(data, 460, 167) + this.input.skip(13) + token = this.input.readNBytes(83) } Flag.OTHER -> { @@ -30,6 +35,5 @@ class ServerLoginResendResponsePacket(input: DataInputStream, private val flag: //[this.token] will be set in [Robot] } } - m_tgtgtKey = 取文本中间(data, 16, 47) } } \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResendResponsePacket.md b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseResendPacket.md similarity index 81% rename from mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResendResponsePacket.md rename to mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseResendPacket.md index 99e4d7898..ac0e9f61a 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResendResponsePacket.md +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseResendPacket.md @@ -1,7 +1,7 @@ # ServerLoginResendResponsePacket -## Dispose_0836 +## Dispose_0836 (RESEND) data = TeaDecrypt (取文本中间 (data, 43, 取文本长度 (data) - 45), #shareKey) data = TeaDecrypt (data, m_tgtgtKey) .如果真 (data ≠ “”) @@ -98,3 +98,20 @@ pack.PutDword (crc32_data) 调试输出 (pack.GetPacket ()) ret = #head + “37 13 08 36 ” + seq + “ ” + g_QQ + fix1 + #publicKey + “ 00 00 00 10 ” + _0836key1 + TeaEncrypt (pack.GetPacket (), #shareKey) + #tail 返回 (ret) + + + + + +## Dispose_0836 (VERIFICATION CODE) +data = 取文本中间 (data, 43, 取文本长度 (data) - 45) +data = TeaDecrypt (data, #shareKey) +verifyCode_length = HexToDec (取文本中间 (data, 235, 5)) +g_verifyCode = 取文本中间 (data, 241, verifyCode_length × 3 - 1) +m_bool = 取文本中间 (data, 245 + verifyCode_length × 3 - 1, 2) = “01” +m_00BaToken = 取文本中间 (data, 取文本长度 (data) - 178, 119) +.如果真 (m_bool) + g_sequence = 1 + SendUdp (#head + #ver + “00 BA 31 01 ” + g_QQ + #fixVer + #_00BaKey + “ ” + TeaEncrypt (“00 02 00 00 08 04 01 E0 ” + #_0825date2 + “00 00 38 ” + m_0825token + “ 01 03 00 19 ” + #publicKey + “ 13 00 05 00 00 00 00 ” + DecToHex (g_sequence) + “ 00 28 ” + m_00BaToken + “ 00 10 ” + #_00BaFixKey, #_00BaKey) + #tail) +.如果真结束 +返回 () diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseSucceedPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseSucceedPacket.kt new file mode 100644 index 000000000..c5064185e --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseSucceedPacket.kt @@ -0,0 +1,53 @@ +package net.mamoe.mirai.network.packet.server.login + +import net.mamoe.mirai.network.Protocol +import net.mamoe.mirai.network.packet.server.ServerPacket +import net.mamoe.mirai.util.TEACryptor +import net.mamoe.mirai.util.hexToBytes +import net.mamoe.mirai.util.hexToShort +import java.io.DataInputStream + +/** + * @author Him188moe @ Mirai Project + */ +class ServerLoginResponseSucceedPacket(input: DataInputStream, val tgtgtKey: ByteArray) : ServerPacket(input) { + lateinit var _0828_rec_decr_key: ByteArray + lateinit var nick_length: ByteArray + var age: Int = 0 + var gender: Boolean = false//from 1byte + lateinit var nick: String + lateinit var clientKey: String + + + @ExperimentalUnsignedTypes + override fun decode() { + input.skip(21) + val data = input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }//Drop tail + + val decryptedData = TEACryptor.decrypt(TEACryptor.decrypt(data, Protocol.shareKey.hexToBytes()), tgtgtKey); + + DataInputStream(decryptedData.inputStream()).let { + //换成 readShort + + it.skip(212) + val msgLength = when (it.readShort()) { + "01 07".hexToShort() -> 0 + "00 33".hexToShort() -> 28 * 3 + "01 10".hexToShort() -> 64 * 3 + else -> throw IllegalStateException() + } + + it.skip(((514 + msgLength) / 2 - 212 - 2).toLong()) + _0828_rec_decr_key = it.readBytes(取文本中间(it, 514 + msgLength, 47)) + nick_length = it.readByte(取文本中间(it, 1873 + msgLength, 2)) + nick = it.readBytes(取文本中间(it, 1876 + msgLength, 3 * nick_length - 1)).toString() + + age = it.readShort(取文本中间(it, 取文本长度(it) - 82, 5)).toBoolean() + gender = it.readByte(取文本中间(it, 取文本长度(it) - 94, 2)) + clientKey = it.readBytes(取文本中间(it, 484 * 3 + msgLength + 1, 112 * 3 - 1)) + } + + + //SendUdp (Construct_0828 (“04 34”, 取文本中间 (data, 76, 167), 取文本中间 (data, 568 + msg_length, 407), 取文本中间 (data, 22, 47))) + } +} \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseVerificationCodePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseVerificationCodePacket.kt new file mode 100644 index 000000000..7cc2d5067 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseVerificationCodePacket.kt @@ -0,0 +1,26 @@ +package net.mamoe.mirai.network.packet.server.login + +import net.mamoe.mirai.network.Protocol +import net.mamoe.mirai.network.packet.server.ServerPacket +import java.io.DataInputStream + +/** + * @author Him188moe @ Mirai Project + */ +class ServerLoginResponseVerificationCodePacket(input: DataInputStream) : ServerPacket(input) { + var verifyCodeLength: Int = 0 + lateinit var verifyCode: String + lateinit var token00BA: ByteArray + var unknownBoolean: Boolean? = null + + + override fun decode() { + data = 取文本中间(data, 43, 取文本长度(data) - 45) + data = TeaDecrypt(data, Protocol.shareKey) + + verifyCodeLength = HexToDec(取文本中间(data, 235, 5)) + verifyCode = 取文本中间(data, 241, verifyCodeLength * 3 - 1) + unknownBoolean = 取文本中间(data, 245 + verifyCodeLength * 3 - 1, 2) == "01" + token00BA = 取文本中间(data, 取文本长度(data) - 178, 119) + } +} \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginSucceedResponsePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginSucceedResponsePacket.kt deleted file mode 100644 index c030e730a..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginSucceedResponsePacket.kt +++ /dev/null @@ -1,15 +0,0 @@ -package net.mamoe.mirai.network.packet.server.login - -import net.mamoe.mirai.network.packet.server.ServerPacket -import java.io.DataInputStream - -/** - * @author Him188moe @ Mirai Project - */ -class ServerLoginSucceedResponsePacket(input: DataInputStream) : ServerPacket(input) { - - - override fun decode() { - TODO() - } -} \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginVerificationCodeResponsePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginVerificationCodeResponsePacket.kt deleted file mode 100644 index 803553d72..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginVerificationCodeResponsePacket.kt +++ /dev/null @@ -1,19 +0,0 @@ -package net.mamoe.mirai.network.packet.server.login - -import net.mamoe.mirai.network.packet.server.ServerPacket -import java.io.DataInputStream - -/** - * @author Him188moe @ Mirai Project - */ -class ServerLoginVerificationCodeResponsePacket(input: DataInputStream) : ServerPacket(input) { - private var verifyCodeLength: Int = 0 - private lateinit var verifyCode: String - private lateinit var token00BA: ByteArray - - - override fun decode() { - - TODO() - } -} \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/util/TEACryptor.java b/mirai-core/src/main/java/net/mamoe/mirai/util/TEACryptor.java index a69fc0dd2..6b24973e8 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/util/TEACryptor.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/util/TEACryptor.java @@ -10,6 +10,7 @@ import java.util.Random; */ public class TEACryptor { public static final TEACryptor CRYPTOR_0825KEY = new TEACryptor(Protocol.Companion.hexToBytes(Protocol._0825key)); + public static final TEACryptor CRYPTOR_00BAKEY = new TEACryptor(Protocol.Companion.hexToBytes(Protocol._00BaKey)); private static final long UINT32_MASK = 0xffffffffL; private final long[] mKey; @@ -33,11 +34,11 @@ public class TEACryptor { isFirstBlock = true; } - public static byte[] encrypt(byte[] key, byte[] source) { + public static byte[] encrypt(byte[] source, byte[] key) { return new TEACryptor(key).encrypt(source); } - public static byte[] decrypt(byte[] key, byte[] source) { + public static byte[] decrypt(byte[] source, byte[] key) { return new TEACryptor(key).decrypt(source); } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/util/Utils.kt b/mirai-core/src/main/java/net/mamoe/mirai/util/Utils.kt index 5b9eea91c..e92d8e9fe 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/util/Utils.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/util/Utils.kt @@ -42,6 +42,9 @@ fun String.hexToBytes(): ByteArray = Protocol.hexToBytes(this) @ExperimentalUnsignedTypes fun String.hexToUBytes(): UByteArray = Protocol.hexToUBytes(this) +@ExperimentalUnsignedTypes//todo test that +fun String.hexToShort(): Short = hexToBytes().let { ((it[1].toInt() shl 8) + it[0]).toShort() } + open class ByteArrayDataOutputStream : DataOutputStream(ByteArrayOutputStream()) { open fun toByteArray(): ByteArray = (out as ByteArrayOutputStream).toByteArray() @ExperimentalUnsignedTypes