如何结合Cheat Engine和逆向工程找到CS2内存中的人物地址
或许我们的公众号会有更多你感兴趣的内容

这篇文章发布的时候,关于更多CS2-cheats的代码已经在Github仓库中发布,你可以在下面的链接找到更多CS2的外挂功能
https://github.com/Joe1sn/ExtCheats
游戏环境
Steam上启动的CS2国际服,在设置中加上-insecure参数

在游戏中启用控制台,然后 ` 就可以输入命令了
CS2可以使用CFG文件进行快速的加载,这里我用了一段cfg脚本来进行编写
1 | sv_cheats 1 |
你可以在steam\steamapps\common\Counter-Strike Global Offensive\game\csgo\cfg中防止该文件,然后再游戏中使用exec <不含后缀的文件名>


为了方便调试,可以把屏幕大小改为1280x600

PlayerPawn
简单的CE使用
首先使用准确值找到HP值

在游戏中可以使用hurtme xx来对自身角色造成伤害


这样就找到了几个类似的值,这里有几个要点
- 由于在真实游戏中我们能控制的只有客户端,没有服务端程序,CS的客户端位于
client.dll中,所以我们需要对其进行分析,而且地址最好和该dll相关 - cs2使用了
Valve研发的source2引擎,所以我们可以利用相关开源信息进行查找,比如有人做了cs2偏移的仓库:https://github.com/a2x/cs2-dumper 。
接着找出有哪些地址访问了这些地址,这里有一个取巧的方法,利用上面的偏移,比如HP的全称是Health Point,那么变量的命名就可能和heal相关,在上面推荐的仓库就可以找到

那么对上面的内存找访问


添加该RCX的值,在 浏览相关内存->工具->解析结构体中



我们发现了一个为C_CSPlayerPawn的结构体,接着我们看访问的代码,有两段


那我们就打开client.dll,分析下这段代码
文件位于steam\steamapps\common\Counter-Strike Global Offensive\game\csgo\bin\win64
第一段可能为虚函数

第二段位于另外一个函数中,我们主要看rsi怎么取到的值,结果发现是这个函数的参数

多看看交叉引用发现引用太复杂,随后放弃
PlayerController
从全局变量找到Controller
继续按照上一面的找搜索到的HP的一堆地址的访问地址,发现


同样的方法我们找到这段代码,然后在IDA中分析

IDA中的基地址从0x180000000开始,加上偏移521BB0就找到了这段代码,然后对该函数的引用分析

发现普遍存在这几个函数

我们先分析下他是这么找到v12的,首先在sub_180697FA0传入了一个v10
先分析sub_180697FA0
1 | __int64 __fastcall sub_180697FA0(int a1) |
存在全局变量qword_18191C5B8,在CE中分析一下
分析其中的sub_18060A050
1 | __int64 __fastcall sub_18060A050(__int64 a1, int a2) |
其中a1为全局变量

a2暂时未知,不过我们可以根据fastcall的传参顺序( rcx,rdx,r8,r9)或者汇编来看a2传递的是什么参数


编写python算法模拟一下,但是条件中内存有指针,那没有两种思路
- 利用调试器取值
- 直接都程序内存
这里用第一种,先不管最后一个条件,计算得到v2 = 0000019CA3550808
然后算出v3 = 0x7ffab466e718
1 | def sub_18060A050(): |
用CE看一下*v3这个指针的值

那么返回的就是00007FFAB4652500,用CE发现是一个叫做CCSPlayerController的数据结构

看下github上的偏移表

发现有几处关键信息:

这样就可以通过client.dll + 191C5B8加上下标,通过刚才的函数,找到了CCSPlayerController开始的地址
Controller到Pawn
我们继续逆向,根据Controller+0x7E4为m_hPlayerPawn,就用CE看看这个地址有无读写,在观察下汇编发现

由于CE捕获到的两段代码十分相近,那么给rax赋值的话rax引用就可能出现,搜索对[rax]访问找到

1 | v11 = *a4; // ////////////// |
其中v11就是我捕获到的mPawn值,这里再次出现了全局变量client.dll+1819538

同之前编写脚本计算地址,然后再对照,这里就不再赘述过程了。
得到从CCSPlayerController.mPawn找到对应CSPlayerPawn的方法,这段代码在项目的Cheats的Player类中
1 | void Player::GetPawn() { |
Controller的数组
到这里算是总结了吧。之前从全局变量找到Controller,有许多没用的比较和逻辑运算,这些是为了对其和限制大小
找到Controller的就可变为,改代码位于项目的Cheats.cpp中,在Cheats的构造函数中
1 | DWORD64 ListOffsetA = GetProcessMem(this->hProcess, this->ClientDLLBase + ClientDLL::C_CSPlayerController, 1, 0); |
