13.游戏外挂原理解析:逆向分析与DLL注入实战(植物大战僵尸)

1.前言

注意:写游戏外挂是一个高风险高收益的行业,本文以及本系列文章只做学习探讨,并不鼓励大家从事外挂行业。

外挂本质上其实就是一个简单的程序,只不过它与一般的程序又有些不同。

一般的程序只控制修改自己的数据,而外挂却是去控制修改其它程序的数据。

随便拿一个游戏作为例子,一般一个角色会有一些属性,比如血量、蓝条等等,这些属性数值实际上就是保存在游戏这个程序中的变量而已,更底层一些就是这些数据是保存在这个程序的内存地址空间中。

而角色的动作,比如砍一个怪让其掉血,从代码层面上来说就是调用了一个函数,这个函数用怪的血量减去角色的攻击力,得到怪的剩余血量。

至于你所看到的游戏各种炫酷动画,都是在这些代码基础之上,添加的动画而已。

一些古老的游戏,比如10年左右的一些手机网页游戏,很多都是纯文字的网页游戏,非常纯粹,没有任何动画,就是一个角色拥有各种数值属性、闯关后遇到了什么怪、打了多少下、掉了多少血、获得了什么物品。

游戏的本质其实就是这些,只是现在的游戏将动画提升到了非常高的优先级别,让你不太容易一眼看出游戏的底层逻辑。

其实无论是破解软件、还是写游戏外挂,最好的方式其实就是你先会开发。

只有你真的写过软件,了解过软件的底层运行原理,你才知道应该怎么用最好的方式去破解它,游戏同理。

2.外挂类型

虽然我们常常能看到各种各样的外挂,但只要稍微分析一下就会发现,外挂的功能就只有两种类型:

  • 修改数值
  • 修改行为

数值是比较简单的,就是改角色数值嘛,比如角色金币只有100,直接给它改到9999999。

其实现逻辑其实并不复杂,甚至可以说非常的通用。

对于只是想要简单的、自己使用的游戏外挂,就没必要去写程序,直接用CE这类工具查找数值在内存中的位置,然后修改数值即可。

但如果你想要将这个功能写成一个外挂、发给别人使用,那么你还需要根据找到的地址,继续分析基地址,最后写程序动态获取数值的地址,修改数值。

而行为则比较复杂了,因为其本质上是去修改相应的函数,你得至少会一些汇编的基础,不用会写,只要会看就够了。

比如无敌外挂,本质上就是去找到角色扣血的函数,然后修改这个函数的一些值,比如跳过扣血、让扣血的值始终等于0。

又比如穿墙外挂,本质上就是去找角色与物体的碰撞检测函数,只要找到了这个函数的位置,那么只需要修改其检测的结果,就能让角色穿墙。

3.前置知识

为了更好的理解后文制作外挂的步骤,本节大致过一遍需要的理论知识。

首先是进程隔离与地址空间。

每个应用程序至少包含一个进程,进程之间的数据是互相隔离的、正常情况下无法相互访问,而隔离的方式就是地址空间,每个进程都有自己的地址空间,进程之间无法直接访问互相的地址空间中的数据。

不理解的可以参考前文基础:动态代码注入技术详解

而我们的目标便是突破进程地址空间的限制,让我们的程序能够访问目标进程的地址空间。

winapi中提供了相应的函数可以做到这一点,相应的函数可以参考前文:注入技术

这是比较高级的用法,更简单的方式其实是将我们的代码写进dll,然后直接将我们的dll注入目标游戏进程中,此时就在同一个地址空间中可以直接修改了,为了简化流程,本文将使用更加通用的dll注入技术编写外挂。

上面的基础可以让我们实现修改游戏进程中的数据,但我们如何知道修改目标中哪一块的数据呢?

这就需要一些工具的帮助了,比较常用的就是CE:Cheat Engine安装

它的作用就是搜索指定进程中的某个数据,通过不断的搜索、比较确定目标数据的地址。

然后进一步通过它分析数据的基地址,有了基地址我们就能一步一步转换、动态拿到游戏的数据地址,通过程序进行修改,也就是基本的数值外挂。

注意这里的基地址,实际上指的就是main函数中的变量地址:

struct Role{
	int blood;
}
int main(){
	Role *a=new Role();
}

假设上面是一个游戏中的数据,那么我们用CE搜索角色血量拿到的地址实际是变量blood的地址。

注意,现代游戏基本都是使用C++C#开发的,乃至一些其它语言开发的游戏,对于一个角色的数值结构基本都是使用类、结构体这样的结构进行组织的,因此这里以结构体进行介绍,对于C++语言开发的游戏来说,更可能使用的是类。

但你会发现,Role这个结构体是通过new关键字动态分配的,也就是说,这个地址每次启动后都会变。

那么什么不会变呢?自然就是这里的变量a

a变量始终存在于main函数内部的第一个变量栈空间位置,而main函数在内存中的加载位置又是可以通过winapi拿到的(实际上拿到的地址是更底层的入口,但整体偏移量是固定的)。

所以我们只要能拿到a变量的地址相对于入口地址的偏移量,那么无论程序启动多少次、只要不重新编译,那么我们都能直接拿到a变量的地址,只要能拿到a变量的地址,那么自然就能一步一步跟踪偏移量拿到里面的数值了。

这里只是简单的一级指针,实际的游戏中会有很多层指针,需要耐心分析。

多级指针在代码中的展现形式类似于:关卡类 包含 角色类字段,角色类 包含 基础属性类字段 基础属性类 包含 血条蓝条等基本字段。

作者:余识
全部文章:0
会员文章:0
总阅读量:0
c/c++pythonrustJavaScriptwindowslinux