|
本帖最后由 宇智波·佐助 于 2013-10-16 22:36 编辑
今天来说说WEBQQ登陆过程的分析,抛砖引玉而已
第一步:使用HttpWatch 软件抓取webQQ页面从打开至登陆成功到所有数据包。我们所有到操作均在这里面查找。
等待抓包中..... 这里为了方便 我已经登陆成功一个号码并且抓包成功。 抓包方法很简单 打开IE 打开HttpWatch 点击记录后 开始一系列的登陆操作直到登陆成功后。停止抓
第二步:我们重新打开一个webqq登陆页面。来分析网页源代码。(这里建议大家使用 Chrome 谷歌浏览器。比较方便。它本身带有一个 WEB前端助手 很好)这个前端助手自行安装
1 <input autocomplete="on" name="u" id="u" type="text" style="ime-mode:disabled" class="input01" tabindex="1" value="QQ号码或Email帐号">
2 <input name="p" id="p" maxlength="16" type="password" class="input01" tabindex="2">
3 <input type="submit" class="signin-btn signin2" value="" tabindex="6" id="login_button">
4 <form id="loginform" autocomplete="off" name="loginform" action="http://ptlogin2.qq.com/login" method="post" target="_self" style="margin:0px;">
依次使用 审查元素 查看用户名输入框 密码输入框 和 登陆按钮。这里就要知道HTML的基本概念里 <form></form>这个范围内就是一个表单。提交到是这个范围内到内容。所以 我们要在源代码里找找<form
得到上述结果
第一个用户名框 我们关注一点 name="u" 表示QQ号是从这个框里获取。名字就是 u
第二个是密码输入框 同理 name="p" 表示密码框就是 P
第三个是登陆按钮 type为 submit 表示这是一个提交按钮。点这个就让这个表单提交
第四个就是这个表单的基础信息。我们关注一点 onsubmin 事件表示 该表单被提交。这里就表示 当用户提交表单(点击登陆按钮)后。执行onFormSubmit这个过程。关键1
第三步
我们就需要开始查找这个过程到底进行里什么。
在我们到第一步HttpWatch抓包结果里搜索。 搜索的要求就是 function 这是定义一个函数的意思。表示这个就是此函数的具体过程。
function onFormSubmit(form)
{
if (form.remember_uin.checked){ '这里判断我们提交到表单内 remember_uin控件的 checked(被选中状态)。我们查看一下这个控件到底是什么。默认是假
return ptui_onLoginEx(form, "qq.com") 不执行
}else{
var myDate=new Date();
myDate.setFullYear(1971,1,1);
pt.cookie.set("ptui_loginuin", "", myDate, '/', 'ui.ptlogin2.qq.com');
return ptui_onLogin(form); ’所以这个函数其实是执行到这里。 调用了 ptui_onLogin过程
}
}
这里找到里 onFormSubmit
<input type="hidden" name="remember_uin" value="1" id="remember_uin" /> ‘一个隐藏的控件 选中状态默认为 假 hidden 表示是隐藏到控件。我们在网页上看不见。其实是可见的。通常会用这种来传递一些不需要用户操作的数据。
第四步
在我们到第一步HttpWatch抓包结果里搜索。ptui_onLogin 在这个文件里 https://ui.ptlogin2.qq.com/js/10047/comm.js 经过分析后 以后的所有操作都在这个文件内。
找到里 好大一堆。我们用代码整理软件来整理下。 这里使用谷歌浏览器到WEB前端助手 太乱了。现在是不是好看多了 我们再来搜索
function ptui_onLogin(A) { 具体过程
try {
if (parent.ptlogin2_onLogin) {
if (!parent.ptlogin2_onLogin()) {
return false
}
}
if (parent.ptlogin2_onLoginEx) {
var D = A.u.value;
var B = A.verifycode.value;
if (str_uintip == D) {
D = ""
}
if (!parent.ptlogin2_onLoginEx(D, B)) {
return false
}
}
} catch (C) {}
return ptui_checkValidate(A)
}
这里简单说下 JS的 try catch
try…catch这种结构最常见,它的执行过程是:当没有例外发生执行完毕try块语句后或者发生例外执行完catch块语句后,控制将转移到整个try…catch结构后面的语句。
所以上面那个函数 最终的执行是 return ptui_checkValidate(A) 返回 ptui_checkValidate 过程的结果。
第五步:
在我们到第一步HttpWatch抓包结果里搜索。ptui_checkValidate
依然是这个文件https://ui.ptlogin2.qq.com/js/10047/comm.js
这里我稍稍具体的分析了这个函数过程
function ptui_checkValidate(B) {
var A = B.u; ‘把QQ号输入框对象赋值给A
var D = B.p; 把QQ密码框对象赋值给D
var E = B.verifycode; 把验证码框到值赋值给E
if (A.value == "" || str_uintip == A.value) { 判断 如果QQ输入框到内容为空 或者 同 str_uintip一样 则输出错误消息 QQ号码输入框获取焦点 返回失败
pt.show_err(str_no_uin); 在第一步抓包到内容里搜索 str_uintip可以知道内容 var str_uintip = "QQ号码/手机/邮箱";
A.focus();
return false
}
A.value = A.value.trim(); 如果输入框不为空。则把框里到内容首位空格去掉赋值给 输入框
if (!pt.chkUin(A.value)) { 这里又调用里一个函数判断是否是QQ号码。 如果失败 同样提示输入QQ号,进行输入判断
pt.show_err(str_inv_uin);
A.focus();
A.select();
return false
}
if (D.value == "") { 如果密码内容为空
pt.show_err(str_no_pwd); 输出错误信息
D.focus(); 密码框获取焦点
return false 返回假
}
if (E.value == "") { 如果验证码内容为空
if (!isLoadVC) { 判断是否需要验证码 如果没有载入 则载入验证码
loadVC(true); 载入验证码
g_submitting = true;
return false
}
pt.show_err(str_no_vcode); 显示错误 同样可以搜索 var str_no_vcode = "您还没有输入验证码!";
try {
E.focus()
} catch (C) {} 又是一个 try catch语言 跳到下一句
if (!g_loadcheck) {
ptui_reportAttr(78028)
} else {
ptui_reportAttr(78029)
}
return false
} 这个过程主要是判断验证码
if (E.value.length < 4) { 如果验证码长度小于4 表示验证码错误
pt.show_err(str_inv_vcode);
E.focus();
E.select();
return false
}
if (isLoadVC && !(/^[a-zA-Z0-9]+$/.test(E.value))) { 这里判断 需要载入验证码 且 验证码内容不为 字母和数字组合
pt.show_err(str_correct_vcode);
E.focus();
E.select();
return false 显示错误信息 并且返回等待重新输入
}
D.setAttribute("maxlength", "32"); 设置密码输入框的 最大输入长度属性为 32 也就是说 密码框可以输入32个字符
ajax_Submit(); 这里调用里一个过程 submit 感觉是提交。我们看看这个过程是不是我们需要的。
ptui_reportNum(g_changeNum);
g_changeNum = 0;
return true
}
第六步
在我们到第一步HttpWatch抓包结果里搜索。ajax_Submit
依然是comm.js
function ajax_Submit() {
var A = getSubmitUrl("login"); 又调用里一个过程getSubmitUrl 字面意思是 得到(get)提交(submit)链接(url) 我们找出来看看
pt.winName.set("login_param", encodeURIComponent(login_param));
pt.loadScript(A);
return
}
第七步
在我们到第一步HttpWatch抓包结果里搜索。getSubmitUrl
依然是这个文件 comm.js
这个就不仔细讲解里 其实就是 我们登陆那个 GET链接各个参数的赋值过程。
function getSubmitUrl(K) {
var E = true;
var C = document.forms[0]; 取表单对象 这里其实就是我们登陆那个表单了。
var A = (pt.isHttps ? "https://ssl." : "http://") + "ptlogin2." + g_domain + "/" + K + "?"; 开始构建提交链接 A
var B = document.getElementById("login2qq");
if (pt.regmaster == 2) {
A = "http://ptlogin2.function.qq.com/" + K + "?regmaster=2&"
} else {
if (pt.regmaster == 3) {
A = "http://ptlogin2.crm2.qq.com/" + K + "?regmaster=3&"
}
}
for (var J = 0; J < C.length; J++) {
if (K == "ptqrlogin" && (C[J].name == "u" || C[J].name == "p" || C[J].name == "verifycode" || C[J].name == "h")) {
continue
}
if (C[J].name == "ipFlag" && !C[J].checked) {
A += C[J].name + "=-1&";
continue
}
if (C[J].name == "fp" || C[J].type == "submit") {
continue
}
if (C[J].name == "ptredirect") {
g_ptredirect = C[J].value
}
if (C[J].name == "low_login_enable" && (!C[J].checked)) {
E = false;
continue
}
if (C[J].name == "low_login_hour" && (!E)) {
continue
}
if (C[J].name == "webqq_type" && !B && (!C[J].checked)) {
continue
}
A += C[J].name;
A += "=";
if (C[J].name == "u" && pt.needAt) { 这里是赋值一个u=qq号 这个不需要知道 反正构成提交路径我们是直接赋值
A += pt.needAt + "&";
continue
}
if (C[J].name == "p") { 这里其实就是关键的密码加密处理过程了。 前面我们已经知道 p就是密码输入框的名字。 这里判断 如果控件名字=p 就是 如果目前处理的是密码输入框内容。
var M = C.p.value; 把密码框内容赋值给 M
var I = hexchar2bin(md5(M)); MD5 加密密码 在进行 hexchar2bin编码 赋值给 I
var H = md5(I + pt.uin); 把I的值同 UIN的值链接(这里两个都是文本。文本链接就是 “aa”+"bb"=“aabb” 这样没问题把) 把结果再次MD5 赋值给H
var G = md5(H + C.verifycode.value.toUpperCase()); 把H 同 验证码的大写形式进行连接 。 结果再MD5加密。 得到G
A += G 好吧 我们的G 就是密码结果
} else {
if (C[J].name == "u1" || C[J].name == "ep") {
var D = C[J].value;
var L = "";
if (g_appid == "1003903" && B) {
L = /\?/g.test(D) ? "&" : "?";
var F = document.getElementById("webqq_type").value;
L += "login2qq=" + B.value + "&webqq_type=" + F
}
A += encodeURIComponent(D + L)
} else {
A += C[J].value
}
}
A += "&"
}
A += "fp=loginerroralert&action=" + pt.action.join("-") + "-" + (new Date() - g_begTime) + "&mibao_css=" + pt.mibao_css +
"&t=" + pt.submitN[pt.uin] + "&g=1";
A += "&js_type=" + pt.js_type + "&js_ver=" + window.g_pt_version + "&login_sig=" + window.g_login_sig;
return A
}
第七步
1.上一句又几个地方 pt.uin 这个值是怎么来到呢? 我们通过搜索得到
function checkTimeout() {
var A = $("u").value.trim(); 'u QQ号码输入框 这里就是把 QQ号码输入框内容删除首尾空格后 赋值给A
if (pt.chkAccount.isQQ(A)) { 判断这个QQ号是否正确 如果正确
pt.uin = uin2hex(A); 把我们的QQ号码进行 uin2hex 编码后 赋值给pt.uin
$("verifycode").value = "";
loadVC(true)
}
ptui_reportAttr2(216082)
}
pt.uin = uin2hex(A);
function uin2hex(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
}
一个独立的过程。我们直接用脚本组件运行一下就能得到值
2 密码加密用里很多 MD5 这个好像就是加密关键。我们去提取
调用里 hex_md5,binl2hex,core_md5,str2binl,chrsz......... 我们抽丝剥茧。一个一个到把需要到复制出来 。
这里我们就需要构建一下加密脚本。 增加一个 qqhash函数 传递3个文本参数 依次是 QQ号 密码 验证码 然后返回加密后的密码代码。
至此 完成。 把以下代码加入e语言常量。 然后通过精易模块到 脚本组件模块进行调用。
QQ加密过程查询分析完成
function qqhash(qqnum,password,verifycode){
var I = hexchar2bin(md5(password));
var H = md5(I + uin2hex(qqnum));
var G = md5(H + verifycode.toUpperCase());
return G
}
var hexcase = 1;
var b64pad = "";
var chrsz = 8;
var mode = 32;
function md5(A) {
return hex_md5(A)
}
function hex_md5(A) {
return binl2hex(core_md5(str2binl(A), A.length * chrsz))
}
function str_md5(A) {
return binl2str(core_md5(str2binl(A), A.length * chrsz))
}
function hex_hmac_md5(A, B) {
return binl2hex(core_hmac_md5(A, B))
}
function b64_hmac_md5(A, B) {
return binl2b64(core_hmac_md5(A, B))
}
function str_hmac_md5(A, B) {
return binl2str(core_hmac_md5(A, B))
}
function core_md5(K, F) {
K[F >> 5] |= 128 << ((F) % 32);
K[(((F + 64) >>> 9) << 4) + 14] = F;
var J = 1732584193;
var I = -271733879;
var H = -1732584194;
var G = 271733878;
for (var C = 0; C < K.length; C += 16) {
var E = J;
var D = I;
var B = H;
var A = G;
J = md5_ff(J, I, H, G, K[C + 0], 7, -680876936);
G = md5_ff(G, J, I, H, K[C + 1], 12, -389564586);
H = md5_ff(H, G, J, I, K[C + 2], 17, 606105819);
I = md5_ff(I, H, G, J, K[C + 3], 22, -1044525330);
J = md5_ff(J, I, H, G, K[C + 4], 7, -176418897);
G = md5_ff(G, J, I, H, K[C + 5], 12, 1200080426);
H = md5_ff(H, G, J, I, K[C + 6], 17, -1473231341);
I = md5_ff(I, H, G, J, K[C + 7], 22, -45705983);
J = md5_ff(J, I, H, G, K[C + 8], 7, 1770035416);
G = md5_ff(G, J, I, H, K[C + 9], 12, -1958414417);
H = md5_ff(H, G, J, I, K[C + 10], 17, -42063);
I = md5_ff(I, H, G, J, K[C + 11], 22, -1990404162);
J = md5_ff(J, I, H, G, K[C + 12], 7, 1804603682);
G = md5_ff(G, J, I, H, K[C + 13], 12, -40341101);
H = md5_ff(H, G, J, I, K[C + 14], 17, -1502002290);
I = md5_ff(I, H, G, J, K[C + 15], 22, 1236535329);
J = md5_gg(J, I, H, G, K[C + 1], 5, -165796510);
G = md5_gg(G, J, I, H, K[C + 6], 9, -1069501632);
H = md5_gg(H, G, J, I, K[C + 11], 14, 643717713);
I = md5_gg(I, H, G, J, K[C + 0], 20, -373897302);
J = md5_gg(J, I, H, G, K[C + 5], 5, -701558691);
G = md5_gg(G, J, I, H, K[C + 10], 9, 38016083);
H = md5_gg(H, G, J, I, K[C + 15], 14, -660478335);
I = md5_gg(I, H, G, J, K[C + 4], 20, -405537848);
J = md5_gg(J, I, H, G, K[C + 9], 5, 568446438);
G = md5_gg(G, J, I, H, K[C + 14], 9, -1019803690);
H = md5_gg(H, G, J, I, K[C + 3], 14, -187363961);
I = md5_gg(I, H, G, J, K[C + 8], 20, 1163531501);
J = md5_gg(J, I, H, G, K[C + 13], 5, -1444681467);
G = md5_gg(G, J, I, H, K[C + 2], 9, -51403784);
H = md5_gg(H, G, J, I, K[C + 7], 14, 1735328473);
I = md5_gg(I, H, G, J, K[C + 12], 20, -1926607734);
J = md5_hh(J, I, H, G, K[C + 5], 4, -378558);
G = md5_hh(G, J, I, H, K[C + 8], 11, -2022574463);
H = md5_hh(H, G, J, I, K[C + 11], 16, 1839030562);
I = md5_hh(I, H, G, J, K[C + 14], 23, -35309556);
J = md5_hh(J, I, H, G, K[C + 1], 4, -1530992060);
G = md5_hh(G, J, I, H, K[C + 4], 11, 1272893353);
H = md5_hh(H, G, J, I, K[C + 7], 16, -155497632);
I = md5_hh(I, H, G, J, K[C + 10], 23, -1094730640);
J = md5_hh(J, I, H, G, K[C + 13], 4, 681279174);
G = md5_hh(G, J, I, H, K[C + 0], 11, -358537222);
H = md5_hh(H, G, J, I, K[C + 3], 16, -722521979);
I = md5_hh(I, H, G, J, K[C + 6], 23, 76029189);
J = md5_hh(J, I, H, G, K[C + 9], 4, -640364487);
G = md5_hh(G, J, I, H, K[C + 12], 11, -421815835);
H = md5_hh(H, G, J, I, K[C + 15], 16, 530742520);
I = md5_hh(I, H, G, J, K[C + 2], 23, -995338651);
J = md5_ii(J, I, H, G, K[C + 0], 6, -198630844);
G = md5_ii(G, J, I, H, K[C + 7], 10, 1126891415);
H = md5_ii(H, G, J, I, K[C + 14], 15, -1416354905);
I = md5_ii(I, H, G, J, K[C + 5], 21, -57434055);
J = md5_ii(J, I, H, G, K[C + 12], 6, 1700485571);
G = md5_ii(G, J, I, H, K[C + 3], 10, -1894986606);
H = md5_ii(H, G, J, I, K[C + 10], 15, -1051523);
I = md5_ii(I, H, G, J, K[C + 1], 21, -2054922799);
J = md5_ii(J, I, H, G, K[C + 8], 6, 1873313359);
G = md5_ii(G, J, I, H, K[C + 15], 10, -30611744);
H = md5_ii(H, G, J, I, K[C + 6], 15, -1560198380);
I = md5_ii(I, H, G, J, K[C + 13], 21, 1309151649);
J = md5_ii(J, I, H, G, K[C + 4], 6, -145523070);
G = md5_ii(G, J, I, H, K[C + 11], 10, -1120210379);
H = md5_ii(H, G, J, I, K[C + 2], 15, 718787259);
I = md5_ii(I, H, G, J, K[C + 9], 21, -343485551);
J = safe_add(J, E);
I = safe_add(I, D);
H = safe_add(H, B);
G = safe_add(G, A)
}
if (mode == 16) {
return Array(I, H)
} else {
return Array(J, I, H, G)
}
}
function md5_cmn(F, C, B, A, E, D) {
return safe_add(bit_rol(safe_add(safe_add(C, F), safe_add(A, D)), E), B)
}
function md5_ff(C, B, G, F, A, E, D) {
return md5_cmn((B & G) | ((~B) & F), C, B, A, E, D)
}
function md5_gg(C, B, G, F, A, E, D) {
return md5_cmn((B & F) | (G & (~F)), C, B, A, E, D)
}
function md5_hh(C, B, G, F, A, E, D) {
return md5_cmn(B ^ G ^ F, C, B, A, E, D)
}
function md5_ii(C, B, G, F, A, E, D) {
return md5_cmn(G ^ (B | (~F)), C, B, A, E, D)
}
function core_hmac_md5(C, F) {
var E = str2binl(C);
if (E.length > 16) {
E = core_md5(E, C.length * chrsz)
}
var A = Array(16),
D = Array(16);
for (var B = 0; B < 16; B++) {
A[B] = E[B] ^ 909522486;
D[B] = E[B] ^ 1549556828
}
var G = core_md5(A.concat(str2binl(F)), 512 + F.length * chrsz);
return core_md5(D.concat(G), 512 + 128)
}
function safe_add(A, D) {
var C = (A & 65535) + (D & 65535);
var B = (A >> 16) + (D >> 16) + (C >> 16);
return (B << 16) | (C & 65535)
}
function bit_rol(A, B) {
return (A << B) | (A >>> (32 - B))
}
function str2binl(D) {
var C = Array();
var A = (1 << chrsz) - 1;
for (var B = 0; B < D.length * chrsz; B += chrsz) {
C[B >> 5] |= (D.charCodeAt(B / chrsz) & A) << (B % 32)
}
return C
}
function binl2str(C) {
var D = "";
var A = (1 << chrsz) - 1;
for (var B = 0; B < C.length * 32; B += chrsz) {
D += String.fromCharCode((C[B >> 5] >>> (B % 32)) & A)
}
return D
}
function binl2hex(C) {
var B = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var D = "";
for (var A = 0; A < C.length * 4; A++) {
D += B.charAt((C[A >> 2] >> ((A % 4) * 8 + 4)) & 15) + B.charAt((C[A >> 2] >> ((A % 4) * 8)) & 15)
}
return D
}
function binl2b64(D) {
var C = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var F = "";
for (var B = 0; B < D.length * 4; B += 3) {
var E = (((D[B >> 2] >> 8 * (B % 4)) & 255) << 16) | (((D[B + 1 >> 2] >> 8 * ((B + 1) % 4)) & 255) << 8) | ((D[
B + 2 >> 2] >> 8 * ((B + 2) % 4)) & 255);
for (var A = 0; A < 4; A++) {
if (B * 8 + A * 6 > D.length * 32) {
F += b64pad
} else {
F += C.charAt((E >> 6 * (3 - A)) & 63)
}
}
}
return F
}
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 uin2hex(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
}
|
评分
-
查看全部评分
|