逆向工程实战:解密与修改Flash游戏存档的完整技术指南
最近在整理老游戏时发现不少朋友对《西游大战僵尸2》这款经典Flash游戏的修改和存档继承很感兴趣。网上流传的教程要么语焉不详要么工具失效导致很多想重温童年或者挑战极限玩法的玩家卡在了第一步——存档解密与导入。这篇文章要解决的不是一个简单的“怎么用Fiddler”的问题。其核心在于如何在没有官方服务器支持、游戏逻辑完全本地化的情况下逆向理解其存档数据的加密与存储机制并安全地实现自定义修改。很多人以为这只是抓个包改个数据但实际上真正关键且容易出错的地方在于对存档文件结构、加密密钥以及客户端-服务器尽管是模拟的交互协议的三重理解。盲目操作轻则存档损坏重则触发游戏保护机制导致无法运行。本文将从一个完整的、可复现的技术视角带你一步步拆解《西游大战僵尸2》的存档系统。你会了解到存档的本地存储位置与加密原理不只是Base64那么简单。如何配置Fiddler作为本地代理并模拟游戏所需的“服务器”环境。解密存档、分析JSON结构、进行安全修改的核心步骤与工具。将修改后的存档重新加密并成功导入游戏的完整流程。过程中所有常见的坑点如编码问题、签名验证、数据溢出及排查方案。无论你是想解锁全角色、修改金币宝石还是单纯对Flash游戏的数据安全机制感兴趣这篇教程都将提供从理论到实践的完整路径。我们不仅会“怎么做”更会解释“为什么这么做”让你真正掌握这类单机/弱联网游戏存档修改的通用思路。1. 核心问题为什么修改《西游大战僵尸2》存档需要如此复杂的流程很多现代游戏的存档修改一个十六进制编辑器或内存修改器就能搞定。但《西游大战僵尸2》作为特定时期的Flash游戏其存档机制设计上有几个特殊点导致了修改门槛混合加密与编码存档数据并非明文保存。它通常经历了“原始JSON - 特定加密算法如简单异或或AES变种- Base64编码 - 本地存储/网络传输”的过程。单纯解码Base64得到的是乱码。客户端-服务器验证模拟游戏虽然可以单机运行但存档的加载和保存可能会尝试向一个已不存在的官方服务器发送请求。我们需要用Fiddler拦截并“欺骗”游戏让它认为正在与一个正常的服务器通信从而接受我们提供的存档数据。数据完整性校验修改后的存档数据在加密前或加密后可能需要重新计算一个校验和如CRC32或签名否则游戏加载时会判定数据非法。Flash Player的安全沙箱限制直接操作本地文件系统受到严格限制因此游戏通常通过特定的API如SharedObject读写数据或通过HTTP请求与服务器交互这进一步增加了直接修改文件的难度。因此我们的解决方案是使用Fiddler作为中间人代理拦截游戏发送的存档请求将我们预先解密、修改并重新加密好的存档数据替换到服务器响应中从而“注入”到游戏里。这本质上是一个对本地回环地址127.0.0.1的流量进行拦截和重写的技术。2. 环境与工具准备在开始之前请确保你已准备好以下环境。强烈建议在虚拟机或专用测试环境中操作避免对生产环境或其他网络应用造成影响。2.1 基础运行环境操作系统Windows 7/8/10/11教程以Windows为例原理通用。游戏本体《西游大战僵尸2》的.swf文件或可执行包。确保游戏能正常运行。Flash Player 独立播放器推荐用于单独运行.swf文件避免浏览器插件带来的额外复杂性。可以从可靠的软件存档站获取。Fiddler Classic强大的HTTP调试代理工具。务必使用Classic版本而非Fiddler Everywhere因为后者对脚本自定义的支持不同。2.2 关键工具清单工具名称用途备注Fiddler Classic拦截、解密、重写HTTP/HTTPS流量模拟服务器响应。核心工具需配置证书和脚本。文本编辑器查看和编辑JSON、配置文件。推荐VS Code、Notepad或Sublime Text。Base64解码/编码工具处理存档数据的编码层。在线工具或编程语言如Python均可。加解密分析工具分析可能的加密算法。如CyberChef在线、或自己编写Python脚本进行常见算法异或、AES的尝试。浏览器开发者工具辅助分析网络请求格式。Chrome/Firefox的F12网络面板。2.3 Fiddler 初始配置安装并启动Fiddler Classic。启用HTTPS解密点击菜单栏Tools - Options - HTTPS勾选Decrypt HTTPS traffic。在弹出的安全警告中选择信任并安装Fiddler的根证书。这是拦截游戏流量的关键一步。配置连接在Connections选项卡中确保监听端口默认为8888未被占用并勾选Allow remote computers to connect虽然本次是本地但某些配置需要。重启Fiddler使配置生效。3. 定位与分析存档数据流这是整个过程中最具探索性的一步。我们的目标是找到游戏加载和保存存档时具体向哪个地址发送了什么样的数据。3.1 启动流量捕获关闭所有不必要的网络应用程序。在Fiddler中点击左下角的Capturing按钮确保其为开启状态显示为Capturing。清除之前的会话记录快捷键Ctrl X。3.2 触发存档操作并分析启动《西游大战僵尸2》游戏。在游戏中进行一个会触发存档的动作例如进入游戏后它可能自动尝试加载存档。完成一关后游戏可能自动保存。在游戏内找到“保存”或“上传存档”按钮并点击。立即切换到Fiddler观察会话列表。你会看到一系列HTTP请求。寻找可疑的请求URL特征可能包含save、load、user、game、data等关键词或者指向一个可能已失效的域名如game.xxx.com/save.php。请求方法通常是POST因为存档数据一般放在请求体中。请求体点击一个可疑的会话在右侧Inspectors标签页下的WebForms或TextView中查看。你可能会看到一个很长的、由字母数字组成的字符串Base64编码后的密文或者一个data这样的参数。假设我们找到了一个关键请求URL:http://127.0.0.1:8080/game_save.php(这可能是游戏内置的测试地址或我们后续要模拟的地址)Method: POSTRequest Body:dataU2FsdGVkX1...很长一串...这个data参数的值很可能就是经过加密和Base64编码后的存档数据。4. 存档数据解密与结构分析拿到data字符串后我们需要剥开它的两层“外壳”Base64编码和加密。4.1 Base64解码首先将data参数的值进行Base64解码。你可以使用在线的CyberChef或者写一个简单的Python脚本。# decode_base64.py import base64 encoded_data U2FsdGVkX1...很长一串... # 替换为你抓到的实际数据 try: # 移除可能的URL编码字符如变成空格 encoded_data_clean encoded_data.replace( , ) decoded_bytes base64.b64decode(encoded_data_clean) # 尝试以十六进制和文本形式查看 print(Hex:, decoded_bytes.hex()) # 如果不是纯文本直接打印字节可能会乱码这是正常的 print(Raw bytes (first 100):, decoded_bytes[:100]) except Exception as e: print(Base64 decode error:, e)运行后如果输出是一串规则的十六进制数或者开头有类似Salted__的字节说明里面还有一层加密。4.2 分析加密算法Flash游戏常见的简单加密有异或XOR加密使用一个固定密钥或简单生成的密钥对数据进行逐字节异或。TEA/XXTEA一种轻量级分组加密算法在早期游戏中常见。自定义混淆开发者自己写的简单变换算法。如何试探查找已知常量用十六进制编辑器或脚本搜索解码后数据中是否包含游戏内已知的明文如角色名、物品ID的JSON键名如gold。如果找到可以反推加密算法。静态分析.swf文件使用JPEXS等Flash反编译器搜索save、load、encrypt、decrypt等关键词找到负责序列化和加密的ActionScript代码。这是最直接但门槛较高的方法。动态调试更复杂需要Flash调试器。假设我们通过分析或从社区得知该游戏使用了一个简单的异或加密密钥是0xAA。那么解密脚本如下# decrypt_save.py import base64 def xor_decrypt(ciphertext_bytes, key): 简单的单字节异或解密 return bytes([b ^ key for b in ciphertext_bytes]) # 你的数据 encoded_data U2FsdGVkX1... encryption_key 0xAA # 示例密钥实际需要探查 # 1. Base64解码 decoded_b64 base64.b64decode(encoded_data.replace( , )) # 2. 异或解密 decrypted_bytes xor_decrypt(decoded_b64, encryption_key) # 3. 尝试以UTF-8解码因为JSON是文本 try: save_json_str decrypted_bytes.decode(utf-8) print(解密后的JSON字符串:) print(save_json_str) except UnicodeDecodeError: print(解密后仍非有效UTF-8可能是算法或密钥错误或者还有其它编码。) print(解密后Hex:, decrypted_bytes.hex()[:200])如果成功你将看到一个结构清晰的JSON字符串包含了你的游戏数据如金币、钻石、角色、关卡进度等。4.3 分析JSON结构将解密后的JSON字符串格式化仔细研究其结构。例如{ user: { uid: 10001, name: Player1 }, game: { gold: 1500, diamond: 45, level: 12, characters: [ {id: 1, unlocked: true, level: 5}, {id: 2, unlocked: false, level: 0} ], inventory: [ {item_id: 101, count: 3} ] }, sign: a1b2c3d4e5f6 // 可能的签名或校验码 }重点注意签名字段如sign这是游戏用于验证数据完整性的。如果你修改了gold或diamond的值必须按照游戏原有的算法重新计算这个签名否则游戏可能会拒绝加载。算法可能是对特定字段的MD5、SHA1或CRC32。这通常需要反编译代码才能确定。数据类型确保修改时保持正确的数据类型数字不加引号布尔值是true/false。5. 修改存档与重新加密理解了结构后修改就很简单了。例如将金币修改为99999gold: 99999关键步骤重新计算签名如果存在如果原JSON有sign字段你需要确定签名算法如对goldgold值diamonddiamond值这个字符串取MD5。用修改后的数据按照相同算法计算新的签名。替换掉旧的sign值。# calculate_signature.py (示例算法需自行探查) import hashlib import json # 假设算法是将game对象的所有键值对按字母排序后拼接成字符串然后取MD5 def calculate_sign(game_data_dict): # 排序并拼接键值对 sorted_items sorted(game_data_dict.items()) sign_str for key, value in sorted_items: sign_str f{key}{value} # 计算MD5 return hashlib.md5(sign_str.encode(utf-8)).hexdigest() # 加载修改后的JSON with open(modified_save.json, r, encodingutf-8) as f: save_data json.load(f) # 计算新的签名 new_sign calculate_sign(save_data[game]) # 更新签名 save_data[sign] new_sign # 保存回文件 with open(ready_to_encrypt.json, w, encodingutf-8) as f: json.dump(save_data, f, ensure_asciiFalse, indent2)重新加密与编码将修改并签名后的JSON字符串按照逆向流程处理UTF-8编码为字节。使用相同的密钥和算法如异或加密。Base64编码。# encrypt_and_encode.py import json import base64 def xor_encrypt(plaintext_bytes, key): return bytes([b ^ key for b in plaintext_bytes]) encryption_key 0xAA # 与解密密钥相同 # 读取最终JSON with open(ready_to_encrypt.json, r, encodingutf-8) as f: final_data json.load(f) json_str json.dumps(final_data, ensure_asciiFalse, separators(,, :)) # 1. 字符串转字节 plain_bytes json_str.encode(utf-8) # 2. 加密 encrypted_bytes xor_encrypt(plain_bytes, encryption_key) # 3. Base64编码 final_encoded_data base64.b64encode(encrypted_bytes).decode(ascii) print(最终可用于替换的data参数值:) print(final_encoded_data)保存好这个final_encoded_data字符串。6. 使用Fiddler模拟服务器并注入存档现在我们需要让游戏在请求存档时收到我们修改后的数据。6.1 使用Fiddler的AutoResponder功能推荐这是最直观的方法无需编写代码。在Fiddler中找到之前抓到的那个存档请求会话如POST http://127.0.0.1:8080/game_save.php。将其拖拽到右侧的AutoResponder标签页中。在AutoResponder标签页中勾选Enable rules和Unmatched requests passthrough。选中刚才拖进来的规则在Rule Editor下半部分选择*bpu并点击Save。更关键的是我们需要编辑响应。点击该规则最右边的...按钮选择Edit Response。在弹出的窗口中你可能需要先选择Raw或TextView视图。游戏期望的响应可能是一个简单的成功标识如{code:1}或者直接返回存档数据。我们需要根据抓包时看到的服务器原始响应来构造。如果原响应是{code:1}我们只需确保修改后的请求能成功发出游戏可能就会加载本地或请求体中的数据。此时AutoResponder的作用是确保这个请求不因服务器不存在而失败。如果原响应包含存档数据我们需要用我们生成的final_encoded_data替换响应体中的相应部分。这可能需要在FiddlerScript中进行更动态的替换对于固定修改可以手动编辑一个文本文件作为响应。一个更通用的方法是让Fiddler返回一个成功的HTTP 200响应并在响应体中包含游戏期望的数据格式。例如创建一个文本文件fake_response.txt内容为{code:1, data:final_encoded_data}。然后在AutoResponder规则中选择这个文件作为响应Find a file...。6.2 使用FiddlerScript进行动态响应更灵活如果游戏逻辑复杂需要根据请求动态返回数据可以编辑FiddlerScript。在Fiddler中点击菜单栏Rules - Customize Rules...这会打开FiddlerScript编辑器。找到OnBeforeRequest函数。在这个函数里我们可以检查并修改请求但更常用的是在OnBeforeResponse函数中修改响应不过对于模拟服务器我们通常在OnBeforeRequest中直接返回自定义响应。添加类似下面的代码注意这是一个概念示例需要根据实际请求URL和响应格式调整// FiddlerScript (JScript.NET) import System; import Fiddler; class Handlers { static function OnBeforeRequest(oSession: Session) { // 如果请求的URL是游戏的存档加载地址 if (oSession.uriContains(game_save.php) || oSession.uriContains(load)) { // 阻止请求发往真实服务器 oSession[x-breakrequest] breaker; // 标记以在BEFORE_REQUEST断点处停止 // 或者直接返回一个自定义响应更直接 // 注意这里我们假设游戏通过POST的body发送了请求但期望在响应体中获得存档数据 // 实际情况可能不同可能需要分析原始通信。 // 示例直接返回一个成功的JSON响应并附上我们修改后的存档数据 var myModifiedSaveData U2FsdGVkX1...你的final_encoded_data...; // 替换为你的数据 var jsonResponse {status:ok,save_data: myModifiedSaveData }; oSession.utilSetResponseBody(jsonResponse); oSession.oResponse.headers.HTTPResponseCode 200; oSession.oResponse.headers.HTTPResponseStatus 200 OK; oSession.oResponse.headers[Content-Type] application/json; charsetutf-8; FiddlerObject.alert(已拦截存档请求并注入修改数据); } } }重要编写FiddlerScript需要一定的JavaScript和HTTP知识且必须精确匹配游戏的预期协议。最稳妥的方式是先通过AutoResponder进行静态替换测试成功再考虑脚本化。7. 测试与验证配置好AutoResponder规则或FiddlerScript。清除游戏本地缓存可选但推荐删除Flash SharedObject或游戏本地保存的数据文件迫使游戏从“服务器”重新加载。Flash数据通常位于C:\Users\[你的用户名]\AppData\Roaming\Macromedia\Flash Player\#SharedObjects下的随机文件夹中。确保Fiddler正在捕获流量且规则已启用。启动游戏。观察Fiddler的会话列表确认对存档URL的请求被拦截并且响应是你自定义的。进入游戏查看金币、钻石、角色等数据是否已变为你修改后的值。8. 常见问题与排查思路问题现象可能原因排查方式解决方案Fiddler抓不到游戏请求游戏未走系统代理Flash Player独立播放器可能不默认使用代理。1. 检查Fiddler是否开启捕获。2. 为Flash Player独立播放器配置手动代理127.0.0.1:8888需在播放器启动参数或快捷方式中设置较复杂。3. 尝试在浏览器中运行.swf文件并配置浏览器使用Fiddler代理。推荐在支持代理的浏览器中运行游戏进行抓包分析。Base64解码失败/乱码1. 数据可能经过URL编码变%2B。2. 抓到的数据不是完整的存档可能只是校验码。1. 先进行URL解码。2. 查看请求的完整URL和所有参数尝试其他参数。使用Python的urllib.parse.unquote先解码再用Base64解码。解密后仍是乱码1. 加密算法判断错误。2. 密钥错误。3. 数据可能被压缩如zlib。1. 分析反编译代码。2. 尝试常见密钥如0x00,0xFF或字符串密钥。3. 尝试用zlib.decompress解压解密后的字节。静态分析.swf是终极手段。社区资源如贴吧、论坛也可能有现成的加解密方法。修改后游戏不生效1. 签名校验失败。2. 数据格式或类型错误。3. 注入的响应格式不对游戏解析失败。1. 检查修改后的JSON结构是否合法。2. 对比修改前后sign字段的算法。3. 用Fiddler对比修改前后的服务器原始响应格式。1. 确保签名算法正确。2. 使用Fiddler的Compare功能对比会话。3. 尝试先不修改数据只原样返回看游戏是否能正常加载。游戏崩溃或闪退1. 修改的数据值超出游戏逻辑范围如负数、过大整数。2. JSON格式错误如多余的逗号。3. 加密后的Base64字符串包含非法字符。1. 检查修改的数值是否合理。2. 使用JSON验证工具检查格式。3. 确保Base64编码使用标准字符集。1. 小幅修改测试。2. 使用json.loads()验证JSON。3. 确保加密函数输出字节Base64编码函数输入字节。9. 最佳实践与安全建议备份原始存档在开始任何修改之前务必备份游戏的原始存档文件或SharedObject数据。增量修改与测试不要一次性修改大量数据。先修改一个容易观察的项如金币测试成功后再进行其他修改。理解数据边界不要将数值改为离谱的大数如int溢出或负数除非你确定游戏能处理。尊重版权与乐趣存档修改主要用于单机游戏研究、数据恢复或挑战不同玩法。请勿用于联网对战游戏或损害他人体验。虚拟机环境此类操作涉及系统代理和证书安装在虚拟机中进行可以隔离风险。法律风险仅对您拥有合法副本的游戏进行修改研究且不涉及破解或分发。通过以上步骤你不仅能够成功修改《西游大战僵尸2》的存档更能掌握一套应对类似单机/弱联网Flash或H5游戏存档修改的通用方法论抓包分析 - 数据解码/解密 - 结构解析 - 安全修改 - 重加密/编码 - 流量注入。这个流程的核心在于耐心分析和逆向思维工具只是辅助。希望这篇教程能帮你找回童年的乐趣或开启游戏数据逆向分析的大门。