ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI辅助游戏内存数据分析:从零实现进程内存读写

AI辅助游戏内存数据分析:从零实现进程内存读写 先用一句话说清楚这篇文章不是教大家写外挂而是讲解如何借助 AI 编程能力对一个自建的测试程序进行内存数据分析和修改。文中所有示例都基于你自己编写的 C 语言控制台程序不涉及任何商业游戏、在线游戏也不提供任何作弊功能。搞清楚这个边界之后再来看技术本身。鉴权、进程通信、内存模型、指针偏移、逆向分析这些都是后端开发、安全研究和 AI 应用开发中很有价值的基础能力。当你把这些底层能力与 AI 编程结合起来时其实可以大幅提升分析效率。本文将从概念、环境、原理、实战、排查到最佳实践完整拆解这套流程。1. 背景与核心概念1.1 什么是游戏数据逆向分析游戏数据逆向分析简单来说就是对一个游戏进程在运行时产生的数据进行监测、定位和解析搞清楚某个数值比如角色生命值、金币数量、坐标位置在内存中是如何存储的以及如何通过外部程序读取或修改这些数据。从专业角度定义它属于软件逆向工程的一个子方向核心研究对象是“运行中的进程内存”。操作系统会为每个进程分配独立的虚拟内存空间游戏程序中的变量、对象、资源都会映射到这块空间的某个地址上。我们做逆向分析本质上就是在做这样几件事定位目标数据的内存地址。分析地址之间的偏移关系基址 偏移。解析数据结构的类型和布局。验证读写的稳定性和正确性。这里需要区分两个概念静态地址和动态地址。静态地址指的是每次启动程序时固定不变的地址一般对应全局变量动态地址则是每次启动时变化的堆内存地址如果程序没有提供指针光靠重新搜索很难稳定定位。1.2 AI 编程在逆向分析中的定位AI 编程并不是替代逆向分析工程师而是充当“结对编程助手”。在我实际的体验中AI 对这三类工作帮助最大脚手架代码生成比如用 Python 封装 Windows API、写内存扫描器、写偏移计算器这些结构性代码非常适合 AI 生成。反汇编片段解释把一段汇编命令丢给 AI让它解释寄存器变化和栈帧结构比自己翻手册快得多。批量脚本重构当分析结果中有大量重复性的数据解析逻辑时可以让 AI 快速生成批量脚本。不过要明确一点AI 并不理解内存布局的隐藏语义它不能替你判断某个地址到底是什么数据也不能替你做权限审查。AI 生成代码之后你必须理解每一行在做什么。1.3 适用场景与合法边界适合用这套技术的学习场景包括学习操作系统进程管理与内存管理机制。学习 Windows API 的底层调用方式。对自研程序做外部自动化测试。安全研究中的漏洞分析与补丁对比。单机游戏存档分析和数值机制研究。不合法的场景也很明确修改在线游戏数据、制作外挂、干扰其他玩家的游戏体验、破解商业软件授权、绕过安全机制。这些行为不仅违反用户协议还可能触犯法律。本文后面的所有示例都只针对自建的测试程序请大家务必在合法范围内实践。2. 环境准备与核心工具2.1 开发环境要求本教程的示例使用以下环境读者可以根据自己的实际情况调整版本工具说明操作系统Windows 10 或 Windows 11Python3.8 及以上推荐 3.10编译器MinGW-w64 或 Visual Studio用于编译测试程序代码编辑器VS Code / PyCharm任意一款即可辅助工具Process Explorer查看 PID、Cheat Engine可视化分析参考如果你没有安装 MinGW-w64可以使用 Visual Studio 的开发者命令行或者直接用在线 C 编译器生成 exe 后放到本地运行。示例重点是 Python 代码C 测试程序只是提供一个“分析目标”。2.2 Python 依赖本示例以 Python 标准库ctypes为主不需要额外安装第三方库。ctypes是 Python 调用 C 语言动态库的标准方式通过它可以直接调用 Windows API。如果你希望使用更简易的封装可以了解pymem这个社区维护的库它把 OpenProcess、ReadProcessMemory、WriteProcessMemory 等 API 封装得更友好。不过为了讲解原理本文全部用ctypes手写底层调用。2.3 为什么选择 ctypes 而不是 C/C很多做内存分析的程序员会选择 C/C 直接调用 API性能更高。但在“分析和验证思路”阶段Python 的开发效率明显更高原因有三个Python 不需要编译改写后立即运行。ctypes 可以动态定义结构体解析内存布局很方便。AI 对 Python 代码的生成质量比 C/C 更稳定联想更准确。性能瓶颈一般出现在大量扫描场景这时可以用 C 实现核心扫描器再通过 Python 调用。但作为入门教程先掌握 Python 版本已经足够。3. 游戏内存数据基本原理3.1 进程虚拟内存与地址空间每个进程在 Windows 中都有自己的虚拟地址空间。32 位进程的地址空间通常是 4GB64 位进程则大得多。这块空间被划分为多个区域代码段、数据段、堆、栈等。当游戏程序运行时你在界面上看到的“角色血量 100”这个 100 在内存中通常是一个 4 字节的 int 类型数据存放在堆或全局数据区。外部程序要修改它必须通过 Windows API 向目标进程发起读写内存的请求。操作系统设计这样的隔离机制是为了避免一个进程随意破坏另一个进程的地址空间。因此跨进程内存操作一定要使用系统提供的 API 来做合法性校验。3.2 定位数据地址的两步走要修改一个数据第一步是找到地址第二步是验证稳定性。搜索方式一般有两种第一种是“数值变化扫描”。你先记录当前数值 A让程序运行到数值变成 B 后暂停再扫描满足变化的地址区间。重复几次就能筛选出唯一地址。很多可视化工具如 Cheat Engine就是用这种思路工作的。第二种是“特征码定位”。如果你知道目标数据附近有一串固定字节例如一条特定的汇编指令可以通过扫描特征码来定位。这种方式适合动态地址每次都变化但代码段固定不变的情况。3.3 通过 Windows API 读写进程内存Windows API 提供了几个核心函数它们都位于 kernel32.dllOpenProcess打开指定进程并获取句柄。ReadProcessMemory读取进程指定地址的内存数据。WriteProcessMemory向进程指定地址写入数据。CloseHandle关闭句柄。下面我们用 Python ctypes 封装这些函数形成一个通用的内存操作类。4. AI 辅助逆向分析实战4.1 AI 在逆向分析中的协作方式在开始写代码之前我想先展示一下 AI 是如何参与这个过程的。比如我们可以给 AI 一段提示词请用 Python ctypes 封装 Windows APIOpenProcess、ReadProcessMemory、WriteProcessMemory。 要求 1. 支持 64 位进程。 2. 设置 argtypes 和 restype避免指针截断。 3. 具备错误码获取权限不足时抛出具体异常。 4. 提供 read_int、write_int、read_buffer、write_buffer 四个方法。AI 会生成一份结构完整的基础代码。你不需要让 AI 一次性写完所有内容而是把目标拆成“进程操作”“地址扫描”“结构体解析”三类分别生成再手动拼接。下面来看一个完整的封装效果。4.2 AI 生成的进程内存读写封装创建一个memory.py文件# 文件路径memory.py import ctypes from ctypes import wintypes kernel32 ctypes.WinDLL(kernel32, use_last_errorTrue) # 权限常量 PROCESS_QUERY_INFORMATION 0x0400 PROCESS_VM_READ 0x0010 PROCESS_VM_WRITE 0x0020 PROCESS_VM_OPERATION 0x0008 # 设置函数签名避免 64 位指针截断 kernel32.OpenProcess.restype wintypes.HANDLE kernel32.OpenProcess.argtypes ( wintypes.DWORD, wintypes.BOOL, wintypes.DWORD, ) kernel32.ReadProcessMemory.argtypes ( wintypes.HANDLE, wintypes.LPCVOID, wintypes.LPVOID, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t), ) kernel32.ReadProcessMemory.restype wintypes.BOOL kernel32.WriteProcessMemory.argtypes ( wintypes.HANDLE, wintypes.LPCVOID, wintypes.LPCVOID, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t), ) kernel32.WriteProcessMemory.restype wintypes.BOOL kernel32.CloseHandle.argtypes (wintypes.HANDLE,) kernel32.CloseHandle.restype wintypes.BOOL class ProcessMemory: 进程内存读写工具类 def __init__(self, pid): self.pid pid self.handle None def open(self): 打开进程并获取句柄 self.handle kernel32.OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, False, self.pid, ) if not self.handle: raise ctypes.WinError(ctypes.get_last_error()) return self.handle def close(self): 关闭进程句柄 if self.handle: kernel32.CloseHandle(self.handle) self.handle None def read_memory(self, address, size): 读取任意大小的内存块 buffer ctypes.create_string_buffer(size) bytes_read ctypes.c_size_t(0) ok kernel32.ReadProcessMemory( self.handle, ctypes.c_void_p(address), buffer, size, ctypes.byref(bytes_read), ) if not ok: raise ctypes.WinError(ctypes.get_last_error()) return buffer.raw[: bytes_read.value] def write_memory(self, address, data: bytes): 向指定地址写入字节 buffer ctypes.create_string_buffer(data) bytes_written ctypes.c_size_t(0) ok kernel32.WriteProcessMemory( self.handle, ctypes.c_void_p(address), buffer, len(data), ctypes.byref(bytes_written), ) if not ok: raise ctypes.WinError(ctypes.get_last_error()) return bytes_written.value def read_int(self, address): 读取 4 字节整数 data self.read_memory(address, 4) return int.from_bytes(data, byteorderlittle, signedTrue) def write_int(self, address, value): 写入 4 字节整数 data value.to_bytes(4, byteorderlittle, signedTrue) self.write_memory(address, data)这段代码有几个关键点值得解释kernel32.OpenProcess.restype一定要设置为wintypes.HANDLE否则 64 位系统上句柄会被截断成 32 位引发奇怪错误。ReadProcessMemory的地址参数类型要设置成wintypes.LPCVOID它本质上是指针类型确保 64 位地址能完整传递。int.from_bytes(..., signedTrue)用于把原始字节转成带符号整数玩家血量可以是正数但某些偏移量可能是负值所以保留符号更稳妥。4.3 AI 生成测试代码时的排错要点AI 生成的代码不一定一次就能跑通常见问题包括忘记设置restype和argtypes导致 64 位地址被截断。使用 32 位 Python 去读写 64 位进程会报access denied。没有用管理员身份运行 Python导致OpenProcess返回 5拒绝访问。权限标志不全只申请了PROCESS_VM_READ写入时依然会失败。所以在把 AI 代码接入真实场景前建议先做一层“权限检查”和“地址校验”确认句柄有效、地址范围合理后再执行读写。5. 完整案例自建 C 程序内存数据分析为了让整个流程可复现且完全合法我们使用一个自建的 C 语言控制台程序作为目标进程。这个程序非常简单但足以演示内存定位、读取、修改的完整链路。5.1 编写目标测试程序创建一个target.c文件// 文件路径target.c #include stdio.h #include windows.h int main() { int hp 100; int gold 50; int score 0; printf(PID: %d\n, GetCurrentProcessId()); printf(hp address: %p\n, hp); printf(gold address: %p\n, gold); printf(score address: %p\n, score); while (1) { printf(hp %d, gold %d, score %d\n, hp, gold, score); Sleep(1000); score 1; } return 0; }用 MinGW-w64 编译gcc target.c -o target.exe运行后终端会输出类似下面的信息PID: 12345 hp address: 0x61FE0C gold address: 0x61FE08 score address: 0x61FE04请记录下 PID后面 Python 程序需要用到。5.2 用 Python 读取目标进程数据创建一个reader.py# 文件路径reader.py from memory import ProcessMemory if __name__ __main__: pid int(input(请输入目标进程 PID: )) hp_addr int(input(请输入 hp 地址十六进制: ), 16) pm ProcessMemory(pid) try: pm.open() hp_value pm.read_int(hp_addr) print(f[读取] hp {hp_value}) finally: pm.close()运行python reader.py输入 PID 和地址程序会输出当前的生命值。此时能看到终端里 C 程序的 hp 值稳定在 100。5.3 用 Python 修改目标进程数据# 文件路径writer.py from memory import ProcessMemory if __name__ __main__: pid int(input(请输入目标进程 PID: )) hp_addr int(input(请输入 hp 地址十六进制: ), 16) new_value int(input(请输入要修改的值: )) pm ProcessMemory(pid) try: pm.open() pm.write_int(hp_addr, new_value) print(f[写入] hp 已修改为 {new_value}) finally: pm.close()运行后回到 C 程序的终端可以看到 hp 已经变成了新值。这就完整演示了“游戏内存数据是如何修改的”这一过程。5.4 动态地址与偏移计算实际游戏中hp 往往存放在堆内存中地址每次启动都会变。模拟方式是让 C 程序使用 malloc 分配内存int *hp (int*)malloc(sizeof(int)); *hp 100; printf(hp address: %p\n, hp);这种情况下直接靠编译期地址就无法定位了。通常的做法是通过 CE 一类工具手动查找到 hp 的基址。记录从某个模块基址到 hp 的偏移量。每次启动时动态获取模块基址再计算最终地址。用 Python 读取模块基址需要遍历进程模块快照代码量会明显增加这里只作思路说明。建议初学者先用固定地址把完整链路跑通再逐步研究偏移量。5.5 AI 在案例中发挥的作用在这个案例中AI 可以帮助生成扫描脚本、结构体定义、异常处理逻辑。比如你可以让 AI 生成“遍历进程模块列表”的代码也可以让 AI 解释为什么要用ReadProcessMemory而不是直接解引用指针。AI 的定位是“加速器”而不是“自动驾驶”。6. 常见问题与排查思路在实际操作时很多读者会遇到下面这些问题。我整理了若干高频现象和对应排查思路。问题现象常见原因解决思路OpenProcess 返回错误码 5权限不足未以管理员身份运行用管理员权限打开终端再运行 PythonOpenProcess 返回 87参数错误PID 无效检查 PID 是否为目标进程先确认 PID 存在ReadProcessMemory 读取失败最后错误码为 29964 位进程中使用了 32 位句柄重新编译为 64 位 Python或调整 ctypes 签名读取出的数据始终是 0地址不是数值的准确存储位置用目标程序输出的地址严格比对确认无输入错误写入后目标程序崩溃写入的数据类型大小不匹配确认变量是 int 还是 unsigned int是否多写了字节AI 生成的代码无法运行依赖缺失或 Python 版本不兼容先运行最小示例验证例如print(ctypes.WinDLL(kernel32))32 位 Python 访问 64 位进程失败位数不匹配统一 Python 和编译程序均为 64 位排查时有一个推荐顺序先确认权限 → 再确认 PID → 再确认地址 → 再确认数据类型。不要一上来就怀疑是 API 调用错误。7. 最佳实践与工程建议7.1 安全边界意识无论技术多有趣安全边界必须放在第一位。请坚持以下原则只对自建程序或明确授权的研究对象进行分析。不将任何内存修改技术用于在线游戏不制作外挂。不绕过程序的授权校验、登录校验等防护机制。发布代码时要隐去真实域名、密钥、个人信息等敏感内容。7.2 代码层面的工程化建议在实际项目中不建议把内存读写逻辑散落在各处。建议按照这样的结构组织project/ ├── memory.py # 底层 API 封装 ├── scanner.py # 地址扫描与偏移计算 ├── structure.py # 数据结构定义与解析 ├── main.py # 业务流程控制 └── logs/ # 运行日志在memory.py中重点关注异常处理。跨进程读写失败时需要快速定位失败原因建议封装统一的异常类型class ProcessMemoryError(Exception): 进程内存操作异常 def __init__(self, message, error_codeNone): super().__init__(message) self.error_code error_code7.3 使用日志记录分析过程逆向分析是一个反复试错的过程建议每一步都留下日志。比如记录扫描了哪些地址区间、读取到多少候选值、最终确认的偏移量是多少。这样即使隔天回来看也能快速接续思路。最简单的做法是使用 Python 自带的logging模块import logging logging.basicConfig( levellogging.INFO, format%(asctime)s [%(levelname)s] %(message)s, filenameanalysis.log, )7.4 AI 协作时如何写提示词想让 AI 生成高质量代码提示词要包含足够多的约束信息。建议包含这几个要素目标语言和框架Python、ctypes、Windows API。输入输出定义PID、地址、数据长度。关键函数签名要求argtypes、restype。错误处理方式抛异常还是返回错误码。禁止事项不要读取未知地址不要写日志到敏感路径。一条示例提示词请用 Python 编写一个读取 Windows 进程内存的代码片段。 输入进程 PID、内存地址、读取字节长度。 输出字节串。 要求 1. 使用 ctypes 调用 kernel32.dll。 2. 处理 64 位指针截断问题。 3. 当进程权限不足时抛出包含错误码的异常。 4. 不要使用第三方库。7.5 从“能用”到“稳定”一次性读写成功不代表方案稳定。实际应用中还要考虑目标进程是否可能重启重启后地址是否变化。模块基址在系统版本变化后是否不同。读取的数据是否受多线程并发修改影响。长时间运行是否引发内存泄漏句柄未关闭。建议增加地址有效性校验和句柄生命周期管理确保每次操作完成后都释放句柄。8. 总结与学习路线本文从概念开始解释了游戏数据逆向分析的本质是进程内存数据定位与读写并重点说明了 AI 编程在这个过程中的协作方式AI 负责生成脚手架代码、解释反汇编片段、批量重构工具函数而开发者负责理解原理、确认边界、验证数据。文中完整演示了如何用 Python ctypes 封装 Windows API 的 OpenProcess、ReadProcessMemory、WriteProcessMemory并给出了针对自建 C 程序的内存读取与修改示例。如果你希望进一步深入可以按下面的路线图继续学习。第一站学习 Windows 进程与模块的基础知识理解 PE 文件结构、虚拟内存、进程句柄等概念。第二站研究指针扫描与偏移计算掌握多级指针的解析方法。第三站学习调试器原理理解硬件断点、软件断点、单步跟踪等方式。第四站再回到 AI 编程你会发现 AI 在汇编解释、结构体推测、脚本自动化方面的价值会进一步放大。最后再强调一次边界请把这套技术用于学习、研究和自建程序测试。不要触碰在线游戏修改、商业软件破解等违规场景。保持好奇心、守住边界才能让技术在正确的轨道上创造价值。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进