nand - NAND images
The nand module enables reading and writing of Nintendo 3DS NAND images.
This module is best combined with pyfatfs for interacting with the FAT filesystems inside TWL NAND and CTR NAND. pyfatfs is not a dependency on pyctr so your application must include it manually.
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', *, 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
CryptoEngineobject 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.
- 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_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_raw_section(section)[source]
Opens a raw NCSD section for reading and writing with on-the-fly decryption.
You should use
NANDSectionto 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.- 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:
- essential
The embedded GodMode9 essentials backup.
This usually contains these files:
frndseedhwcal0hwcal1movablenand_cidnand_hdrotpsecinfo
- 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
Writes to the first 0x1BE (before the TWL MBR) are silently discarded to avoid writing a corrupted NCSD header.
- 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.
NANDSectionkeys (negative ints) are for partition and section types, while positive int keys are for physical locations. This means that, for example,NANDSection.TWLMBRand0contain 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_sizeto 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.