本人不太喜欢玩qq空间的,最近加了一个QQ好友,看头像貌似一个大叔。错了,是他加的我,他咨询我微信自动打招呼不能使用的问题,本来上班就挺闲的,于是就和他聊开了。他问了我一句话,我顿时汗颜,感觉愧对自己这一两年。什么话呢?他说“最羡慕我们这群在网络上赚钱的了,你在网上转了几十万了吧?”。由此我内疚啊,几十万角还是几十万元,勒个。这些都是引言,他给我启发,我能否写一个网页工具,让用户导出他自己的QQ群好友呢,导出QQ成员邮箱呢?现在电子商务那么发达,有好些人都借助QQ群做营销,QQ号码的作用也就可想而知了 。上周五的 时候我找了个软件,自动提取QQ群成员的软件,能是能用,骂了隔壁的要收费。不说这些了,言归正传。
首先我找到QQ空间的登录界面i.qq.com,抓一下包 这里是想尝试找到他的登录链接,以及上传的参数信息。为了调试简便,直接找他的登录表单,地址是:http://xui.ptlogin2.qq.com/cgi-bin/xlogin?proxy_url=http%3A//qzs.qq.com/qzone/v6/portal/proxy.html&daid=5&pt_qzone_sig=1&hide_title_bar=1&low_login=0&qlogin_auto_login=1&no_verifyimg=1&link_target=blank&appid=549000912&style=22&target=self&s_url=http%3A//qzs.qq.com/qzone/v5/loginsucc.html?para=izone&pt_qr_app=%E6%89%8B%E6%9C%BAQQ%E7%A9%BA%E9%97%B4&pt_qr_link=http%3A//z.qzone.com/download.html&self_regurl=http%3A//qzs.qq.com/qzone/v6/reg/index.html&pt_qr_help_link=http%3A//z.qzone.com/download.html,这么长一串,看着头都晕了吧,这个地址不用管它,是写死的。
请求这个页面,会返回一个重要的参数,查看这个页面的源码,里面有个login_sig的玩意,登录的签名标识,这个参数在检查登录是否需要验证码的时候会用到
开启网络监听可以发现,在输入完QQ号码的时候,也就是QQ号码输入框失去焦点的时候,浏览器会发一个请求到腾讯服务器,检查此次登录是否需要输入验证码。请求地址:http://check.ptlogin2.qq.com/check?regmaster=&uin=qq号码&appid=549000912&js_ver=10100&js_type=1&login_sig=-C312hp6B8CxoeIKpP*g2HaPLthAwHxTGa5u5WRgaDwrDi*3DrpjYwh38eh8-nyu&u1=http%3A%2F%2Fqzs.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&r=0.22668678383342922,login_sig是上面得到的安全登录签名标识,其它参数可以不变,这个请求会返回一个字符串,ptui_checkVC('0','!GGG','\x00\x00\x00\x00\x23\xe8\xf1\xd0','ffa99a5e8b464c05ab1666287089353996556b1c4964173e52ac0d76716f2d5676c8b41555aa551e5ad33146945d2041');
这里有四个参数,第一个参数为是否需要验证码,1是,0否。如果第一个参数是0,那么第二个参数直接就是验证码,如果第,一个参数是1,第二个参数则是获取验证码的密文,第三个参数不用管它,第四个参数为登录的时候需要的 pt_verifysession_v1参数,除了第三个参数外,这几个都是很重要的参数。注意,如果此次登录需要验证码,那么第四个参数是空的,那么登录是需要的pt_verifysession_v1参数怎么得到呢
如果登录需要验证码,浏览器会发一个请求获取验证图片,地址是:http://captcha.qq.com/getimage?uin=$qq&aid=549000912&cap_cd=$p2&0.7444603350013494 ,
这里我们只需要注意一下cap_cd这个参数,这个参数是上面讲的第二个参数。这个请求回返回一个cookies值,这个值是保持当前验证码和登录一直的重要参数,抓包的时候查看cookies可以看到verifysession。
以上都是登录前的参数收集,现在我们来看一下登录地址:http://ptlogin2.qq.com/login?u=qq号&verifycode=验证码&pt_vcode_v1=0&pt_verifysession_v1=验证码返回的verifysession&p=加密的密码&pt_rsa=0&u1=http%3A%2F%2Fqzs.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&ptredirect=0&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=2-10-1416737142521&js_ver=10100&js_type=1&login_sig=安全登录标识符&pt_uistyle=32&aid=549000912&daid=5&pt_qzone_sig=1&
经过之前的收集 ,我们发现这里只有一个参数我们未知。那就是p参数,那么是怎么加密的呢,我们查看源码,会发现这样一个js文件
加密的算法都在里面了,经过查看源码,我们可以看到它的加密方式,先将密码md5加密,再讲加了密的密码转换为二进制码,然后再和QQ号首尾相连进行md5加密,结果在和验证码首尾相连md5加密,挺复杂的。加密获取p参数 var p = $.Encryption.getEncryption(password,$.str.uin2hex(qq), verifycode);
$.Encryption = function() {
var hexcase = 1;
var b64pad = "";
var chrsz = 8;
var mode = 32;
function md5(s) {
return hex_md5(s)
}
function hex_md5(s) {
return binl2hex(core_md5(str2binl(s), s.length * chrsz))
}
function str_md5(s) {
return binl2str(core_md5(str2binl(s), s.length * chrsz))
}
function hex_hmac_md5(key, data) {
return binl2hex(core_hmac_md5(key, data))
}
function b64_hmac_md5(key, data) {
return binl2b64(core_hmac_md5(key, data))
}
function str_hmac_md5(key, data) {
return binl2str(core_hmac_md5(key, data))
}
function core_md5(x, len) {
x[len >> 5] |= 128 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
for (var i = 0; i < x.length; i += 16) {
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd)
}
if (mode == 16) {
return Array(b, c)
} else {
return Array(a, b, c, d)
}
}
function md5_cmn(q, a, b, x, s, t) {
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b)
}
function md5_ff(a, b, c, d, x, s, t) {
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t)
}
function md5_gg(a, b, c, d, x, s, t) {
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t)
}
function md5_hh(a, b, c, d, x, s, t) {
return md5_cmn(b ^ c ^ d, a, b, x, s, t)
}
function md5_ii(a, b, c, d, x, s, t) {
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t)
}
function core_hmac_md5(key, data) {
var bkey = str2binl(key);
if (bkey.length > 16) {
bkey = core_md5(bkey, key.length * chrsz)
}
var ipad = Array(16),
opad = Array(16);
for (var i = 0; i < 16; i++) {
ipad[i] = bkey[i] ^ 909522486;
opad[i] = bkey[i] ^ 1549556828
}
var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
return core_md5(opad.concat(hash), 512 + 128)
}
function safe_add(x, y) {
var lsw = (x & 65535) + (y & 65535);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 65535)
}
function bit_rol(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt))
}
function str2binl(str) {
var bin = Array();
var mask = (1 << chrsz) - 1;
for (var i = 0; i < str.length * chrsz; i += chrsz) {
bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (i % 32)
}
return bin
}
function binl2str(bin) {
var str = "";
var mask = (1 << chrsz) - 1;
for (var i = 0; i < bin.length * 32; i += chrsz) {
str += String.fromCharCode((bin[i >> 5] >>> (i % 32)) & mask)
}
return str
}
function binl2hex(binarray) {
var hex_tab = hexcase ? "0123456789ABCDEF": "0123456789abcdef";
var str = "";
for (var i = 0; i < binarray.length * 4; i++) {
str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 15) + hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 15)
}
return str
}
function binl2b64(binarray) {
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var str = "";
for (var i = 0; i < binarray.length * 4; i += 3) {
var triplet = (((binarray[i >> 2] >> 8 * (i % 4)) & 255) << 16) | (((binarray[i + 1 >> 2] >> 8 * ((i + 1) % 4)) & 255) << 8) | ((binarray[i + 2 >> 2] >> 8 * ((i + 2) % 4)) & 255);
for (var j = 0; j < 4; j++) {
if (i * 8 + j * 6 > binarray.length * 32) {
str += b64pad
} else {
str += tab.charAt((triplet >> 6 * (3 - j)) & 63)
}
}
}
return str
}
function hexchar2bin(str) {
var arr = [];
for (var i = 0; i < str.length; i = i + 2) {
arr.push("\\x" + str.substr(i, 2))
}
arr = arr.join("");
eval("var temp = '" + arr + "'");
return temp
}
function getEncryption(password, uin, vcode, isMd5) {
var str1 = hexchar2bin(isMd5 ? password: md5(password));
var str2 = md5(str1 + uin);
var str3 = md5(str2 + vcode.toUpperCase());
return str3
}
function getRSAEncryption(password, vcode, isMd5) {
var str1 = isMd5 ? password: md5(password);
var str2 = str1 + vcode.toUpperCase();
var str3 = $.RSA.rsa_encrypt(str2);
return str3
}
return {
getEncryption: getEncryption,
getRSAEncryption: getRSAEncryption,
md5: md5
}
} ();
$.str = (function() {
var htmlDecodeDict = {
quot: '"',
lt: "<",
gt: ">",
amp: "&",
nbsp: " ",
"#34": '"',
"#60": "<",
"#62": ">",
"#38": "&",
"#160": " "
};
var htmlEncodeDict = {
'"': "#34",
"<": "#60",
">": "#62",
"&": "#38",
" ": "#160"
};
return {
decodeHtml: function(s) {
s += "";
return s.replace(/&(quot|lt|gt|amp|nbsp);/ig,
function(all, key) {
return htmlDecodeDict[key]
}).replace(/&#u([a-f\d]{4});/ig,
function(all, hex) {
return String.fromCharCode(parseInt("0x" + hex))
}).replace(/&#(\d+);/ig,
function(all, number) {
return String.fromCharCode( + number)
})
},
encodeHtml: function(s) {
s += "";
return s.replace(/["<>& ]/g,
function(all) {
return "&" + htmlEncodeDict[all] + ";"
})
},
trim: function(str) {
str += "";
var str = str.replace(/^\s+/, ""),
ws = /\s/,
end = str.length;
while (ws.test(str.charAt(--end))) {}
return str.slice(0, end + 1)
},
uin2hex: function(str) {
var maxLength = 16;
str = parseInt(str);
var hex = str.toString(16);
var len = hex.length;
for (var i = len; i < maxLength; i++) {
hex = "0" + hex
}
var arr = [];
for (var j = 0; j < maxLength; j += 2) {
arr.push("\\x" + hex.substr(j, 2))
}
var result = arr.join("");
eval('result="' + result + '"');
return result
},
bin2String: function(a) {
var arr = [];
for (var i = 0,
len = a.length; i < len; i++) {
var temp = a.charCodeAt(i).toString(16);
if (temp.length == 1) {
temp = "0" + temp
}
arr.push(temp)
}
arr = "0x" + arr.join("");
arr = parseInt(arr, 16);
return arr
},
utf8ToUincode: function(s) {
var result = "";
try {
var length = s.length;
var arr = [];
for (i = 0; i < length; i += 2) {
arr.push("%" + s.substr(i, 2))
}
result = decodeURIComponent(arr.join(""));
result = $.str.decodeHtml(result)
} catch(e) {
result = ""
}
return result
},
json2str: function(obj) {
var result = "";
if (typeof JSON != "undefined") {
result = JSON.stringify(obj)
} else {
var arr = [];
for (var i in obj) {
arr.push("'" + i + "':'" + obj[i] + "'")
}
result = "{" + arr.join(",") + "}"
}
return result
},
time33: function(str) {
var hash = 0;
for (var i = 0,
length = str.length; i < length; i++) {
hash = hash * 33 + str.charCodeAt(i)
}
return hash % 4294967296
}
}
})();
好了 ,参数都收集齐了,可以去登录了
登陆返回数据包
58
ptuiCB('0','0','http://web.qq.com/loginproxy.html?login2qq=1&webqq_type=10','0','登录成功!','dzzzzzzzz');
0
QQ登陆错误数据包
ptuiCB('7','0','','0','很遗憾,网络连接出现异常,请您稍后再试。(2772435371)', '624475210');
ptuiCB('3','0','','0','您输入的帐号或密码不正确,请重新输入。', '624475210');
ptuiCB('4','3','','0','登录失败,请重试。*', '624475210');
ptuiCB('4','2','','0','页面过期,请重试。(1485832416)', '602468816');
最后总结一下php模拟登录的几个步骤:
1、访问登录页面,获取login_sig参数,php中通过stream_get_contents函数获取网页内容,通过preg_match正则匹配可获得login_sig
2、检查本次登录是否需要验证码,获取验证码,verifysession参数.
3、如果需要验证码,则获取验证码图片和返回的verifysession,
function _getVImg($url = "", $filename = "") {
$cookie_file = dirname(__FILE__).'/cookie.txt';
if(is_dir(basename($filename))) {
echo "The Dir was not exits";
Return false;
}
//去除URL连接上面可能的引号
$url = preg_replace( '/(?:^[\'"]+|[\'"\/]+$)/', '', $url );
$hander = curl_init();
$fp = fopen($filename,'wb');
curl_setopt($hander,CURLOPT_URL,$url);
curl_setopt($hander,CURLOPT_FILE,$fp);
curl_setopt($hander,CURLOPT_HEADER,0);
//curl_setopt($hander,CURLOPT_FOLLOWLOCATION,1);
curl_setopt($hander, CURLOPT_COOKIEJAR, $cookie_file); //存储cookies
//curl_setopt($hander,CURLOPT_RETURNTRANSFER,false);//以数据流的方式返回数据,当为false是直接显示出来
curl_setopt($hander,CURLOPT_TIMEOUT,60);
$content=curl_exec($hander);
curl_close($hander);
$fp2 = fopen($cookie_file,'r');
$ses='';
while(!feof($fp2))
{
$ses.=fgets($fp2);
}
$nodle='verifysession ';
$sw= strstr($ses,$nodle);
$result = trim(str_replace($nodle,'',$sw));
fclose($fp);
fclose($fp2);
Return $result;
}
4、对密码进行加密,得到需要的p参数
var p = $.Encryption.getEncryption(password,$.str.uin2hex(qq), verifycode);
5、构建登录地址:http://ptlogin2.qq.com/login?u=qq号&verifycode=验证码&pt_vcode_v1=0&pt_verifysession_v1=验证码返回的verifysession&p=加密的密码&pt_rsa=0&u1=http%3A%2F%2Fqzs.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&ptredirect=0&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=2-10-1416737142521&js_ver=10100&js_type=1&login_sig=安全登录标识符&pt_uistyle=32&aid=549000912&daid=5&pt_qzone_sig=1& 把参数放进去就行了。接下来你知道该怎么做了吧
大功告成!