1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-05 03:23:50 +08:00
2021-05-03 14:43:39 +08:00

231 lines
6.0 KiB
Go

package packets
import (
"strconv"
"github.com/pkg/errors"
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/protocol/crypto"
)
var (
ErrUnknownFlag = errors.New("unknown flag")
ErrInvalidPayload = errors.New("invalid payload")
ErrDecryptFailed = errors.New("decrypt failed")
ErrSessionExpired = errors.New("session expired")
ErrPacketDropped = errors.New("packet dropped")
)
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 {
body := encrypt.DoEncrypt(binary.NewWriterF(bodyFunc), key)
return binary.NewWriterF(func(p *binary.Writer) {
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)
})
}
func BuildCode2DRequestPacket(seq uint32, j uint64, cmd uint16, bodyFunc func(writer *binary.Writer)) []byte {
return binary.NewWriterF(func(w *binary.Writer) {
body := binary.NewWriterF(bodyFunc)
w.WriteByte(2)
w.WriteUInt16(uint16(43 + len(body) + 1))
w.WriteUInt16(cmd)
w.Write(make([]byte, 21))
w.WriteByte(3)
w.WriteUInt16(0)
w.WriteUInt16(50) // version
w.WriteUInt32(seq)
w.WriteUInt64(j)
w.Write(body)
w.WriteByte(3)
})
}
func BuildSsoPacket(seq uint16, appID, subAppID uint32, commandName, imei string, extData, outPacketSessionId, body, ksid []byte) []byte {
return binary.NewWriterF(func(p *binary.Writer) {
p.WriteIntLvPacket(4, func(writer *binary.Writer) {
writer.WriteUInt32(uint32(seq))
writer.WriteUInt32(appID)
writer.WriteUInt32(subAppID)
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.WriteIntLvPacket(4, func(w *binary.Writer) {
w.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)
})
})
}
func ParseIncomingPacket(payload, d2key []byte) (*IncomingPacket, error) {
if len(payload) < 6 {
return nil, errors.WithStack(ErrInvalidPayload)
}
reader := binary.NewReader(payload)
flag1 := reader.ReadInt32()
flag2 := reader.ReadByte()
if reader.ReadByte() != 0 { // flag3
// return nil, errors.WithStack(ErrUnknownFlag)
}
reader.ReadString() // uin string
decrypted := func() (data []byte) {
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, errors.WithStack(ErrDecryptFailed)
}
if flag1 != 0x0A && flag1 != 0x0B {
return nil, errors.WithStack(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.WithStack(ErrPacketDropped)
}
seqID := reader.ReadInt32()
retCode := reader.ReadInt32()
if retCode != 0 {
if retCode == -10008 {
return nil, errors.WithStack(ErrSessionExpired)
}
return nil, errors.New("return code unsuccessful: " + strconv.FormatInt(int64(retCode), 10))
}
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, sessionKey []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 == 3 {
return func() []byte {
d := reader.ReadBytes(reader.Len() - 1)
return binary.NewTeaCipher(sessionKey).Decrypt(d)
}(), nil
}
if encryptType == 4 {
panic("todo")
}
return nil, errors.WithStack(ErrUnknownFlag)
}