外挂是怎样做成的
”你会做外挂嘛?“
应该不少会敲代码的朋友在被身边人知道自己会编程以后都会被问到这个问题,俺也一样!
有的时候我也在思考这到底是怎么做出来的。今天在某视频网站上看到了一个CE教程也算是给我扫了扫盲。
CheatEngine
的大名应该玩游戏的人都应该听过,可以扫描程序内存,更改程序的数据。对平时玩玩某些养成游戏的我来说无异于神器,也算是我用的最多的挂了。而今天我将用学到的CE的一些用法来延伸出外挂制作的方法,给大家提供一个方向,来揭开外挂制作的面纱。而正经的外挂制作远比这复杂很多,毕竟外挂于反作弊系统每天都在道高一尺魔高一丈,已经成了神仙打架,不是简单一下所能讲清楚和学会的。
为什么游戏重启以后CE之前所扫描的地址存储的内容发生了变化?
我们普通玩家的使用也止步于简单的用它扫描游戏内存,修改一下数值了。而游戏再次打开以后会发现我们原来扫到的地址已经不是存着原本的那些数据了。而在某些论坛中我们可以找到大神做好的包,导入进CE中就能用。我一直很奇怪为什么他的游戏反复重启可以一直使用,而我的却不行。这就要从我们应用程序的数据在内存中的存储布局说起。我们程序在内存中的布局分为下图六段,而我们主要关注静态空间与自由空间:
代码段(code):他所存储的顾名思义也就是我们程序执行的机器指令。由于我们完成一些功能是由机器命令按不同顺序排列组合而成的,所以我们这段中的内容每次运行都是按顺序排列好的。
数据段(bss+data):存储的是程序的部分数据,而且每次程序运行是这些数据的位置也是固定的。
堆(heap):其中的数据也不是完整、连续的,用于存放程序运行时动态产生的一些数据,而在使用结束后还需要内存回收,所以这里的数据并不能分配固定的地址,也就是说我们这块区域所存的内容在不同时刻是不同的。
栈(stack):栈和堆不一样他的数据是连续的,但是由于他的起始位置的不同也导致他存的数据在每次运行时也不能确定其地址
由此我们可以得知我们在代码段与数据段所存储的地址每次都是确定的,而堆与栈中的数据则是不同的。我们游戏的血量、金币之类的数据都正是保存在堆或栈中的,这才导致我们每次重启游戏后之前所找到的地址内容都会发生变化。
而我们的程序又是如何去找到这些数据的呢?这就得让我们梦回C语言,指针也就应运登场了,指针从存储的是另一个内存的地址。他可以用来将一些关系比较远的数据连接起来,比如我们可以用一个人物的指针指向血量的地址,从而程序就可以找到这个人物的血量。而一些关系比较近的数据会连续存在一起,这就可以增加一些偏移量来获取其他的数据,比如金币,等级,装备。而装备又可以有很多其他的属性,这时候就又可以用一个指针来指向存储装备属性的内存。层次递进就能讲所有数据连接在一起,而一般最开始的指针会存放在数据段,而中间的和末端的指针就存储在栈和堆中。这时候我们只要找到指针的地址与每一层的偏移量就能找到我们所需要修改数据的内存地址。
为什么我们按以上方法制作的修改器只能在特定的一个或多个版本使用?
在我们游戏不断的更新过程中会添加不同的功能,这样导致了我们的代码段于数据段中间有可能会插入新加入的内容从而打断了原来的数据。这样我们读取以前版本中指针的地址也就不一定是存放的是我们原来的那个指针了。
这个时候我们就需要来查看我们代码段究竟是哪个命令调用了存放血量之类数据了。通过这个命令我们就可以找到这个指针究竟存放在哪个变量之中。但我们上面说过代码段的顺序也可能发生改变,对此我们又有什么对策呢。
由于程序的一个功能是由多个指令按顺序组合而成的。所以我们还可以用多个连在一起且顺序唯一的指令块在代码区中匹配来找到这个命令。
写在最后
虽然我所写的不能写出大多数游戏的外挂,但研究他也让我们简单的认识了内存布局,也让我们知道了外挂制作的一些原理,会给我们在制作其他软件的时候有更多的方法。生活不息,折腾不止!