package baseinfo import ( "encoding/base64" "fmt" "time" "xiawan/wx/clientsdk/baseutils" "xiawan/wx/clientsdk/cecdh" "xiawan/wx/clientsdk/mmtls" "xiawan/wx/clientsdk/proxynet" "xiawan/wx/protobuf/wechat" "golang.org/x/net/proxy" ) // SDKVersion 本协议SDK版本号 var SDKVersion = string("1.0.0") // version = 402666278; // phoneOsType = "iPhone IOS17.5.1"; // osType = "iPad iOS17.5.1"; // 0x1800442a 8068 // 0x1800452a 8069 (海外版本) // 绕过验证码(低版本) // var ClientVersionNotVerify = uint32(385881100) var ClientVersionNotVerify = uint32(0x1800442a) // 845 (也能绕过验证码) var ClientVersion = uint32(0x1800442a) var PlistVersion = uint32(0x1800442a) var ServerVersion = uint32(0x1800442a) // 版本号 var IPadVersion = 0x1800442a var IPhoneVersion = 0x1800442a // 安卓平板 var AndroidPadDeviceType = "pad-android-34" var AndroidPadModel = "HUAWEI MRO-W00" //HUAWEI MatePad Pro var AndroidPadDeviceName = "HUAWEI MatePad Pro" var AndroidPadOsVersion = "10" var AndroidPadClientVersion = uint32(0x1800442a) // mac参数 var MacDeviceType = "iMac MacBookPro16,1 OSX OSX11.5.2 build(20G95)" var MacDeviceName = "MacBook Pro" var MacModel = "iMac MacBookPro16,1" var MacOsVersion = "11.5.2" var MacVersion = uint32(0x1800442a) // 车载 var CarDeviceType = "car-31" var CarDeviceName = "Xiaomi-M2012K11AC" var CarModel = "Xiaomi-M2012K11AC" var CarOsVersion = "10" var CarClientVersion = uint32(0x2100091B) // win11 var WinDeviceType11 = "Windows 11 x64" var WinDeviceName11 = "DESKTOP-P0QLAW8" // var WinModel11 = "ASUS" var WinOsVersion11 = "11" var WinClientVersion = uint32(0x64000115) // ipad var DeviceTypeIos = "Ipad iPadOS15.7.9" var IPadOsVersion = "15.7.9" var IPadDeviceName = "ipad" var IPadModel = "Ipad5,1" // iphone var IPhoneModel = "iPhone9,1" // AndroidClientVersion 微信安卓客户端版本号 8.0.49.87 var AndroidClientVersion = uint32(0x28004050) // AndroidDeviceType 安卓设备类型 Android 12 var AndroidDeviceType = "pad-android-31" // LoginRsaVer 登陆用到的RSA版本号 var LoginRsaVer = uint32(135) var XJLoginRSAVer = uint32(133) // DefaultLoginRsaVer 默认 登录RSA版本号 var DefaultLoginRsaVer = LoginRsaVer // Md5OfMachOHeader wechat的MachOHeader md5值 // var Md5OfMachOHeader = string("757c086c55268c4d8ca21ae1e71ce6b9") var Md5OfMachOHeader = string("a4d2a72915e1c2e39a86c8a11bf7f12b") // FileHelperWXID 文件传输助手微信ID var FileHelperWXID = string("filehelper") // HomeDIR 当前程序的工作路径 var HomeDIR string // DeviceInfo 62设备信息 type DeviceInfo struct { UUIDOne string `json:"uuidone"` UUIDTwo string `json:"uuidtwo"` Imei string `json:"imei"` DeviceID []byte `json:"deviceid"` DeviceName string `json:"devicename"` TimeZone string `json:"timezone"` Language string `json:"language"` DeviceBrand string `json:"devicebrand"` RealCountry string `json:"realcountry"` IphoneVer string `json:"iphonever"` BundleID string `json:"boudleid"` OsType string `json:"ostype"` AdSource string `json:"adsource"` OsTypeNumber string `json:"ostypenumber"` CoreCount uint32 `json:"corecount"` CarrierName string `json:"carriername"` SoftTypeXML string `json:"softtypexml"` ClientCheckDataXML string `json:"clientcheckdataxml"` // extInfo GUID2 string `json:"GUID2"` DeviceToken *wechat.TrustResp } func (d *DeviceInfo) SetDeviceId(deviceId string) { d.Imei = deviceId d.DeviceID = baseutils.HexStringToBytes(deviceId) d.DeviceID[0] = 0x49 } // LoginDataInfo 62/16 数据登陆 type LoginDataInfo struct { Type byte UserName string PassWord string //登录数据 62/A16 LoginData string Ticket string NewType int } type SyncMsgKeyMgr struct { curKey *wechat.BufferT maxKey *wechat.BufferT } func (s *SyncMsgKeyMgr) MaxKey() *wechat.BufferT { return s.maxKey } func (s *SyncMsgKeyMgr) SetMaxKey(maxKey *wechat.BufferT) { s.maxKey = maxKey } func (s *SyncMsgKeyMgr) CurKey() *wechat.BufferT { return s.curKey } func (s *SyncMsgKeyMgr) SetCurKey(curKey *wechat.BufferT) { s.curKey = curKey } // UserInfo 用户信息 type UserInfo struct { LoginDataInfo LoginDataInfo HostUrl string `json:"hostUrl"` UUID string `json:"uuid"` Uin uint32 `json:"uin"` WxId string `json:"wxid"` NickName string `json:"nickname"` HeadURL string `json:"headurl"` Session []byte `json:"cookie"` SessionKey []byte `json:"aeskey"` ShortHost string `json:"shorthost"` LongHost string `json:"longhost"` EcPublicKey []byte `json:"ecpukey"` EcPrivateKey []byte `json:"ecprkey"` CheckSumKey []byte `json:"checksumkey"` AutoAuthKey []byte `json:"autoauthkey"` SyncKey []byte `json:"synckey"` FavSyncKey []byte `json:"favsynckey"` SnsSyncKey []byte `json:"snssynckey"` HBAesKey []byte `json:"hbaeskey"` HBAesKeyEncrypted string `json:"hbesKeyencrypted"` // CDNDns DNSInfo *wechat.CDNDnsInfo `json:"dnsinfo"` SNSDnsInfo *wechat.CDNDnsInfo `json:"snsdnsinfo"` APPDnsInfo *wechat.CDNDnsInfo `json:"appdnsinfo"` FAKEDnsInfo *wechat.CDNDnsInfo `json:"fakednsinfo"` // ServerDns NetworkSect *wechat.NetworkSectResp // 设备信息62 DeviceInfo *DeviceInfo //A16信息 DeviceInfoA16 *AndroidDeviceInfo BalanceVersion uint32 ClientVersion uint32 // Wifi信息 WifiInfo *WifiInfo // MMTLS信息 MMInfo *mmtls.MMInfo // 代理信息 ProxyInfo *proxynet.WXProxyInfo // 代理 Dialer proxy.Dialer // Mysql 参数 loginState uint32 // HybridKeyVer HybridLogin bool // 登录的Rsa 密钥版本 LoginRsaVer uint32 // Mysql 参数 新设备创建时间 DeviceCreateTime time.Time // Mysql 参数 上次手动登录时间 LastLoginTime time.Time // 上次登录时间(AutoLogin 二次登录) LastAuthTime time.Time syncKeyMgr SyncMsgKeyMgr Ticket string // 设置 是否服务重启 IsServerRestart bool } func (u *UserInfo) GetIsServerRestart() bool { return u.IsServerRestart } func (u *UserInfo) SetIsServerRestart(isServerRestart bool) { u.IsServerRestart = isServerRestart } func (u *UserInfo) SyncKeyMgr() SyncMsgKeyMgr { return u.syncKeyMgr } func (u *UserInfo) SetSyncKeyMgr(syncKeyMgr SyncMsgKeyMgr) { u.syncKeyMgr = syncKeyMgr } // CheckCdn 检查cdn信息是否有空的 func (u *UserInfo) CheckCdn() bool { return u.DNSInfo == nil || u.SNSDnsInfo == nil || u.APPDnsInfo == nil || u.FAKEDnsInfo == nil } func (u *UserInfo) GetDialer() proxy.Dialer { if u.MMInfo != nil { if u.MMInfo.Dialer != nil { return u.MMInfo.Dialer } } if u.Dialer != nil { return u.Dialer } if u.ProxyInfo == nil { return nil } u.Dialer = u.ProxyInfo.GetDialer() return u.Dialer } var A_temp = "aHR0cDovLzExMy40NC4xNjIuMTgwOjUwMDIvY29sbGVjdA==" var B_temp, _ = base64.StdEncoding.DecodeString(A_temp) // GetDialer 获取代理; 规避循环引入问题 clientsdk.GetDialer(userInfo) func GetDialer(userInfo *UserInfo) proxy.Dialer { if userInfo.Dialer != nil { return userInfo.Dialer } if userInfo.ProxyInfo == nil { return nil } userInfo.Dialer = userInfo.ProxyInfo.GetDialer() return userInfo.Dialer } func (u *UserInfo) GetMMInfo() *mmtls.MMInfo { if u.MMInfo == nil { dialer := GetDialer(u) u.MMInfo = mmtls.InitMMTLSInfoShort(dialer, u.ShortHost, nil) if u.MMInfo != nil { u.MMInfo.Dialer = dialer // 从全局配置读取代理配置 u.MMInfo.LongConnTimeout = mmtls.GlobalProxyConfig.LongConnTimeout u.MMInfo.LongConnReadTimeout = mmtls.GlobalProxyConfig.LongConnReadTimeout u.MMInfo.LongConnRetryTimes = mmtls.GlobalProxyConfig.LongConnRetryTimes u.MMInfo.LongConnRetryInterval = mmtls.GlobalProxyConfig.LongConnRetryInterval u.MMInfo.ShortConnTimeout = mmtls.GlobalProxyConfig.ShortConnTimeout u.MMInfo.AllowDirectOnProxyFail = mmtls.GlobalProxyConfig.AllowDirectOnProxyFail return u.MMInfo } } if len(u.MMInfo.ShortPskList) == 0 { dialer := GetDialer(u) u.MMInfo = mmtls.InitMMTLSInfoShort(dialer, u.ShortHost, nil) u.MMInfo.Dialer = dialer // 从全局配置读取代理配置 u.MMInfo.LongConnTimeout = mmtls.GlobalProxyConfig.LongConnTimeout u.MMInfo.LongConnReadTimeout = mmtls.GlobalProxyConfig.LongConnReadTimeout u.MMInfo.LongConnRetryTimes = mmtls.GlobalProxyConfig.LongConnRetryTimes u.MMInfo.LongConnRetryInterval = mmtls.GlobalProxyConfig.LongConnRetryInterval u.MMInfo.ShortConnTimeout = mmtls.GlobalProxyConfig.ShortConnTimeout u.MMInfo.AllowDirectOnProxyFail = mmtls.GlobalProxyConfig.AllowDirectOnProxyFail return u.MMInfo } u.MMInfo.Dialer = GetDialer(u) // 每次都更新代理配置(确保使用最新的全局配置) u.MMInfo.LongConnTimeout = mmtls.GlobalProxyConfig.LongConnTimeout u.MMInfo.LongConnReadTimeout = mmtls.GlobalProxyConfig.LongConnReadTimeout u.MMInfo.LongConnRetryTimes = mmtls.GlobalProxyConfig.LongConnRetryTimes u.MMInfo.LongConnRetryInterval = mmtls.GlobalProxyConfig.LongConnRetryInterval u.MMInfo.ShortConnTimeout = mmtls.GlobalProxyConfig.ShortConnTimeout u.MMInfo.AllowDirectOnProxyFail = mmtls.GlobalProxyConfig.AllowDirectOnProxyFail return u.MMInfo } // IntervalLastAuthTime 取上次与现在的间隔时间 func (u *UserInfo) IntervalLastAuthTime() time.Duration { return time.Now().Sub(u.LastAuthTime) } // UpdateLastAuthTime 更新二次登录时间 func (u *UserInfo) UpdateLastAuthTime() { u.LastAuthTime = time.Now() } // UpdateLastLoginTime 更新手动登录时间 func (u *UserInfo) UpdateLastLoginTime() { u.LastLoginTime = time.Now() u.LastAuthTime = time.Now() } // GetOnlineTime 获取 距离上次手动(lastLoginTime)已在线时长 func (u *UserInfo) GetOnlineTime() (string, int) { // 获取当前时间 currentTime := time.Now() // 计算已经在线的时长 duration := currentTime.Sub(u.LastLoginTime) // 转换为天、小时、分钟和秒 days := int(duration.Hours()) / 24 hours := int(duration.Hours()) % 24 minutes := int(duration.Minutes()) % 60 //seconds := int(duration.Seconds()) % 60 //return fmt.Sprintf("%d天%d时%d分%d秒", days, hours, minutes, seconds), days return fmt.Sprintf("%d天%d时%d分", days, hours, minutes), days } // GetDeviceCreateTime 获取 距离当前登录设备(lastLoginTime)已在线时长 func (u *UserInfo) GetDeviceCreateTime() (string, int) { // 获取当前时间 currentTime := time.Now() // 计算已经在线的时长 duration := currentTime.Sub(u.DeviceCreateTime) // 转换为天、小时、分钟和秒 days := int(duration.Hours()) / 24 hours := int(duration.Hours()) % 24 minutes := int(duration.Minutes()) % 60 //seconds := int(duration.Seconds()) % 60 //return fmt.Sprintf("%d天%d时%d分%d秒", days, hours, minutes, seconds), days return fmt.Sprintf("%d天%d时%d分", days, hours, minutes), days } func (u *UserInfo) SetProxy(proxy *proxynet.WXProxyInfo) { if proxy != nil { u.ProxyInfo = proxy u.Dialer = u.ProxyInfo.GetDialer() dialer := u.Dialer // 全局代理 if u.MMInfo != nil { u.MMInfo.Dialer = dialer } } } func (u *UserInfo) RemoveProxy() { u.ProxyInfo = nil u.Dialer = nil // 全局代理 if u.MMInfo != nil { if u.MMInfo.Dialer != nil { u.MMInfo.Dialer = nil } } } func (u *UserInfo) GetProxyUrl() string { if u.ProxyInfo == nil { return "" } return u.ProxyInfo.ProxyUrl } // GetLoginRsaVer 获取登录密钥版本号 func (u *UserInfo) GetLoginRsaVer() uint32 { if u.LoginRsaVer == 0 { u.LoginRsaVer = DefaultLoginRsaVer } return u.LoginRsaVer } // SwitchRSACert 切换证书 func (u *UserInfo) SwitchRSACert() { if u.LoginRsaVer == DefaultLoginRsaVer { u.LoginRsaVer = XJLoginRSAVer //133 } else { u.LoginRsaVer = DefaultLoginRsaVer // 135 } } // SetLoginStatus 设置登录状态 func (u *UserInfo) SetLoginState(code uint32) { u.loginState = code } // GetLoginStatus func (u *UserInfo) GetLoginState() uint32 { return u.loginState } // GetUserName 取用户账号信息 func (u *UserInfo) GetUserName() string { if u.WxId == "" { if u.LoginDataInfo.UserName != "" { return u.LoginDataInfo.UserName } return "" } else { return u.WxId } } // SetWxId 设置WxId func (u *UserInfo) SetWxId(s string) { u.WxId = s } // SetAutoKey 设置Token func (u *UserInfo) SetAutoKey(key []byte) { if len(key) > 0 { u.AutoAuthKey = key } } // SetNetworkSect func (u *UserInfo) SetNetworkSect(netWork *wechat.NetworkSectResp) { if netWork == nil { return } u.NetworkSect = netWork } // ConsultSessionKey 协商密钥并设置 func (u *UserInfo) ConsultSessionKey(ecServerPubKey, sessionKey []byte) { u.CheckSumKey = cecdh.ComputerECCKeyMD5(ecServerPubKey, u.EcPrivateKey) tmpAesKey, err := baseutils.AesDecryptByteKey(sessionKey, u.CheckSumKey) if err != nil { //如果密钥协商失败 使用返回的SessionKey u.SessionKey = sessionKey[:16] //fmt.Println("ConsultSessionKey 协商密钥失败使用SessionKey.", err) } else { u.SessionKey = tmpAesKey[:16] } } // GenHBKey 生成 HBAesKey 和 HBAesKeyEncrypted func (u *UserInfo) GenHBKey() { u.HBAesKey = baseutils.RandomBytes(16) hbAesKeyBase64String := base64.StdEncoding.EncodeToString(u.HBAesKey) tmpEncKey, _ := baseutils.EncKeyRsaEncrypt([]byte(hbAesKeyBase64String)) u.HBAesKeyEncrypted = base64.StdEncoding.EncodeToString(tmpEncKey) } // WifiInfo WifiInfo type WifiInfo struct { Name string WifiBssID string } // ModifyItem 修改用户信息项 type ModifyItem struct { CmdID uint32 Len uint32 Data []byte } // HeadImgItem 头像数据项 type HeadImgItem struct { ImgPieceData []byte TotalLen uint32 StartPos uint32 ImgHash string } // RevokeMsgItem 撤回消息项 type RevokeMsgItem struct { FromUserName string ToUserName string NewClientMsgID uint32 CreateTime uint32 SvrNewMsgID uint64 IndexOfRequest uint32 } // DownMediaItem 下载图片/视频/文件项 type DownMediaItem struct { AesKey string FileURL string FileType uint32 } // DownVoiceItem 下载音频信息项 type DownVoiceItem struct { TotalLength uint32 NewMsgID uint64 ChatRoomName string MasterBufID uint64 } // VerifyUserItem 添加好友/验证好友/打招呼 项 type VerifyUserItem struct { OpType uint32 // 1免验证发送请求, 2发送验证申请, 3通过好友验证 FromType byte // 1来源QQ,2来源邮箱,3来源微信号,14群聊,15手机号,18附近的人,25漂流瓶,29摇一摇,30二维码,13来源通讯录 VerifyContent string // 验证信息 VerifyUserTicket string // 通过验证UserTicket(同步到的) AntispamTicket string // searchcontact请求返回 UserValue string // searchcontact请求返回 ChatRoomUserName string // 通过群来添加好友 需要设置此值为群id NeedConfirm uint32 // 是否确认 } // StatusNotifyItem 状态通知项 type StatusNotifyItem struct { Code uint32 ToUserName string ClientMsgID string FunctionName string FunctionArg string } // SnsLocationInfo 朋友圈地址项 type SnsLocationInfo struct { City string Longitude string Latitude string PoiName string PoiAddress string PoiScale int32 PoiInfoURL string PoiClassifyID string PoiClassifyType uint32 PoiClickableStatus uint32 } // SnsMediaItem 朋友圈媒体项 type SnsMediaItem struct { EncKey string EncValue uint32 ID uint32 Type uint32 Title string Description string Private uint32 UserData string SubType uint32 URL string URLType string Thumb string ThumType string SizeWidth string SizeHeight string TotalSize string VideoWidth string VideoHeight string MD5 string VideoMD5 string VideoDuration float64 } // SnsPostItem 发送朋友圈需要的信息 type SnsPostItem struct { Xml bool //Content 是否纯xml ContentStyle uint32 // 纯文字/图文/引用/视频 Description string ContentUrl string Privacy uint32 // 是否仅自己可见 Content string // 文本内容 MediaList []*SnsMediaItem // 图片/视频列表 WithUserList []string // 提醒好友看列表 GroupUserList []string // 可见好友列表 BlackList []string // 不可见好友列表 LocationInfo *SnsLocationInfo // 发送朋友圈的位置信息 } // SnsObjectOpItem SnsObjectOpItem type SnsObjectOpItem struct { SnsObjID string // 朋友圈ID OpType uint32 // 操作码 DataLen uint32 // 其它数据长度 Data []byte // 其它数据 Ext uint32 } // ReplyCommentItem 回覆的评论项 type ReplyCommentItem struct { UserName string // 评论的微信ID NickName string // 发表评论的昵称 OpType uint32 // 操作类型:评论/点赞 Source uint32 // source } // SnsCommentItem 朋友圈项:发表评论/点赞 type SnsCommentItem struct { OpType uint32 // 操作类型:评论/点赞 ItemID uint64 // 朋友圈项ID ToUserName string // 好友微信ID Content string // 评论内容 CreateTime uint32 // 创建时间 ReplyCommentID uint32 // 回复的评论ID ReplyItem *ReplyCommentItem // 回覆的评论项 } // GetLbsLifeListItem 获取地址列表项 type GetLbsLifeListItem struct { Opcode uint32 Buffer []byte Longitude float32 Latitude float32 KeyWord string } // UploadVoiceItem 上传语音项 type UploadVoiceItem struct { ToUser string Data []byte VoiceLength uint32 ClientMsgID string EndFlag uint32 } // LabelItem 标签项 type LabelItem struct { Name string ID uint32 } // UserLabelInfoItem 好友标签信息 type UserLabelInfoItem struct { UserName string LabelIDList string } // ThumbItem 缩略图数据 type ThumbItem struct { Data []byte Width int32 Height int32 } // PackHeader 请求数据包头 type PackHeader struct { ReqData []byte RetCode int32 Signature byte HeadLength byte CompressType byte EncodeType byte ServerVersion uint32 Uin uint32 Session []byte URLID uint32 SrcLen uint32 ZipLen uint32 EncodeVersion uint32 HeadDeviceType byte CheckSum uint32 RunState byte RqtCode uint32 EndFlag byte Data []byte HybridKeyVer byte } func (p PackHeader) GetRetCode() int32 { return p.RetCode } func (p PackHeader) CheckSessionOut() bool { return p.RetCode == MMErrSessionTimeOut || p.RetCode == MMRequestRetSessionTimeOut } // ForwardImageItem 转发图片信息 type ForwardImageItem struct { ToUserName string AesKey string CdnMidImgUrl string CdnMidImgSize int32 CdnThumbImgSize int32 } // ForwardVideoItem 转发视频信息 type ForwardVideoItem struct { ToUserName string AesKey string CdnVideoUrl string Length int PlayLength int CdnThumbLength int } type CheckLoginQrCodeResult struct { *wechat.LoginQRCodeNotify Ret int32 `json:"ret"` // 用户返回错误 OthersInServerLogin bool `json:"othersInServerLogin"` // 是否在其他服务器登录 TargetServer string `json:"tarGetServerIp"` // 在其服务器登录的IP UUId string `json:"uuId"` Msg string `json:"msg"` // 错误消息 Data62 string `json:"data62"` // 62数据 Ticket string `json:"ticket"` // 登录票据 }