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
932a3ef1
Commit
932a3ef1
authored
Feb 28, 2020
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Review: misc improvements
parent
1ff5df1d
Changes
34
Hide whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
676 additions
and
1411 deletions
+676
-1411
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt
...Main/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt
+1
-1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
...tlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
+9
-6
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
.../mirai/qqandroid/network/protocol/packet/PacketFactory.kt
+10
-8
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
...ndroid/network/protocol/packet/chat/receive/OnlinePush.kt
+2
-5
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
.../mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
+17
-20
mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt
mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt
+55
-0
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt
...d/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt
+0
-533
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt
...d/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt
+0
-297
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/utils.kt
...-qqandroid/src/jvmTest/kotlin/androidPacketTests/utils.kt
+0
-66
mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
....mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
+1
-1
mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt
mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt
+102
-0
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt
...n/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt
+28
-18
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
...androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
+7
-1
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
...n/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
+0
-46
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt
...et/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt
+0
-2
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
...droidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
+1
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
+8
-10
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
.../net.mamoe.mirai/event/internal/InternalEventListeners.kt
+1
-2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt
...monMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt
+0
-6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
...mmonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
+5
-6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
...e/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
+4
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/contentToString.kt
...ommonMain/kotlin/net.mamoe.mirai/utils/contentToString.kt
+167
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt
...rc/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt
+57
-94
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt
...mmonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt
+8
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt
...otlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt
+0
-6
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
.../src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
+0
-150
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
...src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
+142
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt
...e/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt
+14
-32
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
.../src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
+4
-2
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
...mMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
+25
-40
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
.../jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
+1
-0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
...n/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
+0
-54
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt
...Main/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt
+0
-2
mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt
.../kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt
+7
-3
No files found.
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt
View file @
932a3ef1
...
...
@@ -10,6 +10,7 @@
package
net.mamoe.mirai.qqandroid.message
import
kotlinx.io.core.buildPacket
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.readBytes
import
kotlinx.io.core.readUInt
import
net.mamoe.mirai.contact.Member
...
...
@@ -19,7 +20,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
import
net.mamoe.mirai.utils.ExternalImage
import
net.mamoe.mirai.utils.MiraiDebugAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.io.discardExact
import
net.mamoe.mirai.utils.io.hexToBytes
import
net.mamoe.mirai.utils.io.read
import
net.mamoe.mirai.utils.io.toByteArray
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
View file @
932a3ef1
...
...
@@ -7,12 +7,13 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"NOTHING_TO_INLINE"
,
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.qqandroid.network
import
kotlinx.atomicfu.AtomicInt
import
kotlinx.atomicfu.atomic
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.toByteArray
import
kotlinx.io.core.*
import
net.mamoe.mirai.BotAccount
import
net.mamoe.mirai.RawAccountIdUse
import
net.mamoe.mirai.data.OnlineStatus
...
...
@@ -20,12 +21,10 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import
net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
import
net.mamoe.mirai.qqandroid.network.protocol.packet.Tlv
import
net.mamoe.mirai.utils.DeviceInfo
import
net.mamoe.mirai.qqandroid.utils.NetworkType
import
net.mamoe.mirai.utils.SystemDeviceInfo
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.cryptor.ECDH
import
net.mamoe.mirai.utils.cryptor.
decryptBy
import
net.mamoe.mirai.utils.cryptor.
TEA
import
net.mamoe.mirai.utils.io.*
/*
...
...
@@ -72,7 +71,7 @@ internal open class QQAndroidClient(
internal
inline
fun
<
R
>
tryDecryptOrNull
(
data
:
ByteArray
,
size
:
Int
=
data
.
size
,
mapper
:
(
ByteArray
)
->
R
):
R
?
{
keys
.
forEach
{
(
key
,
value
)
->
kotlin
.
runCatching
{
return
mapper
(
data
.
decryptBy
(
value
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 $key 解密"
}
})
return
mapper
(
TEA
.
decrypt
(
data
,
value
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 $key 解密"
}
})
}
}
return
null
...
...
@@ -314,6 +313,10 @@ internal class Pt4Token(data: ByteArray, creationTime: Long, expireTime: Long) :
internal
typealias
PSKeyMap
=
MutableMap
<
String
,
PSKey
>
internal
typealias
Pt4TokenMap
=
MutableMap
<
String
,
Pt4Token
>
internal
inline
fun
Input
.
readUShortLVString
():
String
=
kotlinx
.
io
.
core
.
String
(
this
.
readUShortLVByteArray
())
internal
inline
fun
Input
.
readUShortLVByteArray
():
ByteArray
=
this
.
readBytes
(
this
.
readUShort
().
toInt
())
internal
fun
parsePSKeyMapAndPt4TokenMap
(
data
:
ByteArray
,
creationTime
:
Long
,
expireTime
:
Long
,
outPSKeyMap
:
PSKeyMap
,
outPt4TokenMap
:
Pt4TokenMap
)
=
data
.
read
{
repeat
(
readShort
().
toInt
())
{
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
View file @
932a3ef1
...
...
@@ -25,9 +25,10 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.login.Heartbeat
import
net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
import
net.mamoe.mirai.qqandroid.network.protocol.packet.login.WtLogin
import
net.mamoe.mirai.qqandroid.network.readUShortLVByteArray
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.cryptor.TEA
import
net.mamoe.mirai.utils.cryptor.adjustToPublicKey
import
net.mamoe.mirai.utils.cryptor.decryptBy
import
net.mamoe.mirai.utils.io.*
import
kotlin.contracts.ExperimentalContracts
import
kotlin.contracts.contract
...
...
@@ -194,8 +195,8 @@ internal object KnownPacketFactories {
kotlin
.
runCatching
{
when
(
flag2
)
{
2
->
data
.
decryptBy
(
DECRYPTER_16_ZERO
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 16 zero 解密"
}
}
1
->
data
.
decryptBy
(
bot
.
client
.
wLoginSigInfo
.
d2Key
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 d2Key 解密"
}
}
2
->
TEA
.
decrypt
(
data
,
DECRYPTER_16_ZERO
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 16 zero 解密"
}
}
1
->
TEA
.
decrypt
(
data
,
bot
.
client
.
wLoginSigInfo
.
d2Key
,
size
).
also
{
PacketLogger
.
verbose
{
"成功使用 d2Key 解密"
}
}
0
->
data
else
->
error
(
""
)
}
...
...
@@ -335,6 +336,7 @@ internal object KnownPacketFactories {
return
IncomingPacket
(
packetFactory
,
ssoSequenceId
,
packet
,
commandName
)
}
@UseExperimental
(
MiraiInternalAPI
::
class
)
private
suspend
fun
<
T
:
Packet
?
>
ByteReadPacket
.
parseOicqResponse
(
bot
:
QQAndroidBot
,
packetFactory
:
OutgoingPacketFactory
<
T
>,
...
...
@@ -352,10 +354,10 @@ internal object KnownPacketFactories {
this
.
discardExact
(
1
)
// const = 0
val
packet
=
when
(
encryptionMethod
)
{
4
->
{
var
data
=
this
.
decryptBy
(
bot
.
client
.
ecdh
.
keyPair
.
initialShareKey
,
(
this
.
remaining
-
1
).
toInt
())
var
data
=
TEA
.
decrypt
(
this
,
bot
.
client
.
ecdh
.
keyPair
.
initialShareKey
,
(
this
.
remaining
-
1
).
toInt
())
val
peerShareKey
=
bot
.
client
.
ecdh
.
calculateShareKeyByPeerPublicKey
(
readUShortLVByteArray
().
adjustToPublicKey
())
data
=
data
.
decryptBy
(
peerShareKey
)
data
=
TEA
.
decrypt
(
data
,
peerShareKey
)
packetFactory
.
decode
(
bot
,
data
)
}
...
...
@@ -366,13 +368,13 @@ internal object KnownPacketFactories {
this
.
readFully
(
byteArrayBuffer
,
0
,
size
)
runCatching
{
byteArrayBuffer
.
decryptBy
(
bot
.
client
.
ecdh
.
keyPair
.
initialShareKey
,
size
)
TEA
.
decrypt
(
byteArrayBuffer
,
bot
.
client
.
ecdh
.
keyPair
.
initialShareKey
,
size
)
}.
getOrElse
{
byteArrayBuffer
.
decryptBy
(
bot
.
client
.
randomKey
,
size
)
TEA
.
decrypt
(
byteArrayBuffer
,
bot
.
client
.
randomKey
,
size
)
}.
toReadPacket
()
}
}
else
{
this
.
decryptBy
(
bot
.
client
.
randomKey
,
0
,
(
this
.
remaining
-
1
).
toInt
())
TEA
.
decrypt
(
this
,
bot
.
client
.
randomKey
,
0
,
(
this
.
remaining
-
1
).
toInt
())
}
packetFactory
.
decode
(
bot
,
data
)
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
View file @
932a3ef1
...
...
@@ -11,10 +11,7 @@
package
net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.readBytes
import
kotlinx.io.core.readUByte
import
kotlinx.io.core.readUInt
import
kotlinx.io.core.*
import
net.mamoe.mirai.contact.MemberPermission
import
net.mamoe.mirai.data.MultiPacket
import
net.mamoe.mirai.data.NoPacket
...
...
@@ -38,7 +35,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import
net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.debug
import
net.mamoe.mirai.utils.io.discardExact
import
net.mamoe.mirai.utils.io.read
import
net.mamoe.mirai.utils.io.readString
import
net.mamoe.mirai.utils.io.toUHexString
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
View file @
932a3ef1
...
...
@@ -20,14 +20,9 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import
net.mamoe.mirai.qqandroid.utils.GuidSource
import
net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag
import
net.mamoe.mirai.qqandroid.utils.guidFlag
import
net.mamoe.mirai.utils.MiraiDebugAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.cryptor.contentToString
import
net.mamoe.mirai.utils.cryptor.decryptBy
import
net.mamoe.mirai.utils.currentTimeSeconds
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.cryptor.TEA
import
net.mamoe.mirai.utils.io.*
import
net.mamoe.mirai.utils.io.discardExact
import
net.mamoe.mirai.utils.md5
internal
class
WtLogin
{
/**
...
...
@@ -317,7 +312,7 @@ internal class WtLogin {
// println("type=$type")
discardExact
(
2
)
val
tlvMap
:
TlvMap
=
this
.
readTLVMap
()
val
tlvMap
:
TlvMap
=
this
.
_
readTLVMap
()
// tlvMap.printTLVMap()
return
when
(
type
.
toInt
())
{
0
->
onLoginSuccess
(
tlvMap
,
bot
)
...
...
@@ -325,7 +320,7 @@ internal class WtLogin {
2
->
onSolveLoginCaptcha
(
tlvMap
,
bot
)
160
/*-96*/
->
onUnsafeDeviceLogin
(
tlvMap
)
204
/*-52*/
->
onSMSVerifyNeeded
(
tlvMap
,
bot
)
else
->
tlvMap
[
0
x149
]
?.
let
{
analysisTlv149
(
it
)
}
?:
error
(
"unknown login result type: $type, TLVMap = ${tlvMap.
c
ontentToString()}"
)
else
->
tlvMap
[
0
x149
]
?.
let
{
analysisTlv149
(
it
)
}
?:
error
(
"unknown login result type: $type, TLVMap = ${tlvMap.
_miraiC
ontentToString()}"
)
}
}
...
...
@@ -393,7 +388,7 @@ internal class WtLogin {
// } else error("UNKNOWN CAPTCHA QUESTION: ${question.toUHexString()}, tlvMap=" + tlvMap.contentToString())
}
error
(
"UNKNOWN CAPTCHA, tlvMap="
+
tlvMap
.
c
ontentToString
())
error
(
"UNKNOWN CAPTCHA, tlvMap="
+
tlvMap
.
_miraiC
ontentToString
())
}
@UseExperimental
(
MiraiDebugAPI
::
class
)
...
...
@@ -405,17 +400,17 @@ internal class WtLogin {
// tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") }
tlvMap
[
0
x161
]
?.
let
{
client
.
analysisTlv161
(
it
)
}
tlvMap
[
0
x119
]
?.
let
{
t119Data
->
t119Data
.
decryptBy
(
client
.
tgtgtKey
).
read
{
TEA
.
decrypt
(
t119Data
,
client
.
tgtgtKey
).
read
{
discardExact
(
2
)
// always discarded. 00 1C
// 00 1C
// 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00
val
tlvMap119
=
this
.
readTLVMap
()
val
tlvMap119
=
this
.
_
readTLVMap
()
// ???
tlvMap119
[
0
x1c
]
?.
read
{
val
bytes
=
readBytes
()
DebugLogger
.
warning
(
bytes
.
toUHexString
())
DebugLogger
.
warning
(
bytes
.
encodeToString
())
bot
.
network
.
logger
.
debug
(
"onLoginSuccess, tlvMap119[0x1c]: "
+
bytes
.
toUHexString
())
bot
.
network
.
logger
.
debug
(
"onLoginSuccess, tlvMap119[0x1c]: "
+
bytes
.
encodeToString
())
}
tlvMap119
[
0
x130
]
?.
let
{
client
.
analysisTlv130
(
it
)
}
...
...
@@ -490,7 +485,7 @@ internal class WtLogin {
face
=
readUShort
().
toInt
()
age
=
readUByte
().
toInt
()
gender
=
readUByte
().
toInt
()
nick
=
read
UByteLVString
(
)
nick
=
read
String
(
readByte
().
toInt
()
and
0
xff
)
}
}
...
...
@@ -605,7 +600,7 @@ internal class WtLogin {
}
private
inline
fun
analysisTlv0x531
(
t531
:
ByteArray
,
handler
:
(
a1
:
ByteArray
,
noPicSig
:
ByteArray
)
->
Unit
)
{
val
map
=
t531
.
toReadPacket
().
readTLVMap
()
val
map
=
t531
.
toReadPacket
().
_
readTLVMap
()
val
t106
=
map
[
0
x106
]
val
t16a
=
map
[
0
x16a
]
...
...
@@ -621,7 +616,7 @@ internal class WtLogin {
* @throws error
*/
private
fun
QQAndroidClient
.
parseWFastLoginInfoDataOutA1
(
t169
:
ByteArray
):
ByteReadPacket
{
val
map
=
t169
.
toReadPacket
().
readTLVMap
()
val
map
=
t169
.
toReadPacket
().
_
readTLVMap
()
val
t106
=
map
[
0
x106
]
val
t10c
=
map
[
0
x10c
]
...
...
@@ -656,7 +651,7 @@ internal class WtLogin {
//discardExact(2)
loginExtraData
=
LoginExtraData
(
// args are to correct order
uin
=
readUInt
().
toLong
(),
ip
=
read
UByteLVByteArray
(
),
ip
=
read
Bytes
(
readByte
().
toInt
()
and
0
xff
),
time
=
readInt
(),
// correct
version
=
readInt
()
)
...
...
@@ -699,7 +694,7 @@ internal class WtLogin {
}
private
fun
QQAndroidClient
.
analysisTlv161
(
t161
:
ByteArray
)
{
val
tlv
=
t161
.
toReadPacket
().
apply
{
discardExact
(
2
)
}.
readTLVMap
()
val
tlv
=
t161
.
toReadPacket
().
apply
{
discardExact
(
2
)
}.
_
readTLVMap
()
tlv
[
0
x173
]
?.
let
{
analysisTlv173
(
it
)
}
tlv
[
0
x17f
]
?.
let
{
analysisTlv17f
(
it
)
}
...
...
@@ -750,4 +745,6 @@ internal class WtLogin {
}
}
}
}
\ No newline at end of file
}
private
fun
Input
.
readUShortLVString
():
String
=
String
(
this
.
readUShortLVByteArray
())
\ No newline at end of file
mirai-core
/src/commonMain/kotlin/net.mamoe.mirai/utils/io/debugg
ing.kt
→
mirai-core
-qqandroid/src/commonTest/kotlin/test/print
ing.kt
View file @
932a3ef1
...
...
@@ -8,81 +8,34 @@
*/
@
file
:
Suppress
(
"NOTHING_TO_INLINE"
)
@
file
:
JvmMultifileClass
@
file
:
JvmName
(
"Utils"
)
package
net.mamoe.mirai.utils.io
package
test
import
kotlinx.io.core.*
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.Input
import
kotlinx.io.core.readAvailable
import
kotlinx.io.core.use
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.utils.DefaultLogger
import
net.mamoe.mirai.utils.Mirai
Debug
API
import
net.mamoe.mirai.utils.Mirai
Internal
API
import
net.mamoe.mirai.utils.MiraiLoggerWithSwitch
import
net.mamoe.mirai.utils.io.ByteArrayPool
import
net.mamoe.mirai.utils.io.toReadPacket
import
net.mamoe.mirai.utils.io.toUHexString
import
net.mamoe.mirai.utils.withSwitch
import
kotlin.contracts.ExperimentalContracts
import
kotlin.contracts.InvocationKind
import
kotlin.contracts.contract
import
kotlin.jvm.JvmMultifileClass
import
kotlin.jvm.JvmName
@MiraiDebugAPI
(
"Unsatble"
)
val
DebugLogger
:
MiraiLoggerWithSwitch
=
DefaultLogger
(
"Packet Debug"
).
withSwitch
(
false
)
val
DebugLogger
:
MiraiLoggerWithSwitch
=
DefaultLogger
(
"Packet Debug"
).
withSwitch
(
true
)
@MiraiDebugAPI
(
"Unstable"
)
inline
fun
Throwable
.
logStacktrace
(
message
:
String
?
=
null
)
=
DebugLogger
.
error
(
message
,
this
)
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
String
.
debugPrintThis
(
name
:
String
):
String
{
DebugLogger
.
debug
(
"$name=$this"
)
return
this
}
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
ByteArray
.
debugPrintThis
(
name
:
String
):
ByteArray
{
DebugLogger
.
debug
(
name
+
"="
+
this
.
toUHexString
())
return
this
}
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
IoBuffer
.
debugPrintThis
(
name
:
String
):
IoBuffer
{
ByteArrayPool
.
useInstance
{
val
count
=
this
.
readAvailable
(
it
)
DebugLogger
.
debug
(
name
+
"="
+
it
.
toUHexString
(
offset
=
0
,
length
=
count
))
return
it
.
toIoBuffer
(
0
,
count
)
}
}
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
IoBuffer
.
debugCopyUse
(
block
:
IoBuffer
.()
->
Unit
):
IoBuffer
{
ByteArrayPool
.
useInstance
{
val
count
=
this
.
readAvailable
(
it
)
block
(
it
.
toIoBuffer
(
0
,
count
))
return
it
.
toIoBuffer
(
0
,
count
)
}
}
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
Input
.
debugDiscardExact
(
n
:
Number
,
name
:
String
=
""
)
{
DebugLogger
.
debug
(
"Discarded($n) $name="
+
this
.
readBytes
(
n
.
toInt
()).
toUHexString
())
}
@MiraiDebugAPI
(
"Low efficiency."
)
inline
fun
ByteReadPacket
.
debugPrintThis
(
name
:
String
=
""
):
ByteReadPacket
{
ByteArrayPool
.
useInstance
{
val
count
=
this
.
readAvailable
(
it
)
DebugLogger
.
debug
(
"ByteReadPacket $name="
+
it
.
toUHexString
(
offset
=
0
,
length
=
count
))
return
it
.
toReadPacket
(
0
,
count
)
}
}
/**
* 备份数据, 并在 [block] 失败后执行 [onFail].
*
* 此方法非常低效. 请仅在测试环境使用.
*/
@MiraiDebugAPI
(
"Low efficiency"
)
@UseExperimental
(
ExperimentalContracts
::
class
)
@UseExperimental
(
ExperimentalContracts
::
class
,
MiraiInternalAPI
::
class
)
inline
fun
<
R
>
Input
.
debugIfFail
(
name
:
String
=
""
,
onFail
:
(
ByteArray
)
->
ByteReadPacket
=
{
it
.
toReadPacket
()
},
block
:
ByteReadPacket
.()
->
R
):
R
{
contract
{
...
...
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt
deleted
100644 → 0
View file @
1ff5df1d
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"EXPERIMENTAL_UNSIGNED_LITERALS"
)
package
androidPacketTests
import
kotlinx.io.core.*
import
net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
import
net.mamoe.mirai.utils.cryptor.ECDH
import
net.mamoe.mirai.utils.cryptor.contentToString
import
net.mamoe.mirai.utils.cryptor.decryptBy
import
net.mamoe.mirai.utils.cryptor.initialPublicKey
import
net.mamoe.mirai.utils.io.*
import
net.mamoe.mirai.utils.io.discardExact
import
net.mamoe.mirai.utils.md5
import
kotlin.text.toByteArray
// sessionTicket = 55 F7 24 8B 04 4E AA A8 98 E6 77 D2 D6 54 A9 B4 43 91 94 A3 0D DA CF 8F E8 94 E0 F4 A2 6B B4 8B 2B 4F 78 8D 21 EE D4 95 A6 F7 A4 3D B5 87 9B 3D
// sessionTicketKey = B6 9D E4 EC 65 38 64 FD C8 3A D8 33 54 35 0C 73
// randomKey = A4 9A 6A EE 17 5B 7E 3D C0 71 DA 04 1C E1 E4 88
// login send 20da22db750806141ef448110800450005aa50ef400080060000c0a8030a71600dd0fe501f908b8c28a908ce7556501001fb487f00000000058c0000000a0200000004000000000e313939343730313032312b87bf2f7c22eb2396f80b887dc6410b85b6f1fc05874d1331d8722ec60d01a47b9ad330945d474934331ec4de12913e927bae5284862329eef2675a122dc83c00407ca3d4e734d772aaae33c4f17b6833a79e12a2ee549a17b08c7e4034874584f3e35e69c55e995e079ed1d0d877be9b1c9a03e62d0f3d714a1506d4a0292a853c6d28b582f5c715f5b6a6d051446b376cd9b55a311b42ba66f20080dc54a429fd240cdc9089534ffc3bcd02ab15f61b86467268e2e743a81f5709c07af6a7b03a90fb1d742988c13fa0b4b946079f9b9b2034fd8a330bc43fe8b2396ec3cac57d6753db28eb3af9e1904e8d4efbe38a354fd1aa71626a8f124498b24d9983d037e5a9f5ad44833df03335e4268cc9489366e793a84f4ed892a7d78345abce6b2888fe081b0317343a7e3bf6dafbe2c1caa0eb956637cf475a16ff1105b10c798a4ee68ea817d6edb0790aba884ced9f638b68cbfeb527d4178769efd533ba032e332f841fc3ede8b50da138470c1118fdb5070eda09215252e7fbca110d6fd4269aca30ed17e642f93caa5b5ef4b1a4abcbb91416e0388f2e625c1a362936360e5935a03a78433a44a56abc936670d44c350a88d0c2e82e092009e6262c1cbd523e6573aa9de5e304b5fc823877fcc4d4c06a08acc62b1b538c9692a70234985347eae2b00956408e2ab1a08a494118f1508364172a0d09eadfcf876855a41894767dd407f75875de1f21f9cdafc6cf8322f62115b721273659995c82987a6e972af56fa0cdd2cde2fe5de5771cafa1db1f70d5c1d70caa9361506533c0495a6f016c0e24fe196ff8a6f288de96255e6e811d6528b26a6c348b99f61b80992033406a05d005c984802ad055360d4e406090b96189c2733436ec3572fe24d6c516214f1a88e46798e2a5244428e61335d6f3f76a8d459715c172016686630d6e20ea6249ea27e4d8d0ae688c79dfc802257614b22f34232eb62d361df629957a05daf743fc8f0e14802035b7534a63628bd10bf128b4ae2ae067a2f6f6f5b62c772ff2afc09a5735e7fd3550886584d99694d001bda4ff945cf0a160b6c21ece0e7fda622ad74d1d15bc6f5173f44d7cbf25d320834e794e7a581350c2330a429a350499335cd1997db163d83de42c3adea77209e4620850900e5dd54a75c393dc89d686ea576fa264d382ead875c30de4462cb32c5f16917d0e2bbad2767a3c4de1a0318a073974f80277b1b9f25af00d0a966511f60de1d4eaa0c576cc27cbc1d910a6dd6c4bfa3ca66bb83cb0f3d791138df8c8afd5d8e8a2635c3d59cbb35d340fb881ad5d57bccbe1042f26ae4dff6b5347938cf5d2cda08f2beef38d666db5333b9168c677acf537b9a37d380ac8c8b4a8da11f5f118316b29f0a465f3616edaa58a0a5360d4b7f503db7685d3b3b702db3b5fb4e1bd8ecd674a1a2d8c85bf0c8c233f3255347991737c2f080f36f3012bfcd9814a205cb5b1ac0d6a99c6f43078d74d4babed46470cde8cf1839f75306d41278020aa94001b9eef595b57a568c03b4f59a5a59d3ac37609d896db7f88aed3a1f3bdc35a7035bcf8249758f41af966e33eb99a33972b5a8f3c1b8e34a27cfb59840a96b944eea462389b5e0c1762d039968bb707874bbbdafa539fbfcfcce58bcfbb913f2cef7a03ace5cdc14c43f195e1494c15cd6af5cc5cb0a13f6e272f65ee3625894c5f950473516c79168944695e295ca3dfd04d1b91ba70eb1c612a570b7bf1ca06c8d83da20c2e442acf5b70b7b3597a802bcf8b024a765f429b964809fcb48f43d727e0606b56fa4545361f743418a042c086f6dbce97ceb5719467dcbbb759e77eebd6c97b9f4ccc6312bbd972215a58ff0a83ff1717430273e11b04751f9beaeca285546ad1d24edc9ff19a9718e4e6eb6ea80e7e16a0518a96059659dc39c50bcbd4aa64f888d3085c7c5899b3a2c2342b598d
// trans emp 1 send 20da22db750806141ef4481108004500038450f2400080060000c0a8030a71600dd0fe501f908b8c2e3508ce7dde50180200465900000000035c0000000a0200000004000000000e31393934373031303231fbb1b5c286bc9eb6a2ae43ce77353dcb0bebf522873dfc23064ea499d060fd11751986d486a6744341c3ff8ee89c30566491a5a449363549f9b917f20e9b19eb04c58d7347e51ec00be55a5e4c2433f4fd981f617726a7f74f66f6b253080103d4754ccd9474a6451123818c94b84a9613875fce9aee86c9f3879df9d0918663ea888389ddb66007827a5bf38c97a7ea6f2ef60468519679c3405444df4a334108f03ca88fdebeb3e3ed39c0b8de6d440469428ceea3fac54ccb4c620d394ec98f94534419f34ec3c2200f6d066cee9d6b3dbc6e46dc313e3863681529f1647bf9d5726747954e3ffa7515105a98bb5a9b17b92a6c56cbcff298d46b653d2f72cbc24165cc0118910aba8c56b1cb6b35b2f7df51f30965bc74cdf422611779e6d82bda537e4790a0acb3b25004fd49cfcae70cc5f52e4c267e1aeb63acf1db34a0f591282024382d99453dee4a75aa6d9e0b69fe42efd1aeb914a4324066aa65037a1c8ca151e562c0bd502f2f5eb80deff7d817ef5cb5a4a03d13f08ce1bfe4485ced084f81376b2fb83f82200725c2a9e5be2f0ebea6b9a68d8ce072c68a62be4eaab170ef94036269267f53f75ed3f6369c80c50ceb9e481c8858e077e16a8d7a80df1406e792a561f635e6a4d5e6662e2422ec88617e350b8686b17ab3c17b6a3b59f9af1519c4c73642e14b9a533073455170daa51fbed6352fa3c957038256079a4395eecc2b6712d0ecdf9a62be9191c2b7cd22dd81c78865ba57626614415f78d8b7812f107acc9110bcff90a376a52e2dd652743770df9eaa9f199bc9e66997fbe121a005c606e9e3855473452379bc4e68f30f3be75f0346c452db79076bd7a47ecc3ad1b8fb2bb796fbf6c3899f1fbc61ee1560d5e9fed4ec157e6e377098e3d7ad43997f342393472e5084b6e4c44be0f2e527f01cb3ea849621ed0108d6109992b08db4d51b13918ca59622f7c0e8389520d79ec96e7818cebd47dd2a700069c32972133cf87083c58571a74c94b2a56c3bbc6f0a94eb95218122e19763deda732536a599138a4bd0b68a59526bba99476c3a5bf065f11b5abe9fd5c74d7bc2b407a362373cb5cf243ae198185b5dc9154d36409153c79097578c8d7c1ae3629dc46c5c9c0302c61c12d05051f82381029e6affe7c65d9b66ec
// GrayUinPro.Check (UniPacket) send 20da22db750806141ef4481108004500018c50f4400080060000c0a8030a71600dd0fe501f908b8c32f508ce7dde5018020044610000000001640000000a0200000004000000000e31393934373031303231f822fc392e93d773a975a2d467d2c40df1021fa5748fd80e8e86af4f4aa9c7745671b903fcb3dea0f314b7e9543b22f02410bd5288fcf358666cb9db4d452cefde2cc9e11b27c7e2ef386a7e8b523af49340e1a9ed10c3a37e6417028f5c019272c7b8e0e1a5af0b27d005c1333777376d960bb41f419842352c2a00e4ede8c642c4f4fd1339d8e81950e9490637cacf42c3ddb5dcb0e987836e77aeb65cf50d6a0867d061b08639f72eafe7b7c5f44240a1e1a9905526bdc6037373bfa20a3fe6d38db3696381831ef1725dfafc5e65b9c1fe77a85080f1a5dfe0c4961d21cd5b70623551b5371f0b4a6d9792d0332b5611cb54e56aa4b99704b34b27a661b7775cc0d16b981c7a7b57283b803b818869d21c91b84ade0ffda282f83bf6619084ef4a17b6301d096211c7bb00768e0d481b11f4907a130f092b4e2fbefdd9570718294c52232eae
// ConfigurationService.ReqGetConfig send 20da22db750806141ef4481108004500017450f7400080060000c0a8030a71600dd0fe501f908b8c345908ce7ff6501801fe444900000000014c0000000a0100000044e0e22a59327abb9ce80cf63be86694210344fab2f2b065d7785a32cacfa4075cd509f49cb37a075ad0685cbd472344bfdef1ede611c0ad81129be9e2d7e476d2000000000e3139393437303130323110192b12a4688c94df8f36df4db7a4f485475e5166166ac2d63858b4d6c0a5748b88373db0353efe12acb0df584dae933d1b6e81f3c19dfeea5e3457db3e82523f71d153dc1e6e14e3f144b4c1fda31aac8321d2d23cad0d4365985e2bcc39e20411a9520dedc8cba4ba1580215e6a27889390a8253aa783315e9ca4dd9f637cd137b4eec24ef8384851b989dfe562e69b9ee519da64abbe9ef25507f42804bd96357a219e62e594f025303dde5901a6b662f704ca3d21e40e2593910315fd3fbb571f513f047ac4b4abf6dea7324459cc6040c6776810bcfd2dd531fc139624394ae11c0143f261ecbbb331e83e1af3
// CertifiedAccountSvc.certified_account_read.GetMainPage send 20da22db750806141ef4481108004500025c50fb400080060000c0a8030a71600dd0fe501f908b8c37d908ce913e5018020045310000000002340000000b0100014e74000000000e31393934373031303231c64688d01401061eff81d0ddcdd5a41f2cf87fa1fb2057ef139d96c1e5b27f530e1b7378df9ea59103616c09895434f33523add35ab97703588ea24da0f616cc56a745f76c9071a9408b3d71ff8dbfd7dcfb9b69897c6bf9d42aef2a237f92e0ecf036f9ae20de61d6859d6681d195ccfcaadf6d3eca4211eec7ddb6ee8764ad9061353bf1e1f943a9546a88e4842e2ade0ee6229df968c1d0a119af4bd804af16863152203526dc6057216a676a80c0eb1f678dc2d9d0ab6dd5f6420d67a9889b7559e81a2c1cff31477a9f2dc6ae79c34b6da8564e589e6cb6ed4d201a9e47d85829ec21a5bd34066f8b2bf620dec4ed59dd2268e781cb8d53301e59dccb325a8f21c4e01cd56cf7f06566287afc58a16bd53cb6724f31629a3126a38cb8a29fb58f91fed11d90faacc2607dc853940c9eae2abcb26a6f54d868bbba36bad09a895baaf1915f79ad635a6af00144eac218d0a1664f83a261db7f3500ae85a26deaccc43ee98af0fb2be07b6cdda12931c51fe880e0bfb2f219993e71b4d36f5b22a05648c4ce23d9e767f695e25e6a2a303917d50934f80c1c9dfdfcee585d240269f7145ca4392231b808f8d4836ac56e02d5ece083432ccaf71228ea76a228484f4ec80688a08e452edbb02d033b752c1311bac1132a9bc14e33078b112113d29e305fafa2ac3aaad765420e1e28ff93fbbdf8c94c6d10046f3398d29f06917fcbdcd5c68735ef0e95558312d24df95909f6a7fa4f07
// x2
// OidbSvc.0xcf8 (getShoppingCardInfo)send 20da22db750806141ef4481108004500009450fd400080060000c0a8030a71600dd0fe501f908b8c3b2908ce927e501801fe436900000000006c0000000b0100014e78000000000e31393934373031303231fde67e3ef90dfab4c8706743671f3abf660fc3f77e4efa3fabdeb96e9984107b3f4bf7677b5d34c72e88a6e212af9107e3c52c5f97b7e41c2c3cecfe9301293ab4be504d4d2eca28a816ec514deca805
// OidbSvc.0x59f (requestIsFirstLogin) send 20da22db750806141ef4481108004500008c50fe400080060000c0a8030a71600dd0fe501f908b8c3b9508ce9366501801fe43610000000000640000000b0100014e7a000000000e313939343730313032319798479335a617d6037b4351bf340716d7095f01a83a19090ced9170a5d2e45578915b186770906f21b623304ee25b7ccb52126e3ab84275d2063f4801b3ea88f4b8fd064be98e75
// friendlist.GetTroopListReqV2 send 20da22db750806141ef4481108004500011450ff400080060000c0a8030a71600dd0fe501f908b8c3bf908ce963e501801fb43e90000000000ec0000000b0100014e7b000000000e31393934373031303231e92012cde1f02af5088df9ee11e33f90ffc5416dce23cff1193985eb747ea1f453957b000b9f642708e394f590d5c8e7030064ef6f56210330319d4dd4fe21f0463095d74c6d5e21d94fcfb318c58e18d5a83ae548c03827b28c384e9a598f2ec9758ccb313fb825d44b727c10f8929682c5b6d34a8721f06e7ebb6768d39c6e1283b29bbdc15c6f35943fcc26195d1db57ae8de55d96637c7ae4caaa432bcf6768f17a39aa7f49882a86af04876146a2e44c3dcebf3ab106944cf7de19c85111add7b97c8d6bb53f773f11c09525614
// OidbSvc.0x791_0 (getRedPointInfo) send 20da22db750806141ef448110800450000bc5101400080060000c0a8030a71600dd0fe501f908b8c3ce508ce96a6501801fa43910000000000940000000b0100014e7d000000000e313939343730313032311ca9030fef4676440544c30ee8455143b3d29bf4b96fcb3923e437c30fd64795a325c777e38cd08f173f5dd6814541ca160a2de1ebdb46dcd73386cbf35ee2faae6cdac5c91aa81cf0beca175d7fdee366bacea06859de8b3e163e95d6765256fc523b7e435dc8f73d1a3f5a5c6c793706c88e103a55efa5
// OidbSvc.0x480_9 (getDetailCardInfo) send 20da22db750806141ef4481108004500008c5102400080060000c0a8030a71600dd0fe501f908b8c3d7908ce97165018020043610000000000640000000b0100014e7e000000000e3139393437303130323183063dc1abd81bf314960d1a00d2a457ff44cb694c4d17d048b888600262a1a957713055cd6e2a68a55d239e1d70d26f9a39bfc61418a8cfbdd96dac2ae1be32e34f94a0271339d8
// OidbSvc.0x5eb_15 (getHiddenSwitch) send 20da22db750806141ef4481108004500009c5105400080060000c0a8030a71600dd0fe501f908b8c3ddd08ce9e7e501801fe43710000000000740000000b0100014e7f000000000e31393934373031303231e80e3bc84057f0f3cdbfcde52cf82c5c640f69afd3680638b3bbd86bab49ddb8e29e297815f1f41ddfecfe241f76b57f999b02988fe5bb6f92da0693e27e023baea0504b7ca768c541630e636c31a01bdce3df4c27f88906
// CliLogSvc.UploadReq send 20da22db750806141ef448110800450001cc5106400080060000c0a8030a71600dd0fe501f908b8c3e5108ce9eee501801fd44a10000000001a40000000b0100014e80000000000e31393934373031303231393e687d1ca6ee2f38f879c89a930bfbd7a9d0d5d6a84e3e76574b272980e8222c35564db3952d09f452bd954248922527ac9aee51a742b3508d50d3794ce81120245bff0a4e3ac74b026fb5444e9e5a5fad8a5c9865e855ede492fa445cc27e48a27f05d81f39e7f9818890717bffeab38df651f21e884cf5a7292c0f94aa3a849cbfdc84bc7e560e371fbbc4d62c1bd134b41f66915272cee5e4094ef47a5641cdb7e19a070a6babd955a02e722b2a5fbad6adc161f58f295d9a6406dd8bd9ac14bfc520e454eb2f0422ae20beaf6789d25ca367cb22fae28488670f5871e4fa7cd8953eb854a4b53cdd8ef0406f5efbd46870afb095efb7b375f956d4f085b6e03c7f7eada6b35d3cdc6e17c2e0169177f6bc24074f1a5fe90ea54cd8af627a00cb78518e6b7f3daab0811fc0872408581f385d25efeecfa4634998ffa81d43868521467262ff08f7fc338fac60499c3452f530bce46272599401fb4cf363ff74e8813c8e12a51b6bfb992e24d3dcf1dfde34af16e6e892f0a3cf146d2d3e444b62432e5e2932
// CliLogSvc.UploadReq send 20da22db750806141ef448110800450001c45107400080060000c0a8030a71600dd0fe501f908b8c3ff508ce9eee501801fd449900000000019c0000000b0100014e81000000000e3139393437303130323128f6ceffe231f5a913fe9b4c59d24ea84d7dd8a67ef3c802f5eb2705ffeec4e98ddb95ec61e2adb2a52bcf4f744613a956a7af05be228b1dc95d40a0c4e187e79b495946e95bf2ffcabfc550c4f384269f87fea6d550a744a493cc0e11fea05cd7816fcf625b48ee6ed270e13fbacaeaef462a43dd17be5fa587e41e28fa40c083caa7e632cdcdef307404d75c30dce888de061ad715192916ec0b740c5614589b8e06683526042f8b74e896ba1049b71b8deff9354b1b2e83991d7426e2844cc592f511d9d51697361f6ef54370c4043b1bec4d7aa227804dc2b539ef95313b77561faa4d23f7392a2d18f5d796f7768af59d3faa65892169636d9c0e26a7b55be18bcb78719bd0a8ab5fa446f55e515ec5f2d0bfe9e0977ac98fe9af99f0426bc7d65066248d605eefed9b3c42f9d95d578e82515afce2fe239b857619bf64faadee7b10268bd3a079c23a413fffa209fdb9c7d23adab92e4c42fb446add76c24556d2f176f9020183814dfb2fa68861576bfcc41dfdd254d3a63dc8528b95
// OidbSvc.0x787_11 (getTroopMemberListBy0x787) send 20da22db750806141ef448110800450000a45108400080060000c0a8030a71600dd0fe501f908b8c419108ce9eee501801fd437900000000007c0000000b0100014e84000000000e31393934373031303231de7a3d7936e5679df9be661fdb98fa181243fed121a7c37e0a0a035537b27705ed59f5707bf86495d9b4781418f5c8dc36f561d8b659e2936d4549b55ec6c807650b4e5af7c79fe881ee9cb3df4b4b307748103b3b52eef06aeaf81adb25767c
// OidbSvc.0xaf6_0 (getTroopMemberListForHeadBatch) send 20da22db750806141ef44811080045000094510a400080060000c0a8030a71600dd0fe501f908b8c420d08ce9f86501801fd436900000000006c0000000b0100014e85000000000e313939343730313032311425f3e63a4e0a0454c45b48639abbe10c101b19f0a66f37e774289e87cc3c54def76486925c7291f5d37392120d7e0d30a9caba77f73808b201f49ff2a04a55c5ec502b10a6f861dbffb6eed7324b92
// OidbSvc.0xdc9 (getHostTroopHonorList) send 20da22db750806141ef448110800450000d4510b400080060000c0a8030a71600dd0fe501f908b8c427908cea04e501801fc43a90000000000ac0000000b0100014e86000000000e31393934373031303231abc2f4d9170210b0504e058282363e45527ce2ec6080f48a99d24ca40171ab146ae451517cc60d98585c746d2d00d434d3b44a9e3c9bcbe0a5ee23d406d8ae9c88b9d51beae9d05a3c22fa67f5a03bea60c17017848d32852b7e6abbe290974f74183d7af2b20d6c43c133b52ec2506f9e752676a40518cf73b922505e1e5497fb58146e526627c8fdd2c8ec4424ff96
// IncreaseURLSvr.QQHeadUrlReq, IncreaseURLSvr.QQHeadUrlReq send 20da22db750806141ef44811080045000150510c400080060000c0a8030a71600dd0fe501f908b8c432508cea116501801fb44250000000000b40000000b0100014e87000000000e31393934373031303231a3e5c904a29879bfe01d903db3522e51d9d388a6f976387267c78f937ebfb4b800cf847d9af9522bcf8b4e710d905935a904be7e144a8f85db87e784b42bdcc1f19b3dbf307b37d62ba3ee42e6145249161da0a9c608289f139e4f545b9efe40f868a9d7fc06381e59499b04da34e36aac978bb42ad6b93efa55fd63ddc18db1216c68afafc50c954caace2c159cc2ae5cdda25d4bcb5f2b000000740000000b0100014e88000000000e31393934373031303231c571c13b189ca8a129aeecfc734352ff401917c63da218afd73b3f95eaf5121a54331f4ae9534b5c5c5add7307c612fe935cbdfedb775ff1b386adba02b35984aac175015452125c1e93cb333e43c314d53cbb0201ebf0a5
// OidbSvc.0xc42 send 20da22db750806141ef448110800450004ec510d400080060000c0a8030a71600dd0fe501f908b8c444d08cea3ee5018020047c10000000004c40000000b0100014e89000000000e31393934373031303231e59b7a31945585845fec9d893236808c4868e458601b681c89bdefb2b52d4c2b4f3f3d6e6abe0e0e0fc8df28dccc8733c80a4bb9869cfd7203664227c9c485d1e22d02f7fb24944d70542610a73cdb657fb915b1ded3c1a5a52eece81fddbe4df82302b948c88b51bb4fa1bcc9af6302304ffd89ae1fa47f6b732c84c1deb3d4b1bbd6818359f066be51e868d90bd500bb5b3d65ae0cc11a40c3aa03e3aa1e1144dea014d65d1d1ac82cbf6df2415c4b0629bd07323d61f5735bc768d0bbcc7be7fd66ebd9b65232b5491a7c2b33c1079f0af971c0ff178868361cd0fb40b05e3d421145e43a92696ef394f3fbc9e1ac0c037791aa57ad1c6542195c681eb951e7e59532fc9c1eed68c96e79e720d3057f1d6ae4cf4ef253af35fdb2feadec3bac82091d8146a7022b5f2578e3f126a474e9f1cec9d987b055c9217c003931a5cf03530a0062641e6f4549cedf50ea16c75feb7035a849a3229a3663c135cd8905f17176e878fe29942a55107895041595e5b497ca8796d9489d0682e8823589965240ade202ea0efa9f84f1ebc0e25d33e3e81b3a43d7bdc75005fe8b43d09fe914c6af785ba4ad4cb40cb41f5bc622af6dc1cad1460871b8c7fdec63e08a80536ddf340e907b1eddd8fa90d2323d3e2920c7cd80fd379d633e9f8b8c526f47f9e101d6a1edeccaf15d61c893cc55e9c192cd23b7a417c3175461096146a70a08717a8e654086d8db042b674e72dc54a057a80dd7dd291b85af7d4f7ee0b8dabb8a0e58cafb411f1e501b855a2e6ea6b5a089cb8d066d5d9181d0e16d5389123f96a06d630269efe3727ccc2ca84a4b3dbd1f5d786c3a655e2b3e9311bae60acf3855e55b5e96981295193564ab0172ab8fd6fb4a6c05bff477e7800dcfa6d1e6d2face51219e69987a1a03419ce7d6a43b6216f5f3f02d87b8c9e4faf227f4509427f3b26316f3e996a4a9dae90afc693b3eb359ecd2226087305d77bfe3f6fcbb949061f7a4d1076634144fa1b3f2491ca34d75ee4d3c7a4f4ba17233b7e56bcc8c04044ee5c0d0eed5c53814a6efc0347251e47eaf5981bfc5ccc7b3c2c9e6b2d8b5c1b81f2d79d5134420f5909d0526c5d761b94c0afade2dda634f2d44b911b977630dec1c3f7aa0ed1aea422aebb5c6aef49a9873ce5df751bea10926983c43843fa5adc56077f835e84bc07fd434867e4af0d605c64c729baa77e6a22d5b58d4b9378f518b10f57a61a486a815afb0e3591f85b5c5cdb37f5fba5c89b2638b21c27cc369acd6c995932ead6ceb49fdf6122e2c09b385eb43ffbf2e81acb31057beba0700fb388374ce9fb33794b3991e9b3a18123e732b8d14e5aef01391424200dab855c12b47ade4a2c60b760cd9e3c5400d452fa040ead996a21aad6ce6fac722121f35910b39a3cc8e20543fb2539fe3fe538162f086e1de508f0f9b0cc2414b8af6800ece710f944c95031d2536470ca87083a41641392edbf976413844cd7c7d64a6ed61a2f7ce8e09a6948099d62da1f81b28fcb50424f88b1f0a9b271a7172247ff0313e0dff87ef3b2b4d965852d200e0476fb56aba5a71e5e7a2ad500e62e7f15dc165f0a6b315bffde194822420b846fd18ed2f510eb2610d5d23ccaffbded2c495224c0a215859bf03f0cb1ffc7f3373aff418326c2a98c7a44e8bffe93b
// ConfigPushSvc.GetIpDirect send 20da22db750806141ef44811080045000094510e400080060000c0a8030a71600dd0fe501f908b8c491108cea4b6501801ff436900000000006c0000000b0100014e8a000000000e31393934373031303231831dcd7b8023b2e8434f5897e7a34a13b67bcf4def875c650022a4a6b71417d9151938fe206612ae192f55d174e41aa848a02e51441700440260e2fdaf7882cfc60b2a10946dc2e76c172ddefc309389
// * friendlist.getFriendGroupList send 20da22db750806141ef448110800450001245111400080060000c0a8030a71600dd0fe501f908b8c497d08ceae5e501801fd43f90000000000fc0000000b0100014ea4000000000e31393934373031303231899eb2e0bc999ad0799e23cd538fcb600850fda949fd86368e3897c1e1b736ce01509901fc190da6c2c0eb00edd851ebad2efac593997488b51ec58d289d3a050ff1a732a616e3b4fffd40b7aca1da2d04dc0c9e1205bcc63c9473d9ed5b4386cf41d4a786f7382f94266b09db3e3993a27bff4685ffb7d9eda7f66fcf592c8c8d593aa8c00e3fae4274371e5c4b4c3c23bdbc4f6a255d1f5b66f4bf2d56798099afbb34507f516d16ae24b344607bd23c899e241048c18b227a6094cbdcb12d2a8a33b443f7a4f26bc9b3f04fd8001daa46302bdeb22bf32bdc72d06c4a7959
// * StatSvc.GetOnlineStatus send 20da22db750806141ef4481108004500008c5112400080060000c0a8030a71600dd0fe501f908b8c4a7908ceae5e501801fd43610000000000640000000b0100014ea5000000000e31393934373031303231954e34510d4f281e8676c7345c1e8d453c99aeca52c7649053a6230953ac2bca49f6e250394081be6153eceaa531aec179450ec3d671d72251db0c36cff7e1ba3b6de0e6ade53de2
// * account.RequestQueryQQMobileContactsV3 send 20da22db750806141ef448110800450001445115400080060000c0a8030a71600dd0fe501f908b8c4add08ceba36501801ff441900000000011c0000000b0100014ea9000000000e31393934373031303231a78ed17a376177e537dec3ed98b7e47b51e9be299048820b1bf3ae88d8a18324e6773ef89922a7e7cd46f24d18a6f8aa53472bfeb09000bb7c44ef5f646d8a8f9e0d04accb35183b0216bea7dee1ef8a51207f11a0681fbcfcd09364a21b16bd6974fef7d66c7eca43aea623c499f1c81fb995d11d5490e64f7929861b670e2f24dc975684ea3098f0b96aff986b4e0b6b09a45bf7f2e301fa58ba1b462e755685681c112c3d2ac369d785d56c25c12b1c66bf39396030b374400879c922a7c43d6d36cc40aa8482f079ef48355b9a2562669f5bce3d3ace4f014ce0454b158693f2727833d62d29bdc7884b165b93c57e60d443351e9fff7e093494f0c61918
// OidbSvc.0x58a (getDiscuss) send 20da22db750806141ef448110800450000945116400080060000c0a8030a71600dd0fe501f908b8c4bf908cebb5e501801fe436900000000006c0000000b0100014eaa000000000e3139393437303130323178d1c8e8c5d15a4dca043edc70fa514b19b92706d26a84f2bdb1b3e478d5ef53748b8d6082dea9a74f1c9f0ab519406c1ef36fcc0502cf201f658ecac1cd1682a34bb1ff42ac64da5f35e3af7fabcc8c
// OidbSvc.0x7c4_0, OidbSvc.0xbe8 send 20da22db750806141ef448110800450001085117400080060000c0a8030a71600dd0fe501f908b8c4c6508cebbe6501801fe43dd00000000006c0000000b0100014eab000000000e31393934373031303231a047827363fff4e4596299c74f6d847be9e8b5c2e4327bdaddffb8dd40e8df0eb76c54a6c7b7a56eddbae7136bb2b076386eb3b51091c46e756d1f9241678b656498da02276578cfb1ebfb6445513f51000000740000000b0100014eac000000000e31393934373031303231bd70b59f03df1144ae0ab45768257763ae622d5517bc20092fffaf4a5a3d55118ef6cd02907467c0eaa4a45309661277166f75e8ec4b75c0ec5fbf313e03b4a667ce26dfb0d148039a4d9ef8be7ad8cdf5e5a810edb60dd4
// OidbSvc.0x58b_0, CertifiedAccountSvc.certified_account_read.GetFollowList send 20da22db750806141ef448110800450002f05118400080060000c0a8030a71600dd0fe501f908b8c4d4508cebcd6501801fd45c500000000009c0000000b0100014ead000000000e313939343730313032311c087c0ad4a44631d3e9eb9a6ebb4456a003db2e7eaa5e27ffd4c3ee592b8de7b89b9f6a56fc8260e57eef4f3b6616a5255ab830548dd50e9a99cf87011584f3811591c35fc95cadfc364bbeb0ed337063a518aca85fd17c30ab57408389f5c071ed2626ec47b1086478164d0ae0bd0f21222eb7d4b7a5601bc160f99a8f70330000022c0000000b0100014eae000000000e313939343730313032312dd572fad1b71384d3fdb16a1c57f3cbadac7e41ae6de9d13cc2827262e4c5a4bb95829233b72b5f9076b4652c1ffd1ebba874003bcf7e3be86cdc1034ac656efde24be90a49484209c954b78dec531b3d194cc92a297efef340d8273de90a63335bf543109e96a5aa958283161e7e78f50b74b49adb176472ac82fdad36adb68b38421deba0d1c1dcf24f8342bb8d39c1181545a675abb92a3e28431e60a02aaca6c2b4f5ea79c3f4431865444932c9af983f07dd39dd6d81e47f0f59e386e5320051472fe9235f3b98b8850a9335074173730103565ddf81fb57fedb200f328d9ba16d2e52ecf1cc590382087529678501b1ab6b5866afb25b70f0d03b0773f84691e6b94e6d03c3c47f1baa4e17552fafce1bcc90e336ae0d7fca76babcb035378ce191aee9315885197a39301f319857c33cd25a017f6a0480e16030b623547216bafe886f41014f65ae2e6cf3b4c58a4ce96b8f0967c4b0e0be6a9e669c25a06e6b412f115522522af613c6b8fee922d4f3f189d3c2696521afd63fdfc4103bd827a3eb4aba949a6e07424510af29c74e3de4faa1706bf128ac7f9f66306852da2390bb6cb8ed34c885b49d577e22b86222e95142b351dec31690804b98d98baa66b405cb6d649069d9e21254f2f81a6963962955c86e6a74be6c159eb9dce9eff7eb7e0c34f864d826ececbc1e577b4498b403140983fd716ca2b04aa5c23b8c18fe28367bc5ba76081c4aafd3
// CertifiedAccountSvc.certified_account_read.GetFollowList send 20da22db750806141ef44811080045000274511c400080060000c0a8030a71600dd0fe501f908b8c500d08ceca66501801ff454900000000024c0000000b0100014eaf000000000e313939343730313032318b6198dc33ae901346ecbe82c9821a5e2e7e68d070b2baa3dc8525246a625cb7461bf329a93acc9d20b0221108cd838505d2c0e2c9b944cf7c57eb17b74e74c99c873febfc6e89395b139c033c68d66b731b0ad4d9d7444471b7638628924c3ebb4c2ed326a87b1761d5d5a72043c5e3e538d5797843333867177b5f52445e0b5fa5805cba497e5f0f7da203da3504d74977180e1b86dcdb0c3b7298c4b10d6e53a145c277033e20445a82d0fcc78a1ce597294bf767e43ca42608e7531577d185cc14f91db8f3b71b614f5299ab75c448dd7462a0ce66b1a0992db05ebbab6667ca79ff39d2e9e5f58f73eb2b47cf1896e5560ebe389289b2222687ea8af254b4518ccc6d3468dd03ebce6247e7aac4278ba99210b4c91c49c6f29fd3ecb2653eae174e7bddaf40ef743f8da7605d09d36bca5ef84ee9b75d126db616fbe2b67cdbbb26809d56e97b2e4f6175da52694f0ee640ea1990dc79e5a0c2b266ddc16d0b7bf19818ad7727add2fd2ab379ba1b41763d953d0e21dd26e2f469e8273059f5130355a0976d5a01ca438f3b698b96a2c5394b6ebe381b3818b032ee5226a1a246935a741d39fb84feb917f9680d6c7b2e950e74def6523c318cbbe592904167f2ff8dbaf10616fb2b39a5af68fa19443e874ad2a6bc0639240903fccf8d2eecaacb9404c15ec387faf459f438c229e4a571234403078d270499a2634b13baf5051979ebe31ec8fedb55854e73d58534e51a924c637ee797389de4a27da494dbe7b50c69e1c4857537d3f369752e
// AuthSvr.ThemeAuth send 20da22db750806141ef448110800450000a4511d400080060000c0a8030a71600dd0fe501f908b8c525908ceca66501801ff437900000000007c0000000b0100014eb0000000000e31393934373031303231080fdebf8f489c29fc1aa0611a0ee8dbcf8ada3129852e779569f93003e9af00101b5b5bf3c4a87ec47420f735c83d1029c82729ba3191fac7059dac5122342dc4560637b8ff15bfc84d8e84e8dfbc5dc8de067ae6b902c50c3e5a1915f0753a
// CertifiedAccountSvc.certified_account_read.GetFollowList send 20da22db750806141ef4481108004500027c5120400080060000c0a8030a71600dd0fe501f908b8c52d508ced606501801ff45510000000002540000000b0100014eb1000000000e31393934373031303231379d9ef3c72b17257dadcc13699bb8b027ccdd304c5ec4df57671b991739b43493c60017360ba5f88dd8a8afada19c134c82cf9ae4b5216f2abd4bc30563b981cf22865a55edf3856e46c6a3d7081eed19eb3c4f0a45bfe3120dca2d49e105827873ca33d5c2abcfb89428a5bff03fdb6337d86bff7421ffbc35887803ea9820abb58a0f7ccf607b41e203a8c3902933246a1e3b2f751ff97f8e2cc4661185f8f62f5183306222b8926c00249a80a285d9b43bee57935a825b2b07c1b4850953f04e866006538c8e7202444c25e5d87e6b040de52a7ccfd5cbba56e748b6a1d26d6e14bd4968039ae1c0f3745e7f2d41e1f8b3f46547c0dfcf045cef83bfedc9fc5b59e0adc8ec33d752835f814197e3fd2d5d5d99b20961b33b519e5457c14c84a45d4a5a8cf28ef428b33003a79753c7406181a1ac8de81fdfcac09c6a6265787fc1d27db83d52f5f3676da6589ffc2434465a9363f458b12b56c962eef53d354ed450f0671d5be9df26c6611725840cb5c5bce33ff0bb0ac45cbd1ea735122cb0aa7d489720c31afa83eeef8e2b692e9b83afcfb38700391788335dbc811b43bc018062ff9205273bc8271d2afa958f7f3b7c4077beddc6acb77e1864ff2c614f3af60265e6876a5ecbb95ad78e5f8cf74162a7cf87887db855808181a3cd8d2c363ded3af6105ef8164b408ae7925e5b9311091ceaf8b116be765d29c6d1317c1b4d7febe2e3862ebab3c28eb2ea05587ed09cab27ecaedb38f0e651f2662f605251f90d9b029bd4b617188ab49afe306d1094df0ae0
// OidbSvc.0x7a2_0 send 20da22db750806141ef448110800450000845123400080060000c0a8030a71600dd0fe501f908b8c552908cedcde50180200435900000000005c0000000b0100014eb6000000000e31393934373031303231d751dc69de793526fd65f55e83c1b8d43435ece7da38acf0b8503d8259484c583dbe89b603a4abc1f1f665d1a92876d711431b5833908d216efd7398286b8adf
// ConfigPushSvc.PushResp, ** StatSvc.register send 20da22db750806141ef448110800450002f85124400080060000c0a8030a71600dd0fe501f908b8c558508cee2605018020045cd0000000000ac0000000b01a4da6eb0000000000e3139393437303130323122465f76192ce454d64e90a36fe651eb96d052893396c002a815851dc04994d16f7a596b06ca02b4743077fb387442f00409806ece8bf2f533a129073db60c8d5a22b9d4224387629e8ce29959a683f505b741aa1a83068239f6df0f97a4e5499c1035092e7466b005307110460211aecd1f11c10cea0cc861007e08882accb0bbaec68cd9c9a83fd19bb5eebda016ff000002240000000a0100000044e0e22a59327abb9ce80cf63be86694210344fab2f2b065d7785a32cacfa4075cd509f49cb37a075ad0685cbd472344bfdef1ede611c0ad81129be9e2d7e476d2000000000e31393934373031303231f4cf6ac405adbbfab64c0905f9f1f4b3236e4b17c34bc8aaefd77b184f012c036978eea06bb7f9e6d443a9d71f6eaf8b08b3c49858269de0405d7acf5076fd3000357cdbced53142200fcd8f87dc2a047ecfdd0d374aa0bb1b5c97b35e59a7cc77f146c930d04be6b70a75be3d71704d1b95794cbc36ef0ffe96533d61521c7b4b676f6b067859c0760ae5689f146b3c1a19668b694a50aa1d561f2feb76b49d507dc530d49007ba08f84297b23290177539d0b2a3ceb0a2ef8a64b65494e574ed78857fc1c93911c7639dce6699d5fde605f432d4ab7dfd6cf8cc5451950b29d79f5e755f90faadf55dfcb3993d9b4fb9479f44e47c1a0702205da298327b0deb180e2976d07be7b6ac0302b128d792200a092e084ca6908fe0c13e142d50f42783009029b3d2da23bd1050a2cc56023f943ce3b164002fa31fdf9624a255455cc77b774223726f36cccb2603240ca528534ee1533eb66872b007ea20bb5e76f87efa35d7dcb4be7e5f410ed7276c09e20f04db63ea4b79fd7d2201ef0ec14b5f9795bd2bd028e58b3ab8d3461a188f113a8b50406e9c3db9f8b2115924faadc9f2707c6715dc2dda73119228079467fbe145cf951cf1948e755aa428f4e478a6ab9708ad39ded4
internal
var
wtSessionTicketKey
=
"B6 9D E4 EC 65 38 64 FD C8 3A D8 33 54 35 0C 73"
.
hexToBytes
()
internal
var
tgtgtKey
=
"D7 71 03 E3 4C E5 8F 6B 05 D8 C7 8C 96 FB FB 23"
.
hexToBytes
()
internal
var
deviceToken
=
"CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40"
.
hexToBytes
()
internal
var
D2Key
=
"44 28 6B 35 7A 54 2D 45 45 5D 56 32 44 33 47 49"
.
hexToBytes
()
internal
var
userStKey
=
"35 29 42 54 78 62 47 68 5E 77 68 54 6B 76 57 5F"
.
hexToBytes
()
internal
var
tgtKey
=
"44 24 3F 43 3F 21 37 2B 29 44 6E 47 70 3A 4E 3D"
.
hexToBytes
()
internal
val
t108
=
"BD 12 96 6C 83 53 EF DD 06 16 52 16 B8 1B 25 69"
.
hexToBytes
()
internal
val
t10c
=
"23 7D 2C 7A 3F 4A 41 35 7D 3B 45 51 6D 3D 2A 56"
.
hexToBytes
()
internal
val
t163
=
"2C 7A 7B 23 4E 24 3F 24 24 47 62 6B 69 2E 47 50"
.
hexToBytes
()
var
ecdhPrivateKeyS
=
"97a52992cb7a2110413629af94a3c249c68a3b731510caa8"
internal
val
shareKeyCalculatedByConstPubKey
by
lazy
{
ECDH
.
calculateShareKey
(
loadPrivateKey
(
ecdhPrivateKeyS
),
initialPublicKey
)
}
var
passwordMd5
:
ByteArray
=
byteArrayOf
()
var
uin
:
Long
=
0L
fun
main
()
{
val
data
=
"""
20da22db750806141ef448110800450000285129400080060000c0a8030a71600dd0fe501f908b8c5bf508cef1de5010020042fd0000
"""
.
trimIndent
()
.
trim
().
split
(
"\n"
).
map
{
val
bytes
=
it
.
trim
().
autoHexToBytes
()
if
(
bytes
[
0
].
toInt
()
==
0
)
{
bytes
}
else
bytes
.
dropTCPHead
()
}.
flatMap
{
it
.
toList
()
}.
toByteArray
()
data
.
read
{
decodeMultiClientToServerPackets
()
}
}
/**
* 顶层方法. TCP 切掉头后直接来这里
*/
fun
ByteReadPacket
.
decodeMultiClientToServerPackets
()
{
DebugLogger
.
enable
()
PacketLogger
.
enable
()
println
(
"=======================处理客户端到服务器======================="
)
var
count
=
0
while
(
remaining
!=
0L
)
{
readBytes
((
readUInt
()
-
4
u
).
toInt
()).
toReadPacket
().
runCatching
{
analysisOneFullPacket
()
}.
exceptionOrNull
()
?.
printStackTrace
()
count
++
if
(
remaining
!=
0L
)
{
println
()
println
()
println
()
println
()
println
()
}
else
DebugLogger
.
info
(
"=======================共有 $count 个包======================="
)
}
println
()
}
fun
Map
<
Int
,
ByteArray
>.
printTLVMap
(
name
:
String
=
""
,
keyLength
:
Int
=
2
)
=
DebugLogger
.
debug
(
"TLVMap $name= "
+
this
.
mapValues
{
(
_
,
value
)
->
value
.
toUHexString
()
}.
mapKeys
{
when
(
keyLength
)
{
1
->
it
.
key
.
toUByte
().
contentToString
()
2
->
it
.
key
.
toUShort
().
contentToString
()
4
->
it
.
key
.
toUInt
().
contentToString
()
else
->
error
(
"Expecting 1, 2 or 4 for keyLength"
)
}
}.
entries
.
joinToString
(
prefix
=
"{"
,
postfix
=
"}"
,
separator
=
"\n"
))
fun
ByteReadPacket
.
analysisOneFullPacket
():
ByteReadPacket
=
debugIfFail
(
"Failed"
,
{
buildPacket
{
writeInt
(
it
.
size
+
4
);
writeFully
(
it
)
}
})
{
val
flag1
=
readInt
()
print
(
"flag1="
+
flag1
.
contentToString
()
+
", "
)
val
flag2
=
readByte
().
toInt
()
print
(
"flag2=$flag2"
+
", "
)
if
(
flag1
==
0
x0B
)
{
if
(
flag2
==
1
)
{
print
(
"sequenceId = "
+
readInt
().
toUHexString
()
+
", "
)
}
else
{
print
(
"extra data="
+
readBytes
(
readInt
()
-
4
).
toUHexString
()
+
", "
)
}
}
else
{
//if (flag2 == 1) {
val
loginExtraData
=
readBytes
(
readInt
()
-
4
)
loginExtraData
.
debugPrintThis
(
"loginExtraData"
)
// } else {
// this.debugPrint()
// error("未知 flag2")
// }
}
print
(
"flag3="
+
readByte
().
toUHexString
()
+
", "
)
readString
(
readInt
()
-
4
)
print
(
"// 解密 body"
)
val
encrypted
=
readBytes
()
val
decrypted
=
encrypted
.
tryDecryptOrNull
()
if
(
decrypted
==
null
)
{
println
(
", cannot decrypt: ${encrypted.toUHexString()}"
)
error
(
"cannot decrypt: ${encrypted.toUHexString()}"
)
}
else
{
decrypted
.
toReadPacket
().
debugPrintThis
(
"outer body decrypted"
).
apply
{
when
(
flag1
)
{
0
x0A
->
decodeSso
()
0
x0B
->
decodeUni
()
else
->
error
(
"unknown flag1: $flag1"
)
}
when
(
flag2
)
{
2
->
{
this
.
debugPrintThis
(
"Oicq Request"
).
apply
{
/*
byte 2 // head flag
short 27 + 2 + remaining.length
ushort client.protocolVersion // const 8001
ushort 0x0001 // const0
uint client.uin
byte 3 // const1
ubyte encryptMethod.value // [EncryptMethod]
byte 0 // const2
int 2 // const3
int client.appClientVersion
int 0 // const4
*/
discardExact
(
3
)
readShort
().
toInt
().
takeIf
{
it
!=
8001
}
?.
let
{
println
(
"这个包不是 oicqRequest"
)
return
@
debugIfFail
this
//println(" got new protocolVersion=$it")
}
val
commandId
=
readUShort
().
toInt
()
println
(
" commandId=0x${commandId.toShort().toUHexString()}"
)
readUShort
().
toInt
().
takeIf
{
it
!=
1
}
?.
let
{
println
(
" got new const0=$it"
)
}
println
(
" uin=${readUInt()}"
)
readByte
().
toInt
().
takeIf
{
it
!=
3
}
?.
let
{
println
(
" got new const1=$it"
)
}
val
encryptionMethod
=
readUByte
().
toInt
()
readByte
().
toInt
().
takeIf
{
it
!=
0
}
?.
let
{
println
(
" got new const2=$it"
)
}
readInt
().
takeIf
{
it
!=
2
}
?.
let
{
println
(
" got new const3=$it"
)
}
readInt
().
takeIf
{
it
!=
0
}
?.
let
{
println
(
" got new appClientVersion=$it"
)
}
readInt
().
takeIf
{
it
!=
0
}
?.
let
{
println
(
" got new const4=$it"
)
}
discardExact
(
1
)
discardExact
(
1
)
val
randomKey
=
readBytes
(
16
)
println
(
"randomKey= ${randomKey.toUHexString()}"
)
readUShort
().
toInt
().
takeIf
{
it
!=
258
}
?.
let
{
println
(
" got new const in ECDH head(originally=258)=$it"
)
}
val
publicKey
=
readBytes
(
readShort
().
toInt
())
println
(
"ecdh publicKey="
+
publicKey
.
toUHexString
())
val
encrypt
=
when
(
encryptionMethod
)
{
135
,
7
->
{
ECDH
.
calculateShareKey
(
loadPrivateKey
(
ecdhPrivateKeyS
),
//"04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7".chunkedHexToBytes().adjustToPublicKey() // QQ: 04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7
ECDH
.
constructPublicKey
(
"30 46 30 10 06 07 2A 86 48 CE 3D 02 01 06 05 2B 81 04 00 1F 03 32 00"
.
hexToBytes
()
+
publicKey
)
)
}
69
->
{
error
(
"encryptionMethod 69"
)
}
else
->
error
(
"unknown encryptionMethod=$encryptionMethod"
)
}
val
encryptedBody
=
readBytes
((
remaining
-
1
).
toInt
())
@Suppress
(
"NAME_SHADOWING"
)
val
decrypted
=
kotlin
.
runCatching
{
encryptedBody
.
decryptBy
(
encrypt
).
also
{
println
(
"first by calculatedShareKey or sessionKey(method=7)"
)
}
}.
getOrElse
{
encryptedBody
.
decryptBy
(
shareKeyCalculatedByConstPubKey
).
also
{
println
(
"first by shareKeyCalculatedByConstPubKey"
)
}
}.
let
{
firstDecrypted
->
runCatching
{
firstDecrypted
.
decryptBy
(
encrypt
).
also
{
println
(
"second by calculatedShareKey"
)
}
}.
getOrElse
{
kotlin
.
runCatching
{
firstDecrypted
.
decryptBy
(
shareKeyCalculatedByConstPubKey
)
}.
getOrDefault
(
firstDecrypted
)
}
}
PacketLogger
.
info
(
"Real body="
+
decrypted
.
toUHexString
())
decrypted
.
toReadPacket
().
apply
{
if
(
commandId
==
0
x0810
)
{
DebugLogger
.
info
(
"发送 login!! 正在获取 tgtgtKey"
)
try
{
discardExact
(
4
)
val
tlvMap
=
readTLVMap
()
tlvMap
.
printTLVMap
()
tlvMap
[
0
x106
]
?.
also
{
DebugLogger
.
info
(
"找到了 0x106"
)
}
?.
decryptBy
(
md5
(
passwordMd5
+
ByteArray
(
4
)
+
uin
.
toInt
().
toByteArray
()))
?.
read
{
discardExact
(
2
+
4
*
4
+
8
+
4
+
4
+
1
+
16
)
tgtgtKey
=
readBytes
(
16
)
DebugLogger
.
info
(
"获取 tgtgtKey=${tgtgtKey.toUHexString()}"
)
}
?:
DebugLogger
.
info
(
"找不到 0x106"
)
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
}
}
}
}
}
else
->
{
this
.
debugPrintThis
(
"uni packet"
)
}
}
}
}
}
fun
ByteReadPacket
.
decodeUni
()
{
// 00 00 00 C7 A4 DA 6F A2 20 02 ED BD 20 02 ED BD 01 00 00 00 00 00 00 00 00 00 01 00 00 00 00 4C B8 12 0D E1 DA 19 AF D3 EB 36 76 BD 42 08 F6 DC A5 35 69 C0 8F F2 75 28 B4 CE 09 C9 B7 86 E3 5A 14 D1 0D CA 5D D4 CB 16 77 8B 32 8D 81 3B 3F D9 52 13 77 03 D3 F7 0E CD 7B 21 95 D2 59 CE 0C 31 D6 F1 38 2A FA 82 AD 60 00 00 00 1A 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 50 75 73 68 52 65 73 70 00 00 00 08 02 B0 5B 8B 00 00 00 13 38 35 38 34 31 34 33 36 39 32 31 31 39 39 33 00 00 00 04 00 22 7C 34 35 34 30 30 31 32 32 38 34 33 37 35 39 30 7C 41 38 2E 32 2E 30 2E 32 37 66 36 65 61 39 36 00 00 00 04 00 00 00 5B 10 03 2C 3C 4C 56 23 51 51 53 65 72 76 69 63 65 2E 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 4D 61 69 6E 53 65 72 76 61 6E 74 66 08 50 75 73 68 52 65 73 70 7D 00 00 1A 08 00 01 06 08 50 75 73 68 52 65 73 70 1D 00 00 09 0A 10 02 22 14 DA 6F A3 0B 8C 98 0C A8 0C
// 00 00 00 2A 00 00 00 1A 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 50 75 73 68 52 65 73 70 00 00 00 08 02 B0 5B 8B 00 00 00 04
// 00 00 00 5B 10 03 2C 3C 4C 56 23 51 51 53 65 72 76 69 63 65 2E 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 4D 61 69 6E 53 65 72 76 61 6E 74 66 08 50 75 73 68 52 65 73 70 7D 00 00 1A 08 00 01 06 08 50 75 73 68 52 65 73 70 1D 00 00 09 0A 10 01 22 14 DA 6E B1 0B 8C 98 0C A8 0C
println
(
"// 尝试解 Uni"
)
println
(
"// head"
)
//return
readBytes
(
readInt
()
-
4
).
debugPrintThis
(
"head"
).
toReadPacket
().
apply
{
val
commandName
=
readString
(
readInt
()
-
4
).
also
{
PacketLogger
.
warning
(
"commandName=$it"
)
}
println
(
commandName
)
println
(
" unknown4Bytes="
+
readBytes
(
readInt
()
-
4
).
toUHexString
())
// 00 00 00 1A 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 50 75 73 68 52 65 73 70
// 00 00 00 08 02 B0 5B 8B
// 00 00 00 04
println
(
" extraData="
+
readBytes
(
readInt
()
-
4
).
toUHexString
())
}
readBytes
(
readInt
()
-
4
).
debugPrintThis
(
"Real body"
).
read
{
// real body
//10 03 2C 3C 4C 56 23 51 51 53 65 72 76 69 63 65 2E 43 6F 6E 66 69 67 50 75 73 68 53 76 63 2E 4D 61 69 6E 53 65 72 76 61 6E 74 66 08 50 75 73 68 52 65 73 70 7D 00 00 1A 08 00 01 06 08 50 75 73 68 52 65 73 70 1D 00 00 09 0A 10 01 22 14 DA 6E B1 0B 8C 98 0C A8 0C
}
}
fun
ByteReadPacket
.
decodeSso
()
{
// 00 00 02 24
// 00 00 00 0A 01 00 00 00 44 E0 E2 2A 59 32 7A BB 9C E8 0C F6 3B E8 66 94 21 03 44 FA B2 F2 B0 65 D7 78 5A 32 CA CF A4 07 5C D5 09 F4 9C B3 7A 07 5A D0 68 5C BD 47 23 44 BF DE F1 ED E6 11 C0 AD 81 12 9B E9 E2 D7 E4 76 D2 00 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 F4 CF 6A C4 05 AD BB FA B6 4C 09 05 F9 F1 F4 B3 23 6E 4B 17 C3 4B C8 AA EF D7 7B 18 4F 01 2C 03 69 78 EE A0 6B B7 F9 E6 D4 43 A9 D7 1F 6E AF 8B 08 B3 C4 98 58 26 9D E0 40 5D 7A CF 50 76 FD 30 00 35 7C DB CE D5 31 42 20 0F CD 8F 87 DC 2A 04 7E CF DD 0D 37 4A A0 BB 1B 5C 97 B3 5E 59 A7 CC 77 F1 46 C9 30 D0 4B E6 B7 0A 75 BE 3D 71 70 4D 1B 95 79 4C BC 36 EF 0F FE 96 53 3D 61 52 1C 7B 4B 67 6F 6B 06 78 59 C0 76 0A E5 68 9F 14 6B 3C 1A 19 66 8B 69 4A 50 AA 1D 56 1F 2F EB 76 B4 9D 50 7D C5 30 D4 90 07 BA 08 F8 42 97 B2 32 90 17 75 39 D0 B2 A3 CE B0 A2 EF 8A 64 B6 54 94 E5 74 ED 78 85 7F C1 C9 39 11 C7 63 9D CE 66 99 D5 FD E6 05 F4 32 D4 AB 7D FD 6C F8 CC 54 51 95 0B 29 D7 9F 5E 75 5F 90 FA AD F5 5D FC B3 99 3D 9B 4F B9 47 9F 44 E4 7C 1A 07 02 20 5D A2 98 32 7B 0D EB 18 0E 29 76 D0 7B E7 B6 AC 03 02 B1 28 D7 92 20 0A 09 2E 08 4C A6 90 8F E0 C1 3E 14 2D 50 F4 27 83 00 90 29 B3 D2 DA 23 BD 10 50 A2 CC 56 02 3F 94 3C E3 B1 64 00 2F A3 1F DF 96 24 A2 55 45 5C C7 7B 77 42 23 72 6F 36 CC CB 26 03 24 0C A5 28 53 4E E1 53 3E B6 68 72 B0 07 EA 20 BB 5E 76 F8 7E FA 35 D7 DC B4 BE 7E 5F 41 0E D7 27 6C 09 E2 0F 04 DB 63 EA 4B 79 FD 7D 22 01 EF 0E C1 4B 5F 97 95 BD 2B D0 28 E5 8B 3A B8 D3 46 1A 18 8F 11 3A 8B 50 40 6E 9C 3D B9 F8 B2 11 59 24 FA AD C9 F2 70 7C 67 15 DC 2D DA 73 11 92 28 07 94 67 FB E1 45 CF 95 1C F1 94 8E 75 5A A4 28 F4 E4 78 A6 AB 97 08 AD 39 DE D4
// 00 00 00 C1
// 00 01 4E B9 //sequence
// 20 02 ED BD
// 20 02 ED BD
// 01 00 00 00 00 00 00 00 00 00 01 00
//
// 00 00 00 4C // extra
// B8 12 0D E1 DA 19 AF D3 EB 36 76 BD 42 08 F6 DC A5 35 69 C0 8F F2 75 28 B4 CE 09 C9 B7 86 E3 5A 14 D1 0D CA 5D D4 CB 16 77 8B 32 8D 81 3B 3F D9 52 13 77 03 D3 F7 0E CD 7B 21 95 D2 59 CE 0C 31 D6 F1 38 2A FA 82 AD 60
//
// 00 00 00 14 //cmd
// 53 74 61 74 53 76 63 2E 72 65 67 69 73 74 65 72
//
// 00 00 00 08
// 02 B0 5B 8B
//
// 00 00 00 13
// 38 35 38 34 31 34 33 36 39 32 31 31 39 39 33
//
// 00 00 00 04
//
// 00 22 7C 34 35 34 30 30 31 32 32 38 34 33 37 35 39 30 7C 41 38 2E 32 2E 30 2E 32 37 66 36 65 61 39 36
//
// 00 00 00 04
// 00 00 00 FD 10 03 2C 3C 4C 56 0B 50 75 73 68 53 65 72 76 69 63 65 66 0E 53 76 63 52 65 71 52 65 67 69 73 74 65 72 7D 00 01 00 CD 08 00 01 06 0E 53 76 63 52 65 71 52 65 67 69 73 74 65 72 1D 00 01 00 B5 0A 02 76 E4 B8 DD 10 07 2C 36 00 40 0B 5C 6C 7C 8C 9C AC B0 19 C0 01 D6 00 EC FD 10 00 00 10 BB 95 0A A0 8F AB 2E 55 38 39 FF 6D 90 40 B3 48 F1 11 08 04 FC 12 F6 13 0D 4F 4E 45 50 4C 55 53 20 41 35 30 30 30 F6 14 0D 4F 4E 45 50 4C 55 53 20 41 35 30 30 30 F6 15 05 37 2E 31 2E 31 F0 16 01 F1 17 06 0F FC 18 FC 1A F3 1B 00 00 00 00 D0 0D 60 71 F6 1C 00 FC 1D F6 1E 0A 5B 75 5D 4F 6E 65 50 6C 75 73 F6 1F 14 3F 4F 4E 45 50 4C 55 53 20 41 35 30 30 30 5F 32 33 5F 31 37 F6 20 00 FD 21 00 00 0D 0A 04 08 2E 10 00 0A 05 08 9B 02 10 00 FC 22 FC 24 0B 8C 98 0C A8 0C
println
(
"// 尝试解 SSO"
)
println
(
"// head"
)
discardExact
(
4
)
(
" sequenceId="
+
readUInt
())
println
(
" subAppId="
+
readUInt
())
println
(
" subAppId2="
+
readUInt
())
println
(
" unknownHex="
+
readBytes
(
12
).
toUHexString
())
println
(
" extraData="
+
readBytes
(
readInt
()
-
4
).
toUHexString
())
val
commandName
=
readBytes
(
readInt
()
-
4
).
encodeToString
()
PacketLogger
.
warning
(
" commandName=$commandName"
)
(
" unknown4Bytes="
+
readBytes
(
readInt
()
-
4
).
toUHexString
())
(
" imei="
+
readBytes
(
readInt
()
-
4
).
toUHexString
())
(
" 0 bytes="
+
readBytes
(
readInt
()
-
4
).
toUHexString
())
(
" ksid="
+
readBytes
(
readShort
()
-
2
).
toUHexString
())
(
" 0 bytes="
+
readBytes
(
readInt
()
-
4
).
toUHexString
())
println
()
discardExact
(
4
)
println
(
"// body(maybe OicqRequest)"
)
}
val
keys
:
Map
<
String
,
ByteArray
>
get
()
=
mapOf
(
"16 zero"
to
ByteArray
(
16
),
"wtSessionTicketKey"
to
wtSessionTicketKey
,
"D2 key"
to
D2Key
,
"tgtgtKey"
to
tgtgtKey
,
"tgtKey"
to
tgtKey
,
"userStKey"
to
userStKey
,
"deviceToken"
to
deviceToken
,
"shareKeyCalculatedByConstPubKey"
to
shareKeyCalculatedByConstPubKey
,
"t108"
to
t108
,
"t10c"
to
t10c
,
"t163"
to
t163
)
fun
ByteArray
.
tryDecrypt
():
ByteArray
{
return
this
.
tryDecryptOrNull
()
?:
error
(
"Cannot decrypt. Encrypted data="
+
this
.
toUHexString
())
}
fun
ByteArray
.
tryDecryptOrNull
():
ByteArray
?
{
keys
.
forEach
{
(
key
,
value
)
->
kotlin
.
runCatching
{
return
decryptBy
(
value
).
also
{
println
(
"outer by $key"
)
}
}
}
return
null
}
fun
main1
()
{
val
toUHexString
=
"20da22db750806141ef4481108004500012c525d400080060000c0a8030a71600dd0fe501f908b8d83b908d5d6de501803fe44010000000001040000000b01000150ce000000000e31393934373031303231d2d5378a3c47b184e294b2afbf14704d7317bb38be8273dfa287e00a7aba8a8171771de1717fb7c1661d8c3d414f51096ab7b77b8828a65aab7e40259bc8359cc6e23a5f941d700fd7894d416b7a29a270773df81d3265d7d8d16d13429c0c72db48954b66efb9e6e4c13b2c36b0d73fe285c82a8c650f0b1cf1a7c7e11f0c32f50814aa5a43cd8ea88214249763f053794e338d5f1cf81c893b3944cca7635ffcbf8742892da5f4bcb2694954ddaee63fa2a298dc3bd4a22710f2064293c5304ad4faf5baa5b24b56455994ca4c4b1755c723aff08be5dc3a1bb6a72e10bb9ae77054baf54b7091"
.
chunkedHexToBytes
().
drop
(
16
*
3
+
6
).
toByteArray
().
toUHexString
()
println
(
toUHexString
)
println
()
println
()
/*
00 00 01 23 remaining + 4
00 00 00 0A
02 00 00 00
04 00 00 00
00 05
00 05
30 40 3C 5C D4 C3 65 C7 7F A4 40 A3 4F 88 7F D8 56 1C F1 12 EE 3E FC 7E 51 94 F6 D9 2E 01 2D CB BB 1D 7E 3A 01 0E AB 97 FB 55 20 2C 05 82 6D 70 87 33 F0 97 6F B3 04 DC 90 EC C1 D3 C6 C3 66 D8 26 1A B2 08 0B 89 0F 25 AB 8B 91 5C B8 C9 FF A1 DE 43 0F D2 F4 E5 F6 C0 1D DE 65 0B 72 1D 24 D8 7E C0 A6 31 64 71 1C C2 7D 39 93 6B 86 9F 62 2B 76 58 6C 49 5D 60 0B A6 E7 90 AB CB A8 72 E5 3F 6F 25 B2 AD A6 C8 C6 B7 B5 2D 90 19 71 A0 46 57 F4 BD 96 7D E2 EF 86 DA BE B8 F9 EB DA BB D0 B6 F0 73 1C 27 14 DB 3A 66 BF F9 68 CA 4A 7B 4A D2 DF 66 C8 B4 C5 56 93 72 22 D0 38 FD CA 61 74 31 6A C5 3D 0B 3F E2 92 6A 84 16 B3 E5 86 AD D3 87 7C 32 3E 86 DA B4 E7 69 A0 AF A3 C7 97 DF 90 DC 9A 5A 46 5F DA 32 2A 15 21 C6 A0 8C 8D DA AE B2 4D 49 0E 07 05 5F 12 03 1D 0F 5B 53 6A 8E F0 29 78 41 BD 19 AC BB 92 44 D7 2F 7A FB A9 46 39 AF 69
*/
// first (cli log)
// 00 00 01 23
// 00 00 00 0A
// 02 00 00 00
// 04 00
// 00 00 00 05
// 30
//
// 40 3C 5C D4 C3 65 C7 7F A4 40 A3 4F 88 7F D8 56 1C F1 12 EE 3E FC 7E 51 94 F6 D9 2E 01 2D CB BB 1D 7E 3A 01 0E AB 97 FB 55 20 2C 05 82 6D 70 87 33 F0 97 6F B3 04 DC 90 EC C1 D3 C6 C3 66 D8 26 1A B2 08 0B 89 0F 25 AB 8B 91 5C B8 C9 FF A1 DE 43 0F D2 F4 E5 F6 C0 1D DE 65 0B 72 1D 24 D8 7E C0 A6 31 64 71 1C C2 7D 39 93 6B 86 9F 62 2B 76 58 6C 49 5D 60 0B A6 E7 90 AB CB A8 72 E5 3F 6F 25 B2 AD A6 C8 C6 B7 B5 2D 90 19 71 A0 46 57 F4 BD 96 7D E2 EF 86 DA BE B8 F9 EB DA BB D0 B6 F0 73 1C 27 14 DB 3A 66 BF F9 68 CA 4A 7B 4A D2 DF 66 C8 B4 C5 56 93 72 22 D0 38 FD CA 61 74 31 6A C5 3D 0B 3F E2 92 6A 84 16 B3 E5 86 AD D3 87 7C 32 3E 86 DA B4 E7 69 A0 AF A3 C7 97 DF 90 DC 9A 5A 46 5F DA 32 2A 15 21 C6 A0 8C 8D DA AE B2 4D 49 0E 07 05 5F 12 03 1D 0F 5B 53 6A 8E F0 29 78 41 BD 19 AC BB 92 44 D7 2F 7A FB A9 46 39 AF 69
//
// second, cli log
// third, longest
// full trans_emp packet:
/*
00 00 03 5C // =860
00 00 00 0A
02
00 00 00 04 extra data length
00
00 00 00 0E
31 39 39 34 37 30 31 30 32 31 // uin
// encrypted by 16 zero
FB B1 B5 C2 86 BC 9E B6 A2 AE 43 CE 77 35 3D CB 0B EB F5 22 87 3D FC 23 06 4E A4 99 D0 60 FD 11 75 19 86 D4 86 A6 74 43 41 C3 FF 8E E8 9C 30 56 64 91 A5 A4 49 36 35 49 F9 B9 17 F2 0E 9B 19 EB 04 C5 8D 73 47 E5 1E C0 0B E5 5A 5E 4C 24 33 F4 FD 98 1F 61 77 26 A7 F7 4F 66 F6 B2 53 08 01 03 D4 75 4C CD 94 74 A6 45 11 23 81 8C 94 B8 4A 96 13 87 5F CE 9A EE 86 C9 F3 87 9D F9 D0 91 86 63 EA 88 83 89 DD B6 60 07 82 7A 5B F3 8C 97 A7 EA 6F 2E F6 04 68 51 96 79 C3 40 54 44 DF 4A 33 41 08 F0 3C A8 8F DE BE B3 E3 ED 39 C0 B8 DE 6D 44 04 69 42 8C EE A3 FA C5 4C CB 4C 62 0D 39 4E C9 8F 94 53 44 19 F3 4E C3 C2 20 0F 6D 06 6C EE 9D 6B 3D BC 6E 46 DC 31 3E 38 63 68 15 29 F1 64 7B F9 D5 72 67 47 95 4E 3F FA 75 15 10 5A 98 BB 5A 9B 17 B9 2A 6C 56 CB CF F2 98 D4 6B 65 3D 2F 72 CB C2 41 65 CC 01 18 91 0A BA 8C 56 B1 CB 6B 35 B2 F7 DF 51 F3 09 65 BC 74 CD F4 22 61 17 79 E6 D8 2B DA 53 7E 47 90 A0 AC B3 B2 50 04 FD 49 CF CA E7 0C C5 F5 2E 4C 26 7E 1A EB 63 AC F1 DB 34 A0 F5 91 28 20 24 38 2D 99 45 3D EE 4A 75 AA 6D 9E 0B 69 FE 42 EF D1 AE B9 14 A4 32 40 66 AA 65 03 7A 1C 8C A1 51 E5 62 C0 BD 50 2F 2F 5E B8 0D EF F7 D8 17 EF 5C B5 A4 A0 3D 13 F0 8C E1 BF E4 48 5C ED 08 4F 81 37 6B 2F B8 3F 82 20 07 25 C2 A9 E5 BE 2F 0E BE A6 B9 A6 8D 8C E0 72 C6 8A 62 BE 4E AA B1 70 EF 94 03 62 69 26 7F 53 F7 5E D3 F6 36 9C 80 C5 0C EB 9E 48 1C 88 58 E0 77 E1 6A 8D 7A 80 DF 14 06 E7 92 A5 61 F6 35 E6 A4 D5 E6 66 2E 24 22 EC 88 61 7E 35 0B 86 86 B1 7A B3 C1 7B 6A 3B 59 F9 AF 15 19 C4 C7 36 42 E1 4B 9A 53 30 73 45 51 70 DA A5 1F BE D6 35 2F A3 C9 57 03 82 56 07 9A 43 95 EE CC 2B 67 12 D0 EC DF 9A 62 BE 91 91 C2 B7 CD 22 DD 81 C7 88 65 BA 57 62 66 14 41 5F 78 D8 B7 81 2F 10 7A CC 91 10 BC FF 90 A3 76 A5 2E 2D D6 52 74 37 70 DF 9E AA 9F 19 9B C9 E6 69 97 FB E1 21 A0 05 C6 06 E9 E3 85 54 73 45 23 79 BC 4E 68 F3 0F 3B E7 5F 03 46 C4 52 DB 79 07 6B D7 A4 7E CC 3A D1 B8 FB 2B B7 96 FB F6 C3 89 9F 1F BC 61 EE 15 60 D5 E9 FE D4 EC 15 7E 6E 37 70 98 E3 D7 AD 43 99 7F 34 23 93 47 2E 50 84 B6 E4 C4 4B E0 F2 E5 27 F0 1C B3 EA 84 96 21 ED 01 08 D6 10 99 92 B0 8D B4 D5 1B 13 91 8C A5 96 22 F7 C0 E8 38 95 20 D7 9E C9 6E 78 18 CE BD 47 DD 2A 70 00 69 C3 29 72 13 3C F8 70 83 C5 85 71 A7 4C 94 B2 A5 6C 3B BC 6F 0A 94 EB 95 21 81 22 E1 97 63 DE DA 73 25 36 A5 99 13 8A 4B D0 B6 8A 59 52 6B BA 99 47 6C 3A 5B F0 65 F1 1B 5A BE 9F D5 C7 4D 7B C2 B4 07 A3 62 37 3C B5 CF 24 3A E1 98 18 5B 5D C9 15 4D 36 40 91 53 C7 90 97 57 8C 8D 7C 1A E3 62 9D C4 6C 5C 9C 03 02 C6 1C 12 D0 50 51 F8 23 81 02 9E 6A FF E7 C6 5D 9B 66 EC
*/
// decrypted:
// 00 00 00 C2
// 00 01 4E 66 // =85606
//
// 20 02 ED BD // =537062845
// 20 02 ED BD
//
// 01 00 00 00 00 00 00 00 00 00 01 00
//
// 00 00 00 4C //72+4, unknown
// B8 12 0D E1
// DA 19 AF D3
// EB 36 76 BD
// 42 08 F6 DC A5 35 69 C0 8F F2 75 28 B4 CE 09 C9 B7 86 E3 5A 14 D1 0D CA 5D D4 CB 16 77 8B 32 8D 81 3B 3F D9 52 13 77 03 D3 F7 0E CD 7B 21 95 D2 59 CE 0C 31 D6 F1 38 2A FA 82 AD 60
//
// 00 00 00 15 // 17+4, command
// 77 74 6C 6F 67 69 6E 2E 74 72 61 6E 73 5F 65 6D 70 // wtlogin.trans_emp
//
// 00 00 00 08 // 4+4
// 02 B0 5B 8B // unknown, =45112203
//
// 00 00 00 13 // 15+4, imei:
// 38 35 38 34 31 34 33 36 39 32 31 31 39 39 33
//
// 00 00 00 04
//
// 00 22 // 32+2, ksid:
// 7C 34 35 34 30 30 31 32 32 38 34 33 37 35 39 30 7C 41 38 2E 32 2E 30 2E 32 37 66 36 65 61 39 36
//
// 00 00 00 04
//
// 00 00 02 70 // remaining size=620+4
// 02
// 02 6C // 27+2+body.size = 620 = 27+2+591
// 1F 41 // 8001
// 08 12 // commandId 2066
// 00 01 // const?
// 76 E4 B8 DD // accountId=1994701021
// 03
// 07 // EncryptMethod
// 00
// 00 00 00 02
// 00 00 00 00
// 00 00 00 00 // const
//
// // OutgoingPacket.body: ECDH encrypted
// 01
// 01
// // private key: len=16
// A4 9A 6A EE 17 5B 7E 3D C0 71 DA 04 1C E1 E4 88
// 01 02 // =258
// // pub key: len=49
// [00 31] 04 CB 36 66 98 56 1E 93 6E 80 C1 57 E0 74 CA B1 3B 0B B6 8D DE B2 82 45 48 A1 B1 8D D4 FB 61 22 AF E1 2F E4 8C 52 66 D8 D7 26 9D 76 51 A8 EB 6F E7
// E8 6F F6 C1 D9 8B 9C C1 B2 99 A9 53 68 80 E0 4A 34 F9 C2 F7 6D A5 4E E6 25 F1 31 A7 16 46 6D 2A E5 14 2B 64 8D EA 29 15 19 48 69 34 C4 90 D1 50 9A A6 3F 58 69 94 B1 E2 1E E7 C5 D9 53 5B 6B 71 9F 20 9D 2D 02 B3 0D DF B0 F1 0E 03 1E 2C E8 5F F6 28 D6 97 FB A9 45 C6 E1 FF 79 84 C7 7E 42 79 81 BB 48 48 AD D5 9F 46 7C 45 EA B5 C1 10 F0 41 EF 94 A2 D5 80 67 EB CC 11 05 9E DE 06 A1 9E 5E 71 40 68 4A C8 32 B8 C8 48 73 6D AD 41 51 07 4F 43 E3 C6 7D 8C 7E 49 5D CF A3 D8 3F 29 22 AD 08 AE C4 15 29 90 22 DC 01 5D 81 BA B8 B0 06 ED B1 93 EE CC CC FC 65 97 1F 1F 22 36 AD 85 B1 3D 1B 02 7E C3 0C 1E 6E 4A 30 CB 4F 09 9D 67 C4 D7 DB 06 89 36 A0 7A 03 D0 46 5C 0E C1 B9 24 E4 30 6E BE 0C 60 25 10 57 1E D7 45 CD 0A B3 23 18 1C 47 0C 62 79 29 8F 55 3B F0 0D A2 FB DE 05 B7 71 AD B8 B2 D7 AD 4B 15 E0 ED EB 26 25 CC DE 39 66 8B 1A AE 96 0B E5 4E AB C7 A3 0C 09 82 D6 CD F9 3E 9D 6E C6 73 C5 20 20 F6 8E DF 80 95 13 68 9C 3B C7 EF 71 C9 FC 96 2C 07 48 0A 9A 06 8F 96 7E 90 1F 31 3A 05 86 86 E5 64 5D 5A 08 2C 6C EE 72 7C C2 DF 9B 3C F7 52 5C 17 0E 9B C9 AE 36 8E 54 C9 B5 5E E9 D6 F8 C4 54 81 AC 78 DE 1D 4A C3 31 C6 2E 3F 6D C7 9C FF 5F 7F 88 2C B4 63 CC AC FC 57 1B 84 5D 66 7E C0 14 29 1D 70 74 A8 EE 03 55 07 C2 D7 F5 CE 15 8F 9B DF DA AD C8 F9 33 90 CD 57 98 A1 62 94 E1 3E DD 0F F6 F4 6D 17 35 F1 CE 1D EE 72 72 8B 7A AB B0 7F 68 8F 40 68 96 20 11 BF 05 91 C5 C1 71 AB BF BC A1 9D CC 0B 7D 5F 24 23 3D AB 46 02 43 D6 15 5E 64 BD FD 35 19 C0 47 15 69 C1 EA 0E 9B 5E 8A CD C2 20 CF A7 39 2E 8B D9 89 FC 94 63 18 E9 DD 3F 5B 1B C0 27 4A 85 7A 84 3B C2 50 BC 6A 86 48 0A
// // decrypted by calculated share key:
// 00 00 01 04
// 00 00 00 0B 01 00 01 50 CE 00
// 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 D2 D5 37 8A 3C 47 B1 84 E2 94 B2 AF BF 14 70 4D 73 17 BB 38 BE 82 73 DF A2 87 E0 0A 7A BA 8A 81 71 77 1D E1 71 7F B7 C1 66 1D 8C 3D 41 4F 51 09 6A B7 B7 7B 88 28 A6 5A AB 7E 40 25 9B C8 35 9C C6 E2 3A 5F 94 1D 70 0F D7 89 4D 41 6B 7A 29 A2 70 77 3D F8 1D 32 65 D7 D8 D1 6D 13 42 9C 0C 72 DB 48 95 4B 66 EF B9 E6 E4 C1 3B 2C 36 B0 D7 3F E2 85 C8 2A 8C 65 0F 0B 1C F1 A7 C7 E1 1F 0C 32 F5 08 14 AA 5A 43 CD 8E A8 82 14 24 97 63 F0 53 79 4E 33 8D 5F 1C F8 1C 89 3B 39 44 CC A7 63 5F FC BF 87 42 89 2D A5 F4 BC B2 69 49 54 DD AE E6 3F A2 A2 98 DC 3B D4 A2 27 10 F2 06 42 93 C5 30 4A D4 FA F5 BA A5 B2 4B 56 45 59 94 CA 4C 4B 17 55 C7 23 AF F0 8B E5 DC 3A 1B B6 A7 2E 10 BB 9A E7 70 54 BA F5 4B 70 91
// 03
val
data
=
"""
E8 6F F6 C1 D9 8B 9C C1 B2 99 A9 53 68 80 E0 4A 34 F9 C2 F7 6D A5 4E E6 25 F1 31 A7 16 46 6D 2A E5 14 2B 64 8D EA 29 15 19 48 69 34 C4 90 D1 50 9A A6 3F 58 69 94 B1 E2 1E E7 C5 D9 53 5B 6B 71 9F 20 9D 2D 02 B3 0D DF B0 F1 0E 03 1E 2C E8 5F F6 28 D6 97 FB A9 45 C6 E1 FF 79 84 C7 7E 42 79 81 BB 48 48 AD D5 9F 46 7C 45 EA B5 C1 10 F0 41 EF 94 A2 D5 80 67 EB CC 11 05 9E DE 06 A1 9E 5E 71 40 68 4A C8 32 B8 C8 48 73 6D AD 41 51 07 4F 43 E3 C6 7D 8C 7E 49 5D CF A3 D8 3F 29 22 AD 08 AE C4 15 29 90 22 DC 01 5D 81 BA B8 B0 06 ED B1 93 EE CC CC FC 65 97 1F 1F 22 36 AD 85 B1 3D 1B 02 7E C3 0C 1E 6E 4A 30 CB 4F 09 9D 67 C4 D7 DB 06 89 36 A0 7A 03 D0 46 5C 0E C1 B9 24 E4 30 6E BE 0C 60 25 10 57 1E D7 45 CD 0A B3 23 18 1C 47 0C 62 79 29 8F 55 3B F0 0D A2 FB DE 05 B7 71 AD B8 B2 D7 AD 4B 15 E0 ED EB 26 25 CC DE 39 66 8B 1A AE 96 0B E5 4E AB C7 A3 0C 09 82 D6 CD F9 3E 9D 6E C6 73 C5 20 20 F6 8E DF 80 95 13 68 9C 3B C7 EF 71 C9 FC 96 2C 07 48 0A 9A 06 8F 96 7E 90 1F 31 3A 05 86 86 E5 64 5D 5A 08 2C 6C EE 72 7C C2 DF 9B 3C F7 52 5C 17 0E 9B C9 AE 36 8E 54 C9 B5 5E E9 D6 F8 C4 54 81 AC 78 DE 1D 4A C3 31 C6 2E 3F 6D C7 9C FF 5F 7F 88 2C B4 63 CC AC FC 57 1B 84 5D 66 7E C0 14 29 1D 70 74 A8 EE 03 55 07 C2 D7 F5 CE 15 8F 9B DF DA AD C8 F9 33 90 CD 57 98 A1 62 94 E1 3E DD 0F F6 F4 6D 17 35 F1 CE 1D EE 72 72 8B 7A AB B0 7F 68 8F 40 68 96 20 11 BF 05 91 C5 C1 71 AB BF BC A1 9D CC 0B 7D 5F 24 23 3D AB 46 02 43 D6 15 5E 64 BD FD 35 19 C0 47 15 69 C1 EA 0E 9B 5E 8A CD C2 20 CF A7 39 2E 8B D9 89 FC 94 63 18 E9 DD 3F 5B 1B C0 27 4A 85 7A 84 3B C2 50 BC 6A 86 48 0A
"""
.
trimIndent
().
hexToBytes
()
try
{
println
(
data
.
decryptBy
(
"F1 E3 A0 9F AD 63 80 68 43 3C 11 98 53 13 D4 BF"
.
hexToBytes
()))
println
(
data
.
decryptBy
(
ByteArray
(
16
)).
toUHexString
())
println
(
"With key 16 zero"
)
}
catch
(
e
:
Exception
)
{
println
(
data
.
decryptBy
(
"%4;7t>;28<fc.5*6"
.
toByteArray
()).
toUHexString
())
println
(
"With key %4;7t>;28<fc.5*6"
)
}
}
\ No newline at end of file
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt
deleted
100644 → 0
View file @
1ff5df1d
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
androidPacketTests
import
kotlinx.io.core.*
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.qqandroid.network.protocol.packet.*
import
net.mamoe.mirai.utils.cryptor.ECDH
import
net.mamoe.mirai.utils.cryptor.adjustToPublicKey
import
net.mamoe.mirai.utils.cryptor.decryptBy
import
net.mamoe.mirai.utils.io.*
import
net.mamoe.mirai.utils.unzip
/*
*/
fun
main
()
{
val
data
=
"""
06141ef4481120da22db75080800450805aad52640003506283d71600dd0c0a8030a1f90fe5008cee55e8b8c5585501000f989000000000009080000000b01000000000e313939343730313032312dde8c38f7075e6bb46a3d04dfd5d720d12afb2eb067ba4f4595edbefd367615ce43b590f822f561d979fa1d05d94a02da20da629c562f7bd431e8969d5c36ea635429b9e5e0bff6ce8dd15ec796ce2edcd73637ff1f7eb4e1b1e7668abe6f29a9e94a0bba03220c95e89b927307c037a7c9bc2b5255647189b14c50ceb05c5c68d05ac732e23253b365d98295fe6c472db908e1ff173b4d43cda220f8bab1271fbb923b3df0585d582617d27369a1cfa0166b3fb680a0f608fb117f3355bdcdbef7d65d6de5f69b59be79b3836fc6cff3c0892cbb87a4cb1569955a866924509c933d79d423caea20b933b0738df02de26a9d057924191b34a2ec4c153c047886a0160ad536874bf67316c9776348b5a6f0bc4f542d3cab81dfa1198c19e4eeadc8484c4fbe47a3a290a57d02762937ff289a7c3859219d34d19cf00595674e063c1bf6d3f02199a1bbc2dd816c3760bd359b10160050a8bcadae6adcba3901cabb41f0c12d0f740a49e4508468cc60ab016527488ee921ec029d45db3b552bd73cb78942385ea7c1f3eb9b329ac3ad899547e7919a76c43953123e01221e656a2de4a307f068978e87b19db48ca05ac82860043869c8f1675e514dbb828ec98ee0591684a74143807d6a422927af4f448251b7d592cb7a4c8b34a8f13d9f9cf54e37e4c344f9aa54ac44be66b5450f237731fd24c4f42633078cd10907928987a25bdcb4cbe44ae507c700579680e8fb64ccd61dad8e6a9ceeac91ee9419f466bad22d6ac288c1219b00d4fb9009e170d7b5fcac2a672c11aced6a5eb6dd0b3dde1f29446ab6ddef6763661ed69bc8f0ab3520cfc332cdbe53355395bba697f1b6262507f62b1090e0733e351a76647e779d746f46685d36de7ba3d6a37485bba566060c55929f26e4773675ba4680d940e74c94b8cf64054ab298f100ebc7c19950443534a1fa342b53a315275b0004fc07ac95896620adc2f8befbdde38ef75c912cecd5e3a128d2a3f63773fdd0a4507debaddd527b31a19f63ff383b8ce6a00a0f5e2d7fbedcbd3eb9f2746f561b69e8cf0e9a9939e902a6158eea32e2add8f2975889174cbeb382596a3113e2741d47490382245ad4c405a9079779d989116df6fff14b89d1725907f809c4c7a9ff145852330bcde5f3ba26af3953a23b23dfdcaaab5905eaf5c063134ab26e0573dac18bbff2937479c2d658cff8ded19359fe1414c426dcd99bca93d93d1adfe768110837a7d8deb99359b5049babbfc37534609fff1674c549e1fc3f778579c91a8a10fdd2c75dfef12898353631643ce635ce91bd439ef5a0604afd722a3bd79d7bb65c6b17cc71473c9a65ba1562571aa9cd90fc6d245f2d66cb53530236000af5114bf827938bb76bece6549931f47e87d98ddbab02dabda7bd253fd28e7f7508fd48d0291a399eb1b0cb4a0d286838cdcf782de178a1657ad9373e1ef6d776bf6991bfeab8c1b99f17b7457e397f2afed8c4db005e45564b0b6bb8ba4231e014eaa94e113b981e7e7f854e8bdd849653634b51d10d703fed840a73831e228e737fd707a6649af0c3acb1eca585ff2390d8b714f75f2b5a2fed4b946166e26145a845a417a4ddbb93fa87526a18336c63437dc9561e9ff0b9e82440893553e5cb127b73405828adbc4bcc16e2628bece509adf4b5b4c213e30e47ce2b833cf1ae63b66f98e8b0ab82f4699f81101e8fc45c02c392f73d56a05e393c4ef26cc9cb18bda6f06b3c3c5e17256cdd413b1c7e53a36be6e5f8327ef2d74479b0f758f6867099a7954a25170f0598b88d0178e4264597d539f49db904f5af0c5fbaa0d1d3f6e267d271fd88d651d63a5d076b6c9cb83baeeeb9ee07119d5cc0d3301b8580c6f13f01193fe744accaf23d4998a328ba0153e091ff47e72115790ac2ee8bc2760389f6afa1aad5c6fde22b7e030e54260a66c82b
06141ef4481120da22db750808004508040ed5274000350629d871600dd0c0a8030a1f90fe5008ceeae08b8c5855501800f9a1740000352b369d0f6a6ccc3241ebaffcf56f1448539de4424794e92d376150a7d779cbe3bddb9ddfcaa8a5516508f0e151f14905709d11cf5731f02d7a17594878a148d3513e3a40cf2b2083d94e4473933919183c689da0d9870cac1045e1177de021dc8cf35876b628efacc94f76c8e5ac1c52bcd063c40c1c7a507a7bfb6e7cbce4573c10de847f154540c455bcd3fa044dde17f833a53b457011148b769418de4fa0cb03f0f0003548307c15c798f83eff4dbd9ed1ac79fc2ab1e4a699a6ebfd7a2ab8aa41f212480dad50f92a41eef9bab19d8f5c535973e283fe09fefbb96d9c714528c5d05fc83d158ffecd438c30a95f38d58d3df3c554d5b3c0fb2fd75ae0fa38de87092bca3a79e7d934b673d7fcc3d2fb8b35874c199dbd82f51fd2903a3dd3195f0d5eac55c260cd63d4dae3ad90c13979401face609ca6608828a49e91cfc4e0861996bbc608ee8f94d7b32253ee99a627d0d14a0660cac7a4a0f814cb43cff81983b4ff2f9bb19933371be4e837bfea8af9f50810994b3c543eee966b178d423a73837533666efc50ff9c3b0ddca442e85505117ad1ee3d618048e77ff5f151e1305d04c0ed4ab12f38e587003d8648fb1c48328f0d97062b9e1d1e298991572bdc488532211f249b87f554d243ece0359d79c501df79feff20b6e64dee4f72d0eeb4258959dbaa0cdcd5b78544935f2bf8fdc8610623f0380cc3443597cfd2d969eb4b9c8c91583c1a09ad40784309daedc300f1231a51658e050d8146c116974dc8e1b80ccdd9de0e809cc5cf099ff31f3ba0aef36291e885750b482b999b1e8a12cc9831641b6c7afcee924cd9d95ecd4571145963f1f1dd65da39ed33274e57d8103139e8ea523c2c51ca3df4ba25f0d762fa708df0308adc600039f07d5f7968b48b921f6e2a5e597722343456d5e836a3478f907dbd26c9ffff11491be61937ce4e951d3e27321c5ced54c0cbe807eb6b555d63303b1e5835d867bed9237e50eca36b953a52c3ace2106cf0a76e616e64e43f3caf1b3e4c36a6f07ef36a8e4d0c459abb835a8937b011cd7fe22c8cb3a1915d3b35ec071d3ea30149024ddcb5cc6688fb1aaefdfa691fcc1e1abb789c93d9a50a3ae5a3c42f962bb1ea18d0ee41f947025dfe11b553710437c553acc05cd71eb8bcf7312ae7ba93e0999a7d250b3534f8a12ffb0863f813e50099b5ab99488bec6c9a7c3ddbabba689d8d5d074ac7a8a51be0dbd24e83946212900bda167da74502df6a4ca19ea0e96dccac3000000600000000b01000000000e3139393437303130323124091c1e3f66dd4380dfb31d6672e8247ca8334d3bafad5222abe08994b3752544f7fff26d145f9096623e882a351abcd8d62ccb381df30e6087034a250896c23148902ab7673325
06141ef4481120da22db7508080045080326d525400035062ac271600dd0c0a8030a1f90fe5008cee2608b8c5585501800f9cc090000869d266455aceba6d61bb5545b381d7eb65ef8f78b409dce42d67276a3e6d8e26cc58a5b713dad2e5c2690dbab11f9e3750beb6e6213e6c4b852c664a180f0c81421fdc2e5ee1e79aa094eb225e1d54f10706dd69088247dd5a813991d0a776f28023a45177a21a6dabc9b9180c2e9efcccafb1b6801d4b7a548efc5a523be32fa19ab786fc9eb0ab7c5c4bf9d92d77b95125fe19c4cd36ff5c4f55c4dc375a09b2d5c20ebe1025e8d410ca48e27926f18e71e5c12653d7f57e2ab861a7e07881056b1553b54f69602f18e49ccd2f37e80884b16eaa351eb988ce3d683255b29c19361f2b1c3df9384fc51ca53e62ca849ea9d7f84fe042238094200a80f30587ca1f8e02648d1c227a1609f72dac4fa87be933c89c7b8370882fd78c9395501ec38152a84209062fa5f05da560ffc27c8323b785175f6eb9c097a79aa989c7c635ea8d3b70447fe8dc0fd083fd2e3c587857e7fbe39c9153bbb482b8cc61718e4552b1ba2fe4857e4efc9c8bdbd556a48af3ce635f898f32d2f81063b4614c763126ded43cfec3c9a6ee69ffb22ad07ecdc0da74d2a5525969af87a39ea98ccb7fc26e281ed012ba411b215e6be4ee9510a1b66a0176d2569490c8236b5c8749c54fff7873a535efb9f6b0d087c5b84c7f8f78936f2a068678e3666187ce351965995cb62bd9991b9953f3fedefd37c773733cd19f7da499cf81ce27ada2cfdc9834a571e1bb2966d78ade710a4edd4494770f10304f3456f80302c5526187c8fe0a1ede70ea2d456adc593726445181b27ebf450d1414c502aadde9ef2707ea128e8601e4948fbee57f6c2ebfbeec38cdb48364db069ca62119002ba19570cd6b744669f30565b4ebc3ed470394f6dc85da8a085351b10234dc82bd33543780a730904566620ebcebf56c8ab84545847737d55babb66cccee94054e6fc1ffd0bbf0c801c7e29b477a2e2baae339b37b815a8ce8811c7fb87b8fd3be55a1174f6f55f35b5d42c4d96cfb40053fa55efa31a9ae334248567e06d421d227fb9a30765dc4e85b938aabbcedac32901598bc5772ecde797779c7034c22cd81a
"""
.
trimIndent
()
.
trim
().
split
(
"\n"
).
filterNot
{
it
.
isBlank
()
}.
map
{
val
bytes
=
it
.
trim
().
autoHexToBytes
()
if
(
bytes
[
0
].
toInt
()
==
0
)
{
bytes
}
else
bytes
.
dropTCPHead
()
}.
flatMap
{
it
.
toList
()
}.
toByteArray
()
data
.
read
{
decodeMultiServerToClientPackets
()
}
}
fun
ByteReadPacket
.
decodeMultiServerToClientPackets
()
{
PacketLogger
.
enable
()
println
(
"=======================处理服务器到客户端客户端======================="
)
var
count
=
0
while
(
remaining
!=
0L
)
{
processFullPacketWithoutLength
(
readBytes
(
readInt
()
-
4
).
toReadPacket
())
if
(
remaining
!=
0L
)
{
println
()
println
()
println
()
println
()
count
++
}
else
{
DebugLogger
.
info
(
"=======================共有 $count 个包======================="
)
}
}
println
()
}
private
fun
processFullPacketWithoutLength
(
packet
:
ByteReadPacket
)
{
packet
.
debugPrintThis
(
"正在处理"
).
apply
{
require
(
remaining
<
Int
.
MAX_VALUE
)
{
"rawInput is too long"
}
// login
val
flag1
=
readInt
()
PacketLogger
.
verbose
(
"flag1(0A/0B) = ${flag1.toUByte().toUHexString()}"
)
// 00 00 05 30
// 00 00 00 0A // flag 1
// 01 // packet type. 02: sso, 01: uni
//
// 00 00 00 00 0E 31 39 39 34 37 30 31 30 32 31 40 3C 63 DC A2 8F FC E7 09 66 62 11 A3 5A B6 AB DC 6E A1 CA CF E2 0A 6F A8 6D 36 64 4E 22 4B A9 8A ED 07 7A 0A 9E F3 C7 7B 72 EF C1 C7 6E 9A 28 27 10 F8 2A 7F 37 49 B6 48 35 52 E9 CF 2A B5 F3 26 90 33 68 9B 5A 04 2A 8B F5 78 13 82 FE 3C 13 C4 F9 38 39 0E 02 4C 3D 91 0A 2A 94 3F 9F A6 52 B9 14 89 C5 D9 57 0F 96 F8 0E 7D 32 81 8E 10 DB C0 CA BE C7 3F EC D0 B1 F0 9D A2 4B 9F B3 8D E0 EB 1F 42 52 EA 5E 9E 76 E2 F4 13 9D 0E 7E 6D 0A E3 56 C3 EE 8A 80 24 DE FB 08 82 FB B7 AF CE 2A 69 16 E3 C3 79 5C C7 CD 44 BA AA 08 A2 51 0B 43 31 69 A1 12 D1 AE 48 15 AE 76 E9 AB BB D2 E0 16 03 EB 2D 47 A4 61 24 65 5E CC C5 03 B3 96 3E 7A 39 90 3D DB 63 56 2B 23 85 CE 5F 9E 04 20 45 31 79 7B BF 78 33 77 34 C1 8E 83 B3 50 88 2A 01 C0 C4 E4 BF 2D 0D B9 37 32 AB E0 BB 82 36 B1 4E 51 4B F7 07 6A 12 3E 79 EA 93 3D BD 06 4E AE 1C 49 82 17 14 00 09 59 40 A6 A9 01 56 1A 23 86 A8 33 B3 9A 70 7B 3A C1 F9 31 03 FD DB 4B 5C 7B F9 BB 43 94 65 A0 1C DA 2B 85 AA AD 7B 79 42 F2 EB 25 5E F0 DA B7 E7 AD 4B 25 02 36 BB 78 5F 83 7F F7 78 F0 99 D2 B5 A3 0C 4A 7F 0E B0 A6 C4 99 F7 9E 0B C6 4D FC F5 8D 6B 5F 35 27 36 D3 DB D0 46 C7 10 76 7D 96 91 48 EA 1C B2 B7 D7 2F D2 88 A8 4C 87 D6 A9 40 33 4C 76 C5 48 3E 32 4D C1 C3 7F 5C D9 B3 22 00 88 BE 04 82 64 A9 73 AA E1 65 1A EF 49 B4 54 74 53 FF 75 B6 E9 57 1B 89 2D 6F 2A 6A CE 23 BF 41 CB 55 B3 A0 53 87 AD A0 22 EE 6B 3F 4A 97 23 36 BF 7E 08 2D 0A 9E 2E 4B F2 2E 00 59 EC F1 21 34 45 75 DB 6B F2 EC 65 24 30 69 50 CC 45 78 00 AF C8 F6 3D 8E 03 60 CF CA A1 88 14 18 82 6F 56 58 D0 BC E0 48 FD AA 86 63 CA C1 01 63 07 16 4A 79 79 17 9D 1F E2 40 4B B6 77 6E 44 84 DE BE 02 4C 33 7A F5 2F 93 21 3E 17 62 38 81 95 E6 84 8B 7C C8 7B E2 23 FB 12 4F E8 42 5F 1D 48 92 84 B1 45 FF 69 97 3C 30 C9 09 E8 84 E8 07 0E 17 D4 A1 CC 35 D6 FE 7B D2 9A 44 8B 17 BF E7 D6 98 1D 98 D7 30 BE 55 19 A9 F4 D6 0D E8 18 80 35 85 B6 AB B9 20 32 C7 ED C6 AD A7 AE 19 48 B7 17 02 B3 45 C3 A2 B9 C9 B7 58 B5 8B 4C AF 52 AD A1 E1 62 45 AB 58 26 67 20 C7 64 AA DA 7E F3 70 8B C2 92 69 E3 3E 3E 6F 39 6F 2B 35 35 0F 00 FC 52 B5 5C 5B 73 FE F6 F5 10 55 36 7C 9A 84 FC A6 23 29 4A 75 49 7C 13 1C CA 54 A2 A2 FA 2A 63 A5 4C 9A B4 27 E8 5F 9F 23 96 B2 E7 AA E6 8B E0 E2 6A 75 8A B2 F4 E4 7E 09 E8 22 70 2A 42 8B E3 DC AD E8 A8 A2 92 71 6B A2 12 78 E1 DA CC 70 57 67 F5 B4 52 F3 B4 4C 17 AB 05 33 DA 6E 47 52 C5 B2 B7 9A D2 A8 BC 44 64 D3 26 1A 6B C6 C5 36 1C 2B 8F BD B7 27 91 3E C0 C2 FC 03 41 FE 02 D3 4B B1 E5 5F 5B 50 05 29 BD 3A 64 85 E3 8C FB 11 F2 1D 94 DB D7 78 AF AD 77 A3 9C D4 39 5D 8B EA DF 9D 08 CA 92 7C 5F D5 17 49 0E FA A1 21 1C 9F C3 88 1A DC E7 D8 82 80 85 86 32 99 15 E4 89 BA 91 2B 4B FB 87 EC 44 B4 D9 83 CC 79 77 A4 A0 D0 50 E3 4F 00 E7 DA DA 79 38 1E D8 04 86 16 CD 25 BE BA 76 E4 8C F9 86 91 69 6E C7 A0 EF 6B 44 2B C9 C3 DC 8D 2D 65 60 7A F4 37 02 D4 8F 38 D0 D5 20 30 DE A5 F5 A8 75 C7 EE 0B 0F 1B 88 C2 8A CC 6F 70 1D E4 D8 4E DD 04 A5 5B B8 04 B1 29 42 08 92 19 78 E2 26 EB 6B 07 49 DE 8A AF A3 41 72 1D E2 3C 62 0F 7E 7B DE A3 0F 71 8C 5D EC E9 96 96 45 A9 39 33 8A 87 C9 93 CE 3B 6D 75 50 21 1F 4C 03 E9 A7 AD 03 0F 5E A9 EE 60 CC EA 05 4F DF E1 B1 13 A6 7D C7 B9 37 58 53 3B 06 1A AD 98 E5 06 D9 74 2A B1 96 75 DE A6 B7 89 25 53 2A A3 07 B6 70 C6 86 1F 59 EB 53 08 57 6E 86 D7 A1 5C DB 26 D7 86 3E 97 BB FD 6A 0A 4C E1 81 B9 4C C1 A0 49 89 57 29 E0 CD 79 6F 0A 46 C1 C6 62 75 49 C6 9A B9 22 75 EE 10 C7 56 E6 D5 DE 4D EC 89 5A 6F AC 60 0F B3 CC 37 9E F2 BE 49 A7 77 3C 05 AE 92 66 C8 BE 16 E5 35 17 24 18 A5 CE B8 BB AE CD 88 DE 01 53 40 84 E0 06 C6 77 96 09 DF D7 76 3B CA C9 B5 B2 91 95 07 54 6F 51 EB 12 58 16 8A AF C3 E3 B9 4A EC 25 A5 D1 19 59 72 F5 E3 4F 7C 40 B2 D0 4E 9F 50 13 FB 86 C3 6A 88 32 5B 67 EC 4F 0E 0B 31 F8 0C 02 6C CE 8D 50 55 A2 B3 57 73 7C 78 D3 43 1F 48 33 51 E7 0A D0 6D 46 71 4A AD 66 50 F9 96 11 4F A5 5B 3C A0 3E 46 D2 CB 3B A1 03 84 9C 8E 4E 2D 83 69 2E 17 9B F8 36 63 F1 93 CA F9 32 57 2B AB 4E 14 A3 5A F1 39 B0 3F 0F 99 CC 9B FB 7E BC 0A AA C9 65 3C C8 B4 B0 1F
val
flag2
=
readByte
().
toInt
()
PacketLogger
.
verbose
(
"包类型(flag2) = $flag2. (可能是 ${if (flag2 == 2) "
sso
" else "
uni
"})"
)
val
flag3
=
readByte
().
toInt
()
readString
(
readInt
()
-
4
)
//uin
//debugPrint("remaining")
(
if
(
flag2
==
2
)
{
//PacketLogger.verbose("SSO, 尝试使用 16 zero 解密.")
kotlin
.
runCatching
{
decryptBy
(
DECRYPTER_16_ZERO
).
also
{
PacketLogger
.
verbose
(
"成功使用 16 zero 解密"
)
}
}
}
else
{
//PacketLogger.verbose("Uni, 尝试使用 d2Key 解密.")
kotlin
.
runCatching
{
decryptBy
(
D2Key
).
also
{
PacketLogger
.
verbose
(
"成功使用 d2Key 解密"
)
}
}
}).
getOrElse
{
PacketLogger
.
verbose
(
"解密失败, 尝试其他各种key"
)
this
.
readBytes
().
tryDecryptOrNull
()
?.
toReadPacket
()
}
?.
debugPrintThis
(
"sso/uni body="
)
?.
let
{
if
(
flag1
==
0
x0A
)
{
parseSsoFrame
(
flag3
,
it
)
}
else
{
parseSsoFrame
(
flag3
,
it
)
}
}
?.
let
{
val
bytes
=
it
.
data
.
readBytes
()
if
(
flag2
==
2
&&
it
.
packetFactory
!=
null
)
{
PacketLogger
.
debug
(
"Oicq Reuqest= "
+
bytes
.
toUHexString
())
try
{
bytes
.
toReadPacket
().
parseOicqResponse
{
debugIfFail
{
if
((
it
.
packetFactory
as
?
OutgoingPacketFactory
<
*
>)
?.
commandName
==
"wtlogin.login"
)
{
DebugLogger
.
info
(
"服务器发来了 wtlogin.login. 正在解析 key"
)
try
{
val
subCommand
=
readUShort
().
toInt
()
println
(
"subCommand=$subCommand"
)
val
type
=
readUByte
().
toInt
()
println
(
"type=$type"
)
discardExact
(
2
)
val
tlvMap
:
Map
<
Int
,
ByteArray
>
=
this
.
readTLVMap
()
println
(
"tlvMap: "
)
tlvMap
.
forEach
{
println
(
it
.
key
.
toShort
().
toUHexString
()
+
" = "
+
it
.
value
.
toUHexString
())
}
tlvMap
[
0
x119
]
?.
let
{
t119Data
->
t119Data
.
decryptBy
(
tgtgtKey
).
toReadPacket
().
debugPrintThis
(
"0x119data"
).
apply
{
discardExact
(
2
)
// always discarded. 00 1C
// 00 1C
// 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00
val
tlvMap119
=
this
.
readTLVMap
()
userStKey
=
tlvMap119
.
getOrEmpty
(
0
x10e
)
wtSessionTicketKey
=
tlvMap119
.
getOrEmpty
(
0
x133
)
D2Key
=
tlvMap119
.
getOrEmpty
(
0
x305
)
DebugLogger
.
info
(
"userStKey=${userStKey.toUHexString()}"
)
DebugLogger
.
info
(
"wtSessionTicketKey=${wtSessionTicketKey.toUHexString()}"
)
DebugLogger
.
info
(
"D2Key=${D2Key.toUHexString()}"
)
}
}
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
}
}
}
}
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
}
}
else
// always discarded. 00 1C
// 00 1C
// 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00
{
PacketLogger
.
debug
(
"不是oicq response(可能是 UNI/PB)= "
+
bytes
.
toUHexString
())
}
}
?:
inline
{
PacketLogger
.
error
(
"任何key都无法解密"
)
return
}
}
}
private
fun
Map
<
Int
,
ByteArray
>.
getOrEmpty
(
key
:
Int
):
ByteArray
{
return
this
[
key
]
?:
byteArrayOf
()
}
var
randomKey
:
ByteArray
=
byteArrayOf
()
private
fun
ByteReadPacket
.
parseOicqResponse
(
body
:
ByteReadPacket
.()
->
Unit
)
{
readIoBuffer
(
readInt
()
-
4
).
withUse
{
check
(
readByte
().
toInt
()
==
2
)
this
.
discardExact
(
2
)
// 27 + 2 + body.size
this
.
discardExact
(
2
)
// const, =8001
this
.
readUShort
()
// commandId
this
.
readShort
()
// const, =0x0001
this
.
readUInt
().
toLong
()
// qq
val
encryptionMethod
=
this
.
readUShort
().
toInt
()
this
.
discardExact
(
1
)
// const = 0
@Suppress
(
"UNUSED_VARIABLE"
)
val
packet
=
when
(
encryptionMethod
)
{
4
->
{
// peer public key, ECDH
var
data
=
this
.
decryptBy
(
shareKeyCalculatedByConstPubKey
,
0
,
this
.
readRemaining
-
1
)
data
.
read
{
println
(
"第一层解密: ${data.toUHexString()}"
)
val
peerShareKey
=
ECDH
.
calculateShareKey
(
loadPrivateKey
(
ecdhPrivateKeyS
),
readUShortLVByteArray
().
adjustToPublicKey
())
body
(
this
.
decryptBy
(
peerShareKey
))
}
}
0
->
{
val
data
=
if
(
0
==
0
)
{
ByteArrayPool
.
useInstance
{
byteArrayBuffer
->
val
size
=
this
.
readRemaining
-
1
this
.
readFully
(
byteArrayBuffer
,
0
,
size
)
runCatching
{
byteArrayBuffer
.
decryptBy
(
shareKeyCalculatedByConstPubKey
,
size
)
}.
getOrElse
{
byteArrayBuffer
.
decryptBy
(
randomKey
,
size
)
}
// 这里实际上应该用 privateKey(另一个random出来的key)
}
}
else
{
this
.
decryptBy
(
randomKey
,
0
,
this
.
readRemaining
-
1
)
}
PacketLogger
.
info
(
"OicqRequest, Real body="
+
data
.
toUHexString
())
body
(
data
.
toReadPacket
())
}
else
->
error
(
"Illegal encryption method. expected 0 or 4, got $encryptionMethod"
)
}
}
}
fun
ByteReadPacket
.
readIoBuffer
(
n
:
Int
=
remaining
.
toInt
()
//not that safe but adequate
):
IoBuffer
=
IoBuffer
.
Pool
.
borrow
().
also
{
this
.
readFully
(
it
,
n
)
}
/**
* 解析 SSO 层包装
*/
@UseExperimental
(
ExperimentalUnsignedTypes
::
class
)
private
fun
parseSsoFrame
(
flag3
:
Int
,
input
:
ByteReadPacket
):
KnownPacketFactories
.
IncomingPacket
<
*
>
{
val
commandName
:
String
val
ssoSequenceId
:
Int
// head
input
.
readIoBuffer
(
input
.
readInt
()
-
4
).
withUse
{
ssoSequenceId
=
readInt
()
PacketLogger
.
verbose
(
"sequenceId = $ssoSequenceId"
)
check
(
readInt
()
==
0
)
val
extraData
=
readBytes
(
readInt
()
-
4
)
PacketLogger
.
verbose
(
"sso(inner)extraData = ${extraData.toUHexString()}"
)
commandName
=
readString
(
readInt
()
-
4
)
DebugLogger
.
warning
(
"commandName=$commandName"
)
readBytes
(
readInt
()
-
4
)
// unknown, sessionId?
//if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}")
check
(
readInt
()
==
0
)
}
// body
val
packetFactory
=
KnownPacketFactories
.
findPacketFactory
(
commandName
)
if
(
packetFactory
==
null
)
{
println
(
"找不到包 PacketFactory"
)
PacketLogger
.
verbose
(
"传递给 PacketFactory 的数据 = ${input.readBytes().toUHexString()}"
)
}
var
data
=
input
.
readBytes
()
if
(
flag3
==
1
)
{
data
=
data
.
unzip
(
offset
=
4
)
}
else
{
}
return
KnownPacketFactories
.
IncomingPacket
(
packetFactory
,
ssoSequenceId
,
data
.
toReadPacket
(),
commandName
)
}
/**
* 解析 Uni 层包装
*/
@UseExperimental
(
ExperimentalUnsignedTypes
::
class
)
private
fun
parseUniFrame
(
input
:
ByteReadPacket
):
KnownPacketFactories
.
IncomingPacket
<
*
>
{
// 00 00 00 30 00 01 2F 7C 00 00 00 00 00 00 00 04 00 00 00 14 67 78 68 72 65 70 6F 72 74 2E 72 65 70 6F 72 74 00 00 00 08 66 82 D3 0B 00 00 00 00
// 00 00 00 06 08 00
//00 00 00 2D 00 01 2F 7E 00 00 00 00 00 00 00 04 00 00 00 11 4F 69 64 62 53 76 63 2E 30 78 35 39 66 00 00 00 08 66 82 D3 0B 00 00 00 00
// 00 00 00 19 08 9F 0B 10 01 18 00 22 0C 10 00 18 00 20 00 A8 01 00 A0 06 01
val
commandName
:
String
val
ssoSequenceId
:
Int
// head
input
.
readIoBuffer
(
input
.
readInt
()
-
4
).
withUse
{
ssoSequenceId
=
readInt
()
PacketLogger
.
verbose
(
"sequenceId = $ssoSequenceId"
)
check
(
readInt
()
==
0
)
val
extraData
=
readBytes
(
readInt
()
-
4
)
PacketLogger
.
verbose
(
"sso(inner)extraData = ${extraData.toUHexString()}"
)
commandName
=
readString
(
readInt
()
-
4
)
DebugLogger
.
warning
(
"commandName=$commandName"
)
readBytes
(
readInt
()
-
4
)
// unknown
//if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}")
check
(
readInt
()
==
0
)
}
// body
val
packetFactory
=
KnownPacketFactories
.
findPacketFactory
(
commandName
)
if
(
packetFactory
==
null
)
{
println
(
"找不到包 PacketFactory"
)
PacketLogger
.
verbose
(
"传递给 PacketFactory 的数据 = ${input.readBytes().toUHexString()}"
)
}
return
KnownPacketFactories
.
IncomingPacket
(
packetFactory
,
ssoSequenceId
,
input
,
commandName
)
}
private
inline
fun
<
R
>
inline
(
block
:
()
->
R
):
R
=
block
()
mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/utils.kt
deleted
100644 → 0
View file @
1ff5df1d
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"DEPRECATION"
)
package
androidPacketTests
import
net.mamoe.mirai.utils.cryptor.decryptBy
import
org.bouncycastle.jce.provider.JCEECPrivateKey
import
org.bouncycastle.jce.spec.ECParameterSpec
import
org.bouncycastle.jce.spec.ECPrivateKeySpec
import
org.bouncycastle.math.ec.ECConstants
import
org.bouncycastle.math.ec.ECCurve
import
org.bouncycastle.util.encoders.Hex
import
java.math.BigInteger
import
java.security.interfaces.ECPrivateKey
fun
ByteArray
.
decryptBy16Zero
()
=
this
.
decryptBy
(
ByteArray
(
16
))
fun
ByteArray
.
dropTCPHead
():
ByteArray
=
this
.
drop
(
16
*
3
+
6
).
toByteArray
()
@Suppress
(
"LocalVariableName"
)
fun
loadPrivateKey
(
s
:
String
):
ECPrivateKey
{
fun
fromHex
(
hex
:
String
):
BigInteger
{
return
BigInteger
(
1
,
Hex
.
decode
(
hex
))
}
// p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1
// p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1
val
p
=
fromHex
(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"
)
val
a
=
ECConstants
.
ZERO
val
b
=
BigInteger
.
valueOf
(
3
)
val
n
=
fromHex
(
"FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"
)
val
h
=
BigInteger
.
valueOf
(
1
)
val
curve
:
ECCurve
=
ECCurve
.
Fp
(
p
,
a
,
b
)
//ECPoint G = curve.decodePoint(Hex.decode("03"
//+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D"));
//ECPoint G = curve.decodePoint(Hex.decode("03"
//+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D"));
val
G
=
curve
.
decodePoint
(
Hex
.
decode
(
"04"
+
"DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D"
+
"9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D"
)
)
return
JCEECPrivateKey
(
"EC"
,
ECPrivateKeySpec
(
fromHex
(
s
),
ECParameterSpec
(
curve
,
G
,
n
,
h
)
)
)
// return KeyFactory.getInstance("ECDH").generatePrivate(PKCS8EncodedKeySpec(s))
}
mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
View file @
932a3ef1
...
...
@@ -17,7 +17,7 @@ import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.JceOutput
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.buildJcePacket
import net.mamoe.mirai.utils.c
ryptor.c
ontentToString
import net.mamoe.mirai.utils.contentToString
import net.mamoe.mirai.utils.io.toUHexString
import kotlin.test.Test
import kotlin.test.assertEquals
...
...
mirai-core-qqandroid/src/jvmTest/kotlin/test/protoBuf.kt
0 → 100644
View file @
932a3ef1
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"unused"
,
"NO_REFLECTION_IN_CLASS_PATH"
)
package
net.mamoe.mirai.utils.cryptor
import
net.mamoe.mirai.utils.MiraiDebugAPI
// ProtoBuf utilities
@Suppress
(
"FunctionName"
,
"SpellCheckingInspection"
)
/*
* Type Meaning Used For
* 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
* 1 64-bit fixed64, sfixed64, double
* 2 Length-delimi string, bytes, embedded messages, packed repeated fields
* 3 Start group Groups (deprecated)
* 4 End group Groups (deprecated)
* 5 32-bit fixed32, sfixed32, float
*
* https://www.jianshu.com/p/f888907adaeb
*/
@MiraiDebugAPI
fun
ProtoFieldId
(
serializedId
:
UInt
):
ProtoFieldId
=
ProtoFieldId
(
protoFieldNumber
(
serializedId
),
protoType
(
serializedId
)
)
@MiraiDebugAPI
data class
ProtoFieldId
(
val
fieldNumber
:
Int
,
val
type
:
ProtoType
)
{
override
fun
toString
():
String
=
"$type $fieldNumber"
}
@Suppress
(
"SpellCheckingInspection"
)
@MiraiDebugAPI
enum
class
ProtoType
(
val
value
:
Byte
,
private
val
typeName
:
String
)
{
/**
* int32, int64, uint32, uint64, sint32, sint64, bool, enum
*/
VAR_INT
(
0
x00
,
"varint"
),
/**
* fixed64, sfixed64, double
*/
BIT_64
(
0
x01
,
" 64bit"
),
/**
* string, bytes, embedded messages, packed repeated fields
*/
LENGTH_DELIMI
(
0
x02
,
"delimi"
),
/**
* Groups (deprecated)
*/
START_GROUP
(
0
x03
,
"startg"
),
/**
* Groups (deprecated)
*/
END_GROUP
(
0
x04
,
" endg"
),
/**
* fixed32, sfixed32, float
*/
BIT_32
(
0
x05
,
" 32bit"
),
;
override
fun
toString
():
String
=
this
.
typeName
companion
object
{
fun
valueOf
(
value
:
Byte
):
ProtoType
=
values
().
firstOrNull
{
it
.
value
==
value
}
?:
error
(
"Unknown ProtoType $value"
)
}
}
/**
* 由 ProtoBuf 序列化后的 id 得到类型
*
* serializedId = (fieldNumber << 3) | wireType
*/
@MiraiDebugAPI
fun
protoType
(
number
:
UInt
):
ProtoType
=
ProtoType
.
valueOf
(
number
.
toInt
().
shl
(
29
).
ushr
(
29
).
toByte
())
/**
* ProtoBuf 序列化后的 id 转为序列前标记的 id
*
* serializedId = (fieldNumber << 3) | wireType
*/
@MiraiDebugAPI
fun
protoFieldNumber
(
number
:
UInt
):
Int
=
number
.
toInt
().
ushr
(
3
)
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/BotConfigurationAndroid.kt
View file @
932a3ef1
...
...
@@ -15,23 +15,6 @@ import net.mamoe.mirai.network.BotNetworkHandler
import
kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.EmptyCoroutineContext
/**
* 在各平台实现的默认的验证码处理器.
*/
actual
var
defaultLoginSolver
:
LoginSolver
=
object
:
LoginSolver
()
{
override
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
{
error
(
"should be implemented manually by you"
)
}
override
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
{
error
(
"should be implemented manually by you"
)
}
override
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
{
error
(
"should be implemented manually by you"
)
}
}
@Suppress
(
"ClassName"
,
"PropertyName"
)
actual
open
class
BotConfiguration
actual
constructor
()
{
/**
...
...
@@ -76,7 +59,7 @@ actual open class BotConfiguration actual constructor() {
/**
* 验证码处理器
*/
actual
var
loginSolver
:
LoginSolver
=
defaultLoginSolver
actual
var
loginSolver
:
LoginSolver
=
LoginSolver
.
Default
actual
companion
object
{
/**
...
...
@@ -115,4 +98,31 @@ inline class FileBasedDeviceInfo @BotConfigurationDsl constructor(val filepath:
*/
@BotConfigurationDsl
companion
object
ByDeviceDotJson
}
/**
* 验证码, 设备锁解决器
*/
actual
abstract
class
LoginSolver
{
actual
abstract
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
actual
abstract
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
actual
abstract
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
actual
companion
object
{
actual
val
Default
:
LoginSolver
get
()
=
object
:
LoginSolver
()
{
override
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
{
error
(
"should be implemented manually by you"
)
}
override
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
{
error
(
"should be implemented manually by you"
)
}
override
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
{
error
(
"should be implemented manually by you"
)
}
}
}
}
\ No newline at end of file
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
View file @
932a3ef1
package
net.mamoe.mirai.utils
import
android.os.Build
private
var
isAddSuppressedSupported
:
Boolean
=
true
@MiraiInternalAPI
...
...
@@ -9,7 +11,11 @@ actual fun Throwable.addSuppressed(e: Throwable) {
return
}
try
{
this
.
addSuppressed
(
e
)
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
KITKAT
)
{
this
.
addSuppressed
(
e
)
}
else
{
isAddSuppressedSupported
=
false
}
}
catch
(
e
:
Exception
)
{
isAddSuppressedSupported
=
false
}
...
...
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
deleted
100644 → 0
View file @
1ff5df1d
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package
net.mamoe.mirai.utils.cryptor
import
net.mamoe.mirai.utils.MiraiDebugAPI
import
java.lang.reflect.Field
import
kotlin.reflect.full.allSuperclasses
@MiraiDebugAPI
actual
fun
Any
.
contentToStringReflectively
(
prefix
:
String
,
filter
:
((
name
:
String
,
value
:
Any
?)
->
Boolean
)?):
String
{
return
(
this
::
class
.
simpleName
?:
"<UnnamedClass>"
)
+
"#"
+
this
::
class
.
hashCode
()
+
" {\n"
+
this
.
allFieldsFromSuperClassesMatching
{
it
.
name
.
startsWith
(
"net.mamoe.mirai"
)
}
.
distinctBy
{
it
.
name
}
.
filterNot
{
it
.
name
.
contains
(
"$"
)
||
it
.
name
==
"Companion"
||
it
.
isSynthetic
||
it
.
name
==
"serialVersionUID"
}
.
joinToStringPrefixed
(
prefix
=
prefix
)
{
it
.
isAccessible
=
true
if
(
filter
!=
null
)
{
kotlin
.
runCatching
{
if
(!
filter
(
it
.
name
,
it
.
get
(
this
)))
return
@
joinToStringPrefixed
""
}
}
it
.
name
+
"="
+
kotlin
.
runCatching
{
val
value
=
it
.
get
(
this
)
if
(
value
==
this
)
"<this>"
else
value
.
contentToString
(
prefix
)
}.
getOrElse
{
"<!>"
}
}
+
"\n$prefix}"
}
internal
fun
Any
.
allFieldsFromSuperClassesMatching
(
classFilter
:
(
Class
<
out
Any
>)
->
Boolean
):
Sequence
<
Field
>
{
return
(
this
::
class
.
java
.
takeIf
(
classFilter
)
?.
declaredFields
?.
asSequence
()
?:
sequenceOf
<
Field
>())
+
this
::
class
.
allSuperclasses
.
asSequence
()
.
map
{
it
.
java
}
.
filter
(
classFilter
)
.
flatMap
{
it
.
declaredFields
.
asSequence
()
}
}
\ No newline at end of file
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt
View file @
932a3ef1
...
...
@@ -20,8 +20,6 @@ import java.nio.channels.DatagramChannel
import
java.nio.channels.ReadableByteChannel
import
java.nio.channels.WritableByteChannel
actual
typealias
ClosedChannelException
=
java
.
nio
.
channels
.
ClosedChannelException
/**
* 多平台适配的 DatagramChannel.
*/
...
...
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
View file @
932a3ef1
...
...
@@ -75,6 +75,7 @@ private inline fun InputStream.readInSequence(block: (Int) -> Unit) {
}
}
@UseExperimental
(
MiraiInternalAPI
::
class
)
actual
fun
ByteArray
.
unzip
(
offset
:
Int
,
length
:
Int
):
ByteArray
{
this
.
checkOffsetAndLength
(
offset
,
length
)
if
(
length
==
0
)
return
ByteArray
(
0
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt
View file @
932a3ef1
...
...
@@ -22,7 +22,6 @@ import net.mamoe.mirai.network.ForceOfflineException
import
net.mamoe.mirai.network.LoginFailedException
import
net.mamoe.mirai.network.closeAndJoin
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.io.logStacktrace
import
kotlin.coroutines.CoroutineContext
/*
...
...
@@ -144,17 +143,16 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
}
suspend
fun
doInit
()
{
repeat
(
2
)
{
try
{
_network
.
init
()
return
}
catch
(
e
:
Exception
)
{
e
.
logStacktrace
()
tryNTimesOrException
(
2
)
{
if
(
it
!=
0
)
{
delay
(
3000
)
logger
.
warning
(
"Init failed. Retrying in 3s..."
)
}
logger
.
warning
(
"Init failed. Retrying in 3s..."
)
delay
(
3000
)
_network
.
init
()
}
?.
let
{
network
.
logger
.
error
(
it
)
logger
.
error
(
"cannot init. some features may be affected"
)
}
logger
.
error
(
"cannot init. some features may be affected"
)
}
logger
.
info
(
"Initializing BotNetworkHandler"
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
View file @
932a3ef1
...
...
@@ -17,7 +17,6 @@ import net.mamoe.mirai.event.EventDisabled
import
net.mamoe.mirai.event.Listener
import
net.mamoe.mirai.event.ListeningStatus
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.io.logStacktrace
import
kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.coroutineContext
import
kotlin.jvm.JvmField
...
...
@@ -65,8 +64,8 @@ internal class Handler<in E : Event>
MiraiLogger
.
warning
(
"""Event processing: An exception occurred but no CoroutineExceptionHandler found,
either in coroutineContext from Handler job, or in subscriberContext"""
.
trimIndent
()
,
e
)
e
.
logStacktrace
(
"Event processing(No CoroutineExceptionHandler found)"
)
}
// this.complete() // do not `completeExceptionally`, otherwise parentJob will fai`l.
// ListeningStatus.STOPPED
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt
View file @
932a3ef1
...
...
@@ -34,7 +34,6 @@ import kotlin.coroutines.EmptyCoroutineContext
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
CoroutineScope
.
subscribeMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
MessagePacket
<
*
,
*
>>.()
->
R
...
...
@@ -60,7 +59,6 @@ inline fun <R> CoroutineScope.subscribeMessages(
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
CoroutineScope
.
subscribeGroupMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
GroupMessage
>.()
->
R
...
...
@@ -81,7 +79,6 @@ inline fun <R> CoroutineScope.subscribeGroupMessages(
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
CoroutineScope
.
subscribeFriendMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
FriendMessage
>.()
->
R
...
...
@@ -102,7 +99,6 @@ inline fun <R> CoroutineScope.subscribeFriendMessages(
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
Bot
.
subscribeMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
MessagePacket
<
*
,
*
>>.()
->
R
...
...
@@ -125,7 +121,6 @@ inline fun <R> Bot.subscribeMessages(
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
Bot
.
subscribeGroupMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
GroupMessage
>.()
->
R
...
...
@@ -146,7 +141,6 @@ inline fun <R> Bot.subscribeGroupMessages(
* @see CoroutineScope.incoming
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
inline
fun
<
R
>
Bot
.
subscribeFriendMessages
(
coroutineContext
:
CoroutineContext
=
EmptyCoroutineContext
,
crossinline
listeners
:
MessageSubscribersBuilder
<
FriendMessage
>.()
->
R
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
View file @
932a3ef1
...
...
@@ -18,18 +18,17 @@ import kotlin.jvm.JvmStatic
/**
* 验证码, 设备锁解决器
*/
abstract
class
LoginSolver
{
expect
abstract
class
LoginSolver
{
abstract
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
abstract
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
abstract
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
}
/**
* 在各平台实现的默认的验证码处理器.
*/
expect
var
defaultLoginSolver
:
LoginSolver
companion
object
{
val
Default
:
LoginSolver
}
}
/**
* [Bot] 配置
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
View file @
932a3ef1
...
...
@@ -29,6 +29,7 @@ import kotlin.jvm.JvmName
* 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst]
*/
suspend
fun
ByteReadChannel
.
copyTo
(
dst
:
OutputStream
)
{
@UseExperimental
(
MiraiInternalAPI
::
class
)
ByteArrayPool
.
useInstance
{
do
{
val
size
=
this
.
readAvailable
(
it
)
...
...
@@ -41,6 +42,7 @@ suspend fun ByteReadChannel.copyTo(dst: OutputStream) {
* 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst]
*/
suspend
fun
ByteReadChannel
.
copyTo
(
dst
:
Output
)
{
@UseExperimental
(
MiraiInternalAPI
::
class
)
ByteArrayPool
.
useInstance
{
do
{
val
size
=
this
.
readAvailable
(
it
)
...
...
@@ -72,6 +74,7 @@ suspend fun ByteReadChannel.copyTo(dst: kotlinx.coroutines.io.ByteWriteChannel)
*/
suspend
fun
ByteReadChannel
.
copyAndClose
(
dst
:
OutputStream
)
{
try
{
@UseExperimental
(
MiraiInternalAPI
::
class
)
ByteArrayPool
.
useInstance
{
do
{
val
size
=
this
.
readAvailable
(
it
)
...
...
@@ -88,6 +91,7 @@ suspend fun ByteReadChannel.copyAndClose(dst: OutputStream) {
*/
suspend
fun
ByteReadChannel
.
copyAndClose
(
dst
:
Output
)
{
try
{
@UseExperimental
(
MiraiInternalAPI
::
class
)
ByteArrayPool
.
useInstance
{
do
{
val
size
=
this
.
readAvailable
(
it
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/c
ryptor/protoBuf
.kt
→
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/c
ontentToString
.kt
View file @
932a3ef1
...
...
@@ -9,152 +9,36 @@
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
,
"unused"
,
"NO_REFLECTION_IN_CLASS_PATH"
)
package
net.mamoe.mirai.utils
.cryptor
package
net.mamoe.mirai.utils
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.readBytes
import
kotlinx.io.core.readUInt
import
kotlinx.io.core.readULong
import
net.mamoe.mirai.utils.MiraiDebugAPI
import
net.mamoe.mirai.utils.MiraiExperimentalAPI
import
net.mamoe.mirai.utils.io.*
import
kotlin.jvm.JvmStatic
import
net.mamoe.mirai.utils.io.toUHexString
import
kotlin.reflect.KClass
import
kotlin.reflect.KProperty0
// ProtoBuf utilities
@Suppress
(
"FunctionName"
,
"SpellCheckingInspection"
)
/*
* Type Meaning Used For
* 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
* 1 64-bit fixed64, sfixed64, double
* 2 Length-delimi string, bytes, embedded messages, packed repeated fields
* 3 Start group Groups (deprecated)
* 4 End group Groups (deprecated)
* 5 32-bit fixed32, sfixed32, float
*
* https://www.jianshu.com/p/f888907adaeb
*/
@MiraiDebugAPI
fun
ProtoFieldId
(
serializedId
:
UInt
):
ProtoFieldId
=
ProtoFieldId
(
protoFieldNumber
(
serializedId
),
protoType
(
serializedId
)
)
@MiraiDebugAPI
data class
ProtoFieldId
(
val
fieldNumber
:
Int
,
val
type
:
ProtoType
)
{
override
fun
toString
():
String
=
"$type $fieldNumber"
}
@Suppress
(
"SpellCheckingInspection"
)
@MiraiDebugAPI
enum
class
ProtoType
(
val
value
:
Byte
,
private
val
typeName
:
String
)
{
/**
* int32, int64, uint32, uint64, sint32, sint64, bool, enum
*/
VAR_INT
(
0
x00
,
"varint"
),
/**
* fixed64, sfixed64, double
*/
BIT_64
(
0
x01
,
" 64bit"
),
/**
* string, bytes, embedded messages, packed repeated fields
*/
LENGTH_DELIMI
(
0
x02
,
"delimi"
),
/**
* Groups (deprecated)
*/
START_GROUP
(
0
x03
,
"startg"
),
/**
* Groups (deprecated)
*/
END_GROUP
(
0
x04
,
" endg"
),
/**
* fixed32, sfixed32, float
*/
BIT_32
(
0
x05
,
" 32bit"
),
;
override
fun
toString
():
String
=
this
.
typeName
companion
object
{
fun
valueOf
(
value
:
Byte
):
ProtoType
=
values
().
firstOrNull
{
it
.
value
==
value
}
?:
error
(
"Unknown ProtoType $value"
)
}
}
/**
* 由 ProtoBuf 序列化后的 id 得到类型
*
* serializedId = (fieldNumber << 3) | wireType
*/
@MiraiDebugAPI
fun
protoType
(
number
:
UInt
):
ProtoType
=
ProtoType
.
valueOf
(
number
.
toInt
().
shl
(
29
).
ushr
(
29
).
toByte
())
/**
* ProtoBuf 序列化后的 id 转为序列前标记的 id
*
* serializedId = (fieldNumber << 3) | wireType
*/
@MiraiDebugAPI
fun
protoFieldNumber
(
number
:
UInt
):
Int
=
number
.
toInt
().
ushr
(
3
)
@MiraiDebugAPI
class
ProtoMap
(
map
:
MutableMap
<
ProtoFieldId
,
Any
>)
:
MutableMap
<
ProtoFieldId
,
Any
>
by
map
{
companion
object
{
@JvmStatic
internal
val
indent
:
String
=
" "
}
override
fun
toString
():
String
{
return
this
.
entries
.
joinToString
(
prefix
=
"ProtoMap(size=$size){\n$indent"
,
postfix
=
"\n}"
,
separator
=
"\n$indent"
)
{
"${it.key}="
+
it
.
value
.
contentToString
()
}
}
fun
toStringPrefixed
(
prefix
:
String
):
String
{
return
this
.
entries
.
joinToString
(
prefix
=
"$prefix$indent"
,
separator
=
"\n$prefix$indent"
)
{
"${it.key}="
+
it
.
value
.
contentToString
(
prefix
)
}
}
/*
override fun put(key: ProtoFieldId, value: Any): Any? {
println("${key}=" + value.contentToString())
return null
}*/
}
private
val
indent
:
String
=
" "
.
repeat
(
4
)
/**
* 将所有元素加入转换为多行的字符串表示.
*/
@MiraiDebugAPI
fun
<
T
>
Sequence
<
T
>.
joinToStringPrefixed
(
prefix
:
String
,
transform
:
(
T
)
->
CharSequence
):
String
{
return
this
.
joinToString
(
prefix
=
"$prefix$
{ProtoMap.indent}"
,
separator
=
"\n$prefix${ProtoMap.indent}
"
,
transform
=
transform
)
internal
fun
<
T
>
Sequence
<
T
>.
joinToStringPrefixed
(
prefix
:
String
,
transform
:
(
T
)
->
CharSequence
):
String
{
return
this
.
joinToString
(
prefix
=
"$prefix$
indent"
,
separator
=
"\n$prefix$indent
"
,
transform
=
transform
)
}
/**
* 将内容格式化为较可读的字符串输出.
*
* 各数字类型及其无符号类型: 十六进制表示 + 十进制表示. e.g. `0x1000(4096)`
* [ByteArray] 和 [UByteaArray]: 十六进制表示, 通过 [ByteArray.toUHexString]
* [ProtoMap]: 调用 [ProtoMap.toStringPrefixed]
* [ByteArray] 和 [UByteArray]: 十六进制表示, 通过 [ByteArray.toUHexString]
* [Iterable], [Iterator], [Sequence]: 调用各自的 joinToString.
* [Map]: 多行输出. 每行显示一个值. 递归调用 [
c
ontentToString]. 嵌套结构将会以缩进表示
* [Map]: 多行输出. 每行显示一个值. 递归调用 [
_miraiC
ontentToString]. 嵌套结构将会以缩进表示
* `data class`: 调用其 [toString]
* 其他类型: 反射获取它和它的所有来自 Mirai 的 super 类型的所有自有属性并递归调用 [
c
ontentToString]. 嵌套结构将会以缩进表示
* 其他类型: 反射获取它和它的所有来自 Mirai 的 super 类型的所有自有属性并递归调用 [
_miraiC
ontentToString]. 嵌套结构将会以缩进表示
*/
@Suppress
(
"FunctionName"
)
// 这样就不容易被 IDE 提示
@MiraiDebugAPI
(
"Extremely slow"
)
//@Suppress("Unsupported") // false positive
fun
Any
?.
c
ontentToString
(
prefix
:
String
=
""
):
String
=
when
(
this
)
{
fun
Any
?.
_miraiC
ontentToString
(
prefix
:
String
=
""
):
String
=
when
(
this
)
{
is
Unit
->
"Unit"
is
UInt
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
UByte
->
"0x"
+
this
.
toUHexString
()
+
"($this)"
...
...
@@ -165,8 +49,6 @@ fun Any?.contentToString(prefix: String = ""): String = when (this) {
is
Short
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
Long
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
UVarInt
->
"0x"
+
this
.
toUHexString
(
""
)
+
"($this)"
is
Boolean
->
if
(
this
)
"true"
else
"false"
is
ByteArray
->
{
...
...
@@ -179,114 +61,107 @@ fun Any?.contentToString(prefix: String = ""): String = when (this) {
}
is
ShortArray
->
{
if
(
this
.
size
==
0
)
"<Empty ShortArray>"
else
this
.
iterator
().
c
ontentToString
()
else
this
.
iterator
().
_miraiC
ontentToString
()
}
is
IntArray
->
{
if
(
this
.
size
==
0
)
"<Empty IntArray>"
else
this
.
iterator
().
c
ontentToString
()
else
this
.
iterator
().
_miraiC
ontentToString
()
}
is
LongArray
->
{
if
(
this
.
size
==
0
)
"<Empty LongArray>"
else
this
.
iterator
().
c
ontentToString
()
else
this
.
iterator
().
_miraiC
ontentToString
()
}
is
FloatArray
->
{
if
(
this
.
size
==
0
)
"<Empty FloatArray>"
else
this
.
iterator
().
c
ontentToString
()
else
this
.
iterator
().
_miraiC
ontentToString
()
}
is
DoubleArray
->
{
if
(
this
.
size
==
0
)
"<Empty DoubleArray>"
else
this
.
iterator
().
c
ontentToString
()
else
this
.
iterator
().
_miraiC
ontentToString
()
}
is
UShortArray
->
{
if
(
this
.
size
==
0
)
"<Empty ShortArray>"
else
this
.
iterator
().
c
ontentToString
()
else
this
.
iterator
().
_miraiC
ontentToString
()
}
is
UIntArray
->
{
if
(
this
.
size
==
0
)
"<Empty IntArray>"
else
this
.
iterator
().
c
ontentToString
()
else
this
.
iterator
().
_miraiC
ontentToString
()
}
is
ULongArray
->
{
if
(
this
.
size
==
0
)
"<Empty LongArray>"
else
this
.
iterator
().
c
ontentToString
()
else
this
.
iterator
().
_miraiC
ontentToString
()
}
is
Array
<
*
>
->
{
if
(
this
.
size
==
0
)
"<Empty Array>"
else
this
.
iterator
().
c
ontentToString
()
else
this
.
iterator
().
_miraiC
ontentToString
()
}
is
BooleanArray
->
{
if
(
this
.
size
==
0
)
"<Empty BooleanArray>"
else
this
.
iterator
().
c
ontentToString
()
else
this
.
iterator
().
_miraiC
ontentToString
()
}
is
ProtoMap
->
"ProtoMap(size=$size){\n"
+
this
.
toStringPrefixed
(
"$prefix${ProtoMap.indent}${ProtoMap.indent}"
)
+
"\n$prefix${ProtoMap.indent}}"
is
Iterable
<
*
>
->
this
.
joinToString
(
prefix
=
"["
,
postfix
=
"]"
)
{
it
.
contentToString
(
prefix
)
}
is
Iterator
<
*
>
->
this
.
asSequence
().
joinToString
(
prefix
=
"["
,
postfix
=
"]"
)
{
it
.
contentToString
(
prefix
)
}
is
Sequence
<
*
>
->
this
.
joinToString
(
prefix
=
"["
,
postfix
=
"]"
)
{
it
.
contentToString
(
prefix
)
}
is
Map
<
*
,
*
>
->
this
.
entries
.
joinToString
(
prefix
=
"{"
,
postfix
=
"}"
)
{
it
.
key
.
contentToString
(
prefix
)
+
"="
+
it
.
value
.
contentToString
(
prefix
)
}
is
Iterable
<
*
>
->
this
.
joinToString
(
prefix
=
"["
,
postfix
=
"]"
)
{
it
.
_miraiContentToString
(
prefix
)
}
is
Iterator
<
*
>
->
this
.
asSequence
().
joinToString
(
prefix
=
"["
,
postfix
=
"]"
)
{
it
.
_miraiContentToString
(
prefix
)
}
is
Sequence
<
*
>
->
this
.
joinToString
(
prefix
=
"["
,
postfix
=
"]"
)
{
it
.
_miraiContentToString
(
prefix
)
}
is
Map
<
*
,
*
>
->
this
.
entries
.
joinToString
(
prefix
=
"{"
,
postfix
=
"}"
)
{
it
.
key
.
_miraiContentToString
(
prefix
)
+
"="
+
it
.
value
.
_miraiContentToString
(
prefix
)
}
else
->
{
if
(
this
==
null
)
"null"
else
if
(
this
::
class
.
isData
)
this
.
toString
()
else
{
if
(
this
::
class
.
qualifiedName
?.
startsWith
(
"net.mamoe.mirai."
)
==
true
)
{
this
.
contentToStringReflectively
(
prefix
+
ProtoMap
.
indent
)
this
.
contentToStringReflectively
(
prefix
+
indent
)
}
else
this
.
toString
()
/*
(this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + "{\n" +
this::class.members.asSequence().filterIsInstance<KProperty<*>>().filter { !it.isSuspend && it.visibility == KVisibility.PUBLIC }
.joinToStringPrefixed(
prefix =
ProtoMap.
indent
) { it.name + "=" + kotlin.runCatching { it.call(it).contentToString(
ProtoMap.
indent) }.getOrElse { "<!>" } }
prefix = indent
) { it.name + "=" + kotlin.runCatching { it.call(it).contentToString(indent) }.getOrElse { "<!>" } }
*/
}
}
}
@MiraiExperimentalAPI
(
"Extremely slow"
)
@MiraiDebugAPI
(
"Extremely slow"
)
expect
fun
Any
.
contentToStringReflectively
(
prefix
:
String
=
""
,
filter
:
((
String
,
Any
?)
->
Boolean
)?
=
null
):
String
@MiraiDebugAPI
@Suppress
(
"UNCHECKED_CAST"
)
fun
ByteReadPacket
.
readProtoMap
(
length
:
Long
=
this
.
remaining
):
ProtoMap
{
val
map
=
ProtoMap
(
mutableMapOf
())
val
expectingRemaining
=
this
.
remaining
-
length
while
(
this
.
remaining
!=
expectingRemaining
)
{
require
(
this
.
remaining
>
expectingRemaining
)
{
"Expecting to read $length bytes, but read ${expectingRemaining + length - this.remaining}"
}
try
{
val
id
=
ProtoFieldId
(
readUVarInt
())
fun
readValue
():
Any
=
when
(
id
.
type
)
{
ProtoType
.
VAR_INT
->
UVarInt
(
readUVarInt
())
ProtoType
.
BIT_32
->
readUInt
()
ProtoType
.
BIT_64
->
readULong
()
ProtoType
.
LENGTH_DELIMI
->
tryReadProtoMapOrByteArray
(
readUVarInt
().
toInt
())
ProtoType
.
START_GROUP
->
Unit
ProtoType
.
END_GROUP
->
Unit
}
private
fun
Any
.
contentToStringReflectively
(
prefix
:
String
,
filter
:
((
name
:
String
,
value
:
Any
?)
->
Boolean
)?
=
null
):
String
{
val
newPrefix
=
"$prefix "
return
(
this
::
class
.
simpleName
?:
"<UnnamedClass>"
)
+
"#"
+
this
::
class
.
hashCode
()
+
" {\n"
+
this
.
allMembersFromSuperClassesMatching
{
it
.
simpleName
?.
startsWith
(
"net.mamoe.mirai"
)
==
true
}
.
distinctBy
{
it
.
name
}
.
filterNot
{
it
.
name
.
contains
(
"$"
)
||
it
.
name
==
"Companion"
||
it
.
isConst
||
it
.
name
==
"serialVersionUID"
}
.
mapNotNull
{
val
value
=
it
.
get
()
if
(
filter
!=
null
)
{
kotlin
.
runCatching
{
if
(!
filter
(
it
.
name
,
value
))
return
@
mapNotNull
it
.
name
to
value
}
}
null
}
.
joinToStringPrefixed
(
prefix
=
newPrefix
)
{
(
name
:
String
,
value
:
Any
?)
->
"$name="
+
kotlin
.
runCatching
{
if
(
value
==
this
)
"<this>"
else
value
.
_miraiContentToString
(
newPrefix
)
}.
getOrElse
{
"<!>"
}
}
+
"\n$prefix}"
}
if
(
map
.
containsKey
(
id
))
{
if
(
map
[
id
]
!
is
MutableList
<
*
>)
map
[
id
]
=
mutableListOf
(
map
[
id
]
!!
)
(
map
[
id
]
as
MutableList
<
Any
>)
+=
readValue
()
}
else
{
map
[
id
]
=
readValue
()
}
}
catch
(
e
:
IllegalStateException
)
{
e
.
logStacktrace
()
return
map
}
}
return
map
private
fun
Any
.
thisClassAndSuperclassSequence
():
Sequence
<
KClass
<
out
Any
>>
{
return
sequenceOf
(
this
::
class
)
+
this
::
class
.
supertypes
.
asSequence
()
.
mapNotNull
{
type
->
type
.
classifier
?.
takeIf
{
it
is
KClass
<
*
>
}
as
?
KClass
<
out
Any
>
}
}
private
fun
ByteReadPacket
.
tryReadProtoMapOrByteArray
(
length
:
Int
):
Any
{
val
bytes
=
this
.
readBytes
(
length
)
return
try
{
bytes
.
toReadPacket
().
readProtoMap
().
apply
{
require
(
none
{
it
.
key
.
type
==
ProtoType
.
START_GROUP
||
it
.
key
.
type
==
ProtoType
.
END_GROUP
})
}
}
catch
(
e
:
Exception
)
{
bytes
}
private
fun
Any
.
allMembersFromSuperClassesMatching
(
classFilter
:
(
KClass
<
out
Any
>)
->
Boolean
):
Sequence
<
KProperty0
<
*
>>
{
return
this
.
thisClassAndSuperclassSequence
()
.
filter
{
classFilter
(
it
)
}
.
map
{
it
.
members
}
.
flatMap
{
it
.
asSequence
()
}
.
mapNotNull
{
it
as
?
KProperty0
<
*
>
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt
View file @
932a3ef1
...
...
@@ -10,8 +10,8 @@
package
net.mamoe.mirai.utils.cryptor
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.IoBuffer
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.io.ByteArrayPool
import
net.mamoe.mirai.utils.io.toByteArray
import
net.mamoe.mirai.utils.io.toUHexString
...
...
@@ -20,7 +20,6 @@ import kotlin.experimental.xor
import
kotlin.jvm.JvmStatic
import
kotlin.random.Random
/**
* 解密错误
*/
...
...
@@ -29,98 +28,52 @@ class DecryptionFailedException : Exception {
constructor
(
message
:
String
?)
:
super
(
message
)
}
// region encrypt
/**
* 使用 [key] 解密 [this]
*
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
fun
ByteArray
.
encryptBy
(
key
:
ByteArray
,
length
:
Int
=
this
.
size
):
ByteArray
=
TEA
.
encrypt
(
this
,
key
,
sourceLength
=
length
)
/**
* 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 加密.
*
* @param key 长度至少为 16
* @consumer 由于缓存需要被回收, 需在方法内执行解密后明文的消耗过程
* @throws DecryptionFailedException 解密错误时
*/
inline
fun
ByteReadPacket
.
encryptBy
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
remaining
.
toInt
()
-
offset
,
consumer
:
(
ByteArray
)
->
Unit
)
{
ByteArrayPool
.
useInstance
{
this
.
readFully
(
it
,
offset
,
length
)
consumer
(
it
.
encryptBy
(
key
,
length
=
length
))
}
}
// endregion
// region decrypt
/**
* 使用 [key] 解密 [this].
*
* @param key 固定长度 16
* @throws DecryptionFailedException 解密错误时
*/
fun
ByteArray
.
decryptBy
(
key
:
ByteArray
,
length
:
Int
=
this
.
size
):
ByteArray
=
TEA
.
decrypt
(
checkDataLengthAndReturnSelf
(
length
),
key
,
sourceLength
=
length
)
/**
* 使用 [key] 解密 [this].
* [key] 将会被读取掉前 16 个字节
* 将会使用 [ByteArrayPool] 来缓存 [key].
* TEA 算法加密解密工具类.
*
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
* **注意**: 此为 Mirai 内部 API. 它可能会在任何时刻被改变.
*/
fun
ByteArray
.
decryptBy
(
key
:
IoBuffer
,
length
:
Int
=
this
.
size
):
ByteArray
{
checkDataLengthAndReturnSelf
(
length
)
return
ByteArrayPool
.
useInstance
{
keyBuffer
->
key
.
readFully
(
keyBuffer
,
0
,
key
.
readRemaining
)
TEA
.
decrypt
(
this
,
keyBuffer
,
sourceLength
=
length
)
@MiraiInternalAPI
object
TEA
{
// TODO: 2020/2/28 使用 stream 式输入以避免缓存
/**
* 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 加密.
*
* @param key 长度至少为 16
* @consumer 由于缓存需要被回收, 需在方法内执行解密后明文的消耗过程
* @throws DecryptionFailedException 解密错误时
*/
inline
fun
encrypt
(
receiver
:
ByteReadPacket
,
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
receiver
.
remaining
.
toInt
()
-
offset
,
consumer
:
(
ByteArray
)
->
Unit
)
{
ByteArrayPool
.
useInstance
{
receiver
.
readFully
(
it
,
offset
,
length
)
consumer
(
encrypt
(
it
,
key
,
length
=
length
))
}
}
}
/**
* 在 [ByteArrayPool] 缓存 [this], 然后使用 [key] 解密.
*
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
fun
IoBuffer
.
decryptBy
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
readRemaining
-
offset
):
ByteArray
{
return
ByteArrayPool
.
useInstance
{
this
.
readFully
(
it
,
offset
,
length
)
it
.
checkDataLengthAndReturnSelf
(
length
)
TEA
.
decrypt
(
it
,
key
,
length
)
@JvmStatic
fun
decrypt
(
receiver
:
ByteReadPacket
,
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
(
receiver
.
remaining
-
offset
).
toInt
()):
ByteReadPacket
=
decryptAsByteArray
(
receiver
,
key
,
offset
,
length
)
{
data
->
ByteReadPacket
(
data
)
}
inline
fun
<
R
>
decryptAsByteArray
(
receiver
:
ByteReadPacket
,
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
(
receiver
.
remaining
-
offset
).
toInt
(),
consumer
:
(
ByteArray
)
->
R
):
R
{
return
ByteArrayPool
.
useInstance
{
receiver
.
readFully
(
it
,
offset
,
length
)
consumer
(
decrypt
(
it
,
key
,
length
))
}.
also
{
receiver
.
close
()
}
}
}
// endregion
// region ByteReadPacket extension
fun
ByteReadPacket
.
decryptBy
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
(
this
.
remaining
-
offset
).
toInt
()):
ByteReadPacket
=
decryptAsByteArray
(
key
,
offset
,
length
)
{
data
->
ByteReadPacket
(
data
)
}
fun
ByteReadPacket
.
decryptBy
(
key
:
IoBuffer
,
offset
:
Int
=
0
,
length
:
Int
=
(
this
.
remaining
-
offset
).
toInt
()):
ByteReadPacket
=
decryptAsByteArray
(
key
,
offset
,
length
)
{
data
->
ByteReadPacket
(
data
)
}
inline
fun
<
R
>
ByteReadPacket
.
decryptAsByteArray
(
key
:
ByteArray
,
offset
:
Int
=
0
,
length
:
Int
=
(
this
.
remaining
-
offset
).
toInt
(),
consumer
:
(
ByteArray
)
->
R
):
R
=
ByteArrayPool
.
useInstance
{
readFully
(
it
,
offset
,
length
)
consumer
(
it
.
decryptBy
(
key
,
length
))
}.
also
{
close
()
}
inline
fun
<
R
>
ByteReadPacket
.
decryptAsByteArray
(
key
:
IoBuffer
,
offset
:
Int
=
0
,
length
:
Int
=
(
this
.
remaining
-
offset
).
toInt
(),
consumer
:
(
ByteArray
)
->
R
):
R
=
ByteArrayPool
.
useInstance
{
readFully
(
it
,
offset
,
length
)
consumer
(
it
.
decryptBy
(
key
,
length
))
}.
also
{
close
()
}
// endregion
private
object
TEA
{
private
const
val
UINT32_MASK
=
0
xffffffffL
private
fun
doOption
(
data
:
ByteArray
,
key
:
ByteArray
,
length
:
Int
,
encrypt
:
Boolean
):
ByteArray
{
...
...
@@ -345,15 +298,25 @@ private object TEA {
private
fun
fail
():
Nothing
=
throw
DecryptionFailedException
()
@PublishedApi
/**
* 使用 [key] 加密 [source]
*
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
@JvmStatic
internal
fun
encrypt
(
source
:
ByteArray
,
key
:
ByteArray
,
sourceLength
:
Int
=
source
.
size
):
ByteArray
=
doOption
(
source
,
key
,
sourceLength
,
true
)
@PublishedApi
fun
encrypt
(
source
:
ByteArray
,
key
:
ByteArray
,
length
:
Int
=
source
.
size
):
ByteArray
=
doOption
(
source
,
key
,
length
,
true
)
/**
* 使用 [key] 解密 [source]
*
* @param key 长度至少为 16
* @throws DecryptionFailedException 解密错误时
*/
@JvmStatic
internal
fun
decrypt
(
source
:
ByteArray
,
key
:
ByteArray
,
sourceL
ength
:
Int
=
source
.
size
):
ByteArray
=
doOption
(
source
,
key
,
sourceL
ength
,
false
)
fun
decrypt
(
source
:
ByteArray
,
key
:
ByteArray
,
l
ength
:
Int
=
source
.
size
):
ByteArray
=
doOption
(
source
,
key
,
l
ength
,
false
)
private
fun
ByteArray
.
pack
(
offset
:
Int
,
len
:
Int
):
Long
{
var
result
:
Long
=
0
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayPool.kt
View file @
932a3ef1
...
...
@@ -30,5 +30,13 @@ object ByteArrayPool : DefaultPool<ByteArray>(256) {
override
fun
produceInstance
():
ByteArray
=
ByteArray
(
BUFFER_SIZE
)
override
fun
clearInstance
(
instance
:
ByteArray
):
ByteArray
=
instance
fun
checkBufferSize
(
size
:
Int
)
{
require
(
size
<=
BUFFER_SIZE
)
{
"sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE"
}
}
fun
checkBufferSize
(
size
:
Long
)
{
require
(
size
<=
BUFFER_SIZE
)
{
"sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE"
}
}
}
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt
View file @
932a3ef1
...
...
@@ -11,7 +11,6 @@ package net.mamoe.mirai.utils.io
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.Closeable
import
kotlinx.io.errors.IOException
import
net.mamoe.mirai.utils.MiraiInternalAPI
/**
...
...
@@ -32,11 +31,6 @@ expect class PlatformDatagramChannel(serverHost: String, serverPort: Short) : Cl
val
isOpen
:
Boolean
}
/**
* Channel 被关闭
*/
expect
class
ClosedChannelException
:
IOException
/**
* 在 [PlatformDatagramChannel.send] 或 [PlatformDatagramChannel.read] 时出现的错误.
*/
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
deleted
100644 → 0
View file @
1ff5df1d
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@
file
:
JvmName
(
"Varint"
)
@
file
:
Suppress
(
"EXPERIMENTAL_API_USAGE"
)
package
net.mamoe.mirai.utils.io
import
kotlinx.io.core.Input
import
kotlinx.io.core.Output
import
kotlin.experimental.or
import
kotlin.jvm.JvmName
import
kotlin.jvm.JvmSynthetic
/**
* Tool class for VarInt or VarLong operations.
*
* Some code from http://wiki.vg/Protocol.
*
* Source project: [Nukkit](http://github.com/nukkit/nukkit)
*
* @author MagicDroidX from Nukkit Project
* @author lmlstarqaq from Nukkit Project
*/
internal
fun
encodeZigZag32
(
signedInt
:
Int
):
Long
{
return
(
signedInt
shl
1
xor
(
signedInt
shr
31
)).
toLong
()
}
@JvmSynthetic
internal
fun
decodeZigZag32
(
uint
:
UInt
):
Int
{
return
decodeZigZag32
(
uint
.
toLong
())
}
internal
fun
decodeZigZag32
(
uint
:
Long
):
Int
{
return
(
uint
shr
1
).
toInt
()
xor
-(
uint
and
1
).
toInt
()
}
internal
fun
encodeZigZag64
(
signedLong
:
Long
):
Long
{
return
signedLong
shl
1
xor
(
signedLong
shr
63
)
}
internal
fun
decodeZigZag64
(
signedLong
:
Long
):
Long
{
return
signedLong
.
ushr
(
1
)
xor
-(
signedLong
and
1
)
}
inline
class
UVarInt
(
val
data
:
UInt
)
@JvmSynthetic
fun
Input
.
readUVarInt
():
UInt
{
return
read
(
this
,
5
).
toUInt
()
}
fun
Input
.
readVarLong
():
Long
{
return
decodeZigZag64
(
readUVarLong
().
toLong
())
}
@JvmSynthetic
fun
Input
.
readUVarLong
():
ULong
{
return
read
(
this
,
10
).
toULong
()
}
fun
Output
.
writeVarInt
(
signedInt
:
Int
)
{
this
.
writeUVarInt
(
encodeZigZag32
(
signedInt
))
}
@JvmSynthetic
fun
Output
.
writeUVarInt
(
uint
:
UInt
)
{
return
writeUVarInt
(
uint
.
toLong
())
}
fun
Output
.
writeUVarInt
(
uint
:
Long
)
{
this
.
write0
(
uint
)
}
fun
Output
.
writeVarLong
(
signedLong
:
Long
)
{
this
.
writeUVarLong
(
encodeZigZag64
(
signedLong
))
}
fun
Output
.
writeUVarLong
(
ulong
:
Long
)
{
this
.
write0
(
ulong
)
}
fun
UVarInt
.
toByteArray
():
ByteArray
{
val
list
=
mutableListOf
<
Byte
>()
var
value
=
this
.
data
.
toLong
()
do
{
var
temp
=
(
value
and
127
).
toByte
()
value
=
value
ushr
7
if
(
value
!=
0L
)
{
temp
=
temp
or
128
.
toByte
()
}
list
+=
temp
}
while
(
value
!=
0L
)
return
list
.
toByteArray
()
}
fun
UVarInt
.
toUHexString
(
separator
:
String
=
" "
):
String
=
buildString
{
var
value
=
data
.
toLong
()
var
isFirst
=
true
do
{
if
(!
isFirst
)
{
append
(
separator
)
}
var
temp
=
(
value
and
127
).
toByte
()
value
=
value
ushr
7
if
(
value
!=
0L
)
{
temp
=
temp
or
128
.
toByte
()
}
append
(
temp
.
toUByte
().
fixToUHex
())
isFirst
=
false
}
while
(
value
!=
0L
)
}
private
fun
Output
.
write0
(
long
:
Long
)
{
var
value
=
long
do
{
var
temp
=
(
value
and
127
).
toByte
()
value
=
value
ushr
7
if
(
value
!=
0L
)
{
temp
=
temp
or
128
.
toByte
()
}
this
.
writeByte
(
temp
)
}
while
(
value
!=
0L
)
}
private
fun
read
(
stream
:
Input
,
maxSize
:
Int
):
Long
{
var
value
:
Long
=
0
var
size
=
0
var
b
=
stream
.
readByte
().
toInt
()
while
(
b
and
0
x80
==
0
x80
)
{
value
=
value
or
((
b
and
0
x7F
).
toLong
()
shl
size
++
*
7
)
require
(
size
<
maxSize
)
{
"VarLong too big(expecting maxSize=$maxSize)"
}
b
=
stream
.
readByte
().
toInt
()
}
return
value
or
((
b
and
0
x7F
).
toLong
()
shl
size
*
7
)
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
0 → 100644
View file @
932a3ef1
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package
net.mamoe.mirai.utils.io
import
io.ktor.utils.io.ByteReadChannel
import
kotlinx.coroutines.ExperimentalCoroutinesApi
import
kotlinx.coroutines.flow.Flow
import
kotlinx.coroutines.flow.flow
import
kotlinx.coroutines.flow.flowOf
import
kotlinx.io.InputStream
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.Input
import
kotlinx.io.core.readAvailable
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.utils.MiraiInternalAPI
/**
* 由 [chunkedFlow] 分割得到的区块
*/
class
ChunkedInput
(
/**
* 区块的数据.
* 由 [ByteArrayPool] 缓存并管理, 只可在 [Flow.collect] 中访问.
* 它的大小由 [ByteArrayPool.BUFFER_SIZE] 决定, 而有效(有数据)的大小由 [bufferSize] 决定.
*
* **注意**: 不要将他带出 [Flow.collect] 作用域, 否则将造成内存泄露
*/
val
buffer
:
ByteArray
,
internal
var
size
:
Int
)
{
/**
* [buffer] 的有效大小
*/
val
bufferSize
:
Int
get
()
=
size
}
/**
* 创建将 [ByteReadPacket] 以固定大小分割的 [Sequence].
*
* 对于一个 1000 长度的 [ByteReadPacket] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 其长度分别为: 300, 300, 300, 100.
*
* 若 [ByteReadPacket.remaining] 小于 [sizePerPacket], 将会返回唯一元素 [this] 的 [Sequence]
*/
@UseExperimental
(
MiraiInternalAPI
::
class
)
fun
ByteReadPacket
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
if
(
this
.
remaining
<=
sizePerPacket
.
toLong
())
{
ByteArrayPool
.
useInstance
{
buffer
->
return
flowOf
(
ChunkedInput
(
buffer
,
this
.
readAvailable
(
buffer
)))
}
}
return
flow
{
ByteArrayPool
.
useInstance
{
buffer
->
val
chunkedInput
=
ChunkedInput
(
buffer
,
0
)
do
{
chunkedInput
.
size
=
this
@
chunkedFlow
.
readAvailable
(
buffer
)
emit
(
chunkedInput
)
}
while
(
this
@
chunkedFlow
.
isNotEmpty
)
}
}
}
/**
* 创建将 [ByteReadChannel] 以固定大小分割的 [Sequence].
*
* 对于一个 1000 长度的 [ByteReadChannel] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 其长度分别为: 300, 300, 300, 100.
*/
@UseExperimental
(
MiraiInternalAPI
::
class
)
fun
ByteReadChannel
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
if
(
this
.
isClosedForRead
)
{
return
flowOf
()
}
return
flow
{
ByteArrayPool
.
useInstance
{
buffer
->
val
chunkedInput
=
ChunkedInput
(
buffer
,
0
)
do
{
chunkedInput
.
size
=
this
@
chunkedFlow
.
readAvailable
(
buffer
,
0
,
buffer
.
size
)
emit
(
chunkedInput
)
}
while
(!
this
@
chunkedFlow
.
isClosedForRead
)
}
}
}
/**
* 创建将 [Input] 以固定大小分割的 [Sequence].
*
* 对于一个 1000 长度的 [Input] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 其长度分别为: 300, 300, 300, 100.
*/
@UseExperimental
(
MiraiInternalAPI
::
class
,
ExperimentalCoroutinesApi
::
class
)
internal
fun
Input
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
if
(
this
.
endOfInput
)
{
return
flowOf
()
}
return
flow
{
ByteArrayPool
.
useInstance
{
buffer
->
val
chunkedInput
=
ChunkedInput
(
buffer
,
0
)
while
(!
this
@
chunkedFlow
.
endOfInput
)
{
chunkedInput
.
size
=
this
@
chunkedFlow
.
readAvailable
(
buffer
)
emit
(
chunkedInput
)
}
}
}
}
/**
* 创建将 [ByteReadPacket] 以固定大小分割的 [Sequence].
*
* 对于一个 1000 长度的 [ByteReadPacket] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 其长度分别为: 300, 300, 300, 100.
*
* 若 [ByteReadPacket.remaining] 小于 [sizePerPacket], 将会返回唯一元素 [this] 的 [Sequence]
*/
@UseExperimental
(
MiraiInternalAPI
::
class
,
ExperimentalCoroutinesApi
::
class
)
internal
fun
InputStream
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
return
flow
{
ByteArrayPool
.
useInstance
{
buffer
->
val
chunkedInput
=
ChunkedInput
(
buffer
,
0
)
while
(
this
@
chunkedFlow
.
available
()
!=
0
)
{
chunkedInput
.
size
=
this
@
chunkedFlow
.
read
(
buffer
)
emit
(
chunkedInput
)
}
}
}
}
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/input.kt
View file @
932a3ef1
...
...
@@ -20,26 +20,12 @@ import kotlinx.io.core.*
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.utils.MiraiDebugAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.cryptor.contentToString
import
kotlin.jvm.JvmMultifileClass
import
kotlin.jvm.JvmName
import
kotlin.jvm.JvmSynthetic
@Suppress
(
"NOTHING_TO_INLINE"
)
inline
fun
Input
.
discardExact
(
n
:
Short
)
=
this
.
discardExact
(
n
.
toInt
())
@Suppress
(
"NOTHING_TO_INLINE"
)
@JvmSynthetic
inline
fun
Input
.
discardExact
(
n
:
UShort
)
=
this
.
discardExact
(
n
.
toInt
())
@Suppress
(
"NOTHING_TO_INLINE"
)
@JvmSynthetic
inline
fun
Input
.
discardExact
(
n
:
UByte
)
=
this
.
discardExact
(
n
.
toInt
())
@Suppress
(
"NOTHING_TO_INLINE"
)
inline
fun
Input
.
discardExact
(
n
:
Byte
)
=
this
.
discardExact
(
n
.
toInt
())
fun
ByteReadPacket
.
transferTo
(
outputStream
:
OutputStream
)
{
@UseExperimental
(
MiraiInternalAPI
::
class
)
fun
ByteReadPacket
.
copyTo
(
outputStream
:
OutputStream
)
{
ByteArrayPool
.
useInstance
{
while
(
this
.
isNotEmpty
)
{
outputStream
.
write
(
it
,
0
,
this
.
readAvailable
(
it
))
...
...
@@ -56,21 +42,13 @@ inline fun <R> ByteReadPacket.useBytes(
block
(
it
,
n
)
}
@MiraiInternalAPI
inline
fun
ByteReadPacket
.
readPacketExact
(
n
:
Int
=
remaining
.
toInt
()
//not that safe but adequate
):
ByteReadPacket
=
this
.
readBytes
(
n
).
toReadPacket
()
inline
fun
Input
.
readUByteLVString
():
String
=
String
(
this
.
readUByteLVByteArray
())
inline
fun
Input
.
readUShortLVString
():
String
=
String
(
this
.
readUShortLVByteArray
())
inline
fun
Input
.
readUByteLVByteArray
():
ByteArray
=
this
.
readBytes
(
this
.
readUByte
().
toInt
())
inline
fun
Input
.
readUShortLVByteArray
():
ByteArray
=
this
.
readBytes
(
this
.
readUShort
().
toInt
())
private
inline
fun
<
R
>
inline
(
block
:
()
->
R
):
R
=
block
()
typealias
TlvMap
=
MutableMap
<
Int
,
ByteArray
>
inline
fun
TlvMap
.
getOrFail
(
tag
:
Int
):
ByteArray
{
...
...
@@ -81,12 +59,13 @@ inline fun TlvMap.getOrFail(tag: Int, lazyMessage: (tag: Int) -> String): ByteAr
return
this
[
tag
]
?:
error
(
lazyMessage
(
tag
))
}
@Suppress
(
"FunctionName"
)
@MiraiInternalAPI
inline
fun
Input
.
readTLVMap
(
tagSize
:
Int
=
2
,
suppressDuplication
:
Boolean
=
true
):
TlvMap
=
readTLVMap
(
true
,
tagSize
,
suppressDuplication
)
inline
fun
Input
.
_readTLVMap
(
tagSize
:
Int
=
2
,
suppressDuplication
:
Boolean
=
true
):
TlvMap
=
_
readTLVMap
(
true
,
tagSize
,
suppressDuplication
)
@MiraiDebugAPI
@Suppress
(
"DuplicatedCode"
)
fun
Input
.
readTLVMap
(
expectingEOF
:
Boolean
=
true
,
tagSize
:
Int
,
suppressDuplication
:
Boolean
=
true
):
TlvMap
{
@Suppress
(
"DuplicatedCode"
,
"FunctionName"
)
fun
Input
.
_
readTLVMap
(
expectingEOF
:
Boolean
=
true
,
tagSize
:
Int
,
suppressDuplication
:
Boolean
=
true
):
TlvMap
{
val
map
=
mutableMapOf
<
Int
,
ByteArray
>()
var
key
=
0
...
...
@@ -108,11 +87,14 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int, suppressDuplica
}.
toUByte
()
!=
UByte
.
MAX_VALUE
)
{
if
(
map
.
containsKey
(
key
))
{
@Suppress
(
"ControlFlowWithEmptyBody"
)
if
(!
suppressDuplication
)
{
DebugLogger
.
error
(
/*
@Suppress("DEPRECATION")
MiraiLogger.error(
@Suppress("IMPLICIT_CAST_TO_ANY")
"""
Error readTLVMap:
Error readTLVMap:
duplicated key ${when (tagSize) {
1 -> key.toByte()
2 -> key.toShort()
...
...
@@ -122,13 +104,13 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int, suppressDuplica
map=${map.contentToString()}
duplicating value=${this.readUShortLVByteArray().toUHexString()}
""".trimIndent()
)
)
*/
}
else
{
this
.
discardExact
(
this
.
readShort
().
toInt
()
and
0
xffff
)
}
}
else
{
try
{
map
[
key
]
=
this
.
read
UShortLVByteArray
(
)
map
[
key
]
=
this
.
read
Bytes
(
readUShort
().
toInt
()
)
}
catch
(
e
:
Exception
)
{
// BufferUnderflowException, java.io.EOFException
// if (expectingEOF) {
// return map
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
View file @
932a3ef1
...
...
@@ -14,8 +14,9 @@
package
net.mamoe.mirai.utils.io
import
kotlinx.io.core.*
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.coerceAtMostOrFail
import
net.mamoe.mirai.utils.cryptor.
encryptBy
import
net.mamoe.mirai.utils.cryptor.
TEA
import
kotlin.jvm.JvmMultifileClass
import
kotlin.jvm.JvmName
...
...
@@ -67,5 +68,6 @@ fun BytePacketBuilder.writeHex(uHex: String) {
/**
* 会使用 [ByteArrayPool] 缓存
*/
@UseExperimental
(
MiraiInternalAPI
::
class
)
inline
fun
BytePacketBuilder
.
encryptAndWrite
(
key
:
ByteArray
,
encoder
:
BytePacketBuilder
.()
->
Unit
)
=
BytePacketBuilder
().
apply
(
encoder
).
build
().
encryptBy
(
key
)
{
decrypted
->
writeFully
(
decrypted
)
}
\ No newline at end of file
TEA
.
encrypt
(
BytePacketBuilder
().
apply
(
encoder
).
build
(),
key
)
{
decrypted
->
writeFully
(
decrypted
)
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
View file @
932a3ef1
...
...
@@ -31,40 +31,12 @@ import javax.imageio.ImageIO
import
kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.EmptyCoroutineContext
/**
* 平台默认的验证码识别器.
*
* 可被修改, 除覆盖配置外全局生效.
*/
actual
var
defaultLoginSolver
:
LoginSolver
=
DefaultLoginSolver
()
interface
LoginSolverInputReader
{
suspend
fun
read
(
question
:
String
):
String
?
suspend
operator
fun
invoke
(
question
:
String
):
String
?{
return
read
(
question
)
}
}
class
DefaultLoginSolverInputReader
:
LoginSolverInputReader
{
override
suspend
fun
read
(
question
:
String
):
String
?
{
return
readLine
()
}
}
class
DefaultLoginSolver
(
val
reader
:
LoginSolverInputReader
=
DefaultLoginSolverInputReader
()
,
val
overrideLogger
:
MiraiLogger
?
=
null
private
val
input
:
suspend
()
->
String
,
private
val
overrideLogger
:
MiraiLogger
?
=
null
)
:
LoginSolver
()
{
fun
getLogger
(
bot
:
Bot
):
MiraiLogger
{
if
(
overrideLogger
!=
null
){
return
overrideLogger
}
return
bot
.
logger
}
override
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
=
loginSolverLock
.
withLock
{
val
logger
=
getLogger
(
bot
)
val
logger
=
overrideLogger
?:
bot
.
logger
val
tempFile
:
File
=
createTempFile
(
suffix
=
".png"
).
apply
{
deleteOnExit
()
}
withContext
(
Dispatchers
.
IO
)
{
tempFile
.
createNewFile
()
...
...
@@ -86,39 +58,38 @@ class DefaultLoginSolver(
}
}
logger
.
info
(
"请输入 4 位字母验证码. 若要更换验证码, 请直接回车"
)
return
reader
(
"请输入 4 位字母验证码. 若要更换验证码, 请直接回车"
)
!!
.
takeUnless
{
it
.
isEmpty
()
||
it
.
length
!=
4
}.
also
{
return
input
()
.
takeUnless
{
it
.
isEmpty
()
||
it
.
length
!=
4
}.
also
{
logger
.
info
(
"正在提交[$it]中..."
)
}
}
override
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
=
loginSolverLock
.
withLock
{
val
logger
=
getLogger
(
bot
)
val
logger
=
overrideLogger
?:
bot
.
logger
logger
.
info
(
"需要滑动验证码"
)
logger
.
info
(
"请在任意浏览器中打开以下链接并完成验证码. "
)
logger
.
info
(
"完成后请输入任意字符 "
)
logger
.
info
(
url
)
return
reader
(
"完成后请输入任意字符"
).
also
{
return
input
(
).
also
{
logger
.
info
(
"正在提交中..."
)
}
}
override
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
=
loginSolverLock
.
withLock
{
val
logger
=
getLogger
(
bot
)
val
logger
=
overrideLogger
?:
bot
.
logger
logger
.
info
(
"需要进行账户安全认证"
)
logger
.
info
(
"该账户有[设备锁]/[不常用登录地点]/[不常用设备登录]的问题"
)
logger
.
info
(
"完成以下账号认证即可成功登录|理论本认证在mirai每个账户中最多出现1次"
)
logger
.
info
(
"请将该链接在QQ浏览器中打开并完成认证, 成功后输入任意字符"
)
logger
.
info
(
"这步操作将在后续的版本中优化"
)
logger
.
info
(
url
)
return
reader
(
"完成后请输入任意字符"
).
also
{
return
input
(
).
also
{
logger
.
info
(
"正在提交中..."
)
}
}
}
// Copied from Ktor CIO
p
ublic
fun
File
.
writeChannel
(
p
rivate
fun
File
.
writeChannel
(
coroutineContext
:
CoroutineContext
=
Dispatchers
.
IO
):
ByteWriteChannel
=
GlobalScope
.
reader
(
CoroutineName
(
"file-writer"
)
+
coroutineContext
,
autoFlush
=
true
)
{
@Suppress
(
"BlockingMethodInNonBlockingContext"
)
...
...
@@ -134,7 +105,7 @@ private val loginSolverLock = Mutex()
/**
* @author NaturalHG
*/
p
ublic
fun
BufferedImage
.
createCharImg
(
outputWidth
:
Int
=
100
,
ignoreRate
:
Double
=
0.95
):
String
{
p
rivate
fun
BufferedImage
.
createCharImg
(
outputWidth
:
Int
=
100
,
ignoreRate
:
Double
=
0.95
):
String
{
val
newHeight
=
(
this
.
height
*
(
outputWidth
.
toDouble
()
/
this
.
width
)).
toInt
()
val
tmp
=
this
.
getScaledInstance
(
outputWidth
,
newHeight
,
Image
.
SCALE_SMOOTH
)
val
image
=
BufferedImage
(
outputWidth
,
newHeight
,
BufferedImage
.
TYPE_INT_ARGB
)
...
...
@@ -229,7 +200,7 @@ actual open class BotConfiguration actual constructor() {
/**
* 验证码处理器
*/
actual
var
loginSolver
:
LoginSolver
=
defaultLoginSolver
actual
var
loginSolver
:
LoginSolver
=
LoginSolver
.
Default
actual
companion
object
{
/**
...
...
@@ -279,4 +250,18 @@ inline class FileBasedDeviceInfo @BotConfigurationDsl constructor(val filepath:
*/
@BotConfigurationDsl
companion
object
ByDeviceDotJson
}
/**
* 验证码, 设备锁解决器
*/
actual
abstract
class
LoginSolver
{
actual
abstract
suspend
fun
onSolvePicCaptcha
(
bot
:
Bot
,
data
:
IoBuffer
):
String
?
actual
abstract
suspend
fun
onSolveSliderCaptcha
(
bot
:
Bot
,
url
:
String
):
String
?
actual
abstract
suspend
fun
onSolveUnsafeDeviceLoginVerify
(
bot
:
Bot
,
url
:
String
):
String
?
actual
companion
object
{
actual
val
Default
:
LoginSolver
get
()
=
DefaultLoginSolver
(
input
=
{
withContext
(
Dispatchers
.
IO
)
{
readLine
()
}
?:
error
(
"No standard input"
)
})
}
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
View file @
932a3ef1
...
...
@@ -56,6 +56,7 @@ actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
actual
val
Http
:
HttpClient
get
()
=
HttpClient
(
CIO
)
@UseExperimental
(
MiraiInternalAPI
::
class
)
actual
fun
ByteArray
.
unzip
(
offset
:
Int
,
length
:
Int
):
ByteArray
{
this
.
checkOffsetAndLength
(
offset
,
length
)
if
(
length
==
0
)
return
ByteArray
(
0
)
...
...
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
deleted
100644 → 0
View file @
1ff5df1d
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package
net.mamoe.mirai.utils.cryptor
import
net.mamoe.mirai.utils.MiraiDebugAPI
import
java.lang.reflect.Field
import
kotlin.reflect.full.allSuperclasses
val
FIELD_TRY_SET_ACCESSIBLE
=
Field
::
class
.
java
.
declaredMethods
.
firstOrNull
{
it
.
name
==
"trySetAccessible"
}
@MiraiDebugAPI
actual
fun
Any
.
contentToStringReflectively
(
prefix
:
String
,
filter
:
((
name
:
String
,
value
:
Any
?)
->
Boolean
)?):
String
{
val
newPrefix
=
prefix
+
ProtoMap
.
indent
return
(
this
::
class
.
simpleName
?:
"<UnnamedClass>"
)
+
"#"
+
this
::
class
.
hashCode
()
+
" {\n"
+
this
.
allFieldsFromSuperClassesMatching
{
it
.
name
.
startsWith
(
"net.mamoe.mirai"
)
}
.
distinctBy
{
it
.
name
}
.
filterNot
{
it
.
name
.
contains
(
"$"
)
||
it
.
name
==
"Companion"
||
it
.
isSynthetic
||
it
.
name
==
"serialVersionUID"
}
.
filterNot
{
it
.
isEnumConstant
}
.
map
{
FIELD_TRY_SET_ACCESSIBLE
?.
invoke
(
it
,
true
)
?:
kotlin
.
run
{
it
.
isAccessible
=
true
}
val
value
=
it
.
get
(
this
)
if
(
filter
!=
null
)
{
kotlin
.
runCatching
{
if
(!
filter
(
it
.
name
,
value
))
return
@
map
it
.
name
to
FIELD_TRY_SET_ACCESSIBLE
}
}
it
.
name
to
value
}
.
filterNot
{
it
.
second
===
FIELD_TRY_SET_ACCESSIBLE
}
.
joinToStringPrefixed
(
prefix
=
newPrefix
)
{
(
name
,
value
)
->
"$name="
+
kotlin
.
runCatching
{
if
(
value
==
this
)
"<this>"
else
value
.
contentToString
(
newPrefix
)
}.
getOrElse
{
"<!>"
}
}
+
"\n$prefix}"
}
internal
fun
Any
.
allFieldsFromSuperClassesMatching
(
classFilter
:
(
Class
<
out
Any
>)
->
Boolean
):
Sequence
<
Field
>
{
return
(
this
::
class
.
java
.
takeIf
(
classFilter
)
?.
declaredFields
?.
asSequence
()
?:
sequenceOf
<
Field
>())
+
this
::
class
.
allSuperclasses
.
asSequence
()
.
map
{
it
.
java
}
.
filter
(
classFilter
)
.
flatMap
{
it
.
declaredFields
.
asSequence
()
}
}
\ No newline at end of file
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt
View file @
932a3ef1
...
...
@@ -21,8 +21,6 @@ import java.nio.channels.ReadableByteChannel
import
java.nio.channels.WritableByteChannel
actual
typealias
ClosedChannelException
=
java
.
nio
.
channels
.
ClosedChannelException
/**
* 多平台适配的 DatagramChannel.
*/
...
...
mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt
View file @
932a3ef1
...
...
@@ -9,16 +9,20 @@
package
mirai.test.testCaptchaPacket
import
net.mamoe.mirai.utils.cryptor.decryptBy
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.cryptor.TEA.decrypt
import
net.mamoe.mirai.utils.io.hexToBytes
import
net.mamoe.mirai.utils.io.toUHexString
@MiraiInternalAPI
fun
main
()
{
val
key
=
"65 F7 F3 14 E3 94 10 1F DD 95 84 A3 F5 9F AD 94"
.
hexToBytes
()
val
data
=
"8D 4F 6A 70 F8 4A DE 43 AF 75 D1 3F 3A 3F F2 E0 A8 16 1A 46 13 CD B0 51 45 00 29 52 57 75 6D 4A 4C D9 B7 98 8C B0 96 EC 57 4E 67 FB 8D C5 F1 BF 72 38 40 42 19 54 C2 28 F4 72 C8 AE 24 EB 66 B5 D0 45 0B 72 44 81 E2 F6 2B EE C3 85 93 BA CB B7 72 F4 1A 30 F9 5B 3D B0 79 3E F4 0B F2 1A A7 49 60 3B 37 02 60 0C 5D D5 76 76 47 4F B5 B3 F5 CA 58 6C FC D2 41 3E 24 D1 FB 0A 18 53 D8 E5 A5 85 A8 BC 51 54 3B 66 5B 21 C6 7B AF C9 62 F0 AA 9C CF 2E 84 0F CC 15 5B 35 93 49 5C E4 28 49 A7 8A D3 30 A9 6E 36 4E 7A 49 28 69 4D C3 25 39 6E 45 6E 40 F2 86 1E F4 4F 00 A6 9D E6 9B 84 19 69 C1 31 6A 17 BA F0 0D 8A 22 09 86 24 92 F7 22 C3 47 7F F2 BF 94 8A 8A B5 29"
.
hexToBytes
()
.
decryptBy
(
key
)
decrypt
(
"8D 4F 6A 70 F8 4A DE 43 AF 75 D1 3F 3A 3F F2 E0 A8 16 1A 46 13 CD B0 51 45 00 29 52 57 75 6D 4A 4C D9 B7 98 8C B0 96 EC 57 4E 67 FB 8D C5 F1 BF 72 38 40 42 19 54 C2 28 F4 72 C8 AE 24 EB 66 B5 D0 45 0B 72 44 81 E2 F6 2B EE C3 85 93 BA CB B7 72 F4 1A 30 F9 5B 3D B0 79 3E F4 0B F2 1A A7 49 60 3B 37 02 60 0C 5D D5 76 76 47 4F B5 B3 F5 CA 58 6C FC D2 41 3E 24 D1 FB 0A 18 53 D8 E5 A5 85 A8 BC 51 54 3B 66 5B 21 C6 7B AF C9 62 F0 AA 9C CF 2E 84 0F CC 15 5B 35 93 49 5C E4 28 49 A7 8A D3 30 A9 6E 36 4E 7A 49 28 69 4D C3 25 39 6E 45 6E 40 F2 86 1E F4 4F 00 A6 9D E6 9B 84 19 69 C1 31 6A 17 BA F0 0D 8A 22 09 86 24 92 F7 22 C3 47 7F F2 BF 94 8A 8A B5 29"
.
hexToBytes
(),
key
)
println
(
data
.
toUHexString
())
//00 02 00 00 08 04 01 E0 00 00 04 56 00 00 00 01 00 00 15 E3 01 00 38 58 CE A0 12 81 31 5C 5E 36 23 5B E4 0E 05 A6 47 BF 7C 1A 7A 35 37 59 90 17 50 66 0C 07 03 77 E4 48 DB 28 0A CF C3 A9 B7 C0 95 D3 9D 00 AA A5 EB FB D6 85 8D 10 61 5A D0 01 03 00 19 02 CA 53 7E F0 7B 32 82 EC 9F DE CF 51 8B A4 93 26 76 EC 42 1C 02 00 74 58 14 00 05 00 00 00 00 00 04 6C 73 64 61 00 40 CE 99 84 E8 F1 59 31 B0 3F 6C 4D 44 09 E4 82 77 96 67 03 A7 3A EA 8F 36 B9 20 79 7E C9 0F 75 3C 2A C3 E1 E5 C6 00 B3 5E 91 5B 47 63 EF AF 30 C0 48 2F 58 23 96 CF 65 2F 4C 75 95 A6 CA 5A 2C 5C 00 10 E1 50 C9 F4 F6 F4 2F D1 7F E9 8C AB B6 1C 38 7B
...
...
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