github.com/Mrs4s/MiraiGo@v0.0.0-20240226124653-54bdd873e3fe/client/global.go (about) 1 package client 2 3 import ( 4 "crypto/md5" 5 crand "crypto/rand" 6 "encoding/hex" 7 "fmt" 8 "math/rand" 9 "net" 10 "net/netip" 11 "sort" 12 "strconv" 13 "strings" 14 "sync" 15 "time" 16 17 "github.com/pkg/errors" 18 19 "github.com/Mrs4s/MiraiGo/binary" 20 "github.com/Mrs4s/MiraiGo/binary/jce" 21 "github.com/Mrs4s/MiraiGo/client/internal/auth" 22 "github.com/Mrs4s/MiraiGo/client/pb/msg" 23 "github.com/Mrs4s/MiraiGo/client/pb/oidb" 24 "github.com/Mrs4s/MiraiGo/internal/proto" 25 "github.com/Mrs4s/MiraiGo/message" 26 "github.com/Mrs4s/MiraiGo/utils" 27 ) 28 29 type ( 30 DeviceInfo = auth.Device 31 Version = auth.OSVersion 32 ) 33 34 var EmptyBytes = make([]byte, 0) 35 36 func GenRandomDevice() *DeviceInfo { 37 r := make([]byte, 16) 38 crand.Read(r) 39 const numberRange = "0123456789" 40 41 var device = &DeviceInfo{ 42 Product: []byte("mirai"), 43 Device: []byte("mirai"), 44 Board: []byte("mirai"), 45 Brand: []byte("mamoe"), 46 Model: []byte("mirai"), 47 Bootloader: []byte("unknown"), 48 BootId: []byte("cb886ae2-00b6-4d68-a230-787f111d12c7"), 49 ProcVersion: []byte("Linux version 3.0.31-cb886ae2 (android-build@xxx.xxx.xxx.xxx.com)"), 50 BaseBand: EmptyBytes, 51 SimInfo: []byte("T-Mobile"), 52 OSType: []byte("android"), 53 MacAddress: []byte("00:50:56:C0:00:08"), 54 IpAddress: []byte{10, 0, 1, 3}, // 10.0.1.3 55 WifiBSSID: []byte("00:50:56:C0:00:08"), 56 WifiSSID: []byte("<unknown ssid>"), 57 IMEI: "468356291846738", 58 AndroidId: []byte("MIRAI.123456.001"), 59 APN: []byte("wifi"), 60 VendorName: []byte("MIUI"), 61 VendorOSName: []byte("mirai"), 62 Protocol: AndroidPad, 63 Version: &Version{ 64 Incremental: []byte("5891938"), 65 Release: []byte("10"), 66 CodeName: []byte("REL"), 67 SDK: 29, 68 }, 69 } 70 71 device.Display = []byte("MIRAI." + utils.RandomStringRange(6, numberRange) + ".001") 72 device.FingerPrint = []byte("mamoe/mirai/mirai:10/MIRAI.200122.001/" + utils.RandomStringRange(7, numberRange) + ":user/release-keys") 73 device.BootId = binary.GenUUID(r) 74 device.ProcVersion = []byte("Linux version 3.0.31-" + utils.RandomString(8) + " (android-build@xxx.xxx.xxx.xxx.com)") 75 crand.Read(r) 76 t := md5.Sum(r) 77 device.IMSIMd5 = t[:] 78 device.IMEI = GenIMEI() 79 r = make([]byte, 8) 80 crand.Read(r) 81 hex.Encode(device.AndroidId, r) 82 device.GenNewGuid() 83 device.GenNewTgtgtKey() 84 device.RequestQImei() 85 return device 86 } 87 88 func GenIMEI() string { 89 sum := 0 // the control sum of digits 90 var final strings.Builder 91 92 randGen := rand.New(rand.NewSource(time.Now().UnixNano())) 93 for i := 0; i < 14; i++ { // generating all the base digits 94 toAdd := randGen.Intn(10) 95 final.WriteString(strconv.Itoa(toAdd)) 96 if (i+1)%2 == 0 { // special proc for every 2nd one 97 toAdd *= 2 98 if toAdd >= 10 { 99 toAdd = (toAdd % 10) + 1 100 } 101 } 102 sum += toAdd // and even add them here! 103 } 104 ctrlDigit := (sum * 9) % 10 // calculating the control digit 105 final.WriteString(strconv.Itoa(ctrlDigit)) 106 return final.String() 107 } 108 109 func UpdateAppVersion(protocolType auth.ProtocolType, data []byte) error { 110 if _, ok := auth.AppVersions[protocolType]; !ok { 111 return errors.New("unknown protocol type: " + strconv.Itoa(int(protocolType))) 112 } 113 return auth.AppVersions[protocolType].UpdateFromJson(data) 114 } 115 116 func getSSOAddress(device *auth.Device) ([]netip.AddrPort, error) { 117 protocol := device.Protocol.Version() 118 key, _ := hex.DecodeString("F0441F5FF42DA58FDCF7949ABA62D411") 119 payload := jce.NewJceWriter(). // see ServerConfig.d 120 WriteInt64(0, 1).WriteInt64(0, 2).WriteByte(1, 3). 121 WriteString("00000", 4).WriteInt32(100, 5). 122 WriteInt32(int32(protocol.AppId), 6).WriteString(device.IMEI, 7). 123 WriteInt64(0, 8).WriteInt64(0, 9).WriteInt64(0, 10). 124 WriteInt64(0, 11).WriteByte(0, 12).WriteInt64(0, 13).Bytes() 125 buf := &jce.RequestDataVersion3{ 126 Map: map[string][]byte{"HttpServerListReq": packUniRequestData(payload)}, 127 } 128 pkt := &jce.RequestPacket{ 129 IVersion: 3, 130 SServantName: "ConfigHttp", 131 SFuncName: "HttpServerListReq", 132 SBuffer: buf.ToBytes(), 133 } 134 b, cl := binary.OpenWriterF(func(w *binary.Writer) { 135 w.WriteIntLvPacket(0, func(w *binary.Writer) { 136 w.Write(pkt.ToBytes()) 137 }) 138 }) 139 tea := binary.NewTeaCipher(key) 140 encpkt := tea.Encrypt(b) 141 cl() 142 rsp, err := utils.HttpPostBytes("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp?mType=getssolist", encpkt) 143 if err != nil { 144 return nil, errors.Wrap(err, "unable to fetch server list") 145 } 146 rspPkt := &jce.RequestPacket{} 147 data := &jce.RequestDataVersion3{} 148 rspPkt.ReadFrom(jce.NewJceReader(tea.Decrypt(rsp)[4:])) 149 data.ReadFrom(jce.NewJceReader(rspPkt.SBuffer)) 150 reader := jce.NewJceReader(data.Map["HttpServerListRes"][1:]) 151 servers := reader.ReadSsoServerInfos(2) 152 adds := make([]netip.AddrPort, 0, len(servers)) 153 for _, s := range servers { 154 if strings.Contains(s.Server, "com") { 155 continue 156 } 157 ip, ok := netip.AddrFromSlice(net.ParseIP(s.Server)) 158 if ok { 159 adds = append(adds, netip.AddrPortFrom(ip, uint16(s.Port))) 160 } 161 } 162 return adds, nil 163 } 164 165 func qualityTest(addr string) (int64, error) { 166 // see QualityTestManager 167 start := time.Now() 168 conn, err := net.DialTimeout("tcp", addr, time.Second*5) 169 if err != nil { 170 return 0, errors.Wrap(err, "failed to connect to server during quality test") 171 } 172 _ = conn.Close() 173 return time.Since(start).Milliseconds(), nil 174 } 175 176 func (c *QQClient) parsePrivateMessage(msg *msg.Message) *message.PrivateMessage { 177 friend := c.FindFriend(msg.Head.FromUin.Unwrap()) 178 var sender *message.Sender 179 if friend != nil { 180 sender = &message.Sender{ 181 Uin: friend.Uin, 182 Nickname: friend.Nickname, 183 IsFriend: true, 184 } 185 } else { 186 sender = &message.Sender{ 187 Uin: msg.Head.FromUin.Unwrap(), 188 Nickname: msg.Head.FromNick.Unwrap(), 189 } 190 } 191 ret := &message.PrivateMessage{ 192 Id: msg.Head.MsgSeq.Unwrap(), 193 Target: msg.Head.ToUin.Unwrap(), 194 Time: msg.Head.MsgTime.Unwrap(), 195 Sender: sender, 196 Self: c.Uin, 197 Elements: func() []message.IMessageElement { 198 if msg.Body.RichText.Ptt != nil { 199 return []message.IMessageElement{ 200 &message.VoiceElement{ 201 Name: msg.Body.RichText.Ptt.FileName.Unwrap(), 202 Md5: msg.Body.RichText.Ptt.FileMd5, 203 Size: msg.Body.RichText.Ptt.FileSize.Unwrap(), 204 Url: string(msg.Body.RichText.Ptt.DownPara), 205 }, 206 } 207 } 208 return message.ParseMessageElems(msg.Body.RichText.Elems) 209 }(), 210 } 211 if msg.Body.RichText.Attr != nil { 212 ret.InternalId = msg.Body.RichText.Attr.Random.Unwrap() 213 } 214 return ret 215 } 216 217 func (c *QQClient) parseTempMessage(msg *msg.Message) *message.TempMessage { 218 var groupCode int64 219 var groupName string 220 group := c.FindGroupByUin(msg.Head.C2CTmpMsgHead.GroupUin.Unwrap()) 221 sender := &message.Sender{ 222 Uin: msg.Head.FromUin.Unwrap(), 223 Nickname: "Unknown", 224 IsFriend: false, 225 } 226 if group != nil { 227 groupCode = group.Code 228 groupName = group.Name 229 mem := group.FindMember(msg.Head.FromUin.Unwrap()) 230 if mem != nil { 231 sender.Nickname = mem.Nickname 232 sender.CardName = mem.CardName 233 } 234 } 235 return &message.TempMessage{ 236 Id: msg.Head.MsgSeq.Unwrap(), 237 GroupCode: groupCode, 238 GroupName: groupName, 239 Self: c.Uin, 240 Sender: sender, 241 Elements: message.ParseMessageElems(msg.Body.RichText.Elems), 242 } 243 } 244 245 func (c *QQClient) messageBuilder(seq int32) *messageBuilder { 246 actual, ok := c.msgBuilders.Load(seq) 247 if !ok { 248 builder := &messageBuilder{} 249 actual, _ = c.msgBuilders.LoadOrStore(seq, builder) 250 time.AfterFunc(time.Minute, func() { 251 c.msgBuilders.Delete(seq) // delete avoid memory leak 252 }) 253 } 254 return actual 255 } 256 257 type messageBuilder struct { 258 lock sync.Mutex 259 slices []*msg.Message 260 } 261 262 func (b *messageBuilder) append(msg *msg.Message) { 263 b.lock.Lock() 264 defer b.lock.Unlock() 265 b.slices = append(b.slices, msg) 266 } 267 268 func (b *messageBuilder) len() int32 { 269 b.lock.Lock() 270 x := len(b.slices) 271 b.lock.Unlock() 272 return int32(x) 273 } 274 275 func (b *messageBuilder) build() *msg.Message { 276 b.lock.Lock() 277 defer b.lock.Unlock() 278 sort.Slice(b.slices, func(i, j int) bool { 279 return b.slices[i].Content.PkgIndex.Unwrap() < b.slices[j].Content.PkgIndex.Unwrap() 280 }) 281 base := b.slices[0] 282 for _, m := range b.slices[1:] { 283 base.Body.RichText.Elems = append(base.Body.RichText.Elems, m.Body.RichText.Elems...) 284 } 285 return base 286 } 287 288 func packUniRequestData(data []byte) []byte { 289 r := make([]byte, 0, len(data)+2) 290 r = append(r, 0x0a) 291 r = append(r, data...) 292 r = append(r, 0x0B) 293 return r 294 } 295 296 func genForwardTemplate(resID, preview, summary string, ts int64, items []*msg.PbMultiMsgItem) *message.ForwardElement { 297 template := forwardDisplay(resID, strconv.FormatInt(ts, 10), preview, summary) 298 return &message.ForwardElement{ 299 FileName: strconv.FormatInt(ts, 10), 300 Content: template, 301 ResId: resID, 302 Items: items, 303 } 304 } 305 306 func (c *QQClient) getWebDeviceInfo() (i string) { 307 qimei := strings.ToLower(utils.RandomString(36)) 308 i += fmt.Sprintf("i=%v&imsi=&mac=%v&m=%v&o=%v&", c.Device().IMEI, utils.B2S(c.Device().MacAddress), utils.B2S(c.Device().Device), utils.B2S(c.Device().Version.Release)) 309 i += fmt.Sprintf("a=%v&sd=0&c64=0&sc=1&p=1080*2210&aid=%v&", c.Device().Version.SDK, c.Device().IMEI) 310 i += fmt.Sprintf("f=%v&mm=%v&cf=%v&cc=%v&", c.Device().Brand, 5629 /* Total Memory*/, 1725 /* CPU Frequency */, 8 /* CPU Core Count */) 311 i += fmt.Sprintf("qimei=%v&qimei36=%v&", qimei, qimei) 312 i += "sharpP=1&n=wifi&support_xsj_live=true&client_mod=default&timezone=Asia/Shanghai&material_sdk_version=2.9.0&vh265=null&refreshrate=60" 313 return 314 } 315 316 var oidbSSOPool = sync.Pool{} 317 318 func getOidbSSOPackage() *oidb.OIDBSSOPkg { 319 g := oidbSSOPool.Get() 320 if g == nil { 321 return new(oidb.OIDBSSOPkg) 322 } 323 return g.(*oidb.OIDBSSOPkg) 324 } 325 326 func (c *QQClient) packOIDBPackage(cmd, serviceType int32, body []byte) []byte { 327 pkg := getOidbSSOPackage() 328 defer oidbSSOPool.Put(pkg) 329 *pkg = oidb.OIDBSSOPkg{ 330 Command: cmd, 331 ServiceType: serviceType, 332 Bodybuffer: body, 333 ClientVersion: "Android " + c.version().SortVersionName, 334 } 335 r, _ := proto.Marshal(pkg) 336 return r 337 } 338 339 func (c *QQClient) packOIDBPackageDynamically(cmd, serviceType int32, msg proto.DynamicMessage) []byte { 340 return c.packOIDBPackage(cmd, serviceType, msg.Encode()) 341 } 342 343 func (c *QQClient) packOIDBPackageProto(cmd, serviceType int32, msg proto.Message) []byte { 344 b, _ := proto.Marshal(msg) 345 return c.packOIDBPackage(cmd, serviceType, b) 346 } 347 348 func unpackOIDBPackage(payload []byte, rsp proto.Message) error { 349 pkg := getOidbSSOPackage() 350 defer oidbSSOPool.Put(pkg) 351 if err := proto.Unmarshal(payload, pkg); err != nil { 352 return errors.Wrap(err, "failed to unmarshal protobuf message") 353 } 354 if pkg.Result != 0 { 355 return errors.Errorf("oidb result unsuccessful: %v msg: %v", pkg.Result, pkg.ErrorMsg) 356 } 357 if err := proto.Unmarshal(pkg.Bodybuffer, rsp); err != nil { 358 return errors.Wrap(err, "failed to unmarshal protobuf message") 359 } 360 return nil 361 }