您的位置:首页 > 理论基础 > 计算机网络

协议型外挂的制作一

2004-10-25 20:45 288 查看
协议型外挂的制作一
       By rix
       目前,网络游戏的外挂从程序角度主要分为辅助型的动作外挂和内核型的协议型外挂。动作外挂主要帮助玩家进行一些重复性的劳动量,网络上有许多介绍这方面的程序,按键精灵就是一个很好的例子。协议型外挂则给人一个很神秘的感觉,这方面在网络游戏上最多的恐怕是传奇的脱机外挂,这也是因为传奇最受欢迎而已。
       在具体的讲解前,我很想扯点别的事情,毕竟学院不是我的风格,我更喜欢自由一点的。既然我们主要是针对协议型外挂的制作教程,就先扯扯网络游戏的流程和一些乱其八糟的事情,如果有冒犯你们的地方本人则不胜感激。如果你感到烦的话可以直接跳到你感兴趣的地方。
       我玩网游的历史很短,刚开始玩得是魔力宝贝,总是在免费的时候玩,在一个服务器里面呆的最长的就是在四川卧龙了,现在服务器大概早就该名或者并组了吧,在里面我的名字叫Bluerose,名字前面带的是我的职业,小号一大堆,不过名字都是一样的。用这个名字是因为朋友说我很忧郁,但却总抱着希望,所以就用这个名字了。后来玩的是大话西游II,是朋友拉着玩的,现在虽然上了班,但偶尔还玩,里面的名字叫星泪,用的是女性玩家,因为这个名字太女性化了。在刚开始玩得时候,我觉得自己好孤独(那时候在大学虽然旁边都是同学朋友,但内心仍然孤独),朋友拉我玩大话的时候,那天晚上星光倒不错,洒下的星光让我想起了眼泪的感觉,所以就叫独孤星泪了,不过感觉这个名字太裸露,就将独孤二字去掉了。
       由于玩大话西游II的历史比较长,而且对大话西游的游戏也比较熟悉,所以这次的教程就用大话西游II做为目标了,但教程尽可能考虑通用性。不过提前声明,本人对网络游戏并不熟悉,因此请勿和本人谈论网络游戏的前因后果和发展以及网游外挂对网络游戏的冲击等乱其八糟的事情。我对网络编程也是一知半解,因此我尽可能的避免含有网络的代码,同时,由于协议型的外挂需要发送数据,但可以通过其它的办法来进行(比如通过网游客户端来代理)。
       先说说服务器和客户端的通讯,由于服务器和本地的客户端不在同一个地理位置,相距比较遥远,因此数据的传输就需要一定的时间,这就决定了在网游中数据只能进行采样的收集处理而不能进行真正实时的数据处理。举例来说,局域网C-S(反恐精英)的数据处理,当你移动的时候,必须告知别的电脑玩家自己的移动,这个数据的传输由于在局域网内部,数据的传输比较快,量也比较小,电脑可以进行快速的采样和数据处理来进行判断是否打中或者移动是否违反规则(比如凌空徐步)等等,但在网络上进行玩的时候,对数据的采样就不像本地局域网那么快了,以大话西游II(以后简称大话吧,少打几个字)为例,在移动的时候,并不是将每一步的数据一个一个传送给服务器,而是将本次移动规则打成数据包提交给服务器,让后客户端开始播放动画,当服务器处理完数据之后,就会将位置回传给客户端,客户端以这个位置数据为基点,进行人物的下次移动,这个数据的采集需要隔一段时间来能进行一次采集,相对于CS来说,这个采集密度要比CS采集密度小。
       当服务器和客户端进行通讯的时候,数据包是至关重要的。数据包中数据的规则则是协议型外挂最重要的基础之一。由于网络数据可以进行拦截,为了防止数据被修改,数据包中的数据都是加密进行,至于如何加密,这由服务器和客户端通过一定的算法来执行。因此,服务器和客户端的通讯大概就是下面这个样子:
客户端进行数据的采集===〉数据打包==〉数据加密==〉发送数据到服务器==〉服务器进行数据解密====〉服务器处理数据包==(处理完毕回传数据)==〉回传数据打包==〉回传数据加密==〉数据回传==〉客户端接收数据==〉客户端解密数据==〉客户端数据处理
       大概的流程就是上面的,本来想用图的,由于我前两天玩内存不小心弄坏了一根内存条(大概内存条拔插的时候颗粒随坏了吧),现在电脑128MB的内存,运行一个Word 2003都不是非常的快了,因此就放弃了在Photoshop中画图了,抱歉抱歉。
       我们的目标就是数据包,即拦截游戏通讯间的数据来进行相应的修改或者进行发送伪数据包。我大概定了一个计划,不过这一节肯定不能全部讲完,我只能在下班之后写上一点,时间有限,能写多少就写多少吧。
       目标程序:大话西游II客户端。(你手上有服务器端吗?有的话我也想要^_^)
       目标:数据包
       目的:数据包拦截,修改,伪发送
       编程软件:这个无所谓吧,不过我这里用的是C++ Builder 6,前段时间做CB 6相关的项目,而且CB 对于程序界面的编写是最方便不过的了,就是编译的有点慢和生成的程序有点大。(旁白:又不是做手机项目,担心容量吗?)我做受限程序做惯了,养成了不良习惯,没办法了。
       思路:我们的程序要干扰别的程序的运行,最好的办法是使用debug的办法,不过,我并没有打算使用debug的办法,我对程序的debug并不太熟悉,而且讨厌编写没用的代码。我准备采用线程注入的办法,至于线程注入,和为什么要线程注入才能干预,这方面的知识最好自己看看《Windows 核心编程》里面讲的,否则这个教程要没完没了了。当我们的线程注入到目标程序之后就方便多了,就可以为所欲为了。因此我们的第一目的是将线程注入到目标程序中。
       预备活动:
       线程注入最简单的莫过于hook了,如果连这都不知道的话,最好赶快到网上查查或者翻翻《Windows核心编程》。为了防止游戏内部存在反hook的存在和外挂的检测,我将用自己的程序来启动目标程序。由于网游的不定期更新,因此在启动程序的时候最好将升级跳过去,至少在大话这样的程序中我是这样做的,因为频繁升级和版本检测总让我等的时间太长。
       下面来进行具体的做法,我尽可能的弄出详细的步骤,如果你用的是VC或者其它的话,只要注意核心的代码就可以了。
       新建一个工程,在窗体上添加两个按钮(TButton或者其它类型的按钮),一个将标题改为启动游戏,另一个标题改为启动外挂。再添加一个TOpenDialog。对于默认的窗体那么大的界面有点浪费,因此将窗体弄得小点,别大大的怪吓人的。
       双点启动游戏的按钮就可以进行编写该按钮的事件了,默认的是OnClick事件。下面就是事件的代码:
if(FileExists(ExtractFileDir(Application->ExeName)+"//path.ini")==FALSE)
{     /*我将目标程序的路径保存到了当前程序目录中的path.ini文件中,但如果当前程序第一次运行的话,是不存在这个文件的,所以就可以用TOpdnDialog来打开了,做这点只是为了方便,不用每次都得点目标程序*/
       if(OpenFile->Execute())
       {
       AnsiString AppPath="path="+ExtractFilePath(OpenFile->FileName);
       WritePrivateProfileSection("XY2PATH",AppPath.c_str(),(ExtractFileDir(Application->ExeName)+"//path.ini").c_str());//蒋目标程序的路径存到path.ini文件中。
       }else
       {
       return;
       }
}
//下面的代码开始启动目标程序
PROCESS_INFORMATION pi;
STARTUPINFO si;
si.cb=sizeof(si);
si.lpReserved=NULL;
si.lpDesktop=NULL;
si.lpTitle=NULL;
si.cbReserved2=0;
si.lpReserved2=NULL;
si.dwFlags=STARTF_USEPOSITION;
si.dwX=0;
si.dwY=0;
char Appname[300];
GetPrivateProfileString("XY2PATH","path","",Appname,250,(ExtractFileDir(Application->ExeName)+"//path.ini").c_str());
strcat(Appname,"//xy2.exe");
/*以上都在构建目标程序的环境设置,下面调用CreateProcess来启动目标程序,注意将倒数第3个参数要填为目标程序的路径,第6个参数为CREATE_SUSPENDED是为了将程序加载到内存中之后可以进行一些修改,以更好的配合外挂程序的运行*/
if(CreateProcess(Appname,NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,ExtractFileDir(Appname).c_str(),&si,&pi)==0)
{
//启动目标程序失败
       ShowMessage("error open exe file");
       return;
}
gamehandle=pi.hProcess;
/*在本节中要执行程序的话,最好将这个条件注释掉,我将在以后的教程中进行讲解,这里大概说一下功能,第一个Write是为了跳过Update,第二个是为了退出的时候不打开网页,我的电脑要是退出大话的时候打开网页的话,中间的时间可以抽上几根烟了,所以将程序改了*/
if(WriteProcessMemory(gamehandle,(void*)0x0042BC13,No_Update,1,NULL)==false
||WriteProcessMemory(gamehandle,(void*)0x<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />00430a80,No_HTML,2,NULL)==false
)
       return;
threadhand=pi.hThread;
gamethreadid=pi.dwThreadId;
//恢复程序,让程序执行
ResumeThread(pi.hThread);
/*下面的代码也是本节中不需要的,我将物品的有关信息存到了当前目录(外挂启动程序目录)中的item.ini文件中,但目标程序中并不知道外挂启动程序的路径,因此我在目标程序文件夹中建立了一个名字叫path.ini文件,里面包含了item.ini的路径*/
String inipath= "path="+ExtractFileDir(Application->ExeName)+"//item.ini";
WritePrivateProfileSection("ITEM",inipath.c_str(),(ExtractFileDir(OpenFile->FileName)+"//path.ini").c_str());
       启动程序中将启动属性设置为CREATE_SUSPENDED属性是为了考虑到程序的通用性和稳定性,在该函数之后,如果目标程序中存在有必要修改的代码的话,可以在这里进行修改,也可以对目标程序进行反反外挂的处理。其实,debug形式的外挂就可以在这里进行debug环境的建立,以及在目标程序中插入Int 3指令来进行拦截处理了(我怎么越来越感觉到自己在写调试器的教程??)。
       这节就讲到这里,如果再晚的话,我就没办法赶上公交车了,然后还得走回家,天哪,这么冷的天~~~~赶紧上传回家吧。CSDN上的Blog不知道怎么贴附件,我还得考虑附件放到什么地方,因为有些文件没办法贴出来了。不过gameres上可以贴上300K的附件,希望能够用的。
       本文章只贴于www.csdn.net和www.gameres.com 的Blog上面,请勿将文章用于任何商业场合,如果因为本教程引起其他的后果的话,则与本人无关,本人只讲技术实现。如果要转贴的话,请注明出处,如果有疑问或者商议的话,请发E-Mail到zeze0556@sina.com或者QQ:23033206留言,MSN:zeze0556@msn.com。另外请勿给我信箱发垃圾邮件,在添加好友的时候一定要写好附言,我已经被莫名其妙的广告信件和流言蜚语吓得没有胆了。算是我求各位大虾了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息