Enhanced output & login flow

This commit is contained in:
Him188moe 2019-09-07 20:50:00 +08:00
parent 2c108b17a2
commit 7fb588be87
13 changed files with 151 additions and 73 deletions

View File

@ -8,6 +8,8 @@ import net.mamoe.mirai.network.packet.login.LoginState;
import net.mamoe.mirai.task.MiraiTaskManager;
import net.mamoe.mirai.utils.LoggerTextFormat;
import net.mamoe.mirai.utils.MiraiLogger;
import net.mamoe.mirai.utils.MiraiLoggerKt;
import net.mamoe.mirai.utils.RobotAccount;
import net.mamoe.mirai.utils.config.MiraiConfig;
import net.mamoe.mirai.utils.config.MiraiConfigSection;
import net.mamoe.mirai.utils.setting.MiraiSettingListSection;
@ -16,7 +18,9 @@ import net.mamoe.mirai.utils.setting.MiraiSettings;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
/**
* @author NaturalHG
@ -174,17 +178,24 @@ public class MiraiServer {
getLogger().info("Initializing [Robot]s");
try {
getAvailableRobot();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
/*
this.qqs.keySet().stream().map(key -> this.qqs.getSection(key)).forEach(section -> {
getLogger().info("Initializing [Robot] " + section.getString("account"));
try {
Robot robot = new Robot(section);
var state = robot.network.tryLogin$mirai_core().get();
//robot.network.tryLogin$mirai_core().whenComplete((state, e) -> {
if (state == LoginState.SUCCEED) {
if (state == LoginState.SUCCESS) {
Robot.instances.add(robot);
getLogger().success(" Login Succeed");
getLogger().success(" Login Succeed");
} else {
getLogger().error(" Login Failed with error " + state);
getLogger().error(" Login Failed with error " + state);
robot.close();
}
// }).get();
@ -194,8 +205,42 @@ public class MiraiServer {
getLogger().error("Could not load QQ robots config!");
System.exit(1);
}
});
});*/
}
String qqList = "3150499752----1234567890\n" +
"3119292829----987654321\n" +
"2399148773----12345678910\n" +
"3145561616----987654321\n" +
"2374150554----12345678910\n" +
"2375307394----12345678910\n" +
"1531848970----1234567890\n" +
"1947293188----a123456789\n" +
"1771231721----123456789a\n" +
"2401645747----12345678910\n" +
"3338427598----987654321\n" +
"3055657369----1234567890\n" +
"3502771486----987654321\n" +
"1515419818----1234567890\n" +
"2402273360----12345678910\n" +
"3107367848----987654321\n" +
"3487109947----123456789a\n" +
"3489288352----123456789a\n" +
"2385617018----12345678910\n" +
"1251003390----123456789a\n";
private Robot getAvailableRobot() throws ExecutionException, InterruptedException {
for (String it : qqList.split("\n")) {
var strings = it.split("----");
var robot = new Robot(new RobotAccount(Long.parseLong(strings[0]), strings[1]), List.of());
if (robot.network.tryLogin$mirai_core().get() == LoginState.SUCCESS) {
MiraiLoggerKt.success(robot, "Login succeed");
return robot;
}
}
throw new RuntimeException();
}
}

View File

@ -90,7 +90,7 @@ class RobotNetworkHandler(private val robot: Robot) : Closeable {
/**
* 仅当 [LoginState] [LoginState.UNKNOWN] 且非 [LoginState.TIMEOUT] 才会调用 [loginHook].
* 如果要输入验证码, 那么会以参数 [LoginState.VERIFICATION_CODE] 调用 [loginHandler], 登录完成后再以 [LoginState.SUCCEED] 调用 [loginHandler]
* 如果要输入验证码, 那么会以参数 [LoginState.VERIFICATION_CODE] 调用 [loginHandler], 登录完成后再以 [LoginState.SUCCESS] 调用 [loginHandler]
*
* @param touchingTimeoutMillis 连接每个服务器的 timeout
*/
@ -296,7 +296,7 @@ class RobotNetworkHandler(private val robot: Robot) : Closeable {
private lateinit var token0825: ByteArray
private var loginTime: Int = 0
private lateinit var loginIP: String
private var tgtgtKey: ByteArray? = null
private var tgtgtKey: ByteArray = getRandomByteArray(16)
private var tlv0105: ByteArray = lazyEncode {
it.writeHex("01 05 00 30")
@ -329,8 +329,7 @@ class RobotNetworkHandler(private val robot: Robot) : Closeable {
this.loginIP = packet.loginIP
this.loginTime = packet.loginTime
this.token0825 = packet.token0825
this.tgtgtKey = packet.tgtgtKey
sendPacket(ClientPasswordSubmissionPacket(robot.account.qqNumber, robot.account.password, packet.loginTime, packet.loginIP, packet.tgtgtKey, packet.token0825))
sendPacket(ClientPasswordSubmissionPacket(robot.account.qqNumber, robot.account.password, packet.loginTime, packet.loginIP, this.tgtgtKey!!, packet.token0825))
}
}
@ -367,10 +366,14 @@ class RobotNetworkHandler(private val robot: Robot) : Closeable {
this.token00BA = packet.token00BA
if (packet.transmissionCompleted) {
(MiraiServer.getInstance().parentFolder + "VerificationCode.png").writeBytes(this.captchaCache!!)
robot notice (CharImageUtil.createCharImg(ImageIO.read(this.captchaCache!!.inputStream())))
robot notice ("需要验证码登录")
robot notice ("若看不清请查根目录下 VerificationCode.png")
robot notice ("需要验证码登录, 验证码为 4 字母")
try {
(MiraiServer.getInstance().parentFolder + "VerificationCode.png").writeBytes(this.captchaCache!!)
robot notice ("若看不清字符图片, 请查看 Mirai 根目录下 VerificationCode.png")
} catch (e: Exception) {
robot notice "无法写出验证码文件, 请尝试查看以上字符图片"
}
robot notice ("若要更换验证码, 请直接回车")
val code = Scanner(System.`in`).nextLine()
if (code.isEmpty() || code.length != 4) {
@ -391,40 +394,18 @@ class RobotNetworkHandler(private val robot: Robot) : Closeable {
}
//是ClientPasswordSubmissionPacket之后服务器回复的
is ServerLoginResponseResendPacket -> {
is ServerLoginResponseKeyExchangePacket -> {
//if (packet.tokenUnknown != null) {
//this.token00BA = packet.token00BA!!
//println("token00BA changed!!! to " + token00BA.toUByteArray())
//}
if (packet.flag == ServerLoginResponseResendPacket.Flag.`08 36 31 03`) {
if (packet.flag == ServerLoginResponseKeyExchangePacket.Flag.`08 36 31 03`) {
this.tgtgtKey = packet.tgtgtKey
sendPacket(ClientLoginResendPacket3104(
robot.account.qqNumber,
robot.account.password,
loginTime,
loginIP,
tgtgtKey!!,
token0825,
when (packet.tokenUnknown != null) {
true -> packet.tokenUnknown!!
false -> this.token00BA
},
packet._0836_tlv0006_encr
))
sendPacket(ClientLoginResendPacket3104(robot.account.qqNumber, robot.account.password, loginTime, loginIP, tgtgtKey, token0825, packet.tokenUnknown
?: this.token00BA, packet.tlv0006))
} else {
sendPacket(ClientLoginResendPacket3106(
robot.account.qqNumber,
robot.account.password,
loginTime,
loginIP,
tgtgtKey!!,
token0825,
when (packet.tokenUnknown != null) {
true -> packet.tokenUnknown!!
false -> this.token00BA
},
packet._0836_tlv0006_encr
))
sendPacket(ClientLoginResendPacket3106(robot.account.qqNumber, robot.account.password, loginTime, loginIP, tgtgtKey, token0825, packet.tokenUnknown
?: token00BA, packet.tlv0006))
}
}
@ -446,7 +427,7 @@ class RobotNetworkHandler(private val robot: Robot) : Closeable {
}
is ServerLoginSuccessPacket -> {
socketHandler.loginFuture!!.complete(LoginState.SUCCEED)
socketHandler.loginFuture!!.complete(LoginState.SUCCESS)
sendPacket(ClientSKeyRequestPacket(robot.account.qqNumber, sessionKey))
}
@ -467,8 +448,8 @@ class RobotNetworkHandler(private val robot: Robot) : Closeable {
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 ServerLoginResponseKeyExchangePacket.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))
@ -489,7 +470,6 @@ class RobotNetworkHandler(private val robot: Robot) : Closeable {
override fun close() {
this.captchaCache = null
this.tgtgtKey = null
this.heartbeatFuture?.cancel(true)
this.sKeyRefresherFuture?.cancel(true)

View File

@ -26,6 +26,10 @@ class ClientAccountInfoRequestPacket(
it.writeByte(0x00)
}
}
override fun getFixedId(): String {
return this.idHex + " ?? ??"
}
}
@PacketId("00 5C")

View File

@ -22,6 +22,10 @@ class ClientHeartbeatPacket(
it.writeHex("00 01 00 01")
}
}
override fun getFixedId(): String {
return this.idHex + " ?? ??"
}
}
class ServerHeartbeatResponsePacket(input: DataInputStream) : ServerPacket(input)

View File

@ -23,6 +23,10 @@ class ClientSKeyRequestPacket(
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")
}
}
override fun getFixedId(): String {
return this.idHex + " ?? ??"
}
}
/**

View File

@ -5,7 +5,10 @@ import net.mamoe.mirai.network.packet.PacketNameFormatter.adjustName
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.TEA
import net.mamoe.mirai.utils.getAllDeclaredFields
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream
/**
@ -50,11 +53,10 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
"08 36 31 03", "08 36 31 04", "08 36 31 05", "08 36 31 06" -> {
when (bytes.size) {
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
}
271, 207 -> return ServerLoginResponseKeyExchangePacket.Encrypted(stream, when (idHex) {
"08 36 31 03" -> ServerLoginResponseKeyExchangePacket.Flag.`08 36 31 03`
else -> ServerLoginResponseKeyExchangePacket.Flag.OTHER
}).apply { this.idHex = idHex }
871 -> return ServerLoginResponseVerificationCodeInitPacket.Encrypted(stream).apply { this.idHex = idHex }
}
@ -132,14 +134,37 @@ 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())
return decryptAsByteArray(key).dataInputStream()
}
@ExperimentalUnsignedTypes
fun decryptBy(keyHex: String): DataInputStream {
return this.decryptBy(keyHex.hexToBytes())
}
fun decryptBy(key1: ByteArray, key2: ByteArray): DataInputStream {
return TEA.decrypt(this.decryptAsByteArray(key1), key2).dataInputStream();
}
@ExperimentalUnsignedTypes
fun decryptBy(key1: String, key2: ByteArray): DataInputStream {
return this.decryptBy(key1.hexToBytes(), key2)
}
@ExperimentalUnsignedTypes
fun decryptBy(key1: ByteArray, key2: String): DataInputStream {
return this.decryptBy(key1, key2.hexToBytes())
}
@ExperimentalUnsignedTypes
fun decryptBy(keyHex1: String, keyHex2: String): DataInputStream {
return this.decryptBy(keyHex1.hexToBytes(), keyHex2.hexToBytes())
}
private fun decryptAsByteArray(key: ByteArray): ByteArray {
input.goto(14)
return TEA.decrypt(input.readAllBytes().cutTail(1), key)
}
}

View File

@ -1,7 +1,10 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEA
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream
import java.io.IOException
@ -20,7 +23,6 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
var loginTime: Int = 0
lateinit var loginIP: String
lateinit var token0825: ByteArray
lateinit var tgtgtKey: ByteArray
enum class Type {
TYPE_08_25_31_01,
@ -41,7 +43,6 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
loginTime = input.readInt()
loginIP = input.readIP()
tgtgtKey = getRandomByteArray(16)
}
else -> {

View File

@ -41,8 +41,8 @@ class ClientPasswordSubmissionPacket(
@PacketId("08 36 31 04")
@ExperimentalUnsignedTypes
class ClientLoginResendPacket3104(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, tlv_0006_encr: ByteArray? = null)
: ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, tlv_0006_encr)
class ClientLoginResendPacket3104(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, tlv0006: ByteArray? = null)
: ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, tlv0006)
@PacketId("08 36 31 05")
@ExperimentalUnsignedTypes
@ -51,8 +51,8 @@ class ClientLoginResendPacket3105(qq: Long, password: String, loginTime: Int, lo
@PacketId("08 36 31 06")
@ExperimentalUnsignedTypes
class ClientLoginResendPacket3106(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, tlv_0006_encr: ByteArray? = null)
: ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, tlv_0006_encr)
class ClientLoginResendPacket3106(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, tlv0006: ByteArray? = null)
: ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, tlv0006)
@ExperimentalUnsignedTypes
open class ClientLoginResendPacket internal constructor(
@ -63,7 +63,7 @@ open class ClientLoginResendPacket internal constructor(
val tgtgtKey: ByteArray,
val token0825: ByteArray,
val token00BA: ByteArray,
val tlv_0006_encr: ByteArray? = null
val tlv0006: ByteArray? = null
) : ClientPacket() {
override fun encode() {
this.writeQQ(qq)
@ -74,7 +74,7 @@ open class ClientLoginResendPacket internal constructor(
this.write(TEA.encrypt(object : ByteArrayDataOutputStream() {
override fun toByteArray(): ByteArray {
this.writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825, tlv_0006_encr)
this.writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825, tlv0006)
this.writeHex("01 10") //tag
this.writeHex("00 3C")//length
@ -94,7 +94,7 @@ open class ClientLoginResendPacket internal constructor(
* @author Him188moe
*/
@ExperimentalUnsignedTypes
private fun DataOutputStream.writePart1(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, tlv_0006_encr: ByteArray? = null) {
private fun DataOutputStream.writePart1(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, tlv0006: ByteArray? = null) {
//this.writeInt(System.currentTimeMillis().toInt())
this.writeHex("01 12")//tag
@ -107,8 +107,8 @@ private fun DataOutputStream.writePart1(qq: Long, password: String, loginTime: I
this.writeQQ(qq)
this.writeHex("00 06")//tag
this.writeHex("00 78")//length
if (tlv_0006_encr != null) {
this.write(tlv_0006_encr)
if (tlv0006 != null) {
this.write(tlv0006)
} else {
this.writeTLV0006(qq, password, loginTime, loginIP, tgtgtKey)
}

View File

@ -7,7 +7,7 @@ enum class LoginState {
/**
* 登录成功
*/
SUCCEED,
SUCCESS,
/**
* 密码错误

View File

@ -1,5 +1,6 @@
package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.goto
@ -7,16 +8,18 @@ import net.mamoe.mirai.utils.TestedSuccessfully
import java.io.DataInputStream
/**
* 服务器进行加密后返回 tgtgtKey
*
* @author NaturalHG
*/
@PacketId("08 36 31 03")
class ServerLoginResponseResendPacket(input: DataInputStream, val flag: Flag) : ServerPacket(input) {
class ServerLoginResponseKeyExchangePacket(input: DataInputStream, val flag: Flag) : ServerPacket(input) {
enum class Flag {
`08 36 31 03`,
OTHER,
}
lateinit var _0836_tlv0006_encr: ByteArray;//120bytes
lateinit var tlv0006: ByteArray;//120bytes
var tokenUnknown: ByteArray? = null
lateinit var tgtgtKey: ByteArray//16bytes
@ -26,7 +29,7 @@ class ServerLoginResponseResendPacket(input: DataInputStream, val flag: Flag) :
tgtgtKey = this.input.readNBytes(16)//22
//this.input.skip(2)//25
this.input.goto(25)
_0836_tlv0006_encr = this.input.readNBytes(120)
tlv0006 = this.input.readNBytes(120)
when (flag) {
Flag.`08 36 31 03` -> {
@ -43,7 +46,10 @@ class ServerLoginResponseResendPacket(input: DataInputStream, val flag: Flag) :
}
class Encrypted(input: DataInputStream, private val flag: Flag) : ServerPacket(input) {
@ExperimentalUnsignedTypes
@TestedSuccessfully
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseResendPacket = ServerLoginResponseResendPacket(this.decryptBy(tgtgtKey), flag).setId(this.idHex)
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseKeyExchangePacket {
return ServerLoginResponseKeyExchangePacket(this.decryptBy(Protocol.shareKey, tgtgtKey), flag).setId(this.idHex)
}
}
}

View File

@ -1,8 +1,10 @@
package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.TEA
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.utils.TestedSuccessfully
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream
@ -53,7 +55,7 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
@ExperimentalUnsignedTypes
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseSuccessPacket {
input goto 14
return ServerLoginResponseSuccessPacket(TEA.decrypt(TEA.decrypt(input.readAllBytes().cutTail(1), Protocol.shareKey), tgtgtKey).dataInputStream()).setId(this.idHex)
return ServerLoginResponseSuccessPacket(this.decryptBy(Protocol.shareKey, tgtgtKey)).setId(this.idHex)
}
}

View File

@ -17,6 +17,7 @@ public final class TEA {
private static final long UINT32_MASK = 0xffffffffL;
private final long[] mKey;
private final Random mRandom;
private final byte[] key;
private byte[] mOutput;
private byte[] mInBlock;
private int mIndexPos;
@ -26,6 +27,7 @@ public final class TEA {
private boolean isFirstBlock;
public TEA(byte[] key) {
this.key = key;
mKey = new long[4];
for (int i = 0; i < 4; i++) {
mKey[i] = pack(key, i * 4, 4);
@ -250,6 +252,12 @@ public final class TEA {
}
public byte[] decrypt(byte[] ciphertext) {
return decrypt(ciphertext, 0, ciphertext.length);
try {
return decrypt(ciphertext, 0, ciphertext.length);
} catch (Exception e) {
System.out.println("Source: " + UtilsKt.toUHexString(ciphertext, " "));
System.out.println("Key: " + UtilsKt.toUHexString(this.key, " "));
throw e;
}
}
}

View File

@ -88,4 +88,3 @@ fun main() {
println(goodRobotList.joinToString("\n") { it.account.qqNumber.toString() + " " + it.account.password })
}