u32 DumpNand(u32 param) { char filename[64]; u8* buffer = BUFFER_ADDRESS; u32 nand_size = getMMCDevice(0)->total_size * NAND_SECTOR_SIZE; u32 result = 0; Debug("Dumping %sNAND. Size (MB): %u", (param & N_EMUNAND) ? "Emu" : "Sys", nand_size / (1024 * 1024)); if (OutputFileNameSelector(filename, "NAND.bin", NULL, param & N_EMUNAND) != 0) return 1; if (!DebugFileCreate(filename, true)) return 1; u32 n_sectors = nand_size / NAND_SECTOR_SIZE; for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) { u32 read_sectors = min(SECTORS_PER_READ, (n_sectors - i)); ShowProgress(i, n_sectors); ReadNandSectors(i, read_sectors, buffer); if(!DebugFileWrite(buffer, NAND_SECTOR_SIZE * read_sectors, i * NAND_SECTOR_SIZE)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; }
u32 CtrNandPadgen(u32 param) { u32 keyslot; u32 nand_size; // legacy sizes & offset, to work with 3DSFAT16Tool if (GetUnitPlatform() == PLATFORM_3DS) { keyslot = 0x4; nand_size = 758; } else { keyslot = 0x5; nand_size = 1055; } Debug("Creating NAND FAT16 xorpad. Size (MB): %u", nand_size); Debug("Filename: nand.fat16.xorpad"); PadInfo padInfo = { .keyslot = keyslot, .setKeyY = 0, .size_mb = nand_size, .filename = "nand.fat16.xorpad", .mode = AES_CNT_CTRNAND_MODE }; if(GetNandCtr(padInfo.ctr, 0xB930000) != 0) return 1; return CreatePad(&padInfo); } u32 TwlNandPadgen(u32 param) { u32 size_mb = (partitions[0].size + (1024 * 1024) - 1) / (1024 * 1024); Debug("Creating TWLN FAT16 xorpad. Size (MB): %u", size_mb); Debug("Filename: twlnand.fat16.xorpad"); PadInfo padInfo = { .keyslot = partitions[0].keyslot, .setKeyY = 0, .size_mb = size_mb, .filename = "twlnand.fat16.xorpad", .mode = AES_CNT_TWLNAND_MODE }; if(GetNandCtr(padInfo.ctr, partitions[0].offset) != 0) return 1; return CreatePad(&padInfo); } u32 Firm0Firm1Padgen(u32 param) { u32 size_mb = (partitions[3].size + partitions[4].size + (1024 * 1024) - 1) / (1024 * 1024); Debug("Creating FIRM0FIRM1 xorpad. Size (MB): %u", size_mb); Debug("Filename: firm0firm1.xorpad"); PadInfo padInfo = { .keyslot = partitions[3].keyslot, .setKeyY = 0, .size_mb = size_mb, .filename = "firm0firm1.xorpad", .mode = AES_CNT_CTRNAND_MODE }; if(GetNandCtr(padInfo.ctr, partitions[3].offset) != 0) return 1; return CreatePad(&padInfo); } u32 GetNandCtr(u8* ctr, u32 offset) { static const char* versions[] = {"4.x", "5.x", "6.x", "7.x", "8.x", "9.x"}; static const u8* version_ctrs[] = { (u8*)0x080D7CAC, (u8*)0x080D858C, (u8*)0x080D748C, (u8*)0x080D740C, (u8*)0x080D74CC, (u8*)0x080D794C }; static const u32 version_ctrs_len = sizeof(version_ctrs) / sizeof(u32); static u8* ctr_start = NULL; if (ctr_start == NULL) { for (u32 i = 0; i < version_ctrs_len; i++) { if (*(u32*)version_ctrs[i] == 0x5C980) { Debug("System version %s", versions[i]); ctr_start = (u8*) version_ctrs[i] + 0x30; } } // If value not in previous list start memory scanning (test range) if (ctr_start == NULL) { for (u8* c = (u8*) 0x080D8FFF; c > (u8*) 0x08000000; c--) { if (*(u32*)c == 0x5C980 && *(u32*)(c + 1) == 0x800005C9) { ctr_start = c + 0x30; Debug("CTR start 0x%08X", ctr_start); break; } } } if (ctr_start == NULL) { Debug("CTR start not found!"); return 1; } } // the ctr is stored backwards in memory if (offset >= 0x0B100000) { // CTRNAND/AGBSAVE region for (u32 i = 0; i < 16; i++) ctr[i] = *(ctr_start + (0xF - i)); } else { // TWL region for (u32 i = 0; i < 16; i++) ctr[i] = *(ctr_start + 0x88 + (0xF - i)); } // increment counter add_ctr(ctr, offset / 0x10); return 0; } u32 DecryptNandToMem(u8* buffer, u32 offset, u32 size, PartitionInfo* partition) { CryptBufferInfo info = {.keyslot = partition->keyslot, .setKeyY = 0, .size = size, .buffer = buffer, .mode = partition->mode}; if(GetNandCtr(info.ctr, offset) != 0) return 1; u32 n_sectors = (size + NAND_SECTOR_SIZE - 1) / NAND_SECTOR_SIZE; u32 start_sector = offset / NAND_SECTOR_SIZE; ReadNandSectors(start_sector, n_sectors, buffer); CryptBuffer(&info); return 0; } u32 DecryptNandToFile(const char* filename, u32 offset, u32 size, PartitionInfo* partition) { u8* buffer = BUFFER_ADDRESS; u32 result = 0; if (!DebugFileCreate(filename, true)) return 1; for (u32 i = 0; i < size; i += NAND_SECTOR_SIZE * SECTORS_PER_READ) { u32 read_bytes = min(NAND_SECTOR_SIZE * SECTORS_PER_READ, (size - i)); ShowProgress(i, size); DecryptNandToMem(buffer, offset + i, read_bytes, partition); if(!DebugFileWrite(buffer, read_bytes, i)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; }