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  }