标题:【原创】游戏修改的常用方法之一——远程读写内存(asm源码详注)作者:王仁军时间: 2008-06-15,20:25:46链接: /showthread.php?t=66624comment *-----------------------------------------------------------------------前几日,几个朋友在局域网中玩红警对战,常因游戏中无钱而使战斗长时间不能结束,有人就想到用游侠修改金钱来作弊,无奈在网络对战中用游侠修改游戏是要暂停游戏的,一人停下大家都停了,作弊就被人发现了。
能不能整它个一键锁定,神不知鬼不觉,不被人发现?于是我就编写了这个“傻瓜式RA2游戏修改器”。
为什么没有去破解游戏或做补钉呢?那样的话别人玩这个机子的RA2游戏也能享受作弊待遇了,且钱数不变易被人发现作弊了:)用法:①把本程序拷到RA2游戏目录中,运行本程序,游戏程序被启动了②开始一场战斗,切记,等游戏界面显示的钱数“不变”时,按下数字键盘上的星号键第一遍搜索开始了,也许要好几秒时间,这段时间内你可以点兵派将,但不可使钱数增减。
③当你听到提示音且鼠标被置于屏幕左上角时,第一遍搜完了。
现在赶紧让你的钱数变化,比如建一座电站,最好是钱数再次“不变“时,可以按下数字键盘上的星号键进行第二遍搜索了,这次是极极极的快,鼠标没有被置于左上角,说明找到正确地址并已自动锁定钱数了。
什么?钱数在变化没有锁定?非也!五秒之内你用钱了自然要减少挣钱了自然要增加不然旁观者看到钱数不变就知道你作弊了:),这是不同于游侠的地方,要的就是这个效果。
当一场战斗终了,要开始下一场战斗时,你只要按一下“-”键,然后重复上述步骤即可。
为什么要重新搜索?因为每一盘游戏的金钱数地址都不同。
本程序在WinXP/SP2、ra2之1.006英文版(有中国超牛机器人的那个)运行通过且稳定无误。
如果是其他版本,只要用十六进制编辑工具搜索数值“008373cch”,改为你想要的地址值即可,共有两处要改。
这个地址值是怎么得来的?最简单的方法是用游侠了。
有游侠为啥还要用我这个破玩意儿?因为游侠的界面和暂停会让别人发现你作弊的。
用游侠搜索到的地址一般有三个,有两个地址值相差4,取较小的那个即可,最大的也是最另类的那个每盘游戏都会变,它才是正直的金钱数保存地址,本程序就是要找到这个变化的地址并锁定数值。
对于那些弹出游侠界面或暂停后就死掉的游戏,用类似的方法就可以修改了吧?!如果数值地址不是象ra2这样有多个,可在游戏中记下几个数值,然后写入一文件中,搜索时从文件中读取数值即可。
也可用键盘钩子记录按键,进行无界面动态输入。
*-------------------------------------------------------------------------------.586.model flat,stdcalloption casemap:noneinclude windows.incinclude kernel32.incinclude user32.incinclude psapi.incincludelib kernel32.libincludelib user32.libincludelib psapi.libADD_DA TA1 equ 008373cch ;00883c84h ;其他版本要改这个地址值;ADD_DA TA2 equ 008373d0h ;00883c88h ;这个不用ADD_DA TA3_START equ 01000000h ;搜索范围开始地址,可根据需要改动ADD_DA TA3_END equ 0f600000h ;搜索范围结束地址,可根据需要改动MEMSIZE equ 10000h ;每次读取数据块的大小,不宜太小.code;------------------------------------------------------------------------;取得游戏进程的句柄。
游戏中按下“*”号键便来到这里;有两种常用的方法:进程快照查找法和当前活动窗口法;------------------------------------------------------------------------_GetProcessHandle proccomment *在调试时用这段代码取得游戏进程的句柄为好LOCAL info:PROCESSENTRY32LOCAL handle:HANDLEinvoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,0 ;进程快照mov handle,eaxmov info.dwSize,sizeof PROCESSENTRY32invoke Process32First,handle,addr info.repeatmov eax,@Finvoke lstrcmpi,addr info.szExeFile,eax;比较是否为我们要找的进程名,不区分大小写.if !eaxinvoke CloseHandle,handle;invoke MessageBox,NULL,addr info.szExeFile,NULL,MB_OKinvoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,info.th32ProcessIDjmp EXIT.endifinvoke Process32Next,handle,addr info.until !eaxinvoke CloseHandle,handlexor eax,eaxEXIT:ret@@:db"Game.exe",0*通常情况下也可以用下面的方法取得游戏进程的句柄,但要注意……LOCAL ProcessIdinvoke GetForegroundWindow ;你必须确保当前窗口为游戏界面窗口,这样才能正确取得游戏进程IDlea edx,ProcessIdinvoke GetWindowThreadProcessId,eax,edxinvoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,ProcessIdret_GetProcessHandle endp;------------------------------------------------------------------------;第一遍搜索。
因是全程搜索,耗时几秒至十多秒(与玩家数多少有关),WinXP 中;凭经验知道本游戏中钱数地址在01000000h至0F600000之间,搜此范围以减少用时;------------------------------------------------------------------------_GetDataAddr proc uses esi edi hProcess:DWORD,num:DWORD,hmem:DWORDLOCAL N,ListMemSize,pListMem,ReadSizeLOCAL mbi:MEMORY_BASIC_INFORMA TIONinvoke EmptyWorkingSet,hProcess ;减少游戏进程提交内存数,希望能减少搜索量,加快搜索速度invoke SetProcessWorkingSetSize,hProcess,-1,-1 ;不知是否有效?!愿听高手指导invoke GlobalLock,hmem ;锁定保存搜索结果的内存mov pListMem,eaxinvoke GlobalSize,hmem;保存搜索结果的内存大小mov ListMemSize,eaxinvoke ReadProcessMemory,hProcess,ADD_DA TA1,addr N,sizeof N,NULL ;不用手输入金钱数,从内存读取金钱数mov edi,N ;保存金钱数,以便后面比较搜索,原理见前文invoke GlobalAlloc,GMEM_FIXED,MEMSIZE ;分配内存,为读取数据做准备mov esi,eax;保存内存地址mov ecx,ADD_DA TA3_START ;设置要搜索的内存地址范围开始处.repeat;循环搜索游戏内存@@: invoke VirtualQueryEx,hProcess,ecx,addr mbi,sizeof MEMORY_BASIC_I NFORMA TION ;返回页面虚拟信息.if mbi.State == MEM_COMMIT && mbi.Protect == PAGE_READWRITE ;已提交且为可读写的区域,加速搜索mov ReadSize,MEMSIZE ;每次可读取的数据大小.repeat;循环读取该内存区段中的数据.if mbi.RegionSize<MEMSIZE ;如果剩下的数据块小于MEMSIZEmov eax,mbi.RegionSizemov ReadSize,eax;读剩下的数据大小.endifinvoke ReadProcessMemory,hProcess,mbi.BaseAddress,esi,ReadSize,addr N ;读游戏数据xor ecx,ecx;ecx为相对于区段首的偏移地址.repeat;在读取的数据块中搜索金钱数的地址.if edi==[esi+ecx] ;数值相等,找到了?mov eax,num ;地址num中记录了搜索结果的个数inc dword ptr[eax] ;搜索的结果个数加一mov eax,dword ptr[eax]shl eax,2h ;保存结果所需的内存大小.if eax>ListMemSize ;如果搜索到的结果较多,内存用完,要重新分配内存push eaxpush ecxadd eax,1000h ;追加4K内存invoke GlobalReAlloc,hmem,eax,GMEM_MOVEABLE ;重新分配内存,原来的数据被复制过来invoke GlobalLock,eaxmov pListMem,eax;保存搜索结果的内存首地址invoke GlobalSize,hmemmov ListMemSize,eax;保存搜索结果的内存大小pop ecxpop eax.endifadd eax,pListMem ;相当于pListMem[num]mov edx,mbi.BaseAddressadd edx,ecx;首地址+偏移地址=实际地址mov [eax-4h],edx;搜索的结果保存起来,pListMem[num-1]=实际地址.endifadd ecx,4h ;金钱数为DWORD型数值,考虑到内存对齐,这里是不用担心漏掉的.until ecx>=N ;读取的数据块比较完了吗?mov eax,ReadSize ;准备再读一次add mbi.BaseAddress,eax;下次从这里开始读sub mbi.RegionSize,eax;下次要读的大小.until mbi.RegionSize<=0h ;下次要读的大小为0了吗?为0则本区段读完,去下一区段.endifmov ecx,mbi.BaseAddressadd ecx,mbi.RegionSize ;下一区段首地址.until ecx>=ADD_DA TA3_END ;下一区段在搜索范围之外了吗?是则完成第一遍搜索invoke GlobalFree,esiret_GetDataAddr endp;------------------------------------------------------------------------;第二、三……遍的搜索,在第一次的结果中找,速度极快;第二次按下“*”键便来到这里,一般只要两遍就可锁定。