mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-13 03:16:05 +08:00
updated
This commit is contained in:
parent
b240b90851
commit
fbfbea59df
@ -1,33 +0,0 @@
|
||||
# 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
|
@ -1,96 +0,0 @@
|
||||
# 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`
|
@ -1,23 +0,0 @@
|
||||
# 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
|
@ -1,124 +0,0 @@
|
||||
# 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
|
@ -1,41 +0,0 @@
|
||||
# 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)
|
@ -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>
|
||||
|
@ -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 tgtgtKey: ByteArray = getRandomByteArray(16)
|
||||
private var randomTgtgtKey: 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.tgtgtKey = getRandomByteArray(16)
|
||||
this.randomTgtgtKey = getRandomByteArray(16)
|
||||
this.token00BA = packet.token00BA
|
||||
socket.sendPacket(ClientLoginResendPacket3105(bot.account.qqNumber, bot.account.password, this.loginTime, this.loginIP, this.tgtgtKey, this.token0825, this.token00BA))
|
||||
socket.sendPacket(ClientLoginResendPacket3105(bot.account.qqNumber, bot.account.password, this.loginTime, this.loginIP, this.randomTgtgtKey, 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.tgtgtKey = packet.tgtgtKey
|
||||
socket.sendPacket(ClientLoginResendPacket3104(bot.account.qqNumber, bot.account.password, loginTime, loginIP, tgtgtKey, token0825, packet.tokenUnknown
|
||||
this.randomTgtgtKey = packet.tgtgtKey
|
||||
socket.sendPacket(ClientLoginResendPacket3104(bot.account.qqNumber, bot.account.password, loginTime, loginIP, randomTgtgtKey, token0825, packet.tokenUnknown
|
||||
?: this.token00BA, packet.tlv0006))
|
||||
} else {
|
||||
socket.sendPacket(ClientLoginResendPacket3106(bot.account.qqNumber, bot.account.password, loginTime, loginIP, tgtgtKey, token0825, packet.tokenUnknown
|
||||
socket.sendPacket(ClientLoginResendPacket3106(bot.account.qqNumber, bot.account.password, loginTime, loginIP, randomTgtgtKey, 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.tgtgtKey))
|
||||
is ServerLoginResponseSuccessPacket.Encrypted -> socket.distributePacket(packet.decrypt(this.tgtgtKey))
|
||||
is ServerLoginResponseKeyExchangePacket.Encrypted -> socket.distributePacket(packet.decrypt(this.randomTgtgtKey))
|
||||
is ServerLoginResponseSuccessPacket.Encrypted -> socket.distributePacket(packet.decrypt(this.randomTgtgtKey))
|
||||
is ServerSessionKeyResponsePacket.Encrypted -> socket.distributePacket(packet.decrypt(this.sessionResponseDecryptionKey))
|
||||
is ServerTouchResponsePacket.Encrypted -> socket.distributePacket(packet.decrypt())
|
||||
|
||||
|
@ -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,15 +77,22 @@ object Protocol {
|
||||
/**
|
||||
* 0836_622_fix2
|
||||
*/
|
||||
const val passwordSubmissionKey2 = "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 passwordSubmissionTLV2 = "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 (?)
|
||||
@ -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())
|
||||
}
|
||||
}
|
@ -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))
|
||||
|
||||
|
@ -244,10 +244,9 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
|
||||
input.skip(2)//2个0x00
|
||||
message = input.readSections()
|
||||
|
||||
val map: Map<Int, ByteArray> = input.readTLVMap(true)
|
||||
println(map[18])
|
||||
val map: Map<Int, ByteArray> = input.readTLVMap(true).withDefault { byteArrayOf() }
|
||||
println(map.getValue(18))
|
||||
|
||||
//todo 后面有昵称可读
|
||||
//19 00 38 01 00 35 AA 02 32 50 03 60 00 68 00 9A 01 29 08 09 20 BF 02 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 B8 03 00 C0 03 00 D0 03 00 E8 03 00 12 00 25 01 00 09 48 69 6D 31 38 38 6D 6F 65 03 00 01 04 04 00 04 00 00 00 08 05 00 04 00 00 00 01 08 00 04 00 00 00 01
|
||||
|
||||
/*
|
||||
@ -272,6 +271,7 @@ private fun DataInputStream.readSection(): Message? {
|
||||
|
||||
0x19 -> {//长文本
|
||||
val value = readLVByteArray()
|
||||
//todo 未知压缩算法
|
||||
PlainText(String(value))
|
||||
|
||||
// PlainText(String(GZip.uncompress( value)))
|
||||
@ -282,11 +282,12 @@ private fun DataInputStream.readSection(): Message? {
|
||||
val value = readLVByteArray()
|
||||
println(value.size)
|
||||
println(value.toUHexString())
|
||||
//todo 未知压缩算法
|
||||
this.skip(7)//几个TLV
|
||||
return PlainText(String(value))
|
||||
}
|
||||
|
||||
0x0E -> {//可能是结尾标志?
|
||||
0x0E -> {
|
||||
//null
|
||||
null
|
||||
}
|
||||
|
@ -292,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()
|
||||
|
@ -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() {
|
||||
|
||||
|
@ -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.writeHex("00 10")
|
||||
it.writeHex(Protocol.key00BAFix)
|
||||
it.writeShort(16)
|
||||
it.writeHex(Protocol.key00BAFix)//16
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
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()
|
||||
}
|
||||
}.toByteArray(), Protocol.shareKey.hexToBytes()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.passwordSubmissionKey2)
|
||||
this.writeHex(Protocol.passwordSubmissionTLV2)
|
||||
this.writeHex("00 1A")//tag
|
||||
this.writeHex("00 40")//length
|
||||
this.write(TEA.encrypt(Protocol.passwordSubmissionKey2.hexToBytes(), tgtgtKey))
|
||||
this.write(TEA.encrypt(Protocol.passwordSubmissionTLV2.hexToBytes(), tgtgtKey))
|
||||
this.writeHex(Protocol.constantData1)
|
||||
this.writeHex(Protocol.constantData2)
|
||||
this.writeQQ(qq)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
206
mirai-core/src/test/java/PacketDebuger.kt
Normal file
206
mirai-core/src/test/java/PacketDebuger.kt
Normal file
@ -0,0 +1,206 @@
|
||||
@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()))
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user