上一篇文中提到,制作外挂的第一步,是与游戏进行连接,即登陆游戏。
对于QQ的游戏来说,可以有两种方法登陆游戏。一种是登陆空间再登陆游戏,QQ农场,QQ抢车位的游戏都可以这样登陆;另一种是登陆QQ校友再登陆游戏,QQ农场,QQ摩天大楼都可以这样登陆。
我要给大家讲的是第二种,或许有更好的方法,我在此权作是抛砖引玉。
有人可能要问了:“我们是否可以这样做,用抓包工具把登陆时与QQ服务器交互的数据包截获下来,查看一下发送的内容和URL,然后我们再照着同样的格式,重放一遍,不就达到目的了吗?这样做就可以省去分析JavaScript的步骤了。”
这个问题问得好,是的,登陆的时候只要知道URL,知道服务器地址,发送的内容无非就是用户名和密码,把用户名和密码插在URL中相应的位置就行了。但是真有这么简单吗?答案是否定的,原因就是密码不是用明文传送的,用户名可以是,但是密码绝对不能是,不然密码很容易被盗,只要对你的网络实施搭线窃\听就可以截获你的QQ密码了。所以密码那部分是经过处理的,一般的处理方式是用哈希算法,现在广泛使用的哈希算法是MD5,但是具体他们怎么用的哈希算法,到底是不是用的MD5,就不得而知了,我们就是要通过分析他们的JavaScript代码,知道他们是处理密码的方式,然后我们用同样的方法处理一下,然后发送给他们的服务器,才能登陆成功。
不知大家做好准备了没,看懂JavaScript也不太难,相信有八成的人是做好准备了。
我们首先打开IE,转到http://xiaoyou.pengyou.qq.com/index.html,这是QQ校友的登陆页面,查看该页面的代码。这个页面的代码没有什么有价值的东西,我们希望截取的是按下登陆按钮后,发生了什么动作。但是这些代码里找不到那个函数,甚至找不到那个按钮,连输入用户名和密码的textbox都找不到。不过别担心,看那页代码靠近最下方,有一句
var iframe_src = 'http://ui.ptlogin2.qq.com/cgi-bin/login? appid=15000102&hide_title_bar=1&qlogin_jumpname=xiaoyou_qlogin&
s_url='+url+'&css=http://imgcache.qq.com/campus/login/login.css&
self_regurl=http://xiaoyou.pengyou.qq.com/emailreg.html';
通过观察其URL的名称,我们猜测,这iframe应该就是装有登陆零配件的iframe,输入用户名和密码的textbox和登陆按钮应该在那个页面上。
我们把代码中的URL复制到IE地址栏,回车之后会发现果然是我们要找的登陆页面,截图如下:
我们继续查看该网页的代码,可以找到一个onsubmit事件,那个form提交表单时执行的事件,我们要找的就是这个,代码如下:
onsubmit="if(!isAbleSubmit){return false;};return ptui_onLoginEx(loginform, 'qq.com')";
我们发现,这个事件最终执行了一个ptui_onLoginEx函数,我们接下来得找到这个函数,按下Ctrl+F输入"ptui_onLoginEx”,但是IE会提示找不到,不过大家放心,这段JavaScript代码一定会在本机执行的,要执行就必须先下载到本机,通过仔细查找,发现原来该页面还加载了一个外部的js文件:
<script language="javascript" src="http://imgcache.qq.com/ptlogin/ac/v5/js/comm.js?v=1.8">script>
复制URL到IE地址栏,把comm.js文件下载到本机,然后查看comm.js,我们在这个文件中找到了ptui_onLoginEx函数。
function ptui_onLoginEx(B, C) {
g_time.time12 = new Date();
if (ptui_onLogin(B)) {
var A = new Date();
A.setHours(A.getHours() + 24 * 30);
if (isNaN(B.u.value) && (B.u.value.indexOf("@") < 0)) {
setCookie("ptui_loginuin2", B.u.value, A, "/", "ui.ptlogin2." + C)
} else {
setCookie("ptui_loginuin", B.u.value, A, "/", "ui.ptlogin2." + C)
}
}
return false
}
该函数调用了ptui_onLogin函数,还在本地添加了COOKIE,可以看到QQ校友登陆成功后COOKIE的有效时间是30天。
下面我们再查看ptui_onLogin函数。
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 (ptui_str(STR_UINTIP) == D) {
D = ""
}
if (!parent.ptlogin2_onLoginEx(D, B)) {
return false
}
}
} catch(C) {}
return ptui_checkValidate(A)
}
这个函数调用了parent的ptlogin2_onLogin以及ptloin2_onLoginEx等方法,这两个方法的实现无光紧要,关键看最后一句返回return ptui_checkValidate(A)。
于是我们继续查看ptui_checkValidate函数。
function ptui_checkValidate(B) {
var A = B.u;
var D = B.p;
var E = B.verifycode;
if (A.value == "" || ptui_str(STR_UINTIP) == A.value) {
alert(ptui_str(STR_NO_UIN));
A.focus();
return false
}
A.value = ptui_trim(A.value);
if (!ptui_checkQQUin(A.value)) {
alert(ptui_str(STR_INV_UIN));
A.focus();
A.select();
return false
}
if (D.value == "") {
alert(ptui_str(STR_NO_PWD));
D.focus();
return false
}
if (E.value == "") {
if (!isLoadVC) {
loadVC(true);
g_submitting = true;
return false
}
alert(ptui_str(STR_NO_VCODE));
try {
E.focus()
} catch(C) {}
if (!g_loadcheck) {
ptui_reportAttr(78028)
} else {
ptui_reportAttr(78029)
}
return false
}
if (E.value.length != 4) {
alert(ptui_str(STR_INV_VCODE));
E.focus();
E.select();
return false
}
D.setAttribute("maxlength", "32");
ajax_Submit();
ptui_reportNum(g_changeNum);
g_changeNum = 0;
return true
}
我们注意最后有一句ajax_Submit,前面一大串别看了统统都没用。
继续ajax_Submit函数。
function ajax_Submit() {
var D = true;
var E = document.forms[0];
var B = "";
for (var A = 0; A < E.length; A++) {
if (E[A].name == "fp" || E[A].type == "submit") {
continue
}
if (E[A].name == "ptredirect") {
g_ptredirect = E[A].value
}
if (E[A].name == "low_login_enable" && (!E[A].checked)) {
D = false;
continue
}
if (E[A].name == "low_login_hour" && (!D)) {
continue
}
if (E[A].name == "webqq_type" && (!E[A].checked)) {
continue
}
B += E[A].name;
B += "=";
if (t_appid == g_appid && E[A].name == "u" && E[A].value.indexOf("@") < 0 && isNaN(E[A].value)) {
B += "@" + E[A].value + "&";
continue
}
if (E[A].name == "p") {
var F = "";
F += E.verifycode.value;
F = F.toUpperCase();
B += md5(md5_3(E.p.value) + F)
} else {
if (E[A].name == "u1" || E[A].name == "ep") {
B += encodeURIComponent(E[A].value)
} else {
B += E[A].value
}
}
B += "&"
}
B += "fp=loginerroralert";
var C = document.createElement("script");
C.src = E.action + "?" + B;
document.cookie = "login_param=" + encodeURIComponent(login_param) + ";domain=ui.ptlogin2." + g_domain + ";path=/";
document.body.appendChild(C);
return
}
大家注意看我标红了的文字,这段就是对密码的处理方法了,将这段代码注释一下,方便大家看懂。
var F = "";
F += E.verifycode.value;//取出验证码
F = F.toUpperCase();//转换为大写
B += md5(md5_3(E.p.value) + F)//将密码明文用MD5算法连续哈希3次之后,加上验证码再哈希一次。
如是我们得到了QQ发送密码的方式即将密码明文用MD5算法连续哈希3次之后,加上验证码再哈希一次,再知道发送的URL和服务器地址,就可以进行登陆了。
好了,JavaScript分析完了,我们掌握了登陆所需的足够信息,下一篇我们就讲,如何模拟客户端进行登陆。