1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-04 19:17:38 +08:00

Merge branch 'Mrs4s:master' into master

This commit is contained in:
A lucky guy 2023-03-12 21:22:37 +08:00 committed by GitHub
commit a277c77cb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 533 additions and 176 deletions

View File

@ -8,7 +8,6 @@ import (
"github.com/Mrs4s/MiraiGo/binary" "github.com/Mrs4s/MiraiGo/binary"
"github.com/Mrs4s/MiraiGo/binary/jce" "github.com/Mrs4s/MiraiGo/binary/jce"
"github.com/Mrs4s/MiraiGo/client/internal/auth"
"github.com/Mrs4s/MiraiGo/client/internal/network" "github.com/Mrs4s/MiraiGo/client/internal/network"
"github.com/Mrs4s/MiraiGo/client/internal/oicq" "github.com/Mrs4s/MiraiGo/client/internal/oicq"
"github.com/Mrs4s/MiraiGo/client/pb" "github.com/Mrs4s/MiraiGo/client/pb"
@ -19,6 +18,7 @@ import (
"github.com/Mrs4s/MiraiGo/client/pb/structmsg" "github.com/Mrs4s/MiraiGo/client/pb/structmsg"
"github.com/Mrs4s/MiraiGo/internal/proto" "github.com/Mrs4s/MiraiGo/internal/proto"
"github.com/Mrs4s/MiraiGo/internal/tlv" "github.com/Mrs4s/MiraiGo/internal/tlv"
"github.com/Mrs4s/MiraiGo/wrapper"
) )
var ( var (
@ -102,6 +102,25 @@ func (c *QQClient) buildLoginPacket() (uint16, []byte) {
tlv.T521(0), tlv.T521(0),
tlv.T525(tlv.T536([]byte{0x01, 0x00})), tlv.T525(tlv.T536([]byte{0x01, 0x00})),
) )
if wrapper.DandelionEnergy != nil {
salt := binary.NewWriterF(func(w *binary.Writer) {
// util.int64_to_buf(bArr42, 0, (int) uin2);
// util.int16_to_buf(bArr42, 4, u.guid.length); // 故意的还是不小心的
w.Write(binary.NewWriterF(func(w *binary.Writer) { w.WriteUInt64(uint64(c.Uin)) })[:4])
w.WriteBytesShort(c.Device().Guid)
w.WriteBytesShort([]byte(c.version().SdkVersion))
w.WriteUInt32(9) // sub command
w.WriteUInt32(0) // 被演了
})
if t544 := tlv.T544Custom(uint64(c.Uin), "810_9", salt, wrapper.DandelionEnergy); t544 != nil {
t.Append(t544)
}
}
if c.Device().QImei36 != "" {
t.Append(tlv.T545([]byte(c.Device().QImei16)))
} else {
t.Append(tlv.T545([]byte(c.Device().IMEI)))
}
req := c.buildOicqRequestPacket(c.Uin, 0x0810, t) req := c.buildOicqRequestPacket(c.Uin, 0x0810, t)
r := network.Request{ r := network.Request{
Type: network.RequestTypeLogin, Type: network.RequestTypeLogin,
@ -137,17 +156,15 @@ func (c *QQClient) buildDeviceLockLoginPacket() (uint16, []byte) {
} }
func (c *QQClient) buildQRCodeFetchRequestPacket(size, margin, ecLevel uint32) (uint16, []byte) { func (c *QQClient) buildQRCodeFetchRequestPacket(size, margin, ecLevel uint32) (uint16, []byte) {
old := c.version() // old := c.version()
watch := auth.AndroidWatch.Version() // watch := auth.AndroidWatch.Version()
c.transport.Version = watch // c.transport.Version = watch
seq := c.nextSeq() seq := c.nextSeq()
req := oicq.Message{ req := oicq.Message{
Command: 0x0812, Command: 0x0812,
EncryptionMethod: oicq.EM_ECDH, EncryptionMethod: oicq.EM_ECDH,
Body: binary.NewWriterF(func(w *binary.Writer) { Body: binary.NewWriterF(func(w *binary.Writer) {
w.WriteHex(`0001110000001000000072000000`) // trans header code2dPacket := buildCode2DRequestPacket(0, 0, 0x31, func(w *binary.Writer) {
w.WriteUInt32(uint32(time.Now().Unix()))
w.Write(buildCode2DRequestPacket(0, 0, 0x31, func(w *binary.Writer) {
w.WriteUInt16(0) // const w.WriteUInt16(0) // const
w.WriteUInt32(16) // app id w.WriteUInt32(16) // app id
w.WriteUInt64(0) // const w.WriteUInt64(0) // const
@ -155,13 +172,20 @@ func (c *QQClient) buildQRCodeFetchRequestPacket(size, margin, ecLevel uint32) (
w.WriteBytesShort(EmptyBytes) w.WriteBytesShort(EmptyBytes)
w.WriteUInt16(6) w.WriteUInt16(6)
w.Write(tlv.T16(watch.SSOVersion, 16, watch.AppId, c.Device().Guid, []byte(watch.ApkId), []byte(watch.SortVersionName), watch.ApkSign)) w.Write(tlv.T16(c.transport.Version.SSOVersion, 16, c.transport.Version.AppId, c.Device().Guid, []byte(c.transport.Version.ApkId), []byte(c.transport.Version.SortVersionName), c.transport.Version.ApkSign))
w.Write(tlv.T1B(0, 0, size, margin, 72, ecLevel, 2)) w.Write(tlv.T1B(0, 0, size, margin, 72, ecLevel, 2))
w.Write(tlv.T1D(watch.MiscBitmap)) w.Write(tlv.T1D(c.transport.Version.MiscBitmap))
w.Write(tlv.T1F(false, c.Device().OSType, []byte("7.1.2"), []byte("China Mobile GSM"), c.Device().APN, 2)) w.Write(tlv.T1F(false, c.Device().OSType, []byte("7.1.2"), []byte("China Mobile GSM"), c.Device().APN, 2))
w.Write(tlv.T33(c.Device().Guid)) w.Write(tlv.T33(c.Device().Guid))
w.Write(tlv.T35(8)) w.Write(tlv.T35(8))
})) })
w.WriteByte(0x0)
w.WriteUInt16(uint16(len(code2dPacket)) + 4)
w.WriteUInt32(16)
w.WriteUInt32(0x72)
w.WriteHex("000000")
w.WriteUInt32(uint32(time.Now().Unix()))
w.Write(code2dPacket)
}), }),
} }
r := network.Request{ r := network.Request{
@ -173,21 +197,19 @@ func (c *QQClient) buildQRCodeFetchRequestPacket(size, margin, ecLevel uint32) (
Body: c.oicq.Marshal(&req), Body: c.oicq.Marshal(&req),
} }
payload := c.transport.PackPacket(&r) payload := c.transport.PackPacket(&r)
c.transport.Version = old // c.transport.Version = old
return seq, payload return seq, payload
} }
func (c *QQClient) buildQRCodeResultQueryRequestPacket(sig []byte) (uint16, []byte) { func (c *QQClient) buildQRCodeResultQueryRequestPacket(sig []byte) (uint16, []byte) {
old := c.version() // old := c.version()
c.transport.Version = auth.AndroidWatch.Version() // c.transport.Version = auth.AndroidWatch.Version()
seq := c.nextSeq() seq := c.nextSeq()
req := oicq.Message{ req := oicq.Message{
Command: 0x0812, Command: 0x0812,
EncryptionMethod: oicq.EM_ECDH, EncryptionMethod: oicq.EM_ECDH,
Body: binary.NewWriterF(func(w *binary.Writer) { Body: binary.NewWriterF(func(w *binary.Writer) {
w.WriteHex(`0000620000001000000072000000`) // trans header code2dPacket := buildCode2DRequestPacket(1, 0, 0x12, func(w *binary.Writer) {
w.WriteUInt32(uint32(time.Now().Unix()))
w.Write(buildCode2DRequestPacket(1, 0, 0x12, func(w *binary.Writer) {
w.WriteUInt16(5) // const w.WriteUInt16(5) // const
w.WriteByte(1) // const w.WriteByte(1) // const
w.WriteUInt32(8) // product type w.WriteUInt32(8) // product type
@ -197,7 +219,14 @@ func (c *QQClient) buildQRCodeResultQueryRequestPacket(sig []byte) (uint16, []by
w.WriteByte(8) // const w.WriteByte(8) // const
w.WriteBytesShort(EmptyBytes) w.WriteBytesShort(EmptyBytes)
w.WriteUInt16(0) // const w.WriteUInt16(0) // const
})) })
w.WriteByte(0x0)
w.WriteUInt16(uint16(len(code2dPacket)) + 4)
w.WriteUInt32(16)
w.WriteUInt32(0x72)
w.WriteHex("000000")
w.WriteUInt32(uint32(time.Now().Unix()))
w.Write(code2dPacket)
}), }),
} }
r := network.Request{ r := network.Request{
@ -209,7 +238,7 @@ func (c *QQClient) buildQRCodeResultQueryRequestPacket(sig []byte) (uint16, []by
Body: c.oicq.Marshal(&req), Body: c.oicq.Marshal(&req),
} }
payload := c.transport.PackPacket(&r) payload := c.transport.PackPacket(&r)
c.transport.Version = old // c.transport.Version = old
return seq, payload return seq, payload
} }
@ -326,7 +355,7 @@ func (c *QQClient) buildSMSRequestPacket() (uint16, []byte) {
func (c *QQClient) buildSMSCodeSubmitPacket(code string) (uint16, []byte) { func (c *QQClient) buildSMSCodeSubmitPacket(code string) (uint16, []byte) {
seq := c.nextSeq() seq := c.nextSeq()
req := c.buildOicqRequestPacket(c.Uin, 0x0810, &oicq.TLV{ t := &oicq.TLV{
Command: 7, Command: 7,
List: [][]byte{ List: [][]byte{
tlv.T8(2052), tlv.T8(2052),
@ -337,7 +366,13 @@ func (c *QQClient) buildSMSCodeSubmitPacket(code string) (uint16, []byte) {
tlv.T401(c.sig.G), tlv.T401(c.sig.G),
tlv.T198(), tlv.T198(),
}, },
}) }
if wrapper.DandelionEnergy != nil {
if t544 := tlv.T544(uint64(c.Uin), "810_7", 7, c.version().SdkVersion, c.Device().Guid, wrapper.DandelionEnergy); t544 != nil {
t.Append(t544)
}
}
req := c.buildOicqRequestPacket(c.Uin, 0x0810, t)
r := network.Request{ r := network.Request{
Type: network.RequestTypeLogin, Type: network.RequestTypeLogin,
EncryptType: network.EncryptTypeEmptyKey, EncryptType: network.EncryptTypeEmptyKey,
@ -363,6 +398,11 @@ func (c *QQClient) buildTicketSubmitPacket(ticket string) (uint16, []byte) {
if c.sig.T547 != nil { if c.sig.T547 != nil {
t.Append(tlv.T(0x547, c.sig.T547)) t.Append(tlv.T(0x547, c.sig.T547))
} }
if wrapper.DandelionEnergy != nil {
if t544 := tlv.T544(uint64(c.Uin), "810_2", 2, c.version().SdkVersion, c.Device().Guid, wrapper.DandelionEnergy); t544 != nil {
t.Append(t544)
}
}
req := c.buildOicqRequestPacket(c.Uin, 0x0810, t) req := c.buildOicqRequestPacket(c.Uin, 0x0810, t)
r := network.Request{ r := network.Request{
Type: network.RequestTypeLogin, Type: network.RequestTypeLogin,
@ -421,16 +461,20 @@ func (c *QQClient) buildRequestTgtgtNopicsigPacket() (uint16, []byte) {
tlv.T516(), tlv.T516(),
tlv.T521(0), tlv.T521(0),
tlv.T525(tlv.T536([]byte{0x01, 0x00})), tlv.T525(tlv.T536([]byte{0x01, 0x00})),
tlv.T545([]byte(c.Device().IMEI)),
}, },
} }
if c.Device().QImei36 != "" {
t.Append(tlv.T545([]byte(c.Device().QImei16)))
} else {
t.Append(tlv.T545([]byte(c.Device().IMEI)))
}
m := oicq.Message{ m := oicq.Message{
Uin: uint32(c.Uin), Uin: uint32(c.Uin),
Command: 0x810, Command: 0x810,
EncryptionMethod: oicq.EM_ST, EncryptionMethod: oicq.EM_ST,
Body: t.Marshal(), Body: t.Marshal(),
} }
nreq := network.Request{ req := network.Request{
Type: network.RequestTypeSimple, Type: network.RequestTypeSimple,
EncryptType: network.EncryptTypeEmptyKey, EncryptType: network.EncryptTypeEmptyKey,
Uin: c.Uin, Uin: c.Uin,
@ -438,7 +482,7 @@ func (c *QQClient) buildRequestTgtgtNopicsigPacket() (uint16, []byte) {
CommandName: "wtlogin.exchange_emp", CommandName: "wtlogin.exchange_emp",
Body: c.oicq.Marshal(&m), Body: c.oicq.Marshal(&m),
} }
return seq, c.transport.PackPacket(&nreq) return seq, c.transport.PackPacket(&req)
} }
func (c *QQClient) buildRequestChangeSigPacket(changeD2 bool) (uint16, []byte) { func (c *QQClient) buildRequestChangeSigPacket(changeD2 bool) (uint16, []byte) {

View File

@ -106,6 +106,8 @@ type QQClient struct {
GroupDigestEvent EventHandle[*GroupDigestEvent] GroupDigestEvent EventHandle[*GroupDigestEvent]
OtherClientStatusChangedEvent EventHandle[*OtherClientStatusChangedEvent] OtherClientStatusChangedEvent EventHandle[*OtherClientStatusChangedEvent]
OfflineFileEvent EventHandle[*OfflineFileEvent] OfflineFileEvent EventHandle[*OfflineFileEvent]
GroupDisbandEvent EventHandle[*GroupDisbandEvent]
DeleteFriendEvent EventHandle[*DeleteFriendEvent]
// message state // message state
msgSvcCache *utils.Cache[unit] msgSvcCache *utils.Cache[unit]

View File

@ -51,6 +51,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
if m.Exists(0x546) { if m.Exists(0x546) {
c.sig.T547 = auth.CalcPow(m[0x546]) c.sig.T547 = auth.CalcPow(m[0x546])
} }
// c.logger.Info("login response %v", t)
if t == 0 { // login success if t == 0 { // login success
// if t150, ok := m[0x150]; ok { // if t150, ok := m[0x150]; ok {
// c.t150 = t150 // c.t150 = t150
@ -71,6 +72,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
if m.Exists(0x192) { if m.Exists(0x192) {
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
VerifyUrl: string(m[0x192]), VerifyUrl: string(m[0x192]),
Error: SliderNeededError, Error: SliderNeededError,
}, nil }, nil
@ -82,6 +84,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
sign := imgData.ReadBytes(int(signLen)) sign := imgData.ReadBytes(int(signLen))
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: NeedCaptcha, Error: NeedCaptcha,
CaptchaImage: imgData.ReadAvailable(), CaptchaImage: imgData.ReadAvailable(),
CaptchaSign: sign, CaptchaSign: sign,
@ -89,6 +92,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
} else { } else {
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: UnknownLoginError, Error: UnknownLoginError,
}, nil }, nil
} }
@ -97,6 +101,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
if t == 40 { if t == 40 {
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
ErrorMessage: "账号被冻结", ErrorMessage: "账号被冻结",
Error: UnknownLoginError, Error: UnknownLoginError,
}, nil }, nil
@ -115,6 +120,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
if t204, ok := m[0x204]; ok { // 同时支持扫码验证 ? if t204, ok := m[0x204]; ok { // 同时支持扫码验证 ?
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: SMSOrVerifyNeededError, Error: SMSOrVerifyNeededError,
VerifyUrl: string(t204), VerifyUrl: string(t204),
SMSPhone: phone, SMSPhone: phone,
@ -123,6 +129,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
} }
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: SMSNeededError, Error: SMSNeededError,
SMSPhone: phone, SMSPhone: phone,
ErrorMessage: string(m[0x17e]), ErrorMessage: string(m[0x17e]),
@ -133,6 +140,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
c.sig.T104 = m[0x104] c.sig.T104 = m[0x104]
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: SMSNeededError, Error: SMSNeededError,
}, nil }, nil
} }
@ -140,6 +148,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
if t204, ok := m[0x204]; ok { // 扫码验证 if t204, ok := m[0x204]; ok { // 扫码验证
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: UnsafeDeviceError, Error: UnsafeDeviceError,
VerifyUrl: string(t204), VerifyUrl: string(t204),
ErrorMessage: "", ErrorMessage: "",
@ -149,6 +158,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
if t == 162 { if t == 162 {
return LoginResponse{ return LoginResponse{
Code: t,
Error: TooManySMSRequestError, Error: TooManySMSRequestError,
}, nil }, nil
} }
@ -165,6 +175,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
t149r.ReadStringShort() // title t149r.ReadStringShort() // title
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: OtherLoginError, Error: OtherLoginError,
ErrorMessage: t149r.ReadStringShort(), ErrorMessage: t149r.ReadStringShort(),
}, nil }, nil
@ -176,6 +187,7 @@ func decodeLoginResponse(c *QQClient, pkt *network.Packet) (any, error) {
t146r.ReadStringShort() // title t146r.ReadStringShort() // title
return LoginResponse{ return LoginResponse{
Success: false, Success: false,
Code: t,
Error: OtherLoginError, Error: OtherLoginError,
ErrorMessage: t146r.ReadStringShort(), ErrorMessage: t146r.ReadStringShort(),
}, nil }, nil
@ -732,6 +744,15 @@ func decodeOnlinePushTransPacket(c *QQClient, pkt *network.Packet) (any, error)
Operator: g.FindMember(operator), Operator: g.FindMember(operator),
}) })
} }
case 0x01, 0x81: // kosbot add: 群解散. 暂时这样 See https://github.com/lz1998/ricq/blob/064ddddca19aa0410e2514852e3a151fd9913371/ricq-core/src/command/online_push/decoder.rs#L86
c.GroupDisbandEvent.dispatch(c, &GroupDisbandEvent{
Group: g,
Operator: g.FindMember(operator),
Time: int64(info.MsgTime.Unwrap()),
})
if err = c.ReloadGroupList(); err != nil {
return nil, err
}
} }
} }
} }

View File

@ -23,10 +23,11 @@ type (
UserOnlineStatus int UserOnlineStatus int
ClientProtocol = auth.Protocol ClientProtocol = auth.ProtocolType
LoginResponse struct { LoginResponse struct {
Success bool Success bool
Code byte
Error LoginError Error LoginError
// Captcha info // Captcha info
@ -191,6 +192,17 @@ type (
DownloadUrl string DownloadUrl string
} }
GroupDisbandEvent struct {
Group *GroupInfo
Time int64
Operator *GroupMemberInfo
}
DeleteFriendEvent struct {
Uin int64
Nickname string
}
// GroupDigest 群精华消息 // GroupDigest 群精华消息
GroupDigest struct { GroupDigest struct {
GroupCode int64 `json:"group_code,string"` GroupCode int64 `json:"group_code,string"`

View File

@ -81,6 +81,7 @@ func GenRandomDevice() *DeviceInfo {
hex.Encode(device.AndroidId, r) hex.Encode(device.AndroidId, r)
device.GenNewGuid() device.GenNewGuid()
device.GenNewTgtgtKey() device.GenNewTgtgtKey()
device.RequestQImei()
return device return device
} }
@ -105,6 +106,13 @@ func GenIMEI() string {
return final.String() return final.String()
} }
func UpdateAppVersion(protocolType auth.ProtocolType, data []byte) error {
if _, ok := auth.AppVersions[protocolType]; !ok {
return errors.New("unknown protocol type: " + strconv.Itoa(int(protocolType)))
}
return auth.AppVersions[protocolType].ReadJson(data)
}
func getSSOAddress(device *auth.Device) ([]netip.AddrPort, error) { func getSSOAddress(device *auth.Device) ([]netip.AddrPort, error) {
protocol := device.Protocol.Version() protocol := device.Protocol.Version()
key, _ := hex.DecodeString("F0441F5FF42DA58FDCF7949ABA62D411") key, _ := hex.DecodeString("F0441F5FF42DA58FDCF7949ABA62D411")

View File

@ -1,10 +1,18 @@
package auth package auth
//go:generate stringer -type=Protocol -linecomment import (
type Protocol int "encoding/hex"
"encoding/json"
"fmt"
"github.com/pkg/errors"
)
//go:generate stringer -type=ProtocolType -linecomment
type ProtocolType int
const ( const (
Unset Protocol = iota Unset ProtocolType = iota
AndroidPhone // Android Phone AndroidPhone // Android Phone
AndroidWatch // Android Watch AndroidWatch // Android Watch
MacOS // MacOS MacOS // MacOS
@ -13,6 +21,101 @@ const (
AndroidPad // Android Pad AndroidPad // Android Pad
) )
var (
AppVersions = map[ProtocolType]*AppVersion{
AndroidPhone: {
ApkId: "com.tencent.mobileqq",
AppId: 537151682,
SubAppId: 537151682,
AppKey: "0S200MNJT807V3GE",
SortVersionName: "8.9.33.10335",
BuildTime: 1673599898,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2534",
SSOVersion: 19,
MiscBitmap: 150470524,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_A5 | WLOGIN_RESERVED | WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST |
WLOGIN_LSKEY | WLOGIN_SKEY | WLOGIN_SIG64 | 1<<16 | WLOGIN_VKEY | WLOGIN_D2 |
WLOGIN_SID | WLOGIN_PSKEY | WLOGIN_AQSIG | WLOGIN_LHSIG | WLOGIN_PAYTOKEN, // 16724722
Protocol: AndroidPhone,
},
AndroidPad: {
ApkId: "com.tencent.mobileqq",
AppId: 537151218,
SubAppId: 537151218,
AppKey: "0S200MNJT807V3GE",
SortVersionName: "8.9.33.10335",
BuildTime: 1673599898,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2534",
SSOVersion: 19,
MiscBitmap: 150470524,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_A5 | WLOGIN_RESERVED | WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST |
WLOGIN_LSKEY | WLOGIN_SKEY | WLOGIN_SIG64 | 1<<16 | WLOGIN_VKEY | WLOGIN_D2 |
WLOGIN_SID | WLOGIN_PSKEY | WLOGIN_AQSIG | WLOGIN_LHSIG | WLOGIN_PAYTOKEN, // 16724722
Protocol: AndroidPad,
},
AndroidWatch: {
ApkId: "com.tencent.qqlite",
AppId: 537064446,
SubAppId: 537064446,
SortVersionName: "2.0.5",
BuildTime: 1559564731,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.236",
SSOVersion: 5,
MiscBitmap: 16252796,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_D2 | WLOGIN_PSKEY | WLOGIN_DA2, // 34869472
Protocol: AndroidWatch,
},
IPad: {
ApkId: "com.tencent.minihd.qq",
AppId: 537118796,
SubAppId: 537118796,
SortVersionName: "5.9.3",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 12,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_VKEY | WLOGIN_D2 | WLOGIN_SID | WLOGIN_PSKEY, // 1970400
Protocol: IPad,
},
MacOS: {
ApkId: "com.tencent.minihd.qq",
AppId: 537128930,
SubAppId: 537128930,
SortVersionName: "5.8.9",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 12,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_VKEY | WLOGIN_D2 | WLOGIN_SID | WLOGIN_PSKEY, // 1970400
Protocol: MacOS,
},
QiDian: {
ApkId: "com.tencent.qidian",
AppId: 537096038,
SubAppId: 537036590,
SortVersionName: "5.0.0",
BuildTime: 1630062176,
ApkSign: []byte{160, 30, 236, 171, 133, 233, 227, 186, 43, 15, 106, 21, 140, 133, 92, 41},
SdkVersion: "6.0.0.2484",
SSOVersion: 18,
MiscBitmap: 184024956,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_D2 | WLOGIN_PSKEY | WLOGIN_DA2, // 34869472
Protocol: QiDian,
},
}
)
// see oicq/wlogin_sdk/request/WtloginHelper.java class SigType // see oicq/wlogin_sdk/request/WtloginHelper.java class SigType
const ( const (
_ = 1 << iota _ = 1 << iota
@ -52,128 +155,44 @@ type AppVersion struct {
SdkVersion string SdkVersion string
AppId uint32 AppId uint32
SubAppId uint32 SubAppId uint32
AppKey string
BuildTime uint32 BuildTime uint32
SSOVersion uint32 SSOVersion uint32
MiscBitmap uint32 MiscBitmap uint32
SubSigmap uint32 SubSigmap uint32
MainSigMap uint32 MainSigMap uint32
Protocol Protocol Protocol ProtocolType
} }
var ( func (v *AppVersion) String() string {
aPhone = &AppVersion{ return fmt.Sprintf("%s %s - %v", v.Protocol.String(), v.SortVersionName, v.BuildTime)
ApkId: "com.tencent.mobileqq",
AppId: 537143097,
SubAppId: 537143097,
SortVersionName: "8.9.23.9425",
BuildTime: 1640921786,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2530",
SSOVersion: 19,
MiscBitmap: 150470524,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_A5 | WLOGIN_RESERVED | WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST |
WLOGIN_LSKEY | WLOGIN_SKEY | WLOGIN_SIG64 | 1<<16 | WLOGIN_VKEY | WLOGIN_D2 |
WLOGIN_SID | WLOGIN_PSKEY | WLOGIN_AQSIG | WLOGIN_LHSIG | WLOGIN_PAYTOKEN, // 16724722
Protocol: AndroidPhone,
} }
aPad = &AppVersion{ func (v *AppVersion) ReadJson(d []byte) error {
ApkId: "com.tencent.mobileqq", var f appVersionFile
AppId: 537142586, if err := json.Unmarshal(d, &f); err != nil {
SubAppId: 537142586, return errors.Wrap(err, "failed to unmarshal json message")
SortVersionName: "8.9.23.9425",
BuildTime: 1640921786,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.2530",
SSOVersion: 19,
MiscBitmap: 150470524,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_A5 | WLOGIN_RESERVED | WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST |
WLOGIN_LSKEY | WLOGIN_SKEY | WLOGIN_SIG64 | 1<<16 | WLOGIN_VKEY | WLOGIN_D2 |
WLOGIN_SID | WLOGIN_PSKEY | WLOGIN_AQSIG | WLOGIN_LHSIG | WLOGIN_PAYTOKEN, // 16724722
Protocol: AndroidPad,
}
aWatch = &AppVersion{
ApkId: "com.tencent.qqlite",
AppId: 537064446,
SubAppId: 537064446,
SortVersionName: "2.0.5",
BuildTime: 1559564731,
ApkSign: []byte{0xA6, 0xB7, 0x45, 0xBF, 0x24, 0xA2, 0xC2, 0x77, 0x52, 0x77, 0x16, 0xF6, 0xF3, 0x6E, 0xB6, 0x8D},
SdkVersion: "6.0.0.236",
SSOVersion: 5,
MiscBitmap: 16252796,
SubSigmap: 0x10400,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_D2 | WLOGIN_PSKEY | WLOGIN_DA2, // 34869472
Protocol: AndroidWatch,
}
ipad = &AppVersion{
ApkId: "com.tencent.minihd.qq",
AppId: 537118796,
SubAppId: 537118796,
SortVersionName: "5.9.3",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 12,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_VKEY | WLOGIN_D2 | WLOGIN_SID | WLOGIN_PSKEY, // 1970400
Protocol: IPad,
}
macOS = &AppVersion{
ApkId: "com.tencent.minihd.qq",
AppId: 537128930,
SubAppId: 537128930,
SortVersionName: "5.8.9",
BuildTime: 1595836208,
ApkSign: []byte{170, 57, 120, 244, 31, 217, 111, 249, 145, 74, 102, 158, 24, 100, 116, 199},
SdkVersion: "6.0.0.2433",
SSOVersion: 12,
MiscBitmap: 150470524,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_VKEY | WLOGIN_D2 | WLOGIN_SID | WLOGIN_PSKEY, // 1970400
Protocol: MacOS,
}
qidian = &AppVersion{
ApkId: "com.tencent.qidian",
AppId: 537096038,
SubAppId: 537036590,
SortVersionName: "5.0.0",
BuildTime: 1630062176,
ApkSign: []byte{160, 30, 236, 171, 133, 233, 227, 186, 43, 15, 106, 21, 140, 133, 92, 41},
SdkVersion: "6.0.0.2484",
SSOVersion: 18,
MiscBitmap: 184024956,
SubSigmap: 66560,
MainSigMap: WLOGIN_STWEB | WLOGIN_A2 | WLOGIN_ST | WLOGIN_SKEY | WLOGIN_D2 | WLOGIN_PSKEY | WLOGIN_DA2, // 34869472
Protocol: QiDian,
}
)
func (i Protocol) Version() *AppVersion {
switch i {
case AndroidPhone:
return aPhone
case AndroidPad:
return aPad
case AndroidWatch:
return aWatch
case IPad:
return ipad
case MacOS:
return macOS
case QiDian:
return qidian
} }
v.ApkId = f.ApkId
v.AppId = f.AppId
v.SubAppId = f.SubAppId
v.AppKey = f.AppKey
v.SortVersionName = f.SortVersionName
v.BuildTime = f.BuildTime
v.ApkSign, _ = hex.DecodeString(f.ApkSign)
v.SdkVersion = f.SdkVersion
v.SSOVersion = f.SSOVersion
v.MiscBitmap = f.MiscBitmap
v.SubSigmap = f.SubSigmap
v.MainSigMap = f.MainSigMap
v.Protocol = f.ProtocolType
return nil return nil
} }
func (i ProtocolType) Version() *AppVersion {
return AppVersions[i]
}
type SigInfo struct { type SigInfo struct {
LoginBitmap uint64 LoginBitmap uint64
TGT []byte TGT []byte
@ -216,3 +235,19 @@ type SigInfo struct {
Ksid []byte Ksid []byte
// msgCtrlBuf []byte // msgCtrlBuf []byte
} }
type appVersionFile struct {
ApkId string `json:"apk_id"`
AppId uint32 `json:"app_id"`
SubAppId uint32 `json:"sub_app_id"`
AppKey string `json:"app_key"`
SortVersionName string `json:"sort_version_name"`
BuildTime uint32 `json:"build_time"`
ApkSign string `json:"apk_sign"` // hex encoded
SdkVersion string `json:"sdk_version"`
SSOVersion uint32 `json:"sso_version"`
MiscBitmap uint32 `json:"misc_bitmap"`
MainSigMap uint32 `json:"main_sig_map"`
SubSigmap uint32 `json:"sub_sig_map"`
ProtocolType ProtocolType `json:"protocol_type"`
}

View File

@ -45,7 +45,9 @@ type Device struct {
VendorOSName []byte VendorOSName []byte
Guid []byte Guid []byte
TgtgtKey []byte TgtgtKey []byte
Protocol Protocol QImei16 string
QImei36 string
Protocol ProtocolType
Version *OSVersion Version *OSVersion
} }
@ -135,7 +137,7 @@ func (info *Device) ReadJson(d []byte) error {
switch f.Protocol { switch f.Protocol {
case 1, 2, 3, 4, 5, 6: case 1, 2, 3, 4, 5, 6:
info.Protocol = Protocol(f.Protocol) info.Protocol = ProtocolType(f.Protocol)
default: default:
info.Protocol = AndroidPad info.Protocol = AndroidPad
} }
@ -149,6 +151,7 @@ func (info *Device) ReadJson(d []byte) error {
info.GenNewGuid() info.GenNewGuid()
info.GenNewTgtgtKey() info.GenNewTgtgtKey()
info.RequestQImei() // 应该可以缓存, 理论上同一设备每次请求都是一样的
return nil return nil
} }

View File

@ -1,29 +0,0 @@
// Code generated by "stringer -type=Protocol -linecomment"; DO NOT EDIT.
package auth
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Unset-0]
_ = x[AndroidPhone-1]
_ = x[AndroidWatch-2]
_ = x[MacOS-3]
_ = x[QiDian-4]
_ = x[IPad-5]
_ = x[AndroidPad-6]
}
const _Protocol_name = "UnsetAndroid PhoneAndroid WatchMacOS企点iPadAndroid Pad"
var _Protocol_index = [...]uint8{0, 5, 18, 31, 36, 42, 46, 57}
func (i Protocol) String() string {
if i < 0 || i >= Protocol(len(_Protocol_index)-1) {
return "Protocol(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Protocol_name[_Protocol_index[i]:_Protocol_index[i+1]]
}

View File

@ -0,0 +1,29 @@
// Code generated by "stringer -type=ProtocolType -linecomment"; DO NOT EDIT.
package auth
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[Unset-0]
_ = x[AndroidPhone-1]
_ = x[AndroidWatch-2]
_ = x[MacOS-3]
_ = x[QiDian-4]
_ = x[IPad-5]
_ = x[AndroidPad-6]
}
const _ProtocolType_name = "UnsetAndroid PhoneAndroid WatchMacOS企点iPadAndroid Pad"
var _ProtocolType_index = [...]uint8{0, 5, 18, 31, 36, 42, 46, 57}
func (i ProtocolType) String() string {
if i < 0 || i >= ProtocolType(len(_ProtocolType_index)-1) {
return "ProtocolType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ProtocolType_name[_ProtocolType_index[i]:_ProtocolType_index[i+1]]
}

View File

@ -0,0 +1,192 @@
package auth
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/pem"
"fmt"
"math/rand"
"time"
"github.com/Mrs4s/MiraiGo/utils"
"github.com/tidwall/gjson"
)
const (
secret = "ZdJqM15EeO2zWc08"
rsaKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEIxgwoutfwoJxcGQeedgP7FG9
qaIuS0qzfR8gWkrkTZKM2iWHn2ajQpBRZjMSoSf6+KJGvar2ORhBfpDXyVtZCKpq
LQ+FLkpncClKVIrBwv6PHyUvuCb0rIarmgDnzkfQAqVufEtR64iazGDKatvJ9y6B
9NMbHddGSAUmRTCrHQIDAQAB
-----END PUBLIC KEY-----`
)
func (info *Device) RequestQImei() {
if info.Protocol.Version().AppKey == "" {
return
}
// init params
payload, _ := json.Marshal(genRandomPayloadByDevice(info))
cryptKey := utils.RandomStringRange(16, "abcdef1234567890")
ts := time.Now().Unix() * 1000
nonce := utils.RandomStringRange(16, "abcdef1234567890")
// init rsa key and aes key
publicKey := initPublicKey()
encryptedAesKey, _ := rsa.EncryptPKCS1v15(rand.New(rand.NewSource(time.Now().UnixNano())), publicKey, []byte(cryptKey))
encryptedPayload := aesEncrypt(payload, []byte(cryptKey))
key := base64.StdEncoding.EncodeToString(encryptedAesKey)
params := base64.StdEncoding.EncodeToString(encryptedPayload)
postData, _ := json.Marshal(map[string]any{
"key": key,
"params": params,
"time": ts,
"nonce": nonce,
"sign": sign(key, params, fmt.Sprint(ts), nonce),
"extra": "",
})
resp, _ := utils.HttpPostBytesWithCookie("https://snowflake.qq.com/ola/android", postData, "", "application/json")
if gjson.GetBytes(resp, "code").Int() != 0 {
return
}
encryptedResponse, _ := base64.StdEncoding.DecodeString(gjson.GetBytes(resp, "data").String())
decryptedResponse := aesDecrypt(encryptedResponse, []byte(cryptKey))
info.QImei16 = gjson.GetBytes(decryptedResponse, "q16").String()
info.QImei36 = gjson.GetBytes(decryptedResponse, "q36").String()
}
func initPublicKey() *rsa.PublicKey {
blockPub, _ := pem.Decode([]byte(rsaKey))
pub, _ := x509.ParsePKIXPublicKey(blockPub.Bytes)
return pub.(*rsa.PublicKey)
}
func sign(key, params, ts, nonce string) string {
h := md5.Sum([]byte(key + params + ts + nonce + secret))
return hex.EncodeToString(h[:])
}
func aesEncrypt(src []byte, key []byte) []byte {
block, _ := aes.NewCipher(key)
ecb := cipher.NewCBCEncrypter(block, key)
content := src
content = pkcs5Padding(content, block.BlockSize())
crypted := make([]byte, len(content))
ecb.CryptBlocks(crypted, content)
return crypted
}
func aesDecrypt(crypt []byte, key []byte) []byte {
block, _ := aes.NewCipher(key)
ecb := cipher.NewCBCDecrypter(block, key)
decrypted := make([]byte, len(crypt))
ecb.CryptBlocks(decrypted, crypt)
return pkcs5Trimming(decrypted)
}
func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func pkcs5Trimming(encrypt []byte) []byte {
padding := encrypt[len(encrypt)-1]
return encrypt[:len(encrypt)-int(padding)]
}
func genRandomPayloadByDevice(info *Device) map[string]any {
now := time.Now()
seed := int64(0x6F4)
for _, b := range info.Guid {
seed += int64(b)
}
fixedRand := rand.New(rand.NewSource(seed))
years := now.Year()
month := now.Month()
if month == 1 {
years--
month = 12
} else {
month--
}
reserved := map[string]string{
"harmony": "0",
"clone": "0",
"containe": "",
"oz": "UhYmelwouA+V2nPWbOvLTgN2/m8jwGB+yUB5v9tysQg=",
"oo": "Xecjt+9S1+f8Pz2VLSxgpw==",
"kelong": "0",
"uptimes": time.Date(years, month, fixedRand.Intn(27)+1, fixedRand.Intn(60), fixedRand.Intn(60), fixedRand.Intn(60), fixedRand.Intn(1e9), now.Location()).Format("2006-01-02 15:04:05"),
"multiUser": "0",
"bod": string(info.Board),
"brd": string(info.Brand),
"dv": string(info.Device),
"firstLevel": "",
"manufact": string(info.Brand),
"name": string(info.Model),
"host": "se.infra",
"kernel": string(info.ProcVersion),
}
reservedBytes, _ := json.Marshal(reserved)
deviceType := "Phone"
if info.Protocol == AndroidPad {
deviceType = "Pad"
}
beaconId := ""
timeMonth := time.Now().Format("2006-01-") + "01"
rand1 := fixedRand.Intn(899999) + 100000
rand2 := fixedRand.Intn(899999999) + 100000000
for i := 1; i <= 40; i++ {
switch i {
case 1, 2, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30, 33, 34, 37, 38:
beaconId += fmt.Sprintf("k%v:%v%v.%v", i, timeMonth, rand1, rand2)
case 3:
beaconId += "k3:0000000000000000"
case 4:
beaconId += "k4:" + utils.RandomStringRange(16, "123456789abcdef")
default:
beaconId += fmt.Sprintf("k%v:%v", i, fixedRand.Intn(10000))
}
beaconId += ";"
}
return map[string]any{
"androidId": string(info.AndroidId),
"platformId": 1,
"appKey": info.Protocol.Version().AppKey,
"appVersion": info.Protocol.Version().SortVersionName,
"beaconIdSrc": beaconId,
"brand": string(info.Brand),
"channelId": "2017",
"cid": "",
"imei": info.IMEI,
"imsi": "",
"mac": "",
"model": string(info.Model),
"networkType": "unknown",
"oaid": "",
"osVersion": "Android " + string(info.Version.Release) + ",level " + fmt.Sprint(info.Version.SDK),
"qimei": "",
"qimei36": "",
"sdkVersion": "1.2.13.6",
"audit": "",
"userId": "{}",
"packageId": info.Protocol.Version().ApkId,
"deviceType": deviceType,
"sdkName": "",
"reserved": string(reservedBytes),
}
}

View File

@ -208,6 +208,10 @@ func msgType0x210Sub27Decoder(c *QQClient, protobuf []byte) error {
if m.DelFriend != nil { if m.DelFriend != nil {
frdUin := m.DelFriend.Uins[0] frdUin := m.DelFriend.Uins[0]
if frd := c.FindFriend(int64(frdUin)); frd != nil { if frd := c.FindFriend(int64(frdUin)); frd != nil {
c.DeleteFriendEvent.dispatch(c, &DeleteFriendEvent{
Uin: frd.Uin,
Nickname: frd.Nickname,
})
if err := c.ReloadFriendList(); err != nil { if err := c.ReloadFriendList(); err != nil {
return errors.Wrap(err, "failed to reload friend list") return errors.Wrap(err, "failed to reload friend list")
} }

33
internal/tlv/t544.go Normal file
View File

@ -0,0 +1,33 @@
package tlv
import "github.com/Mrs4s/MiraiGo/binary"
// temporary solution
func T544(userId uint64, moduleId string, subCmd uint32, sdkVersion string, guid []byte, signer func(uint64, string, []byte) ([]byte, error)) []byte {
salt := binary.NewWriterF(func(w *binary.Writer) {
w.WriteUInt64(userId)
w.WriteBytesShort(guid)
w.WriteBytesShort([]byte(sdkVersion))
w.WriteUInt32(subCmd)
})
sign, err := signer(userId, moduleId, salt)
if err != nil {
return nil
}
return binary.NewWriterF(func(w *binary.Writer) {
w.WriteUInt16(0x544)
w.WriteBytesShort(sign)
})
}
func T544Custom(userId uint64, moduleId string, salt []byte, signer func(uint64, string, []byte) ([]byte, error)) []byte {
sign, err := signer(userId, moduleId, salt)
if err != nil {
return nil
}
return binary.NewWriterF(func(w *binary.Writer) {
w.WriteUInt16(0x544)
w.WriteBytesShort(sign)
})
}

View File

@ -2,9 +2,9 @@ package tlv
import "github.com/Mrs4s/MiraiGo/binary" import "github.com/Mrs4s/MiraiGo/binary"
func T545(imei []byte) []byte { func T545(qimei []byte) []byte {
return binary.NewWriterF(func(w *binary.Writer) { return binary.NewWriterF(func(w *binary.Writer) {
w.WriteUInt16(0x545) w.WriteUInt16(0x545)
w.WriteBytesShort(imei) w.WriteBytesShort(qimei)
}) })
} }

3
wrapper/codec.go Normal file
View File

@ -0,0 +1,3 @@
package wrapper
var DandelionEnergy func(uint64, string, []byte) ([]byte, error)