前言
一直都不太清楚这种游戏辅助是如何实现的,前几天看到了相关的材料就动手尝试了一下。由于个人及其反对使用游戏辅助破坏他人游戏体验的行为。因此下面进行的所有尝试均在离线模式下进行,且不对程序做任何绕过游戏反外挂检测机制的处理。仅仅是对辅助的原理进行简单的探寻并尝试实现一个简单的辅助。
为什么我们要寻找基址
首先,我们的目的是要制作一个方框透视的辅助,那么我们需要的数据就主要有对方和我们的位置,视角等信息,通过这些信息算出对方的相对位置并显示在屏幕上。要寻找到这些信息,就要了解游戏中这些信息的存储机制。
在csgo中,有许多的实体,包括人物,地图中的鸡等等。这些实体是以双向链表的形式链接起来的,链表的形式大致如下。其中entityObj指向的是该实体的信息,包括血量位置等。
1 | struct EntityItem { |
那么我们要如何才能找到这一实体链表呢。对于一个双向链表。只要找到了其中的一个节点。就相当于找到了这一链表中的所有节点。在游戏中我们能直接看到的一个数据就是血量。所以我们考虑通过我们自己的血量,找到我们自身所对应的那个实体链表中的节点。这样也就找到了整个链表。
先来试试找到我们自身的节点吧
我们先开一场离线比赛。可以依次使用下面的命令来方便我们调试。
1 | sv_cheats 1 |
之后打开CE,通过搜索自身血量数值,并通过hurtme的命令来改变血量,不断搜索几次后,我们会剩下大约二十个结果,先将他们都复制到项目地址列表,我们再进行下面的筛选。对于多人竞技游戏,一般来说我们的血量等数值都是存储在服务器中的,由服务器向客户端传输血量信息,即使是本地离线游戏也是会有该过程的。所以我们的客户端的血量应该有一个被赋值的过程(将服务器传来的信息赋值给本地的变量)。
选中一条地址,右键,找出是什么访问了这个地址。或者按F5可直接打开,之后我们会看到一些汇编命令。大致了解一下
1 | mov a,b 将b的值赋给a |
我们要找的血量变量应该尽可能满足下面几个特征:
- 命令为mov
- 赋值的变量有偏移值
- 变量前的计数应该变化较快(每秒约64以上)
- 不涉及乘法运算
解释一下原因:
因为我们的血量是要有一个赋值的过程的。所以需要寻找mov命令,第一条为类似于 cmp dword ptr [edi+00000230],00 的cmp命令可以排除(猜测这一命令是用于比较实体血量是否为零的)。
在csgo中的血量是整形存储的,所以第一条为movdqu命令的我们也可以排除。
因为血量一般不会是结构体的第一个元素(一般为实体的id),所以赋值的过程一般是带有偏移量的。类似于mov eax,[ecx+00000100]一类的都是有可能的。mov [edi],edx一类不带有偏移量的的可能性相对较小。
因为csgo服务器多为64tick和128tick的,所以数据的更新一般比较快。更新较慢的数据也可以排除。
血量的赋值操作一般是不会涉及乘法运算的。所以形如mov eax,[ebx+esi*4]之类的也可以排除。
注:上述方法不是绝对的。只是一些经验总结。还是要结合实际情况来判断的。下图为我尝试找到的血量变量。
找到我们的实体节点
我们发现指令为mov eax,[ecx+00000100] 。说明很有可能ecx中存储的即为EntityItem结构中的指向实体信息的entityObj。而0X100为生命值相对于entityObj的偏移量。而事实也的确如此。所以我们只需要查找是哪个变量存储了ecx中的值,就可以找到我们自身的实体节点了。通过选中指令,我们在下方可以看到ecx的地址。我这一次搜索的地址为643BD450。我们在内存中以16进制的方式查询这一地址,并寻找其中的静态地址(在CE中以绿色标注)。
接下来我们对所找到的静态地址进行筛选。我们要做的就是查找他们所在的那一段内存。看其内存中的一部分是否为链表的形式。在CE的查看内存——工具——分析数据/遍历中,输入我们所要查看的那一段内存的地址。
双击地址即可找到,一般为client.dll + 偏移量的形式。
填入后选择结构——定义新的结构,确认之后我们就能看到这一段的结构了。最后所找到的应该是形如下图的结构。
我们可以看到这一段结构是符合双向链表的。将这一段结构的地址client.dll+4DBD5CC记录下来(每次csgo更新后基址都会发生变化,不要照搬这段基址)这便是我们要找的实体链表的基址了。
寻找位置偏移量
通过存储我们信息的结构体基址+血量偏移量可以寻找到我们的血量值。同样的,通过基址+位置偏移量可以寻找到我们的位置坐标。还记得我们之前找到的人物信息的起始地址吗,也就是ECX寄存器中存储的。我本次寻找到的是643BD450。(这种类型的地址为动态地址,每次开启程序的时候会动态分配,所以每次开启都会变动。只有类似于client.dll + 偏移量的地址才是不会变动的。)在内存查看器中转到这一地址,右键以浮点数的形式查看,上下翻找,并在游戏中移动人物,寻找x,y,z三个值相连。并且与游戏中的操作相匹配的值。例如下图
我们发现x的地址为643BD588,用这一地址减起始地址(643BD450)得到的结果为0x138,这一结果也就是位置的偏移量了。
至此我们已经得到了实体节点的基址:client.dll+4DBD5CC
自身血量偏移量:0x100
自身位置偏移量:0x138
寻找视角矩阵的基址
有了位置信息,我们还需要视角信息才能确定对方在我们屏幕上的相对位置。以便于绘制方框。在游戏中一般不止用角度来描述人物视角。还有用矩阵表示人物视角的方式。找到了视角矩阵也就有了我们的视角信息。
csgo的视角矩阵有如下的特征:
- 矩阵为4x4
- 第一个值为±0.75左右
- 右面四个值数值较大。一般为几百或几千
- 第一行第三列的数字为0
- 第三行第三列和第四行第三列的数字相等。且视角挪到最上方的时候值为1.00,挪到最下方的时候值为-1.00。
我们可以用最后一个特征快速的找到视角矩阵。首先将视角挪到最上方。然后以浮点数的形式搜索1.00,再挪到最下方,搜索-1.00。反复多次。即可将选项迅速减少至一百个左右。再寻找其中的绿色静态地址,用ctrl + B 的快捷键可以快速查看其内存。当找到上下两个值相等。且向上翻一下前面的几项都符合视角矩阵的特征。同时模块为client.dll时。我们就找到了视角矩阵
我们本次得到的起始地址是3693EEE4,但是我们要寻找的是其相对于client.dll的偏移量。这边我采用的方式是用CE搜索3693EEE4地址所对应的值(视角矩阵的第一个值),在多次搜索后,在左侧的列表中找到3693EEE4,并将其添加到下面的列表后,再双击打开就可以看到其相对于client.dll的地址了。(应该还有别的办法,CE用的比较少暂时没想到)
至此我们得到了视角矩阵的基址为client.dll+4DAEEE4,我们的基址寻找工作就圆满完成了!
更便捷的获取基址的方式
目前有一个在github上不断更新的项目,内部有csgo的各项基址
链接:frk1/hazedumper: up to date csgo offsets and hazedumper config (github.com)
在其中的csgo.hpp文件中查找相应的关键词即可查询到基址
m_vecOrigin为位置偏移值
dwViewMatrix为视角矩阵偏移值
dwEntityList为实体链表偏移值