2020/04/02 - 粉红的小小白 - 转载转发请标注来源
以下内容仅做学习交流使用,严禁非法用途
最近闲的无聊,于是随手对QQzone进行一次抓包分析
工作开始前我们需要准备的工具有:
工具名称 | 备注内容 | 下载链接 |
---|---|---|
Chrome | 谷歌浏览器,这里我们用于抓包 | •点击下载 |
postman | 一款接口调试工具 | •点击下载 |
Python3 | 需要用到requests 和 PIL 库 | •点击下载 |
VsCode | 也可以使用PyCharm | •点击下载 |
首先我们打开QQ空间网页版
然后我们打开F12调试,点击Network,找到二维码的来源
获取二维码的接口 (ptqrshow) 地址 为:
https://ssl.ptlogin2.qq.com/ptqrshow?appid=549000912&e=2&l=M&s=3&d=72&v=4&t=0.17442452440865464&daid=5&pt_3rd_aid=0
经过多次获取该地址并对其进行分析,对比后发现:
- 其中的get参数 &t 是随机变动的
- 在python3中,我们可以使用random库构建一个类似的随机数,我们将他封装为一个函数t(),方便我们后面调用
写代码之前,我们先将必须用到的一些库放在首部导入,代码如下
#若没安装该库则使用pip安装
from PIL import Image # 图片处理
import requests # 发送网络请求
import time # 时间库
import random # 随机数
import json # json库,用来格式化
获取参数 &t 的代码如下
def t():
return str(random.random())
接下来我们将获取二维码的过程封装成一个函数
def getQRC(): # 获取二维码
url = 'https://ssl.ptlogin2.qq.com/ptqrshow?appid=549000912&e=2&l=M&s=3&d=72&v=4&t=' + t() + '&daid=5&pt_3rd_aid=0'
res = requests.get(url)
with open('QRC.png', 'wb') as f:
f.write(res.content)
Image.open('QRC.png').show() # 打开二维码图片
return res.headers # 将请求结果返回,后面会用到
我们把二维码接口地址得到后,接下来我们来看它是怎么通过扫描验证码进行登录的
通过Network我们很容易发现,它会每隔一段时间向同一个接口发送请求,分析后得知,该接口用于判断用户是否已经扫码,然后登录
接口 (ptqrlogin) 地址如下:
https://ssl.ptlogin2.qq.com/ptqrlogin?u1=https%3A%2F%2Fqzs.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&ptqrtoken=1889434358&ptredirect=0&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=0-0-1585807008960&js_ver=20032614&js_type=1&login_sig=jhMZlTwCdTy5do0qJy2b2cRJocoxd-rzRYpb9-LszY8DCDM0WJtbcYicUjiNKGI3&pt_uistyle=40&aid=549000912&daid=5&
多次对比分析后发现,我们需要用到的参数有
- &login_sig
- &ptqrtoken
- &action
&login_sig
该参数内容需要从以下接口的 header 里的 Set-Cookie 里获取,并且其参数内容由64位数字,字母和符号"-"构成
接口 (xlogin) 地址如下:
https://xui.ptlogin2.qq.com/cgi-bin/xlogin?proxy_url=https://qzs.qq.com/qzone/v6/portal/proxy.html&daid=5&&hide_title_bar=1&low_login=0&qlogin_auto_login=1&no_verifyimg=1&link_target=blank&appid=549000912&style=22&target=self&s_url=https://qzs.qzone.qq.com/qzone/v5/loginsucc.html?para=izone&pt_qr_app=手机QQ空间&pt_qr_link=http://z.qzone.com/download.html&self_regurl=https://qzs.qq.com/qzone/v6/reg/index.html&pt_qr_help_link=http://z.qzone.com/download.html&pt_no_auth=1
接下来我们调用接口 (xlogin) 后,在header内的Set-Cookie内发现了参数 &login_sig
根据以上需求,我们可以将获取参数 &login_sig 的操作过程封装成函数 (如下),方便我们后面调用
def getLoginSig():
url = 'https://xui.ptlogin2.qq.com/cgi-bin/xlogin?proxy_url=https://qzs.qq.com/qzone/v6/portal/proxy.html&daid=5&&hide_title_bar=1&low_login=0&qlogin_auto_login=1&no_verifyimg=1&link_target=blank&appid=549000912&style=22&target=self&s_url=https://qzs.qzone.qq.com/qzone/v5/loginsucc.html?para=izone&pt_qr_app=手机QQ空间&pt_qr_link=http://z.qzone.com/download.html&self_regurl=https://qzs.qq.com/qzone/v6/reg/index.html&pt_qr_help_link=http://z.qzone.com/download.html&pt_no_auth=1'
headers = requests.get(url).headers
start_index = headers['Set-Cookie'].find('pt_login_sig=') + 13
return headers['Set-Cookie'][start_index:start_index + 64]
&ptqrtoken
该参数内容需要从获取二维码的接口 (ptqrshow) 中的header内获取,因为不同的二维码,该参数值都是不一样的,因此我们在第一步获取二维码并保存的时候,就需要将header返回,用于参数 &ptqrtoken 的获取
我们从获取二维码的接口 (ptqrshow) 的抓包信息来看,一个名为 &qrsig 的参数符合我们的要求
于是我们将获取参数 &ptqrtoken 的过程封装成一个函数
def getQRCSig(headers): # 将获取二维码接口的headers传进来
return ptqrtoken(headers['Set-Cookie'].split(';')[0][6:])
def ptqrtoken(value): # 构造ptqrttoken的加密方式
i = 0
e = 0
n = len(value)
for i in range(n):
e = e + (e << 5)
e = e + ord(value[i])
return str(2147483647 & e)
&action
通过分析后得到,该参数内容为当前的时间戳乘以1000,并在首部加上字符串"0-0-",于是我们将获取参数 &action 的过程封装成一个函数
def getAction(): # 构造action参数
import time
return '0-0-' + str(int(time.time() * 1000))
然后我们现在回到接口 (ptqrlogin), 我们到现在已经获取了三个必须用到的参数,我们现在来构建函数 ifLogin() ,这个函数可以设置while循环,循环一次睡眠5秒,用来检测用户是否扫描成功以及是否登录成功
def ifLogin(ptqrtoken, loginSig):
url = "https://ssl.ptlogin2.qq.com/ptqrlogin?u1=https://qzs.qzone.qq.com/qzone/v5/loginsucc.html?para=izone&ptqrtoken=" + ptqrtoken + "&ptredirect=0&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=" + action() + "&js_ver=19112817&js_type=1&login_sig=" + loginSig + "&pt_uistyle=40&aid=549000912&daid=5&"
return requests.get(url).text[7:-1].replace("'", "").split(',')
下面是该接口 (ptqrlogin) 在不同场景下返回的数据
- 未扫描二维码时返回的数据
- 扫描二维码后返回的数据
- 二维码失效后返回的数据
- 扫描成功后返回的数据
然后分析后得到,我们通过去除返回文本的前七个字符和倒数第一个字符,然后将剩下的文本分割成数组
数组第一个项可以作为我们判断的依据
- 65 已失效
- 66 未失效
- 67 已扫描,但还未点击确认
- 0 已经点击确认,并登录成功
注: 登录成功后返回的数据中,数组的第三项为跳转链接,最后一项为QQ网名
待更新