首页
社区
课程
招聘
[原创]游戏修改的常用方法之二——Dll注入(asm源码详注)
发表于: 2008-6-15 20:29 21573

[原创]游戏修改的常用方法之二——Dll注入(asm源码详注)

2008-6-15 20:29
21573

本程序在WinXP/SP2、ra2之1.006英文版(有中国超牛机器人的那个)运行通过且稳定无误。
;------------------------------------------------------------------------
;文件一:Ra2MDF.Asm   启动游戏 和 DLL注入
;------------------------------------------------------------------------

.586
.model flat, stdcall
option casemap :none

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

.code

;------------------------------------------------------------------------
;把本程序拷入游戏文件夹,运行本程序,游戏被启动,本程序在后台运行,无界面
;本程序运行后启动“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,15000 ;等10秒,游戏程序应该启动了吧?
    invoke TerminateProcess,PI.hProcess,0h ;没用了,停掉它
    invoke CloseHandle,PI.hProcess ;释放内存

    ret
GAME_NAME:
    db "ra2.exe",0

_StartGame endp

start:
;------------------------------------------------------------------------
;
;
;------------------------------------------------------------------------
Main proc uses edi esi
    LOCAL @dwProcessID,@lpLoadLibrary,@lpDllName,@szDllFullPath[MAX_PATH]

    invoke _StartGame    ;启动游戏

    lea edi,@szDllFullPath
    invoke    GetCurrentDirectory,MAX_PATH,edi
    call @F
    db '\Ra2MD.dll',0
@@:    push edi
    call lstrcat    ;获取dll的全路径文件名
    call @F
    db 'Kernel32.dll',0
@@:    call GetModuleHandle
    call @F
    db 'LoadLibraryA',0
@@:    push eax
    call GetProcAddress    ;获取LoadLibrary函数地址
    mov  @lpLoadLibrary,eax

    invoke    GetForegroundWindow    ;取得游戏窗口的句柄
    lea    esi,@dwProcessID
    invoke    GetWindowThreadProcessId,eax,esi    ;获取进程ID
    invoke    OpenProcess,PROCESS_CREATE_THREAD or PROCESS_VM_OPERATION or \
        PROCESS_VM_WRITE,FALSE,@dwProcessID    ;打开进程
    .if    eax
        mov    esi,eax
;********************************************************************
; 在进程中分配空间并将DLL文件名拷贝过去,然后创建一个LoadLibrary线程
;********************************************************************
        invoke    VirtualAllocEx,eax,NULL,MAX_PATH,MEM_COMMIT,PAGE_READWRITE;
        .if    eax
            mov    @lpDllName,eax
            invoke    WriteProcessMemory,esi,eax,edi,MAX_PATH,NULL
            invoke    CreateRemoteThread,esi,NULL,0h,@lpLoadLibrary,@lpDllName,0h,NULL    ;完成DLL注入
            invoke    CloseHandle,eax
        .endif
        invoke    CloseHandle,esi
    .endif
    invoke    ExitProcess,NULL
Main endp

end    start

;-------------------------------------------------------------------------------------
;文件二:Ra2DLL.Asm  用来被嵌入到其它进程执行的dll,完成热键、搜索、修改等全部功能
;作为游戏进程的一部分,可以直接访问游戏内存,不用ReadProcessMemory和WriteProcessMemory
;这样搜索速度相对要快一些。本例中所说的游戏版本与前面的相同。
;-------------------------------------------------------------------------------------

.586
.model flat, stdcall
option casemap :none

include        windows.inc
include    user32.inc
includelib    user32.lib
include        kernel32.inc
includelib    kernel32.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

_GetDataAddr proc uses esi edi num,hmem
    LOCAL N,ListMemSize,pListMem,ReadSize
    LOCAL mbi:MEMORY_BASIC_INFORMATION

    invoke GlobalLock,hmem ;锁定保存搜索结果的内存
    mov  pListMem,eax
    invoke GlobalSize,hmem;保存搜索结果的内存大小
    mov    ListMemSize,eax

    mov edi,ADD_DATA1 ;保存金钱数,以便后面比较搜索,原理见前文
    mov edi,[edi]
    mov ecx,ADD_DATA3_START ;设置要搜索的内存地址范围开始处
    .repeat ;循环搜索游戏内存
@@:        invoke VirtualQuery,ecx,addr mbi,sizeof MEMORY_BASIC_INFORMATION ;返回页面虚拟信息
        .if mbi.State == MEM_COMMIT && mbi.Protect == PAGE_READWRITE ;已提交且为可读写的区域,加速搜索
            invoke IsBadWritePtr,mbi.BaseAddress,mbi.RegionSize
            .if !eax
                mov esi,mbi.BaseAddress
                xor 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,esi
                        add  edx,ecx ;首地址+偏移地址=实际地址
                        mov  [eax-4h],edx ;搜索的结果保存起来,pListMem[num-1]=实际地址
                    .endif
                    add ecx,4h ;金钱数为DWORD型数值,考虑到内存对齐,这里是不用担心漏掉的
                .until    ecx>=mbi.RegionSize
            .endif
        .endif
        mov  ecx,mbi.BaseAddress
        add  ecx,mbi.RegionSize ;下一区段首地址
    .until ecx>=ADD_DATA3_END ;下一区段在搜索范围之外了吗?是则完成第一遍搜索

    ret

_GetDataAddr endp

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

    invoke GlobalLock,hmem
    mov    edi,eax ;前次搜索结果保存的内存首地址
    xor    esi,esi ;指针,指向第一个结果
    mov    N,esi ;本次搜索到的个数初始化为0
    .repeat ;逐个比较
        mov edx,[edi+esi*4h] ;相当于edx=hmem[esi]
        mov eax,ADD_DATA1
        mov eax,[eax]
        .if eax==[edx] ;等于金钱数吗?等则记录下来
            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 hmem
   
    mov    esi,5h ;要锁定的金钱数,别太多,多了是会招贼来偷的:)
    invoke GlobalLock,hmem
    mov    edi,[eax] ;游戏中真正的保存金钱数的地址
    .repeat
        .if esi>=5h ;每五循环锁定一次
            xor    esi,esi ;循环次数清0
            mov    dword ptr[edi],2000
        .endif   
        invoke Sleep,1000 ;定时一秒
        inc    esi ;循环次数加一
        invoke GetAsyncKeyState,VK_SUBTRACT ;按了“-”键吗?
    .until eax;如果按了“-”键则结束循环
   
    ret
   
_WriteProcessData endp

;------------------------------
;新线程,检查按键,响应用户请求
;------------------------------
ThreadProc Proc    pParam:LPVOID
    Local    @dwNum,@hMem

    invoke GlobalAlloc,GMEM_MOVEABLE,1000h ;预留内存空间,搜索时用来保存结果
    mov @hMem,eax ;搜索结果个数的不确定性需要我们用GlobalReAlloc来重新分配内存大小
    mov @dwNum,0h ;搜索结果个数初始化为0

    .while TRUE
        invoke GetAsyncKeyState,VK_SUBTRACT ;按了“-”键吗?
        .if eax
            mov @dwNum,0h ;搜索结果个数置0,表示从未搜索过
            invoke SetCursorPos,9h,9h ;把鼠标置于屏幕左上角,提醒游戏者,第一遍搜索时特别有用
        .endif

        invoke GetAsyncKeyState,VK_MULTIPLY ;按了“*”键吗?
        .if eax ;按了热键“*”
            .if @dwNum==0h ;搜索结果个数为0则从未搜索过,进行第一遍搜索
                invoke _GetDataAddr,addr @dwNum,@hMem
            .elseif @dwNum>1h ;搜索结果个数非0则至少已搜过一遍且未找到正确地址
                invoke _FindAddrInList,addr @dwNum,@hMem  ;再搜它一遍或几遍
            .endif
        
            .if @dwNum==1h ;搜索结果为1,说明找到正确地址了
                invoke _WriteProcessData,@hMem ;去锁定它!
            .endif               
            invoke MessageBeep,MB_OK ;发声提示游戏者,按键收到,该做的本程序都做过了
            invoke SetCursorPos,9h,9h ;把鼠标置于屏幕左上角,提醒游戏者,第一遍搜索时特别有用
        .endif
        invoke Sleep,1000
    .endw
    invoke GlobalUnlock,@hMem
    invoke GlobalFree,@hMem
    ret

ThreadProc endP

DllEntry Proc    _hInstance,_dwReason,_dwReserved
    LOCAL @dwThreadID

    .if    _dwReason == DLL_PROCESS_ATTACH
        invoke    CreateThread,NULL,0h,offset ThreadProc,NULL,NULL,addr @dwThreadID ;创建一个新线程
        invoke    CloseHandle,eax
    .endif
    mov    eax,TRUE
    ret

DllEntry Endp

End DllEntry


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (30)
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
2
代码写得很漂亮,学习
2008-6-15 20:58
0
雪    币: 261
活跃值: (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
3
代码写得相当的好,加精了也是理所当然的!
2008-6-15 21:53
0
雪    币: 222
活跃值: (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
好文,学习了
2008-6-15 22:28
0
雪    币: 375
活跃值: (12)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
5
写得不错

但太常规了。

应该加点猥琐的东西上去。

毕竟,如果网游,人家会做保护的。
2008-6-15 23:12
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
代码写得很漂亮
2008-6-16 09:53
0
雪    币: 463
活跃值: (111)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
ra2 的,只支持本机吧,连网应不行的
2008-6-18 04:44
0
雪    币: 185
活跃值: (467)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
[QUOTE=;]...[/QUOTE]
干嘛非得整出个xx外挂呢  ^_^ 楼主只是在分析原理而已
2008-6-18 09:15
0
雪    币: 219
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
代码写得很漂亮,膜拜!!!
2008-6-18 10:07
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
来这里学习学习了
2008-6-19 14:41
0
雪    币: 222
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
虽然看不懂,但有注释很好,收藏.
2008-6-20 14:10
0
雪    币: 222
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
发现楼主一天之内N个精,拍马也追不上,反方向走了,说不定将来的某个时间能在另一端遇上你.
2008-6-20 14:15
0
雪    币: 716
活跃值: (162)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
13
call @F
db '\Ra2MD.dll',0
@@: push edi
call lstrcat ;获取dll的全路径文件名
call @F
db 'Kernel32.dll',0
@@: call GetModuleHandle
call @F
db 'LoadLibraryA',0
@@: push eax
call GetProcAddress ;获取LoadLibrary函数地址
mov @lpLoadLibrary,eax

还真巧妙,学习了。
2008-6-20 17:07
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
好东西啊
学习了
2008-6-20 17:18
0
雪    币: 192
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
MARK下,学习
2008-6-20 22:31
0
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
好东西啊
学习了
2008-6-30 08:32
0
雪    币: 563
活跃值: (95)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
厉害啊
2008-6-30 17:50
0
雪    币: 107
活跃值: (404)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
啊...这样也可以加个精华???早知道我来写了..555555555
2008-7-2 15:18
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
就当看美女了  代码非常养眼  是个好习惯  学习  膜拜 偶像 方向
2008-7-3 02:55
0
雪    币: 10
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
喜欢注释,看上去好懂,谢了,收藏!~
2008-7-4 17:24
0
雪    币: 267
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
写的很好,顶一下学习了.
2008-7-5 23:35
0
雪    币: 111
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jjh
22
学习,谢谢分享。。。
2008-7-7 09:48
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
看着很有意思,感谢这么用心的楼主
2008-7-7 11:20
0
雪    币: 7
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
留名,要好好学习
2008-7-26 01:31
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
我也学习一下,注释很全,非常不错
2008-10-8 04:30
0
游客
登录 | 注册 方可回帖
返回
//