Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java
This commit is contained in:
liujiahua123123 2019-08-21 11:09:37 +08:00
commit e7c49eb471
23 changed files with 549 additions and 131 deletions

5
.gitignore vendored
View File

@ -28,4 +28,7 @@ hs_err_pid*
mirai.iml
/.idea/
.idea/*
/.idea/*
/.idea/*
test/
/test

View File

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

View File

@ -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
```

View File

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

View File

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

View File

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

View File

@ -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
### S -> C
[Redirection](Redirection.md)

View File

@ -21,6 +21,23 @@
</parent>
<dependencies>
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.mina/mina-filter-ssl -->
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-filter-ssl</artifactId>
<version>1.1.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.mina/mina-filter-compression -->
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-filter-compression</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>

View File

@ -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<Object> 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();
}*/
}

View File

@ -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<T extends MiraiEvent> 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);
}
/**

View File

@ -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<bb.length;i++) {
// System.out.print((char)bb[i]);
// }
IoBuffer buffer1 = IoBuffer.wrap("11".getBytes());//返回信息给Clinet端
session.write(buffer1);
//声明这里message必须为IoBuffer类型
}
}
//连接关闭触发的事件
@Override
public void sessionClosed(IoSession session) throws Exception {
System.out.println("Session closed...");
}
//建立连接触发的事件
@Override
public void sessionCreated(IoSession session) throws Exception {
System.out.println("Session created...");
SocketAddress remoteAddress = session.getRemoteAddress();
System.out.println(remoteAddress);
}
//会话空闲
@Override
public void sessionIdle(IoSession session, IdleStatus status)
throws Exception {
System.out.println("Session idle...");
}
//打开连接触发的事件它与sessionCreated的区别在于一个连接地址A第一次请求Server会建立一个Session默认超时时间为1分钟此时若未达到超时时间这个连接地址A再一次向Server发送请求即是sessionOpened连接地址A第一次向Server发送请求或者连接超时后向Server发送请求时会同时触发sessionCreated和sessionOpened两个事件
@Override
public void sessionOpened(IoSession session) throws Exception {
System.out.println("Session Opened...");
SocketAddress remoteAddress = session.getRemoteAddress();
System.out.println(remoteAddress);
}
public void send(String host, int port) {
try {
InetAddress ia = InetAddress.getByName(host);
DatagramSocket socket = new DatagramSocket(9999);
socket.connect(ia, port);
byte[] buffer = new byte[1024];
buffer = ("22")
.getBytes();
DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
System.out.println(dp.getLength());
DatagramPacket dp1 = new DatagramPacket(new byte[22312], 22312);
socket.send(dp);
socket.receive(dp1);
byte[] bb = dp1.getData();
for (int i = 0; i < dp1.getLength(); i++) {
System.out.print((char) bb[i]);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

View File

@ -1,26 +1,24 @@
package net.mamoe.mirai.network
import io.netty.bootstrap.Bootstrap
import io.netty.channel.*
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.NioDatagramChannel
import io.netty.handler.codec.bytes.ByteArrayDecoder
import io.netty.channel.Channel
import net.mamoe.mirai.network.packet.client.ClientPacket
import net.mamoe.mirai.network.packet.client.login.*
import net.mamoe.mirai.network.packet.client.touch.ClientTouchPacket
import net.mamoe.mirai.network.packet.client.writeHex
import net.mamoe.mirai.network.packet.server.ServerPacket
import net.mamoe.mirai.network.packet.server.login.*
import net.mamoe.mirai.network.packet.server.security.ServerSessionKeyResponsePacket
import net.mamoe.mirai.network.packet.server.security.ServerSessionKeyResponsePacketEncrypted
import net.mamoe.mirai.network.packet.server.touch.ServerTouchResponsePacket
import net.mamoe.mirai.network.packet.server.touch.ServerTouchResponsePacketEncrypted
import net.mamoe.mirai.util.getRandomKey
import net.mamoe.mirai.util.toHexString
import net.mamoe.mirai.utils.MiraiLogger
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetSocketAddress
/**
* [number] is a QQ number.
* A robot account.
*
* @author Him188moe
*/
@ -29,13 +27,13 @@ class Robot(val number: Int, private val password: String) {
private var channel: Channel? = null
private var serverIP: String = ""
var serverIP: String = ""
set(value) {
serverAddress = InetSocketAddress(value, 8000)
field = value
}
private lateinit var serverAddress: InetSocketAddress;
private lateinit var serverAddress: InetSocketAddress
private lateinit var token00BA: ByteArray
private lateinit var token0825: ByteArray
@ -64,7 +62,8 @@ class Robot(val number: Int, private val password: String) {
when (packet) {
is ServerTouchResponsePacket -> {
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("HelloUDPServer!");
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<NioDatagramChannel>() {
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<ByteArray>() {
override fun encode(ctx: ChannelHandlerContext?, msg: ByteArray?, out: MutableList<Any>?) {
out!!.add(DatagramPacket(msg!!))
}
})*/
ch.pipeline().addLast(ByteArrayDecoder())
ch.pipeline().addLast(ByteArrayEncoder())
ch.pipeline().addLast(object : SimpleChannelInboundHandler<ByteArray>() {
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<DatagramPacket>() {
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()
}
}
}*/
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Object>{
public class MiraiConfig extends MiraiConfigSection<Object> {
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<Object>{
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<String,Object> content = yaml.loadAs(Utils.readFile(this.root), LinkedHashMap.class);
if(content != null) {
Map<String, Object> 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<Object>{
}
}

View File

@ -94,6 +94,13 @@
<dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.mina/mina-core -->
<dependency>
<groupId>org.apache.mina</groupId>
<artifactId>mina-core</artifactId>
<version>2.1.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
<dependency>
<groupId>net.java.dev.jna</groupId>