Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
91aed96c
Commit
91aed96c
authored
Sep 19, 2019
by
liujiahua123123
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/master'
parents
c8d92c4b
dd849bbb
Changes
39
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
39 changed files
with
725 additions
and
585 deletions
+725
-585
README.md
README.md
+24
-18
document/protocol/Get_tlv_0006.md
document/protocol/Get_tlv_0006.md
+0
-33
document/protocol/Login Flow.md
document/protocol/Login Flow.md
+0
-96
document/protocol/Password Verified.md
document/protocol/Password Verified.md
+0
-23
document/protocol/Redirection.md
document/protocol/Redirection.md
+0
-124
document/protocol/Touch.md
document/protocol/Touch.md
+0
-41
mirai-core/pom.xml
mirai-core/pom.xml
+9
-0
mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java
mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java
+1
-15
mirai-core/src/main/java/net/mamoe/mirai/event/events/bot/BotEvent.java
.../main/java/net/mamoe/mirai/event/events/bot/BotEvent.java
+1
-1
mirai-core/src/main/java/net/mamoe/mirai/event/events/group/GroupMessageEvent.java
...net/mamoe/mirai/event/events/group/GroupMessageEvent.java
+10
-10
mirai-core/src/main/java/net/mamoe/mirai/event/events/qq/FriendEvent.java
...ain/java/net/mamoe/mirai/event/events/qq/FriendEvent.java
+5
-5
mirai-core/src/main/java/net/mamoe/mirai/event/events/qq/FriendMessageEvent.java
...a/net/mamoe/mirai/event/events/qq/FriendMessageEvent.java
+4
-4
mirai-core/src/main/java/net/mamoe/mirai/message/FaceID.java
mirai-core/src/main/java/net/mamoe/mirai/message/FaceID.java
+1
-1
mirai-core/src/main/java/net/mamoe/mirai/message/Message.kt
mirai-core/src/main/java/net/mamoe/mirai/message/Message.kt
+1
-1
mirai-core/src/main/java/net/mamoe/mirai/message/MessageId.kt
...i-core/src/main/java/net/mamoe/mirai/message/MessageId.kt
+3
-3
mirai-core/src/main/java/net/mamoe/mirai/message/defaults/Face.kt
...re/src/main/java/net/mamoe/mirai/message/defaults/Face.kt
+15
-0
mirai-core/src/main/java/net/mamoe/mirai/message/defaults/Image.kt
...e/src/main/java/net/mamoe/mirai/message/defaults/Image.kt
+47
-9
mirai-core/src/main/java/net/mamoe/mirai/message/defaults/PlainText.kt
...c/main/java/net/mamoe/mirai/message/defaults/PlainText.kt
+9
-0
mirai-core/src/main/java/net/mamoe/mirai/message/defaults/UnsolvedImage.kt
...in/java/net/mamoe/mirai/message/defaults/UnsolvedImage.kt
+9
-9
mirai-core/src/main/java/net/mamoe/mirai/network/BotNetworkHandlerImpl.kt
...ain/java/net/mamoe/mirai/network/BotNetworkHandlerImpl.kt
+11
-10
mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt
mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt
+43
-8
mirai-core/src/main/java/net/mamoe/mirai/network/handler/ActionPacketHandler.kt
...va/net/mamoe/mirai/network/handler/ActionPacketHandler.kt
+6
-6
mirai-core/src/main/java/net/mamoe/mirai/network/handler/MessagePacketHandler.kt
...a/net/mamoe/mirai/network/handler/MessagePacketHandler.kt
+36
-18
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt
.../main/java/net/mamoe/mirai/network/packet/ClientPacket.kt
+12
-6
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt
...c/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt
+110
-53
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt
.../main/java/net/mamoe/mirai/network/packet/ServerPacket.kt
+51
-10
mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt
...ore/src/main/java/net/mamoe/mirai/network/packet/Touch.kt
+4
-5
mirai-core/src/main/java/net/mamoe/mirai/network/packet/UnknownServerPacket.kt
...ava/net/mamoe/mirai/network/packet/UnknownServerPacket.kt
+6
-0
mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt
...n/java/net/mamoe/mirai/network/packet/VerificationCode.kt
+3
-3
mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendFriendMessagePacket.kt
...ai/network/packet/action/ClientSendFriendMessagePacket.kt
+1
-1
mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendGroupMessagePacket.kt
...rai/network/packet/action/ClientSendGroupMessagePacket.kt
+13
-11
mirai-core/src/main/java/net/mamoe/mirai/network/packet/image/UploadGroupImage.kt
.../net/mamoe/mirai/network/packet/image/UploadGroupImage.kt
+11
-15
mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt
.../java/net/mamoe/mirai/network/packet/login/ClientLogin.kt
+25
-24
mirai-core/src/main/java/net/mamoe/mirai/utils/ImageNetworkUtils.java
...rc/main/java/net/mamoe/mirai/utils/ImageNetworkUtils.java
+6
-6
mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt
mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt
+2
-2
mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt
mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt
+22
-0
mirai-core/src/main/java/net/mamoe/mirai/utils/setting/MiraiSettings.java
...ain/java/net/mamoe/mirai/utils/setting/MiraiSettings.java
+15
-14
mirai-core/src/test/java/HexComparator.java
mirai-core/src/test/java/HexComparator.java
+3
-0
mirai-core/src/test/java/PacketDebuger.kt
mirai-core/src/test/java/PacketDebuger.kt
+206
-0
No files found.
README.md
View file @
91aed96c
# Mirai
一个以
<b>
TIM QQ协议(非web)
</b>
驱动的JAVA(+Kotlin) QQ机器人服务端核心
我们坚持免费与开源
采用服务端-插件模式运行,同时提供独立的协议层库
**我们承诺项目的所有模块均开源**
项目处于快速开发阶段
项目处于开发阶段,学生无法每日大量更新。
项目还有很多未完善的地方, 欢迎任何的代码贡献, 或是 issue.
部分协议来自网络上开源项目
一切开发旨在学习
, 请勿用于非法用途
一切开发旨在学习
,请勿用于非法用途
<br>
## 抢先体验
核心框架结构已经开发完毕,一些核心功能也测试完成。
仅需几分钟就可以测试 Mirai.
现在你可以登录小号来测试 Mirai.
即使测试消息时未发现冻结情况,我们也无法100%保证账号冻结不会发生。
A JAVA(+Kotlin) powered open-source project under GPL license
<br>
It use protocols from
<i>
TIM QQ
</i>
, that is, it won't be affected by the close of
<i>
Smart QQ
</i><br>
The project is all for
<b>
learning proposes
</b>
and still in
<b>
developing stage
</b><br>
## 抢先体验
现在你可以使用 Mirai 内置的一些测试qq号体验 Mirai, 但我们现在还不建议你使用自己的 qq 号登录
1.
Clone
2.
Import as Maven project
3.
Run
[
MiraiMain
](
mirai-core/src/main/java/net/mamoe/mirai/MiraiMain.java#L7
)
...
...
@@ -44,13 +43,11 @@ FriendMessageEvent::class.hookAlways{


不过我们还正在努力做发送图片
发送图片已经完成,但我们还在开发上传图片至服务器。
现在你可以通过发送一张图片给机器人账号,再让机器人账号发送这张图片。你可以查看
[
Image
](
src/main/java/net/mamoe/mirai/message/Image.kt
)
## 代码结构
Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持).
与插件相关性强(或其他在二次开发中容易接触)的部分尽量使用 Java 完成,
若使用 Kotlin, 我们会通过 Java interface 实现或 javadoc 帮助未接触过 Kotlin 的开发者.
即使你完全不了解 Kotlin, 你也可以正常开发.
## 语言使用说明
我们使用 Kotlin,但我们也会保留对 Java 和 Java开发者的支持。
# TODO
-
[x] 事件(Event)模块
...
...
@@ -64,10 +61,11 @@ Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持).
-
[ ] Network - Events
-
[ ] Bot - Friend/group list
-
[ ] Bot - Actions(joining group, adding friend, etc.)
-
[
] Message Section
**(Working on)**
-
[
x] Message Section
-
[ ] Image uploading
**(Working on)**
-
[ ] Contact
-
[ ] UI
-
[ ] Console
<br>
...
...
@@ -81,6 +79,14 @@ Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持).
...
```
# Mirai
<br>
A JAVA(+Kotlin) powered open-source project under GPL license
<br>
It use protocols from
<i>
TIM QQ
</i>
, that is, it won't be affected by the close of
<i>
Smart QQ
</i><br>
The project is all for
<b>
learning proposes
</b>
and still in
<b>
developing stage
</b><br>
# Usage
## Requirements
-
Java 11 or higher
...
...
document/protocol/Get_tlv_0006.md
deleted
100644 → 0
View file @
c8d92c4b
# TIM Protocol
## Get_tlv_0006
C 构建包, 近 C 使用
#### Var
type | var name | value/from
---- | ---|---
?bytes | MD51 | md5(raw password)
?bytes | MD52 | md5((MD51 + “ 00 00 00 00 ” + g_QQ).hextobytes())
4bytes |m_loginIP | 服务器提供(Dispose_0825)
16bytes| m_tgtgtKey| |
#### Packet data
type | value
---- | ---
int | random
hex |00 02
int |qq
hex |#_0825data2
hex |00 00 01
bytes|MD51
int |m_loginTime
byte | 0
bytes | 12 zero
int|m_loginIP
bytes | 8 zero
hex | 00 10
hex | 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B
bytes | m_tgtgtKey
TEA加密以上, key=MD52
\ No newline at end of file
document/protocol/Login Flow.md
deleted
100644 → 0
View file @
c8d92c4b
# TIM Protocol
## Login Flow
### 服务器确认 - 通过 touch 包
**[Touch](Touch.md)**
C: 发送登录
`08 25 31 01`
Sample:
```
text
02 37 13 08 25 31 01 76 E4 B8 DD 03 00 00 00 01 2E 01 00 00 68 52 00 00 00 00 A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D 9F A7 43 90 2A 9D 29 B5 EA DB 50 7F D3 78 1C AE 31 7E 7E 4F A9 1B B5 C9 8D A8 4C 78 98 13 E1 45 FC 35 2E 22 3D E0 39 1A 3F C6 8B CA 06 A8 F3 B3 6F 95 D8 64 1A B0 E9 29 06 DB 5C F4 9B 32 47 5A B7 10 57 C5 2F C9 D9 7B 17 22 7F 09 A6 8C 30 04 24 0F 1D 61 A1 42 E2 7A AA 15 36 AC 67 9B 7A 4D 42 14 AD F5 2D D2 A3 CA 03
```
S: 回复
`08 25 31 01`
(ID与C发送的相同), 告诉C是否需 redirect
**[Redirection](Redirection.md)**
如果需要 redirect
C: 发送 redirect 包
`08 25 31 02`
到新的服务器
Sample pk:
g_server = 125.39.132.167
```
text
02 37 13 08 25 31 02 76 E4 B8 DD 03 00 00 00 01 2E 01 00 00 68 52 00 00 00 00 A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B 23 89 DB A3 07 80 49 63 01 76 69 F1 E1 11 32 06 E9 7F E4 6A 6B 98 07 75 EF 0E 1F 81 10 85 86 EB 96 8E 65 78 0F C3 BC F8 FF 51 3E 36 4F 48 3C 78 52 26 3F 4C 20 65 85 69 AC B8 36 B6 50 50 CC 01 4A 35 44 15 5C 80 B9 F7 A7 56 D4 B2 D4 A4 D9 09 56 29 93 39 0C C8 9C 0B F7 2D CE BE B0 D5 4C CE 48 B3 2D 18 28 A2 3C DD 26 C1 F1 6E A1 4B EC 8A 03
```
如果不需要 redirect
C: 发送
`08 36 31 03`
到原服务器, 提交密码
m_loginTime = 5D 59 7D A6
m_loginIP = B7 5F F8 D4
m_0825token = 16 5A 4A C4 FE D1 F8 A3 CB B7 37 DD A5 AE 5C F7 04 74 36 91 4E CD 4A E6 EF 43 31 A7 D1 97 CC 6B 93 C7 9B 15 62 FD 11 3E 19 E1 69 62 B3 BC F4 9A E1 17 19 47 CC A3 1E AC
m_tgtgtKey(由C生成? RANDOM) = DB DE AE DD C7 ED 35 B6 DD 2B 71 6B C4 14 C6 6B
在这个包中会
[
getTLV0006
](
Get_tlv_0006.md
)
,
Sample
```
text
02 37 13 08 36 31 03 76 E4 B8 DD 03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03 00 19 02 6D 28 41 D2 A5 6F D2 FC 3E 2A 1F 03 75 DE 6E 28 8F A8 19 3E 5F 16 49 D3 00 00 00 10 EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA 12 58 AF 79 C5 60 54 5F A9 30 38 87 E9 B0 68 FA D3 83 A7 6A EA B6 7F 54 10 78 F0 47 60 24 1B E2 91 2D FD 60 F4 C7 DE 3C 7C 56 83 BE B4 66 49 60 5F E0 D3 2B 18 BD 5D 64 28 D1 98 8F 83 84 98 03 97 DE 97 83 5A BD 0B AC 1B 63 7C A2 C8 13 C2 26 8A C1 AA 6D B8 5D 4A 91 E7 C8 7B AF 3C 89 76 DF EA F3 F3 53 AD 69 2F 4C 45 90 69 B7 69 3E 05 C9 DE 1B B1 C9 DE D3 F6 4B 70 3D 27 54 BC D6 2B AB 68 13 2D E7 E3 11 FF 98 3F 1E 51 BC D6 F5 AB 26 DA 53 82 7B 3C 23 99 D8 77 95 32 64 C9 11 C5 8D 40 EA F6 E7 84 C6 B0 94 EE 4A 7E 22 1E 30 34 59 AB D1 66 79 EA A5 D4 AD A2 7D 4D 47 B8 FC 86 BC DD 5D 27 15 94 E0 1B 68 00 DD 5E 5A 09 08 E0 F5 91 EF 98 95 CC 92 B9 A0 EB AC 62 B5 5D DD AA EC 4F 36 48 6E C9 7C 2D 1F 21 98 F5 27 28 E5 8E 4A 51 BC 9A 2A BE 50 31 21 EC DF C8 97 35 58 76 B3 CD F9 92 7A 86 0E C4 1D 90 62 86 99 20 92 6C 12 C9 E2 E9 7F 0B 6B AC 59 00 55 7E B6 45 B1 C4 01 37 A6 1D B3 6E 16 06 96 40 59 CD 59 5D 6F 96 E9 B4 97 0D 55 AE 3B BF FA 54 73 D3 06 B3 47 AA 7E A1 89 F5 04 79 62 7C 11 B4 1C 4D F7 24 92 71 42 17 DC 52 67 9C 66 97 5F 64 1D CD 35 68 7D D5 D7 51 9B BA 29 92 E7 8B 6F B4 74 9E 84 54 5F E8 0D 81 89 15 FB 30 A0 1B AD B2 A3 46 3F F1 A7 A7 A1 A2 A6 D1 7D B0 4E D4 E9 87 AA 20 ED 9A 04 22 5F 57 45 20 05 2B 48 CD 06 4B BC 6F F2 92 D5 09 07 DF 83 DA FC 9D 75 50 C3 75 98 56 8C B3 B0 02 80 FD ED 61 03 00 86 EA E1 03 D2 08 68 B4 1F B9 9C EB 7B 75 9C 2D 94 10 F1 C0 40 E8 D9 9A DB 4A 0F 42 90 78 F6 AB 5B 7D 5A 18 ED 3F 45 8E 1F 98 D0 97 79 51 1D 2D 64 23 8D 30 93 FF C1 B2 05 1D 22 0C E6 51 CD F3 D5 F6 D9 DB 31 EC B2 2F B1 D1 ED F3 54 5F B3 F9 B9 74 0B 10 21 4D 84 52 CD 61 A2 39 51 CD 38 AF 2B DD BD CC 70 76 31 76 51 49 B7 03
```
**Password Verified**
S: 发送
`08 36 31 03`
告知登录结果.
若成功, C将会得到:
-
m_0828_rec_decr_key
-
g_clientKey
-
token38
-
token88
-
encryptionKey
若不成功, 理由:
-
需要验证码:
//todo
-
密码错误
-
未知(重新登录)
-
冻结
-
账号不存在
-
设备锁
-
被回收
C: 回复
`08 28 04 34`
, 请求建立 Session
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
```
**Session**
S: 发送
`08 28 04 34`
, 完成 session 建立, 告知:
-
g_sessionKey
-
g_tlv0105
-
g_loginStatus
**Handshake 1**
C: 发送
`00 EC 6E 8E`
```
text
02 37 13 00 EC 6E 8E 76 E4 B8 DD 02 00 00 00 01 01 01 00 00 68 20 C4 28 24 D6 67 13 CE 5F F7 F8 38 79 F4 56 1F CA 13 95 22 4D 7B 5D B6 59 03
```
S: 回复
`00 EC 6E 8E`
**Handshake 2**
C: 发送
`00 1D C5 CB`
```
text
02 37 13 00 1D C5 CB 76 E4 B8 DD 02 00 00 00 01 01 01 00 00 68 20 F3 B2 B9 BF F9 C9 87 EB C2 33 FD BA 6B 16 44 E8 B2 C1 8C 7E 4F 97 01 13 88 D8 00 BF 5F 6C 38 22 E0 50 4F 9B 73 7F 5F 31 64 72 9A C1 11 79 F5 B9 33 C0 EC 81 5E F7 D5 A4 BF C6 29 9F 18 9E C0 99 CE B7 16 E5 E8 BF EE E7 5A C3 5C 28 68 3E 48 18 03
```
S: 回复
`00 1D C5 CB`
**Handshake 3**
C: 发送
`00 5C 7B 2E`
```
text
02 37 13 00 5C 7B 2E 76 E4 B8 DD 02 00 00 00 01 01 01 00 00 68 20 E7 E2 64 22 9C 2F 33 27 A3 8B 4D 9C DE C5 A8 0D 03
```
S: 回复
`00 5C 7B 2E`
\ No newline at end of file
document/protocol/Password Verified.md
deleted
100644 → 0
View file @
c8d92c4b
# 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
document/protocol/Redirection.md
deleted
100644 → 0
View file @
c8d92c4b
# 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
document/protocol/Touch.md
deleted
100644 → 0
View file @
c8d92c4b
# TIM Protocol
## Touch
### C -> S
#### Var
type | var name | value/from
---- | ---|---
int |g_qq | qq number
int| g_server| server ip
#### Packet data
type | value
---- | ---
hex | #head
hex | #ver
int | 08 25 31 01
int | g_qq
hex |#fixVer
hex |#_0825key
?bytes |TEA加密1
hex |#tail
TEA加密1, key = #_0825key:
type | value
---- | ---
hex | #_0825data0
hex | #_0825data2
int | g_qq
hex | 00 00 00 00 03 09 00 08 00 01
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
### S -> C
[
Redirection
](
Redirection.md
)
\ No newline at end of file
mirai-core/pom.xml
View file @
91aed96c
...
...
@@ -17,6 +17,15 @@
</parent>
<dependencies>
<!-- https://mvnrepository.com/artifact/jpcap/jpcap -->
<dependency>
<groupId>
jpcap
</groupId>
<artifactId>
jpcap
</artifactId>
<version>
0.1.18-002
</version>
<scope>
system
</scope>
<systemPath>
${project.basedir}/lib/jpcap.jar
</systemPath>
</dependency>
<dependency>
<groupId>
org.pcap4j
</groupId>
<artifactId>
pcap4j-core
</artifactId>
...
...
mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java
View file @
91aed96c
...
...
@@ -198,21 +198,7 @@ public final class MiraiServer {
String
qqList
=
"2573990098----qq123456789\n"
+
"3303923865----q123456789\n"
+
"3349933294----q123456789\n"
+
"3303708824----q123456789\n"
+
"3227036647----q123456789\n"
+
"3451394431----q123456789\n"
+
"3533243484----q123456789\n"
+
"3364512686----q123456789\n"
+
"3137567463----q123456789\n"
+
"3414786399----q123456789\n"
+
"3347405939----q123456789\n"
+
"3544089622----q123456789\n"
+
"3108512993----q123456789\n"
+
"2985563549----q123456789\n"
+
"3463531892----q123456789\n"
;
"3034551466----zxcvbnm\n"
;
private
Bot
getAvailableBot
()
throws
ExecutionException
,
InterruptedException
{
for
(
String
it
:
qqList
.
split
(
"\n"
))
{
...
...
mirai-core/src/main/java/net/mamoe/mirai/event/events/bot/BotEvent.java
View file @
91aed96c
...
...
@@ -10,7 +10,7 @@ import java.util.Objects;
* @author Him188moe
*/
public
abstract
class
BotEvent
extends
MiraiEvent
{
p
rivate
final
Bot
bot
;
p
ublic
final
Bot
bot
;
public
BotEvent
(
@NotNull
Bot
bot
)
{
this
.
bot
=
Objects
.
requireNonNull
(
bot
);
...
...
mirai-core/src/main/java/net/mamoe/mirai/event/events/group/GroupMessageEvent.java
View file @
91aed96c
...
...
@@ -10,25 +10,25 @@ import org.jetbrains.annotations.NotNull;
* @author Him188moe
*/
public
final
class
GroupMessageEvent
extends
GroupEvent
{
p
rivate
final
QQ
sender
;
p
rivate
final
MessageChain
messageC
hain
;
p
rivate
final
String
messageString
;
p
ublic
final
QQ
sender
;
p
ublic
final
MessageChain
c
hain
;
p
ublic
final
String
message
;
public
GroupMessageEvent
(
@NotNull
Bot
bot
,
@NotNull
Group
group
,
@NotNull
QQ
sender
,
@NotNull
MessageChain
messageC
hain
)
{
public
GroupMessageEvent
(
@NotNull
Bot
bot
,
@NotNull
Group
group
,
@NotNull
QQ
sender
,
@NotNull
MessageChain
c
hain
)
{
super
(
bot
,
group
);
this
.
sender
=
sender
;
this
.
messageChain
=
messageC
hain
;
this
.
message
String
=
messageC
hain
.
toString
();
this
.
chain
=
c
hain
;
this
.
message
=
c
hain
.
toString
();
}
@NotNull
public
MessageChain
get
Message
Chain
()
{
return
messageC
hain
;
public
MessageChain
getChain
()
{
return
c
hain
;
}
@NotNull
public
String
getMessage
String
()
{
return
message
String
;
public
String
getMessage
()
{
return
message
;
}
@NotNull
...
...
mirai-core/src/main/java/net/mamoe/mirai/event/events/qq/FriendEvent.java
View file @
91aed96c
...
...
@@ -11,15 +11,15 @@ import java.util.Objects;
* @author Him188moe
*/
public
abstract
class
FriendEvent
extends
BotEvent
{
p
rivate
final
QQ
qq
;
p
ublic
final
QQ
sender
;
public
FriendEvent
(
@NotNull
Bot
bot
,
@NotNull
QQ
qq
)
{
public
FriendEvent
(
@NotNull
Bot
bot
,
@NotNull
QQ
sender
)
{
super
(
bot
);
this
.
qq
=
Objects
.
requireNonNull
(
qq
);
this
.
sender
=
Objects
.
requireNonNull
(
sender
);
}
@NotNull
public
QQ
get
QQ
()
{
return
qq
;
public
QQ
get
Sender
()
{
return
sender
;
}
}
mirai-core/src/main/java/net/mamoe/mirai/event/events/qq/FriendMessageEvent.java
View file @
91aed96c
...
...
@@ -11,15 +11,15 @@ import java.util.Objects;
* @author Him188moe
*/
public
final
class
FriendMessageEvent
extends
FriendEvent
{
p
rivate
final
MessageChain
messageChain
;
p
ublic
final
MessageChain
message
;
public
FriendMessageEvent
(
@NotNull
Bot
bot
,
@NotNull
QQ
sender
,
@NotNull
MessageChain
message
Chain
)
{
public
FriendMessageEvent
(
@NotNull
Bot
bot
,
@NotNull
QQ
sender
,
@NotNull
MessageChain
message
)
{
super
(
bot
,
sender
);
this
.
message
Chain
=
Objects
.
requireNonNull
(
messageChain
);
this
.
message
=
Objects
.
requireNonNull
(
message
);
}
@NotNull
public
MessageChain
message
()
{
return
message
Chain
;
return
message
;
}
}
mirai-core/src/main/java/net/mamoe/mirai/message/FaceID.java
View file @
91aed96c
...
...
@@ -166,7 +166,7 @@ public enum FaceID {
return
value
;
}
}
return
null
;
return
FaceID
.
unknown
;
}
...
...
mirai-core/src/main/java/net/mamoe/mirai/message/Message.kt
View file @
91aed96c
...
...
@@ -74,7 +74,7 @@ abstract class Message {
/**
* 比较两个 Message 的内容是否相等. 如:
* - [PlainText] 比较 [PlainText.text]
* - [Image] 比较 [Image.imageI
D
]
* - [Image] 比较 [Image.imageI
d
]
*/
abstract
infix
fun
valueEquals
(
another
:
Message
):
Boolean
...
...
mirai-core/src/main/java/net/mamoe/mirai/message/MessageId.kt
View file @
91aed96c
...
...
@@ -10,13 +10,13 @@ package net.mamoe.mirai.message
*/
object
MessageId
{
const
val
AT
:
Int
=
0
x0
0
//todo 不知道是多少
const
val
AT
:
Int
=
0
x0
6
const
val
FACE
:
Int
=
0
x0
0
//todo 不知道是多少
const
val
FACE
:
Int
=
0
x0
2
const
val
TEXT
:
Int
=
0
x01
const
val
IMAGE
:
Int
=
0
x0
6
const
val
IMAGE
:
Int
=
0
x0
3
const
val
CHAIN
:
Int
=
0
xff
//仅用于 equals. Packet 中不存在 Chain 概念
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/message/defaults/Face.kt
View file @
91aed96c
...
...
@@ -3,8 +3,10 @@ package net.mamoe.mirai.message.defaults
import
net.mamoe.mirai.message.FaceID
import
net.mamoe.mirai.message.Message
import
net.mamoe.mirai.message.MessageId
import
net.mamoe.mirai.network.packet.readLVNumber
import
net.mamoe.mirai.network.packet.writeHex
import
net.mamoe.mirai.network.packet.writeLVByteArray
import
net.mamoe.mirai.utils.lazyDecode
import
net.mamoe.mirai.utils.lazyEncode
/**
...
...
@@ -41,5 +43,18 @@ class Face(val id: FaceID) : Message() {
}
return
this
.
id
==
another
.
id
}
companion
object
{
fun
ofByteArray
(
data
:
ByteArray
):
Face
=
lazyDecode
(
data
)
{
//00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0
//00 01 0C 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 4D
it
.
skip
(
1
)
val
id1
=
FaceID
.
ofId
(
it
.
readLVNumber
().
toInt
())
//可能这个是id, 也可能下面那个
it
.
skip
(
it
.
readByte
().
toLong
())
it
.
readLVNumber
()
//某id?
return
@
lazyDecode
Face
(
id1
)
}
}
}
mirai-core/src/main/java/net/mamoe/mirai/message/defaults/Image.kt
View file @
91aed96c
...
...
@@ -2,30 +2,33 @@ package net.mamoe.mirai.message.defaults
import
net.mamoe.mirai.message.Message
import
net.mamoe.mirai.message.MessageId
import
net.mamoe.mirai.network.packet.writeHex
import
net.mamoe.mirai.network.packet.writeLVByteArray
import
net.mamoe.mirai.network.packet.writeLVString
import
net.mamoe.mirai.network.packet.*
import
net.mamoe.mirai.utils.lazyDecode
import
net.mamoe.mirai.utils.lazyEncode
import
net.mamoe.mirai.utils.skip
import
net.mamoe.mirai.utils.toUHexString
/**
* 图片消息.
* 由接收消息时构建, 可直接发送
*
* @param imageId 类似 `{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg`. 群的是大写id, 好友的是小写id
*
* @author Him188moe
*/
open
class
Image
internal
constructor
(
val
imageI
D
:
String
)
:
Message
()
{
open
class
Image
internal
constructor
(
val
imageI
d
:
String
)
:
Message
()
{
override
val
type
:
Int
=
MessageId
.
IMAGE
override
fun
toStringImpl
():
String
{
return
imageI
D
return
imageI
d
}
override
fun
toByteArray
():
ByteArray
=
lazyEncode
{
section
->
section
.
writeByte
(
0
x03
)
//todo 可能是 0x03?
section
.
writeByte
(
MessageId
.
IMAGE
)
section
.
writeLVByteArray
(
lazyEncode
{
child
->
child
.
writeByte
(
0
x02
)
child
.
writeLVString
(
this
.
imageI
D
)
child
.
writeLVString
(
this
.
imageI
d
)
child
.
writeHex
(
"04 00 "
+
"04 9B 53 B0 08 "
+
"05 00 "
+
...
...
@@ -35,16 +38,51 @@ open class Image internal constructor(val imageID: String) : Message() {
"07 00 "
+
"01 43 08 00 00 09 00 01 01 0B 00 00 14 00 04 11 00 00 00 15 00 04 00 00 02 BC 16 00 04 00 00 02 BC 18 00 04 00 00 7D 5E FF 00 5C 15 36 20 39 32 6B 41 31 43 39 62 35 33 62 30 30 38 64 39 38 61 35 61 37 30 20"
)
child
.
writeHex
(
"20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20"
)
child
.
writeBytes
(
this
.
imageI
D
)
child
.
writeBytes
(
this
.
imageI
d
)
child
.
writeByte
(
0
x41
)
})
}
override
fun
valueEquals
(
another
:
Message
):
Boolean
{
if
(
another
is
Image
)
{
return
this
.
imageI
D
==
another
.
imageID
return
this
.
imageI
d
==
another
.
imageId
}
return
false
}
companion
object
{
@JvmStatic
fun
ofByteArray0x06
(
data
:
ByteArray
):
Image
=
lazyDecode
(
data
)
{
it
.
skip
(
1
)
println
(
"好友的图片"
)
println
(
data
.
toUHexString
())
val
filenameLength
=
it
.
readShort
()
val
suffix
=
it
.
readString
(
filenameLength
).
substringAfter
(
"."
)
it
.
skip
(
data
.
size
-
37
-
1
-
filenameLength
-
2
)
val
imageId
=
String
(
it
.
readNBytes
(
36
))
println
(
imageId
)
it
.
skip
(
1
)
//0x41
return
@
lazyDecode
Image
(
"{$imageId}.$suffix"
)
}
@JvmStatic
fun
ofByteArray0x03
(
data
:
ByteArray
):
Image
=
lazyDecode
(
data
)
{
it
.
skip
(
1
)
return
@
lazyDecode
Image
(
String
(
it
.
readLVByteArray
()))
/*
println(String(it.readLVByteArray()))
it.readTLVMap()
return@lazyDecode Image(String(it.readLVByteArray().cutTail(5).getRight(42)))
/
it.skip(data.size - 47)
val imageId = String(it.readNBytes(42))
it.skip(1)//0x41
it.skip(1)//0x42
it.skip(1)//0x43
it.skip(1)//0x41
return@lazyDecode Image(imageId)*/
}
}
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/message/defaults/PlainText.kt
View file @
91aed96c
...
...
@@ -2,8 +2,10 @@ package net.mamoe.mirai.message.defaults
import
net.mamoe.mirai.message.Message
import
net.mamoe.mirai.message.MessageId
import
net.mamoe.mirai.network.packet.readLVString
import
net.mamoe.mirai.network.packet.writeLVByteArray
import
net.mamoe.mirai.network.packet.writeLVString
import
net.mamoe.mirai.utils.lazyDecode
import
net.mamoe.mirai.utils.lazyEncode
/**
...
...
@@ -31,4 +33,11 @@ class PlainText(private val text: String) : Message() {
}
return
this
.
text
==
another
.
text
}
companion
object
{
fun
ofByteArray
(
data
:
ByteArray
):
PlainText
=
lazyDecode
(
data
)
{
it
.
skip
(
1
)
PlainText
(
it
.
readLVString
())
}
}
}
mirai-core/src/main/java/net/mamoe/mirai/message/defaults/UnsolvedImage.kt
View file @
91aed96c
...
...
@@ -3,9 +3,9 @@ package net.mamoe.mirai.message.defaults
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.network.LoginSession
import
net.mamoe.mirai.network.packet.image.ClientTryGetImageIDPacket
import
net.mamoe.mirai.network.packet.image.ServerTry
UploadGroupImage
FailedPacket
import
net.mamoe.mirai.network.packet.image.ServerTry
UploadGroupImage
ResponsePacket
import
net.mamoe.mirai.network.packet.image.ServerTry
UploadGroupImage
SuccessPacket
import
net.mamoe.mirai.network.packet.image.ServerTry
GetImageID
FailedPacket
import
net.mamoe.mirai.network.packet.image.ServerTry
GetImageID
ResponsePacket
import
net.mamoe.mirai.network.packet.image.ServerTry
GetImageID
SuccessPacket
import
net.mamoe.mirai.network.packet.md5
import
net.mamoe.mirai.utils.ImageNetworkUtils
import
net.mamoe.mirai.utils.toByteArray
...
...
@@ -27,16 +27,16 @@ class UnsolvedImage(filename: String, val image: BufferedImage) : Image(getImage
constructor
(
url
:
URL
)
:
this
(
File
(
url
.
file
))
fun
upload
(
session
:
LoginSession
,
contact
:
Contact
):
CompletableFuture
<
Unit
>
{
return
session
.
expectPacket
<
ServerTry
UploadGroupImage
ResponsePacket
>
{
toSend
{
ClientTryGetImageIDPacket
(
session
.
bot
.
account
.
qqNumber
,
session
.
sessionKey
,
session
.
bot
.
account
.
qqN
umber
,
image
)
}
return
session
.
expectPacket
<
ServerTry
GetImageID
ResponsePacket
>
{
toSend
{
ClientTryGetImageIDPacket
(
session
.
bot
.
account
.
qqNumber
,
session
.
sessionKey
,
contact
.
n
umber
,
image
)
}
expect
{
when
(
it
)
{
is
ServerTry
UploadGroupImage
FailedPacket
->
{
is
ServerTry
GetImageID
FailedPacket
->
{
//已经存在于服务器了
}
is
ServerTry
UploadGroupImage
SuccessPacket
->
{
is
ServerTry
GetImageID
SuccessPacket
->
{
val
data
=
image
.
toByteArray
()
if
(!
ImageNetworkUtils
.
postImage
(
it
.
uKey
.
toUHexString
(),
data
.
size
,
session
.
bot
.
account
.
qqNumber
,
contact
.
number
,
data
))
{
throw
RuntimeException
(
"cannot upload image"
)
...
...
@@ -56,8 +56,8 @@ class UnsolvedImage(filename: String, val image: BufferedImage) : Image(getImage
return
"{"
+
md5
.
copyOfRange
(
0
,
4
).
toUHexString
(
""
)
+
"-"
.
plus
(
md5
.
copyOfRange
(
4
,
6
).
toUHexString
(
""
))
+
"-"
.
plus
(
md5
.
copyOfRange
(
6
,
8
).
toUHexString
(
""
))
+
"-"
.
plus
(
md5
.
copyOfRange
(
8
,
1
2
).
toUHexString
(
""
))
+
"-"
.
plus
(
md5
.
copyOfRange
(
1
2
,
16
).
toUHexString
(
""
))
+
"}."
+
if
(
filename
.
endsWith
(
".jpeg"
))
"jpg"
else
filename
.
substringAfter
(
"."
,
"jpg"
)
.
plus
(
md5
.
copyOfRange
(
8
,
1
0
).
toUHexString
(
""
))
+
"-"
.
plus
(
md5
.
copyOfRange
(
1
0
,
16
).
toUHexString
(
""
))
+
"}."
+
if
(
filename
.
endsWith
(
".jpeg"
))
"jpg"
else
filename
.
substringAfter
(
"."
,
"jpg"
)
}
}
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/network/BotNetworkHandlerImpl.kt
View file @
91aed96c
...
...
@@ -239,10 +239,10 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
*/
inner
class
Login
:
Closeable
{
private
lateinit
var
token00BA
:
ByteArray
private
lateinit
var
token0825
:
ByteArray
private
lateinit
var
token0825
:
ByteArray
//56
private
var
loginTime
:
Int
=
0
private
lateinit
var
loginIP
:
String
private
var
t
gtgtKey
:
ByteArray
=
getRandomByteArray
(
16
)
private
var
randomT
gtgtKey
:
ByteArray
=
getRandomByteArray
(
16
)
/**
* 0828_decr_key
...
...
@@ -266,7 +266,8 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
this
.
loginIP
=
packet
.
loginIP
this
.
loginTime
=
packet
.
loginTime
this
.
token0825
=
packet
.
token0825
socket
.
sendPacket
(
ClientPasswordSubmissionPacket
(
bot
.
account
.
qqNumber
,
bot
.
account
.
password
,
packet
.
loginTime
,
packet
.
loginIP
,
this
.
tgtgtKey
,
packet
.
token0825
))
println
(
"token0825="
+
this
.
token0825
.
toUHexString
())
socket
.
sendPacket
(
ClientPasswordSubmissionPacket
(
bot
.
account
.
qqNumber
,
bot
.
account
.
password
,
packet
.
loginTime
,
packet
.
loginIP
,
this
.
randomTgtgtKey
,
packet
.
token0825
))
}
}
...
...
@@ -276,9 +277,9 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
}
is
ServerVerificationCodeCorrectPacket
->
{
this
.
t
gtgtKey
=
getRandomByteArray
(
16
)
this
.
randomT
gtgtKey
=
getRandomByteArray
(
16
)
this
.
token00BA
=
packet
.
token00BA
socket
.
sendPacket
(
ClientLoginResendPacket3105
(
bot
.
account
.
qqNumber
,
bot
.
account
.
password
,
this
.
loginTime
,
this
.
loginIP
,
this
.
t
gtgtKey
,
this
.
token0825
,
this
.
token00BA
))
socket
.
sendPacket
(
ClientLoginResendPacket3105
(
bot
.
account
.
qqNumber
,
bot
.
account
.
password
,
this
.
loginTime
,
this
.
loginIP
,
this
.
randomT
gtgtKey
,
this
.
token0825
,
this
.
token00BA
))
}
is
ServerLoginResponseVerificationCodeInitPacket
->
{
...
...
@@ -337,11 +338,11 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
//println("token00BA changed!!! to " + token00BA.toUByteArray())
//}
if
(
packet
.
flag
==
ServerLoginResponseKeyExchangePacket
.
Flag
.
`
08
36
31
03
`
)
{
this
.
t
gtgtKey
=
packet
.
tgtgtKey
socket
.
sendPacket
(
ClientLoginResendPacket3104
(
bot
.
account
.
qqNumber
,
bot
.
account
.
password
,
loginTime
,
loginIP
,
t
gtgtKey
,
token0825
,
packet
.
tokenUnknown
this
.
randomT
gtgtKey
=
packet
.
tgtgtKey
socket
.
sendPacket
(
ClientLoginResendPacket3104
(
bot
.
account
.
qqNumber
,
bot
.
account
.
password
,
loginTime
,
loginIP
,
randomT
gtgtKey
,
token0825
,
packet
.
tokenUnknown
?:
this
.
token00BA
,
packet
.
tlv0006
))
}
else
{
socket
.
sendPacket
(
ClientLoginResendPacket3106
(
bot
.
account
.
qqNumber
,
bot
.
account
.
password
,
loginTime
,
loginIP
,
t
gtgtKey
,
token0825
,
packet
.
tokenUnknown
socket
.
sendPacket
(
ClientLoginResendPacket3106
(
bot
.
account
.
qqNumber
,
bot
.
account
.
password
,
loginTime
,
loginIP
,
randomT
gtgtKey
,
token0825
,
packet
.
tokenUnknown
?:
token00BA
,
packet
.
tlv0006
))
}
}
...
...
@@ -372,8 +373,8 @@ internal class BotNetworkHandlerImpl(private val bot: Bot) : BotNetworkHandler {
is
ServerVerificationCodePacket
.
Encrypted
->
socket
.
distributePacket
(
packet
.
decrypt
())
is
ServerLoginResponseVerificationCodeInitPacket
.
Encrypted
->
socket
.
distributePacket
(
packet
.
decrypt
())
is
ServerLoginResponseKeyExchangePacket
.
Encrypted
->
socket
.
distributePacket
(
packet
.
decrypt
(
this
.
t
gtgtKey
))
is
ServerLoginResponseSuccessPacket
.
Encrypted
->
socket
.
distributePacket
(
packet
.
decrypt
(
this
.
t
gtgtKey
))
is
ServerLoginResponseKeyExchangePacket
.
Encrypted
->
socket
.
distributePacket
(
packet
.
decrypt
(
this
.
randomT
gtgtKey
))
is
ServerLoginResponseSuccessPacket
.
Encrypted
->
socket
.
distributePacket
(
packet
.
decrypt
(
this
.
randomT
gtgtKey
))
is
ServerSessionKeyResponsePacket
.
Encrypted
->
socket
.
distributePacket
(
packet
.
decrypt
(
this
.
sessionResponseDecryptionKey
))
is
ServerTouchResponsePacket
.
Encrypted
->
socket
.
distributePacket
(
packet
.
decrypt
())
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt
View file @
91aed96c
...
...
@@ -2,6 +2,9 @@
package
net.mamoe.mirai.network
import
net.mamoe.mirai.utils.hexToBytes
import
net.mamoe.mirai.utils.lazyDecode
import
net.mamoe.mirai.utils.readUnsignedVarInt
import
java.net.InetAddress
import
java.util.*
import
java.util.stream.Collectors
...
...
@@ -44,10 +47,28 @@ object Protocol {
* 0825data2
*/
const
val
constantData2
=
"00 00 04 53 00 00 00 01 00 00 15 85 "
const
val
key0825
=
"A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
const
val
redirectionKey
=
"A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B"
const
val
publicKey
=
"02 6D 28 41 D2 A5 6F D2 FC 3E 2A 1F 03 75 DE 6E 28 8F A8 19 3E 5F 16 49 D3"
const
val
shareKey
=
"1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"
/**
* 0825 key
*
* Touch 发出时写入, 并用于加密, 接受 touch response 时解密.
*/
const
val
touchKey
=
"A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
//16
/**
* Redirection 发出时写入, 并用于加密, 接受 Redirection response 时解密.
*/
const
val
redirectionKey
=
"A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B"
//16
/**
*
*/
const
val
publicKey
=
"02 6D 28 41 D2 A5 6F D2 FC 3E 2A 1F 03 75 DE 6E 28 8F A8 19 3E 5F 16 49 D3"
//25
/**
* 没有任何地方写入了这个 key
*/
const
val
shareKey
=
"1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"
//16
const
val
fix0836
=
"06 A9 12 97 B7 F8 76 25 AF AF D3 EA B4 C8 BC E7 "
const
val
key00BA
=
"C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94"
...
...
@@ -56,21 +77,28 @@ object Protocol {
/**
* 0836_622_fix2
*/
const
val
passwordSubmission
Key
2
=
"00 15 00 30 00 01 01 27 9B C7 F5 00 10 65 03 FD 8B 00 00 00 00 00 00 00 00 00 00 00 00 02 90 49 55 33 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B"
;
const
val
passwordSubmission
TLV
2
=
"00 15 00 30 00 01 01 27 9B C7 F5 00 10 65 03 FD 8B 00 00 00 00 00 00 00 00 00 00 00 00 02 90 49 55 33 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B"
;
/**
* 0836_622_fix1
*/
const
val
passwordSubmissionKey1
=
"03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03 00 19"
;
const
val
passwordSubmissionTLV1
=
"03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03"
//19
// 最新版 03 00 00 00 01 2E 01 00 00 69 35 00 00 00 00 00 02 01 03
// 第一版 1.0.2 03 00 00 00 01 2E 01 00 00 68 13 00 00 00 00 00 02 01 03
// 1.0.4 03 00 00 00 01 2E 01 00 00 68 27 00 00 00 00 00 02 01 03
// 1.1 03 00 00 00 01 2E 01 00 00 68 3F 00 00 00 00 00 02 01 03
// 1.2 03 00 00 00 01 2E 01 00 00 68 44 00 00 00 00 00 02 01 03
/**
* fix_0836_1
*
* LoginResend 和 PasswordSubmission 时写入, 但随后都使用 shareKey 加密, 收到回复也是用的 share key
*/
const
val
key0836
=
"EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA"
const
val
key0836
=
"EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA"
//16
/**
* 发送/接受消息中的一个const (?)
* length=15
*/
const
val
friendM
essageConst1
=
"00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91"
const
val
m
essageConst1
=
"00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91"
private
val
hexToByteArrayCacheMap
:
MutableMap
<
Int
,
ByteArray
>
=
mutableMapOf
()
...
...
@@ -96,3 +124,10 @@ object Protocol {
.
collect
(
Collectors
.
toList
()).
toUByteArray
()
}
fun
main
()
{
lazyDecode
(
"03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03"
.
hexToBytes
())
{
it
.
skip
(
7
)
println
(
it
.
readUnsignedVarInt
())
}
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/network/handler/ActionPacketHandler.kt
View file @
91aed96c
...
...
@@ -6,9 +6,9 @@ import net.mamoe.mirai.network.packet.action.AddFriendResult
import
net.mamoe.mirai.network.packet.action.ClientAddFriendPacket
import
net.mamoe.mirai.network.packet.action.ClientCanAddFriendPacket
import
net.mamoe.mirai.network.packet.action.ServerCanAddFriendResponsePacket
import
net.mamoe.mirai.network.packet.image.ServerTry
UploadGroupImage
FailedPacket
import
net.mamoe.mirai.network.packet.image.ServerTry
UploadGroupImage
ResponsePacket
import
net.mamoe.mirai.network.packet.image.ServerTry
UploadGroupImage
SuccessPacket
import
net.mamoe.mirai.network.packet.image.ServerTry
GetImageID
FailedPacket
import
net.mamoe.mirai.network.packet.image.ServerTry
GetImageID
ResponsePacket
import
net.mamoe.mirai.network.packet.image.ServerTry
GetImageID
SuccessPacket
import
net.mamoe.mirai.task.MiraiThreadPool
import
net.mamoe.mirai.utils.getGTK
import
java.awt.image.BufferedImage
...
...
@@ -39,15 +39,15 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
it
.
onPacketReceived
(
packet
)
}
}
is
ServerTry
UploadGroupImage
SuccessPacket
->
{
is
ServerTry
GetImageID
SuccessPacket
->
{
// ImageNetworkUtils.postImage(packet.uKey.toUHexString(), )
}
is
ServerTry
UploadGroupImage
FailedPacket
->
{
is
ServerTry
GetImageID
FailedPacket
->
{
}
is
ServerTry
UploadGroupImage
ResponsePacket
.
Encrypted
->
session
.
socket
.
distributePacket
(
packet
.
decrypt
(
session
.
sessionKey
))
is
ServerTry
GetImageID
ResponsePacket
.
Encrypted
->
session
.
socket
.
distributePacket
(
packet
.
decrypt
(
session
.
sessionKey
))
is
ServerAccountInfoResponsePacket
.
Encrypted
->
session
.
socket
.
distributePacket
(
packet
.
decrypt
(
session
.
sessionKey
))
is
ServerAccountInfoResponsePacket
->
{
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/handler/MessagePacketHandler.kt
View file @
91aed96c
...
...
@@ -2,18 +2,21 @@ package net.mamoe.mirai.network.handler
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.QQ
import
net.mamoe.mirai.event.events.group.GroupMessageEvent
import
net.mamoe.mirai.event.events.qq.FriendMessageEvent
import
net.mamoe.mirai.event.hookWhile
import
net.mamoe.mirai.message.defaults.Image
import
net.mamoe.mirai.message.defaults.MessageChain
import
net.mamoe.mirai.message.defaults.PlainText
import
net.mamoe.mirai.message.defaults.UnsolvedImage
import
net.mamoe.mirai.network.LoginSession
import
net.mamoe.mirai.network.packet.*
import
net.mamoe.mirai.network.packet.ServerFriendMessageEventPacket
import
net.mamoe.mirai.network.packet.ServerGroupMessageEventPacket
import
net.mamoe.mirai.network.packet.ServerGroupUploadFileEventPacket
import
net.mamoe.mirai.network.packet.ServerPacket
import
net.mamoe.mirai.network.packet.action.ClientSendFriendMessagePacket
import
net.mamoe.mirai.network.packet.action.ClientSendGroupMessagePacket
import
net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import
net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
import
java.io.File
/**
* 处理消息事件, 承担消息发送任务.
...
...
@@ -31,21 +34,39 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
return
@
hookWhile
false
}
when
{
it
.
message
()
valueEquals
"你好"
->
it
.
qq
.
sendMessage
(
"你好!"
)
it
.
message
().
toString
().
startsWith
(
"复读"
)
->
it
.
qq
.
sendMessage
(
it
.
message
())
it
.
message
()
.
toString
().
startsWith
(
"发群"
)
->
{
it
.
message
valueEquals
"你好"
->
it
.
sender
.
sendMessage
(
"你好!"
)
it
.
message
.
toString
().
startsWith
(
"复读"
)
->
it
.
sender
.
sendMessage
(
it
.
message
())
it
.
message
.
toString
().
startsWith
(
"发群"
)
->
{
it
.
message
().
list
.
toMutableList
().
let
{
messages
->
messages
.
removeAt
(
0
)
sendGroupMessage
(
Group
(
session
.
bot
,
580266363
),
MessageChain
(
messages
))
}
}
it
.
message
()
valueEquals
"发图片"
->
sendGroupMessage
(
Group
(
session
.
bot
,
580266363
),
PlainText
(
"test"
)
+
UnsolvedImage
(
File
(
"C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg"
)).
also
{
image
->
image
.
upload
(
session
,
it
.
qq
).
get
()
})
/*it.message valueEquals "发图片群" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, Group(session.bot, 580266363)).get()
})*/
it
.
message
valueEquals
"发图片群2"
->
sendGroupMessage
(
Group
(
session
.
bot
,
580266363
),
Image
(
"{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"
).
toChain
())
/* it.message valueEquals "发图片" -> sendFriendMessage(it.sender, PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
image.upload(session, it.sender).get()
})*/
it
.
message
valueEquals
"发图片2"
->
sendFriendMessage
(
it
.
sender
,
PlainText
(
"test"
)
+
Image
(
"{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"
))
}
return
@
hookWhile
true
}
GroupMessageEvent
::
class
.
hookWhile
{
if
(
session
.
socket
.
isClosed
())
{
return
@
hookWhile
false
}
when
{
it
.
message
.
contains
(
"复读"
)
->
it
.
group
.
sendMessage
(
it
.
chain
)
}
return
@
hookWhile
true
}
}
override
fun
onPacketReceived
(
packet
:
ServerPacket
)
{
...
...
@@ -55,20 +76,17 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
}
is
ServerFriendMessageEventPacket
->
{
if
(
ignoreMessage
)
{
return
}
if
(
ignoreMessage
)
return
FriendMessageEvent
(
session
.
bot
,
session
.
bot
.
contacts
.
getQQ
(
packet
.
qq
),
packet
.
message
).
broadcast
()
}
is
ServerGroupMessageEventPacket
->
{
//todo message chain
//GroupMessageEvent(this.bot, bot.contacts.getGroupByNumber(packet.groupNumber), bot.contacts.getQQ(packet.qq), packet.message)
}
if
(
ignoreMessage
)
return
is
UnknownServerEventPacket
->
{
//todo
if
(
packet
.
qq
==
session
.
bot
.
account
.
qqNumber
)
return
GroupMessageEvent
(
session
.
bot
,
session
.
bot
.
contacts
.
getGroupByNumber
(
packet
.
groupNumber
),
session
.
bot
.
contacts
.
getQQ
(
packet
.
qq
),
packet
.
message
).
broadcast
()
}
is
ServerSendFriendMessageResponsePacket
,
...
...
@@ -86,6 +104,6 @@ class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
}
fun
sendGroupMessage
(
group
:
Group
,
message
:
MessageChain
)
{
session
.
socket
.
sendPacket
(
ClientSendGroupMessagePacket
(
group
.
groupId
,
session
.
bot
.
account
.
qqNumber
,
session
.
sessionKey
,
message
))
session
.
socket
.
sendPacket
(
ClientSendGroupMessagePacket
(
session
.
bot
.
account
.
qqNumber
,
group
.
groupId
,
session
.
sessionKey
,
message
))
}
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt
View file @
91aed96c
...
...
@@ -122,28 +122,34 @@ fun DataOutputStream.encryptAndWrite(keyHex: String, encoder: (ByteArrayDataOutp
@Throws
(
IOException
::
class
)
fun
DataOutputStream
.
writeTLV0006
(
qq
:
Long
,
password
:
String
,
loginTime
:
Int
,
loginIP
:
String
,
tgtgtKey
:
ByteArray
)
{
ByteArrayDataOutputStream
().
let
{
val
firstMD5
=
md5
(
password
)
val
secondMD5
=
md5
(
firstMD5
+
"00 00 00 00"
.
hexToBytes
()
+
qq
.
toUInt
().
toByteArray
())
this
.
encryptAndWrite
(
secondMD5
)
{
it
.
writeRandom
(
4
)
it
.
writeHex
(
"00 02"
)
it
.
writeQQ
(
qq
)
it
.
writeHex
(
Protocol
.
constantData2
)
it
.
writeHex
(
"00 00 01"
)
val
firstMD5
=
md5
(
password
)
val
secondMD5
=
md5
(
firstMD5
+
"00 00 00 00"
.
hexToBytes
()
+
qq
.
toUInt
().
toByteArray
())
it
.
write
(
firstMD5
)
it
.
writeInt
(
loginTime
)
it
.
writeByte
(
0
)
it
.
writeZero
(
4
*
3
)
it
.
writeIP
(
loginIP
)
it
.
writeZero
(
8
)
it
.
writeHex
(
"00 10"
)
it
.
writeHex
(
"15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B"
)
it
.
writeHex
(
"00 10"
)
//这两个hex是passwordSubmissionTLV2的末尾
it
.
writeHex
(
"15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B"
)
//16
it
.
write
(
tgtgtKey
)
this
.
write
(
TEA
.
encrypt
(
it
.
toByteArray
(),
secondMD5
))
}
}
fun
main
()
{
println
(
lazyEncode
{
it
.
writeTLV0006
(
1040400290
,
"asdHim188moe"
,
System
.
currentTimeMillis
().
toInt
(),
"123.123.123.123"
,
getRandomByteArray
(
56
))
}.
size
)
}
@Tested
fun
DataOutputStream
.
writeCRC32
()
=
writeCRC32
(
getRandomByteArray
(
16
))
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt
View file @
91aed96c
This diff is collapsed.
Click to expand it.
mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt
View file @
91aed96c
...
...
@@ -11,7 +11,7 @@ import net.mamoe.mirai.network.packet.PacketNameFormatter.adjustName
import
net.mamoe.mirai.network.packet.action.ServerCanAddFriendResponsePacket
import
net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import
net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
import
net.mamoe.mirai.network.packet.image.ServerTry
UploadGroupImage
ResponsePacket
import
net.mamoe.mirai.network.packet.image.ServerTry
GetImageID
ResponsePacket
import
net.mamoe.mirai.network.packet.login.*
import
net.mamoe.mirai.task.MiraiThreadPool
import
net.mamoe.mirai.utils.*
...
...
@@ -82,7 +82,9 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
println
(
bytes
.
size
)
return
ServerLoginResponseFailedPacket
(
when
(
bytes
.
size
)
{
63
,
319
,
135
,
351
->
LoginState
.
WRONG_PASSWORD
//这四个其中一个是被冻结
135
->
LoginState
.
UNKNOWN
//账号已经在另一台电脑登录??
63
,
319
,
351
->
LoginState
.
WRONG_PASSWORD
//63不是密码错误, 应该是登录过频繁
//135 -> LoginState.RETYPE_PASSWORD
279
->
LoginState
.
BLOCKED
263
->
LoginState
.
UNKNOWN_QQ_NUMBER
...
...
@@ -121,9 +123,9 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
"00 A7"
->
ServerCanAddFriendResponsePacket
(
stream
)
"03 88"
->
ServerTry
UploadGroupImage
ResponsePacket
.
Encrypted
(
stream
)
"03 88"
->
ServerTry
GetImageID
ResponsePacket
.
Encrypted
(
stream
)
else
->
throw
IllegalArgumentException
(
idHex
)
else
->
UnknownServerPacket
(
stream
)
}
}.
apply
{
this
.
idHex
=
idHex
}
}
...
...
@@ -197,15 +199,48 @@ fun DataInputStream.readIP(): String {
return
buff
}
fun
DataInputStream
.
read
Var
String
():
String
{
return
String
(
this
.
read
Var
ByteArray
())
fun
DataInputStream
.
read
LV
String
():
String
{
return
String
(
this
.
read
LV
ByteArray
())
}
fun
DataInputStream
.
read
Var
ByteArray
():
ByteArray
{
fun
DataInputStream
.
read
LV
ByteArray
():
ByteArray
{
return
this
.
readNBytes
(
this
.
readShort
().
toInt
())
}
fun
DataInputStream
.
readString
(
length
:
Int
):
String
{
fun
DataInputStream
.
readTLVMap
(
expectingEOF
:
Boolean
=
false
):
Map
<
Int
,
ByteArray
>
{
val
map
=
mutableMapOf
<
Int
,
ByteArray
>()
var
type
:
Int
try
{
type
=
readUnsignedByte
()
}
catch
(
e
:
EOFException
)
{
if
(
expectingEOF
)
{
return
map
}
throw
e
}
while
(
type
!=
0
xff
)
{
map
[
type
]
=
this
.
readLVByteArray
()
try
{
type
=
readUnsignedByte
()
}
catch
(
e
:
EOFException
)
{
if
(
expectingEOF
)
{
return
map
}
throw
e
}
}
return
map
}
fun
Map
<
Int
,
ByteArray
>.
printTLVMap
()
{
println
(
this
.
mapValues
{
(
_
,
value
)
->
value
.
toUHexString
()
})
}
fun
DataInputStream
.
readString
(
length
:
Number
):
String
{
return
String
(
this
.
readNBytes
(
length
))
}
...
...
@@ -257,6 +292,10 @@ fun <N : Number> DataInputStream.readUIntAt(position: N): UInt {
return
this
.
readNBytes
(
4
).
toUInt
()
}
fun
DataInputStream
.
readUInt
():
UInt
{
return
this
.
readNBytes
(
4
).
toUInt
()
}
fun
<
N
:
Number
>
DataInputStream
.
readByteAt
(
position
:
N
):
Byte
{
this
.
goto
(
position
)
return
this
.
readByte
()
...
...
@@ -290,8 +329,8 @@ fun DataInputStream.gotoWhere(matcher: ByteArray): DataInputStream {
if
(
b
!=
matcher
[
i
])
{
continue
@
loop
//todo goto mark
}
return
this
}
return
this
}
}
while
(
true
)
}
...
...
@@ -336,4 +375,6 @@ fun DataInputStream.gotoWhere(matcher: ByteArray) {
} while (true)
}*/
fun
ByteArray
.
cutTail
(
length
:
Int
):
ByteArray
=
this
.
copyOfRange
(
0
,
this
.
size
-
length
)
\ No newline at end of file
fun
ByteArray
.
cutTail
(
length
:
Int
):
ByteArray
=
this
.
copyOfRange
(
0
,
this
.
size
-
length
)
fun
ByteArray
.
getRight
(
length
:
Int
):
ByteArray
=
this
.
copyOfRange
(
this
.
size
-
length
,
this
.
size
)
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt
View file @
91aed96c
...
...
@@ -24,7 +24,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
var
loginTime
:
Int
=
0
lateinit
var
loginIP
:
String
lateinit
var
token0825
:
ByteArray
lateinit
var
token0825
:
ByteArray
//56
enum
class
Type
{
TYPE_08_25_31_01
,
...
...
@@ -57,7 +57,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
fun
decrypt
():
ServerTouchResponsePacket
=
ServerTouchResponsePacket
(
decryptBy
(
when
(
type
)
{
Type
.
TYPE_08_25_31_02
->
Protocol
.
redirectionKey
.
hexToBytes
()
Type
.
TYPE_08_25_31_01
->
Protocol
.
key0825
.
hexToBytes
()
Type
.
TYPE_08_25_31_01
->
Protocol
.
touchKey
.
hexToBytes
()
})).
setId
(
this
.
idHex
)
}
}
...
...
@@ -75,9 +75,9 @@ class ClientTouchPacket(private val qq: Long, private val serverIp: String) : Cl
override
fun
encode
()
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
Protocol
.
fixVer
)
this
.
writeHex
(
Protocol
.
key0825
)
this
.
writeHex
(
Protocol
.
touchKey
)
this
.
encryptAndWrite
(
Protocol
.
key0825
)
{
this
.
encryptAndWrite
(
Protocol
.
touchKey
)
{
it
.
writeHex
(
Protocol
.
constantData1
)
it
.
writeHex
(
Protocol
.
constantData2
)
it
.
writeQQ
(
qq
)
...
...
@@ -94,7 +94,6 @@ class ClientTouchPacket(private val qq: Long, private val serverIp: String) : Cl
*
* @author Him188moe
*/
@PacketId
(
"08 25 31 02"
)
class
ClientServerRedirectionPacket
(
private
val
serverIP
:
String
,
private
val
qq
:
Long
)
:
ClientPacket
()
{
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/UnknownServerPacket.kt
View file @
91aed96c
package
net.mamoe.mirai.network.packet
import
net.mamoe.mirai.utils.LoggerTextFormat
import
net.mamoe.mirai.utils.toUHexString
import
java.io.DataInputStream
/**
...
...
@@ -7,6 +9,10 @@ import java.io.DataInputStream
*/
class
UnknownServerPacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
override
fun
decode
()
{
println
(
"UnknownServerPacket data: "
+
this
.
input
.
goto
(
0
).
readAllBytes
().
toUHexString
())
}
override
fun
toString
():
String
{
return
LoggerTextFormat
.
LIGHT_RED
.
toString
()
+
super
.
toString
()
}
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt
View file @
91aed96c
...
...
@@ -72,15 +72,15 @@ class ClientVerificationCodeSubmitPacket(
it
.
writeHex
(
"01 03"
)
it
.
writeShort
(
25
)
it
.
writeHex
(
Protocol
.
publicKey
)
it
.
writeHex
(
Protocol
.
publicKey
)
//25
it
.
writeHex
(
"14 00 05 00 00 00 00 00 04"
)
it
.
write
(
verificationCode
.
toUpperCase
().
toByteArray
())
it
.
writeHex
(
"00 38"
)
it
.
write
(
verificationToken
)
it
.
write
Hex
(
"00 10"
)
it
.
writeHex
(
Protocol
.
key00BAFix
)
it
.
write
Short
(
16
)
it
.
writeHex
(
Protocol
.
key00BAFix
)
//16
}
}
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendFriendMessagePacket.kt
View file @
91aed96c
...
...
@@ -38,7 +38,7 @@ class ClientSendFriendMessagePacket(
it
.
writeTime
()
it
.
writeRandom
(
4
)
it
.
writeHex
(
"00 00 00 00 09 00 86"
)
it
.
writeHex
(
Protocol
.
friendM
essageConst1
)
//... 85 E9 BB 91
it
.
writeHex
(
Protocol
.
m
essageConst1
)
//... 85 E9 BB 91
it
.
writeZero
(
2
)
it
.
write
(
message
.
toByteArray
())
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendGroupMessagePacket.kt
View file @
91aed96c
...
...
@@ -3,6 +3,7 @@ package net.mamoe.mirai.network.packet.action
import
net.mamoe.mirai.message.defaults.MessageChain
import
net.mamoe.mirai.network.Protocol
import
net.mamoe.mirai.network.packet.*
import
net.mamoe.mirai.utils.lazyEncode
import
net.mamoe.mirai.utils.toUHexString
import
java.io.DataInputStream
...
...
@@ -11,8 +12,8 @@ import java.io.DataInputStream
*/
@PacketId
(
"00 02"
)
class
ClientSendGroupMessagePacket
(
private
val
groupId
:
Long
,
//不是 number
private
val
botQQ
:
Long
,
private
val
groupId
:
Long
,
//不是 number
private
val
sessionKey
:
ByteArray
,
private
val
message
:
MessageChain
)
:
ClientPacket
()
{
...
...
@@ -26,18 +27,19 @@ class ClientSendGroupMessagePacket(
it
.
writeByte
(
0
x2A
)
it
.
writeGroup
(
groupId
)
it
.
write
Short
(
50
+
bytes
.
size
)
it
.
writeHex
(
"00 01 01"
)
it
.
writeHex
(
"00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00"
)
it
.
write
LVByteArray
(
lazyEncode
{
child
->
child
.
writeHex
(
"00 01 01"
)
child
.
writeHex
(
"00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00"
)
it
.
writeTime
()
it
.
writeRandom
(
4
)
it
.
writeHex
(
"00 00 00 00 09 00 86"
)
it
.
writeHex
(
Protocol
.
friendM
essageConst1
)
it
.
writeZero
(
2
)
child
.
writeTime
()
child
.
writeRandom
(
4
)
child
.
writeHex
(
"00 00 00 00 09 00 86"
)
child
.
writeHex
(
Protocol
.
m
essageConst1
)
child
.
writeZero
(
2
)
//messages
it
.
write
(
bytes
)
//messages
child
.
write
(
bytes
)
})
/*it.writeByte(0x01)
it.writeShort(bytes.size + 3)
it.writeByte(0x01)
...
...
mirai-core/src/main/java/net/mamoe/mirai/network/packet/image/UploadGroupImage.kt
View file @
91aed96c
...
...
@@ -11,8 +11,8 @@ import java.io.DataInputStream
/**
* 请求上传图片. 将发送图片的 md5, size.
* 服务器返回以下之一:
* - 服务器已经存有这个图片 [ServerTry
UploadGroupImage
FailedPacket]
* - 服务器未存有, 返回一个 key 用于客户端上传 [ServerTry
UploadGroupImage
SuccessPacket]
* - 服务器已经存有这个图片 [ServerTry
GetImageID
FailedPacket]
* - 服务器未存有, 返回一个 key 用于客户端上传 [ServerTry
GetImageID
SuccessPacket]
*
* @author Him188moe
*/
...
...
@@ -20,7 +20,7 @@ import java.io.DataInputStream
class
ClientTryGetImageIDPacket
(
private
val
botNumber
:
Long
,
private
val
sessionKey
:
ByteArray
,
private
val
groupNumberOrQQNumber
:
Long
,
//todo 为什么还要有qq number呢? bot不就是了么
private
val
groupNumberOrQQNumber
:
Long
,
private
val
image
:
BufferedImage
)
:
ClientPacket
()
{
override
fun
encode
()
{
...
...
@@ -89,18 +89,18 @@ class ClientTryGetImageIDPacket(
}
}
abstract
class
ServerTry
UploadGroupImage
ResponsePacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
abstract
class
ServerTry
GetImageID
ResponsePacket
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
class
Encrypted
(
input
:
DataInputStream
)
:
ServerPacket
(
input
)
{
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerTry
UploadGroupImage
ResponsePacket
{
fun
decrypt
(
sessionKey
:
ByteArray
):
ServerTry
GetImageID
ResponsePacket
{
val
data
=
this
.
decryptAsByteArray
(
sessionKey
)
println
(
data
.
size
)
println
(
data
.
size
)
if
(
data
.
size
==
209
)
{
return
ServerTry
UploadGroupImage
SuccessPacket
(
data
.
dataInputStream
()).
setId
(
this
.
idHex
)
return
ServerTry
GetImageID
SuccessPacket
(
data
.
dataInputStream
()).
setId
(
this
.
idHex
)
}
return
ServerTry
UploadGroupImage
FailedPacket
(
data
.
dataInputStream
())
return
ServerTry
GetImageID
FailedPacket
(
data
.
dataInputStream
())
}
}
}
...
...
@@ -108,25 +108,21 @@ abstract class ServerTryUploadGroupImageResponsePacket(input: DataInputStream) :
/**
* 服务器未存有图片, 返回一个 key 用于客户端上传
*/
class
ServerTry
UploadGroupImageSuccessPacket
(
input
:
DataInputStream
)
:
ServerTryUploadGroupImage
ResponsePacket
(
input
)
{
class
ServerTry
GetImageIDSuccessPacket
(
input
:
DataInputStream
)
:
ServerTryGetImageID
ResponsePacket
(
input
)
{
lateinit
var
uKey
:
ByteArray
override
fun
decode
()
{
uKey
=
this
.
input
.
gotoWhere
(
ubyteArrayOf
(
0
x42u
,
0
x80u
,
0
x01u
)).
readNBytes
(
128
)
this
.
input
.
gotoWhere
(
ubyteArrayOf
(
0
x42u
,
0
x80u
,
0
x01u
))
uKey
=
this
.
input
.
readNBytes
(
128
)
}
}
/**
* 服务器已经存有这个图片
*/
class
ServerTry
UploadGroupImageFailedPacket
(
input
:
DataInputStream
)
:
ServerTryUploadGroupImage
ResponsePacket
(
input
)
{
class
ServerTry
GetImageIDFailedPacket
(
input
:
DataInputStream
)
:
ServerTryGetImageID
ResponsePacket
(
input
)
{
override
fun
decode
()
{
}
}
fun
main
()
{
println
(
0
xff
)
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt
View file @
91aed96c
...
...
@@ -2,7 +2,6 @@ package net.mamoe.mirai.network.packet.login
import
net.mamoe.mirai.network.Protocol
import
net.mamoe.mirai.network.packet.*
import
net.mamoe.mirai.utils.ByteArrayDataOutputStream
import
net.mamoe.mirai.utils.TEA
import
net.mamoe.mirai.utils.Tested
import
net.mamoe.mirai.utils.hexToBytes
...
...
@@ -14,21 +13,23 @@ import java.io.DataOutputStream
* @author Him188moe
*/
@PacketId
(
"08 36 31 03"
)
@Tested
class
ClientPasswordSubmissionPacket
(
private
val
qq
:
Long
,
private
val
password
:
String
,
private
val
loginTime
:
Int
,
private
val
loginIP
:
String
,
private
val
tgtgtKey
:
ByteArray
,
private
val
token0825
:
ByteArray
private
val
tgtgtKey
:
ByteArray
,
//16 random by client
private
val
token0825
:
ByteArray
//56 from server
)
:
ClientPacket
()
{
override
fun
encode
()
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
Protocol
.
passwordSubmissionKey1
)
this
.
writeHex
(
Protocol
.
publicKey
)
this
.
writeHex
(
Protocol
.
passwordSubmissionTLV1
)
this
.
writeShort
(
25
)
this
.
writeHex
(
Protocol
.
publicKey
)
//25
this
.
writeHex
(
"00 00 00 10"
)
this
.
writeHex
(
Protocol
.
key0836
)
...
...
@@ -67,25 +68,25 @@ open class ClientLoginResendPacket internal constructor(
)
:
ClientPacket
()
{
override
fun
encode
()
{
this
.
writeQQ
(
qq
)
this
.
writeHex
(
Protocol
.
passwordSubmissionKey1
)
this
.
writeHex
(
Protocol
.
publicKey
)
this
.
writeHex
(
"00 00 00 10"
)
this
.
writeHex
(
Protocol
.
key0836
)
this
.
writeHex
(
Protocol
.
passwordSubmissionTLV1
)
this
.
write
(
TEA
.
encrypt
(
object
:
ByteArrayDataOutputStream
()
{
override
fun
toByteArray
():
ByteArray
{
this
.
writePart1
(
qq
,
password
,
loginTime
,
loginIP
,
tgtgtKey
,
token0825
,
tlv0006
)
this
.
writeShort
(
25
)
this
.
writeHex
(
Protocol
.
publicKey
)
//25
this
.
writeHex
(
"01 10"
)
//tag
this
.
writeHex
(
"00 3C"
)
//length
this
.
writeHex
(
"00 01"
)
//tag
this
.
writeHex
(
"00 38"
)
//length
this
.
write
(
token00BA
)
//value
this
.
writeHex
(
"00 00 00 10"
)
//=16
this
.
writeHex
(
Protocol
.
key0836
)
//16
this
.
writePart2
()
return
super
.
toByteArray
()
}
}.
toByteArray
(),
Protocol
.
shareKey
.
hexToBytes
()))
this
.
encryptAndWrite
(
Protocol
.
shareKey
.
hexToBytes
())
{
it
.
writePart1
(
qq
,
password
,
loginTime
,
loginIP
,
tgtgtKey
,
token0825
,
tlv0006
)
it
.
writeHex
(
"01 10"
)
//tag
it
.
writeHex
(
"00 3C"
)
//length
it
.
writeHex
(
"00 01"
)
//tag
it
.
writeHex
(
"00 38"
)
//length
it
.
write
(
token00BA
)
//value
it
.
writePart2
()
}
}
}
...
...
@@ -113,10 +114,10 @@ private fun DataOutputStream.writePart1(qq: Long, password: String, loginTime: I
this
.
writeTLV0006
(
qq
,
password
,
loginTime
,
loginIP
,
tgtgtKey
)
}
//fix
this
.
writeHex
(
Protocol
.
passwordSubmission
Key
2
)
this
.
writeHex
(
Protocol
.
passwordSubmission
TLV
2
)
this
.
writeHex
(
"00 1A"
)
//tag
this
.
writeHex
(
"00 40"
)
//length
this
.
write
(
TEA
.
encrypt
(
Protocol
.
passwordSubmission
Key
2
.
hexToBytes
(),
tgtgtKey
))
this
.
write
(
TEA
.
encrypt
(
Protocol
.
passwordSubmission
TLV
2
.
hexToBytes
(),
tgtgtKey
))
this
.
writeHex
(
Protocol
.
constantData1
)
this
.
writeHex
(
Protocol
.
constantData2
)
this
.
writeQQ
(
qq
)
...
...
mirai-core/src/main/java/net/mamoe/mirai/utils/ImageNetworkUtils.java
View file @
91aed96c
...
...
@@ -9,18 +9,18 @@ import java.net.URL;
* @author NaturalHG
*/
public
class
ImageNetworkUtils
{
public
static
boolean
postImage
(
String
uKeyHex
,
int
fileSize
,
long
qq
Number
,
long
groupCode
,
byte
[]
img
)
throws
IOException
{
public
static
boolean
postImage
(
String
uKeyHex
,
int
fileSize
,
long
bot
Number
,
long
groupCode
,
byte
[]
img
)
throws
IOException
{
//http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc&ukey=” + 删全部空 (ukey) + “&filesize=” + 到文本 (fileSize) + “&range=0&uin=” + g_uin + “&groupcode=” + Group
String
builder
=
"http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc"
+
"&ukey="
+
uKeyHex
.
replace
(
" "
,
""
)
+
String
builder
=
"http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc"
+
"&ukey="
+
uKeyHex
.
replace
(
" "
,
""
)
+
"&filezise="
+
fileSize
+
"&range="
+
"0"
+
"&uin="
+
qq
Number
+
"&uin="
+
bot
Number
+
"&groupcode="
+
groupCode
;
HttpURLConnection
conn
=
(
HttpURLConnection
)
new
URL
(
builder
).
openConnection
();
conn
.
setRequestProperty
(
"User-
a
gent"
,
"QQClient"
);
conn
.
setRequestProperty
(
"Content-
l
ength"
,
""
+
fileSize
);
conn
.
setRequestProperty
(
"User-
A
gent"
,
"QQClient"
);
conn
.
setRequestProperty
(
"Content-
L
ength"
,
""
+
fileSize
);
conn
.
setRequestMethod
(
"POST"
);
conn
.
setDoOutput
(
true
);
conn
.
getOutputStream
().
write
(
img
);
...
...
mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt
View file @
91aed96c
...
...
@@ -233,8 +233,8 @@ object TEA {
try
{
return
decrypt
(
data
,
0
,
data
.
size
)
!!
}
catch
(
e
:
Exception
)
{
println
(
"Source: "
+
data
.
toUHexString
(
" "
))
println
(
"Key: "
+
key
.
toUHexString
(
" "
))
//
println("Source: " + data.toUHexString(" "))
//
println("Key: " + key.toUHexString(" "))
throw
e
}
}
...
...
mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt
View file @
91aed96c
...
...
@@ -3,15 +3,20 @@
package
net.mamoe.mirai.utils
import
net.mamoe.mirai.network.Protocol
import
net.mamoe.mirai.network.packet.dataInputStream
import
java.awt.image.BufferedImage
import
java.io.ByteArrayOutputStream
import
java.io.DataInputStream
import
java.io.DataOutputStream
import
java.io.File
import
java.lang.reflect.Field
import
java.util.*
import
java.util.zip.CRC32
import
java.util.zip.GZIPInputStream
import
java.util.zip.GZIPOutputStream
import
javax.imageio.ImageIO
/**
* @author Him188moe
* @author NaturalHG
...
...
@@ -76,6 +81,12 @@ open class ByteArrayDataOutputStream : DataOutputStream(ByteArrayOutputStream())
@JvmSynthetic
fun
lazyEncode
(
t
:
(
ByteArrayDataOutputStream
)
->
Unit
):
ByteArray
=
ByteArrayDataOutputStream
().
also
(
t
).
toByteArray
()
@JvmSynthetic
fun
<
T
>
lazyDecode
(
byteArray
:
ByteArray
,
t
:
(
DataInputStream
)
->
T
):
T
=
byteArray
.
dataInputStream
().
let
(
t
)
fun
DataInputStream
.
skip
(
n
:
Number
)
{
this
.
skip
(
n
.
toLong
())
}
fun
getRandomByteArray
(
length
:
Int
):
ByteArray
{
val
bytes
=
LinkedList
<
Byte
>()
...
...
@@ -144,4 +155,15 @@ fun BufferedImage.toByteArray(formatName: String = "PNG"): ByteArray {
return
lazyEncode
{
ImageIO
.
write
(
this
,
formatName
,
it
)
}
}
object
GZip
{
fun
uncompress
(
bytes
:
ByteArray
):
ByteArray
=
lazyEncode
{
GZIPInputStream
(
bytes
.
inputStream
()).
transferTo
(
it
)
}
fun
compress
(
bytes
:
ByteArray
):
ByteArray
=
ByteArrayOutputStream
().
let
{
GZIPOutputStream
(
it
).
write
(
bytes
)
return
it
.
toByteArray
()
}
}
\ No newline at end of file
mirai-core/src/main/java/net/mamoe/mirai/utils/setting/MiraiSettings.java
View file @
91aed96c
...
...
@@ -3,12 +3,14 @@ package net.mamoe.mirai.utils.setting;
import
net.mamoe.mirai.plugin.MiraiPluginBase
;
import
org.ini4j.Config
;
import
org.ini4j.Ini
;
import
org.jetbrains.annotations.NotNull
;
import
java.io.File
;
import
java.io.IOException
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.concurrent.ConcurrentHashMap
;
/**
...
...
@@ -31,16 +33,15 @@ public class MiraiSettings {
this
(
new
File
(
filename
));
}
public
MiraiSettings
(
File
file
)
{
if
(!
file
.
getName
().
contains
(
"."
)){
public
MiraiSettings
(
@NotNull
File
file
)
{
Objects
.
requireNonNull
(
file
);
if
(!
file
.
getName
().
contains
(
"."
))
{
file
=
new
File
(
file
.
getPath
()
+
".ini"
);
}
this
.
file
=
file
;
try
{
if
(!
file
.
exists
())
{
if
(!
file
.
createNewFile
())
{
throw
new
RuntimeException
(
"cannot create config file "
+
file
);
}
if
(!
file
.
exists
()
&&
!
file
.
createNewFile
())
{
throw
new
RuntimeException
(
"cannot create config file "
+
file
);
}
Config
config
=
new
Config
();
config
.
setMultiSection
(
true
);
...
...
@@ -58,9 +59,9 @@ public class MiraiSettings {
public
synchronized
MiraiSettingMapSection
getMapSection
(
String
key
)
{
if
(!
cacheSection
.
containsKey
(
key
))
{
if
(!
cacheSection
.
containsKey
(
key
))
{
MiraiSettingMapSection
section
=
new
MiraiSettingMapSection
();
if
(
ini
.
containsKey
(
key
))
{
if
(
ini
.
containsKey
(
key
))
{
section
.
putAll
(
ini
.
get
(
key
));
}
cacheSection
.
put
(
key
,
section
);
...
...
@@ -69,9 +70,9 @@ public class MiraiSettings {
}
public
synchronized
MiraiSettingListSection
getListSection
(
String
key
)
{
if
(!
cacheSection
.
containsKey
(
key
))
{
if
(!
cacheSection
.
containsKey
(
key
))
{
MiraiSettingListSection
section
=
new
MiraiSettingListSection
();
if
(
ini
.
containsKey
(
key
))
{
if
(
ini
.
containsKey
(
key
))
{
section
.
addAll
(
ini
.
get
(
key
).
values
());
}
cacheSection
.
put
(
key
,
section
);
...
...
@@ -80,10 +81,10 @@ public class MiraiSettings {
}
public
synchronized
void
save
(){
cacheSection
.
forEach
((
k
,
a
)
->
{
if
(!
ini
.
containsKey
(
k
))
{
ini
.
put
(
k
,
""
,
new
HashMap
<>());
public
synchronized
void
save
()
{
cacheSection
.
forEach
((
k
,
a
)
->
{
if
(!
ini
.
containsKey
(
k
))
{
ini
.
put
(
k
,
""
,
new
HashMap
<>());
}
a
.
saveAsSection
(
ini
.
get
(
k
));
});
...
...
mirai-core/src/test/java/HexComparator.java
View file @
91aed96c
...
...
@@ -58,6 +58,9 @@ public class HexComparator {
private
static
final
String
_1040400290_
=
"3E 03 3F A2"
;
private
static
final
String
_1994701021_
=
"76 E4 B8 DD"
;
private
static
final
String
_jiahua_
=
"B1 89 BE 09"
;
private
static
final
String
_Him188moe_
=
UtilsKt
.
toUHexString
(
"Him188moe"
.
getBytes
(),
" "
);
private
static
final
String
发图片
=
UtilsKt
.
toUHexString
(
"发图片"
.
getBytes
(),
" "
);
private
static
final
String
群
=
UtilsKt
.
toUHexString
(
"发图片"
.
getBytes
(),
" "
);
private
static
final
String
SINGLE_PLAIN_MESSAGE_HEAD
=
"00 00 01 00 09 01"
;
...
...
mirai-core/src/test/java/PacketDebuger.kt
0 → 100644
View file @
91aed96c
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
import
jpcap.JpcapCaptor
import
jpcap.packet.IPPacket
import
jpcap.packet.UDPPacket
import
net.mamoe.mirai.network.Protocol
import
net.mamoe.mirai.network.packet.*
import
net.mamoe.mirai.network.packet.login.ServerLoginResponseFailedPacket
import
net.mamoe.mirai.network.packet.login.ServerLoginResponseKeyExchangePacket
import
net.mamoe.mirai.network.packet.login.ServerLoginResponseSuccessPacket
import
net.mamoe.mirai.network.packet.login.ServerLoginResponseVerificationCodeInitPacket
import
net.mamoe.mirai.utils.*
import
java.io.DataInputStream
/**
* 模拟登录并抓取到 session key
*
* @author Him188moe
*/
object
Main
{
val
localIp
=
"192.168.3.10"
@JvmStatic
fun
main
(
args
:
Array
<
String
>)
{
/*-------------- 第一步绑定网络设备 --------------*/
val
devices
=
JpcapCaptor
.
getDeviceList
()
/*
\Device\NPF_{0E7103E4-BF96-4B66-A23B-F6F630D814CD} | Microsoft
\Device\NPF_{2CCA31E2-93D5-42F2-92C1-5882E18A8E95} | VMware Virtual Ethernet Adapter
\Device\NPF_{A12C8971-858B-4BC8-816C-4077E1636AC5} | VMware Virtual Ethernet Adapter
\Device\NPF_{231C4E27-AF20-4362-BCA3-107236CB8A2E} | MS NDIS 6.0 LoopBack Driver
\Device\NPF_{500B5537-AA10-4E2F-8F7D-E6BD365BDCD1} | Microsoft
\Device\NPF_{A177317B-903A-45B5-8AEA-3698E423ABD6} | Microsoft
*/
/*
for (n in devices) {
println(n.name + " | " + n.description)
}
println("-------------------------------------------")
exitProcess(0)*/
var
jpcap
:
JpcapCaptor
?
=
null
val
caplen
=
4096
val
promiscCheck
=
true
jpcap
=
JpcapCaptor
.
openDevice
(
devices
[
1
],
caplen
,
promiscCheck
,
50
)
/*----------第二步抓包-----------------*/
while
(
true
)
{
assert
(
jpcap
!=
null
)
val
pk
=
jpcap
!!
.
packet
if
(
pk
is
IPPacket
&&
pk
.
version
.
toInt
()
==
4
)
{
if
(
pk
is
UDPPacket
)
{
if
(
pk
.
dst_port
!=
8000
&&
pk
.
src_port
!=
8000
)
{
continue
}
if
(
localIp
==
pk
.
dst_ip
.
hostAddress
)
{
//接受
dataReceived
(
pk
.
data
)
}
else
{
try
{
dataSent
(
pk
.
data
)
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
}
}
}
//pk.dst_ip
}
}
}
fun
dataReceived
(
data
:
ByteArray
)
{
if
(!
debugStarted
)
{
return
}
packetReceived
(
ServerPacket
.
ofByteArray
(
data
))
}
fun
packetReceived
(
packet
:
ServerPacket
)
{
when
(
packet
)
{
is
ServerTouchResponsePacket
.
Encrypted
->
packetReceived
(
packet
.
decrypt
())
is
ServerTouchResponsePacket
->
{
if
(
packet
.
serverIP
==
null
)
{
loginTime
=
packet
.
loginTime
loginIp
=
packet
.
loginIP
token0825
=
packet
.
token0825
}
//then send 08 36 31 03
}
is
ServerLoginResponseFailedPacket
->
{
println
(
"login failed"
)
}
is
ServerLoginResponseKeyExchangePacket
.
Encrypted
->
packetReceived
(
packet
.
decrypt
(
tgtgtKey
))
is
ServerLoginResponseVerificationCodeInitPacket
.
Encrypted
->
packetReceived
(
packet
.
decrypt
())
is
ServerLoginResponseSuccessPacket
.
Encrypted
->
packetReceived
(
packet
.
decrypt
(
tgtgtKey
))
is
ServerLoginResponseKeyExchangePacket
->
{
tgtgtKey
=
packet
.
tgtgtKey
//then 31 04 or 31 06
}
is
ServerLoginResponseSuccessPacket
->
{
sessionResponseDecryptionKey
=
packet
.
sessionResponseDecryptionKey
}
is
ServerSessionKeyResponsePacket
.
Encrypted
->
packetReceived
(
packet
.
decrypt
(
sessionResponseDecryptionKey
))
is
ServerSessionKeyResponsePacket
->
{
sessionKey
=
packet
.
sessionKey
println
(
"Got sessionKey="
+
sessionKey
.
toUHexString
())
}
else
->
{
}
}
}
@Volatile
private
var
debugStarted
=
false
private
val
qq
:
Int
=
1040400290
private
val
password
:
String
=
"asdHim188moe"
lateinit
var
token0825
:
ByteArray
//56
var
loginTime
:
Int
=
0
lateinit
var
loginIp
:
String
lateinit
var
tgtgtKey
:
ByteArray
//16
lateinit
var
sessionKey
:
ByteArray
lateinit
var
sessionResponseDecryptionKey
:
ByteArray
fun
dataSent
(
data
:
ByteArray
)
{
//println("Sent: " + data.toUByteArray().toUHexString())
lazyDecode
(
data
.
cutTail
(
1
))
{
it
.
skip
(
3
)
val
idHex
=
it
.
readNBytes
(
4
).
toUHexString
()
println
(
"qq="
+
it
.
readUInt
())
println
(
idHex
)
when
(
idHex
.
substring
(
0
,
5
))
{
"08 25"
->
{
debugStarted
=
true
println
(
"Detected touch, debug start!!"
)
}
"08 36"
->
{
println
(
"tim的 passwordSubmissionKey1 = "
+
it
.
readNBytes
(
Protocol
.
passwordSubmissionTLV1
.
hexToBytes
().
size
).
toUHexString
())
//it.skipHex(Protocol.passwordSubmissionKey1)
println
(
it
.
readNBytes
(
2
).
toUHexString
())
println
(
"tim的 publicKey = "
+
it
.
readNBytes
(
Protocol
.
publicKey
.
hexToBytes
().
size
).
toUHexString
())
println
(
it
.
readNBytes
(
2
).
toUHexString
())
println
(
"tim的 key0836="
+
it
.
readLVByteArray
().
toUHexString
())
//it.skipHex(Protocol.key0836)
val
encrypted
=
it
.
readAllBytes
()
println
(
encrypted
.
size
)
println
(
encrypted
.
toUHexString
())
val
tlv0006data
=
lazyDecode
(
encrypted
.
decryptBy
(
Protocol
.
shareKey
))
{
section
->
section
.
skip
(
2
+
2
+
56
+
2
)
section
.
skip
(
section
.
readShort
())
//device name
section
.
skip
(
6
+
4
+
2
+
2
)
//tlv0006, encrypted by pwd md5
section
.
readNBytes
(
160
).
decryptBy
(
lazyEncode
{
md5
(
md5
(
password
)
+
"00 00 00 00"
.
hexToBytes
()
+
qq
.
toUInt
().
toByteArray
())
})
}
lazyDecode
(
tlv0006data
)
{
tlv0006
->
tlv0006
.
skip
(
4
+
2
+
4
)
tlv0006
.
skipHex
(
Protocol
.
constantData2
)
tlv0006
.
skip
(
3
)
tlv0006
.
skip
(
16
+
4
+
1
+
4
*
3
+
4
+
8
+
2
)
tlv0006
.
skipHex
(
"15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B"
)
tgtgtKey
=
tlv0006
.
readNBytes
(
16
)
}
println
(
"Got tgtgtKey="
+
tgtgtKey
.
toUHexString
())
//then receive
}
else
->
{
}
}
}
}
private
fun
ByteArray
.
decryptBy
(
key
:
ByteArray
):
ByteArray
=
TEA
.
decrypt
(
this
,
key
)
private
fun
ByteArray
.
decryptBy
(
key
:
String
):
ByteArray
=
TEA
.
decrypt
(
this
,
key
)
private
fun
DataInputStream
.
skipHex
(
uHex
:
String
)
{
this
.
skip
(
uHex
.
hexToBytes
().
size
.
toLong
())
}
}
fun
main
()
{
val
data
=
"FE A1 06 1C 5F 04 04 F6 E2 F9 B2 A7 48 51 A2 81 4D 92 62 7E 67 29 F8 02 A5 77 97 A5 8F F9 81 1D B3 D9 3D DB 63 5C 22 2F E1 C2 53 67 E8 CD A5 BD AB 5A FB B3 14 48 6C 0D DD 67 EE AC EB A8 08 96 28 A0 20 9F D9 52 B7 DC E5 71 18 68 58 4F E3 31 7E 74 15 A2 3E 4D 11 CA D1 7A 59 D1 EA 8C A0 18 54 E7 4D ED CC D6 4C E3 34 43 3F 20 41 93 94 9D 11 F4 51 8E 5A 3A EA A4 5B EA 69 64 AE 4F DA 16 50 89 93 82 EA B3 DB 68 80 A5 10 78 94 16 7C BC 74 C0 D0 03 C7 BA 33 BD A5 BF 3A 90 B4 FB 66 7E 54 C7 3F A4 42 BC 72 60 A9 4F F0 7A 64 E5 BB AD 59 8B E7 48 0D 0E 5A 58 99 17 77 35 52 C9 28 67 77 81 6B 7F 6F D5 CF 12 DC 31 82 39 E9 F9 6D 91 A6 C7 60 A0 3C 7C 80 29 E9 2E 05 63 BC 59 B0 73 D8 0F 84 E9 D1 88 AC 99 B8 E4 DA 8F 8F E6 F5 06 29 E8 CD 8A A8 38 24 BD 4E BF E6 79 79 9B 91 9E 16 44 FD 87 3B 6E 69 14 AF 32 A0 6E AD AF 5A C8 45 64 F3 4C 3B 20 AA 20 16 A7 FA FF D1 F2 A8 78 5F DE D5 FF 37 76 73 73 52 73 91 32 0D 1C 35 4E 8A 21 29 C2 D7 87 55 B3 6D 65 F6 ED 6D 9E 6A 9E DC 46 6A F9 CC 38 09 72 7A B8 84 D1 4C 76 8B CB 2E AA 05 2A B3 31 0C F3 70 2B 34 70 7F BC 5D 8E 65 4E 91 16 77 CB 7A 07 CE 37 CF 42 D0 99 C6 14 5A 11 B1 7D 1C 7B 9B F4 31 FE 91 0C B0 FD 7B 9D 4B 9D D7 34 CC 1B F3 E0 ED 5B BC 71 D9 D5 D5 A8 83 A9 3E BF 2F A6 90 FB 51 9F 72 CC 0C A5 36 A6 05 55 0C 3F 93 6C 0F DF EA 43 E1 F3 51 10 02 5D 75 F0 83 C6 BD 06 21 6B 07 D6 6E 3A CB 20 21 60 89 3A 77 0E EB 86 F7 45 BE B8 54 5C 3A 45 3A 86 19 A9 75 E6 9C 50 3D 36 F1 51 1E B5 97 41 86 CF F0 6F 0C 0F 7E CF E4 E3 50 F2 6A 19 0A A2 CB 74 88 8C D6 62 EC EC 66 1F 87 D3 6F 1C 83 94 79 CE C9 15 66 07 12 AE A7 9A D9 D1 F2 90 F8 56 28 E7 6E 33 AF 8D 58 3D 8A 7C 49 94 A0 E8 8B 48 77 89 B6 78 13 44 5C A0 D9 A5"
.
hexToBytes
()
println
(
TEA
.
decrypt
(
data
,
"E4 23 72 92 79 9C 9C 96 28 9D AF 5C 1D 33 D2 7F"
.
hexToBytes
()))
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment