|
目录辅助最终展示效果一、文章介绍二、工具介绍三、基址搜索3.1、寻找阳光基址3.2、寻找卡槽冷却基址3.3、寻找僵尸刷新时间基址3.4、寻找大阳光刷新时间基址3.5、寻找植物编号基址3.6、寻找场上僵尸数量基址3.7、寻找僵尸X坐标基址3.8、通过找到的僵尸X轴坐标基址寻找Y轴、血量等基址3.9、寻找每只僵尸之间的地址偏移四、代码编写4.1、基础代码4.2、功能代码4.2.1、编写实现无限阳光4.2.2、编写植物无冷却4.2.3、编写僵尸全出功能4.2.4、编写无限刷新大太阳功能4.2.5、编写植物替换功能4.2.6、编写获取场上僵尸数量功能4.2.7、编写获取获取僵尸数组功能4.2.8、编写绘制僵尸方框功能4.3、完整代码五、总结辅助最终展示效果演示视频一、文章介绍 写这篇文章的目的是告诉大家游戏单机辅助是如何编写的,这种方法在其他部分非单机游戏也能适用,开发者该如何去防范,知攻善防,并不鼓励大家写游戏外挂。 游戏辅助本身就是利用游戏漏洞进行开发的,在单机游戏中,游戏数据都是存储在本地的,运行游戏后通过使用相应的内存查找工具获取到当前游戏数据的存储地址,通过对其数据进行修改来达到游戏规则之外的效果,使玩家可以轻松通过。 通过本篇文章的学习我们就可以知道辅助开发者是如何获取到游戏数据的,对我们游戏开发人员是有一定的帮助的,比如我们可以对游戏数据进行加密,增加辅助开发的难度,来尽量维持游戏的平衡等......二、工具介绍1、CE(内存搜索工具)2、植物大战僵尸杂交版2.0.883、精易助手(后文用到的时候有附带下载链接,读者现在不必理会)4、PyCharmCommunityEdition2023.3.4(Python开发环境)5、SafeDog(Python游戏安全测试模块,需要模块联系我)【版本不需要和我一样也可以,不过我所用的Python是  ython3 ,这点大家尽量和我保持一样,因为有些代码的使用在Python2中区别比较大】三、基址搜索3.1、寻找阳光基址打开游戏和CE工具并附加进程附加完成后进入游戏关卡CE中填写设置相关信息回到游戏种植植物或者拾取太阳使阳光数量发生变化在CE中搜索375,其他东西不用变,然后点击NextScan(下一次扫描,是在第一次的基础上进行扫描的),注意不是NewScan只剩一条数据,那么这条就是阳光的地址了,不过这只是临时地址,也就是说,游戏关闭再开地址就变了,我们如果要制作辅助就需要找到数据的基址(不会变动的,不关游戏关了几次或是重新安装,都不会变)然后会打开如下窗口,这个窗口不要关,我们回到游戏再次改变阳光的数量,观察窗口的变化这个eax+5560的5560就是我们的一级偏移选择NewScan再选FirstScan这里告诉大家怎么去找多级指针,我们找地址比较特殊的和其他差别比较大的那种地址,如图所示所以我们就右键查看是谁访问了他,这里要选访问,而不是改写找到这种数据就要偷笑了,因为像这种很整齐的而且偏移都是一样的大概率就是找对了,我们再回到游戏改变一下阳光的数量因为我们第一次找到的是eax+5560 所以这里要找moveax开头的,这些是汇编知识了,寄存器相关的,大家不懂可以去找书或视频看,这里先看操作学习思路双击打开继续搜索,其他操作不变,然后你就会发现找到了基址了,当然了如果你的CE版本和我的不同他可能显示的是十六进制的地址,而不是名称,都这无所谓,他们都是绿色的,这里我试过了,每一条都可以作为基址,随便选一条就好了,这里我选择最后一条填完之后点击OK,然后就会发现关闭游戏再打开它仍然有效(注意CE要重新附加进程),写游戏辅助的时候就需要用到它了PlantsVsZombies.exe+2AA00C+768+55603.2、寻找卡槽冷却基址 好了,通过查找阳光基址相信读者已经大致了解了CE的简单使用了,那么接下来的内容里我就不一一截图解释了,部分通过文字的描述来给大家讲解,操作都是大同小异的,聪明的你肯定能明白。 寻找卡槽基址有什么用呢?当然是实现植物无冷却啊,我们首先要了解游戏的冷却原理,没次种植植物后,植物就会在卡槽中进入生长时间也就是我们说的冷却时间。在植物大战僵尸中冷却结束表示1,冷却开始表示0(也就是拿起植物但不种植时卡槽的状态)。一直重复拿起搜索0,放下搜索1,获取搜索完后不要返回游戏继续点击搜索未变动的数值,过滤掉那些变动的值,因为我们没有对卡槽进行操作,那么它对应的值就不会变扫到200多条数据的时候就差不多了,因为不可能每次都和找阳光那样一下就找得只剩下一条,有些数据过滤完还剩下几千条也有,接下来就需要自己去判断了。点第一条数据,然后把滚动条拉倒中间,按住键盘上的shift键然后再随便选一条数据点一下通用的方法按shift选择一半的数据然后按回车对值进行修改,如果我们的卡槽现在是亮的我们就改成0,反之改成1然后按回车全部锁定,再看一下游戏的卡槽里吗有没有变暗(亮),如果有说明正确的地址在我们选择的数据中,另外一部分没选的数据就可以删除了,然后再利用二分法继续寻找,直到找到正确的地址。接下来就用同样的方法寻找基址即可,不过需要注意的是,要把数据类型改回4Byte这样就可以找到第一个卡槽冷却时间的基址了PlantsVsZombies.exe+2AA00C+ 768+144+70通用的方法我们去寻找第二个第三个卡槽的冷却时间,我就会发现他们的基址分别如下:第二个卡槽:PlantsVsZombies.exe+2AA00C+ 768+144+C0第三个卡槽:PlantsVsZombies.exe+2AA00C+ 768+144+110他们之间偏移相差50(十六进制),那么以此类推就可以知道后面所有卡槽的冷却基址了,我们只需要通过写循环就可以实现全卡槽无冷却。3.3、寻找僵尸刷新时间基址 找到关卡进度条基址我们就可以实现僵尸全部出现,然后快速通过。我们首先要知道,植物大战僵尸中,每一波僵尸出现的间隔是一个倒计时,比如第一波出现了那么就进入倒计时,时间一到就刷新第二波僵尸,我们要找进度条基址也就是找到这个间隔的时间地址,把它改成1并且锁定,那么僵尸就会一次性全部出现。【注意关卡刚开始还未出僵尸的时候也是在倒计时,这时候就可以开始扫描了,当然了一定要卡好时间,因为我们在倒计时期间扫描的是减少的值,一不小心僵尸刷新了,这时候值是变大了,重新开始倒计时,如果还扫描变少的值那就错了,这时候应该扫描变大的值,进入游戏然后再扫描变少的值......】进入游戏然后暂停,搜索值减少了一直重复进游戏暂停扫描值减少,直到第二波僵尸出现就搜索值增大了,然后继续重复前面的步骤通过观察发现这个数据一直重复变小然后变大,变小到一定数的时候僵尸就出现,说明这个就是时间地址了,同样的方法区寻找基址即可,这里就不再赘述。值改成1,锁定然后进入游戏看效果PlantsVsZombies.exe+2AA00C+768+559C3.4、寻找大阳光刷新时间基址 找大阳光的刷新时间和找僵尸的刷新时间是一样的思路,每次刷新的间隔都是在倒计时,不过阳光刷新的时间要比僵尸刷新的时间短。找到后把值改成1,然后锁定,进入游戏观察效果PlantsVsZombies.exe+2AA00C+768+55383.5、寻找植物编号基址通过图鉴我们可以观察到,每个植物是有序号的,如下图所示: 那么我们就可以通过找到这个表示序号的地址,然后令其改变成其他序号的值,比如樱桃辣椒的序号是20,那么我们把地址的值改成20,然后锁定,就会发现在游戏中不管点那个植物种下去的都是樱桃辣椒。 我们进入游戏按图鉴的序号选择植物,目的是方便我们进行扫描,选择第一个卡槽植物就表示0,第二个就表示1,这样就不需要单独去记住植物的序号了。开始游戏,配置好CE我现在拿起坚果,但是并没有种植,坚果在第四个卡槽,编号表示3,所以我们在CE中搜索精确数值3把坚果返回去,不要种植,然后再换一个植物拿起,输入编号继续扫描,重复次操作 找到了两条数据,我们需要进行判断,把其中一条改成20,然后锁定,看一下游戏中拿起植物后会不会发生变化,会就是锁定的那条数据。(不一定是20,也可以是其他植物编号,我习惯用20表示樱桃辣椒)如图所示,我们是没有选择樱桃辣椒的,但是锁定了第二条数据后,选择任何植物都是樱桃辣椒,那就说明第二条就是植物序号的临时地址了,同样的寻找方法找到该数据的基址。注意,这个数据的值在选中植物才会表示植物的编号,未选中时是一个比较大的值,这个不用管它,要用的时候直接改成对应的编号然后锁定就好了PlantsVsZombies.exe+2AA00C+ 768+138+ 283.6、寻找场上僵尸数量基址 寻找僵尸数量可以用另一种方式,因为僵尸个数是整数同时也比较小,一般第一波出场都几只,然后第二波再慢慢增多,一开始我们就可以把范围缩小。也可以观察僵尸死亡情况使用值减少的方法进行扫描。扫描后进入游戏 成功找到场上僵尸数量基址了,这个目前没什么用,但是对后面我们绘制僵尸方框的时候就派上用场了,通过场上僵尸的数量来判断需要绘制多少个方框。虽然绘制方框通常是用在FPS游戏中的方框透视,在植物大战僵尸中没什么用处,但是原理是差不多的,直到这里怎么绘制,通用就明白了在FPS中该如何绘制PlantsVsZombies.exe+2AA00C+768+A03.7、寻找僵尸X坐标基址 找僵尸相关的数据有点特殊,并不是每只僵尸都可以找到,需要通过找到主僵尸(这里我把它称为主僵尸),我们先来分析一下原理: 每当僵尸死亡后它的地址就重新赋给新生成的僵尸,所以地址并不是固定某一只僵尸,通过我不断的尝试我发现一个规律,主僵尸的地址通常在第二波僵尸中,第一波是没有的,所以我们可以等到第二波僵尸出现的时候再扫描,接下来我会在下图文中详细讲解。 因为坐标数据是以浮点型来保存的,所以我们要搜索浮点型才能找到,然后让游戏开起来,等待第二波僵尸出现,当然也可以在第一波僵尸出现就开始扫,说不定还有可能在第一波就出现,为了讲解更详细,这里我在第一波僵尸出现后就开始扫描 扫描的方法:僵尸在向前移动的过程中,x坐标的值是一直在减小的,所以我们扫描值减少的数据就好了在第一波僵尸出现后就可以开始扫描了找到只剩下几十条数据的时候我们就可以手动排除了,如上图所示框起来的那种,值是负数或者值是个位数开头的全部都可以排除,留下百位的数据。这种第一次扫描就出现绿色的也可以直接排除,基址一般不会第一次扫描就出来,手动分析完剩下六条数据,再看游戏发现场上也是六只僵尸,那么这六条分别对应尝试的某一只僵尸,里面就很可能有我们要的主僵尸地址每一条每一条看,第一次右键谁改写了该地址然后如果在找一级指针的时候第一次扫描就出现了三条或者四条数据的那就证明找到了主僵尸的临时地址,再接着往下找就一定可以找到主僵尸基址,其他的地址在找一级偏移的时候出现的都是两条地址,这是我在寻找的过程中发现的规律,可以提高大家寻找的速度。如下图所示就是非主僵尸地址扫描后的结果:在我这个版本的植物大战僵尸杂交版中扫描出来的非主僵尸地址都是两条数据的好,我们回到正题,接着主僵尸地址继续扫描二级偏移那么我们就成功找到了主僵尸的x坐标基址了,通过这个地址添加偏移就可以获得其他僵尸的x坐标基址PlantsVsZombies.exe+2AA00C +768+90+2C3.8、通过找到的僵尸X轴坐标基址寻找Y轴、血量等基址 上一小节我们讲过了如何寻找僵尸的x轴坐标基址,这小节我们就讲解如何通过找到的x坐标基址来找僵尸的其他数据基址,比如:y轴坐标基址,血量基址等。 僵尸的数据其实是保存在一个结构体中的,有个僵尸数值,里面就包含了每只僵尸的数据,只要找到其中一条数据的基址那么就可以通过偏移来寻找到其他数据的基址。刚开始的话我是一个一个试来判断每条地址的意思,这种方式很笨,但是也能找到,有点教程是让你剖析结构体来分析,其实差不多的然后在这里我就先告诉大家,其他地址的偏移在x轴基址上添加偏移4找到僵尸y轴基址在x轴基址上添加偏移9C 找到僵尸y轴基址【都是十六进制的加减法】那么偏移+4就表示下一个地址,如下图所示我现在是知道了偏移来给你们分析,如果遇到一个新游戏那就得自己去尝试分析不同地址代表什么然后让其和初始地址相减就可以得到偏移,得到偏移的好处就是可以在初始基址的基础上通过加找到的偏移来得到其他数据的基址,不需要我们重新去扫描然后再分析找基址。我们已经知道x轴+4偏移得到y轴,那么就可以在x轴基址偏移的基础上加4得到y轴基址PlantsVsZombies.exe+2AA00C+ 768+ 90+ 30【主僵尸y轴基址】 同样的方法找主僵尸血量,这个偏差有点大,通过这种方法难找,我给大家说一下另一种方法,和找x坐标是一样的,通用是要等主僵尸出现,然后再去找,要先判断出主僵尸是谁,我们可以通过前面找到的主僵尸x坐标去判断,改变x坐标看看谁动了,动了的就是主僵尸,然后让植物攻击它,搜索减少的值,要保证主僵尸不会死去,接下来的操作都是一样的,直到找到血量基地址。这里我直接给大家写出来了,前面我们说过了,血量地址就是x坐标地址+9C,用计算机计算一下:所以僵尸血量的偏移是C8PlantsVsZombies.exe+2AA00C+ 768+ 90+C8 【主僵尸血量基址】3.9、寻找每只僵尸之间的地址偏移 回顾3.7小节的内容,我们是不是找到了所有在场僵尸的x轴临时地址,包括主僵尸的临时地址,哪些非主僵尸的地址虽然说不能找到他们的基址,但是可以通过临时地址相减来找到他们之间的偏移。 我们继续3.7找到的数据来讲解:1438004C是我们主僵尸x轴的临时地址,通过观察这个地址的排序是升序,也就是越往下越大,并且每个地址之间的差值是一样的,都是204,这个其实就是每只僵尸之间的偏移。也就是说不管以后找到僵尸的什么数据的基址,只要偏移+204那就是别的僵尸的该数据的基址,通过这个偏移和3.6小节我们找到的僵尸数量利用循环我们就可以对场上所有的僵尸进行方框的绘制,或者是配合僵尸的状态来秒杀所有僵尸,当然了,由于篇幅原因,本篇文章就不讲解僵尸秒杀是如何实现的。四、代码编写4.1、基础代码importSafeDog#辅助开发模块fromctypesimport*#C语言类型库importwin32processfromwin32apiimport*fromwin32processimport*fromwin32guiimportFindWindowPROCESS_ALL_ACCESS=(0x000F0000|0x00100000|0xFFF)#调用最高权限打开一个进程kernel32=windll.LoadLibrary("kernel32.dll")#加载内核模块window_handle=FindWindow(None,"植物大战僵尸杂交版v2.0.88")#获取窗口句柄process_id_read=win32process.GetWindowThreadProcessId(window_handle)[1]#获取进程IDprocess_handles=OpenProcess(0x1F0FFF,False,process_id_read)#以最高权限打开进程,获取进程句柄#获取模块地址defgetModuleAddress():address=SafeDog.getModuleAddress(process_handles,0x2AA00C)result=SafeDog.readMemoryInt(process_handles,address)returnresult这里要改的地方可能就只有窗口的,如果和我的游戏版本一样的话就可以不用改不同的版本可能不一样,这里我推荐一个工具精易编程助手,网上搜索可以下载下载链接:精易编程助手(125.la)获取模块地址也就获取 lantsVsZombies.exe在内存中的初始地址,该地址加上偏移就是我们的基址PlantsVsZombies.exe+2AA00C功能我封装在了模块的getModuleAddress方法中,需要模块可联系我,有了模块就可以直接调用。4.2、功能代码4.2.1、编写实现无限阳光3.1小节中我们找到了阳光的基址PlantsVsZombies.exe+2AA00C+768+5560defchangeSunNumber():addFirsP=getModuleAddress()#获取模块基址addSeconP=addFirsP+int(0x768)#通过添加偏移找到一级指针addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)#一级指针保存的是二级指针的地址,通过读取内存整数型获取到二级指针地址addThirP=addSecondPoint+int(0x5560)#通过二级指针添加偏移找到临时地址sun=99999if(SafeDog.writeMemoryInt(process_handles,addThirP,sun)):print("[+]Thesunlightvalueismodified")调用SafeDog 模块的 readMemoryInt方法读内存整数型,找到应该基地址并调用 writeMemoryInt方法写内存整数型修改阳光数量为999994.2.2、编写植物无冷却卡槽冷却基址:PlantsVsZombies.exe+2AA00C+ 768+144+70 相邻卡槽偏移50defignoreCooling():print("植物无冷却执行中...")addFirstP=getModuleAddress()addSeconP=addFirstP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0x144)addThirdPoint=SafeDog.readMemoryInt(process_handles,addThirP)foriinrange(0,14):num=i*80addFourP_Cardslots=addThirdPoint+(int(0x70)+num)data=1if(SafeDog.writeMemoryInt(process_handles,addFourP_Cardslots,data)):print(f"[+]Ignore{i+1}coolingchangesuccessful")其他代码是一样的,这里就不赘述了,主要说一下循环那一块,一共是14 个卡槽(当然如果刚开始玩卡槽是没这么多的,读者根据自己的需求对循环次数进行修改即可)range的值是左闭右开区间,也就是第二个参数14取不到,最大值只会取到13,所以0~13一共循环14次,每次循环都让num值在原来的基础上+80十六进制的50转十进制就是80,通过传入1,也就是令倒计时时间为1,就能实现修改卡槽无冷却需要注意的是这只是封装在一个函数中,所以直接调用这个函数只会循环一次,也就是刷新一次,执行完毕之后,游戏卡槽冷却又会进入倒计时,无法实现无限无冷却,要想实现需要在调用时通过while来实现循环调用才能实现无冷却功能。if__name__=='__main__':whileTrue:ignoreCooling()4.2.3、编写僵尸全出功能僵尸冷却时间基址:PlantsVsZombies.exe+2AA00C+768+559CdefmonsterAllAppear():addFirstP=getModuleAddress()addSeconP=addFirstP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0x559C)data=1if(SafeDog.writeMemoryInt(process_handles,addThirP,data)):print("[+]Allthemonstersappeared")4.2.4、编写无限刷新大太阳功能大太阳刷新时间基址:PlantsVsZombies.exe+2AA00C+768+5538defsunWithoutCooling():print("阳光刷新无冷却执行中...")addFirstP=getModuleAddress()addSeconP=addFirstP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0x5538)data=1SafeDog.writeMemoryInt(process_handles,addThirP,data)4.2.5、编写植物替换功能植物编号基址:PlantsVsZombies.exe+2AA00C+ 768+138+ 28defchangePlant(id):print("植物替换却执行中...")addFirstP=getModuleAddress()addSeconP=addFirstP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0x138)addThirdPoint=SafeDog.readMemoryInt(process_handles,addThirP)addFourP=addThirdPoint+int(0x28)data=idif(SafeDog.writeMemoryInt(process_handles,addFourP,data)):print("[+]Plantchangesuccessful")4.2.6、编写获取场上僵尸数量功能场上僵尸数量基址:PlantsVsZombies.exe+2AA00C+768+A0defgetzombieNumber():addFirstP=getModuleAddress()addSeconP=addFirstP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0xA0)Zombie_Numbers=SafeDog.readMemoryInt(process_handles,addThirP)#读取到数量returnZombie_Numbers4.2.7、编写获取获取僵尸数组功能PlantsVsZombies.exe+2AA00C+ 768+ 90+C8 【主僵尸血量基址】PlantsVsZombies.exe+2AA00C +768+90+2C【主僵尸x 轴基址】PlantsVsZombies.exe+2AA00C+ 768+ 90+ 30 【主僵尸y轴基址】defzombieInformation():x=[]y=[]b=[]addFirstP=getModuleAddress()#获取模块基址addSeconP_ZBP=addFirstP+int(0x768)addSecondPoint_ZBP=SafeDog.readMemoryInt(process_handles,addSeconP_ZBP)addThirP_ZBP=addSecondPoint_ZBP+int(0x90)addThirdPoint_ZBP=SafeDog.readMemoryInt(process_handles,addThirP_ZBP)Zomb_X_Offset=int(0x2c)foriinrange(0,4):#x轴addFourP_x=addThirdPoint_ZBP+Zomb_X_OffsetaddFourPoint_x=SafeDog.readMemoryFloat(process_handles,addFourP_x)#y轴addFourP_y=addThirdPoint_ZBP+Zomb_X_Offset+int(0x4)addFourPoint_y=SafeDog.readMemoryFloat(process_handles,addFourP_y)#血量addFourP_b=addThirdPoint_ZBP+Zomb_X_Offset+int(0x9C)addFourPoint_b=SafeDog.readMemoryInt(process_handles,addFourP_b)x.append(addFourPoint_x)y.append(addFourPoint_y)b.append(addFourPoint_b)Zomb_X_Offset+=int(0x204)returnx,y,b用列表来保存不同僵尸的信息,不过代码这么写效率很低,所以这里我只获取了4只僵尸的信息作为演示,正确的写法应该使用多线程来执行该函数返回的是僵尸信息的列表,调用该函数的时候需要使用三个列表来进行接收,x表示僵尸x坐标数据、y表示僵尸y坐标数据、b表示僵尸血量数据,该函数是为下面绘制僵尸方框功能做准备的。0x4表示x轴基址偏移+4为 y轴基址0x9C 表示x轴基址偏移+9C 为血量基址0x204表示获取下一只僵尸的数据4.2.8、编写绘制僵尸方框功能defdrawZombiePane():print("绘制僵尸方框执行中...")Class_Draw=SafeDog.Draw(window_handle)#调用模块绘制类dc,rect=Class_Draw.getDcRect()#获取屏幕上下文whileTrue:foriinrange(0,4):x,y,b0=zombieInformation()#获取僵尸数组信息,列表x_r=int(x[i]*1.1)#坐标换算y_r=int(y[i]*0.91)blood=b0[i]Class_Draw.getWindowRect(rect)#获取窗口矩形坐标l=rect.left+x_rt=rect.top+y_rr=l+70b=t+170ifblood>0:#血量不为0则绘制Class_Draw.protractRectangle(dc,l,t,r,b,255,0,0)#绘制方框line=(170/300)*blood#血量与直线的比值Class_Draw.protractLine(dc,l-10,t,l-10,(b-170)+int(line),60,179,113)#绘制血条4.3、完整代码importSafeDog#辅助开发模块fromctypesimport*#C语言类型库importwin32processfromwin32apiimport*fromwin32processimport*fromwin32guiimportFindWindowPROCESS_ALL_ACCESS=(0x000F0000|0x00100000|0xFFF)#调用最高权限打开一个进程kernel32=windll.LoadLibrary("kernel32.dll")#加载内核模块window_handle=FindWindow(None,"植物大战僵尸杂交版v2.0.88")#获取窗口句柄process_id_read=win32process.GetWindowThreadProcessId(window_handle)[1]#获取进程IDprocess_handles=OpenProcess(0x1F0FFF,False,process_id_read)#以最高权限打开进程,获取进程句柄#获取基址defgetModuleAddress():address=SafeDog.getModuleAddress(process_handles,0x2AA00C)result=SafeDog.readMemoryInt(process_handles,address)returnresult#无限阳光defchangeSunNumber():addFirsP=getModuleAddress()addSeconP=addFirsP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0x5560)sun=99999if(SafeDog.writeMemoryInt(process_handles,addThirP,sun)):print("[+]Thesunlightvalueismodified")#植物无冷却defignoreCooling():print("植物无冷却执行中...")addFirstP=getModuleAddress()addSeconP=addFirstP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0x144)addThirdPoint=SafeDog.readMemoryInt(process_handles,addThirP)foriinrange(0,14):num=i*80addFourP_Cardslots=addThirdPoint+(int(0x70)+num)data=1if(SafeDog.writeMemoryInt(process_handles,addFourP_Cardslots,data)):print(f"[+]Ignore{i+1}coolingchangesuccessful")#植物替换defchangePlant(id):print("植物替换却执行中...")addFirstP=getModuleAddress()addSeconP=addFirstP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0x138)addThirdPoint=SafeDog.readMemoryInt(process_handles,addThirP)addFourP=addThirdPoint+int(0x28)data=idif(SafeDog.writeMemoryInt(process_handles,addFourP,data)):print("[+]Plantchangesuccessful")#阳光刷新无冷却defsunWithoutCooling():print("阳光刷新无冷却执行中...")addFirstP=getModuleAddress()addSeconP=addFirstP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0x5538)data=1SafeDog.writeMemoryInt(process_handles,addThirP,data)#僵尸全出defmonsterAllAppear():print("僵尸全出却执行中...")addFirstP=getModuleAddress()addSeconP=addFirstP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0x559C)data=1if(SafeDog.writeMemoryInt(process_handles,addThirP,data)):print("[+]Allthemonstersappeared")#获取场上僵尸数量defgetzombieNumber():addFirstP=getModuleAddress()addSeconP=addFirstP+int(0x768)addSecondPoint=SafeDog.readMemoryInt(process_handles,addSeconP)addThirP=addSecondPoint+int(0xA0)Zombie_Numbers=SafeDog.readMemoryInt(process_handles,addThirP)#读取到数量returnZombie_Numbers#获取僵尸信息defzombieInformation():x=[]y=[]b=[]addFirstP=getModuleAddress()#获取模块基址addSeconP_ZBP=addFirstP+int(0x768)addSecondPoint_ZBP=SafeDog.readMemoryInt(process_handles,addSeconP_ZBP)addThirP_ZBP=addSecondPoint_ZBP+int(0x90)addThirdPoint_ZBP=SafeDog.readMemoryInt(process_handles,addThirP_ZBP)Zomb_X_Offset=int(0x2c)foriinrange(0,4):#x轴addFourP_x=addThirdPoint_ZBP+Zomb_X_OffsetaddFourPoint_x=SafeDog.readMemoryFloat(process_handles,addFourP_x)#y轴addFourP_y=addThirdPoint_ZBP+Zomb_X_Offset+int(0x4)addFourPoint_y=SafeDog.readMemoryFloat(process_handles,addFourP_y)#血量addFourP_b=addThirdPoint_ZBP+Zomb_X_Offset+int(0x9C)addFourPoint_b=SafeDog.readMemoryInt(process_handles,addFourP_b)x.append(addFourPoint_x)y.append(addFourPoint_y)b.append(addFourPoint_b)Zomb_X_Offset+=int(0x204)returnx,y,b#绘制僵尸方框defdrawZombiePane():print("绘制僵尸方框执行中...")Class_Draw=SafeDog.Draw(window_handle)#调用模块绘制类dc,rect=Class_Draw.getDcRect()#获取屏幕上下文whileTrue:foriinrange(0,4):x,y,b0=zombieInformation()#获取僵尸数组信息,列表x_r=int(x[i]*1.1)#坐标换算y_r=int(y[i]*0.91)blood=b0[i]Class_Draw.getWindowRect(rect)#获取窗口矩形坐标l=rect.left+x_rt=rect.top+y_rr=l+70b=t+170ifblood>0:#血量不为0则绘制Class_Draw.protractRectangle(dc,l,t,r,b,255,0,0)#绘制方框line=(170/300)*blood#血量与直线的比值Class_Draw.protractLine(dc,l-10,t,l-10,(b-170)+int(line),60,179,113)#绘制血条defmenu():print("----------1、【无限阳光】----------")print("----------2、【植物无冷却】----------")print("----------3、【植物替换】----------")print("----------4、【阳光刷新无冷却】----------")print("----------5、【僵尸全出】----------")print("----------6、【获取场上僵尸数量】----------")print("----------7、【绘制僵尸方框】----------")print("----------8、【组合功能:快速通关】----------")user_choose=int(input("请选择功能:"))ifuser_choose==1:whileTrue:changeSunNumber()elifuser_choose==2:whileTrue:ignoreCooling()elifuser_choose==3:id=int(input("请更具植物图鉴获取植物编号,输入方法:植物编码-1:"))whileTrue:changePlant(id)elifuser_choose==4:whileTrue:sunWithoutCooling()elifuser_choose==5:whileTrue:monsterAllAppear()elifuser_choose==6:getzombieNumber()elifuser_choose==7:whileTrue:drawZombiePane()elifuser_choose==8:whileTrue:changeSunNumber()monsterAllAppear()#僵尸全出ignoreCooling()#植物无冷却changePlant(20)#默认火爆辣椒if__name__=='__main__':menu()功能全部实现,但是代码需要改进的地方比较多,效率并不高,但本文的内容不是交大家这些,功能能够实现是最主要的。五、总结 那么本篇文章到这里就结束了,不知道你对游戏外挂制作是否有一定的了解。其实游戏外观的制作远不止这么简单,内存查找只是比较简单的方法,而且只针对把游戏数据保存在本地的游戏有效。对于网络游戏这种内存修改的方式就失效了,需要用到封包技术,这些都是后话了,读者感兴趣可自行了解。 最后,不管你学习本文章内容是出于什么目的,不管你是开发者还是游戏辅助制作的初学者,都要记得不要触碰法律的底线。 游戏外挂种类繁多,不同的外挂有不同的制作、运行原理,像这种单机游戏根据游戏的运行规律来制作的辅助工具,不涉及到对客户端和数据包的破解是不构成违法犯罪的,读者可大但的了解和尝试提升自己的编程技术。
|
|