SEGA 街机游戏镜像格式解析
SEGA 街机游戏镜像, 即为 NU 之后出现在 AMFS 中的 .app / .opt / .pack 文件。
通过对 sgxsystemdaemon 以及 sgimgmount 的逆向工程 (4月的时候看了后者, 最近看了前者),现已大致了解了此文件由四大部分组成。
1 . 文件头 (BTID)
该文件头位于文件的前 0x2800
处, 由AES-128-CBC加密, 密钥和IV由一函数计算生成, 与 ICF 文件的加密密钥相同。
文件头包含了该镜像的类型 (PACK/APP/OPT) ,该文件对应的 GameId, PlatformId, 真实名字等 (类似ICF) 。
文件头还定义了文件的区块大小, 区块数量, 镜像的区块位置。通常来说, 区块大小为 0x40000
而镜像的区块通常为 8
(即为 0x200000
字节处)。在 sgxsystemdaemon 的校验中, 镜像区块的位置固定为8 (见下图),因此如果你使用不为此值的镜像区块则无法通过校验。
文件头的结构体大致如下, 在结构体结束之后还有一些不明意义的字符串, 暂不清楚有啥用。
union amsImageBootRecord
{
struct {
uint32_t crc32;
uint32_t unk1;
uint32_t magic;
uint8_t unk2;
uint8_t file_type;
uint8_t unk3;
uint8_t unk4;
char game_id[4];
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint8_t sec;
uint8_t build;
uint8_t patch;
uint8_t minor;
uint8_t major;
uint8_t unk5;
uint64_t total_sector;
uint64_t sector_size;
uint64_t image_offset;
uint64_t unk6;
char platform_id[16];
};
char raw[0x2800];
};
2. HMAC-SHA1 签名
此部分位于 0x2800
- 0x2A00
, 未经加密。其中前 20 字节为有效签名数据, 剩下的 492 字节为随机 (?) 的填充数据。签名由 0x2A00
- 0x200000
之间的数据生成, 该区间即为下一部分的 CRC32 校验。HMAC 的密钥为 64 字节, 和 ICF 密钥一样是在程序中生成的一个固定值, 而不是写死的常量 (虽然都是固定)
3. CRC32 校验
此部分位于 0x2A00
- 0x200000
是对整个镜像的按顺序 CRC32 校验。在校验 0
区块时,由于该区块包含自身 ( 0x2A00
的 4 字节) 以及 HMAC-SHA1 签名, 所以会跳过这些位置 (如下图)。如在校验结束之后还有多余的位置, 则填充了随机 (?) 的数据。
由于校验的数据本身包含了校验的数据, 所以在生成校验的时候需要从最后一个区块开始倒着生成。
4. 镜像
这是最后一部分 (0x200000
- 文件结束), 其中为一个使用了 AES-128-CBC 加密的分区镜像。加密用的密钥和初始向量则来自于加密狗。加密时 IV 会进行轮换,轮换方式为每 0x1000
字节对 IV 使用的高低 8 字节分别使用此时的位置进行异或变换。
小结
通过这些知识, 现已可以完整生成一个可以通过 ALLS 系统校验并且安装后使用的 APP 镜像。但仍然未知 OPT 的动态 IV 是如何生成的