SEGA 街机游戏镜像格式解析

Reverse Engineering Jul 22, 2023

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 (见下图),因此如果你使用不为此值的镜像区块则无法通过校验。

在计算CRC32区块的签名时使用了固定的 8 (amsImageFileVerifySign)

文件头的结构体大致如下, 在结构体结束之后还有一些不明意义的字符串, 暂不清楚有啥用。

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 是如何生成的

标签