diff --git a/binary/reader.go b/binary/reader.go index 3fcaa701..f5ff0bb3 100644 --- a/binary/reader.go +++ b/binary/reader.go @@ -17,8 +17,6 @@ type NetworkReader struct { conn net.Conn } -type TlvMap map[uint16][]byte - // --- ByteStream reader --- func NewReader(data []byte) *Reader { @@ -92,33 +90,6 @@ func (r *Reader) ReadAvailable() []byte { return r.ReadBytes(r.buf.Len()) } -func (r *Reader) ReadTlvMap(tagSize int) (m TlvMap) { - defer func() { - if r := recover(); r != nil { - // TODO: error - } - }() - m = make(map[uint16][]byte) - for { - if r.Len() < tagSize { - return m - } - var k uint16 - switch tagSize { - case 1: - k = uint16(r.ReadByte()) - case 2: - k = r.ReadUInt16() - case 4: - k = uint16(r.ReadInt32()) - } - if k == 255 { - return m - } - m[k] = r.ReadBytes(int(r.ReadUInt16())) - } -} - func (r *Reader) Len() int { return r.buf.Len() } @@ -127,11 +98,6 @@ func (r *Reader) Index() int64 { return r.buf.Size() } -func (tlv TlvMap) Exists(key uint16) bool { - _, ok := tlv[key] - return ok -} - // --- Network reader --- func NewNetworkReader(conn net.Conn) *NetworkReader { diff --git a/client/decoders.go b/client/decoders.go index 55684b46..0289ae83 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -15,6 +15,7 @@ import ( "github.com/Mrs4s/MiraiGo/binary/jce" "github.com/Mrs4s/MiraiGo/client/internal/auth" "github.com/Mrs4s/MiraiGo/client/internal/network" + "github.com/Mrs4s/MiraiGo/client/internal/tlv" "github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/client/pb/cmd0x352" "github.com/Mrs4s/MiraiGo/client/pb/cmd0x6ff" @@ -37,7 +38,10 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b reader.ReadUInt16() // sub command t := reader.ReadByte() reader.ReadUInt16() - m := reader.ReadTlvMap(2) + m, err := tlv.NewDecoder(2, 2).DecodeRecordMap(reader.ReadAvailable()) + if err != nil { + return nil, err + } if m.Exists(0x402) { c.sig.Dpwd = []byte(utils.RandomString(16)) c.sig.T402 = m[0x402] @@ -45,9 +49,7 @@ func decodeLoginResponse(c *QQClient, _ *network.IncomingPacketInfo, payload []b c.sig.G = h[:] } if m.Exists(0x546) { - c.debug("pow start") c.sig.T547 = auth.CalcPow(m[0x546]) - c.debug("pow end") } if t == 0 { // login success // if t150, ok := m[0x150]; ok { @@ -208,7 +210,10 @@ func decodeExchangeEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, paylo cmd := reader.ReadUInt16() t := reader.ReadByte() reader.ReadUInt16() - m := reader.ReadTlvMap(2) + m, err := tlv.NewDecoder(2, 2).DecodeRecordMap(reader.ReadAvailable()) + if err != nil { + return nil, err + } if t != 0 { return nil, errors.Errorf("exchange_emp failed: %v", t) } @@ -248,7 +253,10 @@ func decodeTransEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload } sig := body.ReadBytesShort() body.ReadUInt16() - m := body.ReadTlvMap(2) + m, err := tlv.NewDecoder(2, 2).DecodeRecordMap(reader.ReadAvailable()) + if err != nil { + return nil, err + } if m.Exists(0x17) { return &QRCodeLoginResponse{ State: QRCodeImageFetch, @@ -291,7 +299,10 @@ func decodeTransEmpResponse(c *QQClient, _ *network.IncomingPacketInfo, payload c.highwaySession.Uin = strconv.FormatInt(c.Uin, 10) body.ReadInt32() // sig create time body.ReadUInt16() - m := body.ReadTlvMap(2) + m, err := tlv.NewDecoder(2, 2).DecodeRecordMap(reader.ReadAvailable()) + if err != nil { + return nil, err + } if !m.Exists(0x18) || !m.Exists(0x1e) || !m.Exists(0x19) { return nil, errors.New("wtlogin.trans_emp sub cmd 0x12 error: tlv error") } @@ -447,6 +458,15 @@ func decodeSummaryCardResponse(_ *QQClient, _ *network.IncomingPacketInfo, paylo info.VipLevel = fmt.Sprintf("svip%d", v3.Level) } } + + richSign := rsp.ReadBytes(32) + records, _ := tlv.NewDecoder(1, 1).Decode(richSign) + for _, r := range records { + if r.Tag == 3 { + info.Sign = string(r.Value) + } + } + info.LoginDays = rsp.ReadInt64(36) services := rsp.ReadByteArrArr(46) readService := func(buf []byte) (*profilecard.BusiComm, []byte) { diff --git a/client/internal/tlv/decoder.go b/client/internal/tlv/decoder.go new file mode 100644 index 00000000..82524971 --- /dev/null +++ b/client/internal/tlv/decoder.go @@ -0,0 +1,107 @@ +package tlv + +import ( + "encoding/binary" + + "github.com/pkg/errors" +) + +// Record represents a Tag-Length-Value record. +type Record struct { + Tag int + Length int + Value []byte +} + +type RecordMap map[int][]byte + +func (rm RecordMap) Exists(key int) bool { + _, ok := rm[key] + return ok +} + +var ( + ErrMessageTooShort = errors.New("tlv: message too short") +) + +// Decoder is a configurable TLV decoder. +type Decoder struct { + tagSize uint8 + lenSize uint8 + headSize uint8 +} + +func NewDecoder(tagSize, lenSize uint8) *Decoder { + check := func(t string, s uint8) { + switch s { + case 1, 2, 4: + // ok + default: + panic("invalid " + t) + } + } + check("tag size", tagSize) + check("len size", lenSize) + + return &Decoder{tagSize: tagSize, lenSize: lenSize, headSize: tagSize + lenSize} +} + +func (d *Decoder) decodeRecord(data []byte) (r Record, err error) { + tagSize := d.tagSize + lenSize := d.lenSize + headSize := int(tagSize + lenSize) + if len(data) < headSize { + err = ErrMessageTooShort + return + } + + r.Tag = d.read(tagSize, data) + r.Length = d.read(lenSize, data[tagSize:]) + + if len(data) < headSize+r.Length { + err = ErrMessageTooShort + return + } + r.Value = data[headSize : headSize+r.Length : headSize+r.Length] + return +} + +func (d *Decoder) read(size uint8, data []byte) int { + switch size { + case 1: + return int(data[0]) + case 2: + return int(binary.BigEndian.Uint16(data)) + case 4: + return int(binary.BigEndian.Uint32(data)) + default: + panic("invalid size") + } +} + +func (d *Decoder) Decode(data []byte) ([]Record, error) { + var records []Record + for len(data) > 0 { + r, err := d.decodeRecord(data) + if err != nil { + return nil, err + } + records = append(records, r) + data = data[int(d.headSize)+r.Length:] + } + return records, nil +} + +func (d *Decoder) DecodeRecordMap(data []byte) (RecordMap, error) { + records, err := d.Decode(data) + if err != nil { + return nil, err + } + + rm := make(RecordMap, len(records)) + for _, record := range records { + rm[record.Tag] = record.Value + } + + return rm, nil +} diff --git a/client/tlv_decoders.go b/client/tlv_decoders.go index 030e7ee0..94eba0fb 100644 --- a/client/tlv_decoders.go +++ b/client/tlv_decoders.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/Mrs4s/MiraiGo/client/internal/tlv" "github.com/Mrs4s/MiraiGo/utils" "github.com/Mrs4s/MiraiGo/binary" @@ -25,9 +26,7 @@ func (c *QQClient) decodeT161(data []byte) { func (c *QQClient) decodeT119(data, ek []byte) { tea := binary.NewTeaCipher(ek) - reader := binary.NewReader(tea.Decrypt(data)) - reader.ReadBytes(2) - m := reader.ReadTlvMap(2) + m, _ := tlv.NewDecoder(2, 2).DecodeRecordMap(tea.Decrypt(data)[2:]) if t130, ok := m[0x130]; ok { c.decodeT130(t130) } @@ -133,9 +132,7 @@ func (c *QQClient) decodeT119(data, ek []byte) { // wtlogin.exchange_emp func (c *QQClient) decodeT119R(data []byte) { tea := binary.NewTeaCipher(c.deviceInfo.TgtgtKey) - reader := binary.NewReader(tea.Decrypt(data)) - reader.ReadBytes(2) - m := reader.ReadTlvMap(2) + m, _ := tlv.NewDecoder(2, 2).DecodeRecordMap(tea.Decrypt(data)[2:]) if t120, ok := m[0x120]; ok { c.sig.SKey = t120 c.sig.SKeyExpiredTime = time.Now().Unix() + 21600 @@ -223,8 +220,7 @@ func readT512(data []byte) (psKeyMap map[string][]byte, pt4TokenMap map[string][ } func readT531(data []byte) (a1, noPicSig []byte) { - reader := binary.NewReader(data) - m := reader.ReadTlvMap(2) + m, _ := tlv.NewDecoder(2, 2).DecodeRecordMap(data) if m.Exists(0x103) && m.Exists(0x16a) && m.Exists(0x113) && m.Exists(0x10c) { a1 = append(m[0x106], m[0x10c]...) noPicSig = m[0x16a] diff --git a/go.sum b/go.sum index fa3c6218..6dce1d77 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,11 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fumiama/imgsz v0.0.2 h1:fAkC0FnIscdKOXwAxlyw3EUba5NzxZdSxGaq3Uyfxak= github.com/fumiama/imgsz v0.0.2/go.mod h1:dR71mI3I2O5u6+PCpd47M9TZptzP+39tRBcbdIkoqM4= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -26,8 +31,9 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=