From 3a65d05f33782f6e6d3d00234f44ffd7660bea64 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 19 Dec 2020 14:50:19 +0800 Subject: [PATCH 1/5] fix protocol --- client/builders.go | 10 ++++++---- client/global.go | 2 +- protocol/crypto/crypto.go | 2 +- protocol/tlv/t106.go | 5 ++--- protocol/tlv/t108.go | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/client/builders.go b/client/builders.go index e321b789..77cfd3c2 100644 --- a/client/builders.go +++ b/client/builders.go @@ -36,9 +36,9 @@ func (c *QQClient) buildLoginPacket() (uint16, []byte) { req := packets.BuildOicqRequestPacket(c.Uin, 0x0810, crypto.ECDH, c.RandomKey, func(w *binary.Writer) { w.WriteUInt16(9) if c.AllowSlider { - w.WriteUInt16(0x17) + w.WriteUInt16(0x18) } else { - w.WriteUInt16(0x16) + w.WriteUInt16(0x17) } w.Write(tlv.T18(16, uint32(c.Uin))) @@ -47,6 +47,7 @@ func (c *QQClient) buildLoginPacket() (uint16, []byte) { w.Write(tlv.T116(c.version.MiscBitmap, c.version.SubSigmap)) w.Write(tlv.T100(c.version.SSOVersion, c.version.AppId, c.version.MainSigMap)) w.Write(tlv.T107(0)) + w.Write(tlv.T108(SystemDeviceInfo.IMEI)) w.Write(tlv.T142(c.version.ApkId)) w.Write(tlv.T144( []byte(SystemDeviceInfo.IMEI), @@ -74,10 +75,11 @@ func (c *QQClient) buildLoginPacket() (uint16, []byte) { w.Write(tlv.T8(2052)) w.Write(tlv.T511([]string{ "tenpay.com", "openmobile.qq.com", "docs.qq.com", "connect.qq.com", - "qzone.qq.com", "vip.qq.com", "qun.qq.com", "game.qq.com", "qqweb.qq.com", - "office.qq.com", "ti.qq.com", "mail.qq.com", "qzone.com", "mma.qq.com", + "qzone.qq.com", "vip.qq.com", "gamecenter.qq.com", "qun.qq.com", "game.qq.com", + "qqweb.qq.com", "office.qq.com", "ti.qq.com", "mail.qq.com", "mma.qq.com", })) + //todo: tlv 400 w.Write(tlv.T187(SystemDeviceInfo.MacAddress)) w.Write(tlv.T188(SystemDeviceInfo.AndroidId)) if len(SystemDeviceInfo.IMSIMd5) != 0 { diff --git a/client/global.go b/client/global.go index a52ac046..8f3c8b91 100644 --- a/client/global.go +++ b/client/global.go @@ -151,7 +151,7 @@ func genVersionInfo(p ClientProtocol) *versionInfo { case AndroidPhone: // Dumped by mirai from qq android v8.2.7 return &versionInfo{ ApkId: "com.tencent.mobileqq", - AppId: 537066439, + AppId: 537066419, SortVersionName: "8.4.18", BuildTime: 1604580615, ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D}, diff --git a/protocol/crypto/crypto.go b/protocol/crypto/crypto.go index 06e8dc13..e2964ebd 100644 --- a/protocol/crypto/crypto.go +++ b/protocol/crypto/crypto.go @@ -40,7 +40,7 @@ func init() { panic("Can't Create ECDH key pair") } x, _ := p256.ScalarMult(tenKeyX, tenKeyY, key) - hash := md5.Sum(x.Bytes()[:16]) + hash := md5.Sum(x.Bytes()[:16]) ECDH.InitialShareKey = hash[:] ECDH.PublicKey = make([]byte, 65)[:0] ECDH.PublicKey = append(ECDH.PublicKey, 0x04) diff --git a/protocol/tlv/t106.go b/protocol/tlv/t106.go index 2255d669..699f166b 100644 --- a/protocol/tlv/t106.go +++ b/protocol/tlv/t106.go @@ -5,6 +5,7 @@ import ( binary2 "encoding/binary" "github.com/Mrs4s/MiraiGo/binary" "math/rand" + "strconv" "time" ) @@ -38,9 +39,7 @@ func T106(uin, salt, appId, ssoVer uint32, passwordMd5 [16]byte, guidAvailable b } w.WriteUInt32(appId) w.WriteUInt32(1) // password login - b := make([]byte, 8) - binary2.BigEndian.PutUint64(b, uint64(uin)) - w.WriteTlv(b) + w.WriteTlv([]byte(strconv.FormatInt(int64(uin), 10))) w.WriteUInt16(0) }) w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) { diff --git a/protocol/tlv/t108.go b/protocol/tlv/t108.go index dbf1679f..8fb83f83 100644 --- a/protocol/tlv/t108.go +++ b/protocol/tlv/t108.go @@ -2,9 +2,9 @@ package tlv import "github.com/Mrs4s/MiraiGo/binary" -func T108(arr []byte) []byte { +func T108(imei string) []byte { return binary.NewWriterF(func(w *binary.Writer) { w.WriteUInt16(0x108) - w.WriteTlv(arr) + w.WriteTlv([]byte(imei)) }) } From 478bbdea72a4119e6bb20cf4eee7a7e872439a4f Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Sat, 19 Dec 2020 15:11:50 +0800 Subject: [PATCH 2/5] use code gen sync.Map --- client/client.go | 8 +- client/handler_map_gen.go | 423 ++++++++++++++++++++++++++++++++++++++ go.mod | 2 + go.sum | 24 +++ 4 files changed, 454 insertions(+), 3 deletions(-) create mode 100644 client/handler_map_gen.go diff --git a/client/client.go b/client/client.go index 103cf372..f5b5b95b 100644 --- a/client/client.go +++ b/client/client.go @@ -27,6 +27,8 @@ import ( "github.com/Mrs4s/MiraiGo/utils" ) +//go:generate go run github.com/a8m/syncmap -o "handler_map_gen.go" -pkg client -name HandlerMap "map[uint16]func(i interface{}, err error)" + type QQClient struct { Uin int64 PasswordMd5 [16]byte @@ -46,7 +48,7 @@ type QQClient struct { Conn net.Conn ConnectTime time.Time - handlers sync.Map + handlers HandlerMap servers []*net.TCPAddr currServerIndex int retryTimes int @@ -1017,11 +1019,11 @@ func (c *QQClient) netLoop() { c.Debug("decode pkt %v error: %+v", pkt.CommandName, err) } if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok { - f.(func(i interface{}, err error))(rsp, err) + f(rsp, err) } } else if f, ok := c.handlers.LoadAndDelete(pkt.SequenceId); ok { // does not need decoder - f.(func(i interface{}, err error))(nil, nil) + f(nil, nil) } else { c.Debug("\nUnhandled Command: %s\nSeq: %d\nThis message can be ignored.", pkt.CommandName, pkt.SequenceId) } diff --git a/client/handler_map_gen.go b/client/handler_map_gen.go new file mode 100644 index 00000000..2d9f5b8c --- /dev/null +++ b/client/handler_map_gen.go @@ -0,0 +1,423 @@ +// Code generated by syncmap; DO NOT EDIT. + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package client + +import ( + "sync" + "sync/atomic" + "unsafe" +) + +// Map is like a Go map[interface{}]interface{} but is safe for concurrent use +// by multiple goroutines without additional locking or coordination. +// Loads, stores, and deletes run in amortized constant time. +// +// The Map type is specialized. Most code should use a plain Go map instead, +// with separate locking or coordination, for better type safety and to make it +// easier to maintain other invariants along with the map content. +// +// The Map type is optimized for two common use cases: (1) when the entry for a given +// key is only ever written once but read many times, as in caches that only grow, +// or (2) when multiple goroutines read, write, and overwrite entries for disjoint +// sets of keys. In these two cases, use of a Map may significantly reduce lock +// contention compared to a Go map paired with a separate Mutex or RWMutex. +// +// The zero Map is empty and ready for use. A Map must not be copied after first use. +type HandlerMap struct { + mu sync.Mutex + + // read contains the portion of the map's contents that are safe for + // concurrent access (with or without mu held). + // + // The read field itself is always safe to load, but must only be stored with + // mu held. + // + // Entries stored in read may be updated concurrently without mu, but updating + // a previously-expunged entry requires that the entry be copied to the dirty + // map and unexpunged with mu held. + read atomic.Value // readOnly + + // dirty contains the portion of the map's contents that require mu to be + // held. To ensure that the dirty map can be promoted to the read map quickly, + // it also includes all of the non-expunged entries in the read map. + // + // Expunged entries are not stored in the dirty map. An expunged entry in the + // clean map must be unexpunged and added to the dirty map before a new value + // can be stored to it. + // + // If the dirty map is nil, the next write to the map will initialize it by + // making a shallow copy of the clean map, omitting stale entries. + dirty map[uint16]*entryHandlerMap + + // misses counts the number of loads since the read map was last updated that + // needed to lock mu to determine whether the key was present. + // + // Once enough misses have occurred to cover the cost of copying the dirty + // map, the dirty map will be promoted to the read map (in the unamended + // state) and the next store to the map will make a new dirty copy. + misses int +} + +// readOnly is an immutable struct stored atomically in the Map.read field. +type readOnlyHandlerMap struct { + m map[uint16]*entryHandlerMap + amended bool // true if the dirty map contains some key not in m. +} + +// expunged is an arbitrary pointer that marks entries which have been deleted +// from the dirty map. +var expungedHandlerMap = unsafe.Pointer(new(func(i interface{}, err error, + +))) + +// An entry is a slot in the map corresponding to a particular key. +type entryHandlerMap struct { + // p points to the interface{} value stored for the entry. + // + // If p == nil, the entry has been deleted and m.dirty == nil. + // + // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry + // is missing from m.dirty. + // + // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty + // != nil, in m.dirty[key]. + // + // An entry can be deleted by atomic replacement with nil: when m.dirty is + // next created, it will atomically replace nil with expunged and leave + // m.dirty[key] unset. + // + // An entry's associated value can be updated by atomic replacement, provided + // p != expunged. If p == expunged, an entry's associated value can be updated + // only after first setting m.dirty[key] = e so that lookups using the dirty + // map find the entry. + p unsafe.Pointer // *interface{} +} + +func newEntryHandlerMap(i func(i interface{}, err error, + +)) *entryHandlerMap { + return &entryHandlerMap{p: unsafe.Pointer(&i)} +} + +// Load returns the value stored in the map for a key, or nil if no +// value is present. +// The ok result indicates whether value was found in the map. +func (m *HandlerMap) Load(key uint16) (value func(i interface{}, err error, + +), ok bool) { + read, _ := m.read.Load().(readOnlyHandlerMap) + e, ok := read.m[key] + if !ok && read.amended { + m.mu.Lock() + // Avoid reporting a spurious miss if m.dirty got promoted while we were + // blocked on m.mu. (If further loads of the same key will not miss, it's + // not worth copying the dirty map for this key.) + read, _ = m.read.Load().(readOnlyHandlerMap) + e, ok = read.m[key] + if !ok && read.amended { + e, ok = m.dirty[key] + // Regardless of whether the entry was present, record a miss: this key + // will take the slow path until the dirty map is promoted to the read + // map. + m.missLocked() + } + m.mu.Unlock() + } + if !ok { + return value, false + } + return e.load() +} + +func (e *entryHandlerMap) load() (value func(i interface{}, err error, + +), ok bool) { + p := atomic.LoadPointer(&e.p) + if p == nil || p == expungedHandlerMap { + return value, false + } + return *(*func(i interface{}, err error, + + ))(p), true +} + +// Store sets the value for a key. +func (m *HandlerMap) Store(key uint16, value func(i interface{}, err error, + +)) { + read, _ := m.read.Load().(readOnlyHandlerMap) + if e, ok := read.m[key]; ok && e.tryStore(&value) { + return + } + + m.mu.Lock() + read, _ = m.read.Load().(readOnlyHandlerMap) + if e, ok := read.m[key]; ok { + if e.unexpungeLocked() { + // The entry was previously expunged, which implies that there is a + // non-nil dirty map and this entry is not in it. + m.dirty[key] = e + } + e.storeLocked(&value) + } else if e, ok := m.dirty[key]; ok { + e.storeLocked(&value) + } else { + if !read.amended { + // We're adding the first new key to the dirty map. + // Make sure it is allocated and mark the read-only map as incomplete. + m.dirtyLocked() + m.read.Store(readOnlyHandlerMap{m: read.m, amended: true}) + } + m.dirty[key] = newEntryHandlerMap(value) + } + m.mu.Unlock() +} + +// tryStore stores a value if the entry has not been expunged. +// +// If the entry is expunged, tryStore returns false and leaves the entry +// unchanged. +func (e *entryHandlerMap) tryStore(i *func(i interface{}, err error, + +)) bool { + for { + p := atomic.LoadPointer(&e.p) + if p == expungedHandlerMap { + return false + } + if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { + return true + } + } +} + +// unexpungeLocked ensures that the entry is not marked as expunged. +// +// If the entry was previously expunged, it must be added to the dirty map +// before m.mu is unlocked. +func (e *entryHandlerMap) unexpungeLocked() (wasExpunged bool) { + return atomic.CompareAndSwapPointer(&e.p, expungedHandlerMap, nil) +} + +// storeLocked unconditionally stores a value to the entry. +// +// The entry must be known not to be expunged. +func (e *entryHandlerMap) storeLocked(i *func(i interface{}, err error, + +)) { + atomic.StorePointer(&e.p, unsafe.Pointer(i)) +} + +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. +func (m *HandlerMap) LoadOrStore(key uint16, value func(i interface{}, err error, + +)) (actual func(i interface{}, err error, + +), loaded bool) { + // Avoid locking if it's a clean hit. + read, _ := m.read.Load().(readOnlyHandlerMap) + if e, ok := read.m[key]; ok { + actual, loaded, ok := e.tryLoadOrStore(value) + if ok { + return actual, loaded + } + } + + m.mu.Lock() + read, _ = m.read.Load().(readOnlyHandlerMap) + if e, ok := read.m[key]; ok { + if e.unexpungeLocked() { + m.dirty[key] = e + } + actual, loaded, _ = e.tryLoadOrStore(value) + } else if e, ok := m.dirty[key]; ok { + actual, loaded, _ = e.tryLoadOrStore(value) + m.missLocked() + } else { + if !read.amended { + // We're adding the first new key to the dirty map. + // Make sure it is allocated and mark the read-only map as incomplete. + m.dirtyLocked() + m.read.Store(readOnlyHandlerMap{m: read.m, amended: true}) + } + m.dirty[key] = newEntryHandlerMap(value) + actual, loaded = value, false + } + m.mu.Unlock() + + return actual, loaded +} + +// tryLoadOrStore atomically loads or stores a value if the entry is not +// expunged. +// +// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and +// returns with ok==false. +func (e *entryHandlerMap) tryLoadOrStore(i func(i interface{}, err error, + +)) (actual func(i interface{}, err error, + +), loaded, ok bool) { + p := atomic.LoadPointer(&e.p) + if p == expungedHandlerMap { + return actual, false, false + } + if p != nil { + return *(*func(i interface{}, err error, + + ))(p), true, true + } + + // Copy the interface after the first load to make this method more amenable + // to escape analysis: if we hit the "load" path or the entry is expunged, we + // shouldn't bother heap-allocating. + ic := i + for { + if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) { + return i, false, true + } + p = atomic.LoadPointer(&e.p) + if p == expungedHandlerMap { + return actual, false, false + } + if p != nil { + return *(*func(i interface{}, err error, + + ))(p), true, true + } + } +} + +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *HandlerMap) LoadAndDelete(key uint16) (value func(i interface{}, err error, + +), loaded bool) { + read, _ := m.read.Load().(readOnlyHandlerMap) + e, ok := read.m[key] + if !ok && read.amended { + m.mu.Lock() + read, _ = m.read.Load().(readOnlyHandlerMap) + e, ok = read.m[key] + if !ok && read.amended { + e, ok = m.dirty[key] + delete(m.dirty, key) + // Regardless of whether the entry was present, record a miss: this key + // will take the slow path until the dirty map is promoted to the read + // map. + m.missLocked() + } + m.mu.Unlock() + } + if ok { + return e.delete() + } + return value, false +} + +// Delete deletes the value for a key. +func (m *HandlerMap) Delete(key uint16) { + m.LoadAndDelete(key) +} + +func (e *entryHandlerMap) delete() (value func(i interface{}, err error, + +), ok bool) { + for { + p := atomic.LoadPointer(&e.p) + if p == nil || p == expungedHandlerMap { + return value, false + } + if atomic.CompareAndSwapPointer(&e.p, p, nil) { + return *(*func(i interface{}, err error, + + ))(p), true + } + } +} + +// Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. +func (m *HandlerMap) Range(f func(key uint16, value func(i interface{}, err error, + +)) bool) { + // We need to be able to iterate over all of the keys that were already + // present at the start of the call to Range. + // If read.amended is false, then read.m satisfies that property without + // requiring us to hold m.mu for a long time. + read, _ := m.read.Load().(readOnlyHandlerMap) + if read.amended { + // m.dirty contains keys not in read.m. Fortunately, Range is already O(N) + // (assuming the caller does not break out early), so a call to Range + // amortizes an entire copy of the map: we can promote the dirty copy + // immediately! + m.mu.Lock() + read, _ = m.read.Load().(readOnlyHandlerMap) + if read.amended { + read = readOnlyHandlerMap{m: m.dirty} + m.read.Store(read) + m.dirty = nil + m.misses = 0 + } + m.mu.Unlock() + } + + for k, e := range read.m { + v, ok := e.load() + if !ok { + continue + } + if !f(k, v) { + break + } + } +} + +func (m *HandlerMap) missLocked() { + m.misses++ + if m.misses < len(m.dirty) { + return + } + m.read.Store(readOnlyHandlerMap{m: m.dirty}) + m.dirty = nil + m.misses = 0 +} + +func (m *HandlerMap) dirtyLocked() { + if m.dirty != nil { + return + } + + read, _ := m.read.Load().(readOnlyHandlerMap) + m.dirty = make(map[uint16]*entryHandlerMap, len(read.m)) + for k, e := range read.m { + if !e.tryExpungeLocked() { + m.dirty[k] = e + } + } +} + +func (e *entryHandlerMap) tryExpungeLocked() (isExpunged bool) { + p := atomic.LoadPointer(&e.p) + for p == nil { + if atomic.CompareAndSwapPointer(&e.p, nil, expungedHandlerMap) { + return true + } + p = atomic.LoadPointer(&e.p) + } + return p == expungedHandlerMap +} diff --git a/go.mod b/go.mod index e5cab35d..f6ab844c 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,10 @@ module github.com/Mrs4s/MiraiGo go 1.15 require ( + github.com/a8m/syncmap v0.0.0-20200818084611-4bbbd178de97 // indirect github.com/golang/protobuf v1.4.3 github.com/pkg/errors v0.9.1 github.com/tidwall/gjson v1.6.3 + golang.org/x/tools v0.0.0-20201218024724-ae774e9781d2 // indirect google.golang.org/protobuf v1.25.0 ) diff --git a/go.sum b/go.sum index b47bdce7..f0d726a6 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/a8m/syncmap v0.0.0-20200818084611-4bbbd178de97 h1:QJIAdw5m5tNUy7fjBxgg73+YUs/AkeESeqdJ1L3lN10= +github.com/a8m/syncmap v0.0.0-20200818084611-4bbbd178de97/go.mod h1:f3iF7/3t9i9hsYF8DPgT0XeIVyNzevhMCKf2445Q6pE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -32,28 +34,50 @@ github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU= github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190501045030-23463209683d/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201218024724-ae774e9781d2 h1:lHDhNNs7asPT3p01mm8EP3B+bNyyVfg0bcYjhJUYgxw= +golang.org/x/tools v0.0.0-20201218024724-ae774e9781d2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= From 93ff7797ed6bbfdf51d0aefde4225dfaa0391a49 Mon Sep 17 00:00:00 2001 From: Mrs4s <1844812067@qq.com> Date: Sun, 20 Dec 2020 12:59:33 +0800 Subject: [PATCH 3/5] fix channel closed. --- client/group_msg.go | 1 - 1 file changed, 1 deletion(-) diff --git a/client/group_msg.go b/client/group_msg.go index 41dacec2..6c60a8dd 100644 --- a/client/group_msg.go +++ b/client/group_msg.go @@ -85,7 +85,6 @@ func (c *QQClient) sendGroupMessage(groupCode int64, forward bool, m *message.Se } }) defer c.onGroupMessageReceipt(eid) - defer close(ch) imgCount := m.Count(func(e message.IMessageElement) bool { return e.Type() == message.Image }) msgLen := message.EstimateLength(m.Elements, 703) if (msgLen > 100 || imgCount > 1) && !forward && !m.Any(func(e message.IMessageElement) bool { From deae3e37551374338450235d68422e2a9da9c96c Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Tue, 22 Dec 2020 14:04:56 +0800 Subject: [PATCH 4/5] Android 8.5.0 --- client/builders.go | 6 ++---- client/global.go | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/client/builders.go b/client/builders.go index 77cfd3c2..22b06042 100644 --- a/client/builders.go +++ b/client/builders.go @@ -36,9 +36,9 @@ func (c *QQClient) buildLoginPacket() (uint16, []byte) { req := packets.BuildOicqRequestPacket(c.Uin, 0x0810, crypto.ECDH, c.RandomKey, func(w *binary.Writer) { w.WriteUInt16(9) if c.AllowSlider { - w.WriteUInt16(0x18) - } else { w.WriteUInt16(0x17) + } else { + w.WriteUInt16(0x16) } w.Write(tlv.T18(16, uint32(c.Uin))) @@ -47,7 +47,6 @@ func (c *QQClient) buildLoginPacket() (uint16, []byte) { w.Write(tlv.T116(c.version.MiscBitmap, c.version.SubSigmap)) w.Write(tlv.T100(c.version.SSOVersion, c.version.AppId, c.version.MainSigMap)) w.Write(tlv.T107(0)) - w.Write(tlv.T108(SystemDeviceInfo.IMEI)) w.Write(tlv.T142(c.version.ApkId)) w.Write(tlv.T144( []byte(SystemDeviceInfo.IMEI), @@ -79,7 +78,6 @@ func (c *QQClient) buildLoginPacket() (uint16, []byte) { "qqweb.qq.com", "office.qq.com", "ti.qq.com", "mail.qq.com", "mma.qq.com", })) - //todo: tlv 400 w.Write(tlv.T187(SystemDeviceInfo.MacAddress)) w.Write(tlv.T188(SystemDeviceInfo.AndroidId)) if len(SystemDeviceInfo.IMSIMd5) != 0 { diff --git a/client/global.go b/client/global.go index 8f3c8b91..644548c9 100644 --- a/client/global.go +++ b/client/global.go @@ -151,12 +151,12 @@ func genVersionInfo(p ClientProtocol) *versionInfo { case AndroidPhone: // Dumped by mirai from qq android v8.2.7 return &versionInfo{ ApkId: "com.tencent.mobileqq", - AppId: 537066419, - SortVersionName: "8.4.18", - BuildTime: 1604580615, + AppId: 537066738, + SortVersionName: "8.5.0", + BuildTime: 1607689988, ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D}, SdkVersion: "6.0.0.2454", - SSOVersion: 13, + SSOVersion: 15, MiscBitmap: 184024956, SubSigmap: 0x10400, MainSigMap: 34869472, From 32ff542ba0acc690edf320309af80474b8346782 Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Thu, 24 Dec 2020 14:37:59 +0800 Subject: [PATCH 5/5] =?UTF-8?q?get=20forward=20msg=20=D0=9C=D0=B0=D1=82?= =?UTF-8?q?=D1=80=D1=91=D1=88=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 套娃套娃 --- client/client.go | 15 ++++++++++++++- client/pb/msg/msg.pb.go | 42 +++++++++++++++++++++-------------------- client/pb/msg/msg.proto | 2 +- message/message.go | 3 +-- 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/client/client.go b/client/client.go index f5b5b95b..8328cba4 100644 --- a/client/client.go +++ b/client/client.go @@ -490,7 +490,20 @@ func (c *QQClient) GetForwardMessage(resId string) *message.ForwardMessage { } multiMsg := i.(*msg.PbMultiMsgTransmit) ret := &message.ForwardMessage{} - for _, m := range multiMsg.Msg { + if multiMsg.GetPbItemList() == nil { + return nil + } + var msg *msg.PbMultiMsgItem + for _, m := range multiMsg.GetPbItemList() { + if m.GetFileName() == "MultiMsg" { + msg = m + break + } + } + if msg == nil || msg.GetBuffer() == nil || msg.GetBuffer().GetMsg() == nil { + return nil + } + for _, m := range msg.GetBuffer().GetMsg() { ret.Nodes = append(ret.Nodes, &message.ForwardNode{ SenderId: m.Head.GetFromUin(), SenderName: func() string { diff --git a/client/pb/msg/msg.pb.go b/client/pb/msg/msg.pb.go index 409b42f6..b427899e 100644 --- a/client/pb/msg/msg.pb.go +++ b/client/pb/msg/msg.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 -// protoc v3.11.4 +// protoc v3.13.0 // source: msg.proto package msg @@ -6041,8 +6041,8 @@ type PbMultiMsgItem struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - FileName *string `protobuf:"bytes,1,opt,name=fileName" json:"fileName,omitempty"` - Buffer []byte `protobuf:"bytes,2,opt,name=buffer" json:"buffer,omitempty"` + FileName *string `protobuf:"bytes,1,opt,name=fileName" json:"fileName,omitempty"` + Buffer *PbMultiMsgNew `protobuf:"bytes,2,opt,name=buffer" json:"buffer,omitempty"` } func (x *PbMultiMsgItem) Reset() { @@ -6084,7 +6084,7 @@ func (x *PbMultiMsgItem) GetFileName() string { return "" } -func (x *PbMultiMsgItem) GetBuffer() []byte { +func (x *PbMultiMsgItem) GetBuffer() *PbMultiMsgNew { if x != nil { return x.Buffer } @@ -7692,11 +7692,12 @@ var file_msg_proto_rawDesc = []byte{ 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x70, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x72, 0x70, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x62, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x09, 0x70, 0x62, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x22, 0x44, 0x0a, 0x0e, 0x50, + 0x52, 0x09, 0x70, 0x62, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x22, 0x54, 0x0a, 0x0e, 0x50, 0x62, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x73, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, + 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x62, 0x75, 0x66, + 0x66, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x50, 0x62, 0x4d, 0x75, + 0x6c, 0x74, 0x69, 0x4d, 0x73, 0x67, 0x4e, 0x65, 0x77, 0x52, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x22, 0x2b, 0x0a, 0x0d, 0x50, 0x62, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x4d, 0x73, 0x67, 0x4e, 0x65, 0x77, 0x12, 0x1a, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x61, @@ -7927,19 +7928,20 @@ var file_msg_proto_depIdxs = []int32{ 52, // 55: InstCtrl.msgExcludeInst:type_name -> InstInfo 52, // 56: InstCtrl.msgFromInst:type_name -> InstInfo 53, // 57: TransMsgInfo.extGroupKeyInfo:type_name -> ExtGroupKeyInfo - 20, // 58: PbMultiMsgNew.msg:type_name -> Message - 20, // 59: PbMultiMsgTransmit.msg:type_name -> Message - 57, // 60: PbMultiMsgTransmit.pbItemList:type_name -> PbMultiMsgItem - 44, // 61: MsgElemInfo_servtype3.flash_troop_pic:type_name -> CustomFace - 34, // 62: MsgElemInfo_servtype3.flash_c2c_pic:type_name -> NotOnlineImage - 35, // 63: SubMsgType0x4Body.notOnlineFile:type_name -> NotOnlineFile - 64, // 64: ResvAttr.image_show:type_name -> AnimationImageShow - 20, // 65: GetGroupMsgResp.msg:type_name -> Message - 66, // [66:66] is the sub-list for method output_type - 66, // [66:66] is the sub-list for method input_type - 66, // [66:66] is the sub-list for extension type_name - 66, // [66:66] is the sub-list for extension extendee - 0, // [0:66] is the sub-list for field type_name + 58, // 58: PbMultiMsgItem.buffer:type_name -> PbMultiMsgNew + 20, // 59: PbMultiMsgNew.msg:type_name -> Message + 20, // 60: PbMultiMsgTransmit.msg:type_name -> Message + 57, // 61: PbMultiMsgTransmit.pbItemList:type_name -> PbMultiMsgItem + 44, // 62: MsgElemInfo_servtype3.flash_troop_pic:type_name -> CustomFace + 34, // 63: MsgElemInfo_servtype3.flash_c2c_pic:type_name -> NotOnlineImage + 35, // 64: SubMsgType0x4Body.notOnlineFile:type_name -> NotOnlineFile + 64, // 65: ResvAttr.image_show:type_name -> AnimationImageShow + 20, // 66: GetGroupMsgResp.msg:type_name -> Message + 67, // [67:67] is the sub-list for method output_type + 67, // [67:67] is the sub-list for method input_type + 67, // [67:67] is the sub-list for extension type_name + 67, // [67:67] is the sub-list for extension extendee + 0, // [0:67] is the sub-list for field type_name } func init() { file_msg_proto_init() } diff --git a/client/pb/msg/msg.proto b/client/pb/msg/msg.proto index 01757128..9b5a26d0 100644 --- a/client/pb/msg/msg.proto +++ b/client/pb/msg/msg.proto @@ -700,7 +700,7 @@ message GeneralFlags { message PbMultiMsgItem { optional string fileName = 1; - optional bytes buffer = 2; + optional PbMultiMsgNew buffer = 2; } message PbMultiMsgNew { repeated Message msg = 1; diff --git a/message/message.go b/message/message.go index 7c79f06b..746d8a89 100644 --- a/message/message.go +++ b/message/message.go @@ -557,11 +557,10 @@ func (forMsg *ForwardMessage) CalculateValidationData(seq, random int32, groupCo }, }) } - buf, _ := proto.Marshal(&msg.PbMultiMsgNew{Msg: msgs}) trans := &msg.PbMultiMsgTransmit{Msg: msgs, PbItemList: []*msg.PbMultiMsgItem{ { FileName: proto.String("MultiMsg"), - Buffer: buf, + Buffer: &msg.PbMultiMsgNew{Msg: msgs}, }, }} b, _ := proto.Marshal(trans)