ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

shadPS4Plus破解PS4游戏加密:AES-XTS解密PFS数据区+zlib解压PFSC全流程教程

shadPS4Plus破解PS4游戏加密:AES-XTS解密PFS数据区+zlib解压PFSC全流程教程

【免费下载链接】shadPS4Plus

Standalone PKG extractor based on code that used to be in ShadPS4

项目地址:https://gitcode.com/gh_mirrors/sh/shadPS4Plus
点击查看免费下载

shadPS4Plus是一个开源的 PS4 游戏 PKG 文件解包工具,它从 PS4 模拟器 ShadPS4 的代码中独立而来,无需真机即可还原加密的游戏安装文件。本文是加密原理系列的下篇,带你完整看懂最后两步:如何用AES-XTS 模式解密 PFS 数据区,以及如何用zlib 解压 PFSC 压缩块,把加密字节真正变成可读取的游戏文件 🎮

一、先回顾:上一篇我们做到了哪里

在上篇中,我们走完了 PKG 的密钥推导链。简单回顾整条链路:

  1. 读取 PKG 文件表中的ENTRY_KEYS(ID 0x10)与IMAGE_KEY(ID 0x20)条目;
  2. 用内置私钥RSA-2048 解密出 DK3;
  3. 对「条目数据 + DK3」做SHA256 得到 ivkey,再AES-CBC 解密得到 imgKey;
  4. 第二次 RSA 解密 imgKey,得到解开 PFS 的总钥匙——EK_PFS。

这条链的入口在 pkg.cpp:

  • RSA2048Decrypt(dk3_, key1[3], true)解出 DK3;
  • ivKeyHASH256+aesCbcCfb128Decrypt解出 imgKey;
  • RSA2048Decrypt(ekpfsKey, imgKey, false)解出 EK_PFS。

拿到 EK_PFS 后,还差临门一脚:EK_PFS 还不能直接用来解密 PFS 数据区,它要先「派生」出两把真正的密钥。下面开始下篇的正题。

二、PFS 文件系统:游戏数据藏在哪个结构里

PKG 文件里装游戏的主体部分叫PFS 镜像(PS4 的文件系统,PS4 File System)。它的核心规则:

  • PFS 以4KB(0x1000)为基本扇区(sector),整个 PFS 数据区被按扇区加密;
  • 镜像内嵌一块叫PFSC(PFS Cache)的元数据区,存放文件表(inode)和目录项(dirent);
  • PFSC 内部按64KB(0x10000)大块组织,每个大块要么「原样存放」,要么「zlib 压缩后存放」。

相关的数据结构定义在 pfs.h 的PFSCHdr中,其中block_sz2(块大小)、block_offsets(扇区偏移表位置)、data_length(数据总长)是后文的关键字段。

💡 一句话概括:PFS 管"在哪",AES-XTS 管"解得开",zlib 管"拆得小"。

三、AES-XTS 解密原理:为什么 PS4 选它

AES 加密有两种常见用法:

模式工作方式隐患
AES-CBC数据块之间"连环依赖"相同明文在相同 IV 下产生相同密文,可被比对
AES-XTS每个块额外叠加一个"位置因子"(tweak)位置不同,加密结果就不同

PS4 给 PFS 扇区选的就是AES-XTS。你可以把它想象成「双保险箱」🔐:

  • dataKey(数据密钥):真正的锁芯,负责加解密每个 16 字节的数据块;
  • tweakKey(调整密钥):一个"位置编码器",扇区编号是多少,锁芯的咬合角度就偏多少。

这样即使两个扇区内容完全相同,只要扇区号不同,加密后的密文就完全不同——攻击者无法通过对比密文来猜测或篡改数据。

3.1 密钥派生:EK_PFS + seed → 两把钥匙

两把钥匙由PfsGenCryptoKey生成,源码见 crypto.cpp:

  1. 把0x1(4 字节计数器)和 PFS 头部的 16 字节seed拼成 20 字节输入;
  2. 以EK_PFS 为 HMAC-SHA256 的密钥,对上述输入做摘要,得到 32 字节;
  3. 前 16 字节是tweakKey,后 16 字节是dataKey。

其中 seed 从 PFS 镜像偏移+0x370处读取,派生调用见 pkg.cpp。

3.2 解密循环:每个扇区内的四步舞

XTS 对每个扇区(4096 字节 = 256 个 16 字节块)的处理流程,在 crypto.cpp 的decryptPFS中实现,逻辑可以浓缩为:

tweak ← AES-ECB 加密(tweakKey, 扇区号) 对扇区内的每个 16 字节块: ① 密文块 XOR tweak → 临时块 ② 用 dataKey 做 AES-ECB 解密(临时块) ③ 解密结果再 XOR tweak → 明文块 ④ tweak ← GF(2^128) 乘 {2}(xtsMult),为下一块准备

其中第 ④ 步的「tweak 乘法」在有限域 GF(2^128) 上进行,实现只有短短几行——左移一位、若高位溢出则异或常数0x87,见 xtsMult。正是它保证了「同一个扇区内、不同位置的块使用不同的 tweak」。

⚠️两个实现细节:

  • 扇区号直接以 8 字节小端整数写入 tweak 的前 8 字节(memcpy(tweak, &current_sector, 8)),所以扇区号算错,全盘皆密文;
  • decryptPFS从偏移0x10000起解密(见 源码注释:保留头部不解密),调用时传入的起始扇区号与解密范围要一致。

四、PFSC 在哪里:按魔数扫描

PFS 镜像解密完成后,需要先找到 PFSC 元数据区的起点。shadPS4Plus 的做法非常朴素且稳健——扫描魔数,见 GetPFSCOffset:

  1. 从偏移0x20000开始,每隔0x10000(64KB)取 4 字节;
  2. 与魔数0x43534650(即 ASCII 的"PFSC")比较;
  3. 命中即返回 PFSC 的偏移,否则继续找。

找到后,把 PFSC 整体拷出,并读取头部PFSCHdr,算出大块数量:num_blocks = data_length / block_sz2,再按block_offsets指向的位置读出sectorMap(扇区偏移表)——一个「每块起始偏移」数组,后一块的起始位置就是前一块的实际长度。

五、zlib 解压:sectorSize 就是压缩判据

PFSC 里每个 64KB 大块的「实际长度」由 sectorMap 相邻两项相减得到(见 pkg.cpp):

sectorSize == 0x10000 → 该块未压缩,直接拷贝 sectorSize < 0x10000 → 该块是 zlib 压缩流,需要 inflate 展开

解压函数 DecompressPFSC 是标准 zlib 调用三件套:

  1. inflateInit初始化解压器;
  2. inflate(…, Z_FINISH)一次性展开;
  3. inflateEnd释放资源。

解压后的 64KB 内容按顺序依次是:inode 表(每个 0xA8 字节,记录文件类型、大小、扇区位置)、flat_path_table、"."、".."、然后是各目录项(dirent)。代码正是靠识别这些标记来解析出完整的目录树和文件清单,见 pkg.cpp。

六、完整流水线:从一个文件到磁盘

把上、下两篇串起来,PKG::Extract+ExtractFiles的完整流程是:

  1. 校验 PKG 头(magic0x7F434E54),解析文件表;
  2. 密钥链:RSA → SHA256 → AES-CBC → RSA,得到 EK_PFS(pkg.cpp);
  3. 读 PFS seed,HMAC-SHA256 派生 dataKey / tweakKey;
  4. 读取 PFS 镜像,AES-XTS 整段解密(decryptPFS,起始扇区号为 0);
  5. 扫描PFSC魔数 → 读头部 → 建 sectorMap → zlib 解压各块 → 得到 inode 与目录表;
  6. 对每个文件:按 inode 的loc/Blocks定位其在 PFS 中的扇区 → 逐扇区XTS 解密 + zlib 解压→ 写盘(ExtractFiles)。

第 6 步有个小技巧:解单个文件时会多读一个扇区(pfsc_buf_size = 0x11000)并把读到的整块按正确的扇区号重新解密,保证 tweak 对齐,见 pkg.cpp。

七、动手:使用 extractor 命令行工具

理解了原理,实际使用只需要一条命令。独立工具入口在 extractor/main.cpp,命令行用法:

pkg_extractor <pkg文件路径> [输出目录]
  • 第二个参数可省略,默认解包到 PKG 同目录;
  • 程序会自动识别 PKG 类型:基础游戏(返回码 101)、游戏更新(102,追加-patch目录)、DLC(103,按 CONTENT_ID 中的 label 归档);
  • 配套的 install_pkg.sh 脚本可把游戏直接安装到 ShadPS4 的 games/addons 目录(需先编辑目录配置),Windows 下可直接把 PKG 拖到 exe 上。

八、常见问题 FAQ

Q1:解密出来全是乱码?大概率是扇区号与解密范围不对齐——XTS 的 tweak 由扇区号决定,错位一个扇区全盘失效。检查传入decryptPFS的sector参数是否与数据在镜像中的位置匹配。

Q2:zlib 解压报错?说明该块本应未压缩却被当压缩块处理(或反之)。以sectorSize是否等于0x10000为准,别自己猜测。

Q3:需要 PSN 账号或联网吗?不需要。shadPS4Plus 是纯离线工具,密钥均为公开内置常量,全程不联网。

Q4:可以解什么文件?仅限本地 PKG 安装包(游戏本体 / 更新 / DLC),用于提取或备份自己合法拥有的内容。

写在最后

到这里,shadPS4Plus 的加密世界就完整了:RSA 解开钥匙、SHA256/AES-CBC 层层传递、HMAC 派生双钥、AES-XTS 守护扇区、zlib 压缩瘦身——PS4 的 PKG 加密体系看似复杂,拆开看只是几种经典算法的组合拳。相关文档可参阅 building-linux.md,也欢迎在 Debugging 文档 基础上继续深入源码调试 🕵️

【免费下载链接】shadPS4Plus

Standalone PKG extractor based on code that used to be in ShadPS4

项目地址:https://gitcode.com/gh_mirrors/sh/shadPS4Plus
点击查看免费下载
上一篇:50纳秒到10倍性能提升:gperftools全链路优化实战指南
下一篇:mirrors/ggml-org/models:开源AI模型镜像库全解析,轻松获取全球顶尖语言模型

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

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