PHP静默获取当前QQ所有群信息 |
您所在的位置:网站首页 › 获取qq群key › PHP静默获取当前QQ所有群信息 |
在未来的人工智能(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 |