PHP静默获取当前QQ所有群信息

您所在的位置:网站首页 获取qq群key PHP静默获取当前QQ所有群信息

PHP静默获取当前QQ所有群信息

2024-03-04 13:19| 来源: 网络整理| 查看: 265

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

初级PHP工程师决战千人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]

Ⅲ、最终幻想

//①获得pt_local_token也叫pt_local_tk$token = getToken();//②获得当前登录QQ号$uin = pt_get_uins($token);//③获得clientkey$clientkey = pt_get_st($uin,$token);//④获得QQ登录成功后的skey和richURL$arr = jump($token,$uin,$clientkey);$skey = $arr['skey'];$richURL = $arr['url'];//⑤获得QQ群页面登录成功后的p_skey$p_skey =getPSkey($richURL,$skey);//⑥凭借相关参数获取当前QQ的所在群列表数据$bkn = getBkn($skey);$Cookie = "Cookie: p_skey={$p_skey};uin=o{$uin} ;skey={$skey}";$json = getGroupList($bkn,$Cookie); [源码下载]-一个.php文件无需任何配置直接运行https://github.com/nasaplayer/getCurrentQQGroupList

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

[程序运行效果-gif]

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

①服务器求赐一个pt_local_token

$token = getToken();

getToken向

目标页面:https://xui.ptlogin2.qq.com/cgi-bin/xlogin?appid=715030901&daid=73&hide_close_icon=1&pt_no_auth=1&s_url=https%3A%2F%2Fqun.qq.com%2Fmember.html%23

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

getToken响应头HTTP/1.1 200 OKDate: Sun, 15 Apr 2018 12:50:46 GMTContent-Type: text/htmlContent-Length: 34759Connection: keep-aliveServer: QZHTTP-2.38.41P3P: CP="CAO PSA OUR"Cache-Control: max-age=86400Set-Cookie: pt_user_id=5197250212595915679; EXPIRES=Wed, 12-Apr-2028 12:50:47 GMT; PATH=/; DOMAIN=ui.ptlogin2.qq.com;Set-Cookie: pt_login_sig=2vLVdRAlGxcNvBYEjB5E*JjIE0u0-n21s0ouAQOeQ*bgo7Fkd6Cw3O9DrNS9l7C-; PATH=/; DOMAIN=ptlogin2.qq.com;Set-Cookie: pt_clientip=1be91b13d8b8cf14; PATH=/; DOMAIN=ptlogin2.qq.com;Set-Cookie: pt_serverip=39490abf0e2ff9a8; PATH=/; DOMAIN=ptlogin2.qq.com;Set-Cookie: pt_local_token=1798081340; PATH=/; DOMAIN=ptlogin2.qq.com;Set-Cookie: uikey=b9f012f44bdf628d965a537cd1049fc75f538501135f7f2356b48c0a1e0e8be3; PATH=/; DOMAIN=ptlogin2.qq.com;Set-Cookie: pt_guid_sig=3239abfc37c8eea7b67a560e64664e9929011985be910feb6c9836bcac5c177a; EXPIRES=Tue, 15-May-2018 12:50:47 GMT; PATH=/; DOMAIN=ptlogin2.qq.com;Set-Cookie: ptui_identifier=000D9533813A7A9BEAE8DDB4A01B1C9FA96BB5F524F1F52858C67059; PATH=/; DOMAIN=ui.ptlogin2.qq.com;Last-Modified: Thu, 08 Mar 2012 02:04:00 GMTStrict-Transport-Security: max-age=31536000

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

②问问小企鹅主人的信息

$uin = pt_get_uins($token);

这条语句向

https://localhost.ptlogin2.qq.com:4301/pt_get_uins?callback=ptui_getuins_CB&r=0.0760575656488639&pt_local_tk={$token}

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

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

pt_get_uins响应头HTTP/1.1 200 OKContent-Type: Application/javascriptContent-Length: 198pt_get_uins响应体var var_sso_uin_list=[{"account":"2919386060","client_type":65793,"face_index":735,"gender":1,"nickname":"百万强心剂","uin":"2919386060","uin_flag":58720768}];ptui_getuins_CB(var_sso_uin_l

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

preg_match("/var_sso_uin_list=(.*?);ptui_getuins_CB/", $body, $matches); $json =$matches[1];$jsonObj = json_decode($json, false);$user = $jsonObj[0];//使用第一个QQ快速登录$uin = $user->uin;

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

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

③再问问小企鹅clientkey

$clientkey = pt_get_st($uin,$token);

https://localhost.ptlogin2.qq.com:4301/pt_get_st?clientuin={$uin}&callback=ptui_getst_CB&r=0.4266647630782271&pt_local_tk={$token}

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

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

pt_get_st响应头HTTP/1.1 200 OKContent-Type: Application/javascriptContent-Length: 76Set-Cookie: clientuin=2919386060;path=/;domain=.ptlogin2.qq.comSet-Cookie: clientkey=00015AD353D5006867DD30727AD69245B5A090104A9B93A6D807D31DF79CE0E27049042682F8A107BD2E2BD40E5CCA6234F4C81FC1376F1FAD7CBC6209A45DE359150100E066229559C8058877F25827ECC4F43DB486DCDED8C7679C47944A6FA363E6B9612F1FA65812D7DEE8C86013;path=/;domain=.ptlogin2.qq.comP3P: CP="CAO PSA OUR"pt_get_st响应体var var_sso_get_st_uin={uin:"2919386060"};ptui_getst_CB(var_sso_get_st_uin);

④服务器这次可通过了吧

$arr = jump($token,$uin,$clientkey);$skey = $arr['skey'];$richURL = $arr['url'];

https://ssl.ptlogin2.qq.com/jump?clientuin={$uin}&keyindex=9&pt_aid=715030901&daid=73&u1=https%3A%2F%2Fqun.qq.com%2Fmember.html%23&pt_local_tk={$token}&pt_3rd_aid=0&ptopt=1&style=40

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

jump响应头HTTP/1.1 200 OKDate: Sun, 15 Apr 2018 14:36:44 GMTContent-Type: application/javascriptContent-Length: 453Connection: keep-aliveCache-Control: no-cache, no-store, must-revalidateExpires: -1P3P: CP=CAO PSA OURPragma: no-cacheServer: Tencent Login Server/2.0.0Strict-Transport-Security: max-age=31536000Set-Cookie: ...;Set-Cookie: skey=@wkzS7Ao3s;Path=/;Domain=qq.com;Set-Cookie: ...;jump响应体ptui_qlogin_CB('0', 'https://ptlogin2.qun.qq.com/check_sig?pttype=2&uin=2919386060&service=jump&nodirect=0&ptsigx=103d55c16e79adbdc924a24a7213a596273f36caa9564bd5466a4a4f6d3335aa84a27b285624906610d659180e0eaf850c622541f67b286babab32424274f7fb&s_url=https%3A%2F%2Fqun.qq.com%2Fmember.html&f_url=&ptlang=2052&ptredirect=100&aid=1000101&daid=73&j_later=0&low_login_hour=0®master=0&pt_login_type=2&pt_aid=715030901&pt_aaid=0&pt_light=0&pt_3rd_aid=0', '')

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

ptui_qlogin_CB('-1', 'https://qun.qq.com/member.html', '登录失败,请稍后再试。*')

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

⑤找服务器获取p_skey

$p_skey =getPSkey($richURL,$skey);

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

getPSkey响应头HTTP/1.1 302 FoundDate: Mon, 16 Apr 2018 01:54:23 GMTContent-Length: 0Connection: keep-aliveCache-Control: no-cache, no-store, must-revalidateExpires: -1Location: https://qun.qq.com/member.htmlP3P: CP=CAO PSA OURPragma: no-cacheServer: Tencent Login Server/2.0.0Strict-Transport-Security: max-age=31536000Set-Cookie: ...;Set-Cookie: p_skey=2GbDJky6IQHGAaks4oNWU5D*uWaDNzoubXh9-hBRC8A_;Path=/;Domain=qun.qq.com;Set-Cookie: ...;

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

$rule = "/p_skey=(.*?);/";preg_match($rule, $header, $matches); $p_skey=$matches[1];

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

⑥数据接口随便用

$bkn = getBkn($skey);$Cookie = "Cookie: p_skey={$p_skey};uin=o{$uin} ;skey={$skey}";$json = getGroupList($bkn,$Cookie);

函数getGroupList向

https://qun.qq.com/cgi-bin/qun_mgr/get_group_list

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

//JavaScriptQZONE.FrontPage.getACSRFToken = function () {          var skey = QZFL.cookie.get("p_skey") || QZFL.cookie.get("skey") || QZFL.cookie.get("rv2");          return arguments.callee._DJB(skey)      };  QZONE.FrontPage.getACSRFToken._DJB = function (skey) {          var hash = 5381;          for (var i = 0, len = skey.length; i < len; ++i)              hash += (hash


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3