随笔 - 298  文章 - 377  trackbacks - 0
<2007年7月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

常用链接

留言簿(34)

随笔分类

随笔档案

文章档案

相册

收藏夹

搜索

  •  

最新评论

阅读排行榜

评论排行榜

       网上看了N多的文章,对内存中找怪极少有详细介绍,大多数人搞定人物内存中的有关参数后,止步于内存中的找怪。人物只有一个,而怪有各种各样的,数量又同时出现多个,比在内存中找人物坐标难度要大得多。
      下面我将尽可能详细的讲讲内存中找怪之代码注入篇,抛砖引玉,望高人指点。这里的代码注入是直接把代码注入到游戏文件中,学个破解的人都知道,哪怕游戏原文件加了壳,在游戏原文件中加入自己的代码也是完全可以的。
      由于本人水平有限,有的地方可能表达不清,请耐心慢慢看。有的地方采取的方法也许对高手来说好低劣,见笑了。
一、把周围的怪物名称起始地址集中写到内存中一固定区域。游戏中,玩家周围有许多怪物,所有怪物的名称、坐标、血量等参数不可能会固定在某一内存位置,但对于每一个怪物而言,它的名称、坐标、血量等在内存中的地址之间有着相对固定的差值,只要知道怪物的名称地址就能知道这个怪物的坐标、血量等地址。因此,只要把周围每个怪物的名称地址固定在内存中一定区域,就可知道这些怪物的其他参数。
      1、先把游戏中我们需要打的怪物名称(不是所有怪物,因为有的怪物不爆东西等不值得打)固定放到内存地址为004d2a60起的一块区域,制做一张需要打的怪物名称列表。每个怪名称占12字节,不够12字节的后面用00填充。
    内存地址:004d2a60是怎样来的呢?
    我们用PEditor打开游戏原文件,可以看到PE文件分了好多块,有的块是可以改写的(属性为E0000020或C0000040的可以改写),块里并不全
部写满了数据,还有大块连续为00的空闲区域。用UltraEdit等软件打开游戏文件,看到文件物理地址为000d2a60起有一大块为00的空闲区域。映射到内存中就是地址为004d2a60起一块为00的空闲区域,我们先把怪物名称写到这块地方。
    具体操做是用UltraEdit打开游戏文件,修改文件物理地址为000d2a60起的数据。原文件中这里全部为00,我们把下面数据填进去(部分怪物名称列表)。
000D2A60  B0 EB CA DE D5 BD CA BF 00 00 00 00 B0 EB CA DE  半兽战士....半兽
000D2A70  D3 C2 CA BF 00 00 00 00 BB A2 C9 DF 00 00 00 00  勇士....虎蛇....
000D2A80  00 00 00 00 B6 BE D6 A9 D6 EB 00 00 00 00 00 00  ....毒蜘蛛......
000D2A90  C9 AD C1 D6 D1 A9 C8 CB 00 00 00 00 CD FE CB BC  森林雪人....威思
000D2AA0  B6 F8 D0 A1 B3 E6 00 00 B6 E0 BD C7 B3 E6 00 00  而小虫..多角虫..
000D2AB0  00 00 00 00 BF F8 BC D7 B3 E6 00 00 00 00 00 00  ....盔甲虫......
000D2AC0  B8 AF CA B4 C8 CB B9 ED 00 00 00 00 C0 CB D7 D3  腐蚀人鬼....浪子
000D2AD0  C8 CB B9 ED 00 00 00 00 C0 D7 B5 E7 BD A9 CA AC  人鬼....雷电僵尸
000D2AE0  00 00 00 00 BD A9 CA AC 00 00 00 00 00 00 00 00  ....僵尸........
000D2AF0  C9 AE C2 C2 BD A9 CA AC 00 00 00 00 B6 B4 C7 F9  僧侣僵尸....洞蛆
000D2B00  00 00 00 00 00 00 00 00 F7 BC F7 C3 BE AB C1 E9  ........骷髅精灵
000D2B10  00 00 00 00 CA AC CD F5 00 00 00 00 00 00 00 00  ....尸王........

这样,当游戏运行时,从内存地址004d2a60起的一段区域有了我们需要打的怪物列表。

     2、再把玩家周围实际刷的怪物名称的起始地址(注意是名称的起始地址)固定在内存为004D3000起的长为100(16进制)的地方。
内存地址:004D3000是游戏运行时我们查到的空闲内存地址,我们利用它们来放周围怪物名称的起始地址,每个怪物名称的起始地址占4个字
节,长为100(16进制)可以放64个怪物,玩家周围不会超过64个怪物吧,长为100应足够了。
   
     3、怎么写入这些怪物名称的起始地址呢?
     首先应知道,游戏从哪里把怪物名称写入内存中。
      Cheat Engine会用吧?具体怎么找,简单的说一下:
     先运行游戏再运行Cheat Engine,CE中选择游戏程序,查看内存,搜索内存玩家周围的一个怪物名称。找到后记下怪物名称的起始内存地址,
手动添加地址,把找到的地址添加到列表,类型为文本。从列表中选刚才添加的地址,右键选“寻找什么写入这个地址”。回到游戏中移动玩家,观察刚添加的内存地址的数值变化。变为00时为该怪物从玩家视眼中消失。再移动玩家到看见这怪物,如果运气好,怪物名会再次写入这个内存地址。这样在“以下处理将改变运算码”的窗口中会有汇编代码出现。
     如我的游戏:
      能看到00409872   mov   byte ptr [edi+ecx], dl     这句。
      游戏是从这句把怪物名写入内存中的。

用OllyICE打开游戏。跳到以下这段代码可以看到:
00409870   > /8A11          mov     dl, byte ptr [ecx]        //从这里开始写怪物名称
00409872   . |88140F        mov     byte ptr [edi+ecx], dl  //名称从[edi+ecx]开始
00409875   . |41            inc     ecx
00409876   . |84D2          test    dl, dl
00409878   .^\75 F6         jnz     short 00409870      
0040987A   .  50            push    eax                             //写完了运行到这里。
0040987B   ?  68 4C774C00   push    004C774C
00409880   .  56            push    esi
00409881   .  E8 02F10A00   call    004B8988

     游戏每出现一个怪都会把怪物名称写入[edi+ecx]开始的内存中,显然内存地址[edi+ecx]不是一个固定的值。我们要把这个值复制一份固定到内存中地址为004D3000开始的一块区域。
      这样,周围的怪物名称的起始地址就固定在004D3000、004d3004、004D3008等等内存中.知道怪物名称的起始地址就能算出怪物的坐标、血量等
地址。
      具体怎么做呢?
     我们看到游戏运行到0040987A . 50  push   eax 这行时,怪物名称已经写到内存中,我们从这行开始写入我们的代码,原文件中紧接这行的
下面肯定没有多余的空间来写我们的代码,这就要求改写这行代码,跳到空闲的地方把我们的代码加进去,运行完我们的代码后再跳回来。
     因此把原文件改为如下(对照上面):
00409870   > /8A11          mov     dl, byte ptr [ecx]
00409872   . |88140F        mov     byte ptr [edi+ecx], dl
00409875   . |41            inc     ecx
00409876   . |84D2          test    dl, dl
00409878   .^\75 F6         jnz     short 00409870
0040987A   .- E9 FB690C00   jmp     004D027A    //这里改为跳到004d027a
0040987F      90            nop
00409880   .  56            push    esi
00409881   .  E8 02F10A00   call    004B8988

就是把这行0040987A . 50  push   eax改为:
0040987A   .- E9 FB690C00   jmp  004D027A

从004d027A开始写我们的代码,原文件中004D027A也是一块为00的空闲区域。
下面是我们添加进去的代码,原文件中是为00的空闲区域。

004D027A    50              push    eax     //先把一些用到的寄存器数据入栈,保护现场。
004D027B    53              push    ebx
004D027C    51              push    ecx
004D027D    52              push    edx
004D027E    31D2            xor     edx, edx
004D0280    81C7 A4F61200   add     edi, 12F6A4   //此处就是edi+ecx,ecx为常数12F6A4
004D0286    833F 00         cmp     dword ptr [edi], 0  //怪名是否为空
004D0289    74 63           je      short 004D02EE       //为空不是怪名,直接跳回去不写入
004D028B    33C9            xor     ecx, ecx
004D028D    8B0439          mov     eax, dword ptr [ecx+edi]     //怪名前4个字节放入eax中
004D0290    8B9C11 602A4D00 mov     ebx, dword ptr [ecx+edx+4D2A60] //需要打的怪名前4个字节

004D0297    83C1 04         add     ecx, 4
004D029A    3BC3            cmp     eax, ebx         //比较刷的怪名与需要打的怪名前4个字节
004D029C    75 07           jnz     short 004D02A5   //不相等,跳到与下一个需要打的怪名
004D029E    83F9 0C         cmp     ecx, 0C          //因为怪名长占12个字节,所以要比较三次
004D02A1  ^ 7C EA         jl      short 004D028D   //没比较完返回继续比较
004D02A3    EB 0D          jmp   short 004D02B2  //刷的怪名从需要打的怪名列表中找到了,跳到开始写入.  004D02A5    83C2 0C         add     edx, 0C          //下一个需要打的怪名
004D02A8    81FA 08010000   cmp     edx, 108     //是否到了需要打的怪名列表尽头
004D02AE  ^ 7C DB           jl      short 004D028B   //没到继续比较
004D02B0    EB 3C           jmp     short 004D02EE   //到了尽头刷的这个怪不是需要打的,跳回不写入
004D02B2    33C9            xor     ecx, ecx               //从这开始写入
004D02B4    8B81 00304D00   mov     eax, dword ptr [ecx+4D3000]   //把想写入的内存地址放入eax
004D02BA    83C1 04         add     ecx, 4
004D02BD    81F9 00010000   cmp     ecx, 100       //是否写满,能写64个怪,周围一般同时没有这么多
004D02C3    7F 12           jg      short 004D02D7    //写满了清空这块区域.
004D02C5    83F8 00         cmp     eax, 0               //比较内存地址[ecx+4D3000],是否写了其他怪
004D02C8  ^ 75 EA           jnz     short 004D02B4  //写了,找下一个内存地址
004D02CA    89B9 FC2F4D00   mov     dword ptr [ecx+4D2FFC], edi  //把怪名的起始内存地址写入
004D02D0    E8 BB000000     call    004D0390        //写入一怪后,重新找最近怪,后面详细讲这个call
004D02D5    EB 17           jmp     short 004D02EE  //整理后返回
004D02D7    33C9            xor     ecx, ecx
004D02D9    33C0            xor     eax, eax
004D02DB    8981 00304D00   mov     dword ptr [ecx+4D3000], eax
004D02E1    83C1 04         add     ecx, 4
004D02E4    81F9 00010000   cmp     ecx, 100
004D02EA  ^ 7C EF           jl      short 004D02DB
004D02EC  ^ EB C4           jmp     short 004D02B2
004D02EE    5A              pop     edx           //各寄存器出栈,恢复现场
004D02EF    59              pop     ecx
004D02F0    5B              pop     ebx
004D02F1    58              pop     eax
004D02F2    50              push    eax                    //把原文件的代码补上
004D02F3    68 4C774C00     push    004C774C    //这也是原文件的代码补上
004D02F8  - E9 8395F3FF     jmp     00409880      //跳回到原文件插入跳转指令的下一行

通过以上代码,我们把玩家周围出现的怪,而且是我们需要打的怪名的起始内存地址放到了以内存地址004D3000开始的一段区域内。


4、修改游戏原始文件,下次启动游戏时能运行我们的代码:
    用UltraEdit打开原文件,把原文件地址为0000987A起数据:
    50684C774C00 改为:E9FB690C0090
把下面数据复制到000d027A起的文件里.
50 53 51 52 31 D2 81 C7 A4 F6 12 00 83 3F 00 74  
63 33 C9 8B 04 39 8B 9C 11 60 2A 4D 00 83 C1 04  
3B C3 75 07 83 F9 0C 7C EA EB 0D 83 C2 0C 81 FA  
08 01 00 00 7C DB EB 3C 33 C9 8B 81 00 30 4D 00  
83 C1 04 81 F9 00 01 00 00 7F 12 83 F8 00 75 EA  
89 B9 FC 2F 4D 00 E8 BB 00 00 00 EB 17 33 C9 33  
C0 89 81 00 30 4D 00 83 C1 04 81 F9 00 01 00 00  
7C EF EB C4 5A 59 5B 58 50 68 4C 77 4C 00 E9 83  
95 F3 FF                      
上面的这些16进制数据就是我们上面加入的代码,修改好后存盘。
至此,游戏运行时,不需要其他工具,游戏本身就会把怪名的起始内存地址固定到了以内存地址004D3000开始的一段区域内。


二、怪物消失时,怪名的起始内存地址从004D3000开始的一段区域内清除
      上面只是把怪名的起始地址写到以内存地址004D3000开始的一段区域内。当怪物从玩家视眼中消失时,我们必需把怪名的起始内存地址从004D3000开始的一段区域内清除掉。
      1、怎么把我们先前写入的怪名起始地址清除掉呢?
       先看看游戏本身当怪物消失时从哪里把怪物名称从内存中清除掉。
       同样使用Cheat Engine,当怪物消失时会有这么一句:
       0040F5A8  mov   ecx, 30
      游戏运行到这句时,怪名的内存地址清空为00,寄存器edi正好是怪名的起始内存地址。
       用OllyICE打开游戏。跳到以下这段代码可以看到:
0040F5A2   .  8DBD 48270600 lea     edi, dword ptr [ebp+62748]  //这里开始清空
0040F5A8      B9 30000000   mov     ecx, 30   
0040F5AD   .  F3:AB         rep     stos dword ptr es:[edi]
因此把原文件改为如下(对照上面):
0040F5A2   .  8DBD 48270600 lea     edi, dword ptr [ebp+62748]
0040F5A8    - E9 540D0C00   jmp     004D0301  //修改这里,跳到004d0301
0040F5AD   .  F3:AB         rep     stos dword ptr es:[edi]

从004d0301开始写我们的代码,原文件中004d0301也是一块为00的空闲区域。

004D0301    50              push    eax  //先把一些用到的寄存器数据入栈,保护现场。
004D0302    53              push    ebx
004D0303    51              push    ecx
004D0304    52              push    edx
004D0305    33C9            xor     ecx, ecx
004D0307    8B07            mov     eax, dword ptr [edi] //把[edi]内数据放入eax中
004D0309    83F8 00         cmp     eax, 0                  //比较是否为怪名
004D030C    74 22           je      short 004D0330       //为零,不是怪名,不清除跳到返回
004D030E    8B99 00304D00  mov  ebx, dword ptr [ecx+4D3000] //把已写入的怪名起始地址放入ebx004D0314    83C1 04         add     ecx, 4
004D0317    81F9 00010000   cmp     ecx, 100       //比较是否全部比较完
004D031D    7F 11           jg      short 004D0330 //没找到,说明以前没把这怪写入,也就不用清除
004D031F    3BDF            cmp     ebx, edi      //比较游戏刚消失的怪名地址,是否以前写入过这个地址
004D0321  ^ 75 EB           jnz     short 004D030E //不是这个再比较下一个
004D0323    33D2            xor     edx, edx   //刚消失的怪名地址在以前写入的区域内找到了,开始清除
004D0325    8991 FC2F4D00   mov     dword ptr [ecx+4D2FFC], edx  //以前写入的起始怪名地址清零
004D032B    E8 60000000     call    004D0390     //清除掉一怪后,重新找最近怪,后面详细讲这个call
004D0330    5A              pop     edx            //各寄存器出栈,恢复现场
004D0331    59              pop     ecx
004D0332    5B              pop     ebx
004D0333    58              pop     eax
004D0334    B9 30000000     mov     ecx, 30       //把原文件的代码补上
004D0339  - E9 6FF2F3FF     jmp     0040F5AD    //跳回到原文件插入跳转指令的下一行


通过以上代码,我们把玩家周围消失的怪,从内存地址004D3000开始的一段区域内清除掉。

    2、修改游戏原始文件,下次启动游戏时能运行我们的代码:
    用UltraEdit打开原文件,把原文件地址为0000F5A8起数据:
     B930000000 改为:E9540D0C00
     把下面数据复制到000d0301起的文件里.
50 53 51 52 33 C9 8B 07 83 F8 00 74 22 8B 99 00  
30 4D 00 83 C1 04 81 F9 00 01 00 00 7F 11 3B DF  
75 EB 33 D2 89 91 FC 2F 4D 00 E8 60 00 00 00 5A  
59 5B 58 B9 30 00 00 00 E9 6F F2 F3 FF 90        


数据怎么来的,上面已经说了。改后存盘。
至此,把玩家周围消失的怪,从内存地址004D3000开始的一段区域内清除掉了。

三、当怪物死亡时,怪名的起始内存地址从004D3000开始的一段区域内清除
      当怪物死亡时,游戏并不会立即把怪名内存地址清零,要等怪物尸体消失时才清零。我们必需立即把怪名的起始内存地址从004D3000开始的一段区域内清除掉。
     1、同样,我们用Cheat Engine找到怪物血量减少时的汇编代码:
      00419BBA  word ptr [esi+626F4], ax
      ax中存的就是怪物的血量。
用OllyICE打开游戏。跳到以下这段代码可以看到:
00419BBA   .  66:8986 F4260>mov     word ptr [esi+626F4], ax //esi+626f4为血量地址
00419BC1   .  66:8B4F 08    mov     cx, word ptr [edi+8]
00419BC5   .  66:898E C2010>mov     word ptr [esi+1C2], cx
00419BCC   .  E8 CFF10900   call    004B8DA0
因此把原文件改为如下(对照上面):
00419BBA   .  66:8986 F4260>mov     word ptr [esi+626F4], ax
00419BC1    - E9 CB680B00   jmp     004D0491   //修改这里,跳到004d0491
00419BC6      90            nop
00419BC7      90            nop
00419BC8      90            nop
00419BC9      90            nop
00419BCA      90            nop
00419BCB      90            nop
00419BCC   .  E8 CFF10900   call    004B8DA0

从004d0491开始写我们的代码,原文件中004d0491也是一块为00的空闲区域。
下面是我们添加进去的代码,原文件中是为00的空闲区域。


004D0491    50              push    eax  //先把一些用到的寄存器数据入栈,保护现场。
004D0492    53              push    ebx
004D0493    51              push    ecx
004D0494    52              push    edx
004D0495    56              push    esi
004D0496    33C9            xor     ecx, ecx
004D0498    90              nop
004D0499    90              nop
004D049A    66:83F8 00      cmp     ax, 0    //比较血量是否为0
004D049E    7F 28           jg      short 004D04C8   //大于0,不用清除,直接到返回
004D04A0    81C6 48270600   add     esi, 62748   //esi+62748为怪名的起始内存地址
004D04A6    8B99 00304D00  mov   ebx, dword ptr [ecx+4D3000] //把以前写入的怪名起始地址放入ebx
004D04AC    83C1 04         add     ecx, 4
004D04AF    81F9 00010000   cmp     ecx, 100
004D04B5    7F 11           jg      short 004D04C8
004D04B7    3BDE            cmp     ebx, esi   //比较游戏刚死亡的怪名地址,是否以前写入过这个地址
004D04B9  ^ 75 EB           jnz     short 004D04A6  //不是这个再比较下一个
004D04BB    33D2            xor     edx, edx  //刚死亡的怪名地址在以前写入的区域内找到了,开始清除
004D04BD    8991 FC2F4D00   mov     dword ptr [ecx+4D2FFC], edx //清零
004D04C3    E8 C8FEFFFF     call    004D0390 //清除掉一怪后,重新找最近怪,后面详细讲这个call
004D04C8    5E              pop     esi      //各寄存器出栈,恢复现场
004D04C9    5A              pop     edx
004D04CA    59              pop     ecx
004D04CB    5B              pop     ebx
004D04CC    58              pop     eax
004D04CD    66:8B4F 08      mov     cx, word ptr [edi+8]    //把原文件的代码补上
004D04D1    66:898E C201000>mov     word ptr [esi+1C2], cx  //这也是原文件的代码补上
004D04D8  - E9 EF96F4FF     jmp     00419BCC    //跳回到原文件插入跳转指令的下一行


通过以上代码,把玩家周围死亡的怪,立即从内存地址004D3000开始的一段区域内清除掉。


 2、修改游戏原始文件,下次启动游戏时能运行我们的代码:
       用UltraEdit打开原文件,把原文件地址为00019BC1起数据:
       668B4F0866898EC2010改为:E9CB680B00909090909090
       把下面数据复制到000d0491起的文件里.
50 53 51 52 56 33 C9 90 90 66 83 F8 00 7F 28 81  
C6 48 27 06 00 8B 99 00 30 4D 00 83 C1 04 81 F9  
00 01 00 00 7F 11 3B DE 75 EB 33 D2 89 91 FC 2F  
4D 00 E8 C8 FE FF FF 5E 5A 59 5B 58 66 8B 4F 08  
66 89 8E C2 01 00 00 E9 EF 96 F4 FF              
同样,数据由上面代码段而来,改好后存盘。
至些,把玩家周围死亡的怪,立即从内存地址004D3000开始的一段区域内清除掉。


四、把离玩家最近的怪物参数固定在内存中的某处
       通过以上三大步骤,我们已经实现了把玩家周围的,而且是需要打的活的怪物名称起始内存地址集中写入到了从004D3000到004D3100这小段区域内,在这小段区域内每组不为零的四个字节就代表了一个怪物名的起始内存地址,没有其他不相关的数据在这一小段。如果用按键精灵,已经可以通过读这一小段内存数据可以实现读内存打怪了。为了减少按键精灵读内存次数,和过多的转换运算,我们直接把最近怪的有关参数固定下来。

      这就到了前面多次提到的call  004D0390 这句。
      1、先分析游戏,用Cheat Engine容易找到:
内存中怪名起始内存地址与其他地址之间的关系:
以下为16进制数:
怪名起始内存地址-54=怪血量地址(占两个字节)
怪名起始内存地址-62570=怪X坐标地址(占四个字节)
怪名起始内存地址-625C0=怪y坐标地址(占四个字节)
人物参数内存地址(此游戏竟然是固定的):
人名称内存地址=008AFAF8
人血量内存地址=008AFAA4
人气量内存地址=008AFBD8
人X坐标内存地址=008AFEAC
人y坐标内存地址=008AFEB0
      2、人物坐标,怪物坐标与屏幕坐标的关系
游戏中人物在屏幕正中心,离上下左右各8步,超过8步在屏幕外,鼠标不能点击。游戏坐标差不能超过8。
窗口模式下鼠标能点到这个怪的极限区域(坐标为窗口坐标)。
正上顶点:(393,0)与(408,21)组成的区域。
正下顶点:(393,512)与(408,533)组成的区域。
正左顶点:(10,256)与(24,276)组成的区域。
正右顶点:(778,256)与(792,276)组成的区域。
假设人物坐标(Rx,Ry),怪坐标(Gx,Gy),鼠标点击坐标(X,Y)
其中人物坐标,怪坐标为游戏里的坐标,鼠标点击坐标为窗口坐标。
通过以上分析则有鼠标点击能点到怪的窗口坐标X,Y为:
X=(Gx-(Rx-8))*48+17 (后面加值范围10~24,最好取中间值+17)
y=(Gy-(Ry-8))*32+2   (后面加值范围0~21,因可能点到左下角头像选+2)


   3、有了以上理论就可以写call    004D0390的代码了
       同样从004D0390开始写我们的代码,原文件中004D0390也是一块为00的空闲区域。
       把最近怪物名的起始内存地址固定在[004D2FE0]中,X坐标固定在[004D2FF0]中,Y坐标固定在[004D2FF4]中,怪血量固定在[004D2FF8]中。

004D0390
    50              push    eax    //先把一些用到的寄存器数据入栈,保护现场。
004D0391    53              push    ebx
004D0392    51              push    ecx
004D0393    52              push    edx
004D0394    56              push    esi
004D0395    57              push    edi
004D0396    33C0            xor     eax, eax
004D0398    A3 E02F4D00     mov     dword ptr [4D2FE0], eax //初始化内存
004D039D    A3 F02F4D00     mov     dword ptr [4D2FF0], eax
004D03A2    A3 F42F4D00     mov     dword ptr [4D2FF4], eax
004D03A7    05 FFFF0000     add     eax, 0FFFF
004D03AC    A3 E42F4D00     mov     dword ptr [4D2FE4], eax
004D03B1    33C9            xor     ecx, ecx
004D03B3    8BB9 00304D00   mov     edi, dword ptr [ecx+4D3000] //把怪物起始内存地址放入edi
004D03B9    83C1 04         add     ecx, 4
004D03BC    81F9 00010000   cmp     ecx, 100
004D03C2    0F8F AA000000   jg      004D0472    //全部比较完后返回
004D03C8    83FF 00         cmp     edi, 0      
004D03CB  ^ 74 E6           je      short 004D03B3 //为零找下一个
004D03CD    8B87 90DAF9FF   mov     eax, dword ptr [edi+FFF9DA90] //怪Gx坐标放入eax
004D03D3    8B1D ACFE8A00   mov     ebx, dword ptr [8AFEAC]         //人Rx坐标放入ebx
004D03D9    3BC3            cmp     eax, ebx
004D03DB    7F 01           jg      short 004D03DE
004D03DD    93              xchg    eax, ebx
004D03DE    2BC3            sub     eax, ebx             //Gx与Rx之差放入eax
004D03E0    83F8 08         cmp     eax, 8              //两者差是否大于8
004D03E3  ^ 7F CE           jg      short 004D03B3      //大于8在屏幕外,直接找下一个怪
004D03E5    F7E0            mul     eax                  //横坐标差平方运算
004D03E7    8BF0            mov     esi, eax            //横坐标差的平方放入esi中
004D03E9    8B87 40DAF9FF   mov     eax, dword ptr [edi+FFF9DA40] //怪Gy坐标放入eax
004D03EF    8B1D B0FE8A00   mov     ebx, dword ptr [8AFEB0]           //人Ry坐标放入ebx
004D03F5    3BC3            cmp     eax, ebx
004D03F7    7F 01           jg      short 004D03FA
004D03F9    93              xchg    eax, ebx
004D03FA    2BC3            sub     eax, ebx            //Gy与Ry之差放入eax
004D03FC    83F8 08         cmp     eax, 8             //两者差是否大于8
004D03FF  ^ 7F B2           jg      short 004D03B3     //大于8在屏幕外,直接找下一个怪
004D0401    F7E0            mul     eax                //枞坐标差平方运算
004D0403    03F0            add     esi, eax           //横、枞坐标之差的平方的和放入esi中
004D0405    3B35 E42F4D00  cmp  esi, dword ptr [4D2FE4] //这个距离的平方与前次保存的距离的平方
004D040B  ^ 7D A6         jge   short 004D03B3   //大于或等于说明这个怪离玩家距离更远,找下一个怪
004D040D    893D E02F4D00   mov  dword ptr [4D2FE0], edi //更近怪名的起始地址固定在[4D2FE0]中
004D0413    8B47 AC         mov     eax, dword ptr [edi-54]
004D0416    66:A3 F82F4D00  mov     word ptr [4D2FF8], ax   //血量固定在[4D2FF8]中
004D041C    8935 E42F4D00   mov     dword ptr [4D2FE4], esi //距离平方放在[4D2FE4]中
004D0422    8B87 90DAF9FF   mov     eax, dword ptr [edi+FFF9DA90] //更近怪Gx坐标放入eax
004D0428    8B1D ACFE8A00   mov     ebx, dword ptr [8AFEAC]         //人Rx坐标放入ebx
004D0431    2BC3            sub     eax, ebx
004D0433    BB 30000000     mov     ebx, 30
004D0438    F7E3            mul     ebx
004D043A    83C0 17         add     eax, 17
004D043D    A3 F02F4D00  mov  dword ptr [4D2FF0], eax //换算后,鼠标点击X坐标放入[4D2FF0]中
004D0442    8B87 40DAF9FF   mov     eax, dword ptr [edi+FFF9DA40]
004D0448    8B1D B0FE8A00   mov     ebx, dword ptr [8AFEB0]
004D044E    83C0 08         add     eax, 8
004D0451    2BC3            sub     eax, ebx
004D0453    BB 20000000     mov     ebx, 20
004D0458    F7E3            mul     ebx
004D045A    83C0 02         add     eax, 2
004D045D    A3 F42F4D00   mov dword ptr [4D2FF4], eax  //换算后,鼠标点击Y坐标放入[4D2FF4]中
004D0462    83FE 02         cmp     esi, 2                //距离平方是否小于或等于2
004D0465    7E 0B           jle     short 004D0472     //怪已经紧挨着玩家了,不用再找下一个怪了
004D0467  ^ E9 47FFFFFF     jmp     004D03B3      //这个怪离玩家还有距离,可能还有更近的
004D046C    90              nop
004D046D    90              nop
004D046E    90              nop
004D046F    90              nop
004D0470    90              nop
004D0471    90              nop
004D0472    5F              pop     edi    //各寄存器出栈,恢复现场
004D0473    5E              pop     esi
004D0474    5A              pop     edx
004D0475    59              pop     ecx
004D0476    5B              pop     ebx
004D0477    58              pop     eax
004D0478    C3              retn             //返回调用call的下一行


游戏运行过程中,只要运行以上这段代码,就会把最近怪的窗口X坐标固定在[004D2FF0]中,窗口Y坐标固定在[004D2FF4]中,血量固定在[004D2FF8]中。

     4、用UltraEdit打开原文件,把下面数据复制到000d0390起的文件里.
50 53 51 52 56 57 33 C0 A3 E0 2F 4D 00 A3 F0 2F  
4D 00 A3 F4 2F 4D 00 05 FF FF 00 00 A3 E4 2F 4D  
00 33 C9 8B B9 00 30 4D 00 83 C1 04 81 F9 00 01  
00 00 0F 8F AA 00 00 00 83 FF 00 74 E6 8B 87 90  
DA F9 FF 8B 1D AC FE 8A 00 3B C3 7F 01 93 2B C3  
83 F8 08 7F CE F7 E0 8B F0 8B 87 40 DA F9 FF 8B  
1D B0 FE 8A 00 3B C3 7F 01 93 2B C3 83 F8 08 7F  
B2 F7 E0 03 F0 3B 35 E4 2F 4D 00 7D A6 89 3D E0  
2F 4D 00 8B 47 AC 66 A3 F8 2F 4D 00 89 35 E4 2F  
4D 00 8B 87 90 DA F9 FF 8B 1D AC FE 8A 00 83 C0  
08 2B C3 BB 30 00 00 00 F7 E3 83 C0 17 A3 F0 2F  
4D 00 8B 87 40 DA F9 FF 8B 1D B0 FE 8A 00 83 C0  
08 2B C3 BB 20 00 00 00 F7 E3 83 C0 02 A3 F4 2F  
4D 00 83 FE 02 7E 0B E9 47 FF FF FF 90 90 90 90  
90 90 5F 5E 5A 59 5B 58 C3                       


改好后存盘,这样运行游戏时,游戏本身会把最近怪的窗口X坐标固定在[004D2FF0]中,窗口Y坐标固定在[004D2FF4]中,血量固定在[004D2FF8]中。

五、人物移动时,及时更新最近怪物参数
      上面三个大步骤,刷出一个怪,消失一个怪,死亡一个怪时,都会运行call  004D0390,也就是都会马上更新一下内存地址004D2FE0、004D2FF0、04D2FF4、004D2FF8的数据。人物移动时也必需及时更新最近怪物参数。
      1、同样,我们用Cheat Engine找到人物移动时的汇编代码:
       人物每移动一步都会经过以下代码:
        00432A78   pop     edi
       用OllyICE打开游戏。跳到以下这段代码可以看到:
00432A78  |.  5F            pop     edi
00432A79  |.  5E            pop     esi
00432A7A  |.  5B            pop     ebx
00432A7B  |.  8BE5          mov     esp, ebp
00432A7D  |.  5D            pop     ebp
00432A7E  |.  C2 0400       retn    4
因此把原文件改为如下(对照上面):
00432A78    - E9 D4D80900   jmp     004D0351 //这里改为跳到004d0351
00432A7D  |.  5D            pop     ebp
00432A7E  |.  C2 0400       retn    4

从004D0351开始写我们的代码,原文件中004D0351也是一块为00的空闲区域。
下面是我们添加进去的代码,原文件中是为00的空闲区域。

004D0351    E8 3A000000     call    004D0390 //就是为了补进这句
004D0356    5F              pop     edi                 //把原文件的代码补上
004D0357    5E              pop     esi
004D0358    5B              pop     ebx
004D0359    8BE5            mov     esp, ebp
004D035B  - E9 1D27F6FF     jmp     00432A7D //跳回到原文件插入跳转指令的下一行


       2、修改游戏原始文件,下次启动游戏时能运行我们的代码:
        用UltraEdit打开原文件,把原文件地址为00032a78起数据:
        5F5E5B8BE5 改为:E9D4D80900
        把下面数据复制到000d0351起的文件里.
E8 3A 00 00 00 5F 5E 5B 8B E5 E9 1D 27 F6 FF


      到此为止,我们终于把最近怪物的有用参数固定在004D2FE0、004D2FF0、04D2FF4、004D2FF8四个内存地址中,当然如果还想加入最近怪物的其他参数,如怪物是否被其他玩家攻击,是否为小BOSS等等只要修改CALL 004D0390这段代码就行。
     把以上所有要改的数据改好存盘后,游戏原文件大小没有改变,但已经成功的把我们的代码注入进去了,启动游戏。查看游戏内存数据,可以看到内存004D2FE0、004D2FF0、04D2FF4、004D2FF8,正是我们需要的离玩家最近怪物的名称起址、窗口X坐标、窗口Y坐标、血量。随着游戏的进行,这几个内存地址都会及时正确的更新着。




六、写个简单的内存找怪打怪脚本
前期工作做好后,编写内存找怪打怪按键精灵脚本就简单多了。
下面是简单的脚本:
Dim handle,Rx,Ry,Gx,Gy,G1,G2,X,Y     //其中G1,G2为怪名的起始内存地址
Plugin handle=Window.MousePoint()
Rem 找怪
Plugin G1=Memory.Read32Bit(handle,&h004d2fe0)
Plugin X=Memory.Read32Bit(handle,&h004d2ff0)
Plugin Y=Memory.Read32Bit(handle,&h004d2ff4)
Plugin BGKM4.MMove(handle,X,Y)
Delay 100
Plugin BGKM4.LClick(handle,X,Y)
Rem 判断
Plugin G2=Memory.Read32Bit(handle,&h004d2fe0)
If G2=G1
Delay 100
Goto 判断
EndIf
Delay 100
Goto 找怪

连怪血量的内存地址都不需要用上,因为怪名的起始地址也是及时更新的。

七、其他
      以上脚本只是简单的砍怪,没有加入使用技能,没有加入补血补气,也没有加入捡取物品。游戏中遇到特殊情况也没做相应处理。
      关于使用技能,如可以通过统计紧挨玩家怪物的数量使用群攻技能。
      知道人物的血、气内存地址,补血补气那是很容易实现的了。
      关于游戏地面物品内存中的查找,原理其实同找怪一样,也可预先做一个需要捡取的物品清单,然后,把地面出现的物品与清单相比较,如果是要捡的,可以临时把坐标放入一固定内存区域内,跟据判定物品离玩家距离的远近和周围怪物的多少,决定恰当的时候去捡起。
     游戏中还有些关键的元素,如打怪路线,捡到极品自动存仓库,自动买药买蓝都可以通过读内存实现。
    不说了,再说又是长篇大论。
posted on 2007-07-21 01:39 聂文龙 阅读(7632) 评论(5)  编辑 收藏 引用 所属分类: c++

FeedBack:
# re: 内存中找怪物之代码注入篇[未登录] 2008-02-01 14:47 sea
及时雨呀,顶顶顶  回复  更多评论
  
# re: 内存中找怪物之代码注入篇 2008-06-11 06:52 
大哥 能告诉一下怪物的内存地址怎么找么?  回复  更多评论
  
# re: 内存中找怪物之代码注入篇 2008-06-11 06:59 
我想做个怪物提示的功能~~想知道怪物的内存地址怎么找 您能告诉我么?  回复  更多评论
  
# re: 内存中找怪物之代码注入篇 2008-08-08 09:36 小弟
大哥,看了你的文章,知道你很了得,,我有个想法,不知道你能否和我谈谈,,我的电话是13961465149,,,有个项目和你合作一下,,,是合作,希望能听到你的回音。谢谢!  回复  更多评论
  
# re: 内存中找怪物之代码注入篇 2009-07-01 12:28 风格
# re: 内存中找怪物之代码注入篇 2008-08-08 09:36 小弟
大哥,看了你的文章,知道你很了得,,我有个想法,不知道你能否和我谈谈,,我的电


删除好吗,把我手机删除好吗  回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理