【免费下载链接】shadPS4Plus
Standalone PKG extractor based on code that used to be in ShadPS4
shadPS4Plus是一个开源的 PS4 游戏 PKG 文件解包工具,它从 PS4 模拟器 ShadPS4 的代码中独立而来,无需真机即可还原加密的游戏安装文件。本文是加密原理系列的下篇,带你完整看懂最后两步:如何用AES-XTS 模式解密 PFS 数据区,以及如何用zlib 解压 PFSC 压缩块,把加密字节真正变成可读取的游戏文件 🎮
一、先回顾:上一篇我们做到了哪里
在上篇中,我们走完了 PKG 的密钥推导链。简单回顾整条链路:
- 读取 PKG 文件表中的
ENTRY_KEYS(ID 0x10)与IMAGE_KEY(ID 0x20)条目; - 用内置私钥RSA-2048 解密出 DK3;
- 对「条目数据 + DK3」做SHA256 得到 ivkey,再AES-CBC 解密得到 imgKey;
- 第二次 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:
- 把
0x1(4 字节计数器)和 PFS 头部的 16 字节seed拼成 20 字节输入; - 以EK_PFS 为 HMAC-SHA256 的密钥,对上述输入做摘要,得到 32 字节;
- 前 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, ¤t_sector, 8)),所以扇区号算错,全盘皆密文; decryptPFS从偏移0x10000起解密(见 源码注释:保留头部不解密),调用时传入的起始扇区号与解密范围要一致。
四、PFSC 在哪里:按魔数扫描
PFS 镜像解密完成后,需要先找到 PFSC 元数据区的起点。shadPS4Plus 的做法非常朴素且稳健——扫描魔数,见 GetPFSCOffset:
- 从偏移
0x20000开始,每隔0x10000(64KB)取 4 字节; - 与魔数
0x43534650(即 ASCII 的"PFSC")比较; - 命中即返回 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 调用三件套:
inflateInit初始化解压器;inflate(…, Z_FINISH)一次性展开;inflateEnd释放资源。
解压后的 64KB 内容按顺序依次是:inode 表(每个 0xA8 字节,记录文件类型、大小、扇区位置)、flat_path_table、"."、".."、然后是各目录项(dirent)。代码正是靠识别这些标记来解析出完整的目录树和文件清单,见 pkg.cpp。
六、完整流水线:从一个文件到磁盘
把上、下两篇串起来,PKG::Extract+ExtractFiles的完整流程是:
- 校验 PKG 头(magic
0x7F434E54),解析文件表; - 密钥链:RSA → SHA256 → AES-CBC → RSA,得到 EK_PFS(pkg.cpp);
- 读 PFS seed,HMAC-SHA256 派生 dataKey / tweakKey;
- 读取 PFS 镜像,AES-XTS 整段解密(
decryptPFS,起始扇区号为 0); - 扫描
PFSC魔数 → 读头部 → 建 sectorMap → zlib 解压各块 → 得到 inode 与目录表; - 对每个文件:按 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
相关推荐
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考