CE 修改器是一款本地内存修改工具。它的核心逻辑非常纯粹,即通过扫描和定位程序运行在计算机本地物理内存中的数据,然后对其进行强制修改。虽然它一直以来在游戏逆向和游戏漏洞挖掘(也就是俗称的“开挂”或“修改内存”)中声名鹊起,但它的应用场景远不止于此。我们完全可以使用这款工具去尝试挖掘各类 APP,甚至是 Web 端业务逻辑的漏洞。
关于软件的获取,大家可以直接前往 Cheat Engine 的官方网站下载最新版本并进行无脑安装,安装过程十分简单,本文不再赘述。为了让大家更直观地理解它的强大功能,本教程将随便打开一款游戏作为测试载体,借着这款游戏,手把手带你拆解 CE 的各项核心功能,以及如何利用它去搜索、修改内存数据,并最终转化为漏洞挖掘的实战思路。
一,环境准备与进程附加机制
在使用 CE 修改器进行任何操作之前,最核心的前置条件是“附加进程”。因为内存数据是依附于正在运行的程序进程而存在的。
1.1 寻找目标进程
打开 CE 修改器后,在界面的左上角,你会看到一个带有放大镜图标的小电脑按钮。点击这个图标,会弹出一个“进程列表(Process List)”窗口。这个列表展示了当前计算机上正在运行的所有活跃进程。
1.2 模拟器进程的特殊性
如果我们要测试的是手机游戏或移动端 APP,通常会在电脑上使用安卓模拟器。例如可以使用雷电模拟器。在进程列表中,你需要仔细寻找一个名为 headless 的进程(完整的进程名通常带有特定的前缀,如雷电模拟器可能是 leidian 或 LdBox 等前缀加上 headless)。
不同的模拟器,其进程的前缀可能千差万别,但如果你深入观察,它们的后端渲染或核心运行进程后面,大概率都会带有 headless 这个标识。找到这个目标进程后,直接选中它,然后点击右下角的 Open(打开)按钮,即可成功将 CE 注入并附加到该模拟器的内存空间中。
二,数据类型与搜索模式
附加进程后,CE 的主界面虽然看起来参数众多,但其实入门游戏漏洞挖掘(即修改内存)的逻辑非常简单:在搜索框里输入我们想要修改的数据,然后把它找出来。
2.1 深入理解数据类型
在 CE 搜索界面的右侧,有一个下拉菜单用于选择数据类型。内存世界的数据并不是单一的,它们根据存储需求被划分为不同的类型:
- 四字节 (4 Bytes):这是 CE 的默认选项。在绝大多数游戏和常规应用中,代表数量、状态、金币等常见数值的变量,一般都是使用四字节的整型(Integer)来存储的。大概率我们在测试游戏时,默认的四字节就足够应对 90% 的场景,几乎用不到其他类型。
- 其他类型科普:当然,为了应对复杂的业务逻辑,CE 也支持其他类型。比如八字节 (8 Bytes) 用于存储极其庞大的数值;单浮点 (Float) 和 双浮点 (Double) 常用于存储带有小数点的精确数据(例如游戏人物的坐标 X/Y/Z 轴、复杂的属性加成比例);此外还有专门用于搜索文本的字符串 (String) 类型以及各种各样的数组类型。它们有可能都存在于内存中,但初学者请牢牢锁定“四字节”。
2.2 精确搜索与范围搜索策略
在确认了数据类型后,我们需要决定采用何种扫描类型。CE 提供了极其丰富的条件过滤手段:
- 精确数值:第一个选项,也是最常用的选项。当我们在游戏中明确知道某个数值具体是多少时(例如我现在有 10 个金币),我们就精确搜索这个具体的数字。
- 比...大:当数值被遮挡或模糊化,但你知道它肯定大于某个临界值时使用。例如,你知道当前数值比 10 大。
- 比...小:同理,用于筛选比某个值(如比 10 小)的内存地址。结合上面两项,你可以手动逼近一个范围。
- 介于两者之间:这可以让你直接圈定一个范围区间。例如,你推测某个隐藏属性的数值介于 10 到 100 之间,即可使用此选项进行初步海选。
三,已知数值的精确搜索与“1≠1”
当我们知道目标数值时,搜索看似简单,但实操中会遇到大量干扰。我们将使用下面这个例子来演示这个过程。
3.1 首次扫描的“信息大爆炸”
假设我们现在要搜索一个数值 10。在输入框输入 10 后,点击首次扫描 (First Scan)。
此时,左侧的地址列表会瞬间爆满,可能会扫出几十万个对应的数值来。
- 什么是内存地址? 内存地址就是计算机本地内存条上,用来专门储存这些数据的一个个“房间号”或“坐标”。左边显示的是十六进制的内存地址,右边显示的是该地址当前对应的数值。
这几十万个地址中,只有一个或几个是我们真正需要的,其余的全是模拟器系统中其他应用或系统底层的无关缓存。
3.2 游戏内存中的“1 不一定等于 1”
假设我们在游戏里看到某个道具或状态显示为 1。如果它的数据类型是四字节,并且程序底层逻辑中确实让“显示的 1 等于内存的 1”,那么我们就直接在输入框输入 1,点击首次扫描。
结果非常惊人:左侧可能会出现 500多万个 值全为 1 的内存地址!
- 这第一次搜索的意思是,CE 在整个雷电模拟器(包括里面运行的其他所有后台应用、安卓系统本身以及当前测试的这款游戏)的广袤内存海中,把所有值为 1 的内存地址全部揪了出来。更重要的是,在游戏开发中,
1被极其频繁地用来代表“状态”。比如1代表“开启/激活”,0代表“关闭/取消”。这也是为什么单独搜个 1 能搜出几百万条记录的原因。
3.3 大浪淘沙
面对 500 万个地址,我们不知所措。解决办法是:回到游戏,想办法让这个数值产生变化。
- 数值变成 2:在游戏中操作,让原本的 1 往上增加,变成了
2。 - 再次扫描:切回 CE,在输入框输入
2,点击再次扫描。注意!这不是重新搜索,而是二次过滤。 它的逻辑是:“请帮我把刚才那 500 多万个旧值为 1 的地址中,现在值刚好变成 2 的那些地址筛选出来”。 - 瞬间,地址数量可能会锐减到 2000多个。
- 继续循环:我们将数值继续改成
3(这在测试游戏道具数量、属性加点方面经常用到)。在 CE 中输入 3 继续“再次扫描”,意思是把刚才变成 2 的那两千个地址里,现在变成 3 的筛选出来。此时,地址可能只剩下 17 个。 - 残酷的现实(红字警示):当我们尝试把数值改成
6,并在 CE 中搜索 6 时,左侧列表突然全空了!之前遗留的地址变成了红色(红色代表该内存地址的值在此刻发生了自动改变),但没有任何一个值是 6。
- “在游戏中,1 不一定等于 1。它有可能等于其他任何奇葩的数值。”
四,未知初始值与动态过滤
当“1≠1”的情况出现,或者我们面对的是血条、蓝条、模型大小这种根本没有数字显示的属性时,我们就不知道要搜的值究竟是多少。这时,必须启用未知的初始值扫描模式。
4.1 九亿地址的汪洋大海
在扫描类型中选择“未知的初始值”,这意味着我们要求 CE 把模拟器里所有的内存地址全部找出来。点击首次扫描后,由于没有数值过滤,CE 会抓取海量数据。他们可能是:“个、十、百、千、万、十万、百万、千万、亿……足足 九亿个 内存地址!”
面对这九亿个地址,我们如何定位那个控制特定属性的唯一内存?答案是:寻找变化规律。
4.2 追踪增减规律
假设我们测试的是一个模型大小滑块。
- 我们将滑块从 0 的位置往右拉大(比如拉到了刻度 1 的位置)。虽然不知道底层数字变成了几,但我们确信它的数值变大了。
- 在 CE 中,将扫描类型切换为 “数值增加了”。这也是区别于精确搜索、大于/小于区间的另一种动态逻辑过滤。
- 点击再次扫描,它的逻辑是:“把这九亿个内存地址中,当前值比上一次记录的值变大了的地址全部找到”。
- 瞬间,九亿个地址锐减到了 1000 万个(个十百千万十万百万千万)。
- 继续在游戏中把模型变大,再次使用“数值增加了”进行扫描,不断缩小范围。
4.3 无变化的值的妙用
在不断放大的过程中,假设我们将滑块拉到了最大值 10,无法再超过了。
为了继续筛选,我们可能会把它改回 0,然后再重新拉回 10。
此时,一个致命的逻辑陷阱出现了:
上一次我们扫描的终点状态,滑块是 10。现在我们折腾一圈又拉回了 10。如果此时我们再去搜索“数值变大了”,你会发现找不到目标了!因为当前状态的 10,对比上一次 CE 记录的状态 10,它既没有变大,也没有变小,它是没有改变的!
这时候,就必须祭出 CE 过滤中最核心的一招:无变化的值。
为什么会有这个选项?因为当我们无法确定数值到底变大还是变小时(或者像上述情况兜转回原点时),只要我们知道它相对于上一次扫描没有变化,就可以用它来大清洗。
- 清洗底层噪音:计算机系统中,有无数的内存地址在每一毫秒都在疯狂跳动(系统时间、渲染帧率、网络心跳包等)。比如,我们可以随便搜索一下无变化的值,原本 39000 个地址瞬间就变成了 3900 个!因为那些始终在后台跳动的垃圾数据,直接被“无变化”这个严苛的条件给踢出局了。
- 重复过滤:我们可以不改变游戏内的状态,连续多次点击“再次扫描(无变化的值)”。你会看到它一直在持续剔除那些偷偷发生改变的无关内存地址,直到地址数量彻底稳定下来。
经过反复的“变大(寻找 Increased value)”、“变小(de 开头,寻找 Decreased value)”以及“静止不动(寻找 Unchanged value)”的交叉过滤,最终地址减少的速度会越来越慢,可能卡在 2000 多个地址,再也不像之前那样大幅缩减了。
五,二分法与内存锁定机制
为什么经过这么严密的过滤,还会剩下 2000 多个地址同时在变动呢?
因为某些功能点牵扯到了无数个底层程序。 你拉动一个滑块,不仅改变了模型参数,可能同时触发了 UI 渲染、阴影重算、物理碰撞体积更新等一系列连带反应。这 2000 个地址都在忠实地根据你的拖动而变化。
但控制核心逻辑的源头地址只有一个!我们怎么从这 2000 个长得一模一样的同步变化地址中把它揪出来?
在下面将介绍一种被称为“最笨但也最无解”的方法——二分法内存锁定。
5.1 待修改区域与锁定功能
首,在左侧地址栏按下 Ctrl + A 全选这 2000 多个内存地址。然后点击列表右下方的一个红色对角线小箭头,将这些地址全部转移到底部的**“待修改区域”**。
在这个区域里:
- 双击某个地址后面的数值,可以将其强行改为任意值。
- 更关键的是最前方的方块(锁定框)。一旦勾选锁定,意味着 CE 会以极高的频率冻结这个内存地址。无论你在游戏中怎么拖动、怎么改变状态,这个被锁定的内存地址的数据都将被死死钉住,绝对不会被改变!
5.2 锁定排查实战演练
我们就要利用这个锁定特性来做排除法:
- 最大化状态:首先在游戏中把滑块拉到最大,观察模型大和小的明显视觉变化。
- 锁定一半:在底部的待修改区域,选中最上面的一批内存地址(大概一半),按住
Shift键往下拉选中一段,然后轻轻敲击键盘上的 空格键。瞬间,这批被选中的地址前面都打上了锁定勾。 - 游戏内验证:回到游戏,尝试把滑块往小拉。
- 理论逻辑:如果真正的控制内存被我们锁定了,那么无论滑块怎么动,游戏里的模型应该始终保持“最大”的状态(因为前端数据被冻结了)。
- 实际表现:拉小滑块后,发现模型居然跟着变小了!并没有被影响。这说明什么?说明刚才锁定的那一半地址里,全都是废物,没有我们要找的源头值。
- 无情剔除:既然不是,右键点击这一批地址,选择
Delete(或者直接按键盘快捷键Del),将其直接删除掉。 - 循环逼近:再选中剩下地址中的一半,按空格锁定,回游戏观察。没影响?继续右键 Delete。就这样劈一半、删一半,再劈一半、删一半。
- 曙光乍现:当删到某一批时,再回游戏拖动滑块,发现**“不太对劲了,模型越来越小了,甚至都凹进去了!”** 这说明什么?说明这一次锁定的地址堆里,终于包含了那个真正的核心控制地址。
- 逆向反推:先把凹进去的模型恢复原状取消锁定。既然目标在上面这一批里,那下面剩下的那些地址就全都不用了,直接删掉。
- 终极锁定:在包含目标的这一小堆地址里,继续切分。锁定一半,没反应?删掉。锁定另一半,有反应了!范围缩小到只剩两个地址。两个两个来试,排除掉不是的那俩,删掉。
- 真相大白:最后审视剩下的最后两个内存地址。修改第一个地址,发现游戏画面立刻出现不规则的剧变(“越来越那啥了”)。至此,我们成功揪出了唯一的控制内存!
六,从内存修改到后端业务逻辑漏洞
前面花费了大量篇幅讲解内存搜索与修改,很多人(尤其是纯做 Web 渗透的人)会产生一个巨大的疑惑:“我在本地把内存改了,只有我自己电脑的屏幕上能看到变化(俗称自娱自乐),这到底有什么用呢?”
6.1 案例一:道具购买时的负数绕过漏洞
假设游戏中有一个道具商城,道具有其对应的属性和数量。
正常情况下,我们在商城界面点击购买“一个”道具时,客户端(前端)会在底层组装一个请求,给服务器(后端)传送两个关键数据:
- 道具 ID(比如
1001) - 购买数量(比如
1)
这两个参数,在发送给服务器之前,是实实在在地躺在我们本地内存里的!
如果此时,我们利用上述学过的 CE 搜索技巧,精确找到了储存“购买数量”的那个内存地址,并在它发送请求前,强制把原本的 3 改成了 -3(负三)。
核心爆破点来了:
我们在本地改掉后,再次点击购买按钮。客户端此时抓取的内存数据已经是 -3,它会将这个被篡改的负数原封不动地传给服务器。
如果后端的开发人员在编写业务逻辑时,只校验了“金币是否足够”,却忘记对购买数量进行负数校验,那么服务器就会处理这个 -3。结果往往是:玩家非但没有扣钱,反而因为“扣除 -3 个道具的造价”,导致自己的金币余额瞬间暴涨!这就是利用本地内存修改直接影响前后端数据交互,从而引发的严重业务逻辑漏洞。同理,前面提到的修改人物建模参数,如果后端接收并保存,你就能在全服玩家面前展示出一个突破常规建模的畸形/与众不同的角色。
6.2 案例二:利用隐藏职业 ID 实现越权创建
除了外部数据篡改,内存修改还能窥探和提前利用未开放的功能。
职业切换是一个极其典型的重灾区。假设游戏后端已经实装了下一款大版本更新的职业——“枪炮手”。后端的代码逻辑、数据表都写好了,仅仅是当前的客户端(前端)尚未放出,UI 界面看不到这个按钮。
这时候客户端看不到怎么办?我们依然可以掏出 CE!
- 模糊搜索定位 ID:我们在创建角色界面,反复切换现有的职业。比如先选中弓箭手,使用模糊搜索(未知初始值);然后切换为战士,搜索“变化的值”。通过不断的反复切换、有变化、无变化的筛选,最终在内存中揪出代表“职业 ID”的那个内存地址。
- 寻找工整的规律:这类核心 ID 在游戏里通常设计得非常工整。无论是技能 ID、人物 ID 还是副本 ID,它们绝不是杂乱无章的。比如:战士的职业 ID 是
10101,法师是20101,道士是30101。这是一种高度规范的格式。 - 盲猜与越权发送:掌握了规律后,我们不难推测出那个被隐藏的枪炮手 ID 可能是
40101或某个极其相近的值。我们在找到的内存地址中,将原本战士的 ID 强制篡改为这个隐藏 ID。 - 绝杀:点击“创建角色”按钮!如果后端仅仅依靠前端 UI 来限制玩家,而忘记在接口处做二次校验(鉴权),那么我们向服务器发起的“创建枪炮师”请求就会被绿灯放行。作为一个普通玩家,我们就这样硬生生地越权创建了一个隐藏职业!这在游戏安全测试中,是一个极其经典的漏洞点。
6.3 为什么不用抓包?
讲到这里,很多懂得安全测试的读者一定会抛出一个灵魂拷问:“既然是为了篡改发给服务端的数据,我们用 Burp Suite 或者 Fiddler 直接抓包改包,不是更直接、更简单一些吗?没毛病吧?”
抓包确实更简单,但问题出在网络协议与加密机制上!
- 非 HTTP 协议的阻碍:现在的网游、手游,基本上为了保证通信的低延迟和实时性,极少会走传统的 HTTP/HTTPS 协议,绝大多数都是直接走 TCP 或 UDP 底层协议。传统的 Web 抓包工具对此往往束手无策。
- 加密数据的乱码困境:不仅如此,游戏数据在发送前几乎全被高强度加密了。我们就算用 Wireshark 抓到了包,抓到的也全是一堆毫无规律的乱码。
- 解密门槛过高:如果要走抓包路线,遇到加密,我们要么得自己具备极强的逆向分析能力去写一套解密工具,要么得花钱买解密脚本。大多数人都不会写解密工具,硬着头皮解密极为繁琐和困难。
为什么 CE 能无视这些难点?
因为无论数据在网络传输中被加密成了什么鬼样子,在它刚刚产生于客户端内部、尚未被加密函数处理、还躺在本地物理内存里的那一刻,它一定是明文的! 我们在内存中修改它,相当于在数据加密打包“上车”之前,就把货物给调包了。随后,客户端自己的加密代码会自动帮我们把这份“假数据”完美加密,并走 TCP/UDP 稳稳当当地发给服务器。这是极其优雅的降维打击!
6.4 案例三:突破 APP 加密的直播间越权开播漏洞
这种降维打击不仅适用于游戏,在 APP 和 Web 业务线同样大放异彩。
在测试某些直播平台时,你可能会发现直播间的许多核心功能点没走 HTTP 协议,而是走了 TCP/UDP,并且传输数据全被加密,抓出来的包全是乱码,根本解密不了。没法抓包改包,测试陷入死胡同。
这时候内存测试法就成了破局的唯一希望:越权开播漏洞。
- 我们打开 CE,附加到直播 APP 的进程中。
- 通过数值变化规律,在本地内存里找到了存储“我的直播间房间号”的地址。
- 在点击开播之前,我们将这个内存地址里的数值,强行改成了ID de la sala de transmisión de otro streamer.
- Haz clic en el botón "Iniciar transmisión". El frontend de la aplicación cifró localmente este ID manipulado de otra persona y lo transmitió sin problemas al servidor backend a través de la red.
- Debido a que en ese momento los desarrolladores del backend confiaron excesivamente en el flujo de datos proveniente del frontend (y meticulosamente cifrado),olvidaron realizar una operación de autenticación crucial en el backend (verificar si el UID del usuario que realiza la solicitud tiene permiso para iniciar una transmisión en ese ID de sala), lo que finalmente provocó que se activara la vulnerabilidad: ¡ahora hemos forzado el inicio de una transmisión directamente en la sala de otra persona!
Esto también confirma perfectamente que, al encontrarse en un callejón sin salida con cifrado y protocolos no estándar, la modificación de memoria es, en realidad, un arma divina muy útil en escenarios de negocio específicos.
Siete, no hay truco, solo práctica
¿Qué es un modificador de memoria? ¿Cómo se usa? En esencia, el núcleo se reduce a dos pasos:
- Buscar el valor que quiero encontrar
- Modificar el valor que quiero encontrar
Cheat Engine es solo una espada en tu mano. Los movimientos parecen simples, nada más que "buscar" y "modificar"; pero cuán profundo puede apuñalar esta espada y cuán preciso puede cortar, depende en última instancia de la habilidad del espadachín.
No hay atajos en este camino. Debes ensuciarte las manos, desmontar docenas de juegos, roer la interacción de memoria local de cientos de aplicaciones, examinar repetidamente y cometer errores en cantidades masivas de datos, hasta aproximarte a la única respuesta correcta desde el caos. El proceso a menudo es tedioso, pero precisamente por eso, cuando la verdad emerge, es aún más fascinante.