网络游戏外挂设计深度探索
网络游戏的繁荣使它的“寄生虫”——外挂也迅速繁荣起来,进而形成了一种畸形产业,不少开发高手在这里找到了所谓的“第一桶金”。由于外挂,游戏运营商在不停的“流失营养”,当这种状况达到不堪重负的时候,网络游戏业将轰然倒下,运营商对外挂恨之入骨。
⊙
|
网络游戏外挂编写基础 |
用传统的修改单机版游戏的方法来修改网络游戏是肯定不行的,在网络游戏中通讯是通过IP数据包的传输来实现的,所以我们要做的工作就是分析客户端和服务器之间往来的数据包。 二:什么是封包? 三:怎么来分析我们截获的封包? 四:怎么冒充"客户端"向"服务器"发我们需要的封包? |
⊙
|
游戏外挂基本原理及实现 |
修改游戏无非是修改一下本地内存的数据,或者截获API函数等等。本文将对一些常用的网络游戏修改方法作一个介绍,希望大家能做出很好的外挂来使游戏厂商更好的完善自己的技术。 游戏外挂已经深深地影响着众多网络游戏玩家,今天在网上看到了一些关于游戏外挂编写的技术,于是转载上供大家参考 1、游戏外挂的原理 外挂现在分为好多种,比如模拟键盘的,鼠标的,修改数据包的,还有修改本地内存的,但好像没有修改服务器内存的哦,呵呵。其实修改服务器也是有办法的,只是技术太高一般人没有办法入手而已。(比如请GM去夜总会、送礼、收黑钱等等办法都可以修改服务器数据,哈哈) 修改游戏无非是修改一下本地内存的数据,或者截获API函数等等。这里我把所能想到的方法都作一个介绍,希望大家能做出很好的外挂来使游戏厂商更好的完善自己的技术。我见到一篇文章是讲魔力宝贝的理论分析,写得不错,大概是那个样子。下来我就讲解一下技术方面的东西,以作引玉之用。 2 技术分析部分 2.1 模拟键盘或鼠标的响应 我们一般使用:
API函数。第一个参数是说明第二个参数的矩阵的维数的,第二个参数包含了响应事件,这个自己填充就可以,最后是这个结构的大小,非常简单,这是最简单的方法模拟键盘鼠标了,呵呵。注意,这个函数还有个替代函数:
与
这两个函数非常简单了,我想那些按键精灵就是用的这个吧。上面的是模拟键盘,下面的是模拟鼠标的。这个仅仅是模拟部分,要和游戏联系起来我们还需要找到游戏的窗口才行,或者包含快捷键,就象按键精灵的那个激活键一样,我们可以用GetWindow函数来枚举窗口,也可以用Findwindow函数来查找制定的窗口(注意,还有一个FindWindowEx),FindwindowEx可以找到窗口的子窗口,比如按钮,等什么东西。当游戏切换场景的时候我们可以用FindWindowEx来确定一些当前窗口的特征,从而判断是否还在这个场景,方法很多了,比如可以GetWindowInfo来确定一些东西,比如当查找不到某个按钮的时候就说明游戏场景已经切换了,等等办法。有的游戏没有控件在里面,这是对图像做坐标变换的话,这种方法就要受到限制了。这就需要我们用别的办法来辅助分析了。 至于快捷键我们要用动态连接库实现了,里面要用到hook技术了,这个也非常简单。大家可能都会了,其实就是一个全局的hook对象然后SetWindowHook就可以了,回调函数都是现成的,而且现在网上的例子多如牛毛。这个实现在外挂中已经很普遍了。如果还有谁不明白,那就去看看MSDN查找SetWindowHook就可以了。 不要低估了这个动态连接库的作用,它可以切入所有的进程空间,也就是可以加载到所有的游戏里面哦,只要用对,你会发现很有用途的。这个需要你复习一下Win32编程的基础知识了。呵呵,赶快去看书吧。 2.2 截获消息 有些游戏的响应机制比较简单,是基于消息的,或者用什么定时器的东西。这个时候你就可以用拦截消息来实现一些有趣的功能了。 我们拦截消息使用的也是hook技术,里面包括了键盘消息,鼠标消息,系统消息,日志等,别的对我们没有什么大的用处,我们只用拦截消息的回调函数就可以了,这个不会让我写例子吧。其实这个和上面的一样,都是用SetWindowHook来写的,看看就明白了很简单的。 至于拦截了以后做什么就是你的事情了,比如在每个定时器消息里面处理一些我们的数据判断,或者在定时器里面在模拟一次定时器,那么有些数据就会处理两次,呵呵。后果嘛,不一定是好事情哦,呵呵,不过如果数据计算放在客户端的游戏就可以真的改变数据了,呵呵,试试看吧。用途还有很多,自己想也可以想出来的,呵呵。 2.3 拦截Socket包 这个技术难度要比原来的高很多。 首先我们要替换WinSock.DLL或者WinSock32.DLL,我们写的替换函数要和原来的函数一致才行,就是说它的函数输出什么样的,我们也要输出什么样子的函数,而且参数,参数顺序都要一样才行,然后在我们的函数里面调用真正的WinSock32.DLL里面的函数就可以了。 首先:我们可以替换动态库到系统路径。 其次:我们应用程序启动的时候可以加载原有的动态库,用这个函数LoadLibary然后定位函数入口用GetProcAddress函数获得每个真正Socket函数的入口地址。 当游戏进行的时候它会调用我们的动态库,然后从我们的动态库中处理完毕后才跳转到真正动态库的函数地址,这样我们就可以在里面处理自己的数据了,应该是一切数据。呵呵,兴奋吧,拦截了数据包我们还要分析之后才能进行正确的应答,不要以为这样工作就完成了,还早呢。等分析完毕以后我们还要仿真应答机制来和服务器通信,一个不小心就会被封号。 分析数据才是工作量的来源呢,游戏每次升级有可能加密方式会有所改变,因此我们写外挂的人都是亡命之徒啊,被人愚弄了还不知道。 2.4 截获API 上面的技术如果可以灵活运用的话我们就不用截获API函数了,其实这种技术是一种补充技术。比如我们需要截获Socket以外的函数作为我们的用途,我们就要用这个技术了,其实我们也可以用它直接拦截在Socket中的函数,这样更直接。 现在拦截API的教程到处都是,我就不列举了,我用的比较习惯的方法是根据输入节进行拦截的,这个方法可以用到任何一种操作系统上,比如Windows 98/2000等,有些方法不是跨平台的,我不建议使用。这个技术大家可以参考《Windows核心编程》里面的545页开始的内容来学习,如果是Win98系统可以用“Windows系统奥秘”那个最后一章来学习。 |
⊙
|
网络游戏通讯模型初探 |
网络游戏的致命漏洞就在通讯,这也几乎是唯一的漏洞,在通讯环节提交修改后的数据就能成功欺骗运营商,因此网络游戏的通讯方式是外挂设计者必须要掌握的。 序言 网络游戏,作为游戏与网络有机结合的产物,把玩家带入了新的娱乐领域。网络游戏在中国开始发展至今也仅有3,4年的历史,跟已经拥有几十年开发历史的单机游戏相比,网络游戏还是非常年轻的。当然,它的形成也是根据历史变化而产生的可以说没有互联网的兴起,也就没有网络游戏的诞生。作为新兴产物,网络游戏的开发对广大开发者来说更加神秘,对于一个未知领域,开发者可能更需要了解的是网络游戏与普通单机游戏有何区别,网络游戏如何将玩家们连接起来,以及如何为玩家提供一个互动的娱乐环境。本文就将围绕这三个主题来给大家讲述一下网络游戏的网络互连实现方法。 网络游戏与单机游戏 现在先让我们来看一下普通单机游戏的简化执行流程:
我们来说明一下上面单机游戏的流程。首先,不管是游戏软件还是其他应用软件,初始化部分必不可少,这里需要对游戏的数据进行初始化,包括图像、声音以及一些必备的数据。接下来,我们的游戏对场景、人物以及其他元素进行循环绘制,把游戏世界展现给玩家,同时接收玩家的输入操作,并根据操作来做出响应,此外,游戏还需要对NPC以及一些逻辑AI进行处理。最后,游戏数据被释放,游戏结束。 网络游戏与单机游戏有一个很显著的差别,就是网络游戏除了一个供操作游戏的用户界面平台(如单机游戏)外,还需要一个用于连接所有用户,并为所有用户提供数据服务的服务器,从某些角度来看,游戏服务器就像一个大型的数据库,提供数据以及数据逻辑交互的功能。让我们来看看一个简单的网络游戏模型执行流程: 客户机:
服务器:
让我们来说明一下上面简单网络游戏模型的运行机制。先来讲讲服务器端,这里服务器端分为三个部分(实际上一个完整的网络游戏远不止这些):登入模块、游戏模块和登出模块。登入模块用于监听网络游戏客户端发送过来的网络连接消息,并且验证其合法性,然后在服务器中创建这个玩家并且把玩家带领到游戏模块中; 游戏模块则提供给玩家用户实际的应用服务,我们在后面会详细介绍这个部分; 在得到玩家要离开游戏的消息后,登出模块则会把玩家从服务器中删除,并且把玩家的属性数据保存到服务器数据库中,如: 经验值、等级、生命值等。 以上用简单的伪代码给大家阐述了单机游戏与网络游戏的执行流程,大家应该可以清楚看出两者的差别,以及两者间相互的关系。我们可以换个角度考虑,网络游戏就是把单机游戏的逻辑运算部分搬移到游戏服务器中进行处理,然后把处理结果(包括其他玩家数据)通过游戏服务器返回给连接的玩家。 网络互连
上面通过调用Windows的socket API函数来初始化网络设备,接下来进行网络Socket的创建,代码片段如下:
这里需要说明,客户端和服务端所需要的Socket连接数量是不同的,客户端只需要一个Socket连接足以满足游戏的需要,而服务端必须为每个玩家用户创建一个用于通讯的Socket连接。当然,并不是说如果服务器上没有玩家那就不需要创建Socket连接,服务器端在启动之时会生成一个特殊的Socket用来对玩家创建与服务器连接的请求进行响应,等介绍网络监听部分后会有更详细说明。
这里两个步骤分别对前面所作的创建初始化进行了相应释放。
这里使用的是阻塞式通讯处理,此时程序将处于等待玩家用户连接的状态,倘若这时候有客户端连接进来,则通过accept()来创建针对此玩家用户的Socket连接,代码片段如下:
这里我们创建了sPlayerSocket连接,此后游戏服务器与这个玩家用户的通讯全部通过此Socket进行,到这里为止,我们服务器已经有了接受玩家用户连接的功能,现在让我们来看看游戏客户端是如何连接到游戏服务器上,代码片段如下:
这里的pBuffer为要发送的数据缓冲指针,lLength为需要发送的数据长度,通过这支Socket API函数,我们无论在客户端或者服务端都可以进行数据的发送工作,同时,我们可以通过recv()这支Socket API函数来进行数据接收:
其中pBuffer用来存储获取的网络数据缓冲,lLength则为需要获取的数据长度。 大家知道,游戏需要不断循环处理游戏中的逻辑并进行游戏世界的绘制,上面所介绍的Winsock处理方式均是以阻塞方式进行,这样就违背了游戏的执行本质,可以想象,在客户端连接到服务器的过程中,你的游戏不能得到控制,这时如果玩家想取消连接或者做其他处理,甚至显示一个最基本的动态连接提示都不行。
这里我们创建了一个线程,同时将我们的Socket传入线程函数:
NetThread就是我们将来用于处理网络通讯的网络线程。那么,我们又如何把Socket的处理引入线程中?
通过上面的设置之后,WinSock API函数均会以非阻塞方式运行,也就是函数执行后会立即返回,这时网络通讯会以事件方式存储于hEvent,而不会停顿整支程式。
这里仅对网络连接(FD_CONNECT) 和读取数据(FD_READ) 进行了简单模拟操作,但实际中网络线程接收到事件消息后,会对数据进行组织整理,然后再将数据回传给我们的游戏主线程使用,游戏主线程再将处理过的数据发送出去,这样一个往返就构成了我们网络游戏中的数据通讯,是让网络游戏动起来的最基本要素。
为了能够更简单的给大家阐述网络数据包的组织形式,我们以一个聊天处理模块来进行讨论,看看下面的代码结构:
上面是抽象出来的一个极为简单的消息包结构,我们先来谈谈其各个数据域的用途:
首先,我们假设已经组织好一个数据包,这里MSG_CHAT 是我们自行定义的标识符,当然,这个标识符在服务器和客户端要统一。玩家的ID 则根据游戏需要来进行设置,这里1000 只作为假设,现在继续:
我们通过强行转换把结构体转变为char 类型的数据指针,这样就可以通过这个指针来进行流式数据处理,这里通过sizeof() 获得结构体长度,然后用WinSock 的Send() 函数将数据发送出去。
在通过WinSock 的recv() 函数获取网络数据之后,我们同样通过强行转换把获取出来的缓冲数据转换为相应结构体,这样就可以方便地对数据进行访问。(注:强行转换仅仅作为数据转换的一种手段,实际应用中有更多可选方式,这里只为简洁地说明逻辑)谈到此处,不得不提到服务器/ 客户端如何去筛选处理各种消息以及如何对通讯数据包进行管理。无论是服务器还是客户端,在收到网络消息的时候,通过上面的数据解析之后,还必须对消息类型进行一次筛选和派分,简单来说就是类似Windows 的消息循环,不同消息进行不同处理。这可以通过一个switch 语句(熟悉Windows 消息循环的朋友相信已经明白此意),基于消
上面片段中的MSG_MOVE 和MSG_EXIT 都是我们虚拟的消息标识(一个真实游戏中的标识可能会有上百个,这就需要考虑优化和优先消息处理问题)。此外,一个网络游戏服务器面对的是成百上千的连接用户,我们还需要一些合理的数据组织管理方式来进行相关处理。普通的单体游戏服务器,可能会因为当机或者用户过多而导致整个游戏网络瘫痪,而这也就引入分组服务器机制,我们把服务器分开进行数据的分布式处理。
|
⊙
|
游戏外挂设计技术探讨 |
所谓游戏外挂,其实是一种游戏外辅程序,在这些小小程序中使用了许多高端技术,如拦截Sock技术、拦截API技术、模拟键盘与鼠标技术、直接修改程序内存技术等等。
其中,dwFlags表示了各种各样的鼠标动作和点击活动,它的常用取值如下: MOUSEEVENTF_MOVE 表示模拟鼠标移动事件。 MOUSEEVENTF_LEFTDOWN 表示模拟按下鼠标左键。 MOUSEEVENTF_LEFTUP 表示模拟放开鼠标左键。 MOUSEEVENTF_RIGHTDOWN 表示模拟按下鼠标右键。 MOUSEEVENTF_RIGHTUP 表示模拟放开鼠标右键。 MOUSEEVENTF_MIDDLEDOWN 表示模拟按下鼠标中键。 MOUSEEVENTF_MIDDLEUP 表示模拟放开鼠标中键。 (2)、设置和获取当前鼠标位置的API函数。获取当前鼠标位置使用GetCursorPos()函数,设置当前鼠标位置使用SetCursorPos()函数。
通常游戏角色的行走都是通过鼠标移动至目的地,然后按一下鼠标的按钮就搞定了。下面我们使用上面介绍的API函数来模拟角色行走过程。
2. 键盘模拟技术 在很多游戏中,不仅提供了鼠标的操作,而且还提供了键盘的操作,在对攻击对象进行攻击时还可以使用快捷键。为了使这些攻击过程能够自动进行,外挂程序需要使用键盘模拟技术。像鼠标模拟技术一样,Windows API也提供了一系列API函数来完成对键盘动作的模拟。 模拟键盘动作API函数keydb_event,它可以模拟对键盘上的某个或某些键进行按下或放开的动作。
其中,bVk表示虚拟键值,其实它是一个BYTE类型值的宏,其取值范围为1-254。有关虚拟键值表请在MSDN上使用关键字“Virtual-Key Codes”查找相关资料。bScan表示当键盘上某键被按下和放开时,键盘系统硬件产生的扫描码,我们可以MapVirtualKey()函数在虚拟键值与扫描码之间进行转换。dwFlags表示各种各样的键盘动作,它有两种取值:KEYEVENTF_EXTENDEDKEY和KEYEVENTF_KEYUP。 下面我们使用一段代码实现在游戏中按下Shift+R快捷键对攻击对象进行攻击。
4. 实例实现
(3).在ActiveKey.cpp文件中加入声明"#include ActiveKey.h"。 (4).在ActiveKey.cpp文件中加入共享数据段,代码如下:
(5).在ActiveKey.def文件中设置共享数据段属性,代码如下:
(6).在ActiveKey.cpp文件中加入CkeyHook类的实现代码和钩子函数代码:
(7).修改DllMain函数,代码如下:
(8).编译项目ActiveKey,生成ActiveKey.DLL和ActiveKey.lib。 接着,我们还需要创建一个外壳程序将全局钩子安装了Windows系统中,这个外壳程序编写步骤如下: (1).创建一个对话框模式的应用程序,项目名为Simulate。 (2).在主对话框中加入一个按钮,使用ClassWizard为其创建CLICK事件。 (3).将ActiveKey项目Debug目录下的ActiveKey.DLL和ActiveKey.lib拷贝到Simulate项目目录下。 (4).从“工程”菜单中选择“设置”,弹出Project Setting对话框,选择Link标签,在“对象/库模块”中输入ActiveKey.lib。 (5).将ActiveKey项目中的ActiveKey.h头文件加入到Simulate项目中,并在Stdafx.h中加入#include ActiveKey.h。 (6).在按钮单击事件函数输入如下代码:
(7).编译项目,并运行程序,单击按钮激活外挂。 (8).启动画笔程序,选择文本工具并将笔的颜色设置为红色,将鼠标放在任意位置后,按F10键,画笔程序自动移动鼠标并写下一个红色的大写R。图一展示了按F10键前的画笔程序的状态,图二展示了按F10键后的画笔程序的状态。 图一:按F10前状态(001.jpg) 图二:按F10后状态(002.jpg) 五、封包技术 通过对动作模拟技术的介绍,我们对游戏外挂有了一定程度上的认识,也学会了使用动作模拟技术来实现简单的动作模拟型游戏外挂的制作。这种动作模拟型游戏外挂有一定的局限性,它仅仅只能解决使用计算机代替人力完成那么有规律、繁琐而无聊的游戏动作。但是,随着网络游戏的盛行和复杂度的增加,很多游戏要求将客户端动作信息及时反馈回服务器,通过服务器对这些动作信息进行有效认证后,再向客户端发送下一步游戏动作信息,这样动作模拟技术将失去原有的效应。为了更好地“外挂”这些游戏,游戏外挂程序也进行了升级换代,它们将以前针对游戏用户界面层的模拟推进到数据通讯层,通过封包技术在客户端挡截游戏服务器发送来的游戏控制数据包,分析数据包并修改数据包;同时还需按照游戏数据包结构创建数据包,再模拟客户端发送给游戏服务器,这个过程其实就是一个封包的过程。 封包的技术是实现第二类游戏外挂的最核心的技术。封包技术涉及的知识很广泛,实现方法也很多,如挡截WinSock、挡截API函数、挡截消息、VxD驱动程序等。在此我们也不可能在此文中将所有的封包技术都进行详细介绍,故选择两种在游戏外挂程序中最常用的两种方法:挡截WinSock和挡截API函数。 1. 挡截WinSock 众所周知,Winsock是Windows网络编程接口,它工作于Windows应用层,它提供与底层传输协议无关的高层数据传输编程接口。在Windows系统中,使用WinSock接口为应用程序提供基于TCP/IP协议的网络访问服务,这些服务是由Wsock32.DLL动态链接库提供的函数库来完成的。 由上说明可知,任何Windows基于TCP/IP的应用程序都必须通过WinSock接口访问网络,当然网络游戏程序也不例外。由此我们可以想象一下,如果我们可以控制WinSock接口的话,那么控制游戏客户端程序与服务器之间的数据包也将易如反掌。按着这个思路,下面的工作就是如何完成控制WinSock接口了。由上面的介绍可知,WinSock接口其实是由一个动态链接库提供的一系列函数,由这些函数实现对网络的访问。有了这层的认识,问题就好办多了,我们可以制作一个类似的动态链接库来代替原WinSock接口库,在其中实现WinSock32.dll中实现的所有函数,并保证所有函数的参数个数和顺序、返回值类型都应与原库相同。在这个自制作的动态库中,可以对我们感兴趣的函数(如发送、接收等函数)进行挡截,放入外挂控制代码,最后还继续调用原WinSock库中提供的相应功能函数,这样就可以实现对网络数据包的挡截、修改和发送等封包功能。 下面重点介绍创建挡截WinSock外挂程序的基本步骤: (1) 创建DLL项目,选择Win32 Dynamic-Link Library,再选择An empty DLL project。 (2) 新建文件wsock32.h,按如下步骤输入代码: ① 加入相关变量声明:
② 定义指向原WinSock库中的所有函数地址的指针变量,因WinSock库共提供70多个函数,限于篇幅,在此就只选择几个常用的函数列出,有关这些库函数的说明可参考MSDN相关内容。
(3) 新建wsock32.cpp文件,按如下步骤输入代码: ① 加入相关头文件声明:
② 添加DllMain函数,在此函数中首先需要加载原WinSock库,并获取此库中所有函数的地址。代码如下:
③ 定义库输出函数,在此可以对我们感兴趣的函数中添加外挂控制代码,在所有的输出函数的最后一步都调用原WinSock库的同名函数。部分输出函数定义代码如下:
(4)、新建wsock32.def配置文件,在其中加入所有库输出函数的声明,部分声明代码如下:
......其它输出函数声明代码略。 (5)、从“工程”菜单中选择“设置”,弹出Project Setting对话框,选择Link标签,在“对象/库模块”中输入Ws2_32.lib。 (6)、编译项目,产生wsock32.dll库文件。 (7)、将系统目录下原wsock32.dll库文件拷贝到被外挂程序的目录下,并将其改名为wsock.001;再将上面产生的wsock32.dll文件同样拷贝到被外挂程序的目录下。重新启动游戏程序,此时游戏程序将先加载我们自己制作的wsock32.dll文件,再通过该库文件间接调用原WinSock接口函数来实现访问网络。上面我们仅仅介绍了挡载WinSock的实现过程,至于如何加入外挂控制代码,还需要外挂开发人员对游戏数据包结构、内容、加密算法等方面的仔细分析(这个过程将是一个艰辛的过程),再生成外挂控制代码。关于数据包分析方法和技巧,不是本文讲解的范围,如您感兴趣可以到网上查查相关资料。 2.挡截API 挡截API技术与挡截WinSock技术在原理上很相似,但是前者比后者提供了更强大的功能。挡截WinSock仅只能挡截WinSock接口函数,而挡截API可以实现对应用程序调用的包括WinSock API函数在内的所有API函数的挡截。如果您的外挂程序仅打算对WinSock的函数进行挡截的话,您可以只选择使用上小节介绍的挡截WinSock技术。随着大量外挂程序在功能上的扩展,它们不仅仅只提供对数据包的挡截,而且还对游戏程序中使用的Windows API或其它DLL库函数的挡截,以使外挂的功能更加强大。例如,可以通过挡截相关API函数以实现对非中文游戏的汉化功能,有了这个利器,可以使您的外挂程序无所不能了。 挡截API技术的原理核心也是使用我们自己的函数来替换掉Windows或其它DLL库提供的函数,有点同挡截WinSock原理相似吧。但是,其实现过程却比挡截WinSock要复杂的多,如像实现挡截Winsock过程一样,将应用程序调用的所有的库文件都写一个模拟库有点不大可能,就只说Windows API就有上千个,还有很多库提供的函数结构并未公开,所以写一个模拟库代替的方式不大现实,故我们必须另谋良方。 挡截API的最终目标是使用自定义的函数代替原函数。那么,我们首先应该知道应用程序何时、何地、用何种方式调用原函数。接下来,需要将应用程序中调用该原函数的指令代码进行修改,使它将调用函数的指针指向我们自己定义的函数地址。这样,外挂程序才能完全控制应用程序调用的API函数,至于在其中如何加入外挂代码,就应需求而异了。最后还有一个重要的问题要解决,如何将我们自定义的用来代替原API函数的函数代码注入被外挂游戏程序进行地址空间中,因在Windows系统中应用程序仅只能访问到本进程地址空间内的代码和数据。 综上所述,要实现挡截API函数,至少需要解决如下三个问题: ● 如何定位游戏程序中调用API函数指令代码? ● 如何修改游戏程序中调用API函数指令代码? ● 如何将外挂代码(自定义的替换函数代码)注入到游戏程序进程地址空间? 下面我们逐一介绍这几个问题的解决方法: (1) 、定位调用API函数指令代码 我们知道,在汇编语言中使用CALL指令来调用函数或过程的,它是通过指令参数中的函数地址而定位到相应的函数代码的。那么,我们如果能寻找到程序代码中所有调用被挡截的API函数的CALL指令的话,就可以将该指令中的函数地址参数修改为替代函数的地址。虽然这是一个可行的方案,但是实现起来会很繁琐,也不稳健。庆幸的是,Windows系统中所使用的可执行文件(PE格式)采用了输入地址表机制,将所有在程序调用的API函数的地址信息存放在输入地址表中,而在程序代码CALL指令中使用的地址不是API函数的地址,而是输入地址表中该API函数的地址项,如想使程序代码中调用的API函数被代替掉,只用将输入地址表中该API函数的地址项内容修改即可。具体理解输入地址表运行机制,还需要了解一下PE格式文件结构,其中图三列出了PE格式文件的大致结构。 图三:PE格式大致结构图(003.jpg) PE格式文件一开始是一段DOS程序,当你的程序在不支持Windows的环境中运行时,它就会显示“This Program cannot be run in DOS mode”这样的警告语句,接着这个DOS文件头,就开始真正的PE文件内容了。首先是一段称为“IMAGE_NT_HEADER”的数据,其中是许多关于整个PE文件的消息,在这段数据的尾端是一个称为Data Directory的数据表,通过它能快速定位一些PE文件中段(section)的地址。在这段数据之后,则是一个“IMAGE_SECTION_HEADER”的列表,其中的每一项都详细描述了后面一个段的相关信息。接着它就是PE文件中最主要的段数据了,执行代码、数据和资源等等信息就分别存放在这些段中。 在所有的这些段里,有一个被称为“.idata”的段(输入数据段)值得我们去注意,该段中包含着一些被称为输入地址表(IAT,Import Address Table)的数据列表。每个用隐式方式加载的API所在的DLL都有一个IAT与之对应,同时一个API的地址也与IAT中一项相对应。当一个应用程序加载到内存中后,针对每一个API函数调用,相应的产生如下的汇编指令: JMP DWORD PTR [XXXXXXXX] 或 CALL DWORD PTR [XXXXXXXX] 其中,[XXXXXXXX]表示指向了输入地址表中一个项,其内容是一个DWORD,而正是这个DWORD才是API函数在内存中的真正地址。因此我们要想拦截一个API的调用,只要简单的把那个DWORD改为我们自己的函数的地址。 (2) 、修改调用API函数代码 从上面对PE文件格式的分析可知,修改调用API函数代码其实是修改被调用API函数在输入地址表中IAT项内容。由于Windows系统对应用程序指令代码地址空间的严密保护机制,使得修改程序指令代码非常困难,以至于许多高手为之编写VxD进入Ring0。在这里,我为大家介绍一种较为方便的方法修改进程内存,它仅需要调用几个Windows核心API函数,下面我首先来学会一下这几个API函数:
该函数用于查询关于本进程内虚拟地址页的信息。其中,lpAddress表示被查询页的区域地址;lpBuffer表示用于保存查询页信息的缓冲;dwLength表示缓冲区大小。返回值为实际缓冲大小。
该函数用于改变本进程内虚拟地址页的保护属性。其中,lpAddress表示被改变保护属性页区域地址;dwSize表示页区域大小;flNewProtect表示新的保护属性,可取值为PAGE_READONLY、PAGE_READWRITE、PAGE_EXECUTE等;lpflOldProtect表示用于保存改变前的保护属性。如果函数调用成功返回“T”,否则返回“F”。 有了这两个API函数,我们就可以随心所欲的修改进程内存了。首先,调用VirtualQuery()函数查询被修改内存的页信息,再根据此信息调用VirtualProtect()函数改变这些页的保护属性为PAGE_READWRITE,有了这个权限您就可以任意修改进程内存数据了。下面一段代码演示了如何将进程虚拟地址为0x0040106c处的字节清零。
(3)、注入外挂代码进入被挂游戏进程中
(3) 打开ActiveKey.cpp文件,首先加入一个函数,用于定位输入库在输入数据段中的IAT地址。代码如下:
再加入一个函数,用来定位被挡截API函数的IAT项并修改其内容为替代函数地址。代码如下:
(4) 定义替代函数,此实例中只给MessageBoxA和recv两个API进行挡截。代码如下:
(5) 在KeyboardProc函数中加入激活挡截API代码,在if( wParam == 0X79 )语句中后面加入如下else if语句:
(6) 在ActiveKey.cpp中加入头文件声明 "#include "wsock32.h"。 从“工程”菜单中选择“设置”,弹出Project Setting对话框,选择Link标签,在“对象/库模块”中输入Ws2_32..lib。 (7) 重新编译ActiveKey项目,产生ActiveKey.dll文件,将其拷贝到Simulate.exe目录下。运行Simulate.exe并启动全局钩子。激活任意应用程序,按F11键后,运行此程序中可能调用MessageBoxA函数的操作,看看信息框是不是有所变化。同样,如此程序正在接收网络数据包,就可以实现封包功能了。 六、结束语 除了以上介绍的几种游戏外挂程序常用的技术以外,在一些外挂程序中还使用了游戏数据修改技术、游戏加速技术等。在这篇文章里,就不逐一介绍了。 |
⊙
|
网络游戏的封包技术揭密 |
封包技术是网络游戏的核心技术之一,向服务器传送的数据就是存放在封包中,因此拦截封包是外挂设计的常用技术。 网络游戏的封包技术是大多数编程爱好者都比较关注的关注的问题之一,在这里就让我们一起研究一下这一个问题吧。
|
⊙
|
四种网络游戏外挂的设计方法 |
这四种外挂是:1、动作式;2、本地修改式;3、木马式;4、加速式。前三种可以用VB,Delphi等语言比较好实现,后一种则要用VC等Windows底层支持比较好的编程工具才好实现。 在几年前我看到别人玩网络游戏用上了外挂,做为程序员的我心里实在是不爽,想搞清楚这到底是怎么回事。就拿了一些来研究,小有心得,拿出来与大家共享,外挂无非就是分几种罢了(依制作难度): 1、动作式,所谓动作式,就是指用API发命令给窗口或API控制鼠标、键盘等,使游戏里的人物进行流动或者攻击,最早以前的“石器”外挂就是这种方式。(这种外挂完全是垃圾,TMD,只要会一点点API的人都知道该怎么做,不过这种外挂也是入门级的好东东,虽然不能提高你的战斗力,但是可以提高你的士气) 2、本地修改式,这种外挂跟传统上的一些游戏修改器没有两样,做这种外挂在编程只需要对内存地址有一点认识并且掌握API就可以实现,“精灵”的外挂这是这种方式写成的,它的难点在于找到那些地址码,找地址一般地要借助于别人的工具,有的游戏还有双码校验,正正找起来会比较困难。(这种外挂,比上一种有一点点难度,但是这种外挂做起来能够用,也是有一定难度的啦~~,这种外挂可以很快提升你对内存地址的理解及应用,是你编程技术提高的好东东) 3、木马式,这种外挂的目的是帮外挂制作者偷到用户的密码(TMD,“烂”就一个字,不过要知已知彼所以还是要谈一下啦~~),做这种外挂有一定的难度,需要HOOK或键盘监视技术做底子,才可以完成,它的原理是先首截了用户的帐号或密码,然后发到指定邮箱。(我以前写过这样的东东,但是从来没有用过,我知道这种东东很不道德,所以以后千万别用呀!) 4、加速式,这种外挂可以加快游戏的速度……(对不起大家,这种东东我没有实际做过,所以不能妄自评,惭愧) 这几种外挂之中,前三种可以用VB,Delphi等语言比较好实现,后两种则要用VC等底层支持比较好的编程工具才好实现。 动作式外挂 首先,先来谈一下动作式的外挂,这也是我第一次写外挂时做的最简单的一种。 记得还在“石器”时代的时候,我看到别人挂着一种软件(外挂)人物就可以四外游走(当时我还不知道外挂怎么回事),于是找了这种软件过来研究(拿来后才听别人说这叫外挂),发现这种东东其实实现起来并不难,仔佃看其实人物的行走无非就是鼠标在不同的地方点来点去而已,看后就有实现这功能的冲动,随后跑到MSDN上看了一些资料,发现这种实现这几个功能,只需要几个简单的API函数就可以搞定: 1、首先我们要知道现在鼠标的位置(为了好还原现在鼠标的位置)所以我们就要用到API函数GetCursorPos,它的使用方法如下:
2、我们把鼠标的位置移到要到人物走到的地方,我们就要用到SetCursorPos函数来移动鼠标位置,它的使用方法如下:
3、模拟鼠标发出按下和放开的动作,我们要用到mouse_event函数来实现,具休使用方法用下:
在它的dwFlags处,可用的事件很多如移动MOUSEEVENTF_MOVE,左键按下MOUSEEVENTF_LEFTDOWN,左键放开MOUSEEVENTF_LEFTUP,具体的东东还是查一下MSDN吧~~~~~ 好了,有了前面的知识,我们就可以来看看人物移走是怎么实现的了:
看了以上的代码,是不是觉得人物的游走很简单啦~~,举一仿三,还有好多好东东可以用这个技巧实现(我早就说过,TMD,这是垃圾外挂的做法,相信了吧~~~),接下来,再看看游戏里面自动攻击的做法吧(必需游戏中攻击支持快捷键的),道理还是一样的,只是用的API不同罢了~~~,这回我们要用到的是keybd_event函数,其用法如下:
我们还要知道扫描码不可以直接使用,要用函数MapVirtualKey把键值转成扫描码,MapVirtualKey的具体使用方法如下:
好了,比说此快接键是CTRL+A,接下来让我们看看实际代码是怎么写的:
本地修改式外挂
2、我们GetWindowThreadProcessId来得到游戏窗口相对应进程的进程ID,函数用法如下: DWORD GetWindowThreadProcessId( HWND hWnd, // handle of window LPDWORD lpdwProcessId // address of variable for process identifier ); 3、得到游戏进程ID后,接下来的事是要以最高权限打开进程,所用到的函数OpenProcess的具体使用方法如下:
在dwDesiredAccess之处就是设存取方式的地方,它可设的权限很多,我们在这里使用只要使用PROCESS_ALL_ACCESS 来打开进程就可以,其他的方式我们可以查一下MSDN。 4、打开进程后,我们就可以用函数对存内进行操作,在这里我们只要用到WriteProcessMemory来对内存地址写入数据即可(其他的操作方式比如说:ReadProcessMemory等,我在这里就不一一介绍了),我们看一下WriteProcessMemory的用法:
5、下面用CloseHandle关闭进程句柄就完成了。 这就是这类游戏外挂的程序实现部份的方法,好了,有了此方法,我们就有了理性的认识,我们看看实际例子,提升一下我们的感性认识吧,下面就是XX游戏的外挂代码,我们照上面的方法对应去研究一下吧:
这个游戏是用了多地址对所要提交的数据进行了校验,所以说这类游戏外挂制作并不是很难,最难的是要找到这些地址。 木马式外挂 木马式外挂,可能大多像木马吧,是帮助做外挂的人偷取别人游戏的帐号及密码的东东。因为网络上有此类外挂的存在,所以今天不得不说一下(我个人是非常讨厌这类外挂的,请看过本文的朋友不要到处乱用此技术,谢谢合作)。要做此类外挂的程序实现方法很多(比如HOOK,键盘监视等技术),因为HOOK技术对程序员的技术要求比较高并且在实际应用上需要多带一个动态链接库,所以在文中我会以键盘监视技术来实现此类木马的制作。键盘监视技术只需要一个.exe文件就能实现做到后台键盘监视,这个程序用这种技术来实现比较适合。 在做程序之前我们必需要了解一下程序的思路: 1、我们首先知道你想记录游戏的登录窗口名称。 2、判断登录窗口是否出现。 3、如果登录窗口出现,就记录键盘。 4、当窗口关闭时,把记录信息,通过邮件发送到程序设计者的邮箱。 第一点我就不具体分析了,因为你们比我还要了解你们玩的是什么游戏,登录窗口名称是什么。从第二点开始,我们就开始这类外挂的程序实现之旅: 那么我们要怎么样判断登录窗口虽否出现呢?其实这个很简单,我们用FindWindow函数就可以很轻松的实现了:
实际程序实现中,我们要找到'xx'窗口,就用FindWindow(nil,'xx')如果当返回值大于0时表示窗口已经出现,那么我们就可以对键盘信息进行记录了。 先首我们用SetWindowsHookEx设置监视日志,而该函数的用法如下:
在这里要说明的是在我们程序当中我们要对HOOKPROC这里我们要通过写一个函数,来实现而HINSTANCE这里我们直接用本程序的HINSTANCE就可以了,具体实现方法为:
而HOOKPROC里的函数就要复杂一点点:
以上就是记录键盘的整个过程,简单吧,如果记录完可不要忘记释放呀,UnHookWindowsHookEx(hHook),而hHOOK,就是创建setwindowshookex后所返回的句柄。 我们已经得到了键盘的记录,那么现在最后只要把记录的这些信息发送回来,我们就大功造成了。其他发送这块并不是很难,只要把记录从文本文件里边读出来,用DELPHI自带的电子邮件组件发一下就万事OK了。代码如下:
这个程序全部功能已经实现,编编试试。 加速型外挂 原本我一直以为加速外挂是针对某个游戏而写的,后来发现我这种概念是不对的,所谓加速外挂其实是修改时钟频率达到加速的目的。 以前DOS时代玩过编程的人就会马上想到,这很简单嘛不就是直接修改一下8253寄存器嘛,这在以前DOS时代可能可以行得通,但是windows则不然。windows是一个32位的操作系统,并不是你想改哪就改哪的(微软的东东就是如此霸气,说不给你改就不给你改),但要改也不是不可能,我们可以通过两种方法来实现:第一是写一个硬件驱动来完成,第二是用Ring0来实现(这种方法是CIH的作者陈盈豪首用的,它的原理是修改一下IDE表->创建一个中断门->进入Ring0->调用中断修改向量,但是没有办法只能用ASM汇编来实现这一切*_*,做为高级语言使用者惨啦!),用第一种方法用点麻烦,所以我们在这里就用第二种方法实现吧~~~ 在实现之前我们来理一下思路吧: 1、我们首先要写一个过程在这个过程里嵌入汇编语言来实现修改IDE表、创建中断门,修改向量等工作 2、调用这个过程来实现加速功能 好了,现在思路有了,我们就边看代码边讲解吧: 首先我们建立一个过程,这个过程就是本程序的核心部份:
最核心的东西已经写完了,大部份读者是知其然不知其所以然吧,呵呵,不过不知其所以然也然。下面我们就试着用一下这个过程来做一个类似于“变速齿轮”的一个东东吧! 先加一个窗口,在窗口上放上一个trackbar控件把其Max设为20,Min设为1,把Position设为10,在这个控件的Change事件里写上:
因为windows默认的值为$1742,所以我们把1742做为基数,又因为值越小越快,反之越慢的原理,所以写了这样一个公式,好了,这就是“变速齿轮”的一个Delphi+ASM版了(只适用于win9X),呵呵,试一下吧,这对你帮助会很大的,呵呵。 在win2000里,我们不可能实现在直接对端口进行操作,Ring0也失了效,有的人就会想到,我们可以写驱动程序来完成呀,但在这里我告诉你,windows2000的驱动不是一个VxD就能实现的,像我这样的低手是写不出windows所用的驱动WDM的,没办法,我只有借助外力实现了,ProtTalk就是一个很好的设备驱动,他很方便的来实现对低层端口的操作,从而实现加速外挂。 1、我们首先要下一个PortTalk驱动,他的官方网站是http://www.beyondlogic.org 2、我们要把里面的prottalk.sys拷贝出来。 3、建立一个Protalk.sys的接口(我想省略了,大家可以上http://www.freewebs.com/liuyue/porttalk.pas下个pas文件自己看吧) 4、实现加速外挂。 下面就讲一下这程序的实现方法吧,如果说用ProtTalk来操作端口就容易多了,比win98下用ring权限操作方便。 1、新建一个工程,把刚刚下的接口文件和Protalk.sys一起拷到工程文件保存的文件夹下。 2、我们在我们新建的工程加入我们的接口文件
3、我们建立一个过程
4、先加一个窗口,在窗口上放上一个trackbar控件把其Max设为20,Min设为1,把Position设为10,在这个控件的Change事件里写上:
就这么容易。 |
⊙
|
在内存中修改数据的网游外挂 |
游戏在运行时一些数据都是临时存放在内存中的,那么我们只需要修改具体内存值就能修改游戏中的属性,用这种方法就可以设计一种游戏外挂。 现在很多游戏都是把一些信息存入内存单元的,那么我们只需要修改具体内存值就能修改游戏中的属性,很多网络游戏也不外于此。 曾几何时,一些网络游戏也是可以用内存外挂进行修改的,后来被发现后,这些游戏就把单一内存地址改成多内存地址校验,加大了修改难度,不过仍然可以通过内存分析器可以破解的。诸如“FPE”这样的软件便提供了一定的内存分析功能。 “FPE”是基于内存外挂的佼佼者,是家喻户晓的游戏修改软件。很多同类的软件都是模仿“FPE”而得到玩家的认可。而“FPE”实现的技术到现在都没有公开,很多人只能够通过猜测“FPE”的实现方法,实现同类外挂。笔者也曾经模仿过“FPE”实现相应的功能,如“内存修改”、“内存查询”等技术。稍后会对此技术进行剖析。 既然要做内存外挂,那么就必须对Windows的内存机制有所了解。计算机的内存(RAM)总是不够用的,在操作系统中内存就有物理内存和虚拟内存之分,因为程序创建放入物理内存的地址都是在变化的,所以在得到游戏属性时并不能够直接访问物理内存地址。在v86模式下,段寄存器使用方法与实模式相同,那么可以通过段寄存器的值左移4位加上地址偏移量就可以得到线性地址,而程序创建时在线性地址的中保留4MB-2GB的一段地址,游戏中属性便放于此。在windows中把虚拟内存块称之为页,而每页为4KB,在访问内存时读取游戏属性时,为了不破坏数据完整性的快速浏览内存地址值,最好一次访问一页。 在操作进程内存时,不需要再使用汇编语言,Windows中提供了一些访问进程内存空间的API,便可以直接对进程内存进行操作。但初学者一般掌握不了这一项技术,为了使初学者也能够对内存进行操作,做出基于内存控制的外挂,笔者把一些内存操作及一些内存操作逻辑进行了封装,以控件形式提供给初学者。控件名为:MpMemCtl。 初学者在使用此控件时,要先安装外挂引擎控件包(在此后的每篇文章中外挂引擎控件包仅提供与该文章相应的控制控件),具体控件安装方式,请参阅《Delphi指南》,由于篇幅所限,恕不能详细提供。 在引擎安装完成后,便可以在Delphi中的组件栏内,找到[MP GameControls]控件组,其中可以找到[MpMemCtl]控件。初学者可以使用此控件可以对内存进行控制。 一、 得到进程句柄 需要操作游戏内存,那么首先必须确认要操作的游戏,而游戏程序在运行时所产生的每一个进程都有一个唯一的句柄。 使用控件得到句柄有三种方法: 1、 通过控件打开程序得到句柄。 在控件中,提供了startProgram方法,通过该方法,可以打开程序得到进程句柄,并且可以返回进程信息。
该方法提供了两个参数,第一个参数为要打开的程序路径,第二个参数为打开程序后所创建进程的进程信息。使用这个方法在得到进程信息的同时,并给控件的ProcHandle(进程句柄)属性进行了附值,这时可以使用控件直接对内存进程读写操作。其应用实例如下:
2、通过控件根据程序名称得到句柄。 在控件中,对系统运行进程也有了相应的描述,控件提供了两个方法,用于根据程序名称得到相应的进程句柄。getProcIDs()可以得到系统现在所运行的所有程序的名称列表。getProcID()可以通过所运行程序名称,得到相应进程的句柄。
其应用实例如下: 首先可以通过getProcIDs()并把参数列表返回ComboBox1.Items里:
接着可以通过getProcID()得到相应的进程句柄,并给控件的ProcHandle(进程句柄)属性进行了附值,这时可以使用控件直接对内存进程读写操作。
3、通过控件根据窗口名称得到句柄。 在控件中,控件提供了两个方法,用于根据窗口名称得到相应的进程句柄。可以通过getALLWindow()得到所有在进程中运行的窗口。getWinProcHandle()可以通过相应的窗口名称,得到相应的进程的句柄。
其应用实例如下: 首先可以通过getALLWindow ()并把参数列表返回ComboBox1.Items里:
接着可以通过getWinProcHandle ()得到相应的进程句柄,并给控件的ProcHandle(进程句柄)属性进行了附值,这时可以使用控件直接对内存进程读写操作。
二、使游戏暂停 在程序中,为了便于更好的得到游戏的当前属性。在控件中提供了游戏暂停方法。只需要调用该方法,游戏便可以自由的暂停或启动。该方法为:pauseProc()
控制类型只能够传入参数0或1,0代表使游戏暂停,1代表取消暂停。其应用实例如下:
三、读写内存值 游戏属性其实寄存在内存地址值里,游戏中要了解或修改游戏属性,可以通过对内存地值的读出或写入完成。 通过控件,要读写内存地址值很容易。可以通过调用控件提供的getAddressValue()及setAddressValue()两个方法即可,在使用方法之前,要确认的是要给ProcHandle属性进行附值,因为对内存的操作必须基于进程。给ProcHandle属性附值的方法,在上文中已经介绍。无论是对内存值进行读还是进行写,都要明确所要操作的内存地址。
要注意的是,传入内存地址时,内存地址必须为Pointer型。其应用实例如下: 读取地址值(如果“主角”等级所存放的地址为4549632):
这时aValue变量里的值为内存地址[4549632]的值。 写入地址值:
通过该方法可以把要修改的内存地址值改为87,即把“主角”等级改为87。 四、内存地址值分析 在游戏中要想要到游戏属性存放的内存地址,那么就对相应内存地址进行内存分析,经过分析以后才可得到游戏属性存放的人存地址。 控件提供两种基于内存地址的分析方法。一种是按精确地址值进行搜索分析,另一种是按内存变化增减量进行搜索分析。 1、 如果很明确的知道当前想要修改的地址值,那么就用精确地址值进行搜索分析 在游戏中,需要修改人物的经验值,那么首先要从游戏画面上获得经验值信息,如游戏人物当前经验值为9800,需要把经验值调高,那么这时候就需要对人物经验值在内存中搜索得到相应的内存地址,当然很可能在内存中地址值为9800的很多,第一次很可能搜索出若干个地址值为9800的地址。等待经验值再有所变化,如从9800变为了20000时,再次进行搜索,那么从刚刚所搜索到的地址中,便可以进一步获得范围更少的内存地址,以此类推,那么最后可得到经验值具体存放的地址。 如要用控件来实现内存值精确搜索,其实方法很简单,只需要调用该控件的Search()方法即可。但是在搜索之前要确认搜索的范围,正如前文中所说:“而程序创建时在线性地址的中保留4MB-2GB的一段地址”,所以要搜索的地址应该是4MB-2GB之间,所以要把控件的MaxAddress属性设为2GB,把控件的MinAddress属性设为4MB。还有一个需要确认的是需要搜索的值,那么应该把SearchValue属性设置为当前搜索的值。如果需要显示搜索进度那么可以把ShowGauge属性挂上一个相应的TGauge控件(该控件为进度条控件)。
在搜索分析时为了提高搜索效率、实现业务逻辑,那么需要传入一个参数,从而确认是否是第一次进行内存。其应用实例如下:
2、 如果不明确当前想要修改的地址值,只知道想要修改的值变大或变小,那么就按内存变化增减量进行搜索分析。 如有些游戏的人物血值不显示出来,但要对人物血值进行修改,那么只有借助于内存量增减变化而进行搜索分析出该人物血值存放的地址。如果人物被怪物打了一下,那么人物血值就会减少,那么这时候就用减量进行搜索分析,如果人物吃了“血”人物血值就会增加,那么这时候就用增量进行搜索分析。经过不断搜索,最后会把范围放血值的内存地址给搜索出来。 如要用控件来实现内存值精确搜索,其实方法很简单,只需要调用该控件的compare()方法即可。MaxAddress、MinAddress属性设置上面章节中有详细介绍,在此不再重提。在此分析中不需要再指定SearchValue属性。如果需要显示搜索进度那么可以把ShowGauge属性挂上一个相应的TGauge控件。
在搜索分析时为了提高搜索效率、实现业务逻辑,那么需要传入一个参数,从而确认是否是第一次进行内存。搜索分析类型有两种:如果参数值为0,那么就代表增量搜索。如果参数值为1,那么就代表减量搜索。其应用实例如下:
五、得到内存地址值 在控件中,提供获得分析后内存地址列表的方法,只需要调用getAddressList()方法,便可以获得分析过程中或分析结果地址列表。但如果使用的是按内存变化增减量进行搜索分析的方法,那么第一次可能会搜索出来很多的地址,致使返回速度过长,那么建议使用getAddressCount()方法确定返回列表为一定长度后才给予返回。
其应用实例如下:
通过以上五个步骤,便可以整合成一个功能比较完备的,基于内存控制方法的游戏外挂。有了“FPE”的关键部份功能。利用此工具,通过一些方法,不仅仅可以分析出来游戏属性单内存地址,而且可以分析出一部份多内存游戏属性存放地址。 |
转载于 http://dev.csdn.net/article/51/51928.shtm