读取魔兽世界怀旧服内存的研究

2020年03月29日 3101点热度 12人点赞 4条评论

警告

修改游戏内存、使用脚本和自动化程序有封号风险

最近由于魔兽世界怀旧服排队太严重,天天2小时以上的排队,实在让人崩溃。因此想做一个自动跳跳跳以及掉线自动重登的脚本。进而对读取魔兽世界内存数据有一点小小的研究。

游戏版本基于:wowclassic.exe 1.13.4.33728

计划实现

1、游戏中,实现后台自动跳跳跳。还可以进一步做多个动作,必须搓绷带,随机走几步,喊喊话之类的。防止服务端大数据检测。
2、检测到掉线后,自动将魔兽窗口激活到前台,并自动重登。
3、检测到重登成功,回到步骤1。

难点在于

1、如何判断游戏掉线。由于是后台操作,且必须在30秒内完成重登操作,否则就加入排队大军。如果用找图取色方法,性能很低,且还无法使用。因此最佳方式只有高效率的内存读取。反正已经用了自动脚本,读内存还是找图模拟,该封都是封,没什么区别。
2、已经测试按键精灵9/2014加载乐玩插件可以实现后台跳跳跳。但取色、找图、鼠标都无效。只有后台键盘正常。
3、按键精灵+大漠7.2002可以读取游戏基址。大漠读出来的是vbLongLong数据类型的10进制的基址,和vbLong数据类型的游戏指针地址相加,按键精灵会报错(程序做的真差)。用大漠的Int64ToInt32函数转换基址,转出来是个负数……因此按键精灵的路线彻底over。
4、由于魔兽世界是64位程序,因此32位的CE、按键精灵各版本、seraph、autoit、大漠插件、乐玩插件统统无效。只有64位的autoit可以成功读取魔兽世界内存。但64位的autoit又无法注册大漠/乐玩插件,只能使用autoit内置自带的功能和函数。并且autoit无法读取中文内存数据。所以最终的选择方案,就是使用32位autoit,或者64位autoit加上#AutoIt3Wrapper_UseX64=n命令,配合大漠7.2002,实现脚本的内存读取和全流程控制。不过因为之前没用过autoit,所以一时半会还写不出来完整的掉线重登脚本。

研究成果

经过国外论坛学习和几天的研究,目前成功读取到了游戏的内存数据,且可以用来作为是否掉线的判断。

下面先来张成果图:

CE里列表的内存地址,都可以用于是否掉线的判断。在游戏中,数据都有。掉线了数据都是0。当读蓝条的时候,loadingscreen=1。

CurMgrPointer实际上是一个很重要的“对象管理器”地址指针。通过这个指针,再加上几个辅助地址,可以读取游戏内大部分的物品、人物等信息。不过我没有再进一步研究,再研究就是做刷金脚本/飞天外挂了。

重点1、脚本每次运行必须获得游戏的基址。64位的魔兽世界程序,每次运行基址都是变的,不像32位程序基址是固定值。autoit用KryMemory.au3可以直接通过_Process_GetBaseAddress获取游戏基址。然后,在autoit里按照CE的地址偏移格式,即可读取到数据。比如 CurMgrPointer=wowclassic.exe+2387C88 这种格式,或者 LocalGUID=[wowcliassic.exe+2387C88]+58 指针偏移。

重点2、游戏版本一更新,内存地址就会变动。即使是一个微小的版本更新,内存地址也有可能变动。所以如何找内存地址就又是一个难事。但一般而言,只要游戏的数据结构不变,那偏移层数和偏移量是不变的。基于此,可以用CE查找一个好找的数值后,逆向查找游戏基址,再推算出其他所有基址。比如搜索小地图显示的所在位置,然后已知偏移量0,找上一层地址。类似于  “已知小地图位置的动态显示地址”=[wowclassic.exe+X]+0,求X的值。找到X的值后,又已知上一个版本 基址B=基址X+10,那么新的基址B大概率仍然是“新的基址X+10”。

至于再深一层的研究,比如找人物基址指针,遍历内存获取周围所有人物或者怪物的名字、坐标;或者找到人物XYZ坐标、镜头角度;或者获取各种CALL……我水平不够,研究不出来。而且这些也属于刷钱脚本/飞天外挂范围,并没有做外挂的打算。


游戏版本更新,修正内存地址

2020年4月30日更新文章

魔兽怀旧服客户端版本更新到了1.13.4.34219。之前的内存地址失效了。正好写一下如何根据以前内存地址的格式,找到新版本内存地址。

上文提到过,只要游戏不做重大代码重构,那游戏的数据结构是不会变的。也即游戏的基址会变,但几级偏移是不会变的(偏移量可能微幅变动),只要找出一个新版本的内存地址,其他地址做一下加减法就可以。这里我用读取游戏角色名字内存格式为引子,更新游戏内存地址。因为读取角色名是直接基址+角色名内存地址的方式获取的,没有偏移量,更没有多层指针偏移,方便好找。

我们已知读取游戏名字内存地址格式为:[游戏进程基址+角色名内存地址] 。其中游戏进程基址脚本和CE都可以自动获取,不用操心;角色名内存地址上个版本是$PlayerNameAddr = 0x2688828,现在要找新的角色名内存地址。新的内存地址离旧地址不会太远,就在旧地址附近(经验之谈),因此初始内存地址设置为0x02660000。

在之前autoit读取角色名脚本基础上,简单加了一个读取内存地址遍历来找新地址。

$nameadddrtmp = 0x02660000
Do
$PlayerName = $dm.ReadString($hwnd, HEX($BaseAddr+$nameadddrtmp),2,0)
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $PlayerName = ' & $PlayerName & @CRLF) ;### Debug Console
ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $nameadddrtmp = ' & HEX($nameadddrtmp) & @CRLF) ;### Debug Console
$nameadddrtmp = $nameadddrtmp + 0x1
Until $PlayerName == "恭喜你"

运行脚本,几分钟就可以显示出最新的角色名内存地址。

角色名新地址是0x266C8B8。接下来换算一下就可以知道其他内存地址了。

先算一下新旧地址的差值:角色名旧地址0x2688828 - 角色名新地址0x266C8B8 = 1BF70
小地图所在位置文字旧地址$GetMinimapZoneTextAddr = 0x025A8C40,所以新地址就是 0x025A8C40 - 1BF70 = 258CCD0

剩下的地址自己算一遍,CE里检查一遍没问题就OK。

图标
AutoIt-wow-dm-readmemory.zip 5.87 KB 315 下载
...
图标
CheatEngine-wow.zip 0.77 KB 198 下载
CheatEngine-wow.zip ...

wking

不管博客型博主

文章评论

  • 会飞的知了

    能否加个QQ 1468236111 为什么我用大漠调用出来的 是乱码 转码后 总是少一个汉字

    2020年04月07日
    • king

      是什么语言和大漠?大漠得用最新版7.2002的

      2020年04月09日
  • 您好,在nga论坛看到你的帖子,没想到百度搜索到你的博客了。
    我是偶然需要弄一下魔兽怀旧服辅助,但是遇到游戏内数据无法传递游戏外的情况,看到你的帖子,想请教一下64位CE是如何在读取的时候不崩溃的

    2020年04月25日
    • wking

      搜索内存是不崩溃的,会崩溃的操作是 下断点、监控内存写入、注入wow进程等操作。这些操作会被wow反外挂系统监测出来,导致程序直接崩溃,而且有可能导致封号。但因为这些地址都是动态地址,想逆推基址的话,不监控内存写入是没办法成功的,所以CE是没法找到基址。

      2020年04月28日