1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-04 19:17:38 +08:00
2020-07-06 03:56:25 +08:00

196 lines
4.8 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), nil
}
func parseSsoFrame(payload []byte, flag2 byte) *IncomingPacket {
reader := binary.NewReader(payload)
reader.ReadInt32() // packet len
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{},
}
}
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,
}
}
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
}