免责声明: 本文内容仅供学习与研究探讨,不得恶意传播与用于不法用途.如侵犯到您的权益,请及时与我联系.
说以上那些话,是因为害怕以下要说的内容侵犯了某公司的权益.虽然在我看来事情可能没那么严重,但是还是要谨慎点好.事情发生在昨天,无意间点开了游戏基地这个应用,好久没玩了,发现不少同学也开始玩这个了,而且积分榜上我都是垫底的.为了改善一下最近比较枯燥的生活,就玩上了.这一玩就过了1个多小时,榜上的位置却还是老样.太挫折了~~~虚荣心的驱使下,我想是不是能够自己做个小小的G来改善一下榜上的排名呢?这样一来怎么说我还是靠自己的努力向上爬的啊,只是方式有点另类而已,良心上还是说的过去.于是就开始着手分析游戏的的记分原理.
首先,可以确定的是,我们整个游戏过程都是在浏览器中进行的,也就是说网络数据的传输都是基于http的,于是我先打开HttpWatch,然后找了个最让我受挫折的游戏,打开游戏玩了一遍,httpWatch上显示这过程中所进行的请求与响应的相关数据.从一大堆请求响应中,我发现两个比较重要的请求.他们都是用post的方式发送的请求.如下图:
于是进一步对这两个请求的具体数据进行分析:
第一个post的数据是:
响应的数据是:
第二个post的数据是:
响应的数据是:
其中这两个post的url都是同一个的,而整个游戏过程中,这两个post,一个发生在游戏开始,一个发生在游戏结束后,思路大概清晰了,很容易可以想到第二次post是传送我们的分数的.也就是说游戏的记分机制都是先下载一个flash在我们机子上,玩完后根据我们的分数发送给服务器,你在本机怎么玩,服务器是不知道的,它只认最后的分数,那么我们只要可以模拟发送一个自己想要的数据过去,~~~哈哈~~~~无敌了~~~问题是我们需要发送什么数据,才能让服务器认可我们的分数呢?
又回到上面两个post上来看,从数据上来看,uin是自己的号,这个可以轻易看出,其他数据,多进行几次游戏,可以发现,pid是根据不同游戏而不同的,这个可以断定是每个游戏的id,而key这个字段同一个游戏每一次玩都是不同的,这就是关键,又从第二个post出去的数据中找到了一个key,可以轻易看出第二个key的值后半部分是第一个key的值~~~哈哈~~太幸运了,竟然没用md5,这不是摆明让我知道这两个key之间肯定存在某种联系嘛~~~
从第二次post出去的数据,一眼就看出有一个score字段,而score的值恰恰就是我们每次玩游戏后的分数,那是不是我们模拟发送一个数据包,把其中的score值改一下就可以得到一个高分了呢?事情当然没有那么简单,上面提到的key才是关键.由于之前有研究过类似的web游戏,也有一些经验,服务器可不会傻傻的看你发来的score字段值是多少,就给你记多少分.关键是会通过这个score和第一个post过来的key进行某种计算后,形成一个新的key,然后你发送这个score和新key过去,服务器再通过刚才发送给你的第一个post返回的key和你发送过来的score进行同样运算,然后把结果和你发送过来的新key进行比对,看看是否相等,从而判断你这个分数的真假.
好了一切渐渐清晰,首先两次Post过去的其他部分都可以看作常量,可以轻松的copy过来用,关键是第二次post过去的那个key是怎么计算的.可以肯定这个计算不太可能在服务器端.因为这样就是去了验证的意义了.那就是key计算放在客户端,它是在什么时候被计算出来的,又是由什么规则计算出来的呢?常识告诉我,web客户端的运算肯定就要由web客户端语言来进行,这次连接中有可能参与运算有哪些家伙呢?答案是:javaScript和flash.而且分数是在我们玩flash过程中产生的,所以javaScript不太可能参与.于是就锁定在flash上了.同样是从HttpWatch上找到游戏的flash,下载下来反编译一下,发现代码还真不少,一行一行看是不可能了,直接找key这个关键字吧,果然作者良好的编码风格还是帮了我,不一会就找到了关键的语句,发现这个关键是连接到一个叫comm的flash上.于是又下载了comm反编译一下.哈哈~~看到了,瞧它的编码风格,是多么的良好啊,从他的函数名我就知道这是一个专门负责发送分数数据,计算key,检查有没加速器作弊,和显示排行等功能的flash.而且从其名字上看,这似乎是一个公共平台的类,应该所有游戏都是用同一套方式进行key的计算和发送.这种设计确实有利于游戏的扩展,因为无论什么游戏,都只提供一个公共的接口给你,只要你在原基础上稍作修改就可以实现游戏与好友积分排行的无缝接入了.
话说回来,于是乎,我发现了这段关键代码:
function _getFinalKey(gKey, gScore) { if (!gKey || gScore == undefined) { return (null); } // end if if (gKey.length != 48) { return (null); } // end if gScore = Number(gScore); if (gScore > 999999999) { return (null); } // end if var _loc10 = gKey; gKey = gKey.substr(42, 6); var _loc8 = parseInt(gKey, 36); var _loc4 = _loc8.toString(35); var _loc13 = _loc4.length; var _loc3 = parseInt(gKey.substr(gKey.length - 1), 36) % _loc13; var _loc6 = gScore.toString(36); var _loc12 = _loc4.substr(0, _loc3); var _loc7 = _loc4.substr(_loc3); var _loc5 = _loc3 + 10; var _loc11 = _loc5 + _loc6.length; var _loc9 = [_loc5.toString(36), _loc12, _loc6, _loc7, _loc11.toString(36), _loc10].join(""); return (_loc9); } // End of the function
现在,大致的思路出来了,我只要根据上面第一个post的数据规格,先发一个数据包过去,然后服务器就返回一堆数据给我,而我只要提取其中的关键数据key字段,然后根据上面关键代码的计算规则进行计算,再按照第二个post的数据规格发送回去,如果我得到了响应,而且响应的数据和我们正常游戏的一样,那就表明我成功了.不过,这里还有一个问题不能忽略,那就是session,也可以说是cookie,因为服务器判断session id的依据一般就是你的cookie.也就是说,你要拥有登陆到游戏基地的cookie,服务器才认为你现在在游戏,然后才会承认你发送过去的数据包,没有cookie什么都是扯淡了~~~cookie是一件麻烦的事,因为服务器响应中的任何javascript都可能对cookie进行修改.而如果你要自己模拟这些发到服务器上的cookie是一个痛苦的过程,因为你要去发现这些cookie什么时候发生变化了,发生了哪些变化,是怎么变化的.要知道,一旦cookie被服务器知道有假,它就会无情的拒绝你的数据包.
不过事情并不用那么复杂,我想到了用.net的webBrowser控件,先登陆自己的空间,然后利用这个cookie进行邪恶的数据包传送.不过,这个弄起来就一个字"烦"~~是否有更简单的方法呢?这时我想到了javaScript.这是一个伟大的动态语言.而且更难能可贵的是,它的解释器是无处不在的.我发现上面两个post回来的数据都是以json形式传送的,用javascript来处理再合适不过了,不过对javaScript还是有点生疏,而且Ajax这部分没有涉猎过.更重要的是javaScript是否能解决cookie问题呢?
我想到我在用的多标签浏览器,一般我们用多标签浏览器,cookie应该都是共享的,因为例如去登陆淘宝,然后从新标签中打开淘宝的其他网页,用户登录状态依然存在,如果是这样的话,我只要做一个html文件,然后先在浏览器中登陆到空间,有了cookie后,在用该浏览器打开这个html,运用javaScript模拟发送数据包,cookie就不是问题了.
首先要让javaScript实现模拟发包.于是用到了一个叫XMLlHttpRequest的对象,据说这个对象就是传说中的Ajax的灵魂.更让人惊喜的,flash的As语言与javaScript的语法极为相似,上面的关键代码可以直接Copy过去.一番折腾后,成功了~~哈哈~~而且上面我们提到,comm这个flash是公共的,而且每个游戏都有一个独立pid,也就是说其他游戏的分数的修改方法也都是一样,只是改个id而已.于是乎改了个id试试,又成功了!哈哈~~又进一步思考,如果每次我要改哪个游戏都要手动去找id,那不是很麻烦吗?于是乎,我发现了一个核心的js文件.这里面用一个json记录了当前所有游戏的信息,相信一旦有更新都会写进去,这样我只要读这个json就可以了.
到此,事情完满成功,不过各位不用担心,我并没有用这个来为非作歹,因为那样没意思.影响大家情绪,只是在自己骗自己,最近生活比较枯燥,所以要寻求一些有趣的事情来改善一下,~~哈哈~~出于和谐的考虑,这里就不放出完整源码了.另,出于测试需要,和虚荣心上的满足,所以~~~(人格保证就这么一个,而且以后相信我应该不会再干这种事情,只是测试需要.况且数据并不夸张~~)
这是原来的~~
这是~~~哈哈~~~
js部分的实现,其中用XXXXXX来和谐掉部分需要和谐的代码:
var retData = null; function postScore(id,score){ var data = "act=get&pid=" + id; var url = "XXXXXXXXXXXXXX"; var onComplete1 = function(){ var fnKey = getFinalKey(retData.key,score); var lastData = "key="+fnKey+"&submit=true&uin="+retData.uin+"&score="+score+"&act=set&pid="+id; var onComplete2 = function(){ if(retData.score)alert("修改成功!"+"分数为:"+retData.score); else alert("修改失败!"); } postData(url,lastData,onComplete2); } postData(url,data,onComplete1); } function postData(url,data,onComplete){ //实例化Ajax var ajax = null; if(window.XMLHttpRequest){ ajax = new XMLHttpRequest(); } else if(window.ActiveXObject){ ajax = new ActiveXObject("Microsoft.XMLHTTP"); } else{ alert("可能由于浏览器的原因,发生错误!"); return null; } //通过Post方式打开连接 ajax.open("POST", url, true); //定义传输的文件HTTP头信息 ajax.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); ajax.setRequestHeader("Referer", "XXXXXXXXXXXXXXXX"); //发送POST数据 ajax.send(data); //返回数据的处理函数 ajax.onreadystatechange = function(){ if (ajax.readyState == 4 && ajax.status == 200){ retData = eval("("+ajax.responseText+")"); onComplete(); } } } function getFinalKey(gKey, gScore) { if (!gKey || gScore == undefined) { return (null); } // end if if (gKey.length != 48) { return (null); } // end if gScore = Number(gScore); if (gScore > 999999999) { return (null); } // end if var _loc10 = gKey; gKey = gKey.substr(42, 6); var _loc8 = parseInt(gKey, 36); var _loc4 = _loc8.toString(35); var _loc13 = _loc4.length; var _loc3 = parseInt(gKey.substr(gKey.length - 1), 36) % _loc13; var _loc6 = gScore.toString(36); var _loc12 = _loc4.substr(0, _loc3); var _loc7 = _loc4.substr(_loc3); var _loc5 = _loc3 + 10; var _loc11 = _loc5 + _loc6.length; var _loc9 = [_loc5.toString(36), _loc12, _loc6, _loc7, _loc11.toString(36), _loc10].join(""); return (_loc9); } // End of the function function setGameList(){ var selt = document.getElementById('gamelist'); var opts = selt.options; for(var key in gameInfo) opts.add(new Option(gameInfo[key].name,key)); }
最后再声明一下,本文内容仅用于学习与研究探讨,请勿恶意传播与用于不法用途.