前言

因为最近要写的项目里,刚好有一个是QQ扫码登陆的功能,本来曾研究过QQ的扫码登陆协议,但是发现,在这款APP上有h5sig和一个sign的加密标识,特此记录

首次请求

[GET方式]https://openmobile.qq.com/oauth2.0/m_authorize?cancel_display=1&sdkp=a&display=mobile&format=json&sign=aaa2f79fe600597ba0f63f90f79d6cae
&sdkv=3.5.11.lite&response_type=token&status_os=11&client_id=1105200115&switch=1&status_version=30&show_download_ui=true&pf=openmobile_android&scope=all&compat_v=1&status_machine=MEIZU+18+Pro&style=qr&time=1669700584&redirect_uri=auth%3A%2F%2Ftauth.qq.com%2F

在请求中,我们可以看到,这里面有个 sign=aaa2f79fe600597ba0f63f90f79d6cae,就很好奇,从哪里来的,抓包分析半天也没见,后面想着会不会是从app里计算的

public static String b(Context context, String str) {
        String str2 = "";
        SLog.v("openSDK_LOG.SystemUtils", "OpenUi, getSignValidString");
        try {
            String packageName = context.getPackageName();
            Signature[] signatureArr = context.getPackageManager().getPackageInfo(packageName, 64).signatures;
            MessageDigest messageDigest = MessageDigest.getInstance(MessageDigestAlgorithms.MD5);
            messageDigest.update(signatureArr[0].toByteArray());
            String a2 = m.a(messageDigest.digest());
            messageDigest.reset();
            SLog.v("openSDK_LOG.SystemUtils", "-->sign: " + a2);
            messageDigest.update(m.j(packageName + "_" + a2 + "_" + str + ""));
            str2 = m.a(messageDigest.digest());
            messageDigest.reset();
            StringBuilder sb = new StringBuilder();
            sb.append("-->signEncryped: ");
            sb.append(str2);
            SLog.v("openSDK_LOG.SystemUtils", sb.toString());
            return str2;
        } catch (Exception e) {
            SLog.e("openSDK_LOG.SystemUtils", "OpenUi, getSignValidString error", e);
            return str2;
        }
    }

果然,在Java里,找到了计算方法,从中我们可以知道,sign的加密过程是,app的packageName+"_"+signatures(MD5十六进制文本)+"_"+十位时间戳 最后计算md5值即可

GET方式请求之后在 Body 中,可以得到以下的内容

从中我们可以看到,他会访问 src 的地址,我们解码下,可以得到地址

[GET方式]https://xui.ptlogin2.qq.com/cgi-bin/xlogin?pt_enable_pwd=1&appid=716027609&pt_3rd_aid=1105200115&daid=381&pt_skey_valid=0&style=35&force_qr=1&autorefresh=1&s_url=http%3A%2F%2Fconnect.qq.com&refer_cgi=m_authorize&ucheck=1&fall_to_wv=1&status_os=11&redirect_uri=auth%3A%2F%2Ftauth.qq.com%2F&client_id=1105200115&pf=openmobile_android&response_type=token&scope=all&sdkp=a&sdkv=3.5.11.lite&sign=aaa2f79fe600597ba0f63f90f79d6cae&status_machine=MEIZU+18+Pro&switch=1&time=1669700584&show_download_ui=true&h5sig=F9Zpl6MYUBiuX8TG0UdxDSfRfYkWwl80OQThRCyR2ZE&loginty=6

GET方式请求之后在 Response Headers中,可以得到以下的内容

其中,我们需要把set-cookie全部取出来,之后有用,下面我称之为 全局变量_Cookie

获取二验证码图片

xlogin接口中,我们可以知道,二维码图片的一个链接

[GET请求]https://xui.ptlogin2.qq.com/ssl/ptqrshow?s=8&e=0&appid=716027609&type=0&t=0.11138907652141294&daid=381&pt_3rd_aid=1105200115

请求此处时需要带入 Cookie,至于Cookie,保存的 全局变量_Cookie全部带入即可,当然你也可以测试需要哪些关键 Cookie带入,我为了保险起见所以全部带入了。
返回的二进制内容保存为图片,这个就是第一次的验证码了。在此处需要将 Response Headers中的 qrsig保存记录下来,下面称为 全局变量_qrsig ,以便加密 ptqrtoken所调用。

加密ptqrtoken

上面的二维码码获取了,那么需要将 ptqrtoken进行加密,进行调用,首先得找到加密的JS函数。
根据关键字:ptqrtoken来寻找网页下的所有的JS文件。
寻找到了加密函数在下方JS文件中。

https://qq-web-legacy.cdn-go.cn/any.ptlogin2.qq.com/v1.36.0/ptlogin/js/login_10.js

将JS格式化,寻找关键的代码。发现,str.hash33 是关键方法

可以看到,生成的加密数据与请求中所请求的数据一致,可以确定此加密函数正确。最后编写JS的运行过程,以便于软件调用,此为易语言转换,其它语言请自行转换。

刷新二维码

[GET请求]https://xui.ptlogin2.qq.com/ssl/ptqrlogin?u1=http%3A%2F%2Fconnect.qq.com&from_ui=1&type=1&ptlang=2052&ptqrtoken=2099797035&daid=381&aid=716027609&pt_3rd_aid=1105200115&pt_openlogin_data=pt_enable_pwd%3D1%26appid%3D716027609%26pt_3rd_aid%3D1105200115%26daid%3D381%26pt_skey_valid%3D0%26style%3D35%26force_qr%3D1%26autorefresh%3D1%26s_url%3Dhttp%253A%252F%252Fconnect.qq.com%26refer_cgi%3Dm_authorize%26ucheck%3D1%26fall_to_wv%3D1%26status_os%3D11%26redirect_uri%3Dauth%253A%252F%252Ftauth.qq.com%252F%26client_id%3D1105200115%26pf%3Dopenmobile_android%26response_type%3Dtoken%26scope%3Dall%26sdkp%3Da%26sdkv%3D3.5.11.lite%26sign%3Daaa2f79fe600597ba0f63f90f79d6cae%26status_machine%3DMEIZU%2B18%2BPro%26switch%3D1%26time%3D1669700584%26show_download_ui%3Dtrue%26h5sig%3DF9Zpl6MYUBiuX8TG0UdxDSfRfYkWwl80OQThRCyR2ZE%26loginty%3D6%26pt_flex%3D1%26loginfrom%3D%26h5sig%3DF9Zpl6MYUBiuX8TG0UdxDSfRfYkWwl80OQThRCyR2ZE%26loginty%3D6%26&device=2&ptopt=1&pt_uistyle=35&jsver=v1.36.0&r=0.5454414205246301

注:链接中 ptqrtoken参数后面的 2099797035qrsig加密 ptqrtoken值,需要带入。其中还有一个pt_openlogin_data参数 pt_enable_pwd%3D1%26appid%3D716027609%26pt_3rd_aid%3D1105200115%26daid%3D381%26pt_skey_valid%3D0%26style%3D35%26force_qr%3D1%26autorefresh%3D1%26s_url%3Dhttp%253A%252F%252Fconnect.qq.com%26refer_cgi%3Dm_authorize%26ucheck%3D1%26fall_to_wv%3D1%26status_os%3D11%26redirect_uri%3Dauth%253A%252F%252Ftauth.qq.com%252F%26client_id%3D1105200115%26pf%3Dopenmobile_android%26response_type%3Dtoken%26scope%3Dall%26sdkp%3Da%26sdkv%3D3.5.11.lite%26sign%3Daaa2f79fe600597ba0f63f90f79d6cae%26status_machine%3DMEIZU%2B18%2BPro%26switch%3D1%26time%3D1669700584%26show_download_ui%3Dtrue%26h5sig%3DF9Zpl6MYUBiuX8TG0UdxDSfRfYkWwl80OQThRCyR2ZE%26loginty%3D6%26pt_flex%3D1%26loginfrom%3D%26h5sig%3DF9Zpl6MYUBiuX8TG0UdxDSfRfYkWwl80OQThRCyR2ZE%26loginty%3D6%26这段值也很好看出,是 xlogin接口中所发的请求数据,用易语言写出如下

携带请求头

Cookie: 全局变量_Cookie; qrsig=全局变量_qrsig;

刷新验证码请求中的 qrsig必须带入,不然显示不出来。
其它的全局变量在第一次请求时均已获取到,还有一个 全局变量_qrsig这个是在获取验证码图片中所得到的,可以看上文。

返回的数据

ptuiCB('66', '0', '', '0', '二维码未失效。', '')

扫码成功登陆之后返回的数据将会显示为

ptuiCB('0', '0', 'https://imgcache.qq.com/open/connect/widget/mobile/login/proxy.htm?#&t=1669866495#&openid=********&appid=1105200115&access_token=*****&pay_token=*********&key=*******&serial=&token_key=&browser=0&browser_error=0&status_os=11&sdkv=3.5.11.lite&status_machine=MEIZU+18+Pro&update_auth=&has_auth=&auth_time=1669866495&page_type=0&redirect_uri_key=*******', '0', '登录成功!', 'King丶枫岚')

最后登陆取 openid 和 access_token

模拟过程(特别注意)

在整合过程中,最后发现,从获取二维码开始,就需要带入 Referer 附加请求头,其值就是 xlogin 接口的链接地址,这在以前研究的时候,是不需要的。

模拟扫码登陆成功

最后修改:2022 年 12 月 01 日
如果觉得我的文章对你有用,请随意赞赏