Skip to content

Commit

Permalink
internal/oicq: refactor & support unmarshal
Browse files Browse the repository at this point in the history
  • Loading branch information
wdvxdr1123 committed Dec 23, 2021
1 parent 5dfa452 commit 24b75e4
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 132 deletions.
16 changes: 7 additions & 9 deletions client/builders.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ import (
"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/binary/jce"
"github.com/Mrs4s/MiraiGo/client/internal/auth"
"github.com/Mrs4s/MiraiGo/client/internal/codec"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq"
"github.com/Mrs4s/MiraiGo/client/pb"
"github.com/Mrs4s/MiraiGo/client/pb/cmd0x352"
"github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/client/pb/oidb"
"github.com/Mrs4s/MiraiGo/client/pb/profilecard"
"github.com/Mrs4s/MiraiGo/client/pb/structmsg"
"github.com/Mrs4s/MiraiGo/internal/crypto"
"github.com/Mrs4s/MiraiGo/internal/packets"
"github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/internal/tlv"
Expand Down Expand Up @@ -426,12 +425,11 @@ func (c *QQClient) buildRequestTgtgtNopicsigPacket() (uint16, []byte) {
w.Write(tlv.T545([]byte(c.deviceInfo.IMEI)))
})

oicq := codec.OICQ{
Uin: uint32(c.Uin),
Command: 0x810,
EncryptMethod: crypto.NewEncryptSession(c.sig.T133),
Key: c.sig.WtSessionTicketKey,
Body: req,
m := oicq.Message{
Uin: uint32(c.Uin),
Command: 0x810,
EncryptionMethod: oicq.EM_ST,
Body: req,
}

nreq := network.Request{
Expand All @@ -440,7 +438,7 @@ func (c *QQClient) buildRequestTgtgtNopicsigPacket() (uint16, []byte) {
Uin: c.Uin,
SequenceID: int32(seq),
CommandName: "wtlogin.exchange_emp",
Body: oicq.Encode(),
Body: c.oicq.Marshal(&m),
}
return seq, c.transport.PackPacket(&nreq)
}
Expand Down
13 changes: 5 additions & 8 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import (
"github.com/Mrs4s/MiraiGo/client/internal/auth"
"github.com/Mrs4s/MiraiGo/client/internal/highway"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq"
"github.com/Mrs4s/MiraiGo/client/pb/msg"
"github.com/Mrs4s/MiraiGo/internal/crypto"
"github.com/Mrs4s/MiraiGo/utils"
)

Expand Down Expand Up @@ -54,6 +54,7 @@ type QQClient struct {
ConnectTime time.Time

transport *network.Transport
oicq *oicq.Codec

// internal state
handlers HandlerMap
Expand All @@ -64,7 +65,6 @@ type QQClient struct {
version *auth.AppVersion
deviceInfo *auth.Device
alive bool
ecdh *crypto.EncryptECDH

// session info
qwebSeq atomic.Int64
Expand Down Expand Up @@ -161,7 +161,6 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
Uin: uin,
PasswordMd5: passwordMd5,
AllowSlider: true,
RandomKey: make([]byte, 16),
TCP: &network.TCPListener{},
sig: &auth.SigInfo{
OutPacketSessionID: []byte{0x02, 0xB0, 0x5B, 0x8B},
Expand All @@ -172,7 +171,6 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
onlinePushCache: utils.NewCache(time.Second * 15),
servers: []*net.TCPAddr{},
alive: true,
ecdh: crypto.NewEcdh(),
highwaySession: new(highway.Session),

version: new(auth.AppVersion),
Expand All @@ -184,7 +182,7 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
Version: cli.version,
Device: cli.deviceInfo,
}

cli.oicq = oicq.NewCodec(cli.Uin)
{ // init atomic values
cli.SequenceId.Store(0x3635)
cli.requestPacketRequestID.Store(1921334513)
Expand All @@ -194,7 +192,6 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient {
}
cli.highwaySession.Uin = strconv.FormatInt(cli.Uin, 10)
cli.GuildService = &GuildService{c: cli}
cli.ecdh.FetchPubKey(uin)
cli.UseDevice(SystemDeviceInfo)
sso, err := getSSOAddress()
if err == nil && len(sso) > 0 {
Expand Down Expand Up @@ -300,7 +297,7 @@ func (c *QQClient) TokenLogin(token []byte) error {
c.sig.SrmToken = r.ReadBytesShort()
c.sig.T133 = r.ReadBytesShort()
c.sig.EncryptedA1 = r.ReadBytesShort()
c.sig.WtSessionTicketKey = r.ReadBytesShort()
c.oicq.WtSessionTicketKey = r.ReadBytesShort()
c.sig.OutPacketSessionID = r.ReadBytesShort()
// SystemDeviceInfo.TgtgtKey = r.ReadBytesShort()
c.deviceInfo.TgtgtKey = r.ReadBytesShort()
Expand Down Expand Up @@ -448,7 +445,7 @@ func (c *QQClient) GenToken() []byte {
w.WriteBytesShort(c.sig.SrmToken)
w.WriteBytesShort(c.sig.T133)
w.WriteBytesShort(c.sig.EncryptedA1)
w.WriteBytesShort(c.sig.WtSessionTicketKey)
w.WriteBytesShort(c.oicq.WtSessionTicketKey)
w.WriteBytesShort(c.sig.OutPacketSessionID)
w.WriteBytesShort(c.deviceInfo.TgtgtKey)
})
Expand Down
21 changes: 10 additions & 11 deletions client/internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,16 @@ type SigInfo struct {
TGT []byte
TGTKey []byte

SrmToken []byte // study room manager | 0x16a
T133 []byte
EncryptedA1 []byte
UserStKey []byte
UserStWebSig []byte
SKey []byte
SKeyExpiredTime int64
D2 []byte
D2Key []byte
WtSessionTicketKey []byte
DeviceToken []byte
SrmToken []byte // study room manager | 0x16a
T133 []byte
EncryptedA1 []byte
UserStKey []byte
UserStWebSig []byte
SKey []byte
SKeyExpiredTime int64
D2 []byte
D2Key []byte
DeviceToken []byte

PsKeyMap map[string][]byte
Pt4TokenMap map[string][]byte
Expand Down
40 changes: 0 additions & 40 deletions client/internal/codec/oicq.go

This file was deleted.

130 changes: 130 additions & 0 deletions client/internal/oicq/oicq.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package oicq

import (
goBinary "encoding/binary"
"math/rand"

"github.com/pkg/errors"

"github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/internal/crypto"
)

type Codec struct {
ecdh *crypto.ECDH
randomKey []byte

WtSessionTicketKey []byte
}

func NewCodec(uin int64) *Codec {
c := &Codec{
ecdh: crypto.NewECDH(),
randomKey: make([]byte, 16),
}
rand.Read(c.randomKey)
c.ecdh.FetchPubKey(uin)
return c
}

type EncryptionMethod byte

const (
EM_ECDH EncryptionMethod = iota
EM_ST
)

type Message struct {
Uin uint32
Command uint16
EncryptionMethod EncryptionMethod
Body []byte
}

func (c *Codec) Marshal(m *Message) []byte {
w := binary.SelectWriter()
defer binary.PutWriter(w)

w.WriteByte(0x02)
w.WriteUInt16(0) // len 占位
w.WriteUInt16(8001) // version?
w.WriteUInt16(m.Command)
w.WriteUInt16(1)
w.WriteUInt32(m.Uin)
w.WriteByte(0x03)
switch m.EncryptionMethod {
case EM_ECDH:
w.WriteByte(0x87)
case EM_ST:
w.WriteByte(0x45)
}
w.WriteByte(0)
w.WriteUInt32(2)
w.WriteUInt32(0)
w.WriteUInt32(0)

switch m.EncryptionMethod {
case EM_ECDH:
w.WriteByte(0x02)
w.WriteByte(0x01)
w.Write(c.randomKey)
w.WriteUInt16(0x01_31)
w.WriteUInt16(c.ecdh.SvrPublicKeyVer)
w.WriteUInt16(uint16(len(c.ecdh.PublicKey)))
w.Write(c.ecdh.PublicKey)
w.EncryptAndWrite(c.ecdh.ShareKey, m.Body)

case EM_ST:
w.WriteByte(0x01)
w.WriteByte(0x03)
w.Write(c.randomKey)
w.WriteUInt16(0x0102)
w.WriteUInt16(0x0000)
w.EncryptAndWrite(c.randomKey, m.Body)
}
w.WriteByte(0x03)

buf := append([]byte(nil), w.Bytes()...)
goBinary.BigEndian.PutUint16(buf[1:3], uint16(len(buf)))
return buf
}

var (
ErrUnknownFlag = errors.New("unknown flag")
ErrUnknownEncryptType = errors.New("unknown encrypt type")
)

func (c *Codec) Unmarshal(data []byte) (*Message, error) {
reader := binary.NewReader(data)
if flag := reader.ReadByte(); flag != 2 {
return nil, ErrUnknownFlag
}
m := new(Message)
reader.ReadUInt16() // len
reader.ReadUInt16() // version?
m.Command = reader.ReadUInt16()
reader.ReadUInt16() // 1?
m.Uin = uint32(reader.ReadInt32())
reader.ReadByte()
encryptType := reader.ReadByte()
reader.ReadByte()
switch encryptType {
case 0:
m.Body = func() (decrypted []byte) {
d := reader.ReadBytes(reader.Len() - 1)
defer func() {
if pan := recover(); pan != nil {
tea := binary.NewTeaCipher(c.randomKey)
decrypted = tea.Decrypt(d)
}
}()
return binary.NewTeaCipher(c.ecdh.ShareKey).Decrypt(d)
}()
case 3:
d := reader.ReadBytes(reader.Len() - 1)
m.Body = binary.NewTeaCipher(c.WtSessionTicketKey).Decrypt(d)
default:
return nil, ErrUnknownEncryptType
}
return m, nil
}
3 changes: 2 additions & 1 deletion client/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,15 @@ func (c *QQClient) netLoop() {
continue
}
if pkt.Flag2 == 2 {
pkt.Payload, err = pkt.DecryptPayload(c.ecdh.InitialShareKey, c.RandomKey, c.sig.WtSessionTicketKey)
m, err := c.oicq.Unmarshal(pkt.Payload)
if err != nil {
c.Error("decrypt payload error: %v", err)
if errors.Is(err, packets.ErrUnknownFlag) {
go c.quickReconnect()
}
continue
}
pkt.Payload = m.Body
}
errCount = 0
c.Debug("rev pkt: %v seq: %v", pkt.CommandName, pkt.SequenceId)
Expand Down
15 changes: 7 additions & 8 deletions client/packet.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package client

import (
"github.com/Mrs4s/MiraiGo/client/internal/codec"
"github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq"
)

//go:noinline
func (c *QQClient) buildOicqRequestPacket(uin int64, command uint16, body []byte) []byte {
req := codec.OICQ{
Uin: uint32(uin),
Command: command,
EncryptMethod: c.ecdh,
Key: c.RandomKey,
Body: body,
req := oicq.Message{
Uin: uint32(uin),
Command: command,
EncryptionMethod: oicq.EM_ECDH,
Body: body,
}
return req.Encode()
return c.oicq.Marshal(&req)
}

//go:noinline
Expand Down
3 changes: 2 additions & 1 deletion client/tlv_decoders.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ func (c *QQClient) decodeT119(data, ek []byte) {
// readT138(t138) // chg time
}

c.oicq.WtSessionTicketKey = utils.Select(m[0x134], c.oicq.WtSessionTicketKey)

// we don't use `c.sigInfo = &auth.SigInfo{...}` here,
// because we need keep other fields in `c.sigInfo`
s := c.sig
Expand All @@ -104,7 +106,6 @@ func (c *QQClient) decodeT119(data, ek []byte) {
s.SKeyExpiredTime = time.Now().Unix() + 21600
s.D2 = m[0x143]
s.D2Key = m[0x305]
s.WtSessionTicketKey = utils.Select(m[0x134], s.WtSessionTicketKey)
s.DeviceToken = m[0x322]

s.PsKeyMap = psKeyMap
Expand Down
Loading

0 comments on commit 24b75e4

Please sign in to comment.