首页
社区
课程
招聘
[原创]游戏修改的常用方法之一——远程读写内存(asm源码详注)
2008-6-15 20:25 20272

[原创]游戏修改的常用方法之一——远程读写内存(asm源码详注)

2008-6-15 20:25
20272
comment *-----------------------------------------------------------------------
  前几日,几个朋友在局域网中玩红警对战,常因游戏中无钱而使战斗长时间不能结束,
有人就想到用游侠修改金钱来作弊,无奈在网络对战中用游侠修改游戏是要暂停游戏的,一
人停下大家都停了,作弊就被人发现了。能不能整它个一键锁定,神不知鬼不觉,不被人发
现?于是我就编写了这个“傻瓜式RA2游戏修改器”。为什么没有去破解游戏或做补钉呢?那
样的话别人玩这个机子的RA2游戏也能享受作弊待遇了,且钱数不变易被人发现作弊了:)

用法:
  ①把本程序拷到RA2游戏目录中,运行本程序,游戏程序被启动了
  ②开始一场战斗,切记,等游戏界面显示的钱数“不变”时,按下数字键盘上的星号键
第一遍搜索开始了,也许要好几秒时间,这段时间内你可以点兵派将,但不可使钱数增减。
  ③当你听到提示音且鼠标被置于屏幕左上角时,第一遍搜完了。现在赶紧让你的钱数变
化,比如建一座电站,最好是钱数再次“不变“时,可以按下数字键盘上的星号键进行第二
遍搜索了,这次是极极极的快,鼠标没有被置于左上角,说明找到正确地址并已自动锁定钱
数了。什么?钱数在变化没有锁定?非也!五秒之内你用钱了自然要减少挣钱了自然要增加
不然旁观者看到钱数不变就知道你作弊了:),这是不同于游侠的地方,要的就是这个效果。

  当一场战斗终了,要开始下一场战斗时,你只要按一下“-”键,然后重复上述步骤即
可。为什么要重新搜索?因为每一盘游戏的金钱数地址都不同。

  本程序在WinXP/SP2、ra2之1.006英文版(有中国超牛机器人的那个)运行通过且稳定
无误。如果是其他版本,只要用十六进制编辑工具搜索数值“008373cch”,改为你想要的
地址值即可,共有两处要改。这个地址值是怎么得来的?最简单的方法是用游侠了。有游侠
为啥还要用我这个破玩意儿?因为游侠的界面和暂停会让别人发现你作弊的。用游侠搜索到
的地址一般有三个,有两个地址值相差4,取较小的那个即可,最大的也是最另类的那个每
盘游戏都会变,它才是正直的金钱数保存地址,本程序就是要找到这个变化的地址并锁定数
值。

  对于那些弹出游侠界面或暂停后就死掉的游戏,用类似的方法就可以修改了吧?!如果
数值地址不是象ra2这样有多个,可在游戏中记下几个数值,然后写入一文件中,搜索时从文
件中读取数值即可。也可用键盘钩子记录按键,进行无界面动态输入。
*-------------------------------------------------------------------------------

.586
.model flat,stdcall
option casemap:none

   include windows.inc
   include kernel32.inc
   include user32.inc
   include psapi.inc
   
   includelib kernel32.lib
   includelib user32.lib
   includelib psapi.lib

ADD_DATA1    equ 008373cch    ;00883c84h    ;其他版本要改这个地址值
;ADD_DATA2    equ 008373d0h    ;00883c88h    ;这个不用
ADD_DATA3_START equ 01000000h    ;搜索范围开始地址,可根据需要改动
ADD_DATA3_END    equ 0f600000h    ;搜索范围结束地址,可根据需要改动
MEMSIZE    equ 10000h    ;每次读取数据块的大小,不宜太小

.code

;------------------------------------------------------------------------
;取得游戏进程的句柄。游戏中按下“*”号键便来到这里
;有两种常用的方法:进程快照查找法和当前活动窗口法
;------------------------------------------------------------------------
_GetProcessHandle proc

comment *在调试时用这段代码取得游戏进程的句柄为好
    LOCAL  info:PROCESSENTRY32
    LOCAL  handle:HANDLE
   
    invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,0 ;进程快照
    mov    handle,eax
    mov    info.dwSize,sizeof PROCESSENTRY32
    invoke Process32First,handle,addr info
    .repeat
        mov   eax,@F
        invoke lstrcmpi,addr info.szExeFile,eax ;比较是否为我们要找的进程名,不区分大小写
        .if !eax
            invoke CloseHandle,handle
            ;invoke MessageBox,NULL,addr info.szExeFile,NULL,MB_OK
            invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,info.th32ProcessID
            jmp EXIT
        .endif
        invoke Process32Next,handle,addr info
    .until !eax
    invoke CloseHandle,handle
    xor eax,eax
EXIT:   
    ret

@@:   
    db "Game.exe",0
    *通常情况下也可以用下面的方法取得游戏进程的句柄,但要注意……
    LOCAL ProcessId
    invoke GetForegroundWindow ;你必须确保当前窗口为游戏界面窗口,这样才能正确取得游戏进程ID
    lea    edx,ProcessId
    invoke GetWindowThreadProcessId,eax,edx
    invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,ProcessId
   
    ret
   
_GetProcessHandle endp

;------------------------------------------------------------------------
;第一遍搜索。因是全程搜索,耗时几秒至十多秒(与玩家数多少有关),WinXP中
;凭经验知道本游戏中钱数地址在01000000h至0F600000之间,搜此范围以减少用时
;------------------------------------------------------------------------
_GetDataAddr proc uses esi edi hProcess:DWORD,num:DWORD,hmem:DWORD
    LOCAL N,ListMemSize,pListMem,ReadSize
    LOCAL mbi:MEMORY_BASIC_INFORMATION

    invoke EmptyWorkingSet,hProcess ;减少游戏进程提交内存数,希望能减少搜索量,加快搜索速度
    invoke SetProcessWorkingSetSize,hProcess,-1,-1 ;不知是否有效?!愿听高手指导

    invoke GlobalLock,hmem ;锁定保存搜索结果的内存
    mov  pListMem,eax
    invoke GlobalSize,hmem;保存搜索结果的内存大小
    mov    ListMemSize,eax
    invoke ReadProcessMemory,hProcess,ADD_DATA1,addr N,sizeof N,NULL ;不用手输入金钱数,从内存读取金钱数
    mov edi,N ;保存金钱数,以便后面比较搜索,原理见前文
    invoke GlobalAlloc,GMEM_FIXED,MEMSIZE ;分配内存,为读取数据做准备
    mov esi,eax ;保存内存地址
    mov ecx,ADD_DATA3_START ;设置要搜索的内存地址范围开始处
    .repeat ;循环搜索游戏内存
@@:        invoke VirtualQueryEx,hProcess,ecx,addr mbi,sizeof MEMORY_BASIC_INFORMATION ;返回页面虚拟信息
        .if mbi.State == MEM_COMMIT && mbi.Protect == PAGE_READWRITE ;已提交且为可读写的区域,加速搜索
            mov  ReadSize,MEMSIZE ;每次可读取的数据大小
            .repeat ;循环读取该内存区段中的数据
                .if  mbi.RegionSize<MEMSIZE ;如果剩下的数据块小于MEMSIZE
                    mov  eax,mbi.RegionSize
                    mov  ReadSize,eax ;读剩下的数据大小
                .endif
                invoke 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 eax
                            push ecx
                            add  eax,1000h ;追加4K内存
                            invoke GlobalReAlloc,hmem,eax,GMEM_MOVEABLE ;重新分配内存,原来的数据被复制过来
                            invoke GlobalLock,eax
                            mov  pListMem,eax ;保存搜索结果的内存首地址
                            invoke GlobalSize,hmem
                            mov  ListMemSize,eax ;保存搜索结果的内存大小
                            pop  ecx
                            pop  eax
                        .endif
                        add  eax,pListMem ;相当于pListMem[num]
                        mov  edx,mbi.BaseAddress
                        add  edx,ecx ;首地址+偏移地址=实际地址
                        mov  [eax-4h],edx ;搜索的结果保存起来,pListMem[num-1]=实际地址
                    .endif
                    add ecx,4h ;金钱数为DWORD型数值,考虑到内存对齐,这里是不用担心漏掉的
                .until ecx>=N ;读取的数据块比较完了吗?
                mov  eax,ReadSize ;准备再读一次
                add  mbi.BaseAddress,eax ;下次从这里开始读
                sub  mbi.RegionSize,eax ;下次要读的大小
            .until  mbi.RegionSize<=0h ;下次要读的大小为0了吗?为0则本区段读完,去下一区段
        .endif
        mov  ecx,mbi.BaseAddress
        add  ecx,mbi.RegionSize ;下一区段首地址
    .until ecx>=ADD_DATA3_END ;下一区段在搜索范围之外了吗?是则完成第一遍搜索
    invoke GlobalFree,esi

    ret

_GetDataAddr endp

;------------------------------------------------------------------------
;第二、三……遍的搜索,在第一次的结果中找,速度极快
;第二次按下“*”键便来到这里,一般只要两遍就可锁定。算法:有用地址向前移
;结果个数由num返回,如果num==1就算找到正确的金钱地址了
;------------------------------------------------------------------------
_FindAddrInList proc uses edi esi hProcess:DWORD,num:DWORD,hmem:DWORD
    LOCAL Data,N
    LOCAL DD1,DD2

    invoke ReadProcessMemory,hProcess,ADD_DATA1,addr DD1,sizeof DD1,NULL ;读取金钱数
    invoke GlobalLock,hmem
    mov    edi,eax ;前次搜索结果保存的内存首地址
    xor    esi,esi ;指针,指向第一个结果
    mov    N,esi ;本次搜索到的个数初始化为0
    .repeat ;逐个比较
        mov edx,[edi+esi*4h] ;相当于edx=hmem[esi]
        invoke ReadProcessMemory,hProcess,edx,addr DD2,sizeof DD2,NULL ;读取数值
        mov eax,DD1
        .if eax==DD2 ;等于金钱数吗?等则记录下来
            push [edi+esi*4h]
            mov eax,N
            pop [edi+eax*4h] ;相当于hmem[N]=hmem[esi],即把搜索到的结果向hmem内存前面移
            inc N ;搜索到的个数加一
        .endif
        mov eax,num
        inc esi ;指针指向下一个结果
    .until esi>=[eax] ;每个都比较过了吗?是则完成这次搜索
    mov    edx,N ;这次搜索到的结果个数
    mov    [eax],edx ;修改原来的个数
    shl    edx,2h ;个数×4=内存大小

    invoke GlobalReAlloc,hmem,edx,GMEM_MOVEABLE ;释放多余的内存

    ret
   
_FindAddrInList endp

;------------------------------------------------------------------------
;找到了正确的地址,可以锁定金钱数值了:),每五秒锁定一次,按“-”键停锁
;游戏中钱数看起来有增有减,象未锁定一样,别人不容易发现你作弊
;------------------------------------------------------------------------
_WriteProcessData proc uses edi esi hProcess:DWORD,hmem:DWORD
    LOCAL DATA,IsRun
   
    mov    esi,2000 ;要锁定的金钱数,别太多,多了是会招贼来偷的:)
    mov    DATA,esi
    invoke GlobalLock,hmem
    mov    edi,[eax] ;游戏中真正的保存金钱数的地址
    .repeat
        .if esi>=5h ;每五循环锁定一次
            xor    esi,esi ;循环次数清0
            invoke WriteProcessMemory,hProcess,edi,addr DATA,sizeof DATA,NULL ;写入钱数
        .endif   
        invoke Sleep,1000 ;定时一秒
        inc    esi ;循环次数加一
        invoke GetExitCodeProcess,hProcess,addr IsRun ;游戏程序还在运行吗?
        invoke GetAsyncKeyState,VK_SUBTRACT ;按了“-”键吗?
    .until eax || IsRun!=STILL_ACTIVE ;如果游戏退出或按了“-”键则结束循环
   
    ret
   
_WriteProcessData endp

;------------------------------------------------------------------------
;把本程序拷入游戏文件夹,运行本程序,游戏被启动,本程序在后台运行,无界面
;本程序运行后启动“ra2.exe”,再由“ra2.exe”启动游戏程序,之后“ra2.exe”
;无用了,停掉它以节约内存。
;------------------------------------------------------------------------
_StartGame proc
    LOCAL StartInfo:STARTUPINFO
    LOCAL PI:PROCESS_INFORMATION

    invoke RtlZeroMemory,addr StartInfo,sizeof STARTUPINFO
    mov StartInfo.cb,sizeof STARTUPINFO
    mov ecx,GAME_NAME
    xor edx,edx
    invoke CreateProcess,edx,ecx,edx,edx,edx,edx,edx,edx,addr StartInfo,addr PI ;启动游戏
    invoke Sleep,10000 ;等10秒,游戏程序应该启动了吧?
    invoke TerminateProcess,PI.hProcess,0h ;没用了,停掉它
    invoke CloseHandle,PI.hProcess ;释放内存

    ret
GAME_NAME:
    db "ra2.exe",0

_StartGame endp

start:
MAIN    proc
    LOCAL msg:MSG
    LOCAL num,hmem,hProcess,MutexName

    invoke _StartGame ;启动游戏
    mov eax,"2ar"
    mov MutexName,eax
    invoke CreateMutex,NULL,TRUE,addr MutexName
    invoke GetLastError
    .if eax!=ERROR_ALREADY_EXISTS ;只让本程序的一个实例运行
        invoke RegisterHotKey,NULL,VK_MULTIPLY,0h,VK_MULTIPLY ;注册热键“*”
        invoke RegisterHotKey,NULL,VK_SUBTRACT,0h,VK_SUBTRACT ;注册热键“-”
        invoke RegisterHotKey,NULL,'X',MOD_CONTROL OR MOD_ALT,'X' ;注册热键“Ctrl+Alt+X”
        invoke GlobalAlloc,GMEM_MOVEABLE,1000h ;预留内存空间,搜索时用来保存结果
        mov hmem,eax ;搜索结果个数的不确定性需要我们用GlobalReAlloc来重新分配内存大小
        xor eax,eax
        mov num,eax ;搜索结果个数初始化为0
        mov hProcess,eax ;始化为0
        .while 1
            invoke GetMessage,addr msg,NULL,NULL,NULL ;等待消息
            .if msg.message==WM_HOTKEY ;热键消息
                .break .if msg.wParam=='X' ;按了热键“Ctrl+Alt+X”则退出本程序

                .if msg.wParam==VK_SUBTRACT ;按了热键“-”
                    mov num,0h ;搜索结果个数置0,表示从未搜索过
                .else ;按了热键“*”
                    .if num==0h ;搜索结果个数为0则从未搜索过,进行第一遍搜索
                        .if hProcess ;如果在非游戏界面中按了“*”,这样可避免误操作
                            invoke CloseHandle,hProcess ;释放内存
                        .endif

                        invoke _GetProcessHandle ;取游戏进程句柄
                        mov hProcess,eax
                        lea ecx,num ;指针型参数
                        invoke _GetDataAddr,eax,ecx,hmem
                    .elseif num>1h ;搜索结果个数非0则至少已搜过一遍且未找到正确地址
                        invoke _FindAddrInList,hProcess,addr num,hmem  ;再搜它一遍或几遍
                    .endif
               
                    .if num==1h ;搜索结果为1,说明找到正确地址了
                        invoke _WriteProcessData,hProcess,hmem ;去锁定它!
                        .break .if eax==0 ;如果你没按“-”键,那一定是游戏退出了,咱也退出吧
                    .endif               
                .endif
                invoke MessageBeep,MB_OK ;发声提示游戏者,按键收到,该做的本程序都做过了
                invoke SetCursorPos,9h,9h ;把鼠标置于屏幕左上角,提醒游戏者,第一遍搜索时特别有用
                invoke GetCurrentProcess
                invoke EmptyWorkingSet,eax ;减少自己的内存占用量,不和游戏争内存
            .endif
        .endw
        invoke UnregisterHotKey,NULL,VK_MULTIPLY ;以下为退出前的清理工作
        invoke UnregisterHotKey,NULL,VK_SUBTRACT
        invoke UnregisterHotKey,NULL,'X'

        invoke CloseHandle,hProcess
        invoke GlobalUnlock,hmem
        invoke GlobalFree,hmem
        invoke ExitProcess,NULL
    .endif
   
    ret
   
MAIN    endp

end start

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞7
打赏
分享
最新回复 (33)
雪    币: 485
活跃值: (12)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
petnt 12 2008-6-15 20:35
2
0
不错,参考一下
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
combojiang 26 2008-6-15 21:41
3
0
代码写得漂亮啊
雪    币: 222
活跃值: (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
foresee 2 2008-6-15 22:31
4
0
羡慕中,学习
雪    币: 144
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
卡秋莎 2008-6-15 23:30
5
0
学习ASM。。。
雪    币: 1844
活跃值: (35)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
yingyue 2008-6-15 23:37
6
0
ASM 很好很强大
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
快雪时晴 4 2008-6-15 23:38
7
0
非常不错,而且一下三精华
雪    币: 33
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
PEBOSS 2008-6-16 00:45
8
0
这样做过于麻烦..直接跟踪游戏的基地址.就可以修改了

多次查找.过于麻烦!
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
YoDe 2008-6-16 05:25
9
0
很强大 就是看不懂啊
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhucheba 2008-6-16 09:54
10
0
非常不错,而且一下三精华
雪    币: 246
活跃值: (23)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
WksWlj999 2 2008-6-16 10:25
11
0
收藏学习ASM。
雪    币: 134
活跃值: (84)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
NWMonster 1 2008-6-16 10:57
12
0
好文,收藏。。。。
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
nest 2008-6-16 11:47
13
0
很牛很牛,高人
雪    币: 207
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
clyfish 1 2008-6-16 12:28
14
0
楼主研究下星际争霸吧
雪    币: 340
活跃值: (897)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
noword_forever 5 2008-6-16 14:52
15
0
BS打个游戏还要作弊的。
雪    币: 350
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
要学会编 2008-6-16 19:55
16
0
代码着色的啊, 是用啥写的?  RADASM 还是MASMPLUS啊.
雪    币: 381
活跃值: (125)
能力值: ( LV13,RANK:330 )
在线值:
发帖
回帖
粉丝
HSQ 8 2008-6-16 20:03
17
0
今天收获不小啊,顶了
雪    币: 415
活跃值: (34)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
笨奔 1 2008-6-17 01:38
18
0
楼主和单机游戏干上了,从扫雷到红警。
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wellerweldon 2008-6-17 12:12
19
0
LZ有没有程序??我现在还在玩红警呢
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hack杰 2008-6-20 17:20
20
0
写的真的很不错
雪    币: 212
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
writer15 2008-6-20 22:53
21
0
搞不懂为什么不同步这些数据,导致有作B的机会。
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小菜菜鸟 2008-6-30 08:25
22
0
漂亮漂亮漂亮
雪    币: 215
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dyk158 2008-7-2 12:39
23
0
代码写的不错,值得参考一下。
雪    币: 225
活跃值: (43)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
filly 1 2008-7-2 13:15
24
0
偶也是这么想的!
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11220210 2008-7-2 13:26
25
0
这样代码很好,谢谢分享
游客
登录 | 注册 方可回帖
返回