u32 SdPadgen() { u32 result; SdInfo *info = (SdInfo*)0x20316000; u8 movable_seed[0x120] = {0}; // Load console 0x34 keyY from movable.sed if present on SD card if (DebugFileOpen("/movable.sed")) { if (!DebugFileRead(&movable_seed, 0x120, 0)) { FileClose(); return 1; } FileClose(); if (memcmp(movable_seed, "SEED", 4) != 0) { Debug("movable.sed is too corrupt!"); return 1; } setup_aeskey(0x34, AES_BIG_INPUT|AES_NORMAL_INPUT, &movable_seed[0x110]); use_aeskey(0x34); } if (!DebugFileOpen("/SDinfo.bin")) return 1; if (!DebugFileRead(info, 4, 0)) { FileClose(); return 1; } if (!info->n_entries || info->n_entries > MAX_ENTRIES) { Debug("Too many/few entries!"); return 1; } Debug("Number of entries: %i", info->n_entries); if (!DebugFileRead(info->entries, info->n_entries * sizeof(SdInfoEntry), 4)) { FileClose(); return 1; } FileClose(); for(u32 i = 0; i < info->n_entries; i++) { Debug ("Creating pad number: %i. Size (MB): %i", i+1, info->entries[i].size_mb); PadInfo padInfo = {.keyslot = 0x34, .setKeyY = 0, .size_mb = info->entries[i].size_mb}; memcpy(padInfo.CTR, info->entries[i].CTR, 16); memcpy(padInfo.filename, info->entries[i].filename, 180); result = CreatePad(&padInfo); if (!result) Debug("Done!"); else return 1; } return 0; } static u8* FindNandCtr() { 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); for (u32 i = 0; i < version_ctrs_len; i++) { if (*(u32*)version_ctrs[i] == 0x5C980) { Debug("System version %s", versions[i]); return (u8*)(version_ctrs[i] + 0x30); } } // If value not in previous list start memory scanning (test range) for (u8* c = (u8*)0x080D8FFF; c > (u8*)0x08000000; c--) { if (*(u32*)c == 0x5C980 && *(u32*)(c + 1) == 0x800005C9) { Debug("CTR Start 0x%08X", c + 0x30); return c + 0x30; } } return NULL; } u32 DumpPartition(char* filename, u32 offset, u32 size, u32 keyslot) { DecryptBufferInfo info; u8* buffer = BUFFER_ADDRESS; u8* ctrStart = FindNandCtr(); u32 result = 0; Debug("Dumping System NAND Partition. Size (MB): %u", size / (1024 * 1024)); Debug("Filename: %s", filename); if (ctrStart == NULL) return 1; info.keyslot = keyslot; info.setKeyY = 0; info.size = SECTORS_PER_READ * NAND_SECTOR_SIZE; info.buffer = buffer; for (u32 i = 0; i < 16; i++) { info.CTR[i] = *(ctrStart + (0xF - i)); // The CTR is stored backwards in memory. } add_ctr(info.CTR, offset / 0x10); if (!DebugFileCreate(filename, true)) return 1; u32 n_sectors = size / NAND_SECTOR_SIZE; u32 start_sector = offset / NAND_SECTOR_SIZE; for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) { ShowProgress(i, n_sectors); sdmmc_nand_readsectors(start_sector + i, SECTORS_PER_READ, buffer); DecryptBuffer(&info); if (!DebugFileWrite(buffer, NAND_SECTOR_SIZE * SECTORS_PER_READ, i * NAND_SECTOR_SIZE)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; }
u32 NandPadgen() { u8* ctrStart = FindNandCtr(); if (ctrStart == NULL) return 1; u8 ctr[16] = {0x0}; u32 i = 0; for(i = 0; i < 16; i++) ctr[i] = *(ctrStart + (15 - i)); //The CTR is stored backwards in memory. add_ctr(ctr, 0xB93000); //The CTR stored in memory would theoretically be for NAND block 0, so we need to increment it some. u32 keyslot = 0x0; u32 nand_size = 0; switch (GetUnitPlatform()) { case PLATFORM_3DS: keyslot = 0x4; nand_size = 758; break; case PLATFORM_N3DS: keyslot = 0x5; nand_size = 1055; break; } 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"}; memcpy(padInfo.CTR, ctr, 16); u32 result = CreatePad(&padInfo); if(result == 0) { Debug("Done!"); return 0; } else { return 1; } } u32 CreatePad(PadInfo *info) { static const uint8_t zero_buf[16] __attribute__((aligned(16))) = {0}; u8* buffer = BUFFER_ADDRESS; u32 result = 0; if (!FileCreate(info->filename, true)) // No DebugFileCreate() here - messages are already given return 1; if(info->setKeyY) setup_aeskey(info->keyslot, AES_BIG_INPUT | AES_NORMAL_INPUT, info->keyY); use_aeskey(info->keyslot); u8 ctr[16] __attribute__((aligned(32))); memcpy(ctr, info->CTR, 16); u32 size_bytes = info->size_mb * 1024*1024; for (u32 i = 0; i < size_bytes; i += BUFFER_MAX_SIZE) { u32 curr_block_size = min(BUFFER_MAX_SIZE, size_bytes - i); for (u32 j = 0; j < curr_block_size; j+= 16) { set_ctr(AES_BIG_INPUT | AES_NORMAL_INPUT, ctr); aes_decrypt((void*)zero_buf, (void*)buffer + j, ctr, 1, AES_CTR_MODE); add_ctr(ctr, 1); } ShowProgress(i, size_bytes); if (!DebugFileWrite((void*)buffer, curr_block_size, i)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; } u32 NandDumper() { u8* buffer = BUFFER_ADDRESS; u32 nand_size = (GetUnitPlatform() == PLATFORM_3DS) ? 0x3AF00000 : 0x4D800000; u32 result = 0; Debug("Dumping System NAND. Size (MB): %u", nand_size / (1024 * 1024)); if (!DebugFileCreate("/NAND.bin", true)) return 1; u32 n_sectors = nand_size / NAND_SECTOR_SIZE; for (u32 i = 0; i < n_sectors; i += SECTORS_PER_READ) { ShowProgress(i, n_sectors); sdmmc_nand_readsectors(i, SECTORS_PER_READ, buffer); if(!DebugFileWrite(buffer, NAND_SECTOR_SIZE * SECTORS_PER_READ, i * NAND_SECTOR_SIZE)) { result = 1; break; } } ShowProgress(0, 0); FileClose(); return result; } u32 NandPartitionsDumper() { u32 ctrnand_offset; u32 ctrnand_size; u32 keyslot; switch (GetUnitPlatform()) { case PLATFORM_3DS: ctrnand_offset = 0x0B95CA00; ctrnand_size = 0x2F3E3600; keyslot = 0x4; break; case PLATFORM_N3DS: ctrnand_offset = 0x0B95AE00; ctrnand_size = 0x41D2D200; keyslot = 0x5; break; } // see: http://3dbrew.org/wiki/Flash_Filesystem Debug("Dumping firm0.bin: %s!", DumpPartition("firm0.bin", 0x0B130000, 0x00400000, 0x6) == 0 ? "succeeded" : "failed"); Debug("Dumping firm1.bin: %s!", DumpPartition("firm1.bin", 0x0B530000, 0x00400000, 0x6) == 0 ? "succeeded" : "failed"); Debug("Dumping ctrnand.bin: %s!", DumpPartition("ctrnand.bin", ctrnand_offset, ctrnand_size, keyslot) == 0 ? "succeeded" : "failed"); return 0; }
void InitializeNandCrypto(){ u8* ctrStart = FindNandCtr(); for(u32 i = 0; i < 16; i++) NANDCTR[i] = *(ctrStart + (15 - i)); //The CTR is stored backwards in memory. }