diff --git a/client/builders.go b/client/builders.go index 0c7bd83f..5558c2f0 100644 --- a/client/builders.go +++ b/client/builders.go @@ -137,9 +137,8 @@ func (c *QQClient) buildDeviceLockLoginPacket() (uint16, []byte) { } func (c *QQClient) buildQRCodeFetchRequestPacket(size, margin, ecLevel uint32) (uint16, []byte) { - version := c.transport.Version watch := auth.AndroidWatch.Version() - c.transport.Version = watch + c.transport.Version = &watch seq := c.nextSeq() req := oicq.Message{ Command: 0x0812, @@ -173,13 +172,13 @@ func (c *QQClient) buildQRCodeFetchRequestPacket(size, margin, ecLevel uint32) ( Body: c.oicq.Marshal(&req), } payload := c.transport.PackPacket(&r) - c.transport.Version = version + c.transport.Version = &c.version return seq, payload } func (c *QQClient) buildQRCodeResultQueryRequestPacket(sig []byte) (uint16, []byte) { - version := c.transport.Version - c.transport.Version = auth.AndroidWatch.Version() + watch := auth.AndroidWatch.Version() + c.transport.Version = &watch seq := c.nextSeq() req := oicq.Message{ Command: 0x0812, @@ -209,7 +208,7 @@ func (c *QQClient) buildQRCodeResultQueryRequestPacket(sig []byte) (uint16, []by Body: c.oicq.Marshal(&req), } payload := c.transport.PackPacket(&r) - c.transport.Version = version + c.transport.Version = &c.version return seq, payload } diff --git a/client/client.go b/client/client.go index 3e579ca0..bac2f7e5 100644 --- a/client/client.go +++ b/client/client.go @@ -4,7 +4,6 @@ import ( "crypto/md5" "fmt" "math/rand" - "net" "net/netip" "sort" "strconv" @@ -62,6 +61,7 @@ type QQClient struct { // internal state handlers syncx.Map[uint16, *handlerInfo] waiters syncx.Map[string, func(any, error)] + initServerOnce sync.Once servers []netip.AddrPort currServerIndex int retryTimes int @@ -197,15 +197,12 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient { onlinePushCache: utils.NewCache[unit](time.Second * 15), alive: true, highwaySession: new(highway.Session), - - version: new(auth.AppVersion), - device: new(auth.Device), } cli.transport = &network.Transport{ Sig: cli.sig, - Version: cli.version, - Device: cli.device, + Version: &cli.version, + Device: &cli.device, } cli.oicq = oicq.NewCodec(cli.Uin) { // init atomic values @@ -217,62 +214,18 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient { } cli.highwaySession.Uin = strconv.FormatInt(cli.Uin, 10) cli.GuildService = &GuildService{c: cli} - cli.UseDevice(SystemDeviceInfo) - sso, err := getSSOAddress() - if err == nil && len(sso) > 0 { - cli.servers = append(sso, cli.servers...) - } - adds, err := net.LookupIP("msfwifi.3g.qq.com") // host servers - if err == nil && len(adds) > 0 { - var hostAddrs []netip.AddrPort - for _, addr := range adds { - ip, ok := netip.AddrFromSlice(addr.To4()) - if ok { - hostAddrs = append(hostAddrs, netip.AddrPortFrom(ip, 8080)) - } - } - cli.servers = append(hostAddrs, cli.servers...) - } - if len(cli.servers) == 0 { - cli.servers = []netip.AddrPort{ // default servers - netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 81}), 80), - netip.AddrPortFrom(netip.AddrFrom4([4]byte{114, 221, 148, 59}), 14000), - netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 147}), 443), - netip.AddrPortFrom(netip.AddrFrom4([4]byte{125, 94, 60, 146}), 80), - netip.AddrPortFrom(netip.AddrFrom4([4]byte{114, 221, 144, 215}), 80), - netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 22}), 80), - } - } - pings := make([]int64, len(cli.servers)) - wg := sync.WaitGroup{} - wg.Add(len(cli.servers)) - // println(len(cli.servers)) - for i := range cli.servers { - go func(index int) { - defer wg.Done() - p, err := qualityTest(cli.servers[index].String()) - if err != nil { - pings[index] = 9999 - return - } - pings[index] = p - }(i) - } - wg.Wait() - sort.Slice(cli.servers, func(i, j int) bool { - return pings[i] < pings[j] - }) - if len(cli.servers) > 3 { - cli.servers = cli.servers[0 : len(cli.servers)/2] // 保留ping值中位数以上的server - } cli.TCP.PlannedDisconnect(cli.plannedDisconnect) cli.TCP.UnexpectedDisconnect(cli.unexpectedDisconnect) return cli } +func (c *QQClient) Device() *DeviceInfo { + return c.device +} + func (c *QQClient) UseDevice(info *auth.Device) { - *c.version = *info.Protocol.Version() - *c.device = *info + c.version = info.Protocol.Version() + c.device = info c.highwaySession.AppID = int32(c.version.AppId) c.sig.Ksid = []byte(fmt.Sprintf("|%s|A8.2.7.27f6ea96", info.IMEI)) } diff --git a/client/global.go b/client/global.go index fd60eb46..c5c15e70 100644 --- a/client/global.go +++ b/client/global.go @@ -31,66 +31,57 @@ type ( Version = auth.OSVersion ) -var SystemDeviceInfo = &DeviceInfo{ - Display: []byte("MIRAI.123456.001"), - Product: []byte("mirai"), - Device: []byte("mirai"), - Board: []byte("mirai"), - Brand: []byte("mamoe"), - Model: []byte("mirai"), - Bootloader: []byte("unknown"), - FingerPrint: []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/1234567:user/release-keys"), - BootId: []byte("cb886ae2-00b6-4d68-a230-787f111d12c7"), - ProcVersion: []byte("Linux version 3.0.31-cb886ae2 (android-build@xxx.xxx.xxx.xxx.com)"), - BaseBand: EmptyBytes, - SimInfo: []byte("T-Mobile"), - OSType: []byte("android"), - MacAddress: []byte("00:50:56:C0:00:08"), - IpAddress: []byte{10, 0, 1, 3}, // 10.0.1.3 - WifiBSSID: []byte("00:50:56:C0:00:08"), - WifiSSID: []byte(""), - IMEI: "468356291846738", - AndroidId: []byte("MIRAI.123456.001"), - APN: []byte("wifi"), - VendorName: []byte("MIUI"), - VendorOSName: []byte("mirai"), - Protocol: IPad, - Version: &Version{ - Incremental: []byte("5891938"), - Release: []byte("10"), - CodeName: []byte("REL"), - SDK: 29, - }, -} - var EmptyBytes = make([]byte, 0) -func init() { - r := make([]byte, 16) - crand.Read(r) - t := md5.Sum(r) - SystemDeviceInfo.IMSIMd5 = t[:] - SystemDeviceInfo.GenNewGuid() - SystemDeviceInfo.GenNewTgtgtKey() -} - -func GenRandomDevice() { +func GenRandomDevice() *DeviceInfo { r := make([]byte, 16) crand.Read(r) const numberRange = "0123456789" - SystemDeviceInfo.Display = []byte("MIRAI." + utils.RandomStringRange(6, numberRange) + ".001") - SystemDeviceInfo.FingerPrint = []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/" + utils.RandomStringRange(7, numberRange) + ":user/release-keys") - SystemDeviceInfo.BootId = binary.GenUUID(r) - SystemDeviceInfo.ProcVersion = []byte("Linux version 3.0.31-" + utils.RandomString(8) + " (android-build@xxx.xxx.xxx.xxx.com)") + + var device = &DeviceInfo{ + Product: []byte("mirai"), + Device: []byte("mirai"), + Board: []byte("mirai"), + Brand: []byte("mamoe"), + Model: []byte("mirai"), + Bootloader: []byte("unknown"), + BootId: []byte("cb886ae2-00b6-4d68-a230-787f111d12c7"), + ProcVersion: []byte("Linux version 3.0.31-cb886ae2 (android-build@xxx.xxx.xxx.xxx.com)"), + BaseBand: EmptyBytes, + SimInfo: []byte("T-Mobile"), + OSType: []byte("android"), + MacAddress: []byte("00:50:56:C0:00:08"), + IpAddress: []byte{10, 0, 1, 3}, // 10.0.1.3 + WifiBSSID: []byte("00:50:56:C0:00:08"), + WifiSSID: []byte(""), + IMEI: "468356291846738", + AndroidId: []byte("MIRAI.123456.001"), + APN: []byte("wifi"), + VendorName: []byte("MIUI"), + VendorOSName: []byte("mirai"), + Protocol: IPad, + Version: &Version{ + Incremental: []byte("5891938"), + Release: []byte("10"), + CodeName: []byte("REL"), + SDK: 29, + }, + } + + device.Display = []byte("MIRAI." + utils.RandomStringRange(6, numberRange) + ".001") + device.FingerPrint = []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/" + utils.RandomStringRange(7, numberRange) + ":user/release-keys") + device.BootId = binary.GenUUID(r) + device.ProcVersion = []byte("Linux version 3.0.31-" + utils.RandomString(8) + " (android-build@xxx.xxx.xxx.xxx.com)") crand.Read(r) t := md5.Sum(r) - SystemDeviceInfo.IMSIMd5 = t[:] - SystemDeviceInfo.IMEI = GenIMEI() + device.IMSIMd5 = t[:] + device.IMEI = GenIMEI() r = make([]byte, 8) crand.Read(r) - hex.Encode(SystemDeviceInfo.AndroidId, r) - SystemDeviceInfo.GenNewGuid() - SystemDeviceInfo.GenNewTgtgtKey() + hex.Encode(device.AndroidId, r) + device.GenNewGuid() + device.GenNewTgtgtKey() + return device } func GenIMEI() string { @@ -114,13 +105,13 @@ func GenIMEI() string { return final.String() } -func getSSOAddress() ([]netip.AddrPort, error) { - protocol := SystemDeviceInfo.Protocol.Version() +func getSSOAddress(device *auth.Device) ([]netip.AddrPort, error) { + protocol := device.Protocol.Version() key, _ := hex.DecodeString("F0441F5FF42DA58FDCF7949ABA62D411") payload := jce.NewJceWriter(). // see ServerConfig.d WriteInt64(0, 1).WriteInt64(0, 2).WriteByte(1, 3). WriteString("00000", 4).WriteInt32(100, 5). - WriteInt32(int32(protocol.AppId), 6).WriteString(SystemDeviceInfo.IMEI, 7). + WriteInt32(int32(protocol.AppId), 6).WriteString(device.IMEI, 7). WriteInt64(0, 8).WriteInt64(0, 9).WriteInt64(0, 10). WriteInt64(0, 11).WriteByte(0, 12).WriteInt64(0, 13).Bytes() buf := &jce.RequestDataVersion3{ diff --git a/client/internal/auth/device.go b/client/internal/auth/device.go index 27a1d3f5..9dde0f37 100644 --- a/client/internal/auth/device.go +++ b/client/internal/auth/device.go @@ -139,6 +139,14 @@ func (info *Device) ReadJson(d []byte) error { default: info.Protocol = IPad } + + v := new(OSVersion) + v.SDK = f.Version.Sdk + v.Release = []byte(f.Version.Release) + v.CodeName = []byte(f.Version.Codename) + v.Incremental = []byte(f.Version.Incremental) + info.Version = v + info.GenNewGuid() info.GenNewTgtgtKey() return nil diff --git a/client/internal/network/transport.go b/client/internal/network/transport.go index 26417ffc..f258e4c0 100644 --- a/client/internal/network/transport.go +++ b/client/internal/network/transport.go @@ -10,8 +10,8 @@ import ( // Transport is a network transport. type Transport struct { Sig *auth.SigInfo - Version *auth.AppVersion - Device *auth.Device + Version **auth.AppVersion + Device **auth.Device // connection // conn *TCPClient @@ -21,8 +21,8 @@ func (t *Transport) packBody(req *Request, w *binary.Writer) { pos := w.FillUInt32() if req.Type == RequestTypeLogin { w.WriteUInt32(uint32(req.SequenceID)) - w.WriteUInt32(t.Version.AppId) - w.WriteUInt32(t.Version.SubAppId) + w.WriteUInt32((*t.Version).AppId) + w.WriteUInt32((*t.Version).SubAppId) w.Write([]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}) tgt := t.Sig.TGT if len(tgt) == 0 || len(tgt) == 4 { @@ -36,7 +36,7 @@ func (t *Transport) packBody(req *Request, w *binary.Writer) { w.WriteUInt32(uint32(len(t.Sig.OutPacketSessionID) + 4)) w.Write(t.Sig.OutPacketSessionID) if req.Type == RequestTypeLogin { - w.WriteString(t.Device.IMEI) + w.WriteString((*t.Device).IMEI) w.WriteUInt32(0x04) w.WriteUInt16(uint16(len(t.Sig.Ksid)) + 2) diff --git a/client/network.go b/client/network.go index 55c74f41..6b74d7e9 100644 --- a/client/network.go +++ b/client/network.go @@ -2,7 +2,9 @@ package client import ( "net" + "net/netip" "runtime/debug" + "sort" "sync" "time" @@ -86,8 +88,65 @@ func (c *QQClient) ConnectionQualityTest() *ConnectionQualityInfo { return r } +func (c *QQClient) initServers() { + if c.device == nil { + // must have device. Use c.UseDevice to set it! + panic("client device is nil") + } + + sso, err := getSSOAddress(c.device) + if err == nil && len(sso) > 0 { + c.servers = append(sso, c.servers...) + } + adds, err := net.LookupIP("msfwifi.3g.qq.com") // host servers + if err == nil && len(adds) > 0 { + var hostAddrs []netip.AddrPort + for _, addr := range adds { + ip, ok := netip.AddrFromSlice(addr.To4()) + if ok { + hostAddrs = append(hostAddrs, netip.AddrPortFrom(ip, 8080)) + } + } + c.servers = append(hostAddrs, c.servers...) + } + if len(c.servers) == 0 { + c.servers = []netip.AddrPort{ // default servers + netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 81}), 80), + netip.AddrPortFrom(netip.AddrFrom4([4]byte{114, 221, 148, 59}), 14000), + netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 147}), 443), + netip.AddrPortFrom(netip.AddrFrom4([4]byte{125, 94, 60, 146}), 80), + netip.AddrPortFrom(netip.AddrFrom4([4]byte{114, 221, 144, 215}), 80), + netip.AddrPortFrom(netip.AddrFrom4([4]byte{42, 81, 172, 22}), 80), + } + } + pings := make([]int64, len(c.servers)) + wg := sync.WaitGroup{} + wg.Add(len(c.servers)) + for i := range c.servers { + go func(index int) { + defer wg.Done() + p, err := qualityTest(c.servers[index].String()) + if err != nil { + pings[index] = 9999 + return + } + pings[index] = p + }(i) + } + wg.Wait() + sort.Slice(c.servers, func(i, j int) bool { + return pings[i] < pings[j] + }) + if len(c.servers) > 3 { + c.servers = c.servers[0 : len(c.servers)/2] // 保留ping值中位数以上的server + } +} + // connect 连接到 QQClient.servers 中的服务器 func (c *QQClient) connect() error { + // init qq servers + c.initServerOnce.Do(c.initServers) + addr := c.servers[c.currServerIndex].String() c.info("connect to server: %v", addr) err := c.TCP.Connect(addr)