diff --git a/document/protocol/Get_tlv_0006.md b/document/protocol/Get_tlv_0006.md deleted file mode 100644 index b9dbe0c5a..000000000 --- a/document/protocol/Get_tlv_0006.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/document/protocol/Login Flow.md b/document/protocol/Login Flow.md deleted file mode 100644 index 86d670a16..000000000 --- a/document/protocol/Login Flow.md +++ /dev/null @@ -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` \ No newline at end of file diff --git a/document/protocol/Password Verified.md b/document/protocol/Password Verified.md deleted file mode 100644 index 4fcc00a72..000000000 --- a/document/protocol/Password Verified.md +++ /dev/null @@ -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 \ No newline at end of file diff --git a/document/protocol/Redirection.md b/document/protocol/Redirection.md deleted file mode 100644 index 856d83e0b..000000000 --- a/document/protocol/Redirection.md +++ /dev/null @@ -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 diff --git a/document/protocol/Touch.md b/document/protocol/Touch.md deleted file mode 100644 index 469b6da68..000000000 --- a/document/protocol/Touch.md +++ /dev/null @@ -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) \ No newline at end of file diff --git a/mirai-core/pom.xml b/mirai-core/pom.xml index bff6e2c4d..f9b50986f 100644 --- a/mirai-core/pom.xml +++ b/mirai-core/pom.xml @@ -17,6 +17,15 @@ + + + jpcap + jpcap + 0.1.18-002 + system + ${project.basedir}/lib/jpcap.jar + + org.pcap4j pcap4j-core diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/BotNetworkHandlerImpl.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/BotNetworkHandlerImpl.kt index b22c10713..e0887ba44 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/BotNetworkHandlerImpl.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/BotNetworkHandlerImpl.kt @@ -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()) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt index 098f65ca0..52dfe61cd 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt @@ -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()) + } +} \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt index d0d048630..a20c1e81e 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt @@ -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)) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt index 966938b4d..fea131438 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt @@ -244,10 +244,9 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray input.skip(2)//2个0x00 message = input.readSections() - val map: Map = input.readTLVMap(true) - println(map[18]) + val map: Map = 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 } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt index c34996350..f51196cd0 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt @@ -292,6 +292,10 @@ fun DataInputStream.readUIntAt(position: N): UInt { return this.readNBytes(4).toUInt() } +fun DataInputStream.readUInt(): UInt { + return this.readNBytes(4).toUInt() +} + fun DataInputStream.readByteAt(position: N): Byte { this.goto(position) return this.readByte() diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt index 1b9945670..f02c0a1b1 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt @@ -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() { diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt index 484a19e42..02ed89c4e 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt @@ -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 } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt index cb6807a56..d6ab27eb2 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt @@ -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.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) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt index 852e96cb0..7f2487298 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt @@ -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 } } diff --git a/mirai-core/src/test/java/PacketDebuger.kt b/mirai-core/src/test/java/PacketDebuger.kt new file mode 100644 index 000000000..3caef2250 --- /dev/null +++ b/mirai-core/src/test/java/PacketDebuger.kt @@ -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) { + /*-------------- 第一步绑定网络设备 --------------*/ + 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