From 18d08d870c027a287bdc760b16bc0a17375383c1 Mon Sep 17 00:00:00 2001 From: Logiase Date: Thu, 1 Oct 2020 20:11:46 +0800 Subject: [PATCH 1/3] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0e061367..e218e95d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ qq-android协议的golang实现 移植于mirai 建议基于 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 使用框架开发。 +同时提供不基于 CQHTTP 的原生框架 [MiraiGo-Template](https://github.com/Logiase/MiraiGo-Template)进行开发。 + # 已完成功能/开发计划 #### 登录 - [x] 账号密码登录 From d83f26daeed0eae030dab3460aab049efcac33d4 Mon Sep 17 00:00:00 2001 From: Mrs4s <1844812067@qq.com> Date: Thu, 1 Oct 2020 20:24:48 +0800 Subject: [PATCH 2/3] supported skey refresh. thanks @wfjsw --- README.md | 2 +- client/builders.go | 52 ++++++++++++++++++++++++++++++++++++++++-- client/client.go | 6 +++++ client/decoders.go | 17 ++++++++++++++ client/tlv_decoders.go | 18 ++++++++++++++- protocol/tlv/t100.go | 6 ++--- protocol/tlv/t106.go | 4 ++-- 7 files changed, 96 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e218e95d..a3b22c69 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ qq-android协议的golang实现 移植于mirai 建议基于 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 使用框架开发。 -同时提供不基于 CQHTTP 的原生框架 [MiraiGo-Template](https://github.com/Logiase/MiraiGo-Template)进行开发。 +同时提供不基于 CQHTTP 的原生框架 [MiraiGo-Template] (https://github.com/Logiase/MiraiGo-Template)进行开发。 # 已完成功能/开发计划 #### 登录 diff --git a/client/builders.go b/client/builders.go index b7bd6a74..fd10ca02 100644 --- a/client/builders.go +++ b/client/builders.go @@ -40,9 +40,9 @@ func (c *QQClient) buildLoginPacket() (uint16, []byte) { w.Write(tlv.T18(16, uint32(c.Uin))) w.Write(tlv.T1(uint32(c.Uin), SystemDeviceInfo.IpAddress)) - w.Write(tlv.T106(uint32(c.Uin), 0, uint32(SystemDeviceInfo.Protocol), c.PasswordMd5, true, SystemDeviceInfo.Guid, SystemDeviceInfo.TgtgtKey)) + w.Write(tlv.T106(uint32(c.Uin), 0, uint32(SystemDeviceInfo.Protocol), c.PasswordMd5, true, SystemDeviceInfo.Guid, SystemDeviceInfo.TgtgtKey, 0)) w.Write(tlv.T116(184024956, 0x10400)) - w.Write(tlv.T100(uint32(SystemDeviceInfo.Protocol))) + w.Write(tlv.T100(uint32(SystemDeviceInfo.Protocol), 34869472)) w.Write(tlv.T107(0)) w.Write(tlv.T142("com.tencent.mobileqq")) w.Write(tlv.T144( @@ -127,6 +127,54 @@ func (c *QQClient) buildCaptchaPacket(result string, sign []byte) (uint16, []byt return seq, packet } +func (c *QQClient) buildRequestTgtgtNopicsigPacket() (uint16, []byte) { + seq := c.nextSeq() + req := packets.BuildOicqRequestPacket(c.Uin, 0x0810, crypto.NewEncryptSession(c.sigInfo.t133), c.sigInfo.wtSessionTicketKey, func(w *binary.Writer) { + w.WriteUInt16(15) + w.WriteUInt16(21) + + w.Write(tlv.T18(16, uint32(c.Uin))) + w.Write(tlv.T1(uint32(c.Uin), SystemDeviceInfo.IpAddress)) + w.Write(tlv.T106(uint32(c.Uin), 0, uint32(SystemDeviceInfo.Protocol), c.PasswordMd5, true, SystemDeviceInfo.Guid, SystemDeviceInfo.TgtgtKey, 1)) + w.Write(tlv.T116(150470524, 66560)) + w.Write(tlv.T100(2, 34869472)) + w.Write(tlv.T107(0)) + w.Write(tlv.T144( + SystemDeviceInfo.AndroidId, + SystemDeviceInfo.GenDeviceInfoData(), + SystemDeviceInfo.OSType, + SystemDeviceInfo.Version.Release, + SystemDeviceInfo.SimInfo, + SystemDeviceInfo.APN, + false, true, false, tlv.GuidFlag(), + SystemDeviceInfo.Model, + SystemDeviceInfo.Guid, + SystemDeviceInfo.Brand, + SystemDeviceInfo.TgtgtKey, + )) + w.Write(tlv.T142("com.tencent.mobileqq")) + w.Write(tlv.T145(SystemDeviceInfo.Guid)) + w.Write(tlv.T16A(c.sigInfo.srmToken)) + w.Write(tlv.T154(seq)) + w.Write(tlv.T141(SystemDeviceInfo.SimInfo, SystemDeviceInfo.APN)) + 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", + })) + w.Write(tlv.T147(16, []byte("8.2.7"), []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D})) + w.Write(tlv.T177()) + w.Write(tlv.T187(SystemDeviceInfo.MacAddress)) + w.Write(tlv.T188(SystemDeviceInfo.AndroidId)) + w.Write(tlv.T194(SystemDeviceInfo.IMSIMd5)) + w.Write(tlv.T202(SystemDeviceInfo.WifiBSSID, SystemDeviceInfo.WifiSSID)) + w.Write(tlv.T516()) + }) + packet := packets.BuildUniPacket(c.Uin, seq, "wtlogin.exchange_emp", 2, c.OutGoingPacketSessionId, []byte{}, make([]byte, 16), req) + return seq, packet +} + // StatSvc.register func (c *QQClient) buildClientRegisterPacket() (uint16, []byte) { seq := c.nextSeq() diff --git a/client/client.go b/client/client.go index cd52577e..6065f786 100644 --- a/client/client.go +++ b/client/client.go @@ -94,6 +94,7 @@ type loginSigInfo struct { userStKey []byte userStWebSig []byte sKey []byte + sKeyExpiredTime int64 d2 []byte d2Key []byte wtSessionTicketKey []byte @@ -121,6 +122,7 @@ func NewClientMd5(uin int64, passwordMd5 [16]byte) *QQClient { OutGoingPacketSessionId: []byte{0x02, 0xB0, 0x5B, 0x8B}, decoders: map[string]func(*QQClient, uint16, []byte) (interface{}, error){ "wtlogin.login": decodeLoginResponse, + "wtlogin.exchange_emp": decodeExchangeEmpResponse, "StatSvc.register": decodeClientRegisterResponse, "StatSvc.ReqMSFOffline": decodeMSFOfflinePacket, "StatSvc.GetDevLoginInfo": decodeDevListResponse, @@ -849,6 +851,10 @@ func (c *QQClient) SolveFriendRequest(req *NewFriendRequest, accept bool) { } func (c *QQClient) getCookies() string { + if c.sigInfo.sKeyExpiredTime < time.Now().Unix() { + c.Debug("skey expired. refresh...") + _, _ = c.sendAndWait(c.buildRequestTgtgtNopicsigPacket()) + } return fmt.Sprintf("uin=o%d; skey=%s;", c.Uin, c.sigInfo.sKey) } diff --git a/client/decoders.go b/client/decoders.go index da298be8..cece7b27 100644 --- a/client/decoders.go +++ b/client/decoders.go @@ -127,6 +127,23 @@ func decodeClientRegisterResponse(_ *QQClient, _ uint16, payload []byte) (interf return nil, nil } +// wtlogin.exchange_emp +func decodeExchangeEmpResponse(c *QQClient, _ uint16, payload []byte) (interface{}, error) { + reader := binary.NewReader(payload) + cmd := reader.ReadUInt16() + t := reader.ReadByte() + reader.ReadUInt16() + m := reader.ReadTlvMap(2) + if t != 0 { + c.Error("exchange_emp error: %v", t) + return nil, nil + } + if cmd == 15 { // TODO: 免密登录 + c.decodeT119(m[0x119]) + } + return nil, nil +} + // ConfigPushSvc.PushReq func decodePushReqPacket(c *QQClient, _ uint16, payload []byte) (interface{}, error) { request := &jce.RequestPacket{} diff --git a/client/tlv_decoders.go b/client/tlv_decoders.go index dea1858f..bf548e1d 100644 --- a/client/tlv_decoders.go +++ b/client/tlv_decoders.go @@ -23,7 +23,18 @@ func (c *QQClient) decodeT119(data []byte) { reader := binary.NewReader(tea.Decrypt(data)) reader.ReadBytes(2) m := reader.ReadTlvMap(2) - + if len(c.sigInfo.sKey) > 0 { // refresh + if t120, ok := m[0x120]; ok { + c.sigInfo.sKey = t120 + c.sigInfo.sKeyExpiredTime = time.Now().Unix() + 43200 // 86400 / 2 + c.Debug("skey updated: %v", c.sigInfo.sKey) + } + if t11a, ok := m[0x11a]; ok { + c.Nickname, c.Age, c.Gender = readT11A(t11a) + c.Debug("account info updated: " + c.Nickname) + } + return + } if t130, ok := m[0x130]; ok { c.decodeT130(t130) } @@ -79,6 +90,10 @@ func (c *QQClient) decodeT119(data []byte) { //a1, noPicSig = readT531(t531) } + if _, ok := m[0x138]; ok { + //readT138(t138) // chg time + } + c.sigInfo = &loginSigInfo{ loginBitmap: 0, srmToken: m[0x16a], @@ -88,6 +103,7 @@ func (c *QQClient) decodeT119(data []byte) { userStKey: m[0x10e], userStWebSig: m[0x103], sKey: m[0x120], + sKeyExpiredTime: time.Now().Unix() + 43200, // 86400 / 2 d2: m[0x143], d2Key: m[0x305], wtSessionTicketKey: m[0x134], diff --git a/protocol/tlv/t100.go b/protocol/tlv/t100.go index 62091119..eaca527c 100644 --- a/protocol/tlv/t100.go +++ b/protocol/tlv/t100.go @@ -4,7 +4,7 @@ import ( "github.com/Mrs4s/MiraiGo/binary" ) -func T100(protocol uint32) []byte { +func T100(protocol, mainSigMap uint32) []byte { return binary.NewWriterF(func(w *binary.Writer) { w.WriteUInt16(0x100) w.WriteTlv(binary.NewWriterF(func(w *binary.Writer) { @@ -12,8 +12,8 @@ func T100(protocol uint32) []byte { w.WriteUInt32(5) w.WriteUInt32(16) w.WriteUInt32(protocol) - w.WriteUInt32(0) // App client version - w.WriteUInt32(34869472) + w.WriteUInt32(0) // App client version + w.WriteUInt32(mainSigMap) // 34869472 })) }) } diff --git a/protocol/tlv/t106.go b/protocol/tlv/t106.go index 2e09ef74..1407224e 100644 --- a/protocol/tlv/t106.go +++ b/protocol/tlv/t106.go @@ -8,7 +8,7 @@ import ( "time" ) -func T106(uin, salt, protocol uint32, passwordMd5 [16]byte, guidAvailable bool, guid, tgtgtKey []byte) []byte { +func T106(uin, salt, protocol uint32, passwordMd5 [16]byte, guidAvailable bool, guid, tgtgtKey []byte, wtf uint32) []byte { return binary.NewWriterF(func(w *binary.Writer) { w.WriteUInt16(0x106) body := binary.NewWriterF(func(w *binary.Writer) { @@ -27,7 +27,7 @@ func T106(uin, salt, protocol uint32, passwordMd5 [16]byte, guidAvailable bool, w.WriteByte(0x01) w.Write(passwordMd5[:]) w.Write(tgtgtKey) - w.WriteUInt32(0) + w.WriteUInt32(wtf) w.WriteBool(guidAvailable) if len(guid) == 0 { for i := 0; i < 4; i++ { From 0198f71ff8a671d5d02b6d568642e6a4c961ee46 Mon Sep 17 00:00:00 2001 From: Mrs4s <1844812067@qq.com> Date: Thu, 1 Oct 2020 20:26:17 +0800 Subject: [PATCH 3/3] fix typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3b22c69..d1638b8e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ qq-android协议的golang实现 移植于mirai 建议基于 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 使用框架开发。 -同时提供不基于 CQHTTP 的原生框架 [MiraiGo-Template] (https://github.com/Logiase/MiraiGo-Template)进行开发。 +同时提供不基于 CQHTTP 的原生框架 [MiraiGo-Template](https://github.com/Logiase/MiraiGo-Template) 进行开发。 # 已完成功能/开发计划 #### 登录