http://ouyangjia7.iteye.com/blog/674834

 

网页游戏外挂的编写很简单,不需要研究其源代码,不需要懂得汇编知识,只需要分析发送到服务器和服务器发送到本地的数据包就可以写出来。

      但是如果你想尽快分析数据包中的内容并得到结果,那么你可能还需要研究一下源代码。

      如果游戏是Flash做的,那么你需要下载一个Flash反编译软件,有的反编译软件像ASV 6.0提供搜索IE Cache的功能,网页游戏的Flash文件就保存在IE Cache里,找到这些文件并反编译之,就可以得到网页游戏的源代码,像风靡一时的开心农场,和我将要提到的QQ摩天大楼,都可以用这种方法得到源代码。

      有的游戏不是Flash做的,是JavasSript做的,那么你就需要分析一下那个页面的JavasSript的源代码,对网页直接查看源文件就可以得到源代码了。

      当然,像开头所说的,不分析源代码也是可行的,只要有足够的耐心,有足够的数据,再加上你的聪明才智,外挂也是可以写出来的。

      首先我要声明一句,现在的网络游戏不管是小型B/S的还是有大型客户端的,其数据的处理都已经全部放在了服务器端,也就是说,客户端不处理任何的数据,那客户端处理什么?客户端只负责向服务器端发送数据,接收数据,并向用户呈现数据。所以,现在所谓的外挂已经不能修改数据了,以开心农场为例,我们能做的无非是做点以最快的速度偷菜,自动收菜,种菜之类的工作。如果你硬是想修改数据,恐怕只有黑掉服务器或者贿赂GM两条路可走。

     虽然我说过这种外挂很简单,但我还是不得不列出制作这种外挂需要的基本知识以及为什么需要这些知识。

     (1)JavaScript。我们进入网页游戏是要先登陆的,像要进入校内(人人)网的应用就需要先登陆人人网,要进入QQ的应用就需要先登陆QQ校友或者QQ空间。没有登陆,我们连游戏都进不了,那么外挂就不能与游戏建立连接了,之后的偷菜又从何谈起?那么我们又是从哪里登陆呢?我们从网页登陆,由JavaScript负责发送与接收。所以看不懂(能看懂就行,不需要会写)JavaScript的人,请在此止步,回去翻翻书再继续。

     (2)网络编程知识。任何一门语言都可以,在此我用的是C#,你用Java,C++或者其他的什么都行,只要可以发送Http数据包。当然,能熟悉多线程就再好不过了。如果不幸的是你都不会,那就只好赶紧去学了,切记要稳扎稳打,不要好高骛远,不然只能一事无成。

     另外,如果你会Action Script,那么你就可以分析Flash游戏的代码,对外挂的编写是事半功倍的。

     或许有人已经跃跃欲试,好的,下一篇就说如何分析JavaScript代码并登陆到游戏。

 

 

 

 

 

 

 

上一篇文中提到,制作外挂的第一步,是与游戏进行连接,即登陆游戏。

    对于QQ的游戏来说,可以有两种方法登陆游戏。一种是登陆空间再登陆游戏,QQ农场,QQ抢车位的游戏都可以这样登陆;另一种是登陆QQ校友再登陆游戏,QQ农场,QQ摩天大楼都可以这样登陆。

 

    我要给大家讲的是第二种,或许有更好的方法,我在此权作是抛砖引玉。

 

我在第一篇文章中写道

 

      我们进入网页游戏是要先登陆的,像要进入校内(人人)网的应用就需要先登陆人人网,要进入QQ的应用就需要先登陆QQ校友或者QQ空间。没有登陆,我们连游戏都进不了,那么外挂就不能与游戏建立连接了,之后的偷菜又从何谈起?那么我们又是从哪里登陆呢?我们从网页登陆,由JavaScript负责发送与接收。所以看不懂(能看懂就行,不需要会写)JavaScript的人,请在此止步,回去翻翻书再继续。

 

    有人可能要问了:“我们是否可以这样做,用抓包工具把登陆时与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文件:

 

    <scriptlanguage="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分析完了,我们掌握了登陆所需的足够信息,下一篇我们就讲,如何模拟客户端进行登陆。

 

 

 

刚刚发现腾讯修改了校友网(朋友网)登陆页面的代码,把登陆窗口的地址藏在js文件中去了,因此上篇文章中讲的部分东西就过时了,但是思路还是这个思路,我还是用别的办法找到了所需的信息,大家按照我的思路好好看看javascript代码,待会我还是会告诉大家如何找到发送的完整URL。

    上一篇文章分析了腾讯校友登陆页面的JavaScript代码,掌握了登陆所需的足够信息,其实信息掌握的并不全,我疏忽了,细心的朋友肯定会问:“现在才知道密码的处理方式,我们是不是还不知道用户名和密码的发送方式?”是的,我们需要向服务器发送一个完整的URL,现在我们不知道URL是怎么组成的,就无法向服务器发送登陆请求。

    下面我们就说如何找到腾讯家的URL。

    第一步还是像上篇文章那样,找到文件comm.js,由于腾讯改了js的代码,所以我还是说一下新的方法好了。

    我们先来到朋友网登陆首页www.pengyou.com,用浏览器查看本页源文件,发现不能像以前那样找到iframe的地址了,本页有关iframe的代码如下:

     <iframescrolling="no" id="j_loginFrame" name="login_frame"

src="about:blank" width="356" height="138" frameborder="0"

scrolling="no"allowTransparency="true" style="margin-top:30px;">

    iframe的src那里是"about:blank"。

    仔细查看该页的源文件,可以发现如下三行:

    <script type="text/javascript" src="http://qzonestyle.gtimg.cn/campus/js/Mcommontmp.js">\<\/script>\
    <script type="text/javascript" src="http://qzonestyle.gtimg.cn/bailing/js/BMcommon.js">\<\/script>\       <script type="text/javascript" src="http://qzonestyle.gtimg.cn/bailing/js/BMindexLogin.js">\<\/script>

    这三个js文件一定有内容,下载下来看看。

    先看BMindexLogin.js,发现里面有个initLogin()的函数,

function initLogin() {
 var ptlogin_base_domain=DOMAINS.ROOT;
 //qq.com
 
var ptlogin='http://ui.ptlogin2.'+ptlogin_base_domain+'/cgi-bin/login?appid=15004601&qlogin_jumpname=&hide_title_bar=1&s_url=http://'+DOMAINS.MAIN+'/index.php%3fmod%3Dlogin2%26act%3Dqqlogin&self_regurl=http://'+DOMAINS.REG+'/emailreg.html&css=http://'+DOMAINS.MAIN+'/asset/login.css';
 var set_username_label=function () {
  try
  {
   window.frames['login_frame'].document.getElementById('label_uin').innerHTML='Email/QQ帐号:';
   window.frames['login_frame'].document.getElementById('label_pwd').innerHTML='密码:';
  }
  catch(e)
  {
  }
 };
 
var iframe=document.getElementById('j_loginFrame');
 iframe.src=ptlogin;
 if(iframe.attachEvent) {
  iframe.attachEvent("onload",set_username_label);
 }else {
  iframe.onload=set_username_label;
 }
 var query=window.location.search;
 if(query&&query.length>10&&query.indexOf('act=logout')==-1&&query.indexOf('mod=login')==-1) {
  var m=query.match(/\?([^&]{
   10,200
  })/);
  if(m) {
   var ref=unescape(m[1]);
   if(ref.indexOf('ref=')===0) {
    ref=ref.substr(4);
   }
   ref=ref.replace(/\&/gi,'%26');
   document.cookie='ref='+ref+';domain='+DOMAINS.COOKIE_DOMAIN;
  }
 }
 else {
  document.cookie='ref=deleted;domain='+DOMAINS.COOKIE_DOMAIN;
 }
 var frag=window.location.hash;
 if(frag&&frag.indexOf('qqreg=1')!=-1) {
  reg_by_qq();
 }

     红色的字体就是给iframe的src赋值语句,蓝色的字体就是我们要找的iframe的地址了。但仔细一看,这个地址还不能直接复制到地址栏,因为里头有一些DOMAINS.MAIN之类的东西,没事,我们去另两个js文件找,在Mcommontmp.js里,查找DOMAINS,记得区分大小写,因为貌似有很多domains,于是我们找到如下赋值语句:

    window.DOMAINS={
   COOKIE_DOMAIN:'pengyou.qq.com',MAIN:'pengyou.qq.com',HOME:'home.pengyou.qq.com',REG:'reg.pengyou.qq.com',FEED:'feed.pengyou.qq.com',APP:'app.pengyou.qq.com',API:'api.pengyou.qq.com',ROOT:'qq.com',IMGCACHE:'qzonestyle.gtimg.cn',OFFICE:'pengyou.qq.com',SHARE:'share.pengyou.qq.com',PROFILE:'profile.pengyou.qq.com'
  };

     把这些东西替换到我们之前找到的URL中的相应位置,然后就可以复制到IE地址栏,点击回车,就出现了我们上篇文章中找到的登陆窗口,查看本页源文件,找到<scriptlanguage="javascript" src="http://imgcache.qq.com/ptlogin/ac/v8/js/comm.js?v=1.2.5"></script>,下载这个js文件。

     讲得有点跳跃性,这些都是上篇讲过的东西,所以就没讲得太详细。

     找到ajax_submit,里头有这么一句:

     C.src=E.action+"?"+B;

     一般URL都是xxx+?+参数的格式,因此我们有理由猜测这句就是我们要找的登陆URL。有人会问,里头的E是什么,B又是什么,如果大家仔细看JavaScript文件,就会知道,E就登陆时要提交的表单,B就是登陆时要提交的参数。我们在回到登陆窗口的源文件,不是www.pengyou.com,是我们从js文件中找到的那个,可以在form里看到,action=http://ptlogin2.pengyou.com/login,至于参数,大家就只能在ajax_submit里面根据程序逻辑把参数一个一个的拼起来,没有捷径,除了看我拼好的,我也是一点一点拼的,拼好就是下面这个样子:

 "u=" + 您的QQ号 + "&p=" + 您的密码 + "&verifycode=" + 验证码 + "&aid=15004601&u1=http%3A%2F%2Fxiaoyou.qq.com%2Findex.php%3Fmod%3Dlogin%26adtag%3Dfrom_index&h=1&ptredirect=1&ptlang=2052&from_ui=1&dumy=&fp=loginerroralert"

     这串是我刚刚拼好的,以后腾讯肯定还要改,大家有空自己拼一下吧,实在懒就算了,我这个暂时还能用。

     现在用户名和密码的处理方式和发送方式都知道了,心急的朋友可能会问:“是不是就可以登陆了?”。

     答案是不能,因为还要验证码。我们登陆时,有时需要一个验证码,有时又不需要。对于验证码,有种简单的处理方式,就是每次登陆都要求输入,一定不会有错的,就是用起来麻烦点,如果想做得完美点,就可以像腾讯家一样自动检查一下,检查通过就不需要输入了,这样也可以,就是写程序的时候麻烦一点。

     好我不啰嗦了,下面就说说腾讯家是如何检查是否需要验证码。

     所有都在comm.js里面,有一个ptui_needVC函数,找到这个函数,代码如下:

function ptui_needVC(C,D) {
 if(t_appid==D) {
  if((C.indexOf("@")<0)&&isNaN(C)) {
   C="@"+C
  }
 }var B="";
 if(pt.isHttps) {
  ptui_checkVC("1","");
  return
 }else {
  B="http://ptlogin2."+g_domain+"/check?uin="+C+"&appid="+D+"&r="+Math.random
()
 }var A=document.createElement("script");
 g_imgTime=new Date();
 A.src=B;
 document.body.appendChild(A);
 g_loadcheck=true;
 return
}

       红色的字就是URL了,这个比较直观,不用拼参数了,g_domain可以在登陆窗口的页面源文件中找到,

     var g_domain = "pengyou.com"

     C就是QQ帐号,D也可以在登录窗口的页面源文件中找到,

     var g_appid = 15004601

     Math.random()是JavaScript本身的一个函数,函数功能是取0到1之间的一个随机数。

     把以上内容嵌进去就是检查是否需要验证码的URL了。

 

     如果需要验证码的话,还需要获取验证码的图片,获取方法都可以在comm.js中找到。找到函数loadVC,函数如下:

function loadVC(A) {
 if(isLoadVC==A&&(lastUin==g_uin)) {
  return
 }lastUin=g_uin;
 isLoadVC=A;
 if(A==true) {
  var B=$("imgVerify");
  var F=g_uin;
  if((g_appid==t_appid)&&isNaN(g_uin)&&(g_uin.indexOf("@")<0)) {
   F="@"+g_uin
  }var E="/getimage?aid="+g_appid+"&r="+Math.random()+"&uin="+F;
  if(g_https) {
   E="."+E
  }else {
   E="http://captcha."+g_domain+E+"&vc_type="+vc_type
  }var D=new Date();
  B.src=E;
  $("verifyinput").style.display="";
  $("verifytip").style.display="";
  $("verifyshow").style.display="";
  ptui_notifySize("login");
  try{
   $("p").focus()
  }catch(C) {
  }
 }else {
  $("verifyinput").style.display="none";
  $("verifytip").style.display="none";
  $("verifyshow").style.display="none";
  ptui_notifySize("login");
  try{
   $("p").focus()
  }catch(C) {
  }
 }

  红色的字就是获取的URL了,g_domain前面讲过是pengyou.com,至于vc_type,是由ptui_needVC函数返回的,其实也可以不附加vc_type,整个URL拼起来就像这个样子:"http://captcha.pengyou.com/getimage?aid="+g_appid+"&r="+Math.random()+"&uin="+您的QQ号+"&vc_type="+vc_Type

  g_appid和Math.random()上面都说过是什么,大家找找吧,我每次都说一遍好累啊。

  好了,验证码也知道怎么检查和获取了,至此,JavaScript全部分析完了,上篇也说分析完了,哈哈,不过这次是真的啦,下篇进入实战部分了,是不是好激动的?我会先告诉大家使用一个工具,非常给力的网络抓包工具—Wireshark。