|
这个是工程的大体构架,其中带点的是我们需要编写的:
打开我的源文件(需要VS2010或者VB2010 Express,VB2010Express不能看到WebQQ的加密算法和被封装的Http访问函数的部分,不过没关系,不影响理解算法)
HTTP:这是包含Http访问的C#代码,来源于互联网,感谢作者,Http访问的部分我稍微修改了一下,修复了丢Cookie的Bug
QQ_Encypt:QQ最新的加密算法,来自互联网
Platform_Test:这个是主程序,为控制台界面(我比较懒),为AI模块提供平台,对AI模块提供发送信息的方法和收到信息的事件等功能。
Rin:这个是AI模块,模拟镜音リン,就是我的头像上那只。这部分主要是识别人话,然后根据数据库内容回复,同时具备学习能力。
QQ_Encypt_Test:这个是个演示程序,实际不包含在解决方案里,就是演示QQ登陆时需要的秘文的计算。
由于抓包过程就像抓带有放射性物质的小强一样,这里省略,只分享结果!
好了,下面介绍WebQQ的各种接口,大家认真看啦!
注意:Http操作会返回cookie,应妥善收藏:
Public cookies As New Net.CookieCollection
这些cookie用于在每一次Http操作时验证此次操作的计算机是否和上次是一台、
以后每次提交Http操作的时候就把这些cookie都附带上就好啦
第一步、搞定验证码
首先确定登陆时是否需要验证码,用Get访问下
http://ptlogin2.qq.com/check?uin=qq号&appid=1003903&r=0.23301555978693034
其中uin是qq号,appid是应用程序编号,不可以变,据我观察r是个随机数,变不变问题都不大,为了简单干脆不变,用rnd()函数产生也行,这一步注意收集cookie实例代码:
With Http.HttpWebResponseUtility.CreateGetHttpResponse("http://ptlogin2.qq.com/check?" + "uin=" + QQ_Number + "&appid=1003903&r=0.23301555978693034", -1, "", cookies)
Verify_Code = New System.IO.StreamReader(.GetResponseStream()).ReadToEnd
cookies.Add(.Cookies)
End With
Http.HttpWebResponseUtility.CreateGetHttpResponse就是之前说的C#写的Http访问方法,这个进行Get,用法很简单,参数分别是访问地址、超时、模拟的浏览器(一般写""就好)和要提交的Cookie
如果返回的是类似ptui_checkVC('0','!OSC','\x00\x00\x00\x00\x5c\xa4\x73\x8a');的字符串,就说明不需要输入验证码,根据第一个参数来看,0表示不需要,第二个参数是验证码,会以!开头,需要记住(记作Verify_Code),第三个东西没用,把第三个家伙里面的\x去掉,再用计算器从十六进制转成十进制,你会看到一个熟悉的数字。。。如果第一个参数是1,则说明需要验证码,第二个参数要用来获取验证码,设法下载“http://captcha.qq.com/getimage?aid=1003903&&uin=qq号&vc_type=第二个参数” 这个地址为一个图片(可用用My.Computer.Network.DownloadFile下载),然后让用户手动输入里面的验证码,然后记下来(同样是Verify_Code变量)。如果谁能实现验证码识别就可以实现完全自动了~
第二步、连接服务器
计算密文:Encrypt_Code = QQ.QQMd5.Encrypt(QQ_Number, QQ_Password, Verify_Code)
使用GET来访问这个地址:http://ptlogin2.qq.com/login?u=qq号码&p=计算出来的密文&verifycode=验证码&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=2-17-7145&mibao_css=m_webqq&t=1&g=1,注意不要搞错,后面那长串不要搞错!附带cookie!一定要!这次的cookie要返回一个ptwebqq的值,要记住,登陆时需要用。
如果一切没有错会返回类似:
ptuiCB('0','0','http://web2.qq.com/loginproxy.html?login2qq=1&webqq_type=10','0','登录成功!', '无敌高氯酸');
如果计算的密文算法不对,会出现这个:
ptuiCB('7','0','','0','很遗憾,网络连接出现异常,请您稍后再试。(3064988208)', '1554281354');
对于成功那个,最后会返回用户昵称(这里是无敌高氯酸),前面一个参数是登陆信息,比如成功登陆,再前面一个没用,是给webqq指示要跳转的页面。
到此为止,我们可以简单测试下,打开IE浏览器,输入第一步的网址,会返回一个check文件,打开就是结果,记下验证码,如果需要就获取图片。打开我的那个工程,把QQ_Encypt_Test设为启动项目,运行了输入相关信息,计算出密文,然后用这些信息结合第二部的地址输到刚才那个IE页面的地址栏里然后导航,会下载一个login文件,打开后就是结果,如果看到登陆成功的字样,那就说明成功了。怎么样?不难吧。有人可能会问,我们在IE里没有啥保存cookie的操作,为啥能成功?因为IE会自动保存cookie然后每一次全部提交。
本段参考代码:
Public Function Connect() As Boolean
Encrypt_Code = QQ.QQMd5.Encrypt(QQ_Number, QQ_Password, Verify_Code) 'Calculate the weird encrypt code
With Http.HttpWebResponseUtility.CreateGetHttpResponse("http://ptlogin2.qq.com/login?u=" + QQ_Number + "&p=" + Encrypt_Code + "&verifycode=" + Verify_Code + "&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=2-17-7145&mibao_css=m_webqq&t=1&g=1", -1, "", Cookies)
Cookies.Add(.Cookies)
UserName = New System.IO.StreamReader(.GetResponseStream()).ReadToEnd()
If UserName.StartsWith("ptuiCB('0") Then
UserName = UserName.Split(",")(5).Replace(");", "").Replace("'", "").Trim()
ptwebqq = Cookies("ptwebqq").Value
Return True
Else
Return False
End If
End With
End Function
第三步、登录
第二布的连接成功还没有完,这次才算是真正的登录,第三部如果成功会把已经在线的WebQQ或者普通QQ踢下线。我们要做的就是给“http://d.web2.qq.com/channel/login2”Post一个JSON结构(什么是JSON?这是一种轻量级的数据交换格式,和xml有相似功能,里面可以内嵌属性、数组、多层JSON结构,不熟悉Java的同学比如我都比较陌生,JSON可以画成树形的,好理解点)的字符串:
{"status":"",
"ptwebqq":"上面cookie里ptwebqq的值",
"passwd_sig":"",
"clientid","这里随便来个数就可以了最好是1000000级别的"}
把以上做为r,Post到那个地址里,注意附带之前收集的cookie,注意UTF8的编码,这次要加referer为"http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3"!这三点切记!以后每次Post操作都要记着这三点!后面不再提示了!
这次Post会返回cookie,注意收集;和一个JSON结构,这时JSON解析就要上场,获取里面的psessionid和vfwebqq,以后获取好友列表、收发消息等操作都需要用到
示例代码:
Public Sub Logon()
Dim Login_Info As IDictionary(Of String, String) = New Dictionary(Of String, String)()
Dim Json_Login_str As String
Login_Info.Add("r", "{" + Chr(34) + "status" + Chr(34) + ":" + Chr(34) + Chr(34) + "," + Chr(34) + "ptwebqq" + Chr(34) + ":" + Chr(34) + ptwebqq + Chr(34) + "," + Chr(34) + "passwd_sig" + Chr(34) + ":" + Chr(34) + Chr(34) + "," + Chr(34) + "clientid" + Chr(34) + ":" + Chr(34) + Clientid + Chr(34) + "}")
With Http.HttpWebResponseUtility.CreatePostHttpResponse("http://d.web2.qq.com/channel/login2", Login_Info, -1, "", System.Text.Encoding.UTF8, Cookies, "http://d.web2.qq.com/proxy.html?v=20110331002&callback=1&id=3")
Cookies.Add(.Cookies)
Json_Login_str = New System.IO.StreamReader(.GetResponseStream()).ReadToEnd()
End With
Dim Json_Login As Newtonsoft.Json.Linq.JObject = Newtonsoft.Json.JsonConvert.DeserializeObject(Json_Login_str)
psessionid = Json_Login("result")("psessionid")
vfwebqq = Json_Login("result")("vfwebqq")
End Sub
第四步、获取好友列表
使用Post方法访问http://s.web2.qq.com/api/get_user_friends2可以获得好友列表,注意附带Cookie、Referer要设置正确、UTF8编码!最后一次提示!
Post的唯一一个r参数是这样的:
{"h","hello",
"vfwebqq":"之前获取的vfwebqq的值"}
同样这次还返回一个JSON结构,同样解析之:
{
"retcode": 0,
"result": {
"friends": [
{
"flag": 0,
"uin": 1771529444,
"categories": 0
},
{
"flag": 0,
"uin": 3898920339,
"categories": 0
}
],
"marknames": [],
"categories": [
{
"index": 1,
"sort": 1,
"name": "朋友"
},
{
"index": 2,
"sort": 2,
"name": "家人"
},
{
"index": 3,
"sort": 3,
"name": "同学"
}
],
"vipinfo": [
{
"vip_level": 5,
"u": 1771529444,
"is_vip": 1
},
{
"vip_level": 0,
"u": 3898920339,
"is_vip": 0
}
],
"info": [
{
"face": 303,
"flag": 13107814,
"nick": "1080P·人格",
"uin": 1771529444
},
{
"face": 402,
"flag": 294126146,
"nick": "特斯拉的【2234 3803】人",
"uin": 3898920339
}
]
}
}
这个JSON比较长,结构也比较复杂,retcode表示操作是否成功,0是成功,其他非0的就是失败,如果是失败还有个errmsg,里面是错误信息,但一般是空的。如果成功了,会返回一个result,result里面的东西主要需要friends、categories和info(vipinfo表示用户的vip等级,marknames是备注名称),这几个都是数组(JArray):
info里面包含所有的好友,nick是好友名称,uin是临时分配的序列号,不是QQ号,这个需要记住,以后对这个好友操作时候有用!
categories里包含分组信息,index是组的编号,sort是排列顺序,name是分组名称
最后从friends跟据uin获取该用户所属的组,就这样~
代码见WebQQ_Client.vb的Grab_Friend_List()函数
第五步、取得群列表
相信看了上面的讲解,可能已经对WebQQ是如何工作的有一定了解了吧,就是各种Get和Post,表明身份,告诉正确的地址你需要什么就行了。
取得群列表比取得好友列表简单,只需要把r参数等于{"vfwebqq":"之前取得的vfwebqq的值"} Post给"http://s.web2.qq.com/api/get_group_name_list_mask2"就好了。注意附带Cookie、Referer要设置正确、UTF8编码!最后一次提示!
同样返回的是JSON结构,不过简单多,同样,retcode表示操作是否成功,0是成功,其他非0的就是失败,如果是失败还有个errmsg,里面是错误信息,但一般是空的。如果成功了,会返回一个result。
我们需要关注gnamelist即可,需要注意的是gnamelist里每个项目的name(名称)、gid(临时群编号,不是群号)和code(群代码)都需要,gid是群发信息时需要的,code是取得群信息要用的:
{
"retcode": 0,
"result": {
"gmasklist": [],
"gnamelist": [
{
"flag": 16777217,
"name": "Chemistry Fever",
"gid": 2422023446,
"code": 2035113860
},
{
"flag": 17826817,
"name": "⑨次元の苍歌社",
"gid": 1301755830,
"code": 2271299603
}
],
"gmarklist": []
}
}
上面JSON包含两个群。代码见WebQQ_Client.vb的Grab_Group_list()函数。
|
|