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 }