diff --git a/.gitignore b/.gitignore index 072488eb8..2aae90ca0 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,7 @@ hs_err_pid* mirai.iml /.idea/ .idea/* -/.idea/* \ No newline at end of file +/.idea/* + +test/ +/test \ No newline at end of file diff --git a/document/protocol/Get_tlv_0006.md b/document/protocol/Get_tlv_0006.md index 8759b129d..b9dbe0c5a 100644 --- a/document/protocol/Get_tlv_0006.md +++ b/document/protocol/Get_tlv_0006.md @@ -2,16 +2,16 @@ ## Get_tlv_0006 -C 构建包 +C 构建包, 近 C 使用 -### Var +#### Var type | var name | value/from ---- | ---|--- -?bytes | MD51 | md5(raw password) +?bytes | MD51 | md5(raw password) ?bytes | MD52 | md5((MD51 + “ 00 00 00 00 ” + g_QQ).hextobytes()) -4bytes |m_loginIP | 服务器提供(Dispose_0825) -16bytes| m_tgtgtKey| random -### Packet data +4bytes |m_loginIP | 服务器提供(Dispose_0825) +16bytes| m_tgtgtKey| | +#### Packet data type | value ---- | --- diff --git a/document/protocol/Login Flow.md b/document/protocol/Login Flow.md index 117489bb5..2608f2289 100644 --- a/document/protocol/Login Flow.md +++ b/document/protocol/Login Flow.md @@ -14,7 +14,7 @@ C: 发送登录`08 25 31 01` S: 回复`08 25 31 01`(ID与C发送的相同), 告诉C是否需 redirect -**Redirection** +**[Redirection](Redirection.md)** 如果需要 redirect C: 发送 redirect 包`08 25 31 02`到新的服务器 @@ -46,10 +46,21 @@ S: 发送 `08 36 31 03` 告知登录结果. - g_clientKey - token38 - token88 -- encryptionKey +- encryptionKey + +若不成功, 理由: +- 需要验证码: + //todo +- 密码错误 +- 未知(重新登录) +- 冻结 +- 账号不存在 +- 设备锁 +- 被回收 C: 回复 `08 28 04 34` -Sample +Sample + ```text 02 37 13 08 28 04 34 76 E4 B8 DD 02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A 00 38 BA 24 BF EA 76 94 2C 9B 91 A8 8F 0E 7C EC F5 41 77 3C B9 D4 95 50 F2 00 FD CB E3 48 36 FB 89 13 CE E4 EA 76 A2 2F 20 86 F6 0F E0 54 55 6E D4 0B 9B EA 07 6B E1 D4 87 56 F9 99 8F FA 12 8E 22 A6 5A 9D A6 DC C9 B6 5C 5A EC BE BF CC 38 BD E1 5A 23 21 CE 02 31 F1 E1 BD FB 8E 4D E9 59 E6 BB FB B2 36 0C 47 0A C0 F7 94 63 C3 2F AB 6E AE 00 01 1F 5F 60 8E B5 79 97 EB 10 59 A0 29 B3 3B 9C BA 5D 33 C4 2A 57 CA CF 94 7A 2D DD F7 B3 9C BC 65 5D D5 62 53 A8 1D D2 F1 5B DD D7 24 32 63 60 60 DD 33 1F 3A C0 71 38 86 BC 78 D3 7C 7A E1 97 71 AB B7 59 AD 27 32 D5 AF F3 DC 1B 7B 70 3B 08 C0 91 D8 BC F1 C4 DA E3 DA 86 A1 27 8A EE C3 5F D6 25 42 A0 CB 19 7F 08 80 F8 65 2C 27 31 B7 D4 85 C3 49 BD 99 48 FE A9 63 78 6D 18 C3 4E BB F7 8A C4 80 8C 8A 17 EB 47 AF 2A 12 73 71 08 A6 E7 C3 08 2B 9A 6F 8A C2 6C 3B 1A CF 05 D8 57 63 33 AC BD 45 98 C1 85 56 08 0F 9F 36 FD 60 69 BC D0 94 1A 11 4D C6 3E 78 1D F1 67 D2 1D C4 C8 17 2F DC F4 B6 4F 5F F5 EE 8B 73 68 AA 3B BA C6 94 C8 21 1E 95 6D C2 7A BE 8B 1D 92 21 8D 2C D8 B6 86 D1 30 BB 72 34 B9 A0 D2 2E 4C 98 3C 17 E2 B2 6A AD 75 E8 B0 DE F4 1A 6F 15 93 47 B3 4D DA 6F BE A3 47 D3 9B 58 2D 4B A3 76 0E 39 ED A5 C3 0A 34 BA 78 01 AE 20 A3 38 CE BA CD F6 D7 1B C9 E7 4C 83 6E 31 34 25 16 64 BA EE 4B 8D E7 0E 2F C8 08 72 50 AE 91 16 7F 68 14 60 7E D8 3F CC 26 2D F6 BC 65 72 C8 F4 EA 55 E6 1B E0 BF F4 9F 9C FD A9 93 B6 62 78 F0 A1 19 D2 87 6E B8 B7 E3 70 13 09 95 29 C9 05 EC 99 36 5C 96 47 C1 C4 06 5C 23 5C A3 AD B0 39 BC 70 75 3D AA E9 16 03 0E 62 1B D0 78 EA F2 5C FD 9C 04 D9 AB 75 00 F8 37 F1 A8 DD 7B 65 91 D3 58 DE C5 BA 9E AC 13 DE 35 BA 17 DC D1 AB A5 96 C4 99 81 8E 21 4B 2F C1 9B 4C E1 56 A7 5D AC 26 71 EC 49 F0 A6 B1 F5 43 EA AC BE E6 9F A0 C2 E1 68 35 97 7B 81 76 AF 9E BD BC A7 D8 9E FC C0 E8 21 B0 BA 20 6A D0 BD E7 00 59 06 61 A1 DF AA 9F BA F4 5D A6 7B 5B A1 D8 6B B5 E9 72 66 51 8A D3 CE 51 A9 08 C7 11 4B FB 29 2E 6C 48 5B 8A 50 C6 5D 3A C1 9E A1 51 B6 56 DD 6B F5 D2 FD AE AB A4 4A A8 1F 99 BA 7D 4F 62 D7 64 22 31 04 62 36 62 65 96 B3 5A 35 03 ``` diff --git a/document/protocol/Password Verified.md b/document/protocol/Password Verified.md new file mode 100644 index 000000000..4fcc00a72 --- /dev/null +++ b/document/protocol/Password Verified.md @@ -0,0 +1,23 @@ +# TIM Protocol + +## Password Verified + +### S -> `08 25 31 02`(may be another) + +#### Var +type | var name | value/from +---- | ---|--- +int |g_qq | qq number +16bytes | tgtgtKey | | + +#### Decryption + +//todo + +#### Packet data - Requiring + +//todo + +#### Packet data - Not Requiring + +//todo \ No newline at end of file diff --git a/document/protocol/ReceiveRediretion.md b/document/protocol/ReceiveRediretion.md deleted file mode 100644 index a2739aca6..000000000 --- a/document/protocol/ReceiveRediretion.md +++ /dev/null @@ -1,19 +0,0 @@ -# TIM Protocol - -## Redirection - -S -> C - -### Var -type | var name | value/from ----- | ---|--- -int |g_qq | qq number -int| g_server| server ip -### Packet data - -skip 14 -if (flag == "08 25 31 02") - data = decrypt (read 14..length-1, #redirectionKey) -else - data = decrypt (read 14..length-1, #_0825key) - diff --git a/document/protocol/Redirection.md b/document/protocol/Redirection.md new file mode 100644 index 000000000..856d83e0b --- /dev/null +++ b/document/protocol/Redirection.md @@ -0,0 +1,124 @@ +# TIM Protocol + +## Redirection + +### S -> `08 25 31 02`(may be another) + +#### Decryption + +skip 14 bytes +if (flag == "08 25 31 02") + data = decrypt (read bytes 14..length-1, #redirectionKey) +else data = decrypt (read bytes 14..length-1, #_0825key) + +#### Packet data - Requiring + +**read byte == 0xFE** +skip 94 bytes +String serverIp = read 4 bytes and join them with separator "." + +#### Packet data - Not Requiring + +**read byte == 0x00** +skip 4 bytes +56bytes token0825 = read 56 bytes +skip 6 bytes +int loginTime = read int +skip 1 byte +String loginIP = read 4 bytes and join them with separator "." +16bytes tgtgtKey = random 16 bytes + +### C -> S - Requiring `08 25 31 02` + +#### Var + +type | var name | value/from +---- | ---|--- +int | qq | | +String | server ip | from redirection packet + +#### Packet data + +type | value +---- | --- +hex |#head +hex |#ver +hex |08 25 31 02 +int |qq +hex |#fixver +hex |#redirectionKey +bytes |[TEA encrypted data](#tea-encrypted-data) + +##### TEA encrypted data +Key : #redirectionKey + +type | value +---- | --- +hex |#_0825data0 +hex |#_0825data2 +int |qq +hex |00 01 00 00 03 09 00 0C 00 01 +4bytes |g_server(split with "." and convert to byte) +hex |01 6F A1 58 22 01 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 03 00 19 +hex | #publicKey + +#### Note + +Send the packet to new server via port 8000 + + +### C -> S - Not Requiring(Submitting password) `08 36 31 03` + +#### Var + +type | var name | value/from +---- | ---|--- +int | qq | | +String | password | | +String | device name | UTF8 encoding. Sample: DESKTOP-M19QRYU +16bytes | tgtgtKey | | +bytes | MD5_1 | md5(password) +bytes | MD5_2 | md5(MD5_1 + bytes{0, 0, 0, 0}} + qq.tobytes) + +#### Packet data + +type | value +---- | --- +hex |#head +hex |#ver +hex |08 36 31 03 +int |qq +hex |03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03 00 19 +hex |#publicKey +hex | 00 00 00 10 +hex | EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA +bytes |[TEA encrypted data](#tea-encrypted--data) + +##### TEA encrypted data +Key : #shareKey + +type | value +---- | --- +hex |01 12 +hex |00 38 +int |token0825(from [Packet data - Not Requiring](#packet-data---not-requiring)) +hex |03 0F +int | device name length + 2 +int | device name length +bytes | device name +hex | 00 05 00 06 00 02 +int | qq +hex | 00 06 00 78 +bytes | [TLV0006](Get_tlv_0006.md) Using md5 that you just calculated in +hex | fix = 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 +hex | 00 1A 00 40 +bytes | TEAEncrypt(fix, tgtgtKey) +hex | #_0825data0 +hex | #_0825data2 +int | qq +hex | 00 00 00 00 +hex | 01 03 00 14 00 01 00 10 60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6 03 12 00 05 01 00 00 00 01 05 08 00 05 01 00 00 00 00 03 13 00 19 01 01 02 00 10 04 EA 78 D1 A4 FF CD CC 7C B8 D4 12 7D BB 03 AA +hex | 00 00 00 00 +hex | 01 02 00 62 00 01 04 EB B7 C1 86 F9 08 96 ED 56 84 AB 50 85 2E 48 00 38 E9 AA 2B 4D 26 4C 76 18 FE 59 D5 A9 82 6A 0C 04 B4 49 50 D7 9B B1 FE 5D 97 54 8D 82 F3 22 C2 48 B9 C9 22 69 CA 78 AD 3E 2D E9 C9 DF A8 9E 7D 8C 8D 6B DF 4C D7 34 D0 D3 00 14 +bytes | CRCKey = random 16 +bytes | getCRC(CRCKey) //do it yourself diff --git a/document/protocol/Touch.md b/document/protocol/Touch.md index b82c649a5..469b6da68 100644 --- a/document/protocol/Touch.md +++ b/document/protocol/Touch.md @@ -2,14 +2,15 @@ ## Touch -C -> S +### C -> S -### Var +#### Var type | var name | value/from ---- | ---|--- int |g_qq | qq number int| g_server| server ip -### Packet data + +#### Packet data type | value ---- | --- @@ -35,4 +36,6 @@ int | g_server hex | 00 02 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 02 00 19 hex | #publicKey -TEA加密以上, key=MD52 \ No newline at end of file +### S -> C + +[Redirection](Redirection.md) \ No newline at end of file diff --git a/mirai-core/pom.xml b/mirai-core/pom.xml index f4b0ce946..6d5c6ff3d 100644 --- a/mirai-core/pom.xml +++ b/mirai-core/pom.xml @@ -21,6 +21,23 @@ + + org.apache.mina + mina-core + + + + org.apache.mina + mina-filter-ssl + 1.1.7 + + + + org.apache.mina + mina-filter-compression + 2.1.3 + + io.netty netty-all diff --git a/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java b/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java index e9d7805ce..5bb7ae9fe 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java @@ -5,6 +5,7 @@ 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.Robot; +import net.mamoe.mirai.network.packet.client.touch.ClientTouchPacket; import net.mamoe.mirai.task.MiraiTaskManager; import net.mamoe.mirai.utils.LoggerTextFormat; import net.mamoe.mirai.utils.MiraiLogger; @@ -16,7 +17,6 @@ import net.mamoe.mirai.utils.setting.MiraiSettingMapSection; import java.io.File; import java.io.IOException; -import java.util.List; import java.util.Scanner; public class MiraiServer { @@ -98,7 +98,55 @@ public class MiraiServer { this.initQQConfig(qqs); } - getLogger().info("Ready to enable"); + /* + MiraiSettingMapSection qqs = this.setting.getMapSection("qq"); + qqs.forEach((a,p) -> { + this.getLogger().info(LoggerTextFormat.SKY_BLUE + "Finding available ports between " + "1-65536"); + try { + int port = MiraiNetwork.getAvailablePort(); + this.getLogger().info(LoggerTextFormat.SKY_BLUE + "Listening on port " + port); + + } catch (IOException e) { + e.printStackTrace(); + } + }); + */ + + getLogger().info("ready to connect"); + + Robot robot = new Robot(1994701021, "xiaoqqq"); + try { + //System.out.println(Protocol.Companion.getSERVER_IP().get(3)); + //System.out.println(Protocol.Companion.getSERVER_IP().toString()); + + robot.setServerIP("14.116.136.106"); + robot.sendPacket(new ClientTouchPacket(1994701021, "14.116.136.106")); + while (true) ; + //robot.connect("14.116.136.106"); + //robot.connect(Protocol.Companion.getSERVER_IP().get(2)); + //robot.connect("125.39.132.242"); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } +/* + System.out.println("network test"); + try { + + + MiraiUDPServer server = new MiraiUDPServer(); + MiraiUDPClient client = new MiraiUDPClient(InetAddress.getLocalHost(),9999,MiraiNetwork.getAvailablePort()); + this.getTaskManager().repeatingTask(() -> { + byte[] sendInfo = "test test".getBytes(StandardCharsets.UTF_8); + try { + client.send(new DatagramPacket(sendInfo,sendInfo.length)); + } catch (IOException e) { + e.printStackTrace(); + } + },300); + } catch (IOException e) { + e.printStackTrace(); + }*/ } private void initSetting(File setting) { @@ -134,6 +182,7 @@ public class MiraiServer { MiraiConfigSection section = new MiraiConfigSection<>(); + System.out.println("/"); Scanner scanner = new Scanner(System.in); getLogger().info(LoggerTextFormat.SKY_BLUE + "input one " + LoggerTextFormat.RED + " QQ number " + LoggerTextFormat.SKY_BLUE + "for default robot"); getLogger().info(LoggerTextFormat.SKY_BLUE + "输入用于默认机器人的QQ号"); @@ -143,7 +192,7 @@ public class MiraiServer { String qqPassword = scanner.next(); section.put("password",qqPassword); - section.put("owner", List.of("default")); + section.put("owner","default"); this.qqs.put(String.valueOf(qqNumber),section); this.qqs.save(); @@ -155,34 +204,6 @@ public class MiraiServer { this.enabled = true; getLogger().info(LoggerTextFormat.GREEN + "Server enabled; Welcome to Mirai"); getLogger().info("Mirai Version=" + MiraiServer.MIRAI_VERSION + " QQ Version=" + MiraiServer.QQ_VERSION); - - Robot robot = new Robot(1994701021, "xiaoqqq"); - try { - //System.out.println(Protocol.Companion.getSERVER_IP().toString()); - //robot.connect("127.0.0.1"); - robot.connect("125.39.132.242"); - } catch (InterruptedException e) { - e.printStackTrace(); - System.exit(1); - } -/* - System.out.println("network test"); - try { - - - MiraiUDPServer server = new MiraiUDPServer(); - MiraiUDPClient client = new MiraiUDPClient(InetAddress.getLocalHost(),9999,MiraiNetwork.getAvailablePort()); - this.getTaskManager().repeatingTask(() -> { - byte[] sendInfo = "test test".getBytes(StandardCharsets.UTF_8); - try { - client.send(new DatagramPacket(sendInfo,sendInfo.length)); - } catch (IOException e) { - e.printStackTrace(); - } - },300); - } catch (IOException e) { - e.printStackTrace(); - }*/ } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHook.java b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHook.java index 6264c74d3..a9956d44c 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHook.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHook.java @@ -4,10 +4,8 @@ import lombok.Getter; import lombok.Setter; import net.mamoe.mirai.event.events.Cancellable; import net.mamoe.mirai.event.events.MiraiEvent; -import net.mamoe.mirai.event.events.server.ServerDisableEvent; import java.io.Closeable; -import java.io.IOException; import java.util.function.Consumer; import java.util.function.Predicate; @@ -79,7 +77,7 @@ public class MiraiEventHook implements Closeable { if(!(event instanceof Cancellable && event.isCancelled() && this.isIgnoreCancelled())){ this.getHandler().accept((T) event); } - return this.valid.test((T)event); + return this.valid == null || this.valid.test((T) event); } /** diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/MemoryMonitorTest.java b/mirai-core/src/main/java/net/mamoe/mirai/network/MemoryMonitorTest.java new file mode 100644 index 000000000..2660ed08a --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/MemoryMonitorTest.java @@ -0,0 +1,150 @@ +package net.mamoe.mirai.network; + +import org.apache.mina.core.buffer.IoBuffer; +import org.apache.mina.core.service.IoHandlerAdapter; +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.executor.ExecutorFilter; +import org.apache.mina.filter.logging.LoggingFilter; +import org.apache.mina.transport.socket.DatagramSessionConfig; +import org.apache.mina.transport.socket.nio.NioDatagramAcceptor; + +import java.io.IOException; +import java.net.*; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public class MemoryMonitorTest { + + private static final long serialVersionUID = 1L; + + public static final int PORT = 8080; + + public MemoryMonitorTest() throws IOException { + + NioDatagramAcceptor acceptor = new NioDatagramAcceptor();//创建一个UDP的接收器 + acceptor.setHandler(new YourHandler());//设置接收器的处理程序 + + Executor threadPool = Executors.newFixedThreadPool(1500);//建立线程池 + acceptor.getFilterChain().addLast("exector", new ExecutorFilter(threadPool)); + acceptor.getFilterChain().addLast("logger", new LoggingFilter()); + + DatagramSessionConfig dcfg = acceptor.getSessionConfig();//建立连接的配置文件 + dcfg.setReadBufferSize(4096);//设置接收最大字节默认2048 + dcfg.setReceiveBufferSize(1024);//设置输入缓冲区的大小 + dcfg.setSendBufferSize(1024);//设置输出缓冲区的大小 + dcfg.setReuseAddress(true);//设置每一个非主监听连接的端口可以重用 + + acceptor.bind(new InetSocketAddress(PORT));//绑定端口 + } + + + public static void main(String[] args) throws IOException { + new MemoryMonitorTest(); + } + + public class YourHandler extends IoHandlerAdapter { + +//messageSent是Server响应给Clinet成功后触发的事件 + + @Override + public void messageSent(IoSession session, Object message) throws Exception { + if (message instanceof IoBuffer) { + IoBuffer buffer = (IoBuffer) message; + byte[] bb = buffer.array(); + for (int i = 0; i < bb.length; i++) { + System.out.print((char) bb[i]); + } + } + } + +//抛出异常触发的事件 + + @Override + public void exceptionCaught(IoSession session, Throwable cause) + throws Exception { + cause.printStackTrace(); + session.close(true); + } + +//Server接收到UDP请求触发的事件 + + @Override + public void messageReceived(IoSession session, Object message) + throws Exception { + System.out.println("messageReceived"); + if (message instanceof IoBuffer) { + IoBuffer buffer = (IoBuffer) message; +// byte[] bb = buffer.array(); +// for(int i=0;i { if (packet.serverIP != null) {//redirection - connect(packet.serverIP!!) + serverIP = packet.serverIP!! + //connect(packet.serverIP!!) sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, number)) } else {//password submission this.loginIP = packet.loginIP @@ -93,7 +92,7 @@ class Robot(val number: Int, private val password: String) { is ServerLoginResponseSuccessPacket -> { this._0828_rec_decr_key = packet._0828_rec_decr_key - sendPacket(ClientLoginSucceedConfirmationPacket(this.number, this.serverIP, this.md5_32, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105)) + sendPacket(ClientLoginSucceedConfirmationPacket(this.number, this.serverIP, this.loginIP, this.md5_32, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105)) } //这个有可能是客户端发送验证码之后收到的回复验证码是否正确? @@ -114,14 +113,15 @@ class Robot(val number: Int, private val password: String) { 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()) - else -> throw IllegalStateException() + else -> throw IllegalArgumentException(packet.toString()) } } @ExperimentalUnsignedTypes - private fun sendPacket(packet: ClientPacket) { + fun sendPacket(packet: ClientPacket) { try { MiraiLogger log "Encoding" packet.encode() @@ -129,19 +129,68 @@ class Robot(val number: Int, private val password: String) { e.printStackTrace() } packet.writeHex(Protocol.tail) + println(packet) + println(packet.toByteArray().toUByteArray().toHexString()) /*val p = DatagramPacket(packet.toByteArray()); p.socketAddress = this.serverAddress*/ - channel!!.writeAndFlush(DatagramPacket(packet.toByteArray())) + //ctx.writeAndFlush(packet.toByteArray()).sync() + send(packet.toByteArray()) + //println(channel!!.writeAndFlush(packet.toByteArray()).channel().connect(serverAddress).sync().get()) MiraiLogger info "Packet sent: $packet" } private fun DatagramPacket(toByteArray: ByteArray): DatagramPacket = DatagramPacket(toByteArray, toByteArray.size, this.serverAddress) + // private val socket = DatagramSocket(15314) + @ExperimentalUnsignedTypes + fun send(data: ByteArray) { + try { + val socket = DatagramSocket((15314 + Math.random() * 5).toInt()) + socket.connect(this.serverAddress) + + val dp1 = DatagramPacket(ByteArray(22312), 22312) + socket.send(DatagramPacket(data, data.size)) + socket.receive(dp1) + val zeroByte: Byte = 0 + var i = dp1.data.size - 1; + while (dp1.data[i] == zeroByte) { + --i + } + socket.close() + onPacketReceived(ServerPacket.ofByteArray(dp1.data.copyOfRange(0, i + 1))) + } catch (e: Exception) { + e.printStackTrace() + } + + } + /* +private lateinit var ctx: ChannelHandlerContext @ExperimentalUnsignedTypes @Throws(InterruptedException::class) fun connect(ip: String) { - this.serverIP = ip; + this.serverIP = ip + + + NioDatagramConnector().let { it.handler = object : IoHandlerAdapter(), IoHandler { + + } } + IoConnector connector=udpClient.getConnector(); + connector.getFilterChain().addLast("codec", + ProtocolCodecFilter( + TextLineCodecFactory( + Charset.forName("UTF-8"), + LineDelimiter.WINDOWS.getValue(), + LineDelimiter.WINDOWS.getValue()))); + + ConnectFuture connectFuture=connector.connect(udpClient.getInetSocketAddress()); + // 等待是否连接成功,相当于是转异步执行为同步执行。 + connectFuture.awaitUninterruptibly(); + //连接成功后获取会话对象。如果没有上面的等待,由于connect()方法是异步的, + //connectFuture.getSession(),session可能会无法获取。 + udpClient.setSession(connectFuture.getSession()); + udpClient.getSession().write("Hello,UDPServer!"); + val group = NioEventLoopGroup() try { val b = Bootstrap() @@ -151,14 +200,17 @@ class Robot(val number: Int, private val password: String) { .channel(NioDatagramChannel::class.java) .option(ChannelOption.SO_BROADCAST, true) .handler(object : ChannelInitializer() { + + override fun channelActive(ctx: ChannelHandlerContext?) { + this@Robot.ctx = ctx!! + super.channelActive(ctx) + } + @Throws(Exception::class) override fun initChannel(ch: NioDatagramChannel) { - /*ch.pipeline().addLast(object : MessageToMessageEncoder() { - override fun encode(ctx: ChannelHandlerContext?, msg: ByteArray?, out: MutableList?) { - out!!.add(DatagramPacket(msg!!)) - } - })*/ ch.pipeline().addLast(ByteArrayDecoder()) + ch.pipeline().addLast(ByteArrayEncoder()) + ch.pipeline().addLast(object : SimpleChannelInboundHandler() { override fun channelRead0(ctx: ChannelHandlerContext, bytes: ByteArray) { try { @@ -172,6 +224,20 @@ class Robot(val number: Int, private val password: String) { MiraiLogger.catching(cause) } }) + + ch.pipeline().addLast(object : SimpleChannelInboundHandler() { + override fun channelRead0(ctx: ChannelHandlerContext, bytes: DatagramPacket) { + try { + this@Robot.onPacketReceived(ServerPacket.ofByteArray(bytes.data)) + } catch (e: Exception) { + MiraiLogger.catching(e) + } + } + + override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) { + MiraiLogger.catching(cause) + } + }) } }) @@ -183,5 +249,5 @@ class Robot(val number: Int, private val password: String) { } finally { group.shutdownGracefully().sync() } - } + }*/ } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginPacket.kt index 64152e19b..e6628739e 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientLoginPacket.kt @@ -73,6 +73,7 @@ open class ClientLoginResendPacket internal constructor(val qq: Int, val passwor class ClientLoginSucceedConfirmationPacket( private val qq: Int, private val serverIp: String, + private val loginIP: String, private val md5_32: ByteArray, private val token38: ByteArray, private val token88: ByteArray, @@ -111,7 +112,7 @@ class ClientLoginSucceedConfirmationPacket( this.writeHex("68") this.writeHex("00 00 00 00 00 2D 00 06 00 01") - this.writeIP("127.0.0.1")//本地IP地址? todo test that + this.writeIP(loginIP)//本地IP地址? todo test that return super.toByteArray() } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientServerRedirectionPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientServerRedirectionPacket.kt index dfabce150..6d2f25d6d 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientServerRedirectionPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/login/ClientServerRedirectionPacket.kt @@ -6,6 +6,7 @@ import net.mamoe.mirai.network.packet.client.ClientPacket import net.mamoe.mirai.network.packet.client.writeHex import net.mamoe.mirai.network.packet.client.writeIP import net.mamoe.mirai.network.packet.client.writeQQ +import net.mamoe.mirai.util.ByteArrayDataOutputStream import net.mamoe.mirai.util.TEACryptor import net.mamoe.mirai.util.hexToBytes import java.io.IOException @@ -25,9 +26,9 @@ class ClientServerRedirectionPacket(private val serverIP: String, private val qq this.writeHex(Protocol.redirectionKey) - this.write(TEACryptor.encrypt(object : ClientPacket() { + this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() { @Throws(IOException::class) - override fun encode() { + override fun toByteArray(): ByteArray { this.writeHex(Protocol._0825data0) this.writeHex(Protocol._0825data2) this.writeQQ(qq) @@ -35,7 +36,8 @@ class ClientServerRedirectionPacket(private val serverIP: String, private val qq this.writeIP(serverIP) this.writeHex("01 6F A1 58 22 01 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 03 00 19") this.writeHex(Protocol.publicKey) + return super.toByteArray() } - }.encodeToByteArray(), Protocol.redirectionKey.hexToBytes())) + }.toByteArray(), Protocol.redirectionKey.hexToBytes())) } } \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/touch/ClientTouchPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/touch/ClientTouchPacket.kt index 3f0028464..7e2a9f02d 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/touch/ClientTouchPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/client/touch/ClientTouchPacket.kt @@ -38,10 +38,12 @@ class ClientTouchPacket(val qq: Int, val serverIp: String) : ClientPacket() { this.writeQQ(qq) this.writeHex("00 00 00 00 03 09 00 08 00 01") //this.writeIP("192.168.1.1"); + println("serverIp=$serverIp") this.writeIP(serverIp); //this.writeIP("123456789") this.writeHex("00 02 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 02 00 19") this.writeHex(Protocol.publicKey) + println(super.toUByteArray().toHexString()) return super.toByteArray() } }.toByteArray())) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/ServerPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/ServerPacket.kt index ffdeabd49..6f7321687 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/ServerPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/ServerPacket.kt @@ -19,7 +19,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { @ExperimentalUnsignedTypes fun ofByteArray(bytes: ByteArray): ServerPacket { - println("Raw received: $bytes") + println("Raw received: ${bytes.toUByteArray().toHexString()}") val stream = bytes.dataInputStream() @@ -78,10 +78,9 @@ fun DataInputStream.readUntil(byte: Byte): ByteArray { fun DataInputStream.readIP(): String { var buff = "" for (i in 0..3) { - val byte = readByte() - buff += (byte.toUByte().toString()) + val byte = readUnsignedByte() + buff += byte.toString() if (i != 3) buff += "." - println(byte.toHexString()) } return buff } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseSuccessPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseSuccessPacket.kt index 10ce36612..376d44588 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseSuccessPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/login/ServerLoginResponseSuccessPacket.kt @@ -123,7 +123,7 @@ class ServerLoginResponseSuccessPacketEncrypted(input: DataInputStream, val leng @ExperimentalUnsignedTypes fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseSuccessPacket {//todo test - this.input.skip(14) + this.input.skip(7) return ServerLoginResponseSuccessPacket(TEACryptor.decrypt(TEACryptor.decrypt(this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, Protocol.shareKey.hexToBytes()), tgtgtKey).dataInputStream(), length); //TeaDecrypt(取文本中间(data, 43, 取文本长度(data) - 45), m_0828_rec_decr_key) } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/security/ServerSessionKeyResponsePacketEncrypted.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/security/ServerSessionKeyResponsePacketEncrypted.kt index 831968cf0..f27be641c 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/security/ServerSessionKeyResponsePacketEncrypted.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/security/ServerSessionKeyResponsePacketEncrypted.kt @@ -47,7 +47,7 @@ class ServerSessionKeyResponsePacketEncrypted(inputStream: DataInputStream) : Se } fun decrypt(_0828_rec_decr_key: ByteArray): ServerSessionKeyResponsePacket {//todo test - this.input.skip(14) + this.input.skip(7) return ServerSessionKeyResponsePacket(TEACryptor.decrypt(this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, _0828_rec_decr_key).dataInputStream()); //TeaDecrypt(取文本中间(data, 43, 取文本长度(data) - 45), m_0828_rec_decr_key) } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/touch/ServerTouchResponsePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/touch/ServerTouchResponsePacket.kt index 0ea56a161..1f4ca760e 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/touch/ServerTouchResponsePacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/server/touch/ServerTouchResponsePacket.kt @@ -6,6 +6,8 @@ import net.mamoe.mirai.network.packet.server.ServerPacket import net.mamoe.mirai.network.packet.server.readIP import net.mamoe.mirai.util.TEACryptor import net.mamoe.mirai.util.getRandomKey +import net.mamoe.mirai.util.hexToBytes +import net.mamoe.mirai.util.toHexString import java.io.DataInputStream /** @@ -17,7 +19,7 @@ import java.io.DataInputStream * @author Him188moe */ @ToString -class ServerTouchResponsePacket(private val type: Type, inputStream: DataInputStream) : ServerPacket(inputStream) { +class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inputStream) { var serverIP: String? = null; var loginTime: Int = 0 @@ -32,12 +34,12 @@ class ServerTouchResponsePacket(private val type: Type, inputStream: DataInputSt @ExperimentalUnsignedTypes override fun decode() { - when (input.readByte().toInt()) { + when (val id = input.readByte().toUByte().toInt()) { 0xFE -> { input.skip(94) serverIP = input.readIP() } - 0X00 -> { + 0x00 -> { input.skip(4) token = input.readNBytes(56) input.skip(6) @@ -48,7 +50,7 @@ class ServerTouchResponsePacket(private val type: Type, inputStream: DataInputSt } else -> { - throw IllegalStateException() + throw IllegalStateException(arrayOf(id.toUByte()).toUByteArray().toHexString()) } } } @@ -59,11 +61,16 @@ class ServerTouchResponsePacketEncrypted(private val type: ServerTouchResponsePa } + @ExperimentalUnsignedTypes fun decrypt(): ServerTouchResponsePacket { - input.skip(14) - return ServerTouchResponsePacket(type, DataInputStream(TEACryptor.decrypt(input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, when (type) { - ServerTouchResponsePacket.Type.TYPE_08_25_31_01 -> Protocol.redirectionKey.toByteArray() - ServerTouchResponsePacket.Type.TYPE_08_25_31_02 -> Protocol._0825key.toByteArray() + input.skip(7) + var bytes = input.readAllBytes(); + bytes = bytes.copyOfRange(0, bytes.size - 1); + println(bytes.toUByteArray().toHexString()) + + 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())); } } \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/util/TEACryptor.java b/mirai-core/src/main/java/net/mamoe/mirai/util/TEACryptor.java index 81909e24c..7842e142e 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/util/TEACryptor.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/util/TEACryptor.java @@ -177,7 +177,7 @@ public class TEACryptor { private byte[] decrypt(byte[] ciphertext, int offset, int len) { if (len % 8 != 0 || len < 16) { - return null; + throw new IllegalArgumentException("must len % 8 == 0 && len >= 16"); } mIV = decode(ciphertext, offset); mIndexPos = mIV[0] & 7; @@ -199,7 +199,7 @@ public class TEACryptor { if (mIndexPos == 8) { isFirstBlock = false; if (!decodeOneBlock(ciphertext, offset, len)) { - return null; + throw new RuntimeException("Unable to decode"); } } } @@ -215,14 +215,14 @@ public class TEACryptor { mPreOutPos = mOutPos - 8; isFirstBlock = false; if (!decodeOneBlock(ciphertext, offset, len)) { - return null; + throw new RuntimeException("Unable to decode"); } } } for (g = 0; g < 7; g++) { if (mIndexPos < 8) { if ((ciphertext[mPreOutPos + offset + mIndexPos] ^ mIV[mIndexPos]) != 0) { - return null; + throw new RuntimeException(); } else { ++mIndexPos; } @@ -231,7 +231,7 @@ public class TEACryptor { if (mIndexPos == 8) { mPreOutPos = mOutPos; if (!decodeOneBlock(ciphertext, offset, len)) { - return null; + throw new RuntimeException("Unable to decode"); } } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt index 8e9faf848..d8757a424 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt @@ -11,9 +11,9 @@ object MiraiLogger { this.print(o.toString()) } - infix fun log(o: Any?) = MiraiLogger.info(o) + infix fun log(o: Any?) = info(o) - infix fun println(o: Any?) = MiraiLogger.info(o) + infix fun println(o: Any?) = info(o) infix fun debug(o: Any?) { this.print(o.toString()) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfig.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfig.java index 42a509939..dd3257636 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfig.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfig.java @@ -2,6 +2,7 @@ package net.mamoe.mirai.utils.config; import net.mamoe.mirai.MiraiServer; import net.mamoe.mirai.utils.Utils; +import org.jetbrains.annotations.NotNull; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; @@ -9,27 +10,34 @@ import java.io.File; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; /** * YAML-TYPE CONFIG * Thread SAFE + * * @author NaturalHG */ -public class MiraiConfig extends MiraiConfigSection{ +public class MiraiConfig extends MiraiConfigSection { - private volatile File root; + private final File root; - public MiraiConfig(File file){ + public MiraiConfig(@NotNull String configName) { + this(new File(MiraiServer.getInstance().getParentFolder(), Objects.requireNonNull(configName))); + } + + public MiraiConfig(@NotNull File file) { super(); - if(!file.toURI().getPath().contains(MiraiServer.getInstance().getParentFolder().getPath())){ - file = new File((MiraiServer.getInstance().getParentFolder().getPath() + "/" + file).replace("//","/")); - } + Objects.requireNonNull(file); + /*if (!file.toURI().getPath().contains(MiraiServer.getInstance().getParentFolder().getPath())) { + file = new File(MiraiServer.getInstance().getParentFolder().getPath(), file.getName()); + }*/ this.root = file; - if(!file.exists()){ + if (!file.exists()) { try { - if(!file.createNewFile()){ + if (!file.createNewFile()) { return; } } catch (IOException e) { @@ -39,31 +47,27 @@ public class MiraiConfig extends MiraiConfigSection{ this.parse(); } - private MiraiConfig(){ - - } - - public synchronized void save(){ + public synchronized void save() { DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); Yaml yaml = new Yaml(dumperOptions); String content = yaml.dump(this); try { - Utils.writeFile(this.root,content); + Utils.writeFile(this.root, content); } catch (IOException e) { e.printStackTrace(); } } @SuppressWarnings("unchecked") - private void parse(){ + private void parse() { DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); Yaml yaml = new Yaml(dumperOptions); this.clear(); try { - Map content = yaml.loadAs(Utils.readFile(this.root), LinkedHashMap.class); - if(content != null) { + Map content = yaml.loadAs(Utils.readFile(this.root), LinkedHashMap.class); + if (content != null) { this.putAll(content); } } catch (IOException e) { @@ -72,5 +76,4 @@ public class MiraiConfig extends MiraiConfigSection{ } - } diff --git a/pom.xml b/pom.xml index 36b3683db..7926b018b 100644 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,13 @@ + + + org.apache.mina + mina-core + 2.1.3 + + net.java.dev.jna