mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 11:07:40 +08:00
198 lines
4.9 KiB
Go
198 lines
4.9 KiB
Go
package packets
|
|
|
|
import (
|
|
"errors"
|
|
"github.com/Mrs4s/MiraiGo/binary"
|
|
"github.com/Mrs4s/MiraiGo/protocol/crypto"
|
|
)
|
|
|
|
var ErrUnknownFlag = errors.New("unknown flag")
|
|
var ErrDecryptFailed = errors.New("decrypt failed")
|
|
|
|
type ISendingPacket interface {
|
|
CommandId() uint16
|
|
Writer() *binary.Writer
|
|
}
|
|
|
|
type IncomingPacket struct {
|
|
SequenceId uint16
|
|
Flag2 byte
|
|
CommandName string
|
|
SessionId []byte
|
|
Payload []byte
|
|
}
|
|
|
|
type IEncryptMethod interface {
|
|
DoEncrypt([]byte, []byte) []byte
|
|
Id() byte
|
|
}
|
|
|
|
func BuildOicqRequestPacket(uin int64, commandId uint16, encrypt IEncryptMethod, key []byte, bodyFunc func(writer *binary.Writer)) []byte {
|
|
b := binary.NewWriter()
|
|
bodyFunc(b)
|
|
|
|
body := encrypt.DoEncrypt(b.Bytes(), key)
|
|
p := binary.NewWriter()
|
|
p.WriteByte(0x02)
|
|
p.WriteUInt16(27 + 2 + uint16(len(body)))
|
|
p.WriteUInt16(8001)
|
|
p.WriteUInt16(commandId)
|
|
p.WriteUInt16(1)
|
|
p.WriteUInt32(uint32(uin))
|
|
p.WriteByte(3)
|
|
p.WriteByte(encrypt.Id())
|
|
p.WriteByte(0)
|
|
p.WriteUInt32(2)
|
|
p.WriteUInt32(0)
|
|
p.WriteUInt32(0)
|
|
p.Write(body)
|
|
p.WriteByte(0x03)
|
|
return p.Bytes()
|
|
}
|
|
|
|
func BuildSsoPacket(seq uint16, commandName, imei string, extData, outPacketSessionId, body, ksid []byte) []byte {
|
|
p := binary.NewWriter()
|
|
p.WriteIntLvPacket(4, func(writer *binary.Writer) {
|
|
writer.WriteUInt32(uint32(seq))
|
|
writer.WriteUInt32(537062409) // Android pad (sub app id)
|
|
writer.WriteUInt32(537062409)
|
|
writer.Write([]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00})
|
|
if len(extData) == 0 || len(extData) == 4 {
|
|
writer.WriteUInt32(0x04)
|
|
} else {
|
|
writer.WriteUInt32(uint32(len(extData) + 4))
|
|
writer.Write(extData)
|
|
}
|
|
writer.WriteString(commandName)
|
|
writer.WriteUInt32(0x08)
|
|
writer.Write(outPacketSessionId)
|
|
writer.WriteString(imei)
|
|
writer.WriteUInt32(0x04)
|
|
{
|
|
writer.WriteUInt16(uint16(len(ksid)) + 2)
|
|
writer.Write(ksid)
|
|
}
|
|
writer.WriteUInt32(0x04)
|
|
})
|
|
|
|
p.WriteIntLvPacket(4, func(writer *binary.Writer) {
|
|
writer.Write(body)
|
|
})
|
|
return p.Bytes()
|
|
}
|
|
|
|
func ParseIncomingPacket(payload, d2key []byte) (*IncomingPacket, error) {
|
|
reader := binary.NewReader(payload)
|
|
flag1 := reader.ReadInt32()
|
|
flag2 := reader.ReadByte()
|
|
if reader.ReadByte() != 0 { // flag3
|
|
return nil, ErrUnknownFlag
|
|
}
|
|
reader.ReadString() // uin string
|
|
decrypted := func() (data []byte) {
|
|
defer func() {
|
|
if pan := recover(); pan != nil {
|
|
// TODO: bot.client.tryDecryptOrNull
|
|
}
|
|
}()
|
|
switch flag2 {
|
|
case 0:
|
|
return reader.ReadAvailable()
|
|
case 1:
|
|
d2 := binary.NewTeaCipher(d2key)
|
|
return d2.Decrypt(reader.ReadAvailable())
|
|
case 2:
|
|
z16 := binary.NewTeaCipher(make([]byte, 16))
|
|
return z16.Decrypt(reader.ReadAvailable())
|
|
}
|
|
return nil
|
|
}()
|
|
if len(decrypted) == 0 {
|
|
return nil, ErrDecryptFailed
|
|
}
|
|
if flag1 != 0x0A && flag1 != 0x0B {
|
|
return nil, ErrDecryptFailed
|
|
}
|
|
return parseSsoFrame(decrypted, flag2)
|
|
}
|
|
|
|
func parseSsoFrame(payload []byte, flag2 byte) (*IncomingPacket, error) {
|
|
reader := binary.NewReader(payload)
|
|
if reader.ReadInt32()-4 > int32(reader.Len()) {
|
|
return nil, errors.New("dropped")
|
|
}
|
|
seqId := reader.ReadInt32()
|
|
reader.ReadInt32() // return code
|
|
reader.ReadBytes(int(reader.ReadInt32()) - 4) // extra data
|
|
commandName := reader.ReadString()
|
|
sessionId := reader.ReadBytes(int(reader.ReadInt32()) - 4)
|
|
if commandName == "Heartbeat.Alive" {
|
|
return &IncomingPacket{
|
|
SequenceId: uint16(seqId),
|
|
Flag2: flag2,
|
|
CommandName: commandName,
|
|
SessionId: sessionId,
|
|
Payload: []byte{},
|
|
}, nil
|
|
}
|
|
compressedFlag := reader.ReadInt32()
|
|
packet := func() []byte {
|
|
if compressedFlag == 0 {
|
|
pktSize := uint64(reader.ReadInt32()) & 0xffffffff
|
|
if pktSize == uint64(reader.Len()) || pktSize == uint64(reader.Len()+4) {
|
|
return reader.ReadAvailable()
|
|
} else {
|
|
return reader.ReadAvailable() // some logic
|
|
}
|
|
}
|
|
if compressedFlag == 1 {
|
|
reader.ReadBytes(4)
|
|
return binary.ZlibUncompress(reader.ReadAvailable()) // ?
|
|
}
|
|
if compressedFlag == 8 {
|
|
return reader.ReadAvailable()
|
|
}
|
|
return nil
|
|
}()
|
|
return &IncomingPacket{
|
|
SequenceId: uint16(seqId),
|
|
Flag2: flag2,
|
|
CommandName: commandName,
|
|
SessionId: sessionId,
|
|
Payload: packet,
|
|
}, nil
|
|
}
|
|
|
|
func (pkt *IncomingPacket) DecryptPayload(random []byte) ([]byte, error) {
|
|
reader := binary.NewReader(pkt.Payload)
|
|
if reader.ReadByte() != 2 {
|
|
return nil, ErrUnknownFlag
|
|
}
|
|
reader.ReadBytes(2)
|
|
reader.ReadBytes(2)
|
|
reader.ReadUInt16()
|
|
reader.ReadUInt16()
|
|
reader.ReadInt32()
|
|
encryptType := reader.ReadUInt16()
|
|
reader.ReadByte()
|
|
if encryptType == 0 {
|
|
data := func() (decrypted []byte) {
|
|
d := reader.ReadBytes(reader.Len() - 1)
|
|
defer func() {
|
|
if pan := recover(); pan != nil {
|
|
tea := binary.NewTeaCipher(random)
|
|
decrypted = tea.Decrypt(d)
|
|
}
|
|
}()
|
|
tea := binary.NewTeaCipher(crypto.ECDH.InitialShareKey)
|
|
decrypted = tea.Decrypt(d)
|
|
return
|
|
}()
|
|
return data, nil
|
|
}
|
|
if encryptType == 4 {
|
|
panic("todo")
|
|
}
|
|
return nil, ErrUnknownFlag
|
|
}
|