初级PHP工程师决战千人QQ群成员信息获取(二)

在未来的人工智能(AI)时代,或许没有人知道使用电脑的是人还是电脑本身。看完这篇,只要你的QQ在线,与之相关的很多信息都可以让程序自动帮你获取,不用打开任何页面,那就把这个称作静默获取吧。

在上一篇中,通过鼠标点击人工完成了登录操作,复制产生的bkn和Cookie,使得程序能够直接抵达登录后的页面,获取所在群所有成员信息。但总是需要人为设置一番,让程序沦为一个半自动的作品。

[获取请求头信息-gif]

故事的开端

能够拿到数据对于初级小白已经足够酷了,然而工程师又怎么甘心止步于此——我连鼠标都不想点,于是开始全面着手自动化。

找虐?谁说的。。。

在反反复复的人工登录复制Cookie的操作中,工程师牛牛捕捉到了灵感,开始在心里寻思:如果是登录点的那一下,程序也是可以的;对于服务器,它无法判断这个操作是人还是机器执行的;那https的协议会不会阻碍程序获得身份授权?答案是不会,https只是保证了数据传输的安全,但程序可以完全等价于客户端操作,模仿源头上发起请求,理论上和https的请求方式毫无关系——箭似乎已在弦上。

出于对新人的培养考虑,老牛还是想问问小白有什么解决办法。小白思考了几分钟后喜形于色,就快要蹦起来了。

小白:这还不简单,我们用CURL抓到登录页面,再写一些JavaScript让前端模拟鼠标点击,再捕捉跳转不就行了。

( 这小子啊又在轻敌,简单挂嘴边要他自己实现就痛苦啰。老牛心里这么想,觉得还是要以鼓励为主,毕竟这个行业是需要一些信心,现在CHN百分90%的码农都没活过30岁,时不时产品发布还要杀一两个程序员祭天...)

老牛:你这个想法挺有创意的,但可能会遇到两个难点。一个是,curl抓取回页面不能产生完整的文档dom结构,自定义js很可能不能执行;还有一个是,即使js能够模拟跳转,但页面自刷新产生的内容,可能也在一个大的冲突域,这些都会是很棘手的问题。

小白:那我还要按这个思路试试吗。

老牛:试,一定要试。但现在呢,先在我电脑上登一下QQ。

接下来的一瞬间操作让小白张大了嘴巴,原来老牛已经实现了自动登录取数据,看似只是少一个点击操作,这其中的曲折艰难只有老牛自己清楚。此刻小白乖乖听老牛的话,搬起小板凳在一旁静静地聆听。老牛预料到这次解说会异常费神,提前让小白去买了一罐红牛,这家伙——弄了一箱。

Ⅰ、迷雾消散

为什么在页面上能获取当前正在运行QQ头像。一个F5刷新重现了首次访问的情景。借助浏览器F12的观察,我们从交杂的HTTP请求中寻找接近真相的蛛丝马迹——桌面QQ应用亦是一个本地服务器。

Ⅱ、战备知识点

1、HTTP协议。请求、响应,Cookie和Set-Cookie的区别,状态码200、302等

2、Fiddler。HTTP协议代理工具,用于捕获登录全过程中的请求、响应便于分析。

3、浏览器F12开发者调试。

[Fidder查看完整登录流程-gif]

Ⅲ、最终幻想

上面这段代码包含了我们所有的主流程,确实就这么几行代码。还请留意参数传递,核心的逻辑就在其中,有缘人一眼应该能懂。其中①②③④⑤⑥,粗略认为每一部分的函数都在发起一个HTTP请求(这和你用浏览器打开一个网页一个样),它们之间的差异在于参数不一样。

[程序运行效果-gif]

在程序中,已经将所有的HTTP响应结果都打印在页面上了。一顿操作猛如虎,不识原理真面目,下面我们需要逐步解说。

①服务器求赐一个pt_local_token

getToken向

发起请求,其中通过参数s_url=https://qun.qq.com/member.html#,告诉服务器如果登录成功后请去往s_url这个页面。服务器心想这谁啊陌生人,先给它一个pt_local_token备注下,再通过响应头返回一堆Set-Cookie告诉这个来访者(客户端),你把这些先存好,其中就包括了pt_local_token。想从我这过关还需要uin、clientkey(=、=陌生人怎么会知道这些,当然是Fiddler寻寻觅觅,站在全局才能够纵观内在)。

发现上面的Set-Cookie:pt_local_token了吗?

②问问小企鹅主人的信息

这条语句向

发起请求,这个请求发向了桌面QQ应用(监听端口4301的服务器),小企鹅,主人让我到qun.qq.com,被那可恶服务器拦下来了,把我当陌生人。我需要主人的QQ号等一些基本信息,这是服务器给我的token。

小企鹅(仔细核对):token给我,我确认下正在登陆的号码。好的,这些都办好了。小企鹅留下了一堆字符串,其中响应体包含了当前QQ号的基本信息。

响应体就是一串字符串,将JSON格式的QQ基本信息赋值,PHP用正则获取字参数吧。

现在通过①②两步,获取了token和当前桌面登录的QQ号信息。其实我们所做的工作等同于在浏览器直接访问①的地址,这种情况适用于任何需要QQ登录的地方。可以看到界面只是刷新了一下,背后却有这么多繁杂的请求关系。目前顺利抵达这里,Next。

[经过①②实际的效果-gif]

③再问问小企鹅clientkey

发出请求,这个请求仍然发向了桌面QQ应用,注意到接口路径不一样。小企鹅兄弟,有了token和uin,还不能向服务器证明我的通行是经过主人授权的,还需要你和服务器协定密钥规则,再根据token和uin再产生一个clientkey,悄悄告诉我就好。

小企鹅:好的好的,token+uin,马上去办clientkey。(数毫秒之间)办好了,给你Set-Cookie:clientkey,你自己记一下啊

④服务器这次可通过了吧

发出请求,有了toekn、uin、clientkey那就是自己人了,服务器很热情的准许通过,又强行Set-Cookie塞了十几项。skey一定要拿好了,这就是你下一站通过文书。

响应头中省略了其他的Set-Cookie,skey的意思是qq.com域下的登录授权密钥;响应体中的0表示登录成功,后面的长url是即将跳转的地址带了认证的识别码和其他参数,专业术语叫胖URL(后面命名richURL)。登录失败响应体返回

设想“皇城”有两层关卡,现在我们通过第一关。取得了QQ域的登录授权skey,第二关就是QQ子应用的登录授权,在QQ群管理页面中,还需要一个p_skey。

⑤找服务器获取p_skey

向第④中获得的url地址发出请求,返回

服务器告诉我们Set-Cookie:p_skey,用正则从中获取p_skey

这样我们就获得了QQ群管理页的授权,现在所有确认身份的key我们都拿到了,猜猜我们的程序来到了哪里,顺利抵达QQ群管理页面。下一步,通过key调用接口获取想要的数据。

⑥数据接口随便用

函数getGroupList向

发出请求,返回内容为当前QQ所在群列表的数据。等等,除了Cookie,这里还发出了一个bkn参数(10位数字)。服务器告诉了skey、p_skey后就断开了联系,那bkn一定是客户端计算得出的,通过一些资料的参考,发现是在客户端通过hash算法加密得出,输入参数是skey

bkn算法将skey中每一个字符的字符的 Unicode 编码,与当次hash值向左移5位加和累加,最后将hash值与2147483647做逻辑与运算,得出bkn。js版的没法直接用的,这里用PHP重写

所以对于获取QQ群的列表接口get_group_list,只要有skey就可以算出bkn参数,再结合p_skey,就大功告成了。对于获取每个群详细成员信息的接口search_group_members同理。

我们的功能到这里全部完成。

Ⅳ、回顾与总结

以上6步,简述为:

访问→获得授权1→获得授权2→获取数据

通过访问任何一个需要QQ快捷登录的页面,获取服务器token(pt_local_token),与当前登陆的QQ号的uin一起,拿到了clientkey。clientkey是在点击头像之后登录之前获取的。通过token、uin、clientkey成功登录QQ获得域的授权,通过授权之后的url、skey获得子应用的登录授权p_skey。这里按理说本可以调用任意开放接口,但在QQ群管理页面还需要一个bkn(base_key)参数,我们发现它是通过传入skey参数通过位操作生成。最终,获得我们想要的数据。

在这个曲折的过程中,很多参数是在重复用。个人理解为一种渐进增强的加密方式,出于安全的考虑,去验证确保访问者身份。可以想象为一座具有内外两层防护关卡的“皇城”,这很像一个"回"字形,获得了第一层授权进了城,子应用接口在第二层内,最终调用时需要贯穿全城的授权。即使你拿到了内层p_skey,还是不行,要确保是通过了外层skey进入的,额外还要通过参数bkn质询。实际原理是怎样的呢?这始终会有一些悬念,毕竟我们做的所有工作仍然是基于黑盒测试的,十有八九。

Ⅴ、源码下载

我的github第一个项目

Ⅵ、参考资料

Ⅶ、扩展资料

绘制了登录过程中每个地址的服务器响应Set-Cookie相关设置,编号与上文程序编号是完全对应的,绿色背景表示Cookie所处的域名,红色高亮提示了核心参数。极有可能其他QQ子应用也是类似原理。

Ⅷ、数据放进Excel

对于工程师,看到数据显示就等同于进EXCEL了。对于直接用PHP生成EXCEL文件,现在最新的轮子应该是PHPSpreadsheets,作者也在github提到了这就是下一版的phpEXCEL。并说道,那个旧的我们不更新了,为什么不尝试最新PHP特性的的呢。很对,PHP开发者需要跟随时代革新

完。

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20180416G1QEHZ00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券