mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-24 14:30:09 +08:00
Updated robot & network structure
This commit is contained in:
parent
bde4610f7b
commit
512d27b843
@ -4,7 +4,6 @@ import lombok.Getter;
|
||||
import net.mamoe.mirai.event.MiraiEventManager;
|
||||
import net.mamoe.mirai.event.events.server.ServerDisableEvent;
|
||||
import net.mamoe.mirai.event.events.server.ServerEnableEvent;
|
||||
import net.mamoe.mirai.network.RobotNetworkHandler;
|
||||
import net.mamoe.mirai.network.packet.login.LoginState;
|
||||
import net.mamoe.mirai.task.MiraiTaskManager;
|
||||
import net.mamoe.mirai.utils.LoggerTextFormat;
|
||||
@ -36,7 +35,7 @@ public class MiraiServer {
|
||||
@Getter //is running under UNIX
|
||||
private boolean unix;
|
||||
|
||||
@Getter//file path
|
||||
@Getter//file pathq
|
||||
public File parentFolder;
|
||||
|
||||
@Getter
|
||||
@ -121,8 +120,7 @@ public class MiraiServer {
|
||||
this.qqs.keySet().stream().map(key -> this.qqs.getSection(key)).forEach(section -> {
|
||||
try {
|
||||
Robot robot = new Robot(section);
|
||||
RobotNetworkHandler robotNetworkHandler = robot.getNetworkHandler();
|
||||
robotNetworkHandler.tryLogin$mirai_core(state -> {
|
||||
robot.network.tryLogin$mirai_core(state -> {
|
||||
if (state == LoginState.SUCCEED) {
|
||||
Robot.instances.add(robot);
|
||||
} else {
|
||||
|
@ -5,21 +5,58 @@ import net.mamoe.mirai.contact.Group;
|
||||
import net.mamoe.mirai.contact.QQ;
|
||||
import net.mamoe.mirai.network.RobotNetworkHandler;
|
||||
import net.mamoe.mirai.utils.ContactList;
|
||||
import net.mamoe.mirai.utils.RobotAccount;
|
||||
import net.mamoe.mirai.utils.config.MiraiConfigSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class Robot implements Closeable {
|
||||
/**
|
||||
* Robot that is the base of the whole program.
|
||||
* It contains a {@link ContactSystem}, which manage contacts such as {@link QQ} and {@link Group}.
|
||||
*/
|
||||
public final class Robot implements Closeable {
|
||||
public static final List<Robot> instances = Collections.synchronizedList(new LinkedList<>());
|
||||
|
||||
private final long qqNumber;
|
||||
private final String password;
|
||||
@Getter
|
||||
private final RobotNetworkHandler networkHandler;
|
||||
public final RobotAccount account;
|
||||
|
||||
public final ContactSystem contacts = new ContactSystem();
|
||||
|
||||
public final RobotNetworkHandler network;
|
||||
|
||||
/**
|
||||
* Robot 联系人管理.
|
||||
*
|
||||
* @see Robot#contacts
|
||||
*/
|
||||
public final class ContactSystem {
|
||||
private final ContactList<Group> groups = new ContactList<>();
|
||||
private final ContactList<QQ> qqs = new ContactList<>();
|
||||
|
||||
private ContactSystem() {
|
||||
|
||||
}
|
||||
|
||||
public QQ getQQ(long qqNumber) {
|
||||
if (!this.qqs.containsKey(qqNumber)) {
|
||||
this.qqs.put(qqNumber, new QQ(Robot.this, qqNumber));
|
||||
}
|
||||
return this.qqs.get(qqNumber);
|
||||
}
|
||||
|
||||
public Group getGroupByNumber(long groupNumber) {
|
||||
if (!this.groups.containsKey(groupNumber)) {
|
||||
this.groups.put(groupNumber, new Group(Robot.this, groupNumber));
|
||||
}
|
||||
return groups.get(groupNumber);
|
||||
}
|
||||
|
||||
public Group getGroupById(long groupId) {
|
||||
return getGroupByNumber(Group.Companion.groupIdToNumber(groupId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ref list
|
||||
@ -27,57 +64,36 @@ public class Robot implements Closeable {
|
||||
@Getter
|
||||
private final List<String> owners;
|
||||
|
||||
private final ContactList<Group> groups = new ContactList<>();
|
||||
private final ContactList<QQ> qqs = new ContactList<>();
|
||||
|
||||
public void close() {
|
||||
this.networkHandler.close();
|
||||
this.owners.clear();
|
||||
this.groups.values().forEach(Group::close);
|
||||
this.groups.clear();
|
||||
this.qqs.clear();
|
||||
}
|
||||
|
||||
public boolean isOwnBy(String ownerName) {
|
||||
return owners.contains(ownerName);
|
||||
}
|
||||
|
||||
public long getQQNumber() {
|
||||
return qqNumber;
|
||||
}
|
||||
|
||||
public Robot(MiraiConfigSection<Object> data) throws Throwable {
|
||||
this(
|
||||
data.getLongOrThrow("account", () -> new IllegalArgumentException("account")),
|
||||
data.getStringOrThrow("password", () -> new IllegalArgumentException("password")),
|
||||
new RobotAccount(
|
||||
data.getLongOrThrow("account", () -> new IllegalArgumentException("account")),
|
||||
data.getStringOrThrow("password", () -> new IllegalArgumentException("password"))
|
||||
),
|
||||
data.getAsOrDefault("owners", ArrayList::new)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public Robot(long qqNumber, String password, List<String> owners) {
|
||||
this.qqNumber = qqNumber;
|
||||
this.password = password;
|
||||
public Robot(@NotNull RobotAccount account, @NotNull List<String> owners) {
|
||||
Objects.requireNonNull(account);
|
||||
Objects.requireNonNull(owners);
|
||||
this.account = account;
|
||||
this.owners = Collections.unmodifiableList(owners);
|
||||
this.networkHandler = new RobotNetworkHandler(this, this.qqNumber, this.password);
|
||||
this.network = new RobotNetworkHandler(this);
|
||||
}
|
||||
|
||||
public QQ getQQ(long qqNumber) {
|
||||
if (!this.qqs.containsKey(qqNumber)) {
|
||||
this.qqs.put(qqNumber, new QQ(qqNumber));
|
||||
}
|
||||
return this.qqs.get(qqNumber);
|
||||
|
||||
public void close() {
|
||||
this.network.close();
|
||||
this.owners.clear();
|
||||
this.contacts.groups.values().forEach(Group::close);
|
||||
this.contacts.groups.clear();
|
||||
this.contacts.qqs.clear();
|
||||
}
|
||||
|
||||
public Group getGroup(long groupNumber) {
|
||||
if (!this.groups.containsKey(groupNumber)) {
|
||||
this.groups.put(groupNumber, new Group(groupNumber));
|
||||
}
|
||||
return groups.get(groupNumber);
|
||||
}
|
||||
|
||||
public Group getGroupByGroupId(long groupId) {
|
||||
return getGroup(Group.Companion.groupIdToNumber(groupId));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,16 @@
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import net.mamoe.mirai.Robot
|
||||
import net.mamoe.mirai.message.Message
|
||||
import net.mamoe.mirai.message.defaults.PlainText
|
||||
|
||||
/**
|
||||
* A contact is a [QQ] or a [Group] for one particular [Robot] instance only.
|
||||
*
|
||||
* @param robot Owner [Robot]
|
||||
* @author Him188moe
|
||||
*/
|
||||
abstract class Contact(val number: Long) {
|
||||
abstract class Contact(val robot: Robot, val number: Long) {
|
||||
|
||||
/**
|
||||
* Async
|
||||
|
@ -1,15 +1,16 @@
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import net.mamoe.mirai.Robot
|
||||
import net.mamoe.mirai.message.Message
|
||||
import net.mamoe.mirai.utils.ContactList
|
||||
import java.io.Closeable
|
||||
|
||||
class Group(number: Long) : Contact(number), Closeable {
|
||||
class Group(robot: Robot, number: Long) : Contact(robot, number), Closeable {
|
||||
val groupId = groupNumberToId(number)
|
||||
val members = ContactList<QQ>()
|
||||
|
||||
override fun sendMessage(message: Message) {
|
||||
|
||||
robot.network.packetSystem.sendGroupMessage(this, message)
|
||||
}
|
||||
|
||||
override fun sendXMLMessage(message: String) {
|
||||
|
@ -1,15 +1,18 @@
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import net.mamoe.mirai.Robot
|
||||
import net.mamoe.mirai.message.Message
|
||||
import net.mamoe.mirai.message.defaults.At
|
||||
|
||||
/**
|
||||
* A QQ instance helps you to receive message from or send message to.
|
||||
* Notice that one QQ instance belong to one [Robot], that is, QQ instances from different [Robot] are NOT the same.
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
class QQ(number: Long) : Contact(number) {
|
||||
|
||||
class QQ(robot: Robot, number: Long) : Contact(robot, number) {
|
||||
override fun sendMessage(message: Message) {
|
||||
|
||||
robot.network.packetSystem.sendFriendMessage(this, message)
|
||||
}
|
||||
|
||||
override fun sendXMLMessage(message: String) {
|
||||
|
@ -28,26 +28,44 @@ interface Protocol {
|
||||
|
||||
|
||||
const val head = "02"
|
||||
const val ver = "37 13 "
|
||||
const val fixVer = "03 00 00 00 01 2E 01 00 00 68 52 00 00 00 00 "
|
||||
const val tail = " 03"
|
||||
const val _fixVer = "02 00 00 00 01 01 01 00 00 68 20 "
|
||||
const val _0825data0 = "00 18 00 16 00 01 "
|
||||
const val _0825data2 = "00 00 04 53 00 00 00 01 00 00 15 85 "
|
||||
const val _0825key = "A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
|
||||
const val ver = "37 13"
|
||||
const val fixVer = "03 00 00 00 01 2E 01 00 00 68 52 00 00 00 00"
|
||||
const val tail = "03"
|
||||
/**
|
||||
* _fixVer
|
||||
*/
|
||||
const val fixVer2 = "02 00 00 00 01 01 01 00 00 68 20"
|
||||
/**
|
||||
* 0825data1
|
||||
*/
|
||||
const val constantData0 = "00 18 00 16 00 01 "
|
||||
/**
|
||||
* 0825data2
|
||||
*/
|
||||
const val constantData1 = "00 00 04 53 00 00 00 01 00 00 15 85 "
|
||||
const val key0825 = "A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
|
||||
const val redirectionKey = "A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B"
|
||||
const val publicKey = "02 6D 28 41 D2 A5 6F D2 FC 3E 2A 1F 03 75 DE 6E 28 8F A8 19 3E 5F 16 49 D3"
|
||||
const val shareKey = "1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"
|
||||
const val _0836fix = "06 A9 12 97 B7 F8 76 25 AF AF D3 EA B4 C8 BC E7 "
|
||||
const val fix0836 = "06 A9 12 97 B7 F8 76 25 AF AF D3 EA B4 C8 BC E7 "
|
||||
|
||||
const val _00BaKey = "C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94"
|
||||
const val _00BaFixKey = "69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A"
|
||||
const val key00BA = "C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94"
|
||||
const val key00BAFix = "69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A"
|
||||
|
||||
const val encryptKey = "“BA 42 FF 01 CF B4 FF D2 12 F0 6E A7 1B 7C B3 08”"
|
||||
|
||||
const val _0836_622_fix2 = "00 15 00 30 00 01 01 27 9B C7 F5 00 10 65 03 FD 8B 00 00 00 00 00 00 00 00 00 00 00 00 02 90 49 55 33 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B";
|
||||
const val _0836_622_fix1 = "03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03 00 19";
|
||||
const val _0836key1 = "EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA"
|
||||
/**
|
||||
* 0836_622_fix2
|
||||
*/
|
||||
const val passwordSubmissionKey2 = "00 15 00 30 00 01 01 27 9B C7 F5 00 10 65 03 FD 8B 00 00 00 00 00 00 00 00 00 00 00 00 02 90 49 55 33 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B";
|
||||
/**
|
||||
* 0836_622_fix1
|
||||
*/
|
||||
const val passwordSubmissionKey1 = "03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03 00 19";
|
||||
/**
|
||||
* fix_0836_1
|
||||
*/
|
||||
const val key0836 = "EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA"
|
||||
|
||||
private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf()
|
||||
|
||||
|
@ -2,33 +2,36 @@ package net.mamoe.mirai.network
|
||||
|
||||
import net.mamoe.mirai.Robot
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.event.events.qq.FriendMessageEvent
|
||||
import net.mamoe.mirai.event.events.robot.RobotLoginSucceedEvent
|
||||
import net.mamoe.mirai.message.Message
|
||||
import net.mamoe.mirai.network.packet.*
|
||||
import net.mamoe.mirai.network.packet.login.*
|
||||
import net.mamoe.mirai.network.packet.message.ClientSendGroupMessagePacket
|
||||
import net.mamoe.mirai.network.packet.message.ServerSendFriendMessageResponsePacket
|
||||
import net.mamoe.mirai.network.packet.message.ServerSendGroupMessageResponsePacket
|
||||
import net.mamoe.mirai.task.MiraiThreadPool
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.ClientLoginStatus
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.getGTK
|
||||
import net.mamoe.mirai.utils.lazyEncode
|
||||
import java.io.Closeable
|
||||
import java.net.DatagramPacket
|
||||
import java.net.DatagramSocket
|
||||
import java.net.InetSocketAddress
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
/**
|
||||
* A RobotNetworkHandler is used to connect with Tencent servers.
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
@ExperimentalUnsignedTypes
|
||||
internal class RobotNetworkHandler(val robot: Robot, val number: Long, private val password: String) : Closeable {
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")//to simplify code
|
||||
internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
|
||||
|
||||
var socket: DatagramSocket = DatagramSocket((15314 + Math.random() * 100).toInt())
|
||||
private var socket: DatagramSocket = DatagramSocket((15314 + Math.random() * 100).toInt())
|
||||
|
||||
var serverIP: String = ""
|
||||
private var serverIP: String = ""
|
||||
set(value) {
|
||||
serverAddress = InetSocketAddress(value, 8000)
|
||||
field = value
|
||||
@ -45,7 +48,7 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
private lateinit var loginIP: String
|
||||
private var tgtgtKey: ByteArray? = null
|
||||
private var tlv0105: ByteArray
|
||||
private lateinit var _0828_rec_decr_key: ByteArray
|
||||
private lateinit var sessionResponseDecryptionKey: ByteArray
|
||||
|
||||
private var verificationCodeSequence: Int = 0//这两个验证码使用
|
||||
private var verificationCodeCache: ByteArray? = null//每次包只发一部分验证码来
|
||||
@ -82,10 +85,6 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
private var md5_32: ByteArray = getRandomKey(32)
|
||||
|
||||
/**
|
||||
* Try to login to server
|
||||
*/
|
||||
@ -110,7 +109,7 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
if (loginHook != null) {
|
||||
this.loginHook = loginHook
|
||||
}
|
||||
this.sendPacket(ClientTouchPacket(this.number, this.serverIP))
|
||||
this.sendPacket(ClientTouchPacket(this.robot.account.qqNumber, this.serverIP))
|
||||
}
|
||||
|
||||
private fun restartSocket() {
|
||||
@ -151,20 +150,20 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
packet.decode()
|
||||
MiraiLogger info "Packet received: $packet"
|
||||
if (packet is ServerEventPacket) {
|
||||
sendPacket(ClientMessageResponsePacket(this.number, packet.packetId, this.sessionKey, packet.eventIdentity))
|
||||
sendPacket(ClientMessageResponsePacket(this.robot.account.qqNumber, packet.packetId, this.sessionKey, packet.eventIdentity))
|
||||
}
|
||||
when (packet) {
|
||||
is ServerTouchResponsePacket -> {
|
||||
if (packet.serverIP != null) {//redirection
|
||||
serverIP = packet.serverIP!!
|
||||
//connect(packet.serverIP!!)
|
||||
sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, number))
|
||||
sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, this.robot.account.qqNumber))
|
||||
} else {//password submission
|
||||
this.loginIP = packet.loginIP
|
||||
this.loginTime = packet.loginTime
|
||||
this.token0825 = packet.token0825
|
||||
this.tgtgtKey = packet.tgtgtKey
|
||||
sendPacket(ClientPasswordSubmissionPacket(this.number, this.password, packet.loginTime, packet.loginIP, packet.tgtgtKey, packet.token0825))
|
||||
sendPacket(ClientPasswordSubmissionPacket(this.robot.account.qqNumber, this.robot.account.password, packet.loginTime, packet.loginIP, packet.tgtgtKey, packet.token0825))
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,7 +181,7 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
|
||||
if (packet.unknownBoolean != null && packet.unknownBoolean!!) {
|
||||
this.verificationCodeSequence = 1
|
||||
sendPacket(ClientVerificationCodeTransmissionRequestPacket(1, this.number, this.token0825, this.verificationCodeSequence, this.token00BA))
|
||||
sendPacket(ClientVerificationCodeTransmissionRequestPacket(1, this.robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA))
|
||||
}
|
||||
|
||||
}
|
||||
@ -190,7 +189,7 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
is ServerVerificationCodeRepeatPacket -> {//todo 这个名字正确么
|
||||
this.tgtgtKey = packet.tgtgtKeyUpdate
|
||||
this.token00BA = packet.token00BA
|
||||
sendPacket(ClientLoginResendPacket3105(this.number, this.password, this.loginTime, this.loginIP, this.tgtgtKey!!, this.token0825, this.token00BA))
|
||||
sendPacket(ClientLoginResendPacket3105(this.robot.account.qqNumber, this.robot.account.password, this.loginTime, this.loginIP, this.tgtgtKey!!, this.token0825, this.token00BA))
|
||||
}
|
||||
|
||||
is ServerVerificationCodeTransmissionPacket -> {
|
||||
@ -210,26 +209,26 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
this.verificationCodeCache
|
||||
TODO("验证码好了")
|
||||
} else {
|
||||
sendPacket(ClientVerificationCodeTransmissionRequestPacket(this.verificationCodeCacheCount, this.number, this.token0825, this.verificationCodeSequence, this.token00BA))
|
||||
sendPacket(ClientVerificationCodeTransmissionRequestPacket(this.verificationCodeCacheCount, this.robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA))
|
||||
}
|
||||
}
|
||||
|
||||
is ServerLoginResponseSuccessPacket -> {
|
||||
this._0828_rec_decr_key = packet._0828_rec_decr_key
|
||||
sendPacket(ClientSessionRequestPacket(this.number, this.serverIP, this.loginIP, this.md5_32, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105))
|
||||
this.sessionResponseDecryptionKey = packet._0828_rec_decr_key
|
||||
sendPacket(ClientSessionRequestPacket(this.robot.account.qqNumber, this.serverIP, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105))
|
||||
}
|
||||
|
||||
//是ClientPasswordSubmissionPacket之后服务器回复的
|
||||
is ServerLoginResponseResendPacket -> {
|
||||
if (packet.tokenUnknown != null) {
|
||||
//this.token00BA = packet.token00BA!!
|
||||
//println("token00BA changed!!! to " + token00BA.toUByteArray())
|
||||
}
|
||||
//if (packet.tokenUnknown != null) {
|
||||
//this.token00BA = packet.token00BA!!
|
||||
//println("token00BA changed!!! to " + token00BA.toUByteArray())
|
||||
//}
|
||||
if (packet.flag == ServerLoginResponseResendPacket.Flag.`08 36 31 03`) {
|
||||
this.tgtgtKey = packet.tgtgtKey
|
||||
sendPacket(ClientLoginResendPacket3104(
|
||||
this.number,
|
||||
this.password,
|
||||
this.robot.account.qqNumber,
|
||||
this.robot.account.password,
|
||||
this.loginTime,
|
||||
this.loginIP,
|
||||
this.tgtgtKey!!,
|
||||
@ -242,8 +241,8 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
))
|
||||
} else {
|
||||
sendPacket(ClientLoginResendPacket3106(
|
||||
this.number,
|
||||
this.password,
|
||||
this.robot.account.qqNumber,
|
||||
this.robot.account.password,
|
||||
this.loginTime,
|
||||
this.loginIP,
|
||||
this.tgtgtKey!!,
|
||||
@ -261,7 +260,7 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
is ServerSessionKeyResponsePacket -> {
|
||||
this.sessionKey = packet.sessionKey
|
||||
MiraiThreadPool.getInstance().scheduleWithFixedDelay({
|
||||
sendPacket(ClientHeartbeatPacket(this.number, this.sessionKey))
|
||||
sendPacket(ClientHeartbeatPacket(this.robot.account.qqNumber, this.sessionKey))
|
||||
}, 90000, 90000, TimeUnit.MILLISECONDS)
|
||||
RobotLoginSucceedEvent(robot).broadcast()
|
||||
|
||||
@ -270,24 +269,24 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
}, 2, TimeUnit.SECONDS)
|
||||
|
||||
this.tlv0105 = packet.tlv0105
|
||||
sendPacket(ClientLoginStatusPacket(this.number, this.sessionKey, ClientLoginStatus.ONLINE))
|
||||
sendPacket(ClientChangeOnlineStatusPacket(this.robot.account.qqNumber, this.sessionKey, ClientLoginStatus.ONLINE))
|
||||
}
|
||||
|
||||
is ServerLoginSuccessPacket -> {
|
||||
loginState = LoginState.SUCCEED
|
||||
sendPacket(ClientSKeyRequestPacket(this.number, this.sessionKey))
|
||||
sendPacket(ClientSKeyRequestPacket(this.robot.account.qqNumber, this.sessionKey))
|
||||
}
|
||||
|
||||
is ServerSKeyResponsePacket -> {
|
||||
this.sKey = packet.sKey
|
||||
this.cookies = "uin=o" + this.number + ";skey=" + this.sKey + ";"
|
||||
this.cookies = "uin=o" + this.robot.account.qqNumber + ";skey=" + this.sKey + ";"
|
||||
|
||||
MiraiThreadPool.getInstance().scheduleWithFixedDelay({
|
||||
sendPacket(ClientSKeyRefreshmentRequestPacket(this.number, this.sessionKey))
|
||||
sendPacket(ClientSKeyRefreshmentRequestPacket(this.robot.account.qqNumber, this.sessionKey))
|
||||
}, 1800000, 1800000, TimeUnit.MILLISECONDS)
|
||||
|
||||
this.gtk = getGTK(sKey)
|
||||
sendPacket(ClientAccountInfoRequestPacket(this.number, this.sessionKey))
|
||||
sendPacket(ClientAccountInfoRequestPacket(this.robot.account.qqNumber, this.sessionKey))
|
||||
}
|
||||
|
||||
is ServerHeartbeatResponsePacket -> {
|
||||
@ -304,17 +303,12 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
return
|
||||
}
|
||||
|
||||
FriendMessageEvent(this.robot, this.robot.getQQ(packet.qq), packet.message)
|
||||
FriendMessageEvent(this.robot, this.robot.contacts.getQQ(packet.qq), packet.message)
|
||||
}
|
||||
|
||||
is ServerGroupMessageEventPacket -> {
|
||||
//group message
|
||||
if (packet.message == "牛逼") {
|
||||
sendPacket(ClientSendGroupMessagePacket(Group.groupNumberToId(packet.groupNumber), this.number, this.sessionKey, "牛逼!"))
|
||||
}
|
||||
|
||||
//todo
|
||||
//GroupMessageEvent(this.robot, this.robot.getGroup(packet.groupNumber), this.robot.getQQ(packet.qq), packet.message)
|
||||
//todo message chain
|
||||
//GroupMessageEvent(this.robot, this.robot.contacts.getGroupByNumber(packet.groupNumber), this.robot.contacts.getQQ(packet.qq), packet.message)
|
||||
}
|
||||
|
||||
is UnknownServerEventPacket -> {
|
||||
@ -329,17 +323,17 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
|
||||
}
|
||||
|
||||
is ServerMessageEventPacketRaw -> onPacketReceived(packet.analyze())
|
||||
is ServerEventPacket.Raw -> onPacketReceived(packet.distribute())
|
||||
|
||||
is ServerVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt())
|
||||
is ServerLoginResponseVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt())
|
||||
is ServerLoginResponseResendPacketEncrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
|
||||
is ServerLoginResponseSuccessPacketEncrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
|
||||
is ServerSessionKeyResponsePacketEncrypted -> onPacketReceived(packet.decrypt(this._0828_rec_decr_key))
|
||||
is ServerTouchResponsePacketEncrypted -> onPacketReceived(packet.decrypt())
|
||||
is ServerSKeyResponsePacketEncrypted -> onPacketReceived(packet.decrypt(this.sessionKey))
|
||||
is ServerAccountInfoResponsePacketEncrypted -> onPacketReceived(packet.decrypt(this.sessionKey))
|
||||
is ServerMessageEventPacketRawEncoded -> onPacketReceived(packet.decrypt(this.sessionKey))
|
||||
is ServerVerificationCodePacket.Encrypted -> onPacketReceived(packet.decrypt())
|
||||
is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> onPacketReceived(packet.decrypt())
|
||||
is ServerLoginResponseResendPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
|
||||
is ServerLoginResponseSuccessPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
|
||||
is ServerSessionKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionResponseDecryptionKey))
|
||||
is ServerTouchResponsePacket.Encrypted -> onPacketReceived(packet.decrypt())
|
||||
is ServerSKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionKey))
|
||||
is ServerAccountInfoResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionKey))
|
||||
is ServerEventPacket.Raw.Encrypted -> onPacketReceived(packet.decrypt(this.sessionKey))
|
||||
|
||||
|
||||
is ServerSendFriendMessageResponsePacket,
|
||||
@ -352,6 +346,21 @@ internal class RobotNetworkHandler(val robot: Robot, val number: Long, private v
|
||||
|
||||
}
|
||||
|
||||
internal val packetSystem: PacketSystem = PacketSystem()
|
||||
|
||||
inner class PacketSystem {
|
||||
fun sendFriendMessage(qq: QQ, message: Message) {
|
||||
TODO()
|
||||
//sendPacket(ClientSendFriendMessagePacket(robot.account.qqNumber, qq.number, sessionKey, message))
|
||||
}
|
||||
|
||||
fun sendGroupMessage(group: Group, message: Message): Unit {
|
||||
TODO()
|
||||
//sendPacket(ClientSendGroupMessagePacket(group.groupId, robot.account.qqNumber, sessionKey, message))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
fun sendPacket(packet: ClientPacket) {
|
||||
MiraiThreadPool.getInstance().submit {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.mamoe.mirai.network.packet
|
||||
|
||||
import net.mamoe.mirai.network.Protocol
|
||||
import net.mamoe.mirai.utils.TEACryptor
|
||||
import net.mamoe.mirai.utils.TEA
|
||||
import java.io.DataInputStream
|
||||
|
||||
/**
|
||||
@ -18,7 +18,7 @@ class ClientAccountInfoRequestPacket(
|
||||
override fun encode() {
|
||||
this.writeRandom(2)//part of packet id
|
||||
this.writeQQ(qq)
|
||||
this.writeHex(Protocol._fixVer)
|
||||
this.writeHex(Protocol.fixVer2)
|
||||
this.encryptAndWrite(sessionKey) {
|
||||
it.writeByte(0x88)
|
||||
it.writeQQ(qq)
|
||||
@ -27,6 +27,7 @@ class ClientAccountInfoRequestPacket(
|
||||
}
|
||||
}
|
||||
|
||||
@PacketId("00 5C")
|
||||
class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(input) {
|
||||
//等级
|
||||
//升级剩余活跃天数
|
||||
@ -34,16 +35,13 @@ class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(inp
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class ServerAccountInfoResponsePacketEncrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
|
||||
fun decrypt(sessionKey: ByteArray): ServerAccountInfoResponsePacket {
|
||||
this.input goto 14
|
||||
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
|
||||
return ServerAccountInfoResponsePacket(TEACryptor.decrypt(data, sessionKey).dataInputStream());
|
||||
@PacketId("00 5C")
|
||||
class Encrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
|
||||
fun decrypt(sessionKey: ByteArray): ServerAccountInfoResponsePacket {
|
||||
this.input goto 14
|
||||
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
|
||||
return ServerAccountInfoResponsePacket(TEA.decrypt(data, sessionKey).dataInputStream());
|
||||
}
|
||||
}
|
||||
}
|
@ -108,15 +108,15 @@ fun DataOutputStream.writeVarInt(dec: UInt) {
|
||||
}
|
||||
|
||||
fun DataOutputStream.encryptAndWrite(byteArray: ByteArray, key: ByteArray) {
|
||||
this.write(TEACryptor.encrypt(byteArray, key))
|
||||
this.write(TEA.encrypt(byteArray, key))
|
||||
}
|
||||
|
||||
fun DataOutputStream.encryptAndWrite(byteArray: ByteArray, cryptor: TEACryptor) {
|
||||
fun DataOutputStream.encryptAndWrite(byteArray: ByteArray, cryptor: TEA) {
|
||||
this.write(cryptor.encrypt(byteArray))
|
||||
}
|
||||
|
||||
fun DataOutputStream.encryptAndWrite(key: ByteArray, encoder: (ByteArrayDataOutputStream) -> Unit) {
|
||||
this.write(TEACryptor.encrypt(ByteArrayDataOutputStream().let { encoder(it); it.toByteArray() }, key))
|
||||
this.write(TEA.encrypt(ByteArrayDataOutputStream().let { encoder(it); it.toByteArray() }, key))
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
@ -124,7 +124,7 @@ fun DataOutputStream.encryptAndWrite(keyHex: String, encoder: (ByteArrayDataOutp
|
||||
this.encryptAndWrite(keyHex.hexToBytes(), encoder)
|
||||
}
|
||||
|
||||
fun DataOutputStream.encryptAndWrite(cryptor: TEACryptor, encoder: (ByteArrayDataOutputStream) -> Unit) {
|
||||
fun DataOutputStream.encryptAndWrite(cryptor: TEA, encoder: (ByteArrayDataOutputStream) -> Unit) {
|
||||
this.write(cryptor.encrypt(ByteArrayDataOutputStream().let { encoder(it); it.toByteArray() }))
|
||||
}
|
||||
|
||||
@ -132,24 +132,24 @@ fun DataOutputStream.encryptAndWrite(cryptor: TEACryptor, encoder: (ByteArrayDat
|
||||
@Throws(IOException::class)
|
||||
fun DataOutputStream.writeTLV0006(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray) {
|
||||
ByteArrayDataOutputStream().let {
|
||||
it.writeHex("12 12 12 12")//it.writeRandom(4) todo
|
||||
it.writeRandom(4)
|
||||
it.writeHex("00 02")
|
||||
it.writeQQ(qq)
|
||||
it.writeHex(Protocol._0825data2)
|
||||
it.writeHex(Protocol.constantData1)
|
||||
it.writeHex("00 00 01")
|
||||
|
||||
val md5_1 = md5(password);
|
||||
val md5_2 = md5(md5_1 + "00 00 00 00".hexToBytes() + qq.toUInt().toByteArray())
|
||||
it.write(md5_1)
|
||||
val firstMD5 = md5(password)
|
||||
val secondMD5 = md5(firstMD5 + "00 00 00 00".hexToBytes() + qq.toUInt().toByteArray())
|
||||
it.write(firstMD5)
|
||||
it.writeInt(loginTime)
|
||||
it.writeByte(0);
|
||||
it.writeByte(0)
|
||||
it.writeZero(4 * 3)
|
||||
it.writeIP(loginIP)
|
||||
it.writeZero(8)
|
||||
it.writeHex("00 10")
|
||||
it.writeHex("15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B")
|
||||
it.write(tgtgtKey)
|
||||
this.write(TEACryptor.encrypt(it.toByteArray(), md5_2))
|
||||
this.write(TEA.encrypt(it.toByteArray(), secondMD5))
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,7 +216,7 @@ fun Int.toLByteArray(): ByteArray = byteArrayOf(
|
||||
)
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
fun Int.toHexString(separator: String = " "): String = this.toByteArray().toUByteArray().toUHexString(separator);
|
||||
fun Int.toHexString(separator: String = " "): String = this.toByteArray().toUByteArray().toUHexString(separator)
|
||||
|
||||
internal fun md5(str: String): ByteArray = MessageDigest.getInstance("MD5").digest(str.toByteArray())
|
||||
|
||||
|
@ -24,8 +24,4 @@ class ClientHeartbeatPacket(
|
||||
}
|
||||
}
|
||||
|
||||
class ServerHeartbeatResponsePacket(input: DataInputStream) : ServerPacket(input) {
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
}
|
||||
class ServerHeartbeatResponsePacket(input: DataInputStream) : ServerPacket(input)
|
@ -1,84 +0,0 @@
|
||||
package net.mamoe.mirai.network.packet
|
||||
|
||||
import net.mamoe.mirai.network.Protocol
|
||||
import net.mamoe.mirai.utils.TEACryptor
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
import java.io.DataInputStream
|
||||
|
||||
|
||||
/**
|
||||
* 告知服务器已经收到数据
|
||||
*/
|
||||
@PacketId("")//随后写入
|
||||
@ExperimentalUnsignedTypes
|
||||
class ClientMessageResponsePacket(
|
||||
private val qq: Long,
|
||||
private val packetIdFromServer: ByteArray,
|
||||
private val sessionKey: ByteArray,
|
||||
private val eventIdentity: ByteArray
|
||||
) : ClientPacket() {
|
||||
override fun encode() {
|
||||
this.write(packetIdFromServer)
|
||||
this.writeQQ(qq)
|
||||
this.writeHex(Protocol._fixVer)
|
||||
this.encryptAndWrite(sessionKey) {
|
||||
it.write(eventIdentity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 群聊和好友消息分发
|
||||
*/
|
||||
@PacketId("00 17")
|
||||
class ServerMessageEventPacketRaw(
|
||||
input: DataInputStream,
|
||||
private val dataLength: Int,
|
||||
private val packetId: ByteArray
|
||||
) : ServerPacket(input) {
|
||||
lateinit var type: ByteArray;
|
||||
lateinit var eventIdentity: ByteArray;
|
||||
|
||||
override fun decode() {
|
||||
eventIdentity = this.input.readNBytes(16)
|
||||
type = this.input.goto(18).readNBytes(2)
|
||||
}
|
||||
|
||||
fun analyze(): ServerEventPacket = when (val typeHex = type.toUHexString()) {
|
||||
"00 C4" -> {
|
||||
if (this.input.goto(33).readBoolean()) {
|
||||
ServerAndroidOnlineEventPacket(this.input, packetId, eventIdentity)
|
||||
} else {
|
||||
ServerAndroidOfflineEventPacket(this.input, packetId, eventIdentity)
|
||||
}
|
||||
}
|
||||
"00 2D" -> ServerGroupUploadFileEventPacket(this.input, packetId, eventIdentity)
|
||||
|
||||
"00 52" -> ServerGroupMessageEventPacket(this.input, packetId, eventIdentity)
|
||||
|
||||
"00 A6" -> ServerFriendMessageEventPacket(this.input, packetId, eventIdentity)
|
||||
|
||||
//"02 10", "00 12" -> ServerUnknownEventPacket(this.input, packetId, eventIdentity)
|
||||
|
||||
else -> UnknownServerEventPacket(this.input, packetId, eventIdentity)
|
||||
}
|
||||
}
|
||||
|
||||
class UnknownServerEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity)
|
||||
|
||||
@PacketId("00 17")
|
||||
class ServerMessageEventPacketRawEncoded(input: DataInputStream, val packetId: ByteArray) : ServerPacket(input) {
|
||||
|
||||
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
|
||||
fun decrypt(sessionKey: ByteArray): ServerMessageEventPacketRaw {
|
||||
this.input goto 14
|
||||
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
|
||||
return ServerMessageEventPacketRaw(TEACryptor.decrypt(data, sessionKey).dataInputStream(), data.size, packetId);
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package net.mamoe.mirai.network.packet
|
||||
|
||||
import net.mamoe.mirai.network.Protocol
|
||||
import net.mamoe.mirai.utils.TEACryptor
|
||||
import net.mamoe.mirai.utils.TEA
|
||||
import java.io.DataInputStream
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ class ClientSKeyRequestPacket(
|
||||
this.writeRandom(2)//part of packet id
|
||||
|
||||
this.writeQQ(qq)
|
||||
this.writeHex(Protocol._fixVer)
|
||||
this.writeHex(Protocol.fixVer2)
|
||||
this.encryptAndWrite(sessionKey) {
|
||||
it.writeHex("33 00 05 00 08 74 2E 71 71 2E 63 6F 6D 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 0C 6A 75 62 61 6F 2E 71 71 2E 63 6F 6D 00 09 6B 65 2E 71 71 2E 63 6F 6D")
|
||||
}
|
||||
@ -52,19 +52,13 @@ class ServerSKeyResponsePacket(input: DataInputStream) : ServerPacket(input) {
|
||||
override fun decode() {
|
||||
this.sKey = String(this.input.goto(4).readNBytes(10))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Him188moe
|
||||
*/
|
||||
class ServerSKeyResponsePacketEncrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
|
||||
fun decrypt(sessionKey: ByteArray): ServerSKeyResponsePacket {
|
||||
this.input goto 14
|
||||
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
|
||||
return ServerSKeyResponsePacket(TEACryptor.decrypt(data, sessionKey).dataInputStream());
|
||||
class Encrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
|
||||
fun decrypt(sessionKey: ByteArray): ServerSKeyResponsePacket {
|
||||
this.input goto 14
|
||||
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
|
||||
return ServerSKeyResponsePacket(TEA.decrypt(data, sessionKey).dataInputStream());
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package net.mamoe.mirai.network.packet
|
||||
|
||||
import net.mamoe.mirai.message.defaults.MessageChain
|
||||
import net.mamoe.mirai.message.defaults.PlainText
|
||||
import net.mamoe.mirai.network.Protocol
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
import java.io.ByteArrayOutputStream
|
||||
@ -9,15 +10,50 @@ import java.io.DataInputStream
|
||||
import java.util.zip.GZIPInputStream
|
||||
|
||||
/**
|
||||
* Packet id: `00 CE` or `00 17`
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
open class ServerEventPacket(input: DataInputStream, val packetId: ByteArray, val eventIdentity: ByteArray) : ServerPacket(input) {
|
||||
@PacketId("00 17")
|
||||
class Raw(input: DataInputStream, private val packetId: ByteArray) : ServerPacket(input) {
|
||||
@ExperimentalUnsignedTypes
|
||||
fun distribute(): ServerEventPacket {
|
||||
val eventIdentity = this.input.readNBytes(16)
|
||||
val type = this.input.goto(18).readNBytes(2)
|
||||
|
||||
override fun decode() {
|
||||
return when (type.toUHexString()) {
|
||||
"00 C4" -> {
|
||||
if (this.input.goto(33).readBoolean()) {
|
||||
ServerAndroidOnlineEventPacket(this.input, packetId, eventIdentity)
|
||||
} else {
|
||||
ServerAndroidOfflineEventPacket(this.input, packetId, eventIdentity)
|
||||
}
|
||||
}
|
||||
"00 2D" -> ServerGroupUploadFileEventPacket(this.input, packetId, eventIdentity)
|
||||
|
||||
"00 52" -> ServerGroupMessageEventPacket(this.input, packetId, eventIdentity)
|
||||
|
||||
"00 A6" -> ServerFriendMessageEventPacket(this.input, packetId, eventIdentity)
|
||||
|
||||
//"02 10", "00 12" -> ServerUnknownEventPacket(this.input, packetId, eventIdentity)
|
||||
|
||||
else -> UnknownServerEventPacket(this.input, packetId, eventIdentity)
|
||||
}
|
||||
}
|
||||
|
||||
@PacketId("00 17")
|
||||
class Encrypted(input: DataInputStream, private val packetId: ByteArray) : ServerPacket(input) {
|
||||
fun decrypt(sessionKey: ByteArray): Raw = Raw(decryptBy(sessionKey), packetId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unknown event
|
||||
*/
|
||||
class UnknownServerEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity)
|
||||
|
||||
/**
|
||||
* Android 客户端上线
|
||||
*/
|
||||
@ -32,7 +68,7 @@ class ServerAndroidOfflineEventPacket(input: DataInputStream, packetId: ByteArra
|
||||
* 群文件上传
|
||||
*/
|
||||
class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
|
||||
lateinit var xmlMessage: String
|
||||
private lateinit var xmlMessage: String
|
||||
|
||||
override fun decode() {
|
||||
xmlMessage = String(this.input.goto(65).readNBytes(this.input.goto(60).readShort().toInt()))
|
||||
@ -147,7 +183,6 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
|
||||
var qq: Long = 0
|
||||
lateinit var message: MessageChain
|
||||
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
override fun decode() {
|
||||
//start at Sep1.0:27
|
||||
@ -166,6 +201,30 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 告知服务器已经收到数据
|
||||
*/
|
||||
@PacketId("")//随后写入
|
||||
@ExperimentalUnsignedTypes
|
||||
class ClientMessageResponsePacket(
|
||||
private val qq: Long,
|
||||
private val packetIdFromServer: ByteArray,//4bytes
|
||||
private val sessionKey: ByteArray,
|
||||
private val eventIdentity: ByteArray
|
||||
) : ClientPacket() {
|
||||
override fun encode() {
|
||||
this.write(packetIdFromServer)//packet id 4bytes
|
||||
|
||||
this.writeQQ(qq)
|
||||
this.writeHex(Protocol.fixVer2)
|
||||
this.encryptAndWrite(sessionKey) {
|
||||
it.write(eventIdentity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
3E 03 3F A2 76 E4 B8 DD 00 09 7C 3F 64 5C 2A 60 1F 40 00 A6 00 00 00 2D 00 05 00 02 00 01 00 06 00 04 00 01 2E 01 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 01 00 04 00 00 00 00 00 03 00 01 02 38 03 3E 03 3F A2 76 E4 B8 DD 01 10 9D D6 12 EA BC 07 91 EF DC 29 75 67 A9 1E 00 0B 2F E4 5D 6B A8 F6 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D 6B A8 F6 08 7E 90 CE 00 00 00 00 0C 00 86 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 00 00 01 00 09 01 00 06 E7 89 9B E9 80 BC 0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00
|
||||
3E 03 3F A2 76 E4 B8 DD 00 03 5F 85 64 5C 2A A4 1F 40 00 A6 00 00 00 2D 00 05 00 02 00 01 00 06 00 04 00 01 2E 01 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 01 00 04 00 00 00 00 00 03 00 01 02 38 03 3E 03 3F A2 76 E4 B8 DD 01 10 9D D6 12 EA BC 07 91 EF DC 29 75 67 A9 1E 00 0B 2F E5 5D 6B A9 16 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D 6B A9 17 1B B3 4D D7 00 00 00 00 0C 00 86 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 00 00 01 00 09 01 00 06 E7 89 9B E9 80 BC 0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00
|
||||
|
@ -3,10 +3,7 @@ package net.mamoe.mirai.network.packet
|
||||
import net.mamoe.mirai.network.packet.login.*
|
||||
import net.mamoe.mirai.network.packet.message.ServerSendFriendMessageResponsePacket
|
||||
import net.mamoe.mirai.network.packet.message.ServerSendGroupMessageResponsePacket
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.getAllDeclaredFields
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
import net.mamoe.mirai.utils.*
|
||||
import java.io.DataInputStream
|
||||
|
||||
/**
|
||||
@ -14,7 +11,9 @@ import java.io.DataInputStream
|
||||
*/
|
||||
abstract class ServerPacket(val input: DataInputStream) : Packet {
|
||||
|
||||
abstract fun decode()
|
||||
open fun decode() {
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@ -28,23 +27,22 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
|
||||
|
||||
|
||||
return when (val idHex = stream.readInt().toHexString(" ")) {
|
||||
"08 25 31 01" -> ServerTouchResponsePacketEncrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_01, stream)
|
||||
|
||||
"08 25 31 02" -> ServerTouchResponsePacketEncrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_02, stream)
|
||||
"08 25 31 01" -> ServerTouchResponsePacket.Encrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_01, stream)
|
||||
"08 25 31 02" -> ServerTouchResponsePacket.Encrypted(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 -> return ServerLoginResponseResendPacketEncrypted(stream, when (idHex) {
|
||||
271, 207 -> return ServerLoginResponseResendPacket.Encrypted(stream, when (idHex) {
|
||||
"08 36 31 03" -> ServerLoginResponseResendPacket.Flag.`08 36 31 03`
|
||||
else -> {
|
||||
MiraiLogger debug ("ServerLoginResponseResendPacketEncrypted: flag=$idHex"); ServerLoginResponseResendPacket.Flag.OTHER
|
||||
}
|
||||
})
|
||||
871 -> return ServerLoginResponseVerificationCodePacketEncrypted(stream)
|
||||
871 -> return ServerLoginResponseVerificationCodeInitPacket.Encrypted(stream)
|
||||
}
|
||||
|
||||
if (bytes.size > 700) {
|
||||
return ServerLoginResponseSuccessPacketEncrypted(stream)
|
||||
return ServerLoginResponseSuccessPacket.Encrypted(stream)
|
||||
}
|
||||
|
||||
return ServerLoginResponseFailedPacket(when (bytes.size) {
|
||||
@ -63,20 +61,20 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
|
||||
}, stream)
|
||||
}
|
||||
|
||||
"08 28 04 34" -> ServerSessionKeyResponsePacketEncrypted(stream)
|
||||
"08 28 04 34" -> ServerSessionKeyResponsePacket.Encrypted(stream)
|
||||
|
||||
|
||||
else -> when (idHex.substring(0, 5)) {
|
||||
"00 EC" -> ServerLoginSuccessPacket(stream)
|
||||
"00 1D" -> ServerSKeyResponsePacketEncrypted(stream)
|
||||
"00 5C" -> ServerAccountInfoResponsePacketEncrypted(stream)
|
||||
"00 1D" -> ServerSKeyResponsePacket.Encrypted(stream)
|
||||
"00 5C" -> ServerAccountInfoResponsePacket.Encrypted(stream)
|
||||
|
||||
"00 58" -> ServerHeartbeatResponsePacket(stream)
|
||||
|
||||
"00 BA" -> ServerVerificationCodePacketEncrypted(stream)
|
||||
"00 BA" -> ServerVerificationCodePacket.Encrypted(stream)
|
||||
|
||||
|
||||
"00 CE", "00 17" -> ServerMessageEventPacketRawEncoded(stream, idHex.hexToBytes())
|
||||
"00 CE", "00 17" -> ServerEventPacket.Raw.Encrypted(stream, idHex.hexToBytes())
|
||||
|
||||
"00 81" -> UnknownServerPacket(stream)
|
||||
|
||||
@ -101,6 +99,16 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun decryptBy(key: ByteArray): DataInputStream {
|
||||
input.goto(14)
|
||||
return DataInputStream(TEA.decrypt(input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, key).inputStream())
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
fun decryptBy(keyHex: String): DataInputStream {
|
||||
return this.decryptBy(keyHex.hexToBytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -173,4 +181,6 @@ fun <N : Number> DataInputStream.readByteAt(position: N): Byte {
|
||||
fun <N : Number> DataInputStream.readShortAt(position: N): Short {
|
||||
this.goto(position)
|
||||
return this.readShort();
|
||||
}
|
||||
}
|
||||
|
||||
fun ByteArray.cutTail(length: Int): ByteArray = this.copyOfRange(0, this.size - length)
|
@ -2,7 +2,7 @@ package net.mamoe.mirai.network.packet
|
||||
|
||||
import net.mamoe.mirai.network.Protocol
|
||||
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
|
||||
import net.mamoe.mirai.utils.TEACryptor
|
||||
import net.mamoe.mirai.utils.TEA
|
||||
import net.mamoe.mirai.utils.getRandomKey
|
||||
import net.mamoe.mirai.utils.lazyEncode
|
||||
import java.io.DataInputStream
|
||||
@ -16,8 +16,6 @@ import java.net.InetAddress
|
||||
class ClientSessionRequestPacket(
|
||||
private val qq: Long,
|
||||
private val serverIp: String,
|
||||
private val loginIP: String,
|
||||
private val md5_32: ByteArray,
|
||||
private val token38: ByteArray,
|
||||
private val token88: ByteArray,
|
||||
private val encryptionKey: ByteArray,
|
||||
@ -28,7 +26,7 @@ class ClientSessionRequestPacket(
|
||||
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() {
|
||||
this.write(TEA.encrypt(object : ByteArrayDataOutputStream() {
|
||||
override fun toByteArray(): ByteArray {
|
||||
this.writeHex("00 07 00 88")
|
||||
this.write(token88)
|
||||
@ -36,10 +34,10 @@ class ClientSessionRequestPacket(
|
||||
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(Protocol.fix0836)
|
||||
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.writeHex(Protocol.constantData0)
|
||||
this.writeHex(Protocol.constantData1)
|
||||
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
|
||||
@ -51,7 +49,7 @@ class ClientSessionRequestPacket(
|
||||
|
||||
//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.write(getRandomKey(32))//md5 32
|
||||
this.writeHex("68")
|
||||
|
||||
this.writeHex("00 00 00 00 00 2D 00 06 00 01")
|
||||
@ -106,16 +104,12 @@ class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val d
|
||||
//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)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class ServerSessionKeyResponsePacketEncrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
|
||||
fun decrypt(_0828_rec_decr_key: ByteArray): ServerSessionKeyResponsePacket {
|
||||
this.input goto 14
|
||||
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
|
||||
return ServerSessionKeyResponsePacket(TEACryptor.decrypt(data, _0828_rec_decr_key).dataInputStream(), data.size);
|
||||
class Encrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
|
||||
fun decrypt(_0828_rec_decr_key: ByteArray): ServerSessionKeyResponsePacket {
|
||||
this.input goto 14
|
||||
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
|
||||
return ServerSessionKeyResponsePacket(TEA.decrypt(data, _0828_rec_decr_key).dataInputStream(), data.size);
|
||||
}
|
||||
}
|
||||
}
|
@ -48,24 +48,13 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ServerTouchResponsePacketEncrypted(private val type: ServerTouchResponsePacket.Type, inputStream: DataInputStream) : ServerPacket(inputStream) {
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
fun decrypt(): ServerTouchResponsePacket {
|
||||
input.skip(7)
|
||||
var bytes = input.readAllBytes();
|
||||
bytes = bytes.copyOfRange(0, bytes.size - 1);
|
||||
println(bytes.toUByteArray().toUHexString())
|
||||
|
||||
return ServerTouchResponsePacket(DataInputStream(TEACryptor.decrypt(bytes, when (type) {
|
||||
ServerTouchResponsePacket.Type.TYPE_08_25_31_02 -> Protocol.redirectionKey.hexToBytes()
|
||||
ServerTouchResponsePacket.Type.TYPE_08_25_31_01 -> Protocol._0825key.hexToBytes()
|
||||
}).inputStream()));
|
||||
class Encrypted(private val type: Type, inputStream: DataInputStream) : ServerPacket(inputStream) {
|
||||
@ExperimentalUnsignedTypes
|
||||
fun decrypt(): ServerTouchResponsePacket = ServerTouchResponsePacket(decryptBy(when (type) {
|
||||
Type.TYPE_08_25_31_02 -> Protocol.redirectionKey.hexToBytes()
|
||||
Type.TYPE_08_25_31_01 -> Protocol.key0825.hexToBytes()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,13 +71,13 @@ class ClientTouchPacket(val qq: Long, val serverIp: String) : ClientPacket() {
|
||||
override fun encode() {
|
||||
this.writeQQ(qq)
|
||||
this.writeHex(Protocol.fixVer)
|
||||
this.writeHex(Protocol._0825key)
|
||||
this.writeHex(Protocol.key0825)
|
||||
|
||||
this.write(TEACryptor.CRYPTOR_0825KEY.encrypt(object : ByteArrayDataOutputStream() {
|
||||
this.write(TEA.CRYPTOR_0825KEY.encrypt(object : ByteArrayDataOutputStream() {
|
||||
@Throws(IOException::class)
|
||||
override fun toByteArray(): ByteArray {
|
||||
this.writeHex(Protocol._0825data0)
|
||||
this.writeHex(Protocol._0825data2)
|
||||
this.writeHex(Protocol.constantData0)
|
||||
this.writeHex(Protocol.constantData1)
|
||||
this.writeQQ(qq)
|
||||
this.writeHex("00 00 00 00 03 09 00 08 00 01")
|
||||
this.writeIP(serverIp);
|
||||
@ -116,11 +105,11 @@ class ClientServerRedirectionPacket(private val serverIP: String, private val qq
|
||||
this.writeHex(Protocol.redirectionKey)
|
||||
|
||||
|
||||
this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() {
|
||||
this.write(TEA.encrypt(object : ByteArrayDataOutputStream() {
|
||||
@Throws(IOException::class)
|
||||
override fun toByteArray(): ByteArray {
|
||||
this.writeHex(Protocol._0825data0)
|
||||
this.writeHex(Protocol._0825data2)
|
||||
this.writeHex(Protocol.constantData0)
|
||||
this.writeHex(Protocol.constantData1)
|
||||
this.writeQQ(qq)
|
||||
this.writeHex("00 01 00 00 03 09 00 0C 00 01")
|
||||
this.writeIP(serverIP)
|
||||
|
@ -23,11 +23,11 @@ class ClientVerificationCodeTransmissionRequestPacket(
|
||||
this.writeByte(count)//part of packet id
|
||||
|
||||
this.writeQQ(qq)
|
||||
this.writeHex(Protocol._fixVer)
|
||||
this.writeHex(Protocol._00BaKey)
|
||||
this.encryptAndWrite(Protocol._00BaKey) {
|
||||
this.writeHex(Protocol.fixVer2)
|
||||
this.writeHex(Protocol.key00BA)
|
||||
this.encryptAndWrite(Protocol.key00BA) {
|
||||
it.writeHex("00 02 00 00 08 04 01 E0")
|
||||
it.writeHex(Protocol._0825data2)
|
||||
it.writeHex(Protocol.constantData1)
|
||||
it.writeHex("00 00 38")
|
||||
it.write(token0825)
|
||||
it.writeHex("01 03 00 19")
|
||||
@ -37,7 +37,7 @@ class ClientVerificationCodeTransmissionRequestPacket(
|
||||
it.writeHex("00 28")
|
||||
it.write(token00BA)
|
||||
it.writeHex("00 10")
|
||||
it.writeHex(Protocol._00BaFixKey)
|
||||
it.writeHex(Protocol.key00BAFix)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,22 +87,19 @@ class ServerVerificationCodeRepeatPacket(input: DataInputStream) : ServerVerific
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ServerVerificationCodePacket(input: DataInputStream) : ServerPacket(input)
|
||||
abstract class ServerVerificationCodePacket(input: DataInputStream) : ServerPacket(input) {
|
||||
|
||||
@PacketId("00 BA")
|
||||
class ServerVerificationCodePacketEncrypted(input: DataInputStream) : ServerPacket(input) {
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
fun decrypt(): ServerVerificationCodePacket {
|
||||
this.input goto 14
|
||||
val data = TEACryptor.decrypt(this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, Protocol._00BaKey.hexToBytes())
|
||||
return if (data.size == 95) {
|
||||
ServerVerificationCodeRepeatPacket(data.dataInputStream())
|
||||
} else {
|
||||
ServerVerificationCodeTransmissionPacket(data.dataInputStream(), data.size, this.input.readNBytesAt(3, 4))
|
||||
@PacketId("00 BA")
|
||||
class Encrypted(input: DataInputStream) : ServerPacket(input) {
|
||||
@ExperimentalUnsignedTypes
|
||||
fun decrypt(): ServerVerificationCodePacket {
|
||||
this.input goto 14
|
||||
val data = TEA.decrypt(this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, Protocol.key00BA.hexToBytes())
|
||||
return if (data.size == 95) {
|
||||
ServerVerificationCodeRepeatPacket(data.dataInputStream())
|
||||
} else {
|
||||
ServerVerificationCodeTransmissionPacket(data.dataInputStream(), data.size, this.input.readNBytesAt(3, 4))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,13 @@ import net.mamoe.mirai.network.packet.*
|
||||
import net.mamoe.mirai.utils.ClientLoginStatus
|
||||
|
||||
/**
|
||||
* 改变在线状态: "我在线上", "隐身" 等
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
@ExperimentalUnsignedTypes
|
||||
@PacketId("00 EC")
|
||||
class ClientLoginStatusPacket(
|
||||
class ClientChangeOnlineStatusPacket(
|
||||
private val qq: Long,
|
||||
private val sessionKey: ByteArray,
|
||||
private val loginStatus: ClientLoginStatus
|
||||
@ -18,7 +20,7 @@ class ClientLoginStatusPacket(
|
||||
override fun encode() {
|
||||
this.writeRandom(2)//part of packet id
|
||||
this.writeQQ(qq)
|
||||
this.writeHex(Protocol._fixVer)
|
||||
this.writeHex(Protocol.fixVer2)
|
||||
this.encryptAndWrite(sessionKey) {
|
||||
it.writeHex("01 00")
|
||||
it.writeByte(loginStatus.id)
|
@ -4,7 +4,7 @@ import net.mamoe.mirai.network.Protocol
|
||||
import net.mamoe.mirai.network.packet.*
|
||||
import net.mamoe.mirai.util.TestedSuccessfully
|
||||
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
|
||||
import net.mamoe.mirai.utils.TEACryptor
|
||||
import net.mamoe.mirai.utils.TEA
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
import java.io.DataOutputStream
|
||||
@ -28,10 +28,10 @@ class ClientPasswordSubmissionPacket(
|
||||
@ExperimentalUnsignedTypes
|
||||
override fun encode() {
|
||||
this.writeQQ(qq)
|
||||
this.writeHex(Protocol._0836_622_fix1)
|
||||
this.writeHex(Protocol.passwordSubmissionKey1)
|
||||
this.writeHex(Protocol.publicKey)
|
||||
this.writeHex("00 00 00 10")
|
||||
this.writeHex(Protocol._0836key1)
|
||||
this.writeHex(Protocol.key0836)
|
||||
|
||||
this.encryptAndWrite(Protocol.shareKey.hexToBytes()) {
|
||||
it.writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825)
|
||||
@ -69,12 +69,12 @@ open class ClientLoginResendPacket internal constructor(
|
||||
) : ClientPacket() {
|
||||
override fun encode() {
|
||||
this.writeQQ(qq)
|
||||
this.writeHex(Protocol._0836_622_fix1)
|
||||
this.writeHex(Protocol.passwordSubmissionKey1)
|
||||
this.writeHex(Protocol.publicKey)
|
||||
this.writeHex("00 00 00 10")
|
||||
this.writeHex(Protocol._0836key1)
|
||||
this.writeHex(Protocol.key0836)
|
||||
|
||||
this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() {
|
||||
this.write(TEA.encrypt(object : ByteArrayDataOutputStream() {
|
||||
override fun toByteArray(): ByteArray {
|
||||
this.writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825, tlv_0006_encr)
|
||||
|
||||
@ -115,12 +115,12 @@ private fun DataOutputStream.writePart1(qq: Long, password: String, loginTime: I
|
||||
this.writeTLV0006(qq, password, loginTime, loginIP, tgtgtKey)
|
||||
}
|
||||
//fix
|
||||
this.writeHex(Protocol._0836_622_fix2)
|
||||
this.writeHex(Protocol.passwordSubmissionKey2)
|
||||
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.write(TEA.encrypt(Protocol.passwordSubmissionKey2.hexToBytes(), tgtgtKey))
|
||||
this.writeHex(Protocol.constantData0)
|
||||
this.writeHex(Protocol.constantData1)
|
||||
this.writeQQ(qq)
|
||||
this.writeZero(4)
|
||||
|
@ -1,13 +1,9 @@
|
||||
package net.mamoe.mirai.network.packet.login
|
||||
|
||||
import net.mamoe.mirai.network.Protocol
|
||||
import net.mamoe.mirai.network.packet.ServerPacket
|
||||
import net.mamoe.mirai.network.packet.goto
|
||||
import net.mamoe.mirai.network.packet.readNBytesAt
|
||||
import net.mamoe.mirai.network.packet.readVarString
|
||||
import net.mamoe.mirai.network.packet.*
|
||||
import net.mamoe.mirai.util.TestedSuccessfully
|
||||
import net.mamoe.mirai.utils.TEACryptor
|
||||
import net.mamoe.mirai.utils.hexToBytes
|
||||
import net.mamoe.mirai.utils.TEA
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
import java.io.DataInputStream
|
||||
|
||||
@ -117,21 +113,14 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
|
||||
|
||||
//this.gender = this.input.goto(packetDataLength - 32).readByteAt().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
class ServerLoginResponseSuccessPacketEncrypted(input: DataInputStream) : ServerPacket(input) {
|
||||
override fun decode() {
|
||||
|
||||
class Encrypted(input: DataInputStream) : ServerPacket(input) {
|
||||
@ExperimentalUnsignedTypes
|
||||
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseSuccessPacket {
|
||||
input goto 14
|
||||
return ServerLoginResponseSuccessPacket(TEA.decrypt(TEA.decrypt(input.readAllBytes().cutTail(1), Protocol.shareKey), tgtgtKey).dataInputStream());
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseSuccessPacket {
|
||||
input goto 14
|
||||
var bytes = input.readAllBytes()
|
||||
bytes = bytes.copyOfRange(0, bytes.size - 1)
|
||||
println(bytes.toUByteArray().toUHexString())
|
||||
|
||||
return ServerLoginResponseSuccessPacket(DataInputStream(TEACryptor.decrypt(TEACryptor.decrypt(bytes, Protocol.shareKey.hexToBytes()), tgtgtKey).inputStream()));
|
||||
//TeaDecrypt(取文本中间(data, 43, 取文本长度(data) - 45), m_0828_rec_decr_key)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,10 +2,8 @@ package net.mamoe.mirai.network.packet.login
|
||||
|
||||
import net.mamoe.mirai.network.packet.PacketId
|
||||
import net.mamoe.mirai.network.packet.ServerPacket
|
||||
import net.mamoe.mirai.network.packet.dataInputStream
|
||||
import net.mamoe.mirai.network.packet.goto
|
||||
import net.mamoe.mirai.util.TestedSuccessfully
|
||||
import net.mamoe.mirai.utils.TEACryptor
|
||||
import java.io.DataInputStream
|
||||
|
||||
/**
|
||||
@ -43,20 +41,9 @@ class ServerLoginResponseResendPacket(input: DataInputStream, val flag: Flag) :
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Encrypted(input: DataInputStream, private val flag: Flag) : ServerPacket(input) {
|
||||
@TestedSuccessfully
|
||||
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseResendPacket = ServerLoginResponseResendPacket(decryptBy(tgtgtKey), flag)
|
||||
}
|
||||
}
|
||||
|
||||
class ServerLoginResponseResendPacketEncrypted(input: DataInputStream, private val flag: ServerLoginResponseResendPacket.Flag) : ServerPacket(input) {
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
|
||||
@TestedSuccessfully
|
||||
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseResendPacket {
|
||||
//this.input.skip(7)
|
||||
this.input goto 14
|
||||
var data: ByteArray = this.input.readAllBytes()
|
||||
data = TEACryptor.CRYPTOR_SHARE_KEY.decrypt(data.let { it.copyOfRange(0, it.size - 1) });
|
||||
data = TEACryptor.decrypt(data, tgtgtKey)
|
||||
return ServerLoginResponseResendPacket(data.dataInputStream(), flag)
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
package net.mamoe.mirai.network.packet.login
|
||||
|
||||
import net.mamoe.mirai.network.packet.ServerPacket
|
||||
import net.mamoe.mirai.network.packet.cutTail
|
||||
import net.mamoe.mirai.network.packet.dataInputStream
|
||||
import net.mamoe.mirai.network.packet.goto
|
||||
import net.mamoe.mirai.util.TestedSuccessfully
|
||||
import net.mamoe.mirai.utils.TEACryptor
|
||||
import net.mamoe.mirai.utils.TEA
|
||||
import net.mamoe.mirai.utils.hexToUBytes
|
||||
import java.io.DataInputStream
|
||||
|
||||
@ -32,6 +33,19 @@ class ServerLoginResponseVerificationCodeInitPacket(input: DataInputStream, priv
|
||||
|
||||
this.token00BA = this.input.goto(packetLength - 60).readNBytes(40)
|
||||
}
|
||||
|
||||
|
||||
class Encrypted(input: DataInputStream) : ServerPacket(input) {
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
|
||||
fun decrypt(): ServerLoginResponseVerificationCodeInitPacket {
|
||||
this.input goto 14
|
||||
val data = TEA.CRYPTOR_SHARE_KEY.decrypt(this.input.readAllBytes().cutTail(1));
|
||||
return ServerLoginResponseVerificationCodeInitPacket(data.dataInputStream(), data.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
@ -54,15 +68,3 @@ verify code
|
||||
token00ba
|
||||
42 E6 18 57 D4 B1 4D AE 51 27 D5 EF A2 38 91 39 15 37 6C 5A FE 75 93 49 DB FC 57 3C 12 3F 26 D9 16 1D 83 45 8B 78 39 D8
|
||||
*/
|
||||
|
||||
class ServerLoginResponseVerificationCodePacketEncrypted(input: DataInputStream) : ServerPacket(input) {
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
|
||||
fun decrypt(): ServerLoginResponseVerificationCodeInitPacket {
|
||||
this.input goto 14
|
||||
val data = TEACryptor.CRYPTOR_SHARE_KEY.decrypt(this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) });
|
||||
return ServerLoginResponseVerificationCodeInitPacket(data.dataInputStream(), data.size)
|
||||
}
|
||||
}
|
@ -4,10 +4,8 @@ import net.mamoe.mirai.network.packet.ServerPacket
|
||||
import java.io.DataInputStream
|
||||
|
||||
/**
|
||||
* Congratulations!
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
class ServerLoginSuccessPacket(input: DataInputStream) : ServerPacket(input) {
|
||||
override fun decode() {
|
||||
|
||||
}
|
||||
}
|
||||
class ServerLoginSuccessPacket(input: DataInputStream) : ServerPacket(input)
|
@ -19,7 +19,7 @@ class ClientSendFriendMessagePacket(
|
||||
override fun encode() {
|
||||
this.writeRandom(2)//part of packet id
|
||||
this.writeQQ(robotQQ)
|
||||
this.writeHex(Protocol._fixVer)
|
||||
this.writeHex(Protocol.fixVer2)
|
||||
|
||||
this.encryptAndWrite(sessionKey) {
|
||||
it.writeQQ(robotQQ)
|
||||
@ -58,7 +58,4 @@ class ClientSendFriendMessagePacket(
|
||||
}
|
||||
|
||||
@PacketId("00 CD")
|
||||
class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input) {
|
||||
override fun decode() {
|
||||
}
|
||||
}
|
||||
class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input)
|
@ -2,7 +2,6 @@ package net.mamoe.mirai.network.packet.message
|
||||
|
||||
import net.mamoe.mirai.network.Protocol
|
||||
import net.mamoe.mirai.network.packet.*
|
||||
import net.mamoe.mirai.utils.lazyEncode
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
import java.io.DataInputStream
|
||||
|
||||
@ -13,14 +12,14 @@ import java.io.DataInputStream
|
||||
@ExperimentalUnsignedTypes
|
||||
class ClientSendGroupMessagePacket(
|
||||
private val groupId: Long,//不是 number
|
||||
private val qq: Long,
|
||||
private val robotQQ: Long,
|
||||
private val sessionKey: ByteArray,
|
||||
private val message: String
|
||||
) : ClientPacket() {
|
||||
override fun encode() {
|
||||
this.writeRandom(2)//part of packet id
|
||||
this.writeQQ(qq)
|
||||
this.writeHex(Protocol._fixVer)
|
||||
this.writeQQ(robotQQ)
|
||||
this.writeHex(Protocol.fixVer2)
|
||||
|
||||
this.encryptAndWrite(sessionKey) {
|
||||
val bytes = message.toByteArray()
|
||||
@ -46,24 +45,5 @@ class ClientSendGroupMessagePacket(
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
println(lazyEncode {
|
||||
val bytes = "message".toByteArray()
|
||||
it.writeByte(0x2A)
|
||||
it.writeInt(580266363)
|
||||
it.writeShort(19 + bytes.size)
|
||||
|
||||
it.writeByte(0x01)
|
||||
it.writeByte(0x01)
|
||||
it.writeShort(bytes.size + 3)
|
||||
it.writeByte(0x01)
|
||||
it.writeShort(bytes.size)
|
||||
it.write(bytes)
|
||||
}.toUHexString())
|
||||
}
|
||||
|
||||
@PacketId("00 02")
|
||||
class ServerSendGroupMessageResponsePacket(input: DataInputStream) : ServerPacket(input) {
|
||||
override fun decode() {
|
||||
}
|
||||
}
|
||||
class ServerSendGroupMessageResponsePacket(input: DataInputStream) : ServerPacket(input)
|
@ -4,10 +4,14 @@ package net.mamoe.mirai.utils;
|
||||
* @author Him188moe
|
||||
*/
|
||||
public enum ClientLoginStatus {
|
||||
/**
|
||||
* 我在线上
|
||||
*/
|
||||
ONLINE(0x0A);
|
||||
|
||||
// TODO: 2019/8/31 add more
|
||||
public final int id;//1byte
|
||||
// TODO: 2019/8/31 add more ClientLoginStatus
|
||||
|
||||
public final int id;//1 ubyte
|
||||
|
||||
ClientLoginStatus(int id) {
|
||||
this.id = id;
|
||||
|
@ -0,0 +1,17 @@
|
||||
package net.mamoe.mirai.utils;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Him188moe
|
||||
*/
|
||||
@Data
|
||||
public final class RobotAccount {
|
||||
public final long qqNumber;
|
||||
public final String password;
|
||||
|
||||
public RobotAccount(long qqNumber, String password) {
|
||||
this.qqNumber = qqNumber;
|
||||
this.password = password;
|
||||
}
|
||||
}
|
@ -8,10 +8,10 @@ import java.util.Random;
|
||||
/**
|
||||
* @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java
|
||||
*/
|
||||
public class TEACryptor {
|
||||
public static final TEACryptor CRYPTOR_SHARE_KEY = new TEACryptor(Protocol.Companion.hexToBytes(Protocol.shareKey));
|
||||
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));
|
||||
public class TEA {
|
||||
public static final TEA CRYPTOR_SHARE_KEY = new TEA(Protocol.Companion.hexToBytes(Protocol.shareKey));
|
||||
public static final TEA CRYPTOR_0825KEY = new TEA(Protocol.Companion.hexToBytes(Protocol.key0825));
|
||||
public static final TEA CRYPTOR_00BAKEY = new TEA(Protocol.Companion.hexToBytes(Protocol.key00BA));
|
||||
|
||||
private static final long UINT32_MASK = 0xffffffffL;
|
||||
private final long[] mKey;
|
||||
@ -25,7 +25,7 @@ public class TEACryptor {
|
||||
private boolean isFirstBlock;
|
||||
private boolean isRand;
|
||||
|
||||
public TEACryptor(byte[] key) {
|
||||
public TEA(byte[] key) {
|
||||
mKey = new long[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
mKey[i] = pack(key, i * 4, 4);
|
||||
@ -36,11 +36,19 @@ public class TEACryptor {
|
||||
}
|
||||
|
||||
public static byte[] encrypt(byte[] source, byte[] key) {
|
||||
return new TEACryptor(key).encrypt(source);
|
||||
return new TEA(key).encrypt(source);
|
||||
}
|
||||
|
||||
public static byte[] encrypt(byte[] source, String keyHex) {
|
||||
return encrypt(source, UtilsKt.hexToBytes(keyHex));
|
||||
}
|
||||
|
||||
public static byte[] decrypt(byte[] source, byte[] key) {
|
||||
return new TEACryptor(key).decrypt(source);
|
||||
return new TEA(key).decrypt(source);
|
||||
}
|
||||
|
||||
public static byte[] decrypt(byte[] source, String keyHex) {
|
||||
return decrypt(source, UtilsKt.hexToBytes(keyHex));
|
||||
}
|
||||
|
||||
private static long pack(byte[] bytes, int offset, int len) {
|
Loading…
Reference in New Issue
Block a user