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 264533207..afb353be8 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 @@ -14,10 +14,8 @@ import net.mamoe.mirai.network.packet.client.ClientPacket 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.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.login.* +import net.mamoe.mirai.network.packet.server.security.ServerSessionKeyResponsePacketEncrypted import net.mamoe.mirai.network.packet.server.touch.ServerTouchResponsePacket import net.mamoe.mirai.util.getRandomKey import net.mamoe.mirai.utils.MiraiLogger @@ -34,12 +32,24 @@ class Robot(val number: Int, private val password: String) { private var channel: Channel? = null + private lateinit var serverIP: String; + 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 + /** + * Kind of key, similar to sessionKey + */ + private lateinit var tlv0105: ByteArray + private lateinit var sessionKey: ByteArray + /** + * Kind of key, similar to sessionKey + */ + private lateinit var _0828_rec_decr_key: ByteArray + @ExperimentalUnsignedTypes private var md5_32: ByteArray = getRandomKey(32) @@ -51,23 +61,13 @@ class Robot(val number: Int, private val password: String) { is ServerTouchResponsePacket -> { if (packet.serverIP != null) {//redirection connect(packet.serverIP!!) - sendPacket(ClientServerRedirectionPacket( - serverIP = packet.serverIP!!, - qq = number - )) + sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, 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, - loginTime = packet.loginTime, - loginIP = packet.loginIP, - token0825 = packet.token, - tgtgtKey = packet.tgtgtKey - )) + sendPacket(ClientPasswordSubmissionPacket(this.number, this.password, packet.loginTime, packet.loginIP, packet.tgtgtKey, packet.token)) } } @@ -82,46 +82,29 @@ class Robot(val number: Int, private val password: String) { 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 - )) + sendPacket(ClientLoginVerificationCodePacket(this.number, this.token0825, this.sequence, this.token00BA)) } } is ServerLoginResponseSucceedPacket -> { - + this._0828_rec_decr_key = packet._0828_rec_decr_key + sendPacket(ClientLoginSucceedConfirmationPacket(this.number, this.serverIP, this.md5_32, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105)) } //这个有可能是客户端发送验证码之后收到的回复验证码是否正确? 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 - )) + sendPacket(ClientLoginResendPacket3104(this.number, this.password, this.loginTime, this.loginIP, this.tgtgtKey!!, this.token0825, this.token00BA)) } else { - sendPacket(ClientLoginResendPacket3106( - tgtgtKey = packet.tgtgtKey, - token00BA = packet.token, - qq = this.number, - password = this.password, - loginIP = this.loginIP, - loginTime = this.loginTime, - token0825 = this.token0825 - )) + sendPacket(ClientLoginResendPacket3106(this.number, this.password, this.loginTime, this.loginIP, this.tgtgtKey!!, this.token0825, this.token00BA)) } } + is ServerLoginResponseSucceedPacketEncrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!)) + is ServerSessionKeyResponsePacketEncrypted -> onPacketReceived(packet.decrypt(this._0828_rec_decr_key)) + else -> throw IllegalStateException() } @@ -140,14 +123,15 @@ class Robot(val number: Int, private val password: String) { @ExperimentalUnsignedTypes @Throws(InterruptedException::class) - fun connect(host: String, port: Int = 8000) { + fun connect(ip: String, port: Int = 8000) { + this.serverIP = ip; val group = NioEventLoopGroup() try { val b = Bootstrap() b.group(group) .channel(NioSocketChannel::class.java) - .remoteAddress(InetSocketAddress(host, port)) + .remoteAddress(InetSocketAddress(ip, port)) .handler(object : ChannelInitializer<SocketChannel>() { @Throws(Exception::class) override fun initChannel(ch: SocketChannel) { @@ -163,7 +147,7 @@ class Robot(val number: Int, private val password: String) { Reader.init() remaining }*/ - this@Robot.onPacketReceived(ServerPacket.ofByteArray(bytes, tgtgtKey)) + this@Robot.onPacketReceived(ServerPacket.ofByteArray(bytes)) } catch (e: Exception) { MiraiLogger.catching(e) } 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 index b2c0002b8..3539b77c9 100644 --- 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 @@ -5,6 +5,7 @@ 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.getRandomKey import net.mamoe.mirai.util.hexToBytes /** @@ -33,29 +34,14 @@ class ClientPasswordSubmissionPacket(private val qq: Int, private val password: } } -/** - * 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() { @@ -82,6 +68,57 @@ open class ClientLoginResendPacket(val qq: Int, val password: String, val loginT } } +@ExperimentalUnsignedTypes +@PacketId("08 28 04 34")//todo +class ClientLoginSucceedConfirmationPacket( + private val qq: Int, + private val serverIp: String, + private val md5_32: ByteArray, + private val token38: ByteArray, + private val token88: ByteArray, + private val encryptionKey: ByteArray, + private val tlv0105: ByteArray +) : ClientPacket() { + override fun encode() { + this.writeQQ(qq) + this.writeHex("02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A") + this.writeHex("00 38") + this.write(token38) + this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() { + override fun toByteArray(): ByteArray { + this.writeHex("00 07 00 88") + this.write(token88) + this.writeHex("00 0C 00 16 00 02 00 00 00 00 00 00 00 00 00 00") + this.writeIP(serverIp) + this.writeHex("1F 40 00 00 00 00 00 15 00 30 00 01")//fix1 + this.writeHex("01 92 A5 D2 59 00 10 54 2D CF 9B 60 BF BB EC 0D D4 81 CE 36 87 DE 35 02 AE 6D ED DC 00 10 ") + this.writeHex(Protocol._0836fix) + this.writeHex("00 36 00 12 00 02 00 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00") + this.writeHex(Protocol._0825data0) + this.writeHex(Protocol._0825data2) + this.writeQQ(qq) + this.writeHex("00 00 00 00 00 1F 00 22 00 01") + this.writeHex("1A 68 73 66 E4 BA 79 92 CC C2 D4 EC 14 7C 8B AF 43 B0 62 FB 65 58 A9 EB 37 55 1D 26 13 A8 E5 3D")//device ID + this.write(tlv0105) + this.writeHex("01 0B 00 85 00 02") + this.writeHex("B9 ED EF D7 CD E5 47 96 7A B5 28 34 CA 93 6B 5C")//fix2 + this.write(getRandomKey(1)) + this.writeHex("10 00 00 00 00 00 00 00 02") + + //fix3 + this.writeHex("00 63 3E 00 63 02 04 03 06 02 00 04 00 52 D9 00 00 00 00 A9 58 3E 6D 6D 49 AA F6 A6 D9 33 0A E7 7E 36 84 03 01 00 00 68 20 15 8B 00 00 01 02 00 00 03 00 07 DF 00 0A 00 0C 00 01 00 04 00 03 00 04 20 5C 00") + this.write(md5_32) + this.writeHex("68") + + this.writeHex("00 00 00 00 00 2D 00 06 00 01") + this.writeIP("127.0.0.1")//本地IP地址? todo test that + + return super.toByteArray() + } + }.toByteArray(), encryptionKey)) + } +} + /** * @author Him188moe @ Mirai Project */ 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 a0dd764e9..ef7c60590 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 @@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet.server import net.mamoe.mirai.network.packet.Packet 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.ServerLoginResponseSucceedPacketEncrypted import net.mamoe.mirai.network.packet.server.login.ServerLoginResponseVerificationCodePacket import net.mamoe.mirai.network.packet.server.touch.ServerTouchResponsePacket import net.mamoe.mirai.util.toHexString @@ -18,7 +18,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { companion object { - fun ofByteArray(bytes: ByteArray, tgtgtKey: ByteArray?): ServerPacket { + fun ofByteArray(bytes: ByteArray): ServerPacket { val stream = DataInputStream(bytes.inputStream()) @@ -38,7 +38,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { } if (bytes.size > 700) { - return ServerLoginResponseSucceedPacket(stream) + return ServerLoginResponseSucceedPacketEncrypted(stream) } return ServerLoginResponseFailedPacket(when (bytes.size) { @@ -85,3 +85,6 @@ fun DataInputStream.readIP(): String { } return buff } + + +fun ByteArray.dataInputStream(): DataInputStream = DataInputStream(this.inputStream()) \ No newline at end of file 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 deleted file mode 100644 index 5b8895ad8..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponsePacket.kt +++ /dev/null @@ -1,5 +0,0 @@ -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/ServerLoginResponseSucceedPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseSucceedPacket.kt index ed2dce5ae..6f02496c8 100644 --- 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 @@ -2,6 +2,7 @@ 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.network.packet.server.dataInputStream import net.mamoe.mirai.util.TEACryptor import net.mamoe.mirai.util.hexToBytes import net.mamoe.mirai.util.hexToShort @@ -11,49 +12,51 @@ import java.io.DataInputStream * @author Him188moe @ Mirai Project * @author NaturalHG @ Mirai Project */ -class ServerLoginResponseSucceedPacket(input: DataInputStream, val tgtgtKey: ByteArray) : ServerPacket(input) { +class ServerLoginResponseSucceedPacket(input: DataInputStream) : ServerPacket(input) { lateinit var _0828_rec_decr_key: ByteArray var age: Int = 0 var gender: Boolean = false//from 1byte lateinit var nick: String lateinit var clientKey: String + lateinit var token38: ByteArray + lateinit var token88: ByteArray + lateinit var encryptionKey: ByteArray + @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()) { + this.input.skip(141)//取文本中间 (data, 141 * 3 + 1, 5) + val msgLength = when (this.input.readShort()) { "01 07".hexToShort() -> 0 "00 33".hexToShort() -> 28 * 3 "01 10".hexToShort() -> 64 * 3 else -> throw IllegalStateException() } - age = it.readShort(取文本中间(it, 取文本长度(it) - 82, 5)).toBoolean() - gender = it.readByte(取文本中间(it, 取文本长度(it) - 94, 2)) + _0828_rec_decr_key = 取文本中间(data, 514 + msgLength, 47) + val nickLength = HexToDec(取文本中间(data, 1873 + msgLength, 2)) + nick = 转_Ansi文本(取文本中间(data, 1876 + msgLength, 3 * nickLength - 1)) + age = HexToDec(取文本中间(data, 取文本长度(data) - 82, 5)) + gender = 取文本中间(data, 取文本长度(data) - 94, 2) + clientKey = 删全部空(取文本中间(data, 484 * 3 + msgLength + 1, 112 * 3 - 1)) - var position = ((514 + msgLength) / 2 - 212 - 2).toLong(); - it.skip(position) - _0828_rec_decr_key = it.readNBytes(13) - it.skip((1873 + msgLength) / 2 - position) - position += (1873 + msgLength) / 2 - - nick = it.readNBytes(it.readByte().toInt()).toString() - - - 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))) + token38 = 取文本中间(data, 76, 167) + token88 = 取文本中间(data, 568 + msgLength, 407) + encryptionKey = 取文本中间(data, 22, 47) } -} \ No newline at end of file +} + +class ServerLoginResponseSucceedPacketEncrypted(input: DataInputStream) : ServerPacket(input) { + override fun decode() { + + } + + @ExperimentalUnsignedTypes + fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseSucceedPacket {//todo test + this.input.skip(14) + return ServerLoginResponseSucceedPacket(TEACryptor.decrypt(TEACryptor.decrypt(this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, Protocol.shareKey.hexToBytes()), tgtgtKey).dataInputStream()); + //TeaDecrypt(取文本中间(data, 43, 取文本长度(data) - 45), m_0828_rec_decr_key) + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/security/ServerSessionKeyResponsePacketEncrypted.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/security/ServerSessionKeyResponsePacketEncrypted.kt new file mode 100644 index 000000000..7a7c20e0a --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/security/ServerSessionKeyResponsePacketEncrypted.kt @@ -0,0 +1,57 @@ +package net.mamoe.mirai.network.packet.server.security + +import net.mamoe.mirai.network.packet.server.ServerPacket +import net.mamoe.mirai.network.packet.server.dataInputStream +import net.mamoe.mirai.util.TEACryptor +import java.io.DataInputStream + +/** + * Dispose_0828 + * + * @author Him188moe @ Mirai Project + */ +class ServerSessionKeyResponsePacket(inputStream: DataInputStream) : ServerPacket(inputStream) { + override fun decode() { + var data = this.input.readAllBytes(); + when (data.size) { + 407 -> { + } + + 439 -> { + + } + + 527 -> { + + } + else -> { + } + } + + .判断开始(length = 407) + g_sessionKey = 取文本中间 (data, 76, 47) + .判断(length = 439) + g_sessionKey = 取文本中间 (data, 190, 47) + .判断(length = 527) + g_sessionKey = 取文本中间 (data, 190, 47) + g_tlv0105 = “01 05 00 88 00 01 01 02 ” + “00 40 02 01 03 3C 01 03 00 00 ” + 取文本中间 (data, 取文本长度 (data) - 367, 167) + “ 00 40 02 02 03 3C 01 03 00 00 ” + 取文本中间 (data, 取文本长度 (data) - 166, 167) + + } +} + +/** + * Encrypted using []0828_rec_decr_key], decrypting in Robot + * + * @author Him188moe @ Mirai Project + */ +class ServerSessionKeyResponsePacketEncrypted(inputStream: DataInputStream) : ServerPacket(inputStream) { + override fun decode() { + + } + + fun decrypt(_0828_rec_decr_key: ByteArray): ServerSessionKeyResponsePacket {//todo test + this.input.skip(14) + return ServerSessionKeyResponsePacket(TEACryptor.decrypt(this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, _0828_rec_decr_key).dataInputStream()); + //TeaDecrypt(取文本中间(data, 43, 取文本长度(data) - 45), m_0828_rec_decr_key) + } +} \ No newline at end of file