游戏存档的逆向工程从定位加密算法入手。存档文件通常以.dat或.sav格式保存,通过十六进制编辑器打开后可见数据以非结构化字节序列呈现。若文件头部出现Salted__字符串,表明采用OpenSSL的EVP_BytesToKey派生密钥进行AES加密。存档加密的逻辑起点在于密钥存储位置,部分客户端将密钥硬编码在可执行文件的只读数据段。
使用IDAPro加载主程序或游戏库文件,搜索AES相关的常量特征值。S盒数据是AES算法的固定置换表,二进制特征为637C777BF26B6FC53001672BFED7AB76。在程序的.rdata段中定位到该序列即锁定AES实现代码。逆推引用该数据的函数可定位加密子程序与解密子程序的入口偏移。
密钥与初始化向量的获取路径分两种情况。硬编码密钥直接在数据段提取字节序列。动态生成密钥的版本在程序初始化时调用GetTickCount或rdtsc指令获取时间戳作为随机种子,结合固定盐值通过PBKDF2派生密钥。采用动态密钥的存档每台机器生成的密文互不兼容,逆向时需挂钩密钥派生函数的返回值。
存档数据的二进制结构解析需结合原始内存布局。读取存档时调用CFile类的Read方法将加密数据加载到缓冲区,解密后memcpy到结构体指针。在逆向工具中对解密后的内存区域按已知字段偏移量标注含义。字段顺序采用四字节对齐方式,角色名称固定占位16字节,属性数值按DWORD类型连续排列,背包物品以链表结构在末尾动态扩展。
XOR校验和位于存档末尾4字节,计算方式为从文件头开始逐字节异或所有数据。AES解密后直接修改属性字段会导致校验和不匹配,读档时报错数据损坏。修改流程应为解密→修改内存→重新计算校验和→加密写回。校验和算法通过动态跟踪存档保存函数获取,在函数尾部定位到循环异或指令块即可复制计算逻辑。
补丁制作工具采用dll注入方式修改内存。将解密修改逻辑编译为动态链接库,使用CreateRemoteThread在目标进程中执行LoadLibrary加载。dll入口完成存档数据的内存修改后再返回,不修改硬盘文件。这种方法规避了重新加密出错的风险,每次游戏启动时动态修补存档数据。
使用IDAPro加载主程序或游戏库文件,搜索AES相关的常量特征值。S盒数据是AES算法的固定置换表,二进制特征为637C777BF26B6FC53001672BFED7AB76。在程序的.rdata段中定位到该序列即锁定AES实现代码。逆推引用该数据的函数可定位加密子程序与解密子程序的入口偏移。
密钥与初始化向量的获取路径分两种情况。硬编码密钥直接在数据段提取字节序列。动态生成密钥的版本在程序初始化时调用GetTickCount或rdtsc指令获取时间戳作为随机种子,结合固定盐值通过PBKDF2派生密钥。采用动态密钥的存档每台机器生成的密文互不兼容,逆向时需挂钩密钥派生函数的返回值。
存档数据的二进制结构解析需结合原始内存布局。读取存档时调用CFile类的Read方法将加密数据加载到缓冲区,解密后memcpy到结构体指针。在逆向工具中对解密后的内存区域按已知字段偏移量标注含义。字段顺序采用四字节对齐方式,角色名称固定占位16字节,属性数值按DWORD类型连续排列,背包物品以链表结构在末尾动态扩展。
XOR校验和位于存档末尾4字节,计算方式为从文件头开始逐字节异或所有数据。AES解密后直接修改属性字段会导致校验和不匹配,读档时报错数据损坏。修改流程应为解密→修改内存→重新计算校验和→加密写回。校验和算法通过动态跟踪存档保存函数获取,在函数尾部定位到循环异或指令块即可复制计算逻辑。
补丁制作工具采用dll注入方式修改内存。将解密修改逻辑编译为动态链接库,使用CreateRemoteThread在目标进程中执行LoadLibrary加载。dll入口完成存档数据的内存修改后再返回,不修改硬盘文件。这种方法规避了重新加密出错的风险,每次游戏启动时动态修补存档数据。

