QQ空间协议登录分析|账号密码等参数加密算法的获取以及分析
JS/爬虫逆向玩转qq空间协议保姆级教学—账号密码登录篇
从0实现qq空间账号密码登录协议,qrsig、login_sig、ptqrtoken、p等参数以及算法逆向还原
只要依次按照本文数据包的顺序保存更新好参数和Cookie,成功逆向加密算法之后,通过顺序进行GET请求即可实现QQ空间登录
文章结尾附qq空间协议登录实现代码
目录
一、逆向时间
逆向时间
逆向时间为2024-9-18 是最新的QQ空间协议逆向分析文章
二、抓包、参数分析
2.1数据包1—二维码包
直接GET请求所有参数固定 https://ssl.ptlogin2.qq.com/ptqrshow?appid=549000912&e=2&l=M&s=3&d=72&v=4&t=0.0035106983314870943&daid=5&pt_3rd_aid=0&u1=https://qzs.qq.com/qzone/v5/loginsucc.html?para=izone
二维码包Cookie会返回 qrsig 参数 这个参数是后续重要的参数
2.2数据包2—登录状态包
循环GET即可查询登录状态 https://ssl.ptlogin2.qq.com/ptqrlogin?u1=https://qzs.qq.com/qzone/v5/loginsucc.html?para=izone&ptqrtoken=1302498652&ptredirect=0&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=0-0-1726673826887&js_ver=24090913&js_type=1&login_sig=nJL8y1akA3VvL6a*Ow5YWDHTGeDc-bGa6kOKPOXxcAvnoyf11FRaQMmh6iuwImpQ&pt_uistyle=40&aid=549000912&daid=5&=&o1vId=2b82096ccefcae244d20335bbe82233c&pt_js_version=e8983a1a
这里的第二个参数0意味着在进行账号密码登陆时不需要验证码 这里重要的是这两个标红参数,其他固定即可,
ptqrtoken——用qrsig进行算法计算即可获得
login_sig——GET请求一次下方qq空间的主页网站,在响应的Cookie中可找到
同时还可得到Cookie:pt_clientip、pt_guid_sig、pt_local_token、pt_login_sig、pt_serverip、 pt_user_id、ptui_identifier、uikey
qq空间主页: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.qq.com/qzone/v5/loginsucc.html?para=izone&pt_qr_app=手机QQ空间&pt_qr_link=https://z.qzone.com/download.html&self_regurl=https://qzs.qq.com/qzone/v6/reg/index.html&pt_qr_help_link=https://z.qzone.com/download.html&pt_no_auth=0
2.3数据包3—验证码包
在输入账号密码后,可以抓到https://ssl.ptlogin2.qq.com/check这个包
GET请求 https://ssl.ptlogin2.qq.com/check?regmaster=&pt_tea=2&pt_vcode=1&uin=3592795816&appid=549000912&js_ver=24090913&js_type=1&login_sig=nJL8y1akA3VvL6a*Ow5YWDHTGeDc-bGa6kOKPOXxcAvnoyf11FRaQMmh6iuwImpQ&u1=https://qzs.qq.com/qzone/v5/loginsucc.html?para=izone&r=0.8028792184028488&pt_uistyle=40&daid=5&pt_3rd_aid=0&o1vId=2b82096ccefcae244d20335bbe82233c&pt_js_version=e8983a1a
响应结果ptui_checkVC('0','!AHD','\x00\x00\x00\x00\xd6\x25\xb6\xa8','d209e30814a3ed57d25582d156452fb6cc8b09549b7119366d6fdf1e7f51d80452e7e96375acf888f6ed439f61d2919dc848a8cbef2491ea','2','sj0KT*DVpTNcsfhRgPc2-yt2uXbB7fNBf81xpmiOLfX5YnV5hfuF8lZzETgNZ-B6blq8KxmJ1rs_','7073017071800088268')
在发送验证码包时,uin就是qq号,再从Cookie中读取login_sig即可
响应结果的参数如上所示,并且响应的Cookie中有以下参数confirmuin、ptdrvs、ptvfsession
ptdrvs——sj0KT*DVpTNcsfhRgPc2-yt2uXbB7fNBf81xpmiOLfX5YnV5hfuF8lZzETgNZ-B6blq8KxmJ1rs_
ptvfsession——d209e30814a3ed57d25582d156452fb6cc8b09549b7119366d6fdf1e7f51d80452e7e96375acf888f6ed439f61d2919dc848a8cbef2491ea
验证码:!AHD
uin2hex算法加密后的账号:\x00\x00\x00\x00\xd6\x25\xb6\xa8
2.4数据包4-登录包
https://ssl.ptlogin2.qq.com/login?u=3592795816&verifycode=!AHD&pt_vcode_v1=0&pt_verifysession_v1=d209e30814a3ed57d25582d156452fb6cc8b09549b7119366d6fdf1e7f51d80452e7e96375acf888f6ed439f61d2919dc848a8cbef2491ea&p=nzhWRFgmertBtuWg8UdNPAEP*1SvzBQUF*15oxH9lpmS3ezrwMD*fsXMsA5Z7*gtMrjc4YlvdAfQBsuS21ZhWDQBG6EVGEDmh0rl3D58Q8jQsw7aLZGfBSuvXYZUk*uynaf6Y8NRuCE1O1zRrIIDbmpaerkg*KrSKrJruJHaxfkYnsCnPJ5eiihFoNne0Wk6uHyv-fuDgY8KmbXBttHY8VE2CfRixzP0wxi*bs39aoQ8XR7CyzY9ILl6FXohv9MqOjM-dkQoySEQngZEp0shnjt1Or2*6KDRAaGCvwJiEFJJ10J82*jziPhEwlzPxOmVV0wmAcw48YKUL*PfFD-QGQ__&pt_randsalt=2&pt_ev_token=pg*GymwZf8Usl2nma7yn5-K*wGC2F7U7u3ghMabxYADisu74gpNpDiqQYXduQ3dh&u1=https://qzs.qq.com/qzone/v5/loginsucc.html?para=izone&ptredirect=0&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=2-9-1726675195974&js_ver=24090913&js_type=1&login_sig=nJL8y1akA3VvL6a*Ow5YWDHTGeDc-bGa6kOKPOXxcAvnoyf11FRaQMmh6iuwImpQ&pt_uistyle=40&aid=549000912&daid=5&ptdrvs=sj0KT*DVpTNcsfhRgPc2-yt2uXbB7fNBf81xpmiOLfX5YnV5hfuF8lZzETgNZ-B6blq8KxmJ1rs_&sid=7073017071800088268&=&o1vId=2b82096ccefcae244d20335bbe82233c&pt_js_version=e8983a1a
参数 pt_ev_token、login_sig、ptdrvs都可在前面几个数据包的响应Cookie中获取
u——QQ号
pt_verifysession_v1——GET验证码包后,Cookie中的ptvfsession就是pt_verifysession_v1
p——密码密文
verifycode——验证码包返回的验证码 !AHD
通过登录包分析可知,请求数据包1-2-3并且保存所有Cookie后,只有p参数还有ptqrtoken需要逆向算法计算获得
2.5数据包5—登录返回包
发送完整的登录包之后,在响应的内容中会出现一串 check URL 图片中重要参数已删减,只需要用之前的Cookie GET请求这个URL,即可成果登录QQ空间
三、算法逆向
3.1ptqrtoken算法
直接搜索ptqrtoken即可发现是通过hash33函数进行加密得到,参数就是qrsig
直接搜索hash33即可找到加密函数,函数参数t就是qrsig
3.2密码p算法
先在登录包处查看堆栈,在loadScript、submit、匿名处下断点
可以看到密码p的密文是通过getEncryption这个函数得到的,一共有4个参数 ,注意其中的E.salt
getEncryption函数 可以看到函数的参数
t——密码明文
e——E.salt
n——验证码包响应的验证码
o——空
可以看到E.salt是通过账号进行uin2hex算法得到的,登陆时输入邮箱的话会把@qq.com替换为空
getEncryption函数 e参数算法 参数str为账号明文
四、python代码实现
4.1ptqrtoken算法代码
def hash33(t): #参数:qrsig
e = 0
for n in range(len(t)):
e = (e << 5) + ord(t[n])
return e & 2147483647 # 返回ptqrtoken
这个加密算法较为简单,直接扣下来即可
4.2密码p算法代码
将p算法完整的js代码扣下来之后,使用python的subprocess库对js代码进行调用即可
使用args配合subprocess库,args[0]即为python代码传入的第一个参数,其他同理
vercode就是js代码中args[0]
测试结果: i267PnCDEUJb5h03dENIKxs8lQw7piqg*kUELiyl1L3s7QVsgk2LuM4HGtGJ0G0vc6q-u1aCHsdioDr4n9MQEeLx9WdtB5l4Y*hPMOUn5jQ5ygWyioefo1usP0kguKCzppaxBs0HCiMF0aI0DPSvxXtE8BPPaRaFV*h4rFdNNdeSs2AHF9STi44GUmwtUwxS0rb*MJ75InMO7bxgyP4d-o3mjgHO10mftu0EBJLl-WDn03LCWlhl3GCFyY3t0*gKMKMf*XULEjkA660vGOlB5Je*iLgivVQCWNEokL6JoIVjZp7IP1sy7h0IzAFX7Fy2fI2DKCsOjYKRLbQrq-HbDA__
密码算法正确性测试方法:写完前面所有数据包的话可以直接进行验证
也可以利用burp对登陆包进行拦截,拦截时使用假密码123456789
再用扣下来的代码生成真密码的密文,替换参数p,放行即可成功登录
4.3发包代码
def get_initial_cookies(): #数据包1
global global_cookies
# 发送 GET 请求到指定的 URL 来获取初始 Cookie
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.qqzone.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 = {
"Host": "xui.ptlogin2.qq.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Cache-Control": "max-age=0"
}
# 发起 GET 请求
response = requests.get(url, headers=headers)
if response.status_code == 200:
# 合并获取的 Cookie
global_cookies.update(response.cookies)
return global_cookies, headers
else:
print(f"请求失败,状态码:{response.status_code}")
return None, None
def get_qr_image_with_cookies(cookies, headers): #数据包2
global global_cookies
# 设置二维码请求的 URL
qr_url = "https://ssl.ptlogin2.qq.com/ptqrshow?appid=549000912&e=2&l=M&s=3&d=72&v=4&t=0.39175954831717164&daid=5&pt_3rd_aid=0&u1=https://qzs.qzone.qq.com/qzone/v5/loginsucc.html?para=izone"
# 为二维码请求设置新的请求头
headers.update({
"Host": "ssl.ptlogin2.qq.com",
"Accept": "image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5",
"Referer": "https://xui.ptlogin2.qq.com/",
"Sec-Fetch-Dest": "image",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-site",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
})
response = requests.get(qr_url, headers=headers, cookies=cookies)
# 检查请求是否成功
if response.status_code == 200:
# 保存二维码图片到本地
with open("qrcode_with_new_cookie.png", "wb") as f:
f.write(response.content)
print("二维码已成功保存为 'qrcode_with_new_cookie.png'")
# 将新获取的 Cookie
global_cookies.update(response.cookies)
else:
print(f"二维码请求失败,状态码:{response.status_code}")
def get_vercode(cookies, headers,qquin,loginsig): #数据包3
global global_cookies
global global_code
global global_sid
qr_url = "https://ssl.ptlogin2.qq.com/check?regmaster=&pt_tea=2&pt_vcode=1&uin="+qquin+"&appid=549000912&js_ver=24090913&js_type=1&login_sig="+loginsig+"&u1=https://qzs.qzone.qq.com/qzone/v5/loginsucc.html?para=izone&r=0.48032086977232935&pt_uistyle=40&daid=5&pt_3rd_aid=0&o1vId=989ddff17a71e99927948b1101c6592d&pt_js_version=e8983a1a"
#设置新的请求头
headers.update({
"Host": "ssl.ptlogin2.qq.com",
"Accept": "image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5",
"Referer": "https://xui.ptlogin2.qq.com/",
"Sec-Fetch-Dest": "image",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-site",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
})
response = requests.get(qr_url, headers=headers, cookies=cookies)
# 检查请求是否成功
if response.status_code == 200:
# 打印新获取的 Cookie
content = response.content.decode('utf-8', errors='ignore')
global_code=content.split(',')[1][1:-1] #验证码
global_sid=content.split(',')[6][1:-2] #sid
global_cookies.update(response.cookies)
else:
print(f"请求失败,状态码:{response.status_code}")
def login(cookies, headers,verifycode,v1,p,login_sig,ptdrvs,sid): #数据包4
p=p.replace("\n",'')
global global_cookies
print('-----------------开始登录-----------------')
qr_url = "https://ssl.ptlogin2.qq.com/login?u="+global_cookies['ptui_loginuin']+"&verifycode="+verifycode+"&pt_vcode_v1=0&pt_verifysession_v1="+v1+"&p="+p+"&pt_randsalt=2&u1=https://qzs.qzone.qq.com/qzone/v5/loginsucc.html?para=izone&ptredirect=0&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=2-9-1726375321935&js_ver=24090913&js_type=1&login_sig="+login_sig+"&pt_uistyle=40&aid=549000912&daid=5&ptdrvs="+ptdrvs+"&sid="+sid+"&=&o1vId=989ddff17a71e99927948b1101c6592d&pt_js_version=e8983a1a"
print('loginurl',qr_url)
#设置新的请求头
headers.update({
"Host":"ssl.ptlogin2.qq.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0",
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Sec-GPC": "1",
"Connection": "keep-alive",
"Referer": "https://xui.ptlogin2.qq.com/",
"Sec-Fetch-Dest": "script",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-site",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
})
response = requests.get(qr_url, headers=headers, cookies=cookies)
# 检查请求是否成功
if response.status_code == 200:
# 打印新获取的 Cookie
content = response.content.decode('utf-8', errors='ignore')
print(content)
ckurl = content.split(',')[2][1:-1]
state = content.split(',')[4][1:-2]
name=content.split(',')[5][1:-2]
print(ckurl,state)
# 将获取到最终Cookie
global_cookies.update(response.cookies)
login_finish(global_cookies,headers,ckurl,state,name)
else:
print(f"请求失败,状态码:{response.status_code}")
在数据包4的响应中,会返回一个HTTPS的URL,只要利用Cookie使用GET请求访问一下URL即可成功登录
def login_finish(cookies, headers,url,state,name): #最终登录
global global_cookies
print('-----------------登录-----------------')
print(state, name)
qr_url = url
print(qr_url)
# 设置新的请求头
headers.update({
"Host": "ptlogin2.qzone.qq.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Sec-GPC": "1",
"Connection": "keep-alive",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
"Priority": "u=0,i",
"Upgrade-Insecure-Requests": "1"
})
response = requests.get(qr_url, headers=headers, cookies=cookies, allow_redirects=False)
print(response.content)
# 检查请求是否成功
if response.status_code == 302:
# 打印新获取的 Cookie
content = response.content.decode('utf-8')
print(content)
global_cookies.update(response.cookies)
print("登录成功--------->最终 Cookie:")
for cookie in global_cookies:
print(f"{cookie.name}: {cookie.value}")
else:
print(f"请求失败,状态码:{response.status_code}")
print(state, name, "成功登录")