github.com/LagrangeDev/LagrangeGo@v0.0.0-20240512064304-ad4a85e10cb4/client/client.go (about) 1 package client 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "time" 8 9 "github.com/LagrangeDev/LagrangeGo/client/packets/tlv" 10 "github.com/LagrangeDev/LagrangeGo/client/packets/wtlogin" 11 "github.com/LagrangeDev/LagrangeGo/client/packets/wtlogin/loginState" 12 "github.com/LagrangeDev/LagrangeGo/client/packets/wtlogin/qrcodeState" 13 "github.com/LagrangeDev/LagrangeGo/utils" 14 "github.com/LagrangeDev/LagrangeGo/utils/binary" 15 "github.com/LagrangeDev/LagrangeGo/utils/crypto" 16 ) 17 18 func (c *QQClient) Login(password, qrcodePath string) error { 19 // prefer session login 20 if len(c.transport.Sig.D2) != 0 && c.transport.Sig.Uin != 0 { 21 c.infoln("Session found, try to login with session") 22 c.Uin = c.transport.Sig.Uin 23 if c.Online.Load() { 24 return ErrAlreadyOnline 25 } 26 err := c.connect() 27 if err != nil { 28 return err 29 } 30 err = c.init() 31 if err != nil { 32 err = fmt.Errorf("failed to register session: %v", err) 33 c.errorln(err) 34 return err 35 } 36 return nil 37 } 38 39 if len(c.transport.Sig.TempPwd) != 0 { 40 err := c.keyExchange() 41 if err != nil { 42 return err 43 } 44 45 ret, err := c.TokenLogin() 46 if err != nil { 47 return fmt.Errorf("EasyLogin fail: %s", err) 48 } 49 50 if ret.Successful() { 51 return c.init() 52 } 53 } 54 55 if password != "" { 56 c.infoln("login with password") 57 err := c.keyExchange() 58 if err != nil { 59 return err 60 } 61 62 for { 63 ret, err := c.PasswordLogin(password) 64 if err != nil { 65 return err 66 } 67 switch { 68 case ret.Successful(): 69 return c.init() 70 case ret == loginState.CaptchaVerify: 71 c.warningln("captcha verification required") 72 c.transport.Sig.CaptchaInfo[0] = utils.ReadLine("ticket?->") 73 c.transport.Sig.CaptchaInfo[1] = utils.ReadLine("rand_str?->") 74 default: 75 c.error("Unhandled exception raised: %s", ret.Name()) 76 } 77 } 78 // panic("unreachable") 79 } 80 c.infoln("login with qrcode") 81 png, _, err := c.FecthQRCode() 82 if err != nil { 83 return err 84 } 85 err = os.WriteFile(qrcodePath, png, 0666) 86 if err != nil { 87 return err 88 } 89 c.info("qrcode saved to %s", qrcodePath) 90 err = c.QRCodeLogin(3) 91 if err != nil { 92 return err 93 } 94 return c.init() 95 } 96 97 func (c *QQClient) TokenLogin() (loginState.State, error) { 98 if c.Online.Load() { 99 return -996, ErrAlreadyOnline 100 } 101 err := c.connect() 102 if err != nil { 103 return -997, err 104 } 105 data, err := buildNtloginRequest(c.Uin, c.version(), c.Device(), &c.transport.Sig, c.transport.Sig.TempPwd) 106 if err != nil { 107 return -998, err 108 } 109 packet, err := c.sendUniPacketAndWait( 110 "trpc.login.ecdh.EcdhService.SsoNTLoginEasyLogin", 111 data, 112 ) 113 if err != nil { 114 return -999, err 115 } 116 return parseNtloginResponse(packet, &c.transport.Sig) 117 } 118 119 func (c *QQClient) FecthQRCode() ([]byte, string, error) { 120 if c.Online.Load() { 121 return nil, "", ErrAlreadyOnline 122 } 123 err := c.connect() 124 if err != nil { 125 return nil, "", err 126 } 127 128 body := binary.NewBuilder(nil). 129 WriteU16(0). 130 WriteU64(0). 131 WriteU8(0). 132 WriteTLV( 133 tlv.T16(c.version().AppID, c.version().SubAppID, 134 utils.MustParseHexStr(c.Device().Guid), c.version().PTVersion, c.version().PackageName), 135 tlv.T1b(), 136 tlv.T1d(c.version().MiscBitmap), 137 tlv.T33(utils.MustParseHexStr(c.Device().Guid)), 138 tlv.T35(c.version().PTOSVersion), 139 tlv.T66(c.version().PTOSVersion), 140 tlv.Td1(c.version().OS, c.Device().DeviceName), 141 ).WriteU8(3).ToBytes() 142 143 packet := c.buildCode2dPacket(c.Uin, 0x31, body) 144 response, err := c.sendUniPacketAndWait("wtlogin.trans_emp", packet) 145 if err != nil { 146 return nil, "", err 147 } 148 149 decrypted := binary.NewReader(response) 150 decrypted.SkipBytes(54) 151 retCode := decrypted.ReadU8() 152 qrsig := decrypted.ReadBytesWithLength("u16", false) 153 tlvs := decrypted.ReadTlv() 154 155 if retCode == 0 && tlvs[0x17] != nil { 156 c.transport.Sig.Qrsig = qrsig 157 urlreader := binary.NewReader(tlvs[209]) 158 // 这样是不对的,调试后发现应该丢一个字节,然后读下一个字节才是数据的大小 159 // string(binary.NewReader(tlvs[209]).ReadBytesWithLength("u16", true)) 160 urlreader.ReadU8() 161 return tlvs[0x17], utils.B2S(urlreader.ReadBytesWithLength("u8", false)), nil 162 } 163 164 return nil, "", fmt.Errorf("err qr retcode %d", retCode) 165 } 166 167 func (c *QQClient) GetQRCodeResult() (qrcodeState.State, error) { 168 c.infoln("get qrcode result") 169 if c.transport.Sig.Qrsig == nil { 170 return -1, errors.New("no qrsig found, execute fetch_qrcode first") 171 } 172 173 body := binary.NewBuilder(nil). 174 WritePacketBytes(c.transport.Sig.Qrsig, "u16", false). 175 WriteU64(0). 176 WriteU32(0). 177 WriteU8(0). 178 WriteU8(0x83).ToBytes() 179 180 response, err := c.sendUniPacketAndWait("wtlogin.trans_emp", 181 c.buildCode2dPacket(0, 0x12, body)) 182 if err != nil { 183 return -1, err 184 } 185 186 reader := binary.NewReader(response) 187 //length := reader.ReadU32() 188 reader.SkipBytes(8) // 4 + 4 189 reader.ReadU16() // cmd, 0x12 190 reader.SkipBytes(40) 191 _ = reader.ReadU32() // app id 192 retCode := qrcodeState.State(reader.ReadU8()) 193 194 if retCode == 0 { 195 reader.SkipBytes(4) 196 c.Uin = reader.ReadU32() 197 reader.SkipBytes(4) 198 t := reader.ReadTlv() 199 c.t106 = t[0x18] 200 c.t16a = t[0x19] 201 c.transport.Sig.Tgtgt = t[0x1e] 202 c.debugln("len(c.t106) =", len(c.t106), "len(c.t16a) =", len(c.t16a)) 203 c.debugln("len(c.transport.Sig.Tgtgt) =", len(c.transport.Sig.Tgtgt)) 204 } 205 206 return retCode, nil 207 } 208 209 func (c *QQClient) keyExchange() error { 210 data, err := wtlogin.BuildKexExchangeRequest(c.Uin, c.Device().Guid) 211 if err != nil { 212 return err 213 } 214 packet, err := c.sendUniPacketAndWait( 215 "trpc.login.ecdh.EcdhService.SsoKeyExchange", 216 data, 217 ) 218 if err != nil { 219 c.errorln(err) 220 return err 221 } 222 c.debug("keyexchange proto data: %x", packet) 223 c.transport.Sig.ExchangeKey, c.transport.Sig.KeySig, err = wtlogin.ParseKeyExchangeResponse(packet) 224 return err 225 } 226 227 func (c *QQClient) PasswordLogin(password string) (loginState.State, error) { 228 if c.Online.Load() { 229 return -996, ErrAlreadyOnline 230 } 231 err := c.connect() 232 if err != nil { 233 return -997, err 234 } 235 236 md5Password := crypto.MD5Digest(utils.S2B(password)) 237 238 cr := tlv.T106( 239 c.version().AppID, 240 c.version().AppClientVersion, 241 int(c.Uin), 242 c.Device().Guid, 243 md5Password, 244 c.transport.Sig.Tgtgt, 245 make([]byte, 4), 246 true)[4:] 247 248 data, err := buildNtloginRequest(c.Uin, c.version(), c.Device(), &c.transport.Sig, cr) 249 if err != nil { 250 return -998, err 251 } 252 packet, err := c.sendUniPacketAndWait( 253 "trpc.login.ecdh.EcdhService.SsoNTLoginPasswordLogin", 254 data, 255 ) 256 if err != nil { 257 return -999, err 258 } 259 return parseNtloginResponse(packet, &c.transport.Sig) 260 } 261 262 func (c *QQClient) QRCodeLogin(refreshInterval int) error { 263 if c.transport.Sig.Qrsig == nil { 264 return errors.New("no QrSig found, fetch qrcode first") 265 } 266 267 for { 268 retCode, err := c.GetQRCodeResult() 269 if err != nil { 270 c.errorln(err) 271 return err 272 } 273 if retCode.Waitable() { 274 time.Sleep(time.Duration(refreshInterval) * time.Second) 275 continue 276 } 277 if !retCode.Success() { 278 return errors.New(retCode.Name()) 279 } 280 break 281 } 282 283 app := c.version() 284 device := c.Device() 285 response, err := c.sendUniPacketAndWait( 286 "wtlogin.login", 287 c.buildLoginPacket(c.Uin, "wtlogin.login", binary.NewBuilder(nil). 288 WriteU16(0x09). 289 WriteTLV( 290 binary.NewBuilder(nil).WriteBytes(c.t106).Pack(0x106), 291 tlv.T144(c.transport.Sig.Tgtgt, app, device), 292 tlv.T116(app.SubSigmap), 293 tlv.T142(app.PackageName, 0), 294 tlv.T145(utils.MustParseHexStr(device.Guid)), 295 tlv.T18(0, app.AppClientVersion, int(c.Uin), 0, 5, 0), 296 tlv.T141([]byte("Unknown"), nil), 297 tlv.T177(app.WTLoginSDK, 0), 298 tlv.T191(0), 299 tlv.T100(5, app.AppID, app.SubAppID, 8001, app.MainSigmap, 0), 300 tlv.T107(1, 0x0d, 0, 1), 301 tlv.T318(nil), 302 binary.NewBuilder(nil).WriteBytes(c.t16a).Pack(0x16a), 303 tlv.T166(5), 304 tlv.T521(0x13, "basicim"), 305 ).ToBytes())) 306 307 if err != nil { 308 c.errorln(err) 309 return err 310 } 311 312 return c.decodeLoginResponse(response, &c.transport.Sig) 313 } 314 315 func (c *QQClient) init() error { 316 response, err := c.sendUniPacketAndWait( 317 "trpc.qq_new_tech.status_svc.StatusService.Register", 318 wtlogin.BuildRegisterRequest(c.version(), c.Device())) 319 320 if err != nil { 321 c.errorln(err) 322 return err 323 } 324 325 err = wtlogin.ParseRegisterResponse(response) 326 if err != nil { 327 c.errorln("register failed:", err) 328 return err 329 } 330 c.transport.Sig.Uin = c.Uin 331 c.setOnline() 332 go c.doHeartbeat() 333 c.infoln("register succeeded") 334 return nil 335 }