osx平台上lol英雄联盟launcher启动器的分析实现 我算个LOL玩家吧,虽然是郊区白银段位的,虽然能1打5个电脑,但瘾不小。每次想打LOL,都找不到PC电脑,看着OSX却不能玩国服,只能玩美服、韩服。而外服的网络延迟比较大,根本没法打,跳帧严重,野怪都打不过。每次瘾来了时,总是想在OSX上也能玩,次数多了,决定试试,看看能否在OSX上玩国服。 15年10月份对LOL英雄联盟录像做过解析器在mac osx上看lol国服ob录像的技术分析,文中最后提到过关于LOL客户端架构的组成,以及在OSX上打LOL国服还是有可能的。 现状分析,League of legends有mac版本,在riot拳头运营的地区中,有韩服、美服、欧服之类国外服务器。而腾讯运营国服版,却没有OSX版本。上次的录像分析中,是腾讯运营的windows版本产生的录像文件,可以在OSX版本的美服上解析、渲染、播放,也就以为着协议是相通的,也就意味着用riot运营的OSX客户端,连接腾讯运营的服务器,应该可以正常通讯,正常游戏的。 先来看下国服League of legends英雄联盟的目录结构 Air目录为LolClient.exe所在目录,即游戏大厅目录,air的,也就是flash as的,意味着反编译比较简单... Config目录确定 Cross目录为腾讯游戏必装的各种乱七八糟工具包目录 Game目录为game进程League of legends.exe所在目录 log目录,我也不知道这是记录啥日志的,一直是空的 logs目录,这是launcher启动器记录通讯数据的目录 TCLS目录是腾讯登录SSO的客户端程序目录 TQM目录,腾讯自己的,跟游戏无关 先来对比Windows跟osx上区别,riot运营的(以下以美服代表)游戏,是先选地区,再进入登录界面,再进入游戏。而腾讯运营的是先登录,再选择大区,再进入游戏。显然,腾讯运营的选大区,可以理解为riot运营的选地区,之后再进入游戏。区别是riot先选区,再登录;腾讯(以下简称国服)的先登录,再选区。先看国服在windows上流程,快捷方式打开的是TCLS\Client.exe,内嵌登录的DLL,登录之后,选大区,直接开启LolClient.exe进入对应大区游戏。而美服的登录窗口是在LolClient.exe中完成的,不难看出区别是美服手动登录,国服自动登录。显然,是LolClient.exe启动时,CLI参数包含了一些KEY,来实现了自动登录。 也就是说,问题在登录认证部分,只要我在OSX上能实现LolClient.exe的自动登录就可以...当然要在windows上获取登录的Game Signature Key,先来看下win上的进程启动信息: 果然是CLI中参数带有signature key,参数如下: 可以看出,除了Air运行时参数外,还有8393数字,认证的KEY,当前大区服务器域名,xmpp聊天服务器地址,登录服务器地址之类。另外,在看下LolClient.exe的进程树: 如上图,游戏大厅进程LolClient.exe跟game进程League of legends.exe都是lol.launcher_tencent.exe的子进程。 这里的8393数字其实是LolClient.exe要连接的TCP端口,也就是lol.launcher_tencent.exe进程监听的端口, lol.launcher_tencent.exe进程再fork起LolClient.exe进程,进入游戏大厅。 游戏开局匹配时,都是在LolClient.exe中进行的,匹配完成之后 ,lol.launcher_tencent.exe又createprocess出Game\League of Legends.exe进入游戏。 那么,只要我在osx上实现一个launcher的功能,就能在osx上玩League of legends英雄联盟的国服了,感觉很兴奋的样子,继续往下看...... LolClient.exe进程进入游戏后,lol.launcher_tencent.exe进程是如何知道的呢?它又是如何决定启动League of Legends.exe进程的?在上篇在mac osx上看lol国服ob录像的技术分析中,提到过,game进程启动时,命令行参数有个端口8394,先查看进程监听列表 从结果中,可以看到进程lol.launcher_tencent.exe监听了本地的8393、8394、8395端口,而且,LolClient.exe已经连接了8393端口,也就是其启动时命令行参数中的对应数值。那么另外两个端口8394、8395也应该是其他进程来连接的咯。抓本地数据包,看看他们之间有没有通讯,如看看他们通讯了什么内容:(在windows上,wireshark抓不到回环地址通讯包,可以用rawcap来抓包) 之后,wireshark打开这个文件,可以看到如下TCP数据包: 从协议包结果上来看,对比发送、接收的4个包作为例子,不难看出,他们的数据包格式拆分为前8bytes10 00 00 00 01 00 00 00为固定值(暂且认为是固定值);后面4bytes04 00 00 00、05 00 00 00为一段;再后面的4bytes00 00 00 00、34 00 00 00为另一段;最后一部分的Nbytes长度31 34 2e 31 37 2e 32 33 2e 39 34 20 35 31 35 36 20 51 62 77 41 34 73 6f 42 38 4a 71 37 43 4f 45 51 2b 31 78 63 34 67 3d 3d 20 34 30 30 36 33 31 39 32 33 32为最后一段;以第4个通讯包来看,第三段的对应数值是0x34,显然是LittleEndian小端字节序,显然后面的00000020 - 00000054部分数据长度也是0x34个,也就是说,协议通讯格式的第三段表示后面消息包长度的含义,第四段就是主体消息正文。第一段、第二段都是消息头的部分。第二段多数为command指令。暂且这么假设,那么通讯协议包格式确定了,接下来就要确认下每个command对应的业务含义了。先看下LolClient.exe进程发给launcher,跟League of legends.exe发给launcher进程的TCP数据包,初步确认下我对协议包格式的判断是否正确 如何查找command的类型一共有多少种?如何确认每种command的意义?记得最初LOL目录结构里提到大厅客户端LolClient.exe是flash air的,反编译比较方便,入口文件是LolClient.swf,跟下去是mod/win/ClientWindow.dat,然后加载了&^*($#@#,as代码太乱了,而且,都分布在各个小的flash中,目录还不统一,拓展名还都是dat的,还得手动改成swf,反编译工具才能选它,as代码也不好理解,检索麻烦,索性直接反编译lol.launcher_tencent.exe进程吧。 IDA的导入lol.launcher_tencent.exe进程后,在imports中看到 Maestro_Remove、...等来自RiotLauncher这个library 再静态分析RiotLauncher.dll,在Exports里看到Maestro_MessageTypeToString函数跟进,看到MAESTROMESSAGETYPE_GAMECLIENT_CREATE等字样 ,F5导出 至此,可以看到lol launcher的协议指令类型都在这里。再看看哪里调用了Maestro_MessageTypeToString函数: 再看看MaestroMessageAgent_SendMessage的调用处: 从MaestroGameController_SendGameConnectedToServerMessage函数名的字面含义上理解,此函数为『当游戏连接到服务器时,发送消息』,继续跟进...可是,我静态分析水平比较弱,越是继续跟进,我的思路越混乱,决定另辟蹊径。 在文中最初的LOL目录结构中,有个Logs目录,这个目录下Maestro Logs目录中的maestro-server.log、maestro-game_client.log中有一些日志,是开发人员用来排查调试程序执行结果的信息,大约如下: 这里确实可以对应着launcher协议中,某些command被接收后,会被记录到这里。如何确认每个日志记录指令对应是哪个TCP包发送后的结果呢?如何利用这些日志信息呢?如果windows上有类似linux的strace查看系统调用的工具就好了,你还别说,还真有一款功能类似的,就是ProcessMonitor,有了这工具,我就可以看到lol.launcher.tencent.exe进程在何时发送TCP数据包,发送后写了哪个文件数据,写了什么内容,先写日志还是先发TCP数据包。。。 如上图,可以看到先发TCP包,还是先写日志文件,哪个进程发,哪个进程写。但还有一点要确认的是,发的TCP包的内容如何跟写的日志内容配对问题,显然,ProcessMonitor工具里有些更有用的信息,你可能没注意到进程每次发包后,会记录发的包大小;进程每次写入文件数据时,会记录写时的offset以及写入文件长度,根据launcher进程写日志时,获取的offset位置,可以找到对应maestro-server.log文件的offset,再根据写入文件时的length数值,可以确定写入内容是什么。那么,机智的你不难看出,写入日志内容的字面含义,很容易确认了TCP数据包中对应的command代表的含义,也明白了command代表的业务逻辑。 好了,lol.launcher_tencent.exe的功能大约理清楚了,再来回顾下League of Legends的客户端架构,以及猜测下服务端架构,根据我的理解,我画了一副图 架构图中的虚线部分,为服务器之间节点通讯,是我的猜测,并不是想客户端部分那样,按照分析客户端通讯得来的架构图。可能不准确。 匹配请求处理服务器,用来处理客户端的登录认证、符文、天赋修改、英雄列表,进入战斗的匹配请求等。 匹配服务器,应该是整个大区用同一个,这里会预算所有与大区玩家相关的数据,战斗力匹配,寻找想匹配对手,划分到对应服务器中。这是核心点,也是瓶颈点。 房间管理服务器,应该要独立拆分出来,与匹配服务器解耦,与战场服务器解耦。当匹配服务器完成后,只要将匹配结果的10名玩家,玩家的符文、天赋配置等发送给房间管理服务器,然后由房间管理服务器去创建对战进程,再获取对战服务器的IP PORT 以及每个人的加密KEY(也应该是认证该角色的凭证),比如"14.17.23.94 5156 QbwA4soB8Jq7COEQ+1xc4g== 4006319232"发送给每一个客户端。客户端拿到消息,再发送给lol.launcher.tencent.exe,再有它启动游戏进程。 房间管理服务器都是可以横向扩展的,尤其是游戏服务器,更是可以横向扩展,单个游戏进程,只跟本次游戏中的10个玩家有关。 port范围5000-5500,也就是说单台服务器最多开500个端口,也就是500场战斗,以每场战斗10个人来算,单台服务器是5000人的承载。实际上可能要依赖服务器的性能承载。15年初网上消息LOL同时在线750W,时隔1年,国服现在同时在线有多少人?能承载多少玩家同时在线?匹配服务器虽然是很难拆分的节点,但业务比较单一,CPU密集型,应该大概也许可能不难解决。其他节点,拓展起来就更方便了,堆堆服务器应该可以解决。当然,我没坐过类似的客户端游戏架构,只是基于页游、手游的经验猜。 回到正题,LOL launcher的业务确定了,与另外两个进程通讯的协议格式也确定了,看看实现时,我要怎么做 监听系统信号的线程 监听TCP 8393端口,等待LolClient.exe连接 监听TCP 8394端口,等待League of legends.exe连接 监听TCP 8395端口?暂时不监听了,目前不知道这端口是干啥的 启动client的线程 保持与client的心跳线程 启动game的线程 保持跟game的心跳线程 接收来自game消息的心跳线程 处理client消息转发到game的线程 处理game消息转发到client的线程 解析协议数据包,获取command,数据包长度,数据包内容 分析command,心跳包直接回复。与game无关消息包,直接回复MAESTROMESSAGETYPE_REPLY类型已收到该消息。 部分消息需要转发给game进程,走chan转发,比如进入游戏后,好友还是可以在游戏大厅发消息到正在游戏中的玩家的,聊天消息转发MAESTROMESSAGETYPE_CHATMESSAGE_TO_GAME 就是从client发到launcher,再有launcher转发至game的。 处理client消息转发到game的线程、处理game消息转发到client的线程 当然,还得再启动两个线程来接收launcher转发来的消息, 等等等等,一系列代码写完后,本以为终于可以在osx上玩LOL国服了,没想到,我还是太年轻,想的太简单了。程序启动LolClient.exe之后就假死了,一番排查后,发现是TCP粘包问题 尼玛,是LolClient.exe发送给launcher.exe的TCP数据包分了两个TCP包发送的,尼玛,100个字节都不到,还分两个字节,尼玛。。累不累? 一系列代码写完后,本以为终于可以在osx上玩LOL国服了,没想到,我还是太年轻,想的太简单了。我写的lol launcher启动Lolclient.exe没问题,符文、天赋编辑也都没问题。但启动League of legends.exe时,却进程崩溃退出了,而我却百思不得其解,为什么?通信协议一样的,发送command时机一样的,为什么会崩溃呢?这突然的失败,让我思路中断,思绪全无,不知道如何进行下去。。。。。。冥冥中,我记得在静态分析lol.launcher.tencent.exe时,看到了一个特殊的函数名再回到ProcessMonitor中看看League of legends.exe启动时的环境变量是什么 显然是__COMPAT_LAYER=ElevateCreateProcess(不要问为什么显然...我也曾经懊恼过,上学时,老师总是说显然,可我就是不明白为什么显然) 在OSX上,也找下环境变量是什么获取LeagueOfLegends.app进程执行时的环境变量,去除系统默认的几个,剩下的,就是游戏自己添加的 一系列代码写完后,本以为终于可以在osx上玩LOL国服了,没想到,我还是太年轻,想的太简单了。除了程序执行时的环境变量外,还有一个程序执行时的当前目录: 记得要chdir一下,这么改进后,本以为终于可以在osx上玩LOL国服了,没想到,它确实在windows上登录国服,终于运行起来了 它也在OSX上登录美服,成功运行起来了 那么我只要把OSX上启动LolClient的命令行参数改为国服的参数,就可以登录国服,玩国服游戏咯,尝试一下果然可以,我情不自禁的开始佩服我的聪慧,机智,下一步就开房间,匹配打国服了?然而,我还是太年轻,想的太简单了。程序启动League of Legend.app时出错了,进不去,日志中记录 之后,游戏进程就报错退出了。我太年轻了,想的太简单了......可是,我好像已经不年轻了...... 这到底是为什么,玩个游戏怎么这么难,这么难?我只是想玩个游戏而已,为什么这么难?腾讯LOL项目组的朋友,能不能告诉我,你们还验证了什么?很明显不是版本号,是不是操作系统?是不是拒绝了OSX的登录? 曾经,我曾尝试过从协议上入手,看看LOL游戏协议能不能搞定,可难度比较大,花了好几个星期的空余时间,大约确认了是UDP Enet协议格式,后来,也在网上搜到相关信息,确认了。但网上公开的协议格式,是4.23以前的,现在都6.*的版本,早就改了....唉,以后再说吧... 最近两天,总结了之前研究LOL Launcher的实现过程,疏于工作,拖延了工作进度,我自己也是内心有愧。有愧的不仅仅是会议上的苍白答复,也还有这篇总结本计划与2015年底写完的,也还是拖延到现在。 总结完这边博文,接下来要写部门的年终总结了,离丙申年到来还有5天,犯有严重拖延症的我,能把我自己的年终总结,在乙未年结束之前写完吗? 参考资料: Evolving the Server-Side Architecture of League of Legends IntWars Scaling League of Legends Chat to 70 million Players 代码开源: 2016-02-03 +16 20 0
webgame中常见安全问题、防御方式与挽救措施 十月一的假期间,在知乎上看到一个问题《网页游戏都有哪些安全问题?》,我是一个网页游戏开发者,对这个问题非常感兴趣,印象比较深刻。当时是在游玩,也没时间细看这个问题。后来,在微博上,有一位朋友的转发,又让我看到这个问题,冥冥中,有种想回答的冲动。上周六时,研发部门内部周会时,听到其他项目组的一个整型溢出问题,导致刷钱的bug,又让我想起这个问题,更加坚定我要回答这个问题的决心,总结一下这项目中,所有经历过的webgame安全问题的经验,以加固当前项目安全壁垒,避免损失。亦可分享给其他做webgame研发的朋友,做交流探讨。 知乎中的原问题是『网页游戏都有哪些安全问题?』,我觉得不妥,我给改成了『网页游戏都有哪些安全问题?如何做得更安全?』,同时,问题也从『大家来研究探讨一下,网页游戏攻防技术。必定,这个话题很敏感。目前,网页游戏已经很多了,会不会被黑产盯上?网页游戏会不会被黑,数据库会不会被拖库』改成了『大家来研究探讨一下,网页游戏攻防技术。必定,这个话题很敏感。目前,网页游戏已经很多了,会不会被黑产盯上?网页游戏会不会被入侵?入侵方式有哪些?如何做好网页游戏的入侵防御?挽救措施有哪些?如何才能最小化减少厂商损失?入侵方式有哪些?如何做好网页游戏的入侵防御?挽救措施有哪些?如何才能最小化减少厂商损失?』,更改的理由是『本文原提问者开篇提到「大家来研究探讨一下,网页游戏攻防技术。」,那么应该不光提到如何入侵,更应该提到如何防御,应该细心描述漏洞形成原理,规避方式,以提高研发者技能水平;应该详细讲解安全事件发生后,如何最小化减少厂商损失,减少用户损失,保护游戏平衡。』,幸运的是,这个修改,被知乎通过了。对此,表示感谢。 在后来阅读这篇提问以及回答时,已经有几位网友回答了,多数是站在安全工作者角度上回答了这个问题。在这篇日志里,我将以webgame研发者角度,切合游戏业务模块逻辑,从业务需求,数据库设计,程序编写,操作方式上来讲解漏洞形成原理,规避方案,也欢迎大家讨论。 登录认证 近几年,网页游戏几乎都是以联运方式运营,意味着游戏服务器本身不保存用户密码,用户登录在平台,通过平台跟游戏服务器的接口对接登录。接口做加密认证。故webgame的帐号密码安全问题,这里不提了。但登录认证的hash字符串安全,也还是要注意的。比如登录hash字符串的生效时间,hash字符串的加密参数来源,比如包括用户名、登录IP,浏览器user-agent等数据,以防止改hash被泄漏了,也是很难通过服务器的验证。 游戏充值 webgame的游戏充值流程,跟普通网页充值流程一致,没有特殊的地方,其不同点就是跟其他众多平台做联合运营时,势必要每个公司做接口对接,且接口规范各式各样,且游戏厂商没有话语权,必须按照他们的接口规范来,这实在棘手。腾讯的充值接口的验证方式,安全性做的较为突出,大约代码: 在此基础上,还可以做的严谨点: 增加随机参数名、参数值。随机参数名、参数值由联运方随机生成,按照参数名的字符串所属ASCII码顺序排序,参数名、参数值均参与sign的计算,增加暴力破解密钥(app key)难度。 增加回调验证订单号,金额信息。游戏充值服务器接收到充值请求时,反向到该平台回调接口,确认此笔订单有效性,以防止加密密钥泄漏的问题。 远程文件引入 在网页游戏的研发中,多数都是使用框架来做,即使用REQUEST来的参数,作为请求文件名的一部分,来使用,那么很容易形成远程文件引入的漏洞。在我们之前的游戏中,曾出现过一例这样的漏洞问题。 从代码以及案例图中,可以看到对于REQUEST的参数没有过滤处理,直接作为文件名来include引入的,故导致这种问题,类似上页图中,若PHP version < 5.3.4 ,还会发生Null(%00) 截断的问题,带来更大的安全问题。在我们新的项目中,我们更改了实现方式,我们游戏所有接口都会走gateway,gateway里,对控制器名做类名规范的检测处理,再在指定几个目录下做autoload加载文件,且还会对REQUEST的类名、方法用ReflectionClass反射类的处理,检测到类、方法、参数是否合法。一来避免『远程文件引入』漏洞问题,二来便于前后端联调时,抛出更详细的异常,方便调试。下面为参考代码: [code lang="php"] require_once CONFIG_PATH . "/auto.php"; spl_autoload_register("__autoload"); ...... //默认消息格式 $view->clear(); $view->error(MLanguages::COM__INVALID_REQUST); $msg = new Afx_Amf_plugins_AcknowledgeMessage($val->data[0]->$messageIdField); $msg->setBody($view->get()); $message->data = $msg; ... $a = new Yaf_Request_Simple(); $a->setControllerName($method[0]); $a->setActionName($method[1]); $objC = new ReflectionClass($method[0]."Controller"); $arrParamenter = $objC->getMethod($method[1]."Action")->getParameters(); $arrRequest = isset($val->data[0]->body[0]) ? (array)$val->data[0]->body[0] : array(); $bCanCall = true; foreach ($arrParamenter as $objParam) { $parm = $objParam->getName(); $bIsOption = $objParam->isOptional(); //是否为可选参数 if (isset($arrRequest[$parm])) { $a->setParam($parm , $arrRequest[$parm]); } elseif ($objParam->isOptional()) { //可选参数 } else { $bCanCall = false; } } if ($bCanCall) { $rp = $app->getDispatcher()->dispatch($a); $msg = new Afx_Amf_plugins_AcknowledgeMessage($val->data[0]->$messageIdField); $msg->setBody($view->get()); $message->data = $msg; } [/code] SQL 注入 SQL注入原理、方式,跟普通web应用一样,没什么特别的,在使用REQUEST来的参数时,过滤处理即可。可能在消息格式,以及注入操作简便上,会蒙蔽研发人员的眼睛,被忽略掉了。比如我们项目的AMF消息格式,在前端界面没出来之前,我们后端程序员一般使用Pinta来模拟操作,调试程序。前端界面出来之后,会使用Charles proxy来捕捉http请求。在这些过程中,请求接口、参数的构造,没有普通web那么简单。研发人员也容易忽略对请求参数的过滤,故很容易形成这种问题。形成原理见:《WEB开发安全与运维安全浅见》,防御方式做过滤处理,或SQL预编译。 为了提高游戏服务器的吞吐能力,网页游戏的架构也是一直在演变的。在之前以Mysql作为数据存储的webgame架构中,其他节点都是可以水平扩展,或者说依赖简单粗暴的增加服务器来解决,单单作为唯一数据存储中心,不能这么做。为此,很多webgame的数据存储改用Nosql来代替,甚至java、C/C++的游戏数据,直接在内存中操作,游戏关服时,才写入到DB中。故SQL注入的问题,也会越来越少。 通讯协议与消息格式 网页游戏虽然名字叫网页游戏,但通讯协议并非全是http,也有很多使用socket,以及http+socket并用的做法。我们是http协议+amf消息格式,以及socket并用来实现。在http与https的取舍上,我们考虑到ssl的启用后,大量的ssl解密加密运算,势必会增加服务器大量的CPU计算压力。而传输的内容,多数是游戏业务的操作,响应,是能接受被监听嗅探的行为的(认证信息除外)。站在安全角度,这不能理解。但站在产品角度,考虑一下 投入产出,然后选择http通讯,也是可以理解的。socket在我们游戏中,除了在聊天应用上使用外,在一些组队、帮派战之类需要多个玩家之间同步数据信息时,我们也会使用socket来推送数据。在使用socket作为所有业务传输的协议时,协议格式一般都是开源协议,比如msgpack、protobuf之类,或者自定义的协议。使用自定义协议时,务必检测整个消息包的每一个参数,类型范围,避免个别超大数值、边界数值出现,导致主程序内存越界,以至于服务宕机,无法正常服务的情况发生。 金币复制-整型溢出 上周周六开周会时,听到其他项目组的一个关于整型溢出导致产生刷金币的问题。在这里,我抽象该案例,分享一下。商城出售开启背包格子的所需道具『梧桐木』。在游戏中,用户包裹格子数量一般都会作为一个收费点,一款游戏的格子大约为每行7格子,一共8行这样。比如前面3行是默认开放的,第4行是收费的,而且第一个格子所需品梧桐木的价格1个银子,第二个梧桐木是2个银子,第三个是4个银子。依次类推,意味着这些梧桐木的价格总和其实就是一个第一项为1,公比为2,项为35的等比数列。 当用户选择购买梧桐木数量大于31时,比如32-36中这些数字时,这些等比数列的和就是大于2147483647。(只是举例,实际上不会以这样的价格出售物品) 在java中,4字节的存放int型变量的范围是-2147483648至2147483647。在java、c的有符号int型中存储时,数的最高位描述符号位,4字节共32位,除去最高位的符号位,剩下31位,每个位上能表示2个数字,4字节的有符号的整数表示范围为:负整数2^31个,范围为『-1至-2147483648』;正整数2^31个,范围为『2147483647至1』。 比如下图(注意十进制数字跟二进制表示的变化顺序): 当开启格子数字为大于31时,比如32,那么所需费用就是2147483647个银两,再买点其他物品,凑成超过2147483647的数字,比如又买了3个银子的其他道具,总共花费2147483650个银子,在4字节的有符号int中表示出来的结果,变成符号位为1,即负整数。数值位为0000000 00000000 00000000 00000010,也就是10000000 00000000 00000000 00000010,对应十进制的-2147483646。程序逻辑上,再判断现有银两是否足够支付此笔花费时,是通过的。当使用当前余额减去这笔花费时,将变成减去一个负数,那么实际上就是加上一个正整数。变成了自己银两账户余额的增长。而余额字段类型是long,则正确的存储了这些余额,溢出漏洞被利用。在C中,使用无符号的数值类型,即可完成数值类型溢出刷钱的行为,但在java中,好像没有无符号的类型。这也可以先确定所有参与计算的数值必须为正整数作为必要条件(游戏业务特性,游戏内所有数字,肯定全为正整数,甚至都不包括零),先做大小判断,再做正正相加,不能得负;负负相加,不能得正。来判断是否发生了溢出问题。在PHP中,不用担心溢出问题。 更多信息参考:《溢出,符号与进位》 不光是程序中,整型变量类型得需要注意,使用unsigned int在存储数据的DB中也要做同样的处理,对于存储整型数字的字段,均设置为unsigned int/tinyint/...。在游戏中,数字几乎全是自然数,几乎不会出现负整数的情况。 道具复制-逻辑错误(2014/07/23 更新) 在我的新款webgame中,又发生了一起刷道具的问题,问题发生在背包内道具移动的业务上,业务需求如下: 当从A位置移动到B位置时,若两者是同一类型,且可叠加(两者都是绑定或不绑定),则叠加数量到其中一个上,并销毁另外一个。 若不是同一类型,或不可叠加,则两者呼唤位置。 若目标位置为空,则把物品从A移动到B。 如果这个功能由你看写,你如何写? 程序员的全部代码在 gist:b7d794d3cb663dd86aad 可能比较长,其实这次出问题的在下面的代码中 其出现问题部分就是如上这些,大家看代码时,会认为这前期的判断都是正确的,符合业务逻辑的,问题却发生了“来源格子”跟“目标格子”的判断,没有判断这两个参数是不是同一个位置,若是同一个位置,那么上面的所有业务逻辑判断都是合法的,下面的代码中,将根据这“两个”道具的属性进行堆叠,然后就发生了道具复制的问题..... 修复方法想必大家都会,就是判断来源位置目标位置是否相同 如果是你,你会犯这样的错误吗?我想.....呵呵... 金币复制-并发请求 Rpg类型的网页游戏中,多数都有道具出售的功能,直接卖到商店,以及道具材料从商店买入功能。当玩家同时针对买入、卖出两个操作,瞬间大量并发请求时,在服务器的处理逻辑一般有分别的两个进程处理,共享数据分别数据库中的对应账户余额表,如下图: 卖出请求的处理进程为1,买入请求的处理进程为2。在进程1还没将结果写入到DB时,进程2也从DB读取到余额为50。这是,两个进程拿到的余额信息都是50。进程1按照逻辑代码,计算出剩余余额是150;进程2计算出的剩余余额是0。最后,不管那个进程最后写入余额,都是错误的结果。(注:这里的代码逻辑操作,跟mysql事务无任何关系,事务只能保证单个进程的事务范围内多条语句都正确执行,或回滚。比如能保证扣钱成功,且物品删除掉的两个语句都正确执行。能保证其中之一的语句执行失败时,都正确回滚。) 其实,在事物开启时候,SELECT语句是否可以取到最新的数据,或者是否需要等待锁释放,取决于MYSQL的事务隔离级别。在MYSQL的事务隔离级别中,有一下几种隔离级别: READ-UNCOMMITTED(读取未提交内容)级别 READ-COMMITTED(读取提交内容 REPEATABLE-READ(可重读) SERIERLIZED(可串行化) 对于READ-UNCOMMITTED,可以读取其他事务中未提交的数据,而且据说性能还高不到哪里去,几乎没有在实际应用中使用;对于READ-COMMITTED,在同一事务中,会因为其他事务随时可能有新的commit,导致同一select可能返回不同结果。这个也不适合游戏业务;再说第四个SERIERLIZED,只要事务开启,所有其他查询,均排队等待该事务提交之后,对于上面提到的卖出买入情况,第二个事务的SELECT操作,不会立刻返回,会处于锁等待状态,一直到前一个事务结束。这个隔离级别,虽然能避免上面的问题,但性能较差,一般不会去使用。而REPEATABLE-READ隔离级别,也是mysql默认的隔离级别,从功能上,比较符合游戏业务需要,也应该是广大webgame架构中mysql的默认隔离级别。在使用REPEATABLE-READ隔离级别时,select的数据,都是事务未提交之前的数据,而每个事务都能正常成功执行,故错误的结果被执行出现。 对于这个问题,你可能很快就给出解决办法,把UPDATE语句改为UPDATE `role_gold` SET gold = gold + 100 WHERE role_id = 1或者UPDATE `role_gold` SET gold = 150 WHERE role_id = 1 AND gold = 100来解决,但这种多个事务同时操作修改多个表的多条记录时,还容易引发死锁问题,比如《webgame中Mysql Deadlock ERROR 1213 (40001)错误的排查历程》。而且,当条件为跨表内数据是否存在,或者另外条件不在MYSQL中,而在其他网络接口的响应中时,如何做呢? 金币复制--逻辑漏洞 引用DNF的漏洞新闻 《利用网游漏洞狂刷游戏币赚钱 玩家自曝3天赚17万》 玩家曝出刷币漏洞 一个游戏道具可刷400人民币 该漏洞到底是什么?原来游戏中“云幂袖珍罐”这个道具,可以开出2件一样的游戏装备,还有极少几率开出游戏币,开出的装备不值钱,但如果开出金币了,则分为5000万、8000万以及1亿游戏币。而1亿游戏币,按正常市场行情,可在交易网上卖400多元人民币。据玩家称,在游戏中,角色的装备是需要用包裹来存放的,不过目前角色的包裹最多只有48格,也就是只能存放最多48件装备。漏洞就是利用包裹的有限空间,存放47件装备(存放满了又无法开罐子),只留下一格空位,而在开“云幂袖珍罐”出装备时,就会因包裹空间不足,而导致开罐失败,而罐子还存在。玩家继续开罐,直到出现金币,但金币不会占据包裹的空间,因此开罐成功,然后罐子消失。发现这个漏洞后,部分玩家狂刷游戏币,然后马上在第三方交易平台出售游戏币,兑换成现金。 这种问题,都是研发人员逻辑不严谨导致,这种问题,也较难发现。规避方式可以依赖下面提到的『运营数据监控』。 道具复制--背包整理 跟上面的卖出、买入一样,同时穿装备、整理包裹。在设计时,可能会将身上装备设计在装备表中;将不在身上的装备,设计到背包表中。当同时进行穿装备跟整理包裹的请求并发时,也会发生跟上面卖出,买入的情况,线程1读取DB,发现包裹里有这装备,然后准备删除背包表的这条记录,当准备写入到装备表时,另外一个整理包裹请求的线程来了,读取了整个背包表,进行道具的合并、排序。这时,之前的线程将这个装备写入到装备表,并删除了背包表里的数据,并提交事务。这个穿装备的所有操作都是合理、正常,且正确执行的。但另外一个整理背包的线程读取了之前的背包表里的数据,包括那件被穿上的装备。在游戏中,整理背包需要对可堆叠道具做堆叠操作的,意味着需要合并多个道具,删除部分道具。这意味着这里的操作,当前cgi线程的内存中的数据,将都会以覆盖的形式,写入到DB中,那么意味着,之前被穿到身上的那件装备,也会重新被写入到背包中。那就变成两张表里出现了两个相同唯一ID的相同属性的道具。玩家就可以把背包中的这个道具出售给其他玩家。 在java或者C之类程序中,数据放内存中的游戏,也会存在这个问题,除非做读锁,但读锁会带来锁等待,锁等待会导致线程被占用,阻塞后面请求的处理,堆积大量请求。导致系统负载升高,服务器繁忙,以至于无法响应。好了,大约理解道具复制的形成原因了吗?这个问题,我们从根本原因想想,问题到底出现在哪里?如何规避呢?细心的同学不难发现,对于穿装备的操作结果,会对下一个请求产生影响的,当前操作未得到服务端响应之前,服务端是不能处理下一个响应的。对此,我们做了响应处理锁--『用户并发请求锁』。 用户并发请求锁的实现,php中session以文件形式存储时,php会对session文件加锁,不释放(如果不特意执行session_write_close),知道当前响应完成。另外一个线程才可以正常读取,这简介的形成了单个用户的并发请求锁,但是,后面的进程一直处于等待状态,也会占用一个php-fpm进程,阻塞其他用户的正常请求对php线程的使用。为此,我们使用NOSQL的K-V形式结构,以user_name为key的形式,实现用户并发请求锁,比如 redis的setnx接口,原子性判断操作有则返回false,,没有就添加一个,返回true。那么,对于下一个请求,setnx时,返回false,有这个key了,那么立刻抛出异常,结束响应,FLASH根据异常内容,提醒用户不要进行恶意操作。即不会发生并发请求,又不会阻塞请求处理。同时,在请求结束的析构函数里,对这个锁进行删除操作,不影响下一个正常请求。若因为程序异常,发生语法错误,导致析构函数没法执行,没有删除用户锁时,可以在生成锁的时候,设置过期时间,比如5秒,甚至2秒,利用nosql的过期机制,实现用户解锁,避免用户长时间无法正常游戏。 类CC攻击-多用户共享资源锁的timebomb 我们现在研发的项目,是以NOSQL Redis作为DB,来存储数据的,redis并没有成熟的事务处理机制,watch甚至算不上关系型数据库中的事务处理。对此,更需要对表进行加锁解锁。java之类语言的项目,很多都是直接操作内存的,更是需要资源锁,来解决并发问题,解决多个请求操作同一份数据的问题。公司有另外一个项目,出现过一次因为锁的颗粒度较大,带来的锁等待timebomb的问题,也导致了线程繁阻塞忙,请求堆积,系统负载上升,导致宕机的问题。这个项目的锁是针对所有用户的锁,每个用户的请求发来时,当前线程会对所有用户的数据加锁,直到响应完成,才释放掉。这么做,是为了解决因当前操作,会影响到其他用户数据,比如多人PK,多个玩家之间的交互。 当其他请求一并发来时,那么资源会立刻被锁住,直到上一个请求结束,才释放锁,那么其他线程都处于等待状态。用户基数小时,是看不出来锁带来的影响的,内存操作都比较快。当用户基数大时,或者说请求数增大时,后面的请求的等待时间会越来越长,超过webserver的等待时间,直接返回timeout,不能正常提供服务。 这种问题的发生,是因为锁的颗粒太大了,不应该将所有用户都锁住,最好细化到当前请求所影响到的单个用户,只锁住单个用户的数据。这样,才减少timebomb的发生。 其他 知乎里的朋友提到,很多webgame 的前端做了判断,而后端没做判断的问题,这种问题,实属不该存在。在我们的项目中,后端做的验证判断,远比前端多的多。有时候,为了界面上的动画表现,前端flash一般会在用户操作之后,立刻渲染,然后,再根据后端响应,决定是否继续做界面元素改动。比如脱装备,玩家操作时,会先渲染装备从角色面板,跳到背包里的动画,然后,再根据后端响应结果,决定是否回滚动画。这样,避免显得操作后,一定时间的反映迟钝假象,以提高用户体验。当然,后端是一定会做判断的,判断角色背包是否有空格之类。现在的webgame研发,一般都不会存在前端判断,而后端不判断的做法了。如果有,也应该是个别遗漏情况。 比如去年的time33算法的hash dos的问题,使用json消息格式的webgame一定要注意,php只是在接收请求时,做了最大数量的限制。但在json解码之后的数据中,是没有处理的。这里千万别忘记了。 运营数据异常监控 再完善的防御措施,都仍会有安全漏洞。适当的监控措施,也一定要有,监控等级、金币、游戏币、经验、珍贵物品的变化等等,一旦发现,立刻报警,在漏洞未扩散之前,第一时间去修复漏洞,以减少损失,维护游戏平衡。 日志系统 日志系统一定不能漏掉,所有操作,必须写入日志,当安全事件发生后,可以作为各种数据回滚,交易纠纷处理的可靠数据。也是作为数据监控的最准确的数据来源。 一个真实的故事。去年6月份,我们项目新上线一个系统,以及腾讯充值接口V2升级V3,涉及充值代码改动,研发测试、策划测试、QA测试完毕之后,上线到个别服务区,观察情况。每次新版本上线,整个项目组都会持续观察数据情况,尤其是充值总额, 10、50、100的涨,突然,总额下降了。充值总额下降了,这可是总额啊,只能增加,是不会下降的。肯定哪里有BUG,DBA直接看binlog,查充值记录相关的SQL语句,最后发现充值系统的sql语句为UPDATE table set gold_num = $num,is_pay =1 ,没有WHERE,没有WHERE啊,多么弱智的BUG,这尼玛能容忍么?肯定要拉出来,弹JJ弹到死。当我从SVN日志中看到涉及这个文件的修改者时,我立刻石化了,悄悄的修了bug,默默的上传...... 事情是发生了,原因很弱智。是我自己,忘记写where条件,由于框架封装了,问题并不容易被发现。而且自己测试充值都是正确的,包括后来的策划测试、QA测试,都没有发现问题。所属功劳,就是“数据监控系统“,所以,我们游戏开发商,第一时间,比用户还早的发现了问题所在。数据监控,一定必不可少。 至于修复方案,那要感谢日志系统。每笔充值,都有双份日志,一是各个游戏大区自己的DB中。二是充值中心中。充值中心负责跟其他各大平台对接。这次的事故,影响游戏大区的数据,并未影响到充值中心数据,故可以有据可查。这样,伟大的DBA可以更便捷、放心的修复数据了。对此,不管发生其他刷钱、刷装备、盗号之后的交易纠纷,都可以依赖日志来做处理。日志系统,也一定必不可少。 从新功能发布,到发现BUG,到修复BUG,总共历时不到1小时(svn时间可以看出来,16:30对外的),可想而知,数据监控的效果多么值得称赞。新功能上线时,各个测试环节要去做。按大区服务器,做灰度发布也可以更小的减少bug影响范围,减少损失。 如果你用了我画的小清新般的插图,请记得为图片写上署名来源,画图是最花费我时间的一件事。对了,看转载的同学,建议过来看原文,一来原文的排版格式会更适合阅读,二来原文会持续修正文中错误,增加内容。 2013-10-16 +3 7 0
关于网上流传查找PHP webshell的python脚本中,不严谨的代码 不是闲着蛋疼,也不是批评谁,只是不忍心看到不严谨的代码在网上被疯传,误导初学者。以下引用的代码以及思路来自网络,只针对代码,不针对人。如有雷同,纯属巧合。 来自某大牛python版本的检测服务器上PHP代码中webshell的脚本代码片段如下: 然后,接着是python处理上面列表(数组),把每个列表的第一个元素作为正则表达式内容,然后进行匹配处理的。先看第一个检测“PHP一句话木马”的这个正则。【eval\(\$\_POST】这里转义了字符【_】,字符“_”不是元字符,不需要转义的,其实,转义也无妨,聪明的正则表达式引擎会识别修正的。来说下这个思路的遗漏之处。这里仅仅匹配字符串“eval(”后面的变量“$_POST”,如果是“$_GET”呢?如果是“$_REQUEST”呢?如果程序先把$_POST/$_GET/$REQUEST的key的变量先赋值给一个自定义的变量名,然后在用PHP的“eval”函数执行这个变量呢?是不是就查找不到了?其实,并不需要知道“eval”函数执行的变量名是什么,只要查找“eval(”即可,当然,还要注意“eval”函数后面可以有空格,tab键之类空白字符。上面的代码片段中,也没考虑到这一点。 再看看下一个列表的第一个元素。【(system|shell_exec|exec|popen)】,这个正则的意思是只要字符串里包含“system”、“shell_exec”、“exec”、“popen”这四组字符串即判定为危险字符。很明显,这个方法太不严谨。如果程序员写的代码中,包含了这四组字符,即可被判定为危险函数。很不准确,误报率极高。见下图 到底什么样的代码是可疑的代码?关键词是什么? 可疑的代码肯定是由可以执行危险操作的函数构成,可以执行危险操作的PHP函数最重要的就是“eval”函数了,对于加密的PHP代码(仅变形字符串,非zend等方式加密),肯定要用到“eval”函数,所以,对于不管是用哪种加密方法的代码,肯定要用到“eval”函数。其次就是可以执行系统命令的函数了,比如上面某牛的代码中提到的四个“system”、“shell_exec”、“exec”、“popen”。当然还有其他的,比如passthru等。PHP还支持“·”字符(ESC键下面那个)直接执行系统命令。我们可以把正则写成这样【\b(?P<function>eval|proc_open|popen|shell_exec|exec|passthru|system)\b\s*\(】。 解释一下: 大家都知道【\b\b】用来匹配单词两边的位置的。要保证【\b\b】中间的是单词,即使函数名前面加特殊字符,也一样通过匹配,比如加@来屏蔽错误。后面的【\s*】用来匹配空白字符的,包括空格,tab键,次数为0到无数次。前面的【(?P)】是捕获命名组。用来当作python代码直接引用匹配结果的key。 还有的网友提到了,如果我把代码放到图片拓展名的文件里呢?那你只检测.php,.inc的文件,还是找不到我的呀。嗯,是的,如果恶意代码在gif、jpg、png、aaa等乱七八糟的拓展名文件里,是不能被apache、IIS等web Services解析的,必须通过include/require(_once)来引入。那么,我们只要匹配include/require(_once)后面的文件名是不是常规的“.php”、“.inc”文件。如果不是,则为可疑文件。正则如下【(?P<function>\b(?:include|require)(?:_once)?\b)\s*\(?\s*["'](?P<filename>.*?(?<!\.(?:php|inc)))["']】。 解释一下: 先看【(?P<function>\b(?:include|require)(?:_once)?\b)】,【(?P<name>)】为正则表达式的“命名捕获”,PHP中有同样的用法。也就是说,在这括号内的捕获的数据,会分配到结果数组的key为“name”的value中。再看里面的【\b(?:include|require)(?:_once)?\b】,【\b\b】不解释了,为单词边界位置。里面的【(?:include|require)】匹配字符串“include”、“require”两个单词,其中前面的【(?:)】未不分配组,用于提高效率,可以去掉【?:】变成【(include|require)】。在后面一个【(?:_once)】也是做不分配组的操作,便于提高正则表达式效率。同样,后面的量词是“?”代表这个组可有可无。就满足了“include”、“include_once”、“require”、“require_once”四种情况。有的朋友可能这样写【(include|include_once|require|require_once)】也能实现目的。但是,为了更搞的效率,我们对这个正则做优化,针对部分字符串做分支更改,改成上面那个【\b(?:include|require)(?:_once)?\b】。 再看下面的【\s*\(?\s*["'](?P<filename>.+?(?<!\.(?:php|inc)))["']】中,【\s*】匹配空白字符,包括空格,tab键等。后面的【\(?】,匹配字符“(”,后面的量词“?”表示这半个小酷括号可有可无。防止“incude "123.php"”这种没有括号的情况。再后面【["']】匹配双引号,单引号的。最后的也是。再看看这个【(?P<filename>.+?(?<!\.(?:php|inc)))】,其中【(?P<filename>)】上面介绍了,为命名捕获,把结果放到match.group("filename")里。【.*?】为任意字符,后面的量词是“忽略优先量词”,也就是平常说的“非贪婪”。这里最少匹配零个,(防止.aa、.htaccess这种没有文件名,只有文件拓展名的文件被引入)。后面的【(?<!\.(?:php|inc))】,这里用到了反向零宽断言(环视)的非操作(只匹配位置,不匹配字符串,跟【^$\b】等一样)。这个表达式是针对这个位置的后面字符起作用的,也就是说后面的【["']】的前面不能是“.php”、“.inc”,这里也就是取了文件名的最后的拓展名。(正则里,可以用【^】对字符取非,但是不能对“字符串组”取非,这里用了零宽断言来实现。) 综上所述,最后,鄙人给出的python代码如下: 仅供参考,欢迎斧正。 下面截图为扫描Discuz7.2的效果图,当然,也有误报。相对网上流传的python脚本,误报更少,更精确了。 问:这个方法完美了吗?可以查找目前已知的所有危险函数文件了吗? 答:不能,如果include等引入的文件没有拓展名,这里就匹配不到了。 问:如何解决? 答:留给你解决,聪明的你,肯定可以搞定。 PS:“`”反引号 执行命令的还没写,暂时没好的办法。容易跟SQL语句中的反引号混淆。不太好匹配。如果光匹配反引号就提示的话,那误报太大了。待定吧。(术业有专攻,请勿因为一处不好的代码,否定一个人的能力。你懂的。再次重申,此文只针对代码,不针对人。其次,鄙人给出的python代码随便复制,随便传播,爱留版权就留版权,不爱留就删了相关字符,也就是您爱干吗干吗。) 我先休息一会,明天再说。(前半句为三国杀曹仁的台词,哈。) =============================我是万恶的分割线====================================== 最新代码在这里给出了。 2010-07-26 3 0
eval、gzinflate、 base64_decode三函数加密的代码在线解密 其实,这边日志的主题不是想写被eval、gzinflate、 base64_decode三函数加密的代码的在线解密的。主要写生活中的两三件事。 其一,刘威同学生了个儿子,在这里恭喜一下。俺又多了个侄子,年底回家,连他结婚的喜酒,跟小侄子的见面礼一并给了。(此人已经生子,还未结婚,大家一起来鄙视一下,当然,你也可以羡慕一下。)团队的SCY已经有两个孩子了,一个女儿一个儿子,岁月不饶人啊,哥老了。 其二,前天下午听姐姐说晚上跟今天早上,还有中午打电话回家,都没人接听。我听了之后,有点心慌,也假装镇定到下班回家,先是打了电话到大姐家,接电话的是外甥,其也说也曾经尝试了几次,也是没接通,然后,决定明天去我父母家看望一下(中考完毕),顺便带点农副产品。我听了之后,却愈加担心,也有点欣慰。担心的是家里的电话为什么没人接,欣慰的是外甥很懂事了,也很孝顺他的姥姥,也就是我的父母。接着,我又尝试几遍,仍未有响应。又打了一通电话,后得知家里电话坏了,才放下心来。继续坚持每周一个电话回家。 其三,天气渐热,把去年买的风扇拿出来。安装了一下,发现扇叶以及其他部件都是崭新、一尘不染的,遂不住赞叹自己当初认真清理打扫,收藏放置的态度。 其四,放BLOG的服务器,被入侵了。入口是ECSHOP的BUG引起的。俺也没啥说的,只想对这位“大侠”说:“你爱干吗就干吗,只要别删数据,要是挂黑页,证明你的能力的话,那记得帮俺备份一下,拜谢了”。接着说,顺便发现了其使用的PHP木马,打开看了下源码,加密了。其加密方式为 eval(gzinflate(base64_decode(这种的,见下面代码: 大体代码就这样,没贴完,费了1个多小时,写了解密代码程序,见http://www.cnxct.com/cfc4n/eval-gzinflate-base64_decode.php,方便大家用得到的时候用下。 顺便说下,此PHP木马程序加密了9次左右吧,解密之后的代码为 哦.....这哥们的密码居然就两位字母,就是wx ,这一不小心还能按对。。PS:这木马不错,留着自己用了。打包在下面,想用的可以拿去。 无图无真相,上图。 艹,Wordpress不能上传了,提示"临时文件夹丢失。"好吧,好吧,我先写周报,等会再调试。 搞定了,gleon调试,把php.ini中的upload_tmp_dir配置项去掉了,开启之后,给相应目录足够权限即可,比如755. 附件:php木马加密前与加密后的两个文件打包 下面上图 2010-06-25 +7 11 0
firefox上gladder奇怪的网络请求… 终于完成手头的项目了,可以松了口气.对于一个爱好网络脚本安全的人(请允许我这么说),还是很重视自己写程序的安全性,从sql injection, xss,csrf,都做了过滤,甚至参照discuz,做了表单的formhash,防止本地提交等非法提交.当然,对于安全知识薄弱的我,是不能做的很安全,很完美的,还是需要充电... 下午,特意拜访了牛人的blog剑心,SuperHei 等前辈的blog,找了一些文章阅读,充电...其中,看到剑心前辈的PPT,关于xss, csrf攻击的,谈到了cookie域的问题,也谈到了xss攻击导致雅虎股价下跌的事件. 为什么会出现这样的事情呢? 1,Yahoo登录时的Cookie参数 2,Yahoo服务众多,重要与次要没有分离 3,任何一个XSS都导致所有服务被入侵 4,媒体的炒作 使得小弟倍感兴趣,手也痒痒了,想检测一下自己的程序.打开FF,抓包测试..这时,奇怪的事情发生了,我看到了一些奇怪的数据,如下图.. Firefox带着相同的参数不停的请求不同的域名,请求结束之后,立刻又请求 www.baidu.com....但是我的FF是没有打开任何页面的... 奇怪,问题会出现在哪里?这些域名从哪里来?? 开IceSword查看, 只有一个Firefox进程...而我打开的firefox是没有浏览任何网页的...诡异...我脑海里第一个反映就是FF插件搞的鬼?,赶紧停止几个我自认为比较可以的插件,果然,数据正常了... 继续,追踪... 看了下参数,立刻看出来是 base64加密的URL参数,解密,确实"://www.facebook.com"...疑问,满脸的疑问,为什么参数是这个样的? 跟facebook有什么关系???难道目的是打开那页面,然后那页面会根据后面的参数,打开参数的页面,也就是打开 facebook ???? 疑问中...继续追踪... 忽然发现数据包里有条"https://gneheix.googlegroups.com/web/glype?gda=JLdFjzcAAAA3HjXewK9ot5pvme6zn5DGro6E7oIXET7nVD_-2iAS6KAv_Rdh4sBR1ZlWaLOkDX15Xh26_qkMwaGGFl2NCU0D"的数据,赶忙打开看了下,里面全是域名列表,而且,这些列表,正是FF后台请求的域名列表....继续追踪..... 追踪了好久,也没啥结果,一直在请求那些域名,也不知道干吗的...索性直接到firefox拓展网站看看情况,是否有其他网友返回...终于在google groups 里,找到了一个帖子,说的也是这个问题....作者回复"是为了测试当前代理的可用性吧,没仔细看代码,大概应该是这样吧...不停地访问应该不至于吧?有多频繁?太频繁了应该是 bug 吧. " 我汗, 作者居然说"大概应该是这样吧" 囧..... 遂加了作者的Gtalk,请教了一下,但是作者说,自己并非主要的负责作者,具体的也不太了解,但是会转告...俺又互相客气了一会..... 扯远了扯远了,回来,继续学习前辈的脚本安全.... 2009-10-28 0 0