webQQ的登录

1。首先获取验证码VeriCode
抓包:
GET http://check.ptlogin2.qq.com/check?uin=【QQNo】&appid=1003903 HTTP/1.1
(添加粗体字符,以适应2012.5.25后腾讯的变更)
说明:这个地址是用来判断您的用户名是否是正常状态,以及是否有效用户名,正常时,请求这个地址,会获得类似ptui_checkVC('0','!TDU','\x00\x00\x00\x00\x1d\x44\xac\x91');
这样的返回值,这其中0是代表正常状态,不采用图片验证。
而后面的!TDU是四位,则是我们需要获得的验证码,它是由腾讯服务器随机生成的一个令牌的原始密钥。当我们正确的获得这样的四位码时,就已经完成了第一次的验证!
其后,有一点需要注意的是:腾讯的令牌是存储在cookies中的,所以请注意每次操作都必须要保存好服务器返回来的cookies。
如果返回值,类似这样:ptui_checkVC('1','ae2ae85f6ce5b416257182bf3f0bf3554e5a60ca0bdca4c1'...);
那么就要恭喜您了,那表明需要进行图片验证码验证。您获得也是令牌的原始密钥,只不过不同的是,您获得是图片验证码的原始密钥而已。
此外,0和1以外,还有可能有其他的返回数值,用来标明诸如帐号不存在,帐号状态不正确,帐号已锁定等等的。
经验:这一步的关键,就是如何避免非0的返回值?
有人说,如果短时间内这个帐号多次在不同的ip登录,那么返回值就有可能是非0值--其实不然。我在IE上,换了不同的IP,照样可得到0值,不需要图片验证。与此同时,新装的浏览器safari上,敲了n遍,还是得到1,需要图片验证。等我用safari登陆一次web.qq.com后,其后得到的返回值就是0了...这说明什么?
说明QQ服务器没有存放你的IP地址,但是在你的机器里存放cookies(而不同的浏览器或客户端,其cookies不同),然后根据cookies来判断你是否初来咋到的...
正因为是这样,对于你常用的QQ号码,成功登陆后,要把cookies存起来,以后你每次请求,都能得到0的返回值,不需要图片验证。



C++ (with libcurl)源程序
【抓包GET】http://check.ptlogin2.qq.com/check?uin=XXXXXXXX&appid=1003903 HTTP/1.1

Referer: http://ui.ptlogin2.qq.com/cgi-bin/login?target=self&style=5&mibao_css=m_webqq&appid=1003903&enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2Fweb.qq.com%2Floginproxy.html&f_url=loginerroralert&strong_login=1&login_state=10&t=20120619001
【源代码】
string WebQQ_check(string QQno)
{
string buffer;
string get_url = "http://check.ptlogin2.qq.com/check?uin=" + QQno + "&appid=1003903";
string Ref_url = "http://ui.ptlogin2.qq.com/cgi-bin/login?target=self&style=5&mibao_css=m_webqq&appid=1003903";
Ref_url = Ref_url+"&enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2Fweb.qq.com%2Floginproxy.html&f_url=loginerroralert&strong_login=1&login_state=10";//
// 初始化libcurl
CURLcode return_code;
return_code = curl_global_init(CURL_GLOBAL_WIN32);
if (CURLE_OK != return_code) return "";
// 获取easy handle
CURL *easy_handle = curl_easy_init();
if (NULL == easy_handle)
{
curl_global_cleanup();
return "";
}
// 设置easy handle属性
curl_easy_setopt(easy_handle, CURLOPT_URL, get_url.c_str());
curl_easy_setopt(easy_handle, CURLOPT_REFERER, Ref_url.c_str());
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, writer);
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &buffer);
//提交第一步保存的cookie
curl_easy_setopt(easy_handle, CURLOPT_COOKIEFILE,"D:\\SparkHo\\cookie_login.txt");
//把服务器发过来的cookie保存到cookie_open.txt
curl_easy_setopt(easy_handle, CURLOPT_COOKIEJAR, "D:\\SparkHo\\cookie_open.txt");
// 执行数据请求
curl_easy_perform(easy_handle);
// 释放资源
curl_easy_cleanup(easy_handle);
curl_global_cleanup();

//返还QQ验证数据
return buffer;
}

返回的格式:

ptui_checkVC('0','!TDU','\x00\x00\x00\x00\x1d\x44\xac\x91');



这包括了一组“四位验证码”,还有一组8位的UIN数字(其实就是你的QQ号转化为16进制数字)。

何用之有?用于腾讯密码的变态加密...
因为其后的登陆中,会要求你输入一个由32个字符组成的字符串,其实就是你的UIN数字,加上密码和验证码,按照某种算法得到的md5码。
腾讯已经更新了算法,具体的算法请参考源代码。



C++ (with OpenSSL)源程序
#include <openssl/md5.h>

string newQQMD5(string pass,string VeriCode,string UIN)
{
unsigned char md[16],hexuin[8],plus[24],md2[16],md3[16];
int i;
char tmp[3]={'\0'};
char buf[33]={'\0'};
char buf2[33]={'\0'};

MD5((unsigned char *)pass.c_str(),pass.length(),md);
Hexchar2bin(UIN,hexuin);
for (i = 0; i < 16; i++)
plus[i]=md[i];
for (i = 0; i < 8; i++)
plus[16+i]=hexuin[i];

MD5(plus,24,md2);
for (i = 0; i < 16; i++)
{
sprintf(tmp,"%2.2X",md2[i]);
strcat(buf,tmp);
}

string bufAdd = (string)buf+VeriCode;
MD5((unsigned char *)bufAdd.c_str(),bufAdd.length(),md3);
for (i = 0; i < 16; i++)
{
sprintf(tmp,"%2.2X",md3[i]);
strcat(buf2,tmp);
}
string final=(string)buf2;
return final;
}

附1:
int Hexchar2bin(string webstring,unsigned char* hexuin)
{
size_t start,found;
string Hexstr;
char * p;
unsigned short n;
start = 0;
int i = 0;
while(start<webstring.length())
{
found = webstring.find("\\x",start,2);
if(found <= webstring.length())
{
Hexstr = webstring.substr(found+2,2);
n = strtoul(Hexstr.c_str(),&p,16);
if ( * p != 0 ) n = 0;
if(i<8)
hexuin[i] = (unsigned char)n;
i++;
start = found +4;
}
else
{
if(i<8)
return -1;
break;
}
}
return 1;
}


附2:基于OpenSSL的md5算法,可参考:
http://hi.baidu.com/sparkho/blog/item/88fc9061ec6f90c58cb10dd5.html



2。以QQ号、PassCode、VeriCode,登录web.qq.com网页,完成验证。
【抓包GET】
http://ptlogin2.qq.com/login?u=【QQno】&p=【PassCode】&verifycode=【VeriCode】&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb.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-6-6757&mibao_css=m_webqq&t=1&g=1HTTP/1.1
【Referer】 http://ui.ptlogin2.qq.com/cgi-bin/login?target=self&style=5&mibao_css=m_webqq&appid=1003903&enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2Fweb.qq.com%2Floginproxy.html&f_url=loginerroralert&strong_login=1&login_state=10&t=20120619001


登录成功后,将返回ptuiCB('0','0','http://web.qq.com/loginproxy.html?login2qq=1&webqq_type=10','0','登录成功!', '雨顺');
同时将会返回一些cookies值,如其中ptwebqq,skey等,libcurl可设定存放在指定的Cookies文件中。
注:如果我们直接用IE,输入上述网址,登录不会成功,原因是不能提交第一步保存的cookies...
结果将返回:ptuiCB('7','0','','0','很遗憾,网络连接出现异常,请您稍后再试。(1138754159)');



C++ (with libcurl)源程序

string WebQQ_login(string QQno,string PassCode,string VeriCode)
{
string get_url = "http://ptlogin2.qq.com/login?u=" + QQno + "&p=" + PassCode + "&verifycode=" + VeriCode;
get_url = get_url + "&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb.qq.com%2Floginproxy.html";
get_url = get_url + "%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&from_ui=1&pttype=1&dumy=&fp=loginerroralert&mibao_css=m_webqq&t=1&g=1";


string Ref_url = "http://ui.ptlogin2.qq.com/cgi-bin/login?target=self&style=5&mibao_css=m_webqq&appid=1003903&enable_qlogin=0";
Ref_url = Ref_url+"&no_verifyimg=1&s_url=http%3A%2F%2Fweb.qq.com%2Floginproxy.html&f_url=loginerroralert&strong_login=1&login_state=10";//&t=20120619001

string buffer;
// 初始化libcurl
CURLcode return_code;
return_code = curl_global_init(CURL_GLOBAL_WIN32);
if (CURLE_OK != return_code) return "";
// 获取easy handle
CURL *easy_handle = curl_easy_init();
if (NULL == easy_handle)
{
curl_global_cleanup();
return "";
}
// 设置easy handle属性
curl_easy_setopt(easy_handle, CURLOPT_URL, get_url.c_str());
curl_easy_setopt(easy_handle, CURLOPT_REFERER, Ref_url.c_str());
//curl_easy_setopt(easy_handle, CURLOPT_HEADER,1);
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, writer);
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &buffer);
//提交第一步保存的cookie
curl_easy_setopt(easy_handle, CURLOPT_COOKIEFILE,"D:\\SparkHo\\cookie_open.txt");
//保存登陆后的cookie
curl_easy_setopt(easy_handle, CURLOPT_COOKIEJAR,"D:\\SparkHo\\cookie_login.txt");

// 执行数据请求
curl_easy_perform(easy_handle);
// 释放资源
curl_easy_cleanup(easy_handle);
curl_global_cleanup();
filestatus=0;

return buffer;
}

登录成功,将返回:

ptuiCB('0','0','http://web.qq.com/loginproxy.html?login2qq=1&webqq_type=10','0','登录成功!', '雨顺');

这个时候,如果我们只是要发微博,调用发微博的程序即可。无需进行下面的登录。


3。登录QQ客户端。
上一步的登陆,算是登录webqq.com网页;而这次登陆,才算真正登陆webQQ客户端,成功后会把你原先登陆的QQ踢下线(如果你原先已经登陆QQ)。
抓包:
POSThttp://d.web2.qq.com/channel/login2HTTP/1.1
Referer:http://d.web2.qq.com/proxy.html
POST数据:一个json数据结构:
r={"status":"","ptwebqq":"【ptwebqq】","passwd_sig":"","clientid":"【clientid】"}

需要的参数:
1)【ptwebqq】是在第2步成功登陆后,cookie返回的,我们可在libcurl存档的Cookies文件中读取;
2)【clientid】是用户识别码,每次登录前随机产生的,但我们需要保存该用户识别码,因为以后很多的操作需要引用此唯一的用户识别码,直到再次重新登录才会更新。
如果成功,会返回一个json数据结构:
{"retcode":0,"result":{"uin":QQNo,"cip":3682615122,"index":1073,"port":49410,"status":"online","vfwebqq":"........","psessionid":"........","user_state":0,"f":0}}
如果我们用buffer获取返回的信息,那么我们可以从中提取出的vfwebqq和psessionid,存放于QQLogin.txt。以后的webQQ操作,需要用到这些参数。

C++ (with libcurl)源程序
string WebQQ_login2()
{
size_t found,found2;
char buf[1024];
// 在libcurl存档的Cookies文件中,读取:ptwebqq
string ptwebqq;
ifstream QQCookiesfile("D:\\SparkHo\\cookie_login.txt");
if(!QQCookiesfile)
{
QQCookiesfile.close();
ptwebqq = "";
}
else
{
QQCookiesfile.read(buf,1024);
string QQCookies = (string) buf;
QQCookiesfile.close();
found = QQCookies.find("ptwebqq");
found2 = QQCookies.find("\n",found+8,1);
ptwebqq = QQCookies.substr(found+8,found2-found-8);
}
// clientid,在每次登陆时,自行随机产生...
int iClient;
srand ( time(NULL) ); /* initialize random seed: */
iClient = rand() % 100000000; /* generate secret number: */
sprintf(buf, "%d", iClient);
string clientid = (string)buf;
// 初始化libcurl
CURLcode return_code;
return_code = curl_global_init(CURL_GLOBAL_WIN32);
if (CURLE_OK != return_code) return NULL;
// 获取easy handle
CURL *easy_handle = curl_easy_init();
if (NULL == easy_handle)
{
curl_global_cleanup();
return NULL;
}
string buffer;
string post_url = "http://d.web2.qq.com/channel/login2";
string referer_url = "http://d.web2.qq.com/proxy.html";
string base = "{\"status\":\"\",\"ptwebqq\":\""+ptwebqq+"\",\"passwd_sig\":\"\",\"clientid\":\""+clientid+"\"}";
string urlencode = curl_easy_escape(easy_handle,base.c_str(),0);
string fields = "r="+urlencode;
// 设置easy handle属性
curl_easy_setopt(easy_handle, CURLOPT_URL, post_url.c_str());
curl_easy_setopt(easy_handle, CURLOPT_REFERER, referer_url.c_str());
curl_easy_setopt(easy_handle, CURLOPT_POST, 1);
curl_easy_setopt(easy_handle, CURLOPT_POSTFIELDS, fields.c_str());
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, writer);
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &buffer);
//提交第一步保存的cookie
curl_easy_setopt(easy_handle, CURLOPT_COOKIEFILE,"D:\\SparkHo\\cookie_login.txt");
//保存登陆后的cookie
curl_easy_setopt(easy_handle, CURLOPT_COOKIEJAR,"D:\\SparkHo\\cookie_login.txt");
// 执行数据请求
curl_easy_perform(easy_handle);
// 释放资源
curl_easy_cleanup(easy_handle);
curl_global_cleanup();
//从buffer中,提取vfwebqq、psessionid
found = buffer.find("vfwebqq");
found2 = buffer.find("\"",found+10,1);
string vfwebqq = buffer.substr(found+10,found2-found-10);
found = buffer.find("psessionid");
found2 = buffer.find("\"",found+13,1);
string psessionid = buffer.substr(found+13,found2-found-13);
//记录QQ登录信息,存放在指定的txt文件中:
fstream QQLoginfile("D:\\SparkHo\\QQLogin.txt",ios::out|ios::trunc);
QQLoginfile<<"ClientID:"<<clientid.c_str()<<";";
QQLoginfile<<"vfwebqq:"<<vfwebqq.c_str()<<";";
QQLoginfile<<"psessionid:"<<psessionid.c_str()<<";";
QQLoginfile.close();

return buffer;
}


4。断线重连。
Login2成功登陆后,我们得到一个Cookies文件:内含ptwebqq;还有一个存放QQ登录信息的txt文件:内含clientId和psessionid。
当网络中断、QQ离线或被踢下线,需要重新登录的时候,可以使用上述信息进行重新连接。
抓包:
POSThttp://d.web2.qq.com/channel/login2HTTP/1.1
Referer:http://d.web2.qq.com/proxy.html
POST数据:一个json数据结构:
r={"status":"online","ptwebqq":"【ptwebqq】","passwd_sig":"","clientid":"【clientid】","psessionid":"【psessionid】"}
&clientid=【clientid】&psessionid=【psessionid】
需要的参数:
1)【ptwebqq】在libcurl存档的Cookies文件中读取;
2)【clientid】是上次Login2登录的用户识别码;【psessionid】是上次Login2登录返回的;都可以从以前保存的登录信息文件QQLogin.txt里读出。

C++ (with libcurl)源程序
string WebQQ_login2_r()
{
size_t found,found2;
char buf[1024];
// 在Cookies文件中,读取:ptwebqq
string ptwebqq;
ifstream QQCookiesfile("D:\\SparkHo\\cookie_login.txt");
if(!QQCookiesfile)
{
QQCookiesfile.close();
ptwebqq = "";
}
else
{
QQCookiesfile.read(buf,1024);
string QQCookies = (string) buf;
QQCookiesfile.close();
found = QQCookies.find("ptwebqq");
found2 = QQCookies.find("\n",found+8,1);
ptwebqq = QQCookies.substr(found+8,found2-found-8);
}
// 提取QQ登录信息:
string clientid,psessionid;
ifstream QQLoginfile("D:\\SparkHo\\QQLogin.txt");
if(!QQLoginfile)
{
QQLoginfile.close();
clientid = "";
psessionid = "";
}
else
{
QQLoginfile.read(buf,1024);
string QQLogin = (string) buf;
QQLoginfile.close();
found = QQLogin.find("ClientID");
found2 = QQLogin.find(";",found+9,1);
clientid = QQLogin.substr(found+9,found2-found-9);
found = QQLogin.find("psessionid");
found2 = QQLogin.find(";",found+11,1);
psessionid = QQLogin.substr(found+11,found2-found-11);
}
// 初始化libcurl
CURLcode return_code;
return_code = curl_global_init(CURL_GLOBAL_WIN32);
if (CURLE_OK != return_code) return NULL;
// 获取easy handle
CURL *easy_handle = curl_easy_init();
if (NULL == easy_handle)
{
curl_global_cleanup();
return NULL;
}
string buffer;
string post_url = "http://d.web2.qq.com/channel/login2";
string referer_url = http://d.web2.qq.com/proxy.html;
string base = "{\"status\":\"online\",\"ptwebqq\":\""+ptwebqq+"\",\"passwd_sig\":\"\",\"clientid\":\""+clientid+"\",\"psessionid\":\""+psessionid+"\"}";
string urlencode = curl_easy_escape(easy_handle,base.c_str(),0);
string fields = "r="+urlencode+"&clientid="+clientid+"&psessionid="+psessionid;
// 设置easy handle属性
curl_easy_setopt(easy_handle, CURLOPT_URL, post_url.c_str());
curl_easy_setopt(easy_handle, CURLOPT_REFERER, referer_url.c_str());
curl_easy_setopt(easy_handle, CURLOPT_POST, 1);
curl_easy_setopt(easy_handle, CURLOPT_POSTFIELDS, fields.c_str());
curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, writer);
curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &buffer);
//提交保存的cookie
curl_easy_setopt(easy_handle, CURLOPT_COOKIEFILE,"D:\\SparkHo\\cookie_login.txt");
//保存登陆后的cookie
curl_easy_setopt(easy_handle, CURLOPT_COOKIEJAR,"D:\\SparkHo\\cookie_login.txt");
// 执行数据请求
curl_easy_perform(easy_handle);
// 释放资源
curl_easy_cleanup(easy_handle);
curl_global_cleanup();
//从buffer中,提取vfwebqq、psessionid
found = buffer.find("vfwebqq");
found2 = buffer.find("\"",found+10,1);
string vfwebqq = buffer.substr(found+10,found2-found-10);
found = buffer.find("psessionid");
found2 = buffer.find("\"",found+13,1);
psessionid = buffer.substr(found+13,found2-found-13);
// 更新QQ登录信息:
fstream QQLoginwrite("D:\\SparkHo\\QQLogin.txt",ios::out|ios::trunc);
QQLoginwrite<<"ClientID:"<<clientid.c_str()<<";";
QQLoginwrite<<"vfwebqq:"<<vfwebqq.c_str()<<";";
QQLoginwrite<<"psessionid:"<<psessionid.c_str()<<";";
QQLoginwrite.close();

return buffer;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值