暗号化方式

3DSのセーブデータはゲームカートにまさにDSのそれと同じように保存されています。 DSではこのようなセーブデータはそのまま保存されていましたが、3DSにおいては暗号化して保存する仕組みが加わりました。 これはとてもストリームの暗号化に似ています。排他的論理和を取ることによって、セーブデータの復号鍵が 得られます。                                                                                            

このアルゴリズムを解読できるのは、たった512バイトのキーによって暗号化されているためです。言ってみれば、512バイト周期で同じように復号しているわけです。つまり、暗号化されても、その排他的論理和を取る事によってキーを作ることができます。残念ながら、既存の平文をを暗号化するときにこの方法を使うと、誰でもキーを得ることができるのです。

ということで、ここからどうやって3DSでセーブデータを復号するのでしょうか?まず、データを512バイトの区切りに分けます。次に、データで最も多いのは0xFFであるはずなので、ある値がそれにあてはまるはずです。そこで最もよく使われている値を探します。これを基準値とします。そして、元のセーブデータとを取ることで完全な復号化されたデータをえることができます。もう一度基準値とその排他的論理和を取れば、データを復号化することができます。

Wearleveling

The 3DS employs a wearleveling scheme on the savegame FLASH chips. This is done trough blockmaps and a journal. The blockmap is located at offset 0 of the flash chip, and is immediately followed by the journal. The initial state is dictated by the blockmap, and the journal is then applied to that.

The blockmap structure is simple:

struct header_entry {
        uint8_t chksums[8];
        uint8_t phys_sec;
        uint8_t alloc_cnt;
} __attribute__((__packed__));

The journal structure is as follows:

struct sector_entry {
        uint8_t virt_sec;       // Mapped to sector
        uint8_t prev_virt_sec;  // Physical sector previously mapped to
        uint8_t phys_sec;       // Mapped from sector
        uint8_t prev_phys_sec;  // Virtual sector previously mapped to
        uint8_t phys_realloc_cnt;       // Amount of times physical sector has been remapped
        uint8_t virt_realloc_cnt;       // Amount of times virtual sector has been remapped
        uint8_t chksums[8];
} __attribute__((__packed__));

struct long_sector_entry{
        struct sector_entry sector;
        struct sector_entry dupe;
        uint32_t magic;
}__attribute__((__packed__));

With magic being a constant 0x080d6ce0.

ファイルシステム

フラッシュチップ内に保存されているセーブデータは特殊なFSを使用しています。

ファイルヘッダーは静的オフセットとしてアドレス0x3a00に格納されているようです。

 struct FileEntry {
     u32 Unknown;
     u8  FileName[0x10];
     u32 EntryID;
     u32 Unknown;
     u32 Unknown;
     u32 FileSize;
     u32 Unknown;
     u32 Unknown;
     u32 Unknown;
 }