Fix remaining NAND offset hardcoding

This commit is contained in:
d0k3 2017-05-31 15:48:14 +02:00
parent 4d91a86d12
commit 348741f963
11 changed files with 235 additions and 244 deletions

View File

@ -4,6 +4,7 @@
#include "nand.h"
#include "game.h"
#include "keydb.h"
#include "ctrtransfer.h"
#include "chainload.h"
u32 IdentifyFileType(const char* path) {
@ -30,7 +31,7 @@ u32 IdentifyFileType(const char* path) {
} else if (ValidateMbrHeader((MbrHeader*) data) == 0) {
MbrHeader* mbr = (MbrHeader*) data;
MbrPartitionInfo* partition0 = mbr->partitions;
bool ctr = (CheckNandMbr(data) & (NAND_TYPE_O3DS|NAND_TYPE_N3DS)); // is this a CTRNAND MBR?
bool ctr = (CheckTransferableMbr(mbr) == 0); // is this a CTRNAND MBR?
if ((partition0->sector + partition0->count) <= (fsize / 0x200)) // size check
return IMG_FAT | (ctr ? FLAG_CTR : 0); // possibly an MBR -> also treat as FAT image
} else if (ValidateCiaHeader((CiaHeader*) data) == 0) {

View File

@ -14,15 +14,25 @@ u32 ValidateFirmHeader(FirmHeader* header, u32 data_size) {
return 1;
u32 firm_size = sizeof(FirmHeader);
int section_arm11 = -1;
int section_arm9 = -1;
for (u32 i = 0; i < 4; i++) {
FirmSectionHeader* section = header->sections + i;
if (!section->size) continue;
if (section->offset < firm_size) return 1;
if ((header->entry_arm11 >= section->address) &&
(header->entry_arm11 < section->address + section->size))
section_arm11 = i;
if ((header->entry_arm9 >= section->address) &&
(header->entry_arm9 < section->address + section->size))
section_arm9 = i;
firm_size = section->offset + section->size;
}
if ((firm_size > FIRM_MAX_SIZE) || (data_size && (firm_size > data_size)))
return 1;
if ((header->entry_arm11 && (section_arm11 < 0)) || (header->entry_arm9 && (section_arm9 < 0)))
return 1;
return 0;
}

View File

@ -525,23 +525,12 @@ u32 VerifyFirmFile(const char* path) {
}
fvx_close(&file);
// check arm11 / arm9 entrypoints
int section_arm11 = -1;
int section_arm9 = -1;
for (u32 i = 0; i < 4; i++) {
FirmSectionHeader* section = header.sections + i;
if ((header.entry_arm11 >= section->address) &&
(header.entry_arm11 < section->address + section->size))
section_arm11 = i;
if ((header.entry_arm9 >= section->address) &&
(header.entry_arm9 < section->address + section->size))
section_arm9 = i;
}
// sections for arm11 / arm9 entrypoints not found?
if ((section_arm11 < 0) || (section_arm9 < 0)) {
ShowPrompt(false, "%s\nARM11/ARM9 entrypoint not found", pathstr);
// no arm11 / arm9 entrypoints?
if (!header.entry_arm9) {
ShowPrompt(false, "%s\nARM9 entrypoint is missing", pathstr);
return 1;
} else if (!header.entry_arm11) {
ShowPrompt(false, "%s\nWarning: ARM11 entrypoint is missing", pathstr);
}
return 0;

View File

@ -4,7 +4,7 @@
u32 ValidateNcsdHeader(NcsdHeader* header) {
u8 zeroes[16] = { 0 };
if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number
(memcmp(header->partitions_fs_type, zeroes, 8) != 0)) // prevent detection of NAND images
(memcmp(header->partitions_fs_type, zeroes, 8) != 0) || !header->mediaId) // prevent detection of NAND images
return 1;
u32 data_units = 0;

View File

@ -1199,15 +1199,18 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
}
u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboard) {
NandPartitionInfo np_info;
if (GetNandPartitionInfo(&np_info, NP_TYPE_BONUS, NP_SUBTYPE_CTR, 0, NAND_SYSNAND) != 0) np_info.count = 0;
const char* optionstr[8];
const char* promptstr = "HOME more... menu.\nSelect action:";
u32 n_opt = 0;
int nandbak = ++n_opt;
int sdformat = ++n_opt;
int bonus = (GetNandUnusedSectors(NAND_SYSNAND) > 0x2000) ? (int) ++n_opt : -1; // 4MB minsize
int bonus = (np_info.count > 0x2000) ? (int) ++n_opt : -1; // 4MB minsize
int multi = (CheckMultiEmuNand()) ? (int) ++n_opt : -1;
int bsupport = ++n_opt;
int hsrestore = ((CheckHealthAndSafetyInject("1:") == 0) || (CheckHealthAndSafetyInject("4:") == 0)) ? (int) ++n_opt : -1;
int nandbak = ++n_opt;
if (nandbak > 0) optionstr[nandbak - 1] = "Backup NAND";
if (sdformat > 0) optionstr[sdformat - 1] = "SD format menu";

View File

@ -9,6 +9,40 @@
#include "ui.h"
/*static const u8 twl_mbr[0x42] = { // encrypted version inside the NCSD NAND header (@0x1BE)
0x00, 0x04, 0x18, 0x00, 0x06, 0x01, 0xA0, 0x3F, 0x97, 0x00, 0x00, 0x00, 0xA9, 0x7D, 0x04, 0x00,
0x00, 0x04, 0x8E, 0x40, 0x06, 0x01, 0xA0, 0xC3, 0x8D, 0x80, 0x04, 0x00, 0xB3, 0x05, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xAA
};*/
static const u8 ctr_mbr_o3ds[0x42] = { // found at the beginning of the CTRNAND partition (O3DS)
0x00, 0x05, 0x2B, 0x00, 0x06, 0x02, 0x42, 0x80, 0x65, 0x01, 0x00, 0x00, 0x1B, 0x9F, 0x17, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xAA
};
static const u8 ctr_mbr_n3ds[0x42] = { // found at the beginning of the CTRNAND partition (N3DS)
0x00, 0x05, 0x1D, 0x00, 0x06, 0x02, 0x82, 0x17, 0x57, 0x01, 0x00, 0x00, 0x69, 0xE9, 0x20, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xAA
};
u32 CheckTransferableMbr(void* data) { // strict checks, custom partitions not allowed
u8* mbr = (u8*) data;
if (memcmp(mbr + (0x200 - sizeof(ctr_mbr_o3ds)), ctr_mbr_o3ds, sizeof(ctr_mbr_o3ds)) == 0)
return 0; // is transferable, O3DS type
else if (memcmp(mbr + (0x200 - sizeof(ctr_mbr_n3ds)), ctr_mbr_n3ds, sizeof(ctr_mbr_n3ds)) == 0)
return 0; // is transferable, N3DS type
return 1;
}
u32 TransferCtrNandImage(const char* path_img, const char* drv) {
if (!CheckWritePermissions(drv)) return 1;

View File

@ -2,4 +2,5 @@
#include "common.h"
u32 CheckTransferableMbr(void* data);
u32 TransferCtrNandImage(const char* path_img, const char* drv);

View File

@ -9,7 +9,6 @@
#include "sdmmc.h"
#include "image.h"
#define NAND_MIN_SECTORS ((!IS_O3DS) ? NAND_MIN_SECTORS_N3DS : NAND_MIN_SECTORS_O3DS)
#define KEY95_SHA256 ((IS_DEVKIT) ? slot0x11Key95dev_sha256 : slot0x11Key95_sha256)
#define SECTOR_SHA256 ((IS_DEVKIT) ? sector0x96dev_sha256 : sector0x96_sha256)
@ -58,48 +57,6 @@ static const u8 sector0x96dev_sha256[0x20] = { // hash for legit sector 0x96 (di
0xB2, 0x91, 0xD9, 0xB1, 0x33, 0x05, 0x79, 0x0D, 0x47, 0xC6, 0x06, 0x98, 0x4C, 0x67, 0xC3, 0x70,
0x09, 0x54, 0xE3, 0x85, 0xDE, 0x47, 0x55, 0xAF, 0xC6, 0xCB, 0x1D, 0x8D, 0xC7, 0x84, 0x5A, 0x64
};
static const u8 nand_magic_n3ds[0x60] = { // NCSD NAND header N3DS magic
0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x04, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x05, 0x00, 0x00, 0x88, 0x05, 0x00, 0x80, 0x01, 0x00, 0x00,
0x80, 0x89, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0xA9, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00,
0x80, 0xC9, 0x05, 0x00, 0x80, 0xF6, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const u8 nand_magic_o3ds[0x60] = { // NCSD NAND header O3DS magic
0x4E, 0x43, 0x53, 0x44, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x04, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x05, 0x00, 0x00, 0x88, 0x05, 0x00, 0x80, 0x01, 0x00, 0x00,
0x80, 0x89, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0xA9, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00,
0x80, 0xC9, 0x05, 0x00, 0x80, 0xAE, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const u8 twl_mbr[0x42] = { // encrypted version inside the NCSD NAND header (@0x1BE)
0x00, 0x04, 0x18, 0x00, 0x06, 0x01, 0xA0, 0x3F, 0x97, 0x00, 0x00, 0x00, 0xA9, 0x7D, 0x04, 0x00,
0x00, 0x04, 0x8E, 0x40, 0x06, 0x01, 0xA0, 0xC3, 0x8D, 0x80, 0x04, 0x00, 0xB3, 0x05, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xAA
};
static const u8 ctr_mbr_o3ds[0x42] = { // found at the beginning of the CTRNAND partition (O3DS)
0x00, 0x05, 0x2B, 0x00, 0x06, 0x02, 0x42, 0x80, 0x65, 0x01, 0x00, 0x00, 0x1B, 0x9F, 0x17, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xAA
};
static const u8 ctr_mbr_n3ds[0x42] = { // found at the beginning of the CTRNAND partition (N3DS)
0x00, 0x05, 0x1D, 0x00, 0x06, 0x02, 0x82, 0x17, 0x57, 0x01, 0x00, 0x00, 0x69, 0xE9, 0x20, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x55, 0xAA
};
static u8 CtrNandCtr[16];
static u8 TwlNandCtr[16];
@ -407,9 +364,37 @@ int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 n
return 0;
}
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd) // in sectors
// shamelessly stolen from myself
// see: https://github.com/d0k3/GodMode9/blob/master/source/game/ncsd.c#L4
u32 ValidateNandNcsdHeader(NandNcsdHeader* header)
{
u32 nand_minsize = 1;
u8 zeroes[16] = { 0 };
if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number
(memcmp(header->partitions_fs_type, zeroes, 8) == 0) || header->mediaId) // prevent detection of cart NCSD images
return 1;
u32 data_units = 0;
u32 firm_count = 0;
for (u32 i = 0; i < 8; i++) {
NandNcsdPartition* partition = header->partitions + i;
u8 np_type = header->partitions_fs_type[i];
if ((i == 0) && !partition->size) return 1; // first content must be there
else if (!partition->size) continue;
if (!np_type) return 1; // partition must have a type
if (partition->offset < data_units)
return 1; // overlapping partitions, failed
data_units = partition->offset + partition->size;
if (np_type == NP_TYPE_FIRM) firm_count++; // count firms
}
if (data_units > header->size) return 1;
if (!firm_count) return 1; // at least one firm is required
return 0;
}
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd)
{
u32 nand_minsize = 0;
for (u32 prt_idx = 0; prt_idx < 8; prt_idx++) {
u32 prt_end = ncsd->partitions[prt_idx].offset + ncsd->partitions[prt_idx].size;
if (prt_end > nand_minsize) nand_minsize = prt_end;
@ -418,6 +403,35 @@ u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd) // in sectors
return nand_minsize;
}
u32 GetNandMinSizeSectors(u32 nand_src)
{
NandNcsdHeader ncsd;
if ((ReadNandSectors((u8*) &ncsd, 0, 1, 0xFF, nand_src) != 0) ||
(ValidateNandNcsdHeader(&ncsd) != 0)) return 0;
return GetNandNcsdMinSizeSectors(&ncsd);
}
u32 GetNandSizeSectors(u32 nand_src)
{
u32 sysnand_sectors = getMMCDevice(0)->total_size;
if (nand_src == NAND_SYSNAND) return sysnand_sectors; // for SysNAND
u32 min_sectors = GetNandMinSizeSectors(nand_src);
if (nand_src == NAND_EMUNAND) { // for EmuNAND
u32 partition_offset = GetPartitionOffsetSector("0:");
u32 emunand_max_sectors = (partition_offset >= (emunand_base_sector + 1)) ? // +1 for safety
partition_offset - (emunand_base_sector + 1) : 0;
u32 emunand_min_sectors = (emunand_base_sector % 0x2000 == 0) ? sysnand_sectors : min_sectors;
return (emunand_min_sectors > emunand_max_sectors) ? 0 : emunand_min_sectors;
} else if (nand_src == NAND_IMGNAND) { // for images
u32 img_sectors = (GetMountState() & IMG_NAND) ? GetMountSize() / 0x200 : 0;
return (img_sectors >= min_sectors) ? img_sectors : 0;
}
return 0;
}
u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, NandNcsdHeader* ncsd)
{
// safety / set keyslot
@ -466,7 +480,10 @@ u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32
u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, u32 nand_src)
{
// check NAND NCSD integrity(!!!)
// workaround for info == NULL
NandPartitionInfo dummy;
if (!info) info = &dummy;
// workaround for ZERONAND
if (nand_src == NAND_ZERONAND) nand_src = NAND_SYSNAND;
@ -474,12 +491,12 @@ u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 ind
u8 header[0x200];
ReadNandSectors(header, 0x00, 1, 0xFF, nand_src);
NandNcsdHeader* ncsd = (NandNcsdHeader*) header;
if (((type == NP_TYPE_FAT) && (GetNandNcsdPartitionInfo(info, NP_TYPE_STD, subtype, 0, ncsd) != 0)) ||
if ((ValidateNandNcsdHeader(ncsd) != 0) ||
((type == NP_TYPE_FAT) && (GetNandNcsdPartitionInfo(info, NP_TYPE_STD, subtype, 0, ncsd) != 0)) ||
((type != NP_TYPE_FAT) && (GetNandNcsdPartitionInfo(info, type, subtype, index, ncsd) != 0)))
return 1; // not found
// size of bonus partition
if (type == NP_TYPE_BONUS) {
if (type == NP_TYPE_BONUS) { // size of bonus partition
info->count = GetNandSizeSectors(nand_src) - info->sector;
} else if (type == NP_TYPE_FAT) { // FAT type specific stuff
ReadNandSectors(header, info->sector, 1, info->keyslot, nand_src);
@ -493,76 +510,6 @@ u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 ind
}
return 0;
}
u32 CheckNandMbr(u8* mbr)
{
if (memcmp(mbr + (0x200 - sizeof(twl_mbr)), twl_mbr, sizeof(twl_mbr)) == 0)
return NAND_TYPE_TWL; // TWLNAND MBR (included in NAND header)
else if (memcmp(mbr + (0x200 - sizeof(ctr_mbr_o3ds)), ctr_mbr_o3ds, sizeof(ctr_mbr_o3ds)) == 0)
return NAND_TYPE_O3DS; // CTRNAND MBR (@0x05C980)
else if (memcmp(mbr + (0x200 - sizeof(ctr_mbr_n3ds)), ctr_mbr_n3ds, sizeof(ctr_mbr_n3ds)) == 0)
return NAND_TYPE_N3DS; // CTRNAND MBR (@0x05C980)
else return 0;
}
u32 CheckNandHeader(u8* header)
{
// TWL MBR check
u8 header_dec[0x200];
memcpy(header_dec, header, 0x200);
CryptNand(header_dec, 0, 1, 0x03);
if (CheckNandMbr(header_dec) != NAND_TYPE_TWL)
return 0; // header does not belong to console
// header type check
if (memcmp(header + 0x100, nand_magic_n3ds, sizeof(nand_magic_n3ds)) == 0)
return (IS_O3DS) ? 0 : NAND_TYPE_N3DS;
else if (memcmp(header + 0x100, nand_magic_o3ds, sizeof(nand_magic_o3ds)) == 0)
return NAND_TYPE_O3DS;
return 0;
}
u32 CheckNandType(u32 nand_src)
{
if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF, nand_src) != 0)
return 0;
if (memcmp(NAND_BUFFER + 0x100, nand_magic_n3ds, sizeof(nand_magic_n3ds)) == 0) {
return (IS_O3DS) ? 0 : NAND_TYPE_N3DS;
} else if (memcmp(NAND_BUFFER + 0x100, nand_magic_o3ds, sizeof(nand_magic_o3ds)) == 0) {
u8 magic[8] = {0xE9, 0x00, 0x00, 0x43, 0x54, 0x52, 0x20, 0x20};
if (ReadNandSectors(NAND_BUFFER, 0x5CAE5, 1, 0x04, nand_src) != 0)
return 0;
return ((IS_O3DS) || (memcmp(magic, NAND_BUFFER, 8) == 0)) ?
NAND_TYPE_O3DS : NAND_TYPE_NO3DS;
}
return 0;
}
u64 GetNandSizeSectors(u32 nand_src)
{
u32 sysnand_sectors = getMMCDevice(0)->total_size;
if (nand_src == NAND_EMUNAND) { // for EmuNAND
u32 partition_offset = GetPartitionOffsetSector("0:");
u32 emunand_max_sectors = (partition_offset >= (emunand_base_sector + 1)) ? // +1 for safety
partition_offset - (emunand_base_sector + 1) : 0;
u32 emunand_min_sectors = (emunand_base_sector % 0x2000 == 0) ? sysnand_sectors : NAND_MIN_SECTORS;
return (emunand_min_sectors > emunand_max_sectors) ? 0 : emunand_min_sectors;
} else if (nand_src == NAND_IMGNAND) { // for images
u32 img_sectors = (GetMountState() & IMG_NAND) ? GetMountSize() / 0x200 : 0;
return (img_sectors >= sysnand_sectors) ? sysnand_sectors : (img_sectors >= NAND_MIN_SECTORS) ? NAND_MIN_SECTORS : 0;
} else if (nand_src == NAND_SYSNAND) { // for SysNAND
return sysnand_sectors;
}
return 0;
}
u64 GetNandUnusedSectors(u32 nand_src)
{
return GetNandSizeSectors(nand_src) - NAND_MIN_SECTORS;
}
u32 GetLegitSector0x96(u8* sector)
@ -613,34 +560,36 @@ u32 GetNandCid(void* cid) {
bool CheckMultiEmuNand(void)
{
// this only checks for the theoretical possibility
return (GetPartitionOffsetSector("0:") >= (u64) (align(NAND_MIN_SECTORS + 1, 0x2000) * 2));
u32 emunand_min_sectors = GetNandMinSizeSectors(NAND_EMUNAND);
return (emunand_min_sectors && (GetPartitionOffsetSector("0:") >= (u64) (align(emunand_min_sectors + 1, 0x2000) * 2)));
}
u32 InitEmuNandBase(bool reset)
{
if (!reset) {
u32 last_valid = emunand_base_sector;
u32 emunand_min_sectors = GetNandMinSizeSectors(NAND_EMUNAND);
// legacy type multiNAND
u32 legacy_sectors = (getMMCDevice(0)->total_size > 0x200000) ? 0x400000 : 0x200000;
emunand_base_sector += legacy_sectors - (emunand_base_sector % legacy_sectors);
if (GetNandSizeSectors(NAND_EMUNAND) && CheckNandType(NAND_EMUNAND))
if (GetNandSizeSectors(NAND_EMUNAND) && (GetNandPartitionInfo(NULL, NP_TYPE_NCSD, NP_SUBTYPE_CTR, 0, NAND_EMUNAND) == 0))
return emunand_base_sector; // GW type EmuNAND
emunand_base_sector++;
if (GetNandSizeSectors(NAND_EMUNAND) && CheckNandType(NAND_EMUNAND))
if (GetNandSizeSectors(NAND_EMUNAND) && (GetNandPartitionInfo(NULL, NP_TYPE_NCSD, NP_SUBTYPE_CTR, 0, NAND_EMUNAND) == 0))
return emunand_base_sector; // RedNAND type EmuNAND
// compact type multiNAND
if (last_valid % 0x2000 <= 1) {
u32 compact_sectors = align(NAND_MIN_SECTORS + 1, 0x2000);
if (emunand_min_sectors && (last_valid % 0x2000 <= 1)) {
u32 compact_sectors = align(emunand_min_sectors + 1, 0x2000);
emunand_base_sector = last_valid + compact_sectors;
if (GetNandSizeSectors(NAND_EMUNAND) && CheckNandType(NAND_EMUNAND))
if (GetNandSizeSectors(NAND_EMUNAND) && (GetNandPartitionInfo(NULL, NP_TYPE_NCSD, NP_SUBTYPE_CTR, 0, NAND_EMUNAND) == 0))
return emunand_base_sector;
}
}
emunand_base_sector = 0x000000; // GW type EmuNAND
if (!CheckNandType(NAND_EMUNAND))
if (GetNandPartitionInfo(NULL, NP_TYPE_NCSD, NP_SUBTYPE_CTR, 0, NAND_EMUNAND) != 0)
emunand_base_sector = 0x000001; // RedNAND type EmuNAND
return emunand_base_sector;

View File

@ -6,35 +6,10 @@
#define NAND_EMUNAND (1UL<<1)
#define NAND_IMGNAND (1UL<<2)
#define NAND_ZERONAND (1UL<<3)
#define NAND_TYPE_O3DS (1UL<<4)
#define NAND_TYPE_N3DS (1UL<<5)
#define NAND_TYPE_NO3DS (1UL<<6)
#define NAND_TYPE_TWL (1UL<<7)
// minimum size of NAND memory
#define NAND_MIN_SECTORS_O3DS 0x1D7800
#define NAND_MIN_SECTORS_N3DS 0x26C000
// start sectors of partitions
#define SECTOR_TWL 0x000000
// hardcoded start sectors
#define SECTOR_D0K3 0x000001
#define SECTOR_SECRET 0x000096
#define SECTOR_TWLN 0x000097
#define SECTOR_TWLP 0x04808D
#define SECTOR_AGBSAVE 0x058800
#define SECTOR_FIRM0 0x058980
#define SECTOR_FIRM1 0x05A980
#define SECTOR_CTR 0x05C980
// sizes of partitions (in sectors)
#define SIZE_TWL 0x058800
#define SIZE_TWLN 0x047DA9
#define SIZE_TWLP 0x0105B3
#define SIZE_AGBSAVE 0x000180
#define SIZE_FIRM0 0x002000
#define SIZE_FIRM1 0x002000
#define SIZE_CTR_O3DS 0x17AE80
#define SIZE_CTR_N3DS 0x20F680
// filenames for sector 0x96
#define SECTOR_NAME "sector0x96.bin"
@ -98,14 +73,11 @@ int WriteNandBytes(const u8* buffer, u64 offset, u64 count, u32 keyslot, u32 nan
int ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, u32 src);
int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 dest);
u32 ValidateNandNcsdHeader(NandNcsdHeader* header);
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd);
u64 GetNandSizeSectors(u32 src);
u32 GetNandSizeSectors(u32 src);
u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, NandNcsdHeader* ncsd);
u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, u32 nand_src);
u64 GetNandUnusedSectors(u32 src);
u32 CheckNandMbr(u8* mbr);
u32 CheckNandHeader(u8* header);
u32 CheckNandType(u32 src);
u32 GetLegitSector0x96(u8* sector);
u32 GetOtpHash(void* hash);

View File

@ -61,11 +61,6 @@ u32 BuildEssentialBackup(const char* path, EssentialBackup* essential) {
// mount original file
InitImgFS(path_bak);
// check sizes - stubbed
/*if ((files[0].size != 0x200) || (files[1].size != 0x111) ||
((files[2].size != 0x120) && (files[2].size != 0x140)) || (files[3].size != 0x110))
return 1;*/
// fill nand cid / otp hash
if (GetNandCid(&(essential->nand_cid)) != 0) return 1;
@ -86,7 +81,7 @@ u32 CheckEmbeddedBackup(const char* path) {
EssentialBackup* embedded = (EssentialBackup*) (TEMP_BUFFER + sizeof(EssentialBackup));
UINT btr;
if ((BuildEssentialBackup(path, essential) != 0) ||
(fvx_qread(path, embedded, 0x200, sizeof(EssentialBackup), &btr) != FR_OK) ||
(fvx_qread(path, embedded, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), &btr) != FR_OK) ||
(memcmp(embedded, essential, sizeof(EssentialBackup)) != 0))
return 1;
return 0;
@ -97,20 +92,15 @@ u32 EmbedEssentialBackup(const char* path) {
UINT btw;
// leaving out the write permissions check here, it's okay
if ((BuildEssentialBackup(path, essential) != 0) ||
(CheckNandHeader(essential->nand_hdr) == 0) || // 0 -> header not recognized
(fvx_qwrite(path, essential, 0x200, sizeof(EssentialBackup), &btw) != FR_OK) ||
(ValidateNandNcsdHeader((NandNcsdHeader*) essential->nand_hdr) != 0) ||
(fvx_qwrite(path, essential, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), &btw) != FR_OK) ||
(btw != sizeof(EssentialBackup)))
return 1;
return 0;
}
u32 ValidateNandDump(const char* path) {
const u32 mbr_sectors[] = { SECTOR_TWL, SECTOR_CTR };
const u32 firm_sectors[] = { SECTOR_FIRM0, SECTOR_FIRM1 };
u8 buffer[0x200];
FirmHeader firm;
MbrHeader mbr;
u32 nand_type;
NandPartitionInfo info;
FIL file;
// truncated path string
@ -122,25 +112,29 @@ u32 ValidateNandDump(const char* path) {
return 1;
// check NAND header
if ((ReadNandFile(&file, buffer, 0, 1, 0xFF) != 0) ||
((nand_type = CheckNandHeader(buffer)) == 0)) { // zero means header not recognized
ShowPrompt(false, "%s\nHeader does not belong to device", pathstr);
NandNcsdHeader ncsd;
if ((ReadNandFile(&file, &ncsd, 0, 1, 0xFF) != 0) || (ValidateNandNcsdHeader(&ncsd) != 0)) {
ShowPrompt(false, "%s\nNCSD header is not valid", pathstr);
fvx_close(&file);
return 1;
}
// check size
if (fvx_size(&file) < ((nand_type == NAND_TYPE_O3DS) ? NAND_MIN_SECTORS_O3DS : NAND_MIN_SECTORS_N3DS)) {
if (fvx_size(&file) < (GetNandNcsdMinSizeSectors(&ncsd) * 0x200)) {
ShowPrompt(false, "%s\nNAND dump misses data", pathstr);
fvx_close(&file);
return 1;
}
// check MBRs (TWL & CTR)
for (u32 i = 0; i < sizeof(mbr_sectors) / sizeof(u32); i++) {
u32 keyslot = (i == 0) ? 0x03 : (nand_type == NAND_TYPE_O3DS) ? 0x04 : 0x05;
// check TWL & CTR FAT partitions
for (u32 i = 0; i < 2; i++) {
char* section_type = (i) ? "CTR" : "MBR";
if ((ReadNandFile(&file, &mbr, mbr_sectors[i], 1, keyslot) != 0) ||
if (i == 0) { // check TWL first, then CTR
if (GetNandNcsdPartitionInfo(&info, NP_TYPE_STD, NP_SUBTYPE_TWL, 0, &ncsd) != 0) return 1;
} else if ((GetNandNcsdPartitionInfo(&info, NP_TYPE_STD, NP_SUBTYPE_CTR, 0, &ncsd) != 0) &&
(GetNandNcsdPartitionInfo(&info, NP_TYPE_STD, NP_SUBTYPE_CTR_N, 0, &ncsd) != 0)) return 1;
MbrHeader mbr;
if ((ReadNandFile(&file, &mbr, info.sector, 1, info.keyslot) != 0) ||
(ValidateMbrHeader(&mbr) != 0)) {
ShowPrompt(false, "%s\nError: %s MBR is corrupt", pathstr, section_type);
fvx_close(&file);
@ -148,9 +142,10 @@ u32 ValidateNandDump(const char* path) {
}
for (u32 p = 0; p < 4; p++) {
u32 p_sector = mbr.partitions[p].sector;
u8 fat[0x200];
if (!p_sector) continue;
if ((ReadNandFile(&file, buffer, mbr_sectors[i] + p_sector, 1, keyslot) != 0) ||
(ValidateFatHeader(buffer) != 0)) {
if ((ReadNandFile(&file, fat, info.sector + p_sector, 1, info.keyslot) != 0) ||
(ValidateFatHeader(fat) != 0)) {
ShowPrompt(false, "%s\nError: %s partition%u is corrupt", pathstr, section_type, p);
fvx_close(&file);
return 1;
@ -158,56 +153,54 @@ u32 ValidateNandDump(const char* path) {
}
}
// check FIRMs (FIRM1 must be valid)
for (u32 i = 0; i < sizeof(firm_sectors) / sizeof(u32); i++) {
u32 keyslot = 0x06;
if ((ReadNandFile(&file, &firm, firm_sectors[i], 1, keyslot) != 0) ||
(ValidateFirmHeader(&firm, 0) != 0) ||
(getbe32(firm.dec_magic) != 0)) { // decrypted firms are not allowed
ShowPrompt(false, "%s\nError: FIRM%u header is corrupt", pathstr, i);
// check FIRMs (at least one FIRM must be valid)
// check all 8 firms, also check if ARM9 & ARM11 entrypoints are available
for (u32 f = 0; f <= 8; f++) {
FirmHeader firm;
if (GetNandNcsdPartitionInfo(&info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, f, &ncsd) != 0) {
ShowPrompt(false, "%s\nNo valid FIRM found", pathstr);
fvx_close(&file);
return 1;
}
if ((ReadNandFile(&file, &firm, info.sector, 1, info.keyslot) != 0) ||
(ValidateFirmHeader(&firm, 0) != 0) || (getbe32(firm.dec_magic) != 0) || // decrypted firms are not allowed
(!firm.entry_arm9) || (!firm.entry_arm11)) // arm9 / arm11 entry points must be there
continue;
// hash verify all available sections
if (i == 0) continue; // no hash checks for FIRM0 (might be A9LH)
for (u32 s = 0; s < 4; s++) {
u32 s;
for (s = 0; s < 4; s++) {
FirmSectionHeader* section = firm.sections + s;
u32 sector = firm_sectors[i] + (section->offset / 0x200);
u32 sector = info.sector + (section->offset / 0x200);
u32 count = section->size / 0x200;
if (!count) continue;
sha_init(SHA256_MODE);
// relies on sections being aligned to sectors
for (u32 c = 0; c < count; c += MAIN_BUFFER_SIZE / 0x200) {
u32 read_sectors = min(MAIN_BUFFER_SIZE / 0x200, (count - c));
ReadNandFile(&file, MAIN_BUFFER, sector + c, read_sectors, keyslot);
ReadNandFile(&file, MAIN_BUFFER, sector + c, read_sectors, info.keyslot);
sha_update(MAIN_BUFFER, read_sectors * 0x200);
}
u8 hash[0x20];
sha_get(hash);
if (memcmp(hash, section->hash, 0x20) != 0) {
ShowPrompt(false, "%s\nFIRM%u/%u hash mismatch", pathstr, i, s);
fvx_close(&file);
return 1;
}
if (memcmp(hash, section->hash, 0x20) != 0) break;
}
if (s >= 4) break; // valid FIRM found
}
fvx_close(&file);
return 0;
}
u32 SafeRestoreNandDump(const char* path) {
u32 safe_sectors[] = { SAFE_SECTORS };
FIL file;
if ((ValidateNandDump(path) != 0) && // NAND dump validation
!ShowPrompt(true, "NAND dump corrupt or not from console.\nStill continue?"))
!ShowPrompt(true, "Error: NAND dump is corrupt.\nStill continue?"))
return 1;
if (!IS_A9LH) {
ShowPrompt(false, "Error: A9LH/B9S not detected.");
ShowPrompt(false, "Error: B9S/A9LH not detected.");
return 1;
}
if (!ShowUnlockSequence(5, "!WARNING!\n \nProceeding will overwrite the\nSysNAND with the provided dump.\n \n(A9LH/B9S will be left intact.)"))
if (!ShowUnlockSequence(5, "!WARNING!\n \nProceeding will overwrite the\nSysNAND with the provided dump.\n \n(B9S/A9LH will be left intact.)"))
return 1;
if (!SetWritePermissions(PERM_SYS_LVL1, true)) return 1;
@ -217,39 +210,81 @@ u32 SafeRestoreNandDump(const char* path) {
memset(essential, 0, sizeof(EssentialBackup));
// open file, get size
FIL file;
if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return 1;
u32 fsize = fvx_size(&file);
safe_sectors[(sizeof(safe_sectors) / sizeof(u32)) - 1] = fsize / 0x200;
// get NCSD headers from image and SysNAND
NandNcsdHeader ncsd_loc, ncsd_img;
MbrHeader twl_mbr_img;
if ((ReadNandFile(&file, &ncsd_img, 0, 1, 0xFF) != 0) ||
(ReadNandFile(&file, &twl_mbr_img, 0, 1, 0x03) != 0) ||
(ReadNandSectors((u8*) &ncsd_loc, 0, 1, 0xFF, NAND_SYSNAND) != 0)) {
fvx_close(&file);
return 1;
}
// compare NCSD header partitioning
bool header_inject = false;
if (memcmp(&ncsd_loc, &ncsd_img, sizeof(NandNcsdHeader)) != 0) {
for (int p = -1; p <= 8; p++) {
NandPartitionInfo info_loc = { 0 };
NandPartitionInfo info_img = { 0 };
u32 idx = (p < 0) ? 0 : p;
u32 type = (p < 0) ? NP_TYPE_SECRET : NP_TYPE_FIRM;
u32 subtype = (p < 0) ? NP_SUBTYPE_CTR_N : NP_SUBTYPE_CTR;
bool np_loc = (GetNandNcsdPartitionInfo(&info_loc, type, subtype, idx, &ncsd_loc) == 0);
bool np_img = (GetNandNcsdPartitionInfo(&info_img, type, subtype, idx, &ncsd_img) == 0);
if (!np_loc && !np_img) {
if (p >= 1) header_inject = true;
break;
}
if ((np_loc != np_img) || (memcmp(&info_loc, &info_img, sizeof(NandPartitionInfo)) != 0))
break;
}
if (!header_inject || (ValidateNandNcsdHeader(&ncsd_img) != 0) || (ValidateMbrHeader(&twl_mbr_img) != 0)) {
ShowPrompt(false, "Image NCSD corrupt or customized,\nsafe restore is not possible!");
fvx_close(&file);
return 1;
}
}
// additional warning for elevated write permissions
if (header_inject) {
if (!ShowPrompt(true, "!WARNING!\n \nNCSD differs between image and local,\nelevated write permissions required\n \nProceed on your own risk?") ||
!SetWritePermissions(PERM_SYS_LVL3, true)) {
fvx_close(&file);
return 1;
}
}
// main processing loop
u32 ret = 0;
u32 sector0 = 1; // start at the sector after NCSD
if (!ShowProgress(0, 0, path)) ret = 1;
for (u32 p = 0; p < sizeof(safe_sectors) / sizeof(u32); p += 2) {
u32 sector0 = safe_sectors[p];
u32 sector1 = safe_sectors[p+1];
fvx_lseek(&file, sector0 * 0x200);
for (int p = -1; p < 8; p++) {
NandPartitionInfo np_info;
u32 idx = (p < 0) ? 0 : p;
u32 type = (p < 0) ? NP_TYPE_SECRET : NP_TYPE_FIRM;
u32 subtype = (p < 0) ? NP_SUBTYPE_CTR_N : NP_SUBTYPE_CTR;
u32 sector1 = (GetNandNcsdPartitionInfo(&np_info, type, subtype, idx, &ncsd_loc) == 0) ? np_info.sector : fsize / 0x200;
for (u32 s = sector0; (s < sector1) && (ret == 0); s += MAIN_BUFFER_SIZE / 0x200) {
UINT btr;
u32 count = min(MAIN_BUFFER_SIZE / 0x200, (sector1 - s));
if (fvx_read(&file, MAIN_BUFFER, count * 0x200, &btr) != FR_OK) ret = 1;
if (ReadNandFile(&file, MAIN_BUFFER, s, count, 0xFF)) ret = 1;
if (WriteNandSectors(MAIN_BUFFER, s, count, 0xFF, NAND_SYSNAND)) ret = 1;
if (btr != count * 0x200) ret = 1;
if (!ShowProgress(s + count, fsize / 0x200, path)) ret = 1;
}
}
if (!IS_O3DS && (CheckNandType(NAND_SYSNAND) != NAND_TYPE_N3DS) && (ret == 0)) {
// NAND header handling / special case, only for downgraded N3DS
u8 header[0x200]; // NAND header
UINT btr;
fvx_lseek(&file, 0);
if ((fvx_read(&file, header, 0x200, &btr) == FR_OK) &&
(CheckNandHeader(header) == NAND_TYPE_N3DS) &&
(WriteNandSectors(header, 0, 1, 0xFF, NAND_SYSNAND) != 0))
ret = 1;
if (sector1 == fsize / 0x200) break; // at file end
sector0 = np_info.sector + np_info.count;
}
fvx_close(&file);
// NCSD header inject, should only be required with 2.1 local NANDs on N3DS
if (header_inject && (ret == 0) &&
(WriteNandSectors((u8*) &ncsd_img, 0, 1, 0xFF, NAND_SYSNAND) != 0))
ret = 1;
// inject essential backup to NAND
WriteNandSectors((u8*) essential, ESSENTIAL_SECTOR, (sizeof(EssentialBackup) + 0x1FF) / 0x200, 0xFF, NAND_SYSNAND);

View File

@ -2,9 +2,6 @@
#include "common.h"
#define SAFE_SECTORS 0x000000 + 0x1, SECTOR_SECRET, SECTOR_SECRET + 0x1, \
SECTOR_FIRM0, SECTOR_CTR, 0x000000 // last one is a placeholder
u32 CheckEmbeddedBackup(const char* path);
u32 EmbedEssentialBackup(const char* path);
u32 ValidateNandDump(const char* path);