TL;DR. 本地搭建一个转发 http 与 https 请求到 https 的代理服务器,并签发一个
*.qq.com
与*.vip.qq.com
的自签证书并信任,然后本地修改 hosts 将proxy.vip.qq.com
与verify.qq.com
指向代理服务器即可
本着「又不是不能用」的原则,本地的软件基本不保持更新,所以本地 QQ 也保持在了 8.9.3 版本。
结果某一天作死开了个 QQ SVIP,再次登录时 QQ 提示需要输入独立密码同步信息,结果输入密码后点击确认,没有任何反应。
于是接下来就是喜闻乐见的作死环节了,为了尝试恢复同步消息,在 QQ 的设置里将「同步最近消息到本地」的勾选框取消了。结果重新勾选的时候,弹出了需要输入独立密码,同样无法输入,甚至还无法改变聊天记录同步时间,提示服务器超时。
估计是 API 服务器不兼容旧版 QQ 了,于是在虚拟机上装了个最新的 QQ,结果大片的空白和间距感觉要吐了,老年人审美决定还是不升级了,于是问题回到了怎么折腾 8.x 上的独立密码的问题,唯一的收获是在新版 QQ 上是没问题的。顺带查了下版本号才发现原来 8.9.6 就是 8 的最后一个版本。
首先先启动 Wireshark 抓包,筛选条件过滤 http,点击 QQ 消息的设置按钮,结果还真发现了一个域名为 proxy.vip.qq.com
的 http 请求。追踪 tcp 流,发现这个请求最终 301 到了 https 的链接上。
然而以目标 IP 为搜索过滤条件时,却并没有找到对应的 TLS 连接,怀疑是不是浏览器的版本过老,没有自动进行重定向,导致获取数据失败。
仔细观察了下 HTTP 报文,发现 Chrome/29.0.1547.59
这个 User-Agent 有点诡异,这也太老了吧。既然是 Chrome 的 UA,那估计是用了 CEF,结果果然在 Tencent\QQ\Bin
目录下发现了一个 libcef3.dll
的文件……饿,虽然印证了猜想,但是为什么要数 3 呢……
虽然考虑到 libcef 有可能是自己修改了源码并编译的,不过死马当活马医,从 Spotify 上下载了 预编译的 libcef,然后解压文件,将 libcef.dll
重命名为了 libcef3.dll
,重启 QQ,结果果不其然,QQ 崩溃了(顺带还发现原来聊天记录的展示页也是通过 CEF 展示的……万物皆可 webapp)
既然开源的构建版本不能用,那就从新版 QQ 里找一个覆盖一下,估计同一套接口兼容性会好很多。结果在最新的 QQ 里,libcef 没了,取而代之的是一个叫 qbcore.dll
的玩意……好嘛,看到文件名相似的 .pak
文件,其实还是 CEF 魔改的。于是尝试把相关文件直接覆盖 libcef3.dll
,结果果不其然还是崩溃了。既然新版本不行,那就尽可能找个最接近的版本号呗,结果 8.9.6 也是 qbcore.dll
……感情没赶上重构的末班车
既然魔改内核失败了,那就只能想想其他法子了。既然 QQ 不会自动跳转,那思路就变成了搭一个 MITM 服务器,接收 http 请求,然后向上游服务器直接发起 https 请求,再把数据吐给之前的 http 请求,问题不就解决了?于是接下来照着这个思路在树莓派上写了一份 nginx 配置。饿,至于为什么不在本地写?因为本地 80 端口被本地的 Apache 占用了,所以直接在树莓派现成的 nginx 上改了。
1 2 3 4 5 6 7 8 9 10 |
server { listen 80; server_name proxy.vip.qq.com; location / { proxy_pass https://220.249.244.31; proxy_set_header Host proxy.vip.qq.com; } } |
接下来修改 hosts 将 proxy.vip.qq.com
指向树莓派的 IP,重启 QQ 以清空 DNS 缓存,打开消息记录里的「设置」,成功加载出了消息设置。
但是……输入独立密码,还是无法点击确定。
期间折腾了半天,找不到原因,甚至还试了开启设备锁,结果设备锁没用,还导致手机 QQ 不能再使用独立密码验证了,只能通过设备锁验证,吐了。
最后再仔细观察了下请求的报文,结果发现「管理独立密码」这个弹窗里同时还有一个 verify.qq.com
的请求……再一看一开始的报文,发现当时的请求里就在 Origin
这个 header 里指明了 verify.qq.com
这个域名,表明这个请求是从这个域名的页面上请求的。结果反而折腾了这么久,原来一开始就留下线索了……
于是将 verify.qq.com
写入 nginx 配置并修改 hosts,重启 QQ 重试。然而……事与愿违,点击确认按钮还是不通过。再根据 IP 查请求上下文,发现这个域名其实可能是有 https 连接的,但是代理服务器只支持 http 连接,导致验证不通过。这……一会 http 一会 https 的,为啥不一开始就统一啊 kora
最后签了一个 *.qq.com
与 *.vip.qq.com
的自签证书,然后将证书放在受信任的 CA 证书存储下,再在 nginx 上调整配置如下以接受 https 链接,并自适应转发到对应的 upstream。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
server { listen 80 default_server; listen 443 ssl default_server; server_name _; ssl_certificate /usr/share/nginx/ssl/qq.crt; ssl_certificate_key /usr/share/nginx/ssl/qq.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; ssl_prefer_server_ciphers on; resolver 119.29.29.29; set $backend https://$host; location / { proxy_pass $backend; proxy_set_header Host $host; } } |
重启 QQ,问题成功解决,设置里的同步聊天记录的复选框也可以勾选了,输入独立密码点击确认,消息成功同步到本地。
垃圾 QQ 净折腾人,开了会员还不省心,下个月不开了