nand
- NAND images
The nand
module enables reading and writing of Nintendo 3DS NAND images.
A basic overview of reading and writing to a NAND image is available here: Example: Read and write NAND partitions.
Getting started
Here’s a quick example to get inside CTRNAND and read files from within it using PyFatBytesIOFS
:
from pyctr.type.nand import NAND
from pyfatfs.PyFatFS import PyFatBytesIOFS
with NAND('nand.bin') as nand:
with PyFatBytesIOFS(fp=nand.open_ctr_partition()) as ctrfat:
with ctrfat.open('/private/movable.sed', 'rb') as msed:
msed.read()
A second example trying to load SecureInfo. This one is tricky because some consoles use SecureInfo_A
and some use SecureInfo_B
, so we have to try both.
from pyctr.type.nand import NAND
from pyfatfs.PyFatFS import PyFatBytesIOFS
from fs.errors import ResourceNotFound
with NAND('nand.bin') as nand:
with PyFatBytesIOFS(fp=nand.open_ctr_partition()) as ctrfat:
for l in 'AB':
path = '/rw/sys/SecureInfo_' + l
if ctrfat.exists(path):
with ctrfat.open(path, 'rb') as f:
f.read()
break
Required files
In most cases users will have a NAND backup with an essentials backup embedded. However there are plenty of cases where this may not occur, so you may need to provide support files in other ways.
The only hard requirement is an OTP. This is found in the essentials backup, but otherwise can be provided as a file, file-like object, and a bytestring.
NAND CID is also useful to have but is not required for most consoles. This also is loaded from the essentials backup if found, otherwise it can be provided like OTP. If it’s not found anywhere, PyCTR will attempt to generate the Counter for both CTR and TWL. The Counter for TWL will not be generated if the TWL MBR is corrupt.
In either case the load priority is first file, then bytestring, then essentials backup.
An external essential.exefs
file must manually be loaded with ExeFSReader
and then the individual otp
and nand_cid
read and provided to the NAND
initializer.
Dealing with corruption
There are cases where the NAND is corrupt but you still want to read it.
One of the most common kinds of corruption is an invalid TWL MBR. This happens if the NCSD header is replaced with one of another console. This applies mostly to very old pre-sighax NAND backups. If the TWL MBR cannot be decrypted and parsed, but a NAND CID was loaded, PyCTR will use the default partition information. Otherwise, TWL information will be inaccessible.
NAND objects
- class pyctr.type.nand.NAND(file, mode='rb', *, fs=None, closefd=None, crypto=None, dev=False, otp=None, otp_file=None, cid=None, cid_file=None, auto_raise_exceptions=True)[source]
Reads a Nintendo 3DS NAND image.
If OTP and CID are not provided, it will attempt to load both from essential.exefs.
If OTP is provided but not CID, it will attempt to generate the Counter for both CTR and TWL.
- Parameters:
file (FilePathOrObject) – A file path or a file-like object with the CIA data.
mode (str) – Mode to open the file with, passed to open. Only used if a file path was given.
closefd (bool) – Close the underlying file object when closed. Defaults to True for file paths, and False for file-like objects.
crypto (CryptoEngine) – A custom
CryptoEngine
object to be used. Defaults to None, which causes a new one to be created.dev (bool) – Use devunit keys.
otp (bytes) – OTP, used to generate the required encryption keys. Overrides otp_file if both are provided.
otp_file (FilePath) – Path to a file containing the OTP.
cid (bytes) – NAND CID, used to generate the Counter. Overrides cid_file if both are provided.
cid_file (FilePath) – Path to a file containing the NAND CID.
auto_raise_exceptions (bool) – Automatically raise an exception if the CTR or TWL partitions are inaccessible. This calls
raise_if_ctr_failed()
andraise_if_twl_failed()
at the end of initialization. Set this to False if you still need access to a NAND even if these sections are unavailable.fs (Optional[FS]) –
- open_ctr_partition(partition_index=0)[source]
Opens a raw partition in CTRNAND for reading and writing.
In practice there is only ever one, so this opens it by default.
- Parameters:
partition_index (int) – Partition index number.
- Returns:
A file-like object.
- Return type:
- open_ctr_fat(partition_index=0)[source]
Opens a FAT filesystem inside a CTR partition for reading and writing.
In practice there is only ever one, so this opens it by default.
- Parameters:
partition_index (int) – Partition index number.
- Returns:
A FAT filesystem.
- Return type:
PyFatBytesIOFS
- open_twl_partition(partition_index)[source]
Opens a raw partition in TWLNAND for reading and writing.
0 is TWL NAND and 1 is TWL Photo.
- Parameters:
partition_index (int) – Partition index number.
- Returns:
A file-like object.
- Return type:
- open_twl_fat(partition_index)[source]
Opens a FAT filesystem inside a TWL partition for reading and writing.
0 is TWL NAND and 1 is TWL Photo.
- Parameters:
partition_index – Partition index number.
- Returns:
A FAT filesystem.
- Return type:
PyFatBytesIOFS
- open_raw_section(section)[source]
Opens a raw NCSD section for reading and writing with on-the-fly decryption.
You should use
NANDSection
to get a specific type of partition. Unless you need to interact with the physical location of partitions, using partition indexes could break for users who have moved them around.Note
If you are looking to read from TWL NAND or CTR NAND, you may be looking for
open_twl_partition()
oropen_ctr_partition()
instead to open the raw MBR partition. This will return NCSD partitions, which for TWL NAND and CTR NAND, include the MBR.- Parameters:
section (Union[NANDSection, int]) – The section to open. Numbers 0 to 7 are specific NCSD partitions. Negative numbers are special sections defined by PyCTR.
- Returns:
A file-like object.
- Return type:
- open_bonus_partition()[source]
Opens the GodMode9 bonus partition.
- Returns:
A file-like object.
- Return type:
- open_bonus_fat()[source]
Opens the GodMode9 bonus FAT partition.
- Returns:
A FAT filesystem.
- Return type:
PyFatBytesIOFS
- essential
The embedded GodMode9 essentials backup.
This usually contains these files:
frndseed
hwcal0
hwcal1
movable
nand_cid
nand_hdr
otp
secinfo
- Type:
- ctr_partitions
The list of partitions in the CTR MBR. Always only one in practice, referred to as CTR NAND.
- twl_partitions
The list of partitions in the TWL MBR. First one is TWL NAND and second is TWL Photo.
NAND sections
- class pyctr.type.nand.NANDSection[source]
This defines the location of partitions in a NAND.
All the enums here are negative numbers to refer to different types of partitions rather than physical locations when used with
NAND.open_raw_section()
, because while 99% of users will never alter the partitions, it is still possible to do so and this module will handle those use cases.- Header = -3
NCSD header of the NAND.
- TWLMBR = -4
Decrypted TWL MBR.
- TWLNAND = -11
TWL NAND region.
Note
Don’t write to the first 0x1BE, this is where the NCSD header is on the raw NAND. Future versions of pyctr may silently discard writes to this region.
If writing to the TWL MBR region (0x1BE-0x200), the NCSD header signature may be invalidated. Use the sighax signature to keep a “valid” header. Also keep a backup of the original NCSD header (this may already be in the essentials backup).
- AGBSAVE = -12
AGB_FIRM save region.
- FIRM0 = -13
NATIVE_FIRM partition.
- FIRM1 = -14
NATIVE_FIRM backup partition.
- CTRNAND = -15
CTR NAND region.
Special sections
These are not actual sections of the NAND/NCSD but are included for convenience.
- Sector0x96 = -2
New 3DS keyblob.
Note
Reading this decrypted with
open_raw_section()
is not yet supported. Decrypt it manually if you need access to it.
- GM9BonusVolume = -6
Bonus FAT16 volume set up by GodMode9. Only available on non-minsize backups if a console has a larger NAND chip than required. Setting this automatically requires loading from a file, because the only way to know if it’s there is to check at the end of the image.
- MinSize = -5
The full NAND image up to the minimum image size.
Exceptions
Custom NCSD interaction
These are for those who want to manually interact with the NCSD information.
- class pyctr.type.nand.NANDNCSDHeader(signature, image_size, actual_image_size, partition_table, unknown, twl_mbr_encrypted)[source]
This contains all the information in the NCSD header. This is also used for virtual sections in PyCTR.
- Parameters:
signature (bytes) –
image_size (int) –
actual_image_size (int) –
partition_table (Dict[Union[int, NANDSection], NCSDPartitionInfo]) –
unknown (bytes) –
twl_mbr_encrypted (bytes) –
- image_size: int
Claimed image size. This does not actually line up with the raw image size in sectors, but is useful to determine Old 3DS vs New 3DS.
- actual_image_size: int
Actual image size in bytes. This is the minimum size of a NAND image, but the actual size of the backup may be larger.
- partition_table: Dict[int | NANDSection, NCSDPartitionInfo]
Partition information.
NANDSection
keys (negative ints) are for partition and section types, while positive int keys are for physical locations. This means that, for example,NANDSection.TWLMBR
and0
contain the same partition info.
- unknown: bytes
Unknown padding data. This is preserved so the header can be re-built exactly with a valid signature.
- classmethod load(fp)[source]
Load a NAND header from a file-like object. This will also seek to
actual_image_size
to determine if there is a GodMode9 bonus drive.- Parameters:
fp (BinaryIO) – The file-like object to read from. Must be seekable.
- class pyctr.type.nand.NCSDPartitionInfo[source]
Information for a single partition.
- fs_type: PartitionFSType | int
Type of filesystem.
- encryption_type: PartitionEncryptionType | int
Type of encryption used for the partition.
Enums
- class pyctr.type.nand.PartitionFSType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
Type of filesystem in the partition.
- Special = -1
Specially defined region from PyCTR.
- Nothing = 0
No partition here.
- Normal = 1
Used for TWL and CTR parts.
- FIRM = 3
Used for FIRM partitions.
- AGBFIRMSave = 4
Used for the AGB_FIRM save partition.
- class pyctr.type.nand.PartitionEncryptionType(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]
Type of encryption on the partition. In practice this is only really used to determine what keyslot to use for CTRNAND which changes between Old 3DS and New 3DS. It’s not really known what happens if any of the other partitions have the crypt type changed.
- NoEncryption = -2
No encryption. (Not an actual FS type, only used for special regions in PyCTR.)
- Sector0x96 = -1
New 3DS keyblob. (Not an actual FS type, only used for special handling in PyCTR.)
- TWL = 1
Used for the TWL partitions.
- CTR = 2
Used for FIRM, CTR on Old 3DS, and AGB_FIRM save partitions.
- New3DSCTR = 3
Used for the CTR partitions on New 3DS.