Updated robot & network structure

This commit is contained in:
Him188moe 2019-09-05 22:10:40 +08:00
parent 1446618496
commit 402e8fbb32
19 changed files with 281 additions and 56 deletions

View File

@ -56,6 +56,12 @@
<artifactId>snakeyaml</artifactId>
<version>1.18</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>1.3.41</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

@ -120,7 +120,7 @@ public class MiraiServer {
this.qqs.keySet().stream().map(key -> this.qqs.getSection(key)).forEach(section -> {
try {
Robot robot = new Robot(section);
robot.network.tryLogin$mirai_core(state -> {
robot.network.tryLogin$mirai_core((robot1, state) -> {
if (state == LoginState.SUCCEED) {
Robot.instances.add(robot);
} else {

View File

@ -89,7 +89,6 @@ public final class Robot implements Closeable {
public void close() {
this.network.close();
this.owners.clear();
this.contacts.groups.values().forEach(Group::close);
this.contacts.groups.clear();
this.contacts.qqs.clear();

View File

@ -3,6 +3,9 @@ package net.mamoe.mirai.event
import net.mamoe.mirai.event.events.MiraiEvent
import kotlin.reflect.KClass
object EventManager : MiraiEventManager()
typealias MiraiEventManagerKt = EventManager
typealias EventMgr = EventManager
fun <C : Class<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) {
MiraiEventManager.getInstance().hookAlways(MiraiEventHook<E>(this, hook))

View File

@ -10,14 +10,12 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
public class MiraiEventManager {
private MiraiEventManager() {
MiraiEventManager() {
}
private static MiraiEventManager instance = new MiraiEventManager();
public static MiraiEventManager getInstance() {
return MiraiEventManager.instance;
return EventManager.INSTANCE;
}
private final ReentrantReadWriteLock hooksLock = new ReentrantReadWriteLock();

View File

@ -11,7 +11,7 @@ public abstract class MiraiEvent {
public boolean isCancelled() {
if (!(this instanceof Cancellable)) {
throw new EventException("Event is not Cancellable");
return false;
}
return this.cancelled;
}

View File

@ -0,0 +1,19 @@
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.event.events.MiraiEvent;
import net.mamoe.mirai.network.packet.Packet;
/**
* @author Him188moe
*/
public abstract class PacketEvent extends MiraiEvent {
private final Packet packet;
public PacketEvent(Packet packet) {
this.packet = packet;
}
public Packet getPacket() {
return packet;
}
}

View File

@ -0,0 +1,7 @@
package net.mamoe.mirai.event.events.network;
/**
* @author Him188moe
*/
public class PacketReceivedEvent {
}

View File

@ -0,0 +1,17 @@
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.network.packet.ServerPacket;
/**
* @author Him188moe
*/
public abstract class ServerPacketEvent extends PacketEvent {
public ServerPacketEvent(ServerPacket packet) {
super(packet);
}
@Override
public ServerPacket getPacket() {
return (ServerPacket) super.getPacket();
}
}

View File

@ -0,0 +1,13 @@
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.event.events.Cancellable;
import net.mamoe.mirai.network.packet.ServerPacket;
/**
* @author Him188moe
*/
public class ServerPacketReceivedEvent extends ServerPacketEvent implements Cancellable {
public ServerPacketReceivedEvent(ServerPacket packet) {
super(packet);
}
}

View File

@ -13,13 +13,13 @@ object Protocol {
add("183.60.56.29")
arrayOf(
"sz2.tencent.com",
"sz3.tencent.com",
"sz4.tencent.com",
"sz5.tencent.com",
"sz6.tencent.com",
"sz8.tencent.com",
"sz9.tencent.com"
"sz9.tencent.com",
"sz2.tencent.com"
).forEach { this.add(InetAddress.getByName(it).hostAddress) }
}

View File

@ -3,8 +3,10 @@ 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.network.ServerPacketReceivedEvent
import net.mamoe.mirai.event.events.qq.FriendMessageEvent
import net.mamoe.mirai.event.events.robot.RobotLoginSucceedEvent
import net.mamoe.mirai.event.hookWhile
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
@ -35,7 +37,7 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
val messageHandler = MessageHandler()
val actionHandler = ActionHandler()
private val packetHandlers: Map<KClass<out PacketHandler>, PacketHandler> = mapOf(
private val packetHandlers: Map<KClass<out PacketHandler>, PacketHandler> = linkedMapOf(
DebugHandler::class to debugHandler,
LoginHandler::class to loginHandler,
MessageHandler::class to messageHandler,
@ -63,16 +65,22 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
//private | internal
internal fun tryLogin(loginHook: ((LoginState) -> Unit)? = null) {
/**
* 仅当 [LoginState] [LoginState.UNKNOWN] 且非 [LoginState.TIMEOUT] 才会调用 [loginHook].
* 如果要输入验证码, 那么会以参数 [LoginState.VERIFICATION_CODE] 调用 [loginHandler], 登录完成后再以 [LoginState.SUCCEED] 调用 [loginHandler]
*/
internal fun tryLogin(loginHook: (Robot.(LoginState) -> Unit)? = null) {
val ipQueue: LinkedList<String> = LinkedList(Protocol.SERVER_IP)
fun login(): Boolean {
val ip = ipQueue.poll()
return if (ip != null) {
this@RobotNetworkHandler.socketHandler.touch(ip) { state ->
if (state == LoginState.UNKNOWN) {
if (state == LoginState.UNKNOWN || state == LoginState.TIMEOUT) {
login()
} else {
loginHook?.invoke(state)
if (loginHook != null) {
robot.loginHook(state)
}
}
}
true
@ -82,7 +90,12 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
}
@ExperimentalUnsignedTypes
internal fun onPacketReceived(packet: ServerPacket) {
internal fun distributePacket(packet: ServerPacket) {
packet.decode()
if (ServerPacketReceivedEvent(packet).broadcast().isCancelled) {
debugHandler.onPacketReceived(packet)
return
}
this.packetHandlers.values.forEach {
it.onPacketReceived(packet)
}
@ -90,11 +103,10 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
private inner class SocketHandler : Closeable {
private lateinit var socket: DatagramSocket
private var socket: DatagramSocket? = null
internal var serverIP: String = ""
set(value) {
serverAddress = InetSocketAddress(value, 8000)
field = value
restartSocket()
@ -104,36 +116,29 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
internal var loginState: LoginState? = null
set(value) {
field = value
if (value != null && value != LoginState.UNKNOWN) {
if (value != null) {
loginHook?.invoke(value)
}
}
private lateinit var serverAddress: InetSocketAddress
private fun restartSocket() {
socket = DatagramSocket((15314 + Math.random() * 100).toInt())
socket.close()
socket.connect(this.serverAddress)
socket?.close()
socket = DatagramSocket(0)
socket!!.connect(InetSocketAddress(serverIP, 8000))
Thread {
while (socket.isConnected) {
while (socket!!.isConnected) {
val packet = DatagramPacket(ByteArray(2048), 2048)
kotlin
.runCatching { socket.receive(packet) }
kotlin.runCatching { socket!!.receive(packet) }
.onSuccess {
MiraiThreadPool.getInstance().submit {
try {
onPacketReceived(ServerPacket.ofByteArray(packet.data.removeZeroTail()))
distributePacket(ServerPacket.ofByteArray(packet.data.removeZeroTail()))
} catch (e: Exception) {
e.printStackTrace()
}
}
}.onFailure {
if (it.message == "socket closed") {
if (!closed) {
restartSocket()
}
if (it.message == "Socket closed" || it.message == "socket closed") {
return@Thread
}
it.printStackTrace()
@ -147,11 +152,16 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
* Start network and touch the server
*/
internal fun touch(serverAddress: String, loginHook: ((LoginState) -> Unit)? = null) {
MiraiLogger.info("Connecting server: $serverAddress")
socketHandler.serverIP = serverAddress
if (loginHook != null) {
this.loginHook = loginHook
}
sendPacket(ClientTouchPacket(robot.account.qqNumber, socketHandler.serverIP))
waitForPacket(ServerTouchResponsePacket::class, 100) {
MiraiLogger.error(" Timeout")
loginHook?.invoke(LoginState.TIMEOUT)
}
}
/**
@ -159,20 +169,46 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
*/
@ExperimentalUnsignedTypes
internal fun sendPacket(packet: ClientPacket) {
checkNotNull(socket) { "socket closed" }
try {
packet.encode()
packet.writeHex(Protocol.tail)
val data = packet.toByteArray()
socket.send(DatagramPacket(data, data.size))
socket!!.send(DatagramPacket(data, data.size))
MiraiLogger info "Packet sent: $packet"
} catch (e: Throwable) {
e.printStackTrace()
}
}
@Suppress("UNCHECKED_CAST")
private fun <P : ServerPacket> waitForPacket(packetClass: KClass<P>, timeoutMillis: Long, timeout: () -> Unit) {
var got = false
ServerPacketReceivedEvent::class.hookWhile {
if (packetClass.isInstance(it.packet)) {
got = true
true
} else {
false
}
}
MiraiThreadPool.getInstance().submit {
val startingTime = System.currentTimeMillis()
while (!got) {
if (System.currentTimeMillis() - startingTime > timeoutMillis) {
timeout.invoke()
return@submit
}
Thread.sleep(10)
}
}
}
override fun close() {
this.socket.close()
this.socket?.close()
this.loginState = null
this.loginHook = null
}
@ -194,7 +230,6 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
*/
inner class DebugHandler : PacketHandler() {
override fun onPacketReceived(packet: ServerPacket) {
packet.decode()
MiraiLogger info "Packet received: $packet"
if (packet is ServerEventPacket) {
sendPacket(ClientMessageResponsePacket(robot.account.qqNumber, packet.packetId, sessionKey, packet.eventIdentity))
@ -274,6 +309,8 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
}
is ServerVerificationCodeTransmissionPacket -> {
socketHandler.loginState = LoginState.VERIFICATION_CODE
this.verificationCodeSequence++
this.verificationCodeCache = this.verificationCodeCache!! + packet.verificationCodePartN
@ -371,17 +408,17 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
sendPacket(ClientAccountInfoRequestPacket(robot.account.qqNumber, sessionKey))
}
is ServerEventPacket.Raw -> onPacketReceived(packet.distribute())
is ServerEventPacket.Raw -> distributePacket(packet.distribute())
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(sessionKey))
is ServerAccountInfoResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
is ServerEventPacket.Raw.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
is ServerVerificationCodePacket.Encrypted -> distributePacket(packet.decrypt())
is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> distributePacket(packet.decrypt())
is ServerLoginResponseResendPacket.Encrypted -> distributePacket(packet.decrypt(this.tgtgtKey!!))
is ServerLoginResponseSuccessPacket.Encrypted -> distributePacket(packet.decrypt(this.tgtgtKey!!))
is ServerSessionKeyResponsePacket.Encrypted -> distributePacket(packet.decrypt(this.sessionResponseDecryptionKey))
is ServerTouchResponsePacket.Encrypted -> distributePacket(packet.decrypt())
is ServerSKeyResponsePacket.Encrypted -> distributePacket(packet.decrypt(sessionKey))
is ServerAccountInfoResponsePacket.Encrypted -> distributePacket(packet.decrypt(sessionKey))
is ServerEventPacket.Raw.Encrypted -> distributePacket(packet.decrypt(sessionKey))
is ServerAccountInfoResponsePacket,

View File

@ -152,10 +152,6 @@ fun DataOutputStream.writeTLV0006(qq: Long, password: String, loginTime: Int, lo
}
}
fun main() {
}
/*
@ExperimentalUnsignedTypes
fun main() {
@ -164,7 +160,7 @@ fun main() {
@ExperimentalUnsignedTypes
@TestedSuccessfully
fun DataOutputStream.writeCRC32() = writeCRC32(getRandomKey(16))
fun DataOutputStream.writeCRC32() = writeCRC32(getRandomByteArray(16))
@ExperimentalUnsignedTypes

View File

@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEA
import net.mamoe.mirai.utils.getRandomKey
import net.mamoe.mirai.utils.getRandomByteArray
import net.mamoe.mirai.utils.lazyEncode
import java.io.DataInputStream
import java.net.InetAddress
@ -44,12 +44,12 @@ class ClientSessionRequestPacket(
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.write(getRandomByteArray(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(getRandomKey(32))//md5 32
this.write(getRandomByteArray(32))//md5 32
this.writeHex("68")
this.writeHex("00 00 00 00 00 2D 00 06 00 01")

View File

@ -40,7 +40,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
loginTime = input.readInt()
loginIP = input.readIP()
tgtgtKey = getRandomKey(16)
tgtgtKey = getRandomByteArray(16)
}
else -> {

View File

@ -82,7 +82,7 @@ class ServerVerificationCodeRepeatPacket(input: DataInputStream) : ServerVerific
@ExperimentalUnsignedTypes
override fun decode() {
token00BA = this.input.readNBytesAt(10, 56)
tgtgtKeyUpdate = getRandomKey(16)
tgtgtKeyUpdate = getRandomByteArray(16)
}
}

View File

@ -13,8 +13,11 @@ enum class LoginState {
UNKNOWN_QQ_NUMBER,//你输入的帐号不存在
DEVICE_LOCK,//设备锁
TAKEN_BACK,//被回收
// VERIFICATION_CODE,//需要验证码
VERIFICATION_CODE,//需要验证码
UNKNOWN,
TIMEOUT,
}

View File

@ -69,7 +69,7 @@ open class ByteArrayDataOutputStream : DataOutputStream(ByteArrayOutputStream())
fun lazyEncode(t: (ByteArrayDataOutputStream) -> Unit): ByteArray = ByteArrayDataOutputStream().let { t(it); return it.toByteArray() }
@ExperimentalUnsignedTypes
fun getRandomKey(length: Int): ByteArray {
fun getRandomByteArray(length: Int): ByteArray {
val bytes = LinkedList<Byte>()
repeat(length) { bytes.add((Math.random() * 255).toByte()) }
return bytes.toByteArray()

View File

@ -0,0 +1,127 @@
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.mamoe.mirai.Robot
import net.mamoe.mirai.network.packet.login.LoginState
import net.mamoe.mirai.utils.RobotAccount
import java.util.*
/**
* @author Him188moe
*/
val qqList = "2258868346----123456789.\n" +
"1545483785----yuk7k1dxnf3jn5\n" +
"2948786488----123123123\n" +
"3059674084----qq123456\n" +
"1918079979----123456789.\n" +
"3050478794----18872590321\n" +
"3331537204----123456789.\n" +
"2128659972----123456789.\n" +
"3435376516----abc123456\n" +
"2980527804----a123456\n" +
"2752195782----qq123456789\n" +
"3130257966----13415986622\n" +
"1802730396----123456789\n" +
"3021732783----15866103923\n" +
"306499606----abc123456\n" +
"2893904328----abc123456\n" +
"1765904806----123456789\n" +
"3254202261----15223045268\n" +
"2947707697----abc123456\n" +
"3500959200----123456789.\n" +
"2169513531----123456789.\n" +
"2983688661----a123456\n" +
"1246882194----pz49779866\n" +
"2315275635----147258369\n" +
"2802294904----123456789\n" +
"2955364492----1234567890\n" +
"1753325115----123456789\n" +
"2642725191----qq123456\n" +
"2152972686----123456789.\n" +
"2845953617----123456789.\n" +
"3329641753----123456789.\n" +
"1458302685----123456789a\n" +
"2351156352----987654321\n" +
"2304786984----fkhwt53787\n" +
"3322756212----123456789.\n" +
"3187253283----123456789.\n" +
"3168715730----147258369\n" +
"2189916732----18831892323\n" +
"2965337631----123456789.\n" +
"1901802165----123456789.\n" +
"414015319----abc123456\n" +
"3400636089----123456789a\n" +
"3530336304----seoua80060\n" +
"3147312971----123456789.\n" +
"3011083526----yp70y9\n" +
"286888078----abc123456\n" +
"3126754112----1234567890\n" +
"2924643025----123123123\n" +
"341870356----ncvhZtQD\n" +
"3358177328----123456789a\n" +
"1396419201----eakuj14475\n" +
"3541159580----123456789.\n" +
"2540245592----1234567890\n" +
"2024802855----123456789.\n" +
"2578309660----1234567890\n" +
"1934965091----123456789.\n" +
"3449408956----a123456789\n" +
"2509348670----123456789.\n" +
"2305961679----123456789.\n" +
"3532858521----123456789.\n" +
"3308276898----123456789a\n" +
"1760897490----123456789\n" +
"2920800012----123123123\n" +
"2923942248----123123123\n" +
"3216600579----13882755274\n" +
"3100259299----qq123456\n" +
"3242723735----1234567890\n" +
"2142733062----123456789.\n" +
"1557689693----123456789\n" +
"3505693439----sb2662vqy6q\n" +
"3231125974----123456789.\n" +
"3433048975----13893690883\n" +
"3168017129----18780999209\n" +
"2922045831----123123123\n" +
"3578152022----a123456789\n" +
"2116254935----147258369\n" +
"3158479284----1234567890\n" +
"3149394424----qq123456789\n" +
"2829521712----123456789.\n" +
"3218671461----123456789.\n" +
"3035873094----123456789a\n" +
"2224518667----147258369\n" +
"3175801590----123456789.\n" +
"3203228181----123456789a\n" +
"3213497536----123456789a\n" +
"3377317115----123456789\n" +
"2672537341----qq123456789\n" +
"2945957617----123123123\n" +
"2763390197----123456789.\n" +
"3322711709----123456789."
fun main() {
val goodRobotList = Collections.synchronizedList(mutableListOf<Robot>())
qqList.split("\n").forEach {
GlobalScope.launch {
val strings = it.split("----")
Robot(RobotAccount(strings[0].toLong(), strings[1].let { password ->
if (password.endsWith(".")) {
return@let password.substring(0, password.length - 1)
}
return@let password
}), listOf()).network.tryLogin { state ->
if (!(state == LoginState.BLOCKED || state == LoginState.DEVICE_LOCK || state == LoginState.WRONG_PASSWORD)) {
goodRobotList.add(this)
}
}
}
}
Thread.sleep(9 * 3000)
println(goodRobotList.joinToString("\n") { it.account.qqNumber.toString() + " " + it.account.password })
}