RPG Maker MV 做出来的游戏,在逆向眼里基本属于“半开门”的状态。前阵子接手一个基于 MV 引擎的小体量游戏,需求是分析它的核心养成数值和道具掉落逻辑,顺带把加密的存档结构理清楚。整个过程走下来,最深的感受是:MV 系游戏与其说是在“逆向”,不如说是在“整理”——整理文件结构、整理加密方式、整理脚本逻辑。真正有技术含量的部分在于,你得知道每一步去哪找答案。
这篇文章先把完整思路记录下来,后面如果还有机会碰到 MV 引擎的其他变种(比如 MZ,或者套了壳的版本),再单独写思路二。先把这一套静态拆解的流程讲透。
1. 确认引擎与技术栈:为什么先做识别而不是直接翻文件
拿到任何一个游戏客户端,我做的第一件事永远是“确认引擎”。这不是走流程,而是因为引擎决定了后面所有的分析路径。RPG Maker MV 有一个非常鲜明的特征:游戏目录里必然有一个www文件夹,里面躺着index.html,打开之后是一整套基于 Web 技术的游戏循环。如果是 MZ 版本,结构几乎一样;如果是 VX Ace 或者 XP,那就是纯 Ruby 脚本的天下,思路完全不同。所以第一步花几分钟做识别,能避免后面浪费几个小时走错路。
识别方法其实很简单。先看目录里有没有www文件夹,有的话十有八九是 MV/MZ;再打开www/index.html,如果源码里引用了rpg_core.js或者main.js,那就基本确定了。MV 版本的index.html会直接加载js/rpg_core.js,而 MZ 版本用的是rmmz_core.js。这一步连工具都不用,鼠标点开就能确认。另外一个小技巧是看package.json,MV 早期版本会带上这个文件,里面会写"name": "RPGMV",一眼就能认出来。
这套引擎能成为逆向圈子的“入门教材”,核心原因在于它的技术栈太透明了。MV 的游戏逻辑全部跑在 JavaScript 上,而数据层是 JSON,本质就是一个跑在浏览器环境里的单页应用。没有编译过程、没有二进制混淆、没有原生代码加固——你看到的 JS 就是引擎原始代码和作者自己写的插件脚本,全裸奔。甚至连加密都可有可无,很多小游戏作者压根不加密,直接 JSON 明文躺在data目录下。
确认引擎之后,第二个动作是确定版本号。MV 从 1.0 到 1.6.1,核心代码有细微差别,但大结构不变。查看版本号有个快速方法:打开www/js/rpg_core.js,在文件顶部注释里通常会写RPG Maker MV version之类的字样。确定版本的主要意义在于,你在网上搜到的很多插件、教程和工具都标注了版本兼容范围,知道自己用的是哪个版本,就能快速过滤有效信息。
整个识别过程大概需要五到十分钟,但这十分钟决定了后续工作的方向。以 MV 为例,一旦确认是“Web + JSON + JS”的架构,你的逆向工具箱就完全变了:不再需要 IDA、x64dbg 这一类的原生调试器,而是直接切换到 Chrome DevTools、Node.js、Python 脚本上来。
2. 文件结构与加密机制:找到那把锁,再看看锁芯长什么样
MV 游戏的文件结构非常标准化,就像乐高积木一样,每一块放在哪里都固定。核心是www目录,底下有这么几个关键子目录:
| 目录/文件 | 作用 | 分析价值 |
|---|---|---|
www/index.html | 游戏入口 | 确认加载顺序和加密参数 |
www/js/ | 引擎脚本与插件脚本 | 最核心,所有逻辑都在这里 |
www/data/ | 游戏数据库(JSON) | 角色、技能、道具、地图等配置 |
www/img/ | 图片资源 | 立绘、图标、地图块,解密后可直接查看 |
www/audio/ | 音频资源 | BGM、音效,同样是加密重灾区 |
www/save/ | 存档位置(运行时生成) | 不随客户端打包,但格式固定 |
把目录结构摸完之后,下一步就是看加密。MV 提供了一个可选的加密方案,用于保护图片和音频资源。如果作者开了这个功能,img和audio目录下的文件后缀会变成.rpgmvp和.rpgmvm,而不是常规的.png、.ogg。这些文件不是整体加密,而是在原始文件头部加了一个固定的头信息,再用加密算法处理。
判断是否加密,方法非常直观:打开www/data/System.json,看里面hasEncryptedImages和hasEncryptedAudio这两个字段是不是true。如果是,说明资源被加密了,需要先解开才能提取素材。同时,这个文件里还有两个关键字段:encryptionKey和signature,它们决定了你能否解密。encryptionKey是一个 32 位长度的字符串,signature则是用来校验数据完整性的。
MV 的加密算法有历史演变。早期版本用的是 AES-256-CBC,后来因为兼容性问题和加载性能,1.6.0 之后的版本改成了 RC4 流加密。解密原理不复杂:游戏运行时,rpg_core.js里的Decrypter类会读取encryptionKey,截取前 16 位(key.slice(2, 34)之类的操作)作为实际密钥,然后对.rpgmvp文件头部的特定长度数据做解密,还原出真实的文件头,剩下的数据块再按算法解出来。
对于想做素材提取或者资源替换的人来说,这个加密算是“纸糊的锁”。网上有不少现成工具,比如 GitHub 上的rpgmvp_decrypt、RPGMVDecrypt之类的脚本,原理都是把System.json里的 key 抽出来,然后用对应的算法把所有加密文件批量还原。真正需要动脑的反而是那些做了二次加工的游戏——有些作者会故意改掉rpg_core.js里的解密逻辑,或者在index.html里做文章,导致标准工具失效。碰到这种情况,就得手动定位Decrypter类,看它到底改了什么。
3. 资源解密实操:从 System.json 到批量还原图片音频
这一节直接上实操。我们假设拿到的游戏开了加密,目标是把图片和音频全部还原成可查看、可替换的明文文件。整体分三步走。
第一步:提取密钥和签名。用任何文本编辑器打开www/data/System.json,找到encryptionKey和signature两个字段。正常情况下,encryptionKey是一串 32 字节的十六进制字符串,signature是一段 base64 编码。这两个值先存到一个临时文件里备用。顺便提一句,经常有人拿到的游戏是没加密的,那这一步可以直接跳过,img和audio目录里的文件直接就是原图原声。
第二步:写解密脚本。下面的脚本思路是通用的,无论是 AES 还是 RC4,核心逻辑一致:读取加密文件头部,用密钥解密前 16 字节的头部信息,还原出真实文件头,然后再把解密后的头部和剩余密文拼接起来写回新文件。下面给一个伪代码级别的示例(以早期 AES 版本为例):
import base64 import struct from Crypto.Cipher import AES def decrypt_rpgmvp(filepath, key_hex): # key 需要从 hex 字符串转成字节 key = bytes.fromhex(key_hex[2:]) # 去掉开头可能的 0x 前缀 cipher = AES.new(key, AES.MODE_CBC, IV=bytes(16)) # IV 通常是全零 with open(filepath, 'rb') as f: data = f.read() # 头部固定有一段明文元信息,先解出真实文件头长度 header_size = struct.unpack('<I', data[0:4])[0] decrypted_header = cipher.decrypt(data[4:4+header_size]) # 真实文件 = 解密后的头部 + 剩余密文 real_content = decrypted_header + data[4+header_size:] output_path = filepath.replace('.rpgmvp', '.png') # 按实际类型改后缀 with open(output_path, 'wb') as f: f.write(real_content)如果你搞不清到底是 AES 还是 RC4,最快的办法是打开www/js/rpg_core.js,搜索Decrypter,看createInMainThread函数里面调用的是AESDecrypt还是RC4Decrypt。名字写得很直白,一看便知。RC4 版本的解密脚本网上多如牛毛,自己写也简单,就是一个ARC4类,输入 key 循环异或。
第三步:批量处理。写个小脚本遍历img和audio目录,把所有.rpgmvp文件改名还原为.png,把.rpgmvm还原为.ogg(也可能是.m4a),然后跑一遍解密。成功后你会发现,图片能直接打开了,音频能直接拖进剪辑软件了。到这一步,素材层的“壳”已经被脱掉,剩下的是更核心的——数据与逻辑层。
这里必须提醒一个坑:有些作者为了保护资源,会在rpg_core.js里把encryptionKey加密存放,再用一段自定义函数动态解密出真正的 key。遇到这种情况,静态搜索 key 不好使了,得改用动态调试。做法是:用 Chrome 打开index.html,在Decrypter初始化处下断点,运行时在内存里把最终的 key 打出来。这部分涉及动态调试,属于思路二的范畴,这篇文章里点到为止。
4. 数据层的“明文宝库”:System.json、Actors.json、Items.json 能告诉你什么
资源解开之后,真正好戏才开始。MV 的数值配置全部在data目录下,清一色 JSON 文件。这些文件在未加密状态下,就是纯文本,漂亮得让人感动。它们的命名有严格规律:System.json(系统配置)、Actors.json(角色)、Classes.json(职业)、Skills.json(技能)、Items.json(道具)、Weapons.json(武器)、Armors.json(防具)、Enemies.json(敌人)、Troops.json(敌群)、Map001.json这类以数字命名的文件(地图数据)。这些 JSON 的字段结构在 MV 引擎内部有一套固定规范,网上也有现成的文档可以直接对照。
以Actors.json为例,每个角色对象长这样:
{ "id": 1, "name": "主角", "classId": 1, "initialLevel": 1, "maxLevel": 99, "traits": [ {"code": 22, "dataId": 0, "value": 100} ], "params": [ [100, 110, 120], ... ] }params数组里存的是各个等级下的基础属性成长值,每一行对应一个属性(最大HP、最大MP、攻击、防御、魔攻、魔防、敏捷、幸运),每列对应一个等级。你想知道某个角色 30 级时的攻击力是多少?直接读第 2 列第 3 行就完事了。这套数据模型极其直白,几乎不需要任何逆向技巧,纯粹是“会读 JSON 就会提取数据”。
Items.json更典型,它直接暴露了物品的完整定义:
{ "id": 10, "name": "回复药水", "itypeId": 1, "price": 200, "tpGain": 10, "hpRecovery": 300, "effects": [ {"code": 11, "dataId": 0, "value": 1} ] }effects数组里的code对应 MV 的伤害/效果类型表,dataId和value分别是目标参数和数值。比如code: 11表示“恢复 HP”,value: 300就是恢复量。配合一张效果类型对照表,你就能把整个游戏的物品数值全部反推出来。
地图文件也很有看头。每个MapXXX.json都包含events(事件)、data(地图块数据)等字段。重点提一下events,它记录了地图上所有交互点(宝箱、NPC、剧情触发点)的坐标和条件。比如某个宝箱里放的物品 ID 是多少、打开条件是拿到什么钥匙、事件触发后会不会调用公共事件,全在这里面。想快速摸清一个游戏的隐藏内容分布,直接扫events里面code: 122(物品奖励)和code: 121(开关控制)的类型就够了。
数据分析到这一步,游戏的核心数值、经济系统、掉落池基本就全在你手上了。对于做数值分析、游戏评测或者二次创作的同行来说,到这里信息已经足够。如果需求还涉及到“动态修改内存数值”或者“破解联网校验”,那就得往脚本逻辑层继续挖。
5. 脚本逻辑定位:从 main.js 到插件代码,快速锁定关键函数
MV 的游戏逻辑可以分成两层:引擎层和插件层。引擎层是rpg_core.js、rpg_objects.js、rpg_scenes.js、rpg_managers.js、rpg_sprites.js、rpg_windows.js这一套“rpg_”开头的文件,它们是整个游戏的骨架;插件层是js/plugins.js注册的独立脚本,通常是作者自己写的或者从社区拿来的第三方插件,文件放在js/plugins/目录下。绝大多数“定制功能”都在插件层里,引擎层很少被魔改。
定位关键函数的效率方法,是养成“搜字符串”的习惯。MV 游戏里的数据、提示、选项,都是以明文形式存在于 JS 文件的字符串常量中。举个例子,你想找“战斗结束后金币翻倍”的逻辑,可以先在plugins文件夹里搜“金币”,搜不到再搜“gold”或者“money”。字符串搜索是最高效的定位手段,没有之一。
碰到逻辑藏在引擎层的情况,也别怕。MV 的事件系统是“事件指令”制,地图上的每个 NPC、宝箱、机关背后都是一系列事件指令,而这些事件指令最终会调用Game_Interpreter类的方法。Game_Interpreter定义在rpg_objects.js里,它内部有一个巨大的switch (command.code)分支结构,每一种 code 对应一种指令行为。比如code: 122是“增减物品”命令,code: 101是“显示文字”命令。当你需要精确阻断某段逻辑时(比如想跳过某个 Boss 战),可以搜索Game_Interpreter.prototype,然后在对应 code 的 case 分支里做文章。
另一个值得关注的类是DataManager(在rpg_managers.js里)。它负责加载所有 JSON 数据。如果你是做 MOD 的,可以在DataManager.loadDatabase这个函数里动态注入自定义数据。比如新增一个道具、修改一个敌人的属性,不用去改原文件,直接在加载完成后改内存里的$dataItems数组就完事。这种“运行期注入”的方式,比改文件更灵活,也便于调试和回滚。
插件层的分析也不难。MV 的插件文件头部通常有一大段注释,用/ *: * @plugindesc之类的格式写明插件用途、参数和版本。如果作者没有粗暴地把注释删掉,你甚至不用读代码,光看头注释就知道这个插件负责什么。对于做逆向分析的人来说,插件列表(plugins.js)就是一张“功能地图”,先把地图轮廓画出来,再慢慢往里面填细节。
6. 常见问题与排查技巧:我在这条路上踩过的坑
整个流程走下来,难免会遇到一些莫名其妙的问题,尤其是不同作者会用不同姿势“魔改”引擎。把这段时间踩过的坑整理成速查表,再把典型场景展开聊一下,能帮后来人省下不少时间。
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 资源解密后图片花屏/打不开 | 加密算法判断错误(把 RC4 当 AES) | 回rpg_core.js查Decrypter类实际调用的算法函数 |
找不到encryptionKey | System.json 被二次加密或 key 动态生成 | 用 DevTools 下断点,运行时从内存取 key |
| 改完 JS 后游戏白屏 | 文件编码或语法错误,浏览器直接 JS 报错 | 打开浏览器控制台(F12),看具体报错行 |
| 存档无法解密 | 存档用了StorageManager自定义加密 | 搜索StorageManager类的加密方法,走一遍它的加解密流程 |
| 地图事件找不到宝箱逻辑 | 事件被公共事件(Common Events)远程调用 | 在CommonEvents.json里按被调用 ID 索引查 |
| 插件报错但不知道是哪个插件 | 某个插件不兼容当前 MV 版本 | 检查plugins.js里插件参数是否符合插件头注释的格式要求 |
其中,存档解密是大家问得最多的问题。MV 的存档也有“加密”,但它用的是浏览器localStorage或Web Storage机制,不是传统意义上的文件加密。StorageManager类的逻辑是:把存档对象序列化成 JSON,然后用LZString压缩,再用一个可选的 xor 混淆或 AES 加密写入 localStorage。想解密存档,最简单的方法也和运行时相关——打开游戏,在 DevTools 的 Console 里调用StorageManager.loadGame相关的接口,系统会直接返回对象,规避掉手动解密步骤。
还有一个高频坑:改了rpg_core.js里某段逻辑后,游戏直接卡死或逻辑错乱。这通常是因为你改的时候没有遵循 MV 的“原型链覆盖”约定。正确做法是在插件文件里用Game_Interpreter.prototype.xxx = function() { ... }这种形式覆盖原方法,而不是直接改引擎文件。直接改引擎文件不是不行,但会让后续维护变得极其痛苦,而且一旦引擎版本升级,改动就全废了。我个人的习惯是:所有改动一律以插件方式加载,哪怕是临时测试代码,也放进plugins.js里注册,这样出了问题能一键禁用,快速定位。
7. 这套逆向思路的适用边界与后续扩展方向
文章最后聊两句这套思路的适用边界。整套静态分析流程,最快的情况下能在一两个小时之内完成从拿到包到提取出完整数据和内容的全部步骤,但它并不是万能的。
这套思路只适用于“纯正”的 RPG Maker MV 游戏。如果作者做了这些事,思路一就会失效:
- 加壳或打包成可执行文件:有人会用 NW.js 打包,虽然里面还是那套 Web 技术,但入口变成了
.exe,需要先从里面有package.nw或类似结构里把www解出来。好在解包难度不大,解出来之后继续走本文的流程即可。 - 深度魔改引擎源码:把
rpg_core.js里的类名、函数名全部改成无意义乱码,甚至把data目录的 JSON 改成自定义二进制格式。这种情况下,数据层的解析就不能直接套字段名了,得先做“符号还原”和“格式逆向”。 - 混合开发:部分游戏会把核心战斗逻辑放到原生模块里,通过桥接层和页面通信,防御性比纯 JS 高不少。
如果你遇到的是这些情况,思路二就应该以动态调试和 Hook 为主——用 DevTools 断点分析运行时行为,用代理拦截资源请求,用代码注入的方式在内存里修改数值和逻辑。这篇文章里提到的很多静态找位置技巧,在动态调试时依然通用,只是验证和修改手段从“改文件”变成了“改内存”。
我个人在实际操作中的体会是,MV 游戏的逆向是一个“投入产出比”极高的方向。它不需要太多底层知识,主要是耐心、细心和一个能快速验证思路的工作流。如果你刚开始尝试,建议找一个没加密、没魔改的小游戏练手(比如那种 1 小时通关的作者自制游戏),先把从识别引擎到提取数值的流程完整走一遍,然后再挑战带加密、带自定义插件的复杂案例。熟练之后你会发现,RPG Maker MV 这套体系就像一本翻开的书,每一页都写着答案,你只需要知道从哪页开始读就好。