This commit is contained in:
Him188moe 2019-08-18 16:42:20 +08:00
parent da0e2e829e
commit 2e1b399d73
6 changed files with 174 additions and 95 deletions

View File

@ -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)
}

View File

@ -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
*/

View File

@ -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())

View File

@ -1,5 +0,0 @@
package net.mamoe.mirai.network.packet.server.login
/**
* @author Him188moe @ Mirai Project
*/

View File

@ -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)
}
}
}
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)
}
}

View File

@ -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)
}
}