当前位置:编程学习 > 网站相关 >>

如何编写游戏修改器

前言
       本文乃作者多年研究游戏修改器经验的结晶,通过浅显易懂的语言,并晓之以原理,示之以代码,将为您揭开游戏修改器的技术奥秘。本文包含了当今Win9x/Me/2000/xp平台下游戏修改器的大部分流行甚至可以说“先进”的技术,相信您通过本文的指引,定能写出一款属于自己的游戏修改器。 (SE: 本文所述的技术适用于 Windows 95/97/98/Me/2000/Xp)

       在正式开始前,我将假定您已经具备了如下的阅读条件:

1.        具备在32位Windows 下使用 C/C++ 编程的基本知识(SE:最好是VC),大致了解 Windows的结构及基本原理。

2.        可供查询的Windows编程资料,如MSDN和著名的《Windows 核心编程》等。

3.        使用过游戏修改器,开个玩笑:)


游戏修改器的基本工作原理
所谓游戏修改器,主要是通过修改游戏程序的内存数据或存盘文件来修改游戏中的相关数据,使之达到“无敌”等效果。

游戏修改器主要分为两类:单一游戏的修改器和通用游戏修改器。顾名思义,前者只能修改特定的游戏,此类修改器也叫“无敌引导程序”或“游戏作弊器”;而后者则能够以“不变应万变”,可以修改大多数的游戏。本文主要讨论后者,相比之下,前者是后者只留下修改功能的“简化版”。

总的来说,游戏修改器主要的功能便是反复搜索并筛选某一特定内存地址并将其按照固定的周期修改为特定的值(所谓的“锁定”),当然,将“内存”换为“文件”便可以以同样的方式搜索文件了。

搜索和筛选是如何实现的呢?假如我们已经可以访问游戏的内存,我们将游戏的内存分块读入一个缓冲区,然后借由下面的函数搜索:


//在一个内存块中搜索内容

//成功返回位置

//失败返回-1

//(SE:现在我发现这个顺序查找函数完全可以用 STL 的 std::find() 函数取代,原因是写文章的时候我还不太了解 STL:)

int MemFind(int iStartPosition, LPBYTE pDestBuffer, int iDestBufferLength, LPBYTE pPatternBuffer, int iPatternBufferLength)

{    signed int iFoundPosition, i;
      iFoundPosition = -1;
    if(iStartPosition > iDestBufferLength)return -1;
       for(i = iStartPosition; i < (iDestBufferLength + 1); i++)
    {       
       if(memcmp(&pDestBuffer[i], pPatternBuffer, iPatternBufferLength) == 0)
        {  iFoundPosition = i;
            break;
        }
    }
    return iFoundPosition;
}

由于游戏在内存或文件中保存的数据是二进制格式,因此,当我们搜索一个为123的整数时应用如下的格式:
int nPattern = 123;
dwOffset = MemFind(nStartPosition, pDestBuffer, nDestBufferLength, (LPBYTE)(&nPattern), sizeof(nPattern))

同样,可以这样搜索浮点类型:
float rPattern = 123;
dwOffset = MemFind(nStartPosition, pDestBuffer, nDestBufferLength, (LPBYTE)(&rPattern), sizeof(rPattern)) 

由于内存中肯定会存在许多相同的数据,所以第一次我们肯定会搜索到许多地址,而真正我们要找的地址一定包含在其中。所以,我们建立一个临时文件将这些地址保存起来,并设置“有效”标志,如下代码所示:
struct CTempItem
{    DWORD dwAddress;
    BOOL bEnable;
};

CTempItem item;
item.dwAddress = ?????????;
item.bEnable = TRUE;
fwrite(&item, sizeof(item), 1, fp);

接下来,当用户进行第二次搜索时,将这些保存在临时文件中的数据取出来,先看item.bEnable若等于FALSE则跳过,否则读取item.dwAddress所指示的游戏内存,并于用户第二次输入的数值相比较,若发现相同,则设置item.bEnable = TRUE;若不同设置item.bEnable=FALSE,表示废弃。完成之后,再次把item写回文件,当所有item分析完之后,我们就完成了第二次搜索。再接下来,bEnable=TRUE的地址还有很多,则仍然用第二次搜索的方式反复搜索,直到剩下1-2个地址为止。 (SE:应用读写缓冲区,即成批地读写,可以大大加快速度)

由以上介绍可以看出,游戏修改器的搜索分为2个阶段:第一次搜索和第2、3、4……次搜索,游戏修改器在第一次搜索出的很多地址中分析出与用户输入的数据始终相同的地址。当我们有了目标地址,就可以按照用户的意愿定时或手动方式写入用户指定的数据,这便是游戏修改器的基本原理。

当然,这只是基本原理,当具体编写修改器将遇到许多具体的技术困难,以下章节将为您一一解答。

如何访问游戏程序的内存
当我们的修改器运行于Windows时,首先遇到的问题便是如何访问游戏的内存。

首先,在访问游戏的内存前我们还必须获得游戏进程的句柄,这可以通过ToolHelp函数获取系统中当前运行的所有进程的列表和各进程的ID,经由用户选择之后通过OpenProcess函数来获取。若您的修改器运行于后台,而前台是游戏的话,可以在用户按下“弹出”热键时使用GetForegroundWindow函数获取游戏窗口的HWND,再使用GetWindowThreadProcessId转换成游戏进程的ID,再使用OpenProcess函数获取游戏进程的句柄。

有了游戏进程的句柄之后,便可以使用Windows提供的ReadProcessMemory和WriteProcessMemory这两个API来读写游戏的内存了。但是,在Windows9x中每个进程均拥有各自独立的1GB虚拟地址空间,而在Win2000/XP下更是达到了2GB。显然,搜索这样大的地址空间是不现实的,而且游戏也仅仅用了其中的几十到几百MB。所以,我们需要使用VirtualQueryEx这个函数来查询哪些是已经分配的地址,哪些是未用的地址。以下查询与搜索相结合的示范代码:
DWORD dwBaseAddress;
SYSTEM_INFO si;
GetSystemInfo(si); 

dwBaseAddress = si.lpMinimumApplicationAddress;
while(dwBaseAddress < si.lpMaximumApplicationAddress)
{
    mbi.BaseAddress = (LPVOID)dwBaseAddress;
    ProcessMem.Query((PVOID)dwBaseAddress, &mbi);
    VirtualQueryEx(hProcess, (LPVOID)dwAddress), mbi, sizeof(mbi)
    dwBaseAddress = (DWORD)mbi.BaseAddress + mbi.RegionSize;

    if(mbi.State != MEM_COMMIT || mbi.AllocationProtect != PAGE_READWRITE); //跳过未分配或不可读写的区域
    {
       continue;
    }     
    //搜索这块内存区域
}

资源:请到 http://evenmorn.go.nease.net下载我写的一个很“简陋”的游戏修改器——GameProbe的源程序。

如何实现热键
       热键的原理很简单,使用全局键盘HOOK就可以了,鉴于这方面的资料较多,具体的Hook使用方法请参阅MSDN或相关资料。

如何实现暂停游戏
       这是游戏修改器必须具备的一项功能了,若不暂停游戏,搜索之前数值很可能会改变,从而造成找不到数据的现象。暂停游戏的办法有很多种,主要有:

1.        Debug法,这种方法利用DebugActiveProcess这个Windows API将游戏修改器作为游戏的调试程序,游戏修改器可通过Windows提供的调试事件(DebugEvents)来获取游戏的各个线程的句柄。但此法有一个缺点就是不能在关闭游戏前关闭修改器,否则Windows将会自动终止游戏的运行,运用此方法的典型是FPE2000。

2.        通过使用ToolHelp系列函数获取游戏进程的所有线程,并使用公开的OpenThread这个API来获得各线程的句柄并使用SuspendThread 和 ResumeThread来暂停或恢复游戏的运行,目前大部分程序运用此法。(BTW:著名的 GameMaster使用的是CreateProcess并通过Suspend游戏主线程的方法暂停游戏的,很显然,若游戏采用了多线程,此方法是欠妥的。)

但恼人的是Win9x下并没有OpenThread这个API,不过我们可以通过一个未公开的使用 TCB 的方法在Win9x下替代OpenThread而获取线程的句柄,从而达到了暂停游戏运行的目的。您可以到 http://www.windrun.com/ 下载Ligtest前辈编写的PauseProcess程序的源代码来学习,或者下载我的 TestPopup。

如何在游戏中弹出自己的界面
       这个问题可以和热键问题一并解决:众所周知,Windows是一个消息驱动的32位操作系统。在Windows中,所有正在运的进程都有一个独立的2GB的虚拟地址空间,进程之间相互不可见。Windows的绝大多数API与消息是不能跨越进程的。

“Hook”在Windows中主要是用来截取消息的,形象说,就是用来“钩” 消息的。Hook实际上是一个处理消息的程序段,每当特定的消息发出,在没有到达目的窗口前,Hook函数就先捕获该消息,

补充:综合编程 , 安全编程 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,