ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

5分钟定位游戏矩阵:基于Cheat Engine模糊搜索的逆向工程实践

5分钟定位游戏矩阵:基于Cheat Engine模糊搜索的逆向工程实践 1. 项目概述为什么我们要聊这个如果你是一个对FPS手游比如《和平精英》、《使命召唤手游》这类背后的技术实现或者对游戏逆向、内存修改有点兴趣的玩家或开发者那你肯定对“游戏矩阵”这个词不陌生。简单来说在Unity或Unreal Engine 4UE4这类游戏引擎里矩阵通常是“世界到屏幕的变换矩阵”是决定游戏里一个3D物体比如你的角色、敌人、武器最终在2D屏幕上显示位置的核心数据。找到它理论上你就能知道所有敌人的屏幕坐标从而实现一些“辅助”功能比如自瞄、透视方框、雷达等等。网上关于找矩阵的教程很多但要么过于理论化讲一堆线性代数让人头大要么就是步骤繁琐动不动就要用IDA、x64dbg进行复杂的反汇编和特征码扫描门槛极高劝退无数新手。今天我要分享的是一个完全不同的思路利用Cheat Engine的“模糊搜索”功能结合对游戏引擎渲染流程的理解在5分钟内以一种近乎“物理外挂”的方式快速定位到这个关键矩阵。这个方法不涉及复杂的汇编指令分析不需要你懂C逆向甚至对数学要求也不高。它的核心思想是“黑盒测试”和“过程追踪”我们不去分析代码“怎么算”出矩阵而是去观察“谁在用”这个矩阵以及用了之后产生了什么“可观测”的变化。听起来是不是有点意思这正是利用了Cheat Engine在内存扫描领域的强大灵活性以及Unity/UE4引擎本身数据结构的规律性。2. 核心思路拆解模糊搜索的降维打击在深入实操之前我们必须搞清楚传统方法为什么慢以及我们这个方法为什么快。这决定了整个操作的成败。2.1 传统找矩阵方法的瓶颈常规的找矩阵方法可以概括为“特征码定位法”或“数据访问追踪法”。特征码定位分析游戏渲染函数如Unity的Camera.worldToCameraMatrix或UE4的FSceneView::ProjectWorldToScreen在反汇编代码中找到访问矩阵数据的指令模式将其提炼为一串字节特征码。然后用内存扫描工具在游戏进程中搜索这串特征码找到矩阵的地址。这个方法的问题在于引擎版本差异不同游戏使用的Unity/UE4版本可能不同编译后的指令也会有差异一个游戏的特征码很难通用于另一个游戏。代码混淆与保护很多手游尤其是热门FPS会使用代码混淆、虚拟化保护如VMProtect, Themida的移动端变种等技术让反汇编出来的代码面目全非特征码失效。技术要求高需要熟练使用逆向工具IDA Pro, Ghidra并具备一定的汇编和引擎源码阅读能力。数据访问追踪先找到一个已知的世界坐标比如自己角色的坐标然后在Cheat Engine中搜索这个坐标值。找到后通过“找出是什么访问了这个地址”的功能追踪到读取或修改该坐标的代码再层层回溯最终找到使用坐标进行矩阵运算的函数从而定位矩阵。这个方法同样繁琐且在多线程、优化过的引擎中调用链可能非常深且复杂。2.2 模糊搜索的逆向思维我们的方法完全绕开了代码分析。其核心逻辑基于一个简单的事实游戏矩阵的作用是将3D世界坐标x, y, z转换为2D屏幕坐标x, y。那么如果我们能人为地、大幅度地改变一个已知物体的世界坐标观察其屏幕坐标的剧烈变化并让Cheat Engine去“模糊地”追踪这种变化是不是就能反向定位到参与这个计算过程的关键数据——也就是矩阵呢“模糊搜索”在这里不是指搜索不精确的值而是指搜索“值的变化类型”。Cheat Engine的模糊搜索可以扫描“增加的数值”、“减少的数值”、“变动的数值”、“未变动的数值”等。我们正是利用这一点制造可观测的剧烈输入变化在游戏内找到一个我们可以精确定位和控制的3D物体。最好的目标就是自己角色的坐标。通过游戏本身的功能如移动、传送或简单的内存修改让我们角色的Y轴高度坐标发生极其剧烈、独特的变化例如从正常地面的100瞬间修改到10000。观测对应的剧烈输出变化当角色坐标的Y值巨变时其在屏幕上的Y坐标通常是从屏幕顶部到底部的像素值必然也会发生剧烈且规律的变化例如从屏幕中央瞬间跑到屏幕顶部之外。建立关联扫描我们告诉Cheat Engine请帮我找出所有“值的变化幅度”与“角色Y坐标变化幅度”强相关的内存地址。具体来说就是先扫描“变动的值”然后根据角色坐标Y值“增加”或“减少”在下一轮扫描中筛选“值增加”或“值减少”的地址。经过几轮这样的“变化同步”筛选候选地址数量会急剧减少。从结果中识别矩阵最终留下的少数地址中极有可能就包含了那个将世界Y坐标转换为屏幕Y坐标的矩阵中的相关元素例如矩阵的第四行第三列_m23它直接影响世界Y坐标对屏幕Y坐标的贡献。找到其中一个元素根据矩阵在内存中的连续存储规律通常是16个连续的float值4x4矩阵我们就能顺藤摸瓜找到整个矩阵。这个方法的优势是引擎无关和保护绕过。无论游戏用了什么版本的Unity或UE4无论代码如何混淆只要它最终要通过矩阵运算来渲染物体这个数据流和内存变化规律就是存在的我们就能通过这种“黑盒测试”把它揪出来。3. 工具准备与游戏环境设置工欲善其事必先利其器。这个方法对工具版本和游戏状态有特定要求。3.1 Cheat Engine的选择与配置版本强烈建议使用Cheat Engine 7.5 或更新版本如7.7。新版对模糊搜索的界面和稳定性有优化。不要使用过于陈旧的版本。关键插件启用首次运行Cheat Engine后点击菜单栏的Help-Plugins确保Lua Engine和Auto Assemble这两个插件是启用的状态。虽然本教程主要用不到Lua脚本但它们是CE完整功能的基础。附加进程权限我们将要附加到安卓模拟器进程。以最常用的雷电模拟器为例它运行的是一个dnplayer.exe进程但这个进程下会有多个子进程承载不同的游戏。你需要确保Cheat Engine以管理员身份运行否则可能无法成功打开进程或进行内存搜索。3.2 目标游戏与模拟器设置游戏选择为了成功演示请选择一个你熟悉的、基于Unity或UE4开发的FPS手游在电脑模拟器上运行。《和平精英》国际版《PUBG MOBILE》、《使命召唤手游》、《荒野行动》等都是典型例子。确保游戏能正常运行。模拟器设置打开模拟器的设置或属性设置。找到性能设置或高级设置。将“性能设置”调整为“高性能”或“自定义”至少4核CPU4096MB内存以上。内存扫描是密集型操作足够的资源能保证扫描速度和不卡顿。将渲染模式改为DirectX或OpenGL如果选项中有。这比“兼容模式”通常是Software Render更能反映真实移动端的渲染路径数据布局更标准。关闭模拟器的“高帧率”模式如120帧、144帧锁定为60帧。过于高的帧率可能导致游戏逻辑和渲染循环更快增加内存数据变化的噪音干扰我们的模糊搜索。3.3 关键的前期信息搜集在开始扫描前我们需要获得一个关键的“锚点”自己角色的实时世界坐标。进入训练场或单人模式找一个可以自由移动、没有敌人干扰的环境。训练场是最佳选择。初步定位坐标在游戏中让自己角色站在一个有明显高低差的地方附近比如一个台阶边、斜坡底。打开Cheat Engine点击左上角电脑图标在进程列表中找到你的模拟器游戏进程例如LdVBoxHeadless.exe或xxx.exe具体名称因模拟器而异。雷电模拟器通常是dnplayer.exe下的某个子进程可以通过内存占用大小判断。首次扫描角色坐标。假设我们找Y轴高度。在CE扫描界面数值类型选择Float单精度浮点数。Unity和UE4中坐标基本都是Float。扫描类型选择未知的初始值。点击首次扫描。这会得到海量结果几百万甚至上千万。过滤出坐标回到游戏让角色向前移动一小段距离。此时角色坐标的X和Z值会变化Y值可能不变在平地上。回到CE扫描类型改为变动的数值点击再次扫描。再回到游戏让角色向后移动回原位。扫描类型改为未变动的数值。如此反复“移动-扫描变动值”、“停止-扫描未变动值”几次。同时可以尝试让角色跳一下Y值变化然后扫描变动的数值再落地后扫描未变动的数值。经过多次过滤地址列表会缩小到几千甚至几百个。将这些地址全部添加到下方的地址列表快捷键CtrlA全选然后右键将选中的地址添加到地址列表。识别正确的坐标地址在地址列表中你会看到一堆地址和它们的值。现在在游戏中做一个大幅度的、独特的Y轴坐标变化。比如找到一辆车跳上车顶Y值增加或者从高处跳下Y值短暂变化。观察地址列表中哪个Float值的变化与你的这个动作同步且幅度合理。通常角色坐标的Y值在平地上可能在100.0左右跳上车顶可能变成105.0从高处跳下可能从200.0变为50.0。找到那个变化规律符合你操作的值。记录下这个地址的当前值这就是我们后续制造“剧烈输入变化”的基准线。假设我们记录的正常地面Y坐标为Y_original 100.0。注意这一步可能会找到多个相似的坐标值可能是角色模型不同部位的坐标、摄像机坐标等。选取一个你认为最可能是角色根部脚底或中心的坐标即可。我们的方法对坐标的“绝对精确”要求不高只要它能代表角色位置的大幅变化即可。4. 实操过程5分钟定位矩阵现在我们进入最核心的环节。请确保你已经完成了第3步并记录下了角色的大致Y坐标值例如100.0。4.1 第一步制造极端输入信号我们的目标是让角色的Y坐标发生一个在正常游戏中几乎不可能出现的、巨大的、单向的变化。这样产生的输出信号屏幕坐标变化才会足够强烈和独特便于从内存噪音中分离出来。在Cheat Engine的地址列表中找到并双击你刚才识别的角色Y坐标地址的Value栏。将其值修改为一个极大的数例如10000.0或-10000.0。这相当于让角色瞬间飞到万米高空或坠入地心。切回游戏画面。你会发现角色要么“上天”看不见了要么“入地”看不见了。这正是我们想要的此时角色在屏幕上的Y坐标假设屏幕坐标系原点在左上角Y向下为正很可能已经变成了一个负数在屏幕上缘之上或一个大于屏幕高度的正数在下缘之下。4.2 第二步启动模糊搜索追踪关联变化在Cheat Engine主界面点击新的扫描那个绿色的小望远镜图标打开扫描窗口。设置首次扫描扫描类型选择模糊扫描。数值类型选择Float。搜索区域如果知道游戏模块的大致范围可以设置否则就选整个内存会慢一些但更稳妥。点击首次扫描。这个扫描会记录下当前内存中所有Float值的“状态”。首次扫描可能会花点时间耐心等待。4.3 第三步执行同步变化与筛选这是整个方法的精髓需要严格按顺序操作第一次筛选记录“变化”扫描完成后不要进行任何操作。直接点击再次扫描按钮旁边的下拉箭头将扫描类型从模糊扫描改为变动的数值或者Increased Value/Decreased Value但我们先选更宽泛的变动的数值。点击再次扫描。这次扫描会筛选出从上一次扫描即首次扫描到现在值发生了任何变化的内存地址。因为游戏世界在持续运行物理模拟、动画、特效等会有大量地址变动。但我们已经通过修改角色坐标注入了一个巨大的“信号”。第二次筛选关联“增加/减少”关键判断我们之前把角色Y坐标从100.0改成了10000.0这是一个巨大的增加。因此在CE中将扫描类型改为增加的数值(Increased Value)然后点击再次扫描。原理矩阵中负责将世界Y坐标转换到屏幕Y坐标的那个系数或与之强相关的中间变量其值很可能也因为输入Y的巨幅增加而发生了同向的、大幅度的增加。这次筛选会过滤掉那些值减少或随机小幅度波动的地址。第三次筛选逆向操作强化关联切回Cheat Engine的地址列表将角色Y坐标的值从10000.0改回原来的100.0或者改成一个比原值更小的数比如50.0模拟坠落。切回扫描窗口此时扫描类型应该选择减少的数值(Decreased Value)因为我们的输入信号Y坐标从极大值变回了正常值或更小值。点击再次扫描。重复筛选重复步骤2和3再次将Y坐标改为10000.0扫描增加的数值再改回100.0扫描减少的数值。每执行一次“增加-减少”的循环地址列表的数量都会指数级下降。通常经过2-3个完整的循环地址数量会从最初的数百万减少到几十个甚至几个。4.4 第四步从结果中识别矩阵元素经过几轮筛选后点击扫描按钮旁边的结果按钮查看剩余的地址列表。观察特征在剩下的少数地址中寻找值看起来“有规律”或“像矩阵元素”的Float。矩阵元素通常是在-1到1之间或者绝对值不大的小数如0.5, -0.866, 1.0, 0.0但也可能是投影矩阵相关的缩放系数如640.0, 360.0与屏幕分辨率半宽高相关。更重要的特征是这些地址的值在你修改角色Y坐标时会发生非常明显且同步的数值跳变。你可以通过在地址列表中添加这些可疑地址然后反复修改角色Y坐标观察它们的值是否严格地、大幅度地同步变化来验证。定位矩阵基址假设你找到了一个可疑地址0x1A2B3C4D其值在你操作时从0.0012变到120.0再变回来。右键点击该地址选择找出是什么访问了这个地址。在弹出的窗口中点击找出然后回到游戏稍微移动一下角色触发渲染。如果这个地址确实被游戏代码频繁访问下方会出现访问它的汇编指令。观察这些指令它们通常会是movss xmm0, [raxXX]或mulss xmm1, [rcxXX]这类SSE指令其中[raxXX]或[rcxXX]就是地址。这里的rax或rcx是一个基址寄存器XX是一个偏移量。我们的目标不是分析代码而是找到这个矩阵的基址。在访问指令中如果看到类似[模块名.dll基址偏移]这样的形式那个模块名.dll基址就是矩阵数据结构的起始地址。更简单的方法是直接查看这个可疑地址附近的内存。查看内存区域在Cheat Engine主界面点击菜单查看-内存浏览器或按CtrlM。在内存浏览器中转到地址0x1A2B3C4D。在内存视图中数据默认以16进制显示。点击右键选择显示类型-Float。现在你会看到以4字节为一组的浮点数。一个4x4矩阵在内存中是连续存储的16个Float64字节。常见的存储顺序是“行主序”一行接一行。所以从你找到的可疑地址开始向前或向后滚动寻找一段连续的、看起来有规律包含很多0.0, 1.0, 以及一些其他小数的16个Float值。例如你可能会看到类似这样的序列值仅为示例... 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 100.0, 200.0, 300.0, 1.0 ...这看起来就像一个单位矩阵加上了位移最后一行是平移向量。但这只是模型矩阵或视图矩阵。世界到屏幕的投影矩阵看起来会不同它通常包含类似[0, 0, A, B]和[0, 0, C, D]这样的行其中包含视场角(FOV)、宽高比、近/远裁剪面等信息。如何确认最粗暴的验证方法将这连续的16个Float值作为一个矩阵写一个简单的脚本可以用CE的Auto Assemble或Lua也可以用Python、C写个小程序输入一个已知的世界坐标比如你角色的坐标(X, Y, Z, 1.0)用这个矩阵进行变换看计算出来的屏幕坐标是否与你在游戏中观察到的或通过其他方式获取的大致匹配。如果匹配恭喜你找到了。5. 常见问题、排查技巧与深度优化即使按照步骤操作你也可能会遇到各种问题。这里汇总了一些实战中常见的坑和解决思路。5.1 模糊扫描结果太多或太少问题扫描几轮后地址数量仍然成千上万没有减少到几十个。原因1游戏内其他动态数据如特效粒子、UI动画、物理模拟产生的噪音太大。解决在训练场找一个最安静的地方面向墙壁或空旷天空关闭所有游戏音效和背景音乐减少音频线程干扰确保画面内除了自己和静态地形没有其他活动物体。再进行“修改坐标-扫描”的操作。原因2修改坐标的幅度不够“独特”。从100改到200这种变化可能和很多游戏内正常变化如跳跃、爬坡重叠。解决使用更极端的值如10000和-10000或者999999.0。确保这个变化是游戏正常逻辑下绝对不可能发生的。原因3扫描类型选择错误。在“增加/减少”扫描时要确保与坐标修改方向严格同步。解决每一步操作后确认角色坐标值的变化方向再选择对应的Increased Value或Decreased Value。问题扫描一两轮后地址数量直接变成0。原因1角色坐标地址找错了修改的并不是真正影响渲染的位置坐标。解决回头仔细确认角色坐标地址。尝试找角色的X或Z坐标用同样的方法修改并观察角色是否在水平方向“瞬移”以验证地址有效性。原因2游戏使用了双精度浮点数Double存储坐标或矩阵。解决在CE扫描时将数值类型从Float尝试改为Double然后重新进行模糊扫描流程。Unity和UE4大部分情况用Float但某些特定计算或引擎定制版本可能用Double。原因3游戏有强大的反作弊或内存保护快速修改关键坐标会被检测或修复。解决尝试使用“冻结”值而不是一次性修改。在CE地址列表中找到坐标地址右键点击选择冻结然后输入一个极端值。这样即使游戏尝试重置坐标CE也会立刻把它改回去。然后进行扫描。注意这可能会触发游戏的反作弊机制导致封号请在单机模式或测试服进行。5.2 找到的“矩阵”验证失败问题找到了一段连续的16个Float但用它计算出的屏幕坐标完全不对。原因1找到的可能是其他矩阵如模型本地矩阵、视图矩阵、世界矩阵而不是最终的世界-屏幕投影矩阵。解决世界-屏幕矩阵通常是视图矩阵和投影矩阵的乘积。你可以尝试多找几个可疑的连续Float块。更系统的方法是先找到视图矩阵常与摄像机位置、朝向相关再找到投影矩阵常包含FOV、宽高比参数然后将它们相乘得到世界-屏幕矩阵。我们的模糊搜索法可能直接定位到了这个乘积矩阵也可能定位到的是其中一个因子。原因2矩阵在内存中的存储顺序可能是“列主序”常见于DirectX数学库而你按“行主序”去解析了。解决尝试按列主序来解读这16个Float。即内存中的前4个Float是第一列接着4个是第二列以此类推。用两种顺序都计算一下看哪个结果正确。原因3屏幕坐标系转换问题。游戏可能使用不同的屏幕坐标系原点在左上角还是中心Y轴向上还是向下。解决计算出的屏幕坐标(x, y)通常是齐次坐标或标准化设备坐标NDC范围-1到1。需要经过一步视口变换才能得到像素坐标。公式通常是screenX (x 1.0) * 0.5 * viewportWidth,screenY (1.0 - y) * 0.5 * viewportHeight假设NDC的Y向上屏幕Y向下。你需要根据游戏实际情况调整这个变换。5.3 性能与稳定性优化扫描速度慢在CE扫描设置中扫描设置里可以调整扫描时暂停进程。不要勾选这个选项否则每次扫描都会卡住游戏体验极差且容易崩溃。如果内存区域选择整个内存太慢可以尝试只扫描游戏主模块如GameAssembly.dll,libil2cpp.so,xxxClient-Win64-Shipping.exe等。在搜索区域中选择内存区域-可执行模块。游戏崩溃直接修改角色坐标到极端值有时会导致游戏物理引擎或逻辑异常而崩溃。缓解方案修改坐标后立刻让角色“死亡”或进入一个不会进行物理碰撞检测的状态如观战模式。或者不要修改得过于极端尝试500.0和-500.0这样的值虽然信号弱一些但可能更稳定。反作弊干扰一些游戏会检测Cheat Engine的进程名、窗口名或驱动。可以考虑使用CE的隐藏工具在设置-额外里或者使用修改过的、特征不明显的CE版本需自行寻找注意安全。更高级的反作弊如内核级可能会直接阻止内存读写此时本方法可能失效。5.4 从矩阵元素到完整功能的延伸找到世界-屏幕矩阵只是第一步。要将其转化为实用的功能还需要获取所有实体列表你需要遍历游戏中的实体数组玩家、敌人、物品等获取每个实体的世界坐标通常是Vector3。坐标变换对于每个实体的世界坐标使用找到的矩阵进行变换得到屏幕坐标或NDC。判断可见性变换后的Z值或W值可以用于判断物体是否在摄像机视野内视锥体裁剪。屏幕坐标是否在[0, viewportWidth]和[0, viewportHeight]范围内也是一个简单判断。绘制与交互根据得到的屏幕坐标你可以在游戏画面上绘制方框、线条、文字需要注入DLL或使用外部绘制Overlay。这就是实现“透视”或“方框”的基础。这个过程涉及更深的逆向工程包括找实体列表、遍历逻辑等超出了本文“5分钟定位矩阵”的范围。但有了矩阵这个最关键的钥匙后续的门就更容易打开了。6. 不同引擎Unity vs. UE4的细微差别与应对虽然核心思路通用但Unity和UE4在内存布局上还是有些许不同了解这些能帮你更快地定位和验证。6.1 Unity (IL2CPP / Mono) 游戏矩阵存储Unity中世界-屏幕矩阵通常可以通过Camera.main.worldToCameraMatrix和Camera.main.projectionMatrix相乘得到。在内存中矩阵类Matrix4x4是一个结构体其内部浮点数数组m00到m33是连续存储的。寻找线索除了用模糊搜索直接找最终矩阵你也可以尝试搜索与摄像机相关的数据如摄像机位置Vector3、朝向四元数或欧拉角。修改这些值同样会引起屏幕坐标的剧烈变化可以用同样的模糊搜索法定位然后再顺藤摸瓜找到摄像机对象进而找到其矩阵组件。IL2CPP的挑战IL2CPP将C#代码编译为C优化程度更高函数和数据结构可能更分散。但矩阵数据作为渲染管线的核心输入其存储和访问规律是不变的因此模糊搜索法依然有效。6.2 Unreal Engine 4 游戏矩阵存储UE4中世界-屏幕变换通常在FSceneView结构中完成其中包含ViewMatrices包含视图和投影矩阵。最终的世界-屏幕矩阵是ViewMatrices.GetViewProjectionMatrix()。数据层次更深UE4的对象系统更复杂矩阵数据可能被多层结构体包裹。你通过模糊搜索找到的地址可能是一个FMatrix或FPlane对象的成员。特征值UE4的投影矩阵往往包含非常特定的值如近裁剪面NearPlane、远裁剪面FarPlane和视场角FOV计算出的参数。如果你在内存中看到连续的Float值其中包含像0.75对应4:3宽高比、1.04760度FOV的弧度制一半等有特殊意义的数字那很可能是投影矩阵的一部分。利用SDK和偏移对于热门UE4游戏社区可能已经逆向出了其SDK和偏移量。你可以结合已知的类结构如APlayerController-PlayerCameraManager-CameraCache-POV-ProjectionMatrix来辅助验证你找到的地址是否正确。最后我必须强调本教程分享的技术思路仅供学习和研究游戏引擎原理之用。在在线多人游戏中使用内存修改工具严重违反游戏用户协议会导致账号永久封禁甚至可能承担法律责任。请务必在单机模式、私有服务器或得到明确授权的环境下进行测试尊重游戏开发者的劳动成果维护公平的游戏环境。技术本身是无罪的但如何使用它体现了我们的选择和责任。希望这篇超详细的保姆级教程能帮你打开游戏逆向世界的一扇新窗户从另一个角度理解那些精彩的虚拟世界是如何构建和运行的。
返回列表