mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Fix remaining NAND offset hardcoding
This commit is contained in:
parent
4d91a86d12
commit
348741f963
@ -4,6 +4,7 @@
|
|||||||
#include "nand.h"
|
#include "nand.h"
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "keydb.h"
|
#include "keydb.h"
|
||||||
|
#include "ctrtransfer.h"
|
||||||
#include "chainload.h"
|
#include "chainload.h"
|
||||||
|
|
||||||
u32 IdentifyFileType(const char* path) {
|
u32 IdentifyFileType(const char* path) {
|
||||||
@ -30,7 +31,7 @@ u32 IdentifyFileType(const char* path) {
|
|||||||
} else if (ValidateMbrHeader((MbrHeader*) data) == 0) {
|
} else if (ValidateMbrHeader((MbrHeader*) data) == 0) {
|
||||||
MbrHeader* mbr = (MbrHeader*) data;
|
MbrHeader* mbr = (MbrHeader*) data;
|
||||||
MbrPartitionInfo* partition0 = mbr->partitions;
|
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
|
if ((partition0->sector + partition0->count) <= (fsize / 0x200)) // size check
|
||||||
return IMG_FAT | (ctr ? FLAG_CTR : 0); // possibly an MBR -> also treat as FAT image
|
return IMG_FAT | (ctr ? FLAG_CTR : 0); // possibly an MBR -> also treat as FAT image
|
||||||
} else if (ValidateCiaHeader((CiaHeader*) data) == 0) {
|
} else if (ValidateCiaHeader((CiaHeader*) data) == 0) {
|
||||||
|
@ -14,15 +14,25 @@ u32 ValidateFirmHeader(FirmHeader* header, u32 data_size) {
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
u32 firm_size = sizeof(FirmHeader);
|
u32 firm_size = sizeof(FirmHeader);
|
||||||
|
int section_arm11 = -1;
|
||||||
|
int section_arm9 = -1;
|
||||||
for (u32 i = 0; i < 4; i++) {
|
for (u32 i = 0; i < 4; i++) {
|
||||||
FirmSectionHeader* section = header->sections + i;
|
FirmSectionHeader* section = header->sections + i;
|
||||||
if (!section->size) continue;
|
if (!section->size) continue;
|
||||||
if (section->offset < firm_size) return 1;
|
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;
|
firm_size = section->offset + section->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((firm_size > FIRM_MAX_SIZE) || (data_size && (firm_size > data_size)))
|
if ((firm_size > FIRM_MAX_SIZE) || (data_size && (firm_size > data_size)))
|
||||||
return 1;
|
return 1;
|
||||||
|
if ((header->entry_arm11 && (section_arm11 < 0)) || (header->entry_arm9 && (section_arm9 < 0)))
|
||||||
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -525,23 +525,12 @@ u32 VerifyFirmFile(const char* path) {
|
|||||||
}
|
}
|
||||||
fvx_close(&file);
|
fvx_close(&file);
|
||||||
|
|
||||||
// check arm11 / arm9 entrypoints
|
// no arm11 / arm9 entrypoints?
|
||||||
int section_arm11 = -1;
|
if (!header.entry_arm9) {
|
||||||
int section_arm9 = -1;
|
ShowPrompt(false, "%s\nARM9 entrypoint is missing", pathstr);
|
||||||
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);
|
|
||||||
return 1;
|
return 1;
|
||||||
|
} else if (!header.entry_arm11) {
|
||||||
|
ShowPrompt(false, "%s\nWarning: ARM11 entrypoint is missing", pathstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
u32 ValidateNcsdHeader(NcsdHeader* header) {
|
u32 ValidateNcsdHeader(NcsdHeader* header) {
|
||||||
u8 zeroes[16] = { 0 };
|
u8 zeroes[16] = { 0 };
|
||||||
if ((memcmp(header->magic, "NCSD", 4) != 0) || // check magic number
|
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;
|
return 1;
|
||||||
|
|
||||||
u32 data_units = 0;
|
u32 data_units = 0;
|
||||||
|
@ -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) {
|
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* optionstr[8];
|
||||||
const char* promptstr = "HOME more... menu.\nSelect action:";
|
const char* promptstr = "HOME more... menu.\nSelect action:";
|
||||||
u32 n_opt = 0;
|
u32 n_opt = 0;
|
||||||
|
int nandbak = ++n_opt;
|
||||||
int sdformat = ++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 multi = (CheckMultiEmuNand()) ? (int) ++n_opt : -1;
|
||||||
int bsupport = ++n_opt;
|
int bsupport = ++n_opt;
|
||||||
int hsrestore = ((CheckHealthAndSafetyInject("1:") == 0) || (CheckHealthAndSafetyInject("4:") == 0)) ? (int) ++n_opt : -1;
|
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 (nandbak > 0) optionstr[nandbak - 1] = "Backup NAND";
|
||||||
if (sdformat > 0) optionstr[sdformat - 1] = "SD format menu";
|
if (sdformat > 0) optionstr[sdformat - 1] = "SD format menu";
|
||||||
|
@ -9,6 +9,40 @@
|
|||||||
#include "ui.h"
|
#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) {
|
u32 TransferCtrNandImage(const char* path_img, const char* drv) {
|
||||||
if (!CheckWritePermissions(drv)) return 1;
|
if (!CheckWritePermissions(drv)) return 1;
|
||||||
|
|
||||||
|
@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
u32 CheckTransferableMbr(void* data);
|
||||||
u32 TransferCtrNandImage(const char* path_img, const char* drv);
|
u32 TransferCtrNandImage(const char* path_img, const char* drv);
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#include "sdmmc.h"
|
#include "sdmmc.h"
|
||||||
#include "image.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 KEY95_SHA256 ((IS_DEVKIT) ? slot0x11Key95dev_sha256 : slot0x11Key95_sha256)
|
||||||
#define SECTOR_SHA256 ((IS_DEVKIT) ? sector0x96dev_sha256 : sector0x96_sha256)
|
#define SECTOR_SHA256 ((IS_DEVKIT) ? sector0x96dev_sha256 : sector0x96_sha256)
|
||||||
@ -59,48 +58,6 @@ static const u8 sector0x96dev_sha256[0x20] = { // hash for legit sector 0x96 (di
|
|||||||
0x09, 0x54, 0xE3, 0x85, 0xDE, 0x47, 0x55, 0xAF, 0xC6, 0xCB, 0x1D, 0x8D, 0xC7, 0x84, 0x5A, 0x64
|
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 CtrNandCtr[16];
|
||||||
static u8 TwlNandCtr[16];
|
static u8 TwlNandCtr[16];
|
||||||
static u8 OtpSha256[32] = { 0 };
|
static u8 OtpSha256[32] = { 0 };
|
||||||
@ -407,9 +364,37 @@ int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 n
|
|||||||
return 0;
|
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++) {
|
for (u32 prt_idx = 0; prt_idx < 8; prt_idx++) {
|
||||||
u32 prt_end = ncsd->partitions[prt_idx].offset + ncsd->partitions[prt_idx].size;
|
u32 prt_end = ncsd->partitions[prt_idx].offset + ncsd->partitions[prt_idx].size;
|
||||||
if (prt_end > nand_minsize) nand_minsize = prt_end;
|
if (prt_end > nand_minsize) nand_minsize = prt_end;
|
||||||
@ -418,6 +403,35 @@ u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd) // in sectors
|
|||||||
return nand_minsize;
|
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)
|
u32 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, NandNcsdHeader* ncsd)
|
||||||
{
|
{
|
||||||
// safety / set keyslot
|
// 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)
|
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
|
// workaround for ZERONAND
|
||||||
if (nand_src == NAND_ZERONAND) nand_src = NAND_SYSNAND;
|
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];
|
u8 header[0x200];
|
||||||
ReadNandSectors(header, 0x00, 1, 0xFF, nand_src);
|
ReadNandSectors(header, 0x00, 1, 0xFF, nand_src);
|
||||||
NandNcsdHeader* ncsd = (NandNcsdHeader*) header;
|
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)))
|
((type != NP_TYPE_FAT) && (GetNandNcsdPartitionInfo(info, type, subtype, index, ncsd) != 0)))
|
||||||
return 1; // not found
|
return 1; // not found
|
||||||
|
|
||||||
// size of bonus partition
|
if (type == NP_TYPE_BONUS) { // size of bonus partition
|
||||||
if (type == NP_TYPE_BONUS) {
|
|
||||||
info->count = GetNandSizeSectors(nand_src) - info->sector;
|
info->count = GetNandSizeSectors(nand_src) - info->sector;
|
||||||
} else if (type == NP_TYPE_FAT) { // FAT type specific stuff
|
} else if (type == NP_TYPE_FAT) { // FAT type specific stuff
|
||||||
ReadNandSectors(header, info->sector, 1, info->keyslot, nand_src);
|
ReadNandSectors(header, info->sector, 1, info->keyslot, nand_src);
|
||||||
@ -495,76 +512,6 @@ u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 ind
|
|||||||
return 0;
|
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)
|
u32 GetLegitSector0x96(u8* sector)
|
||||||
{
|
{
|
||||||
// secret sector already in buffer?
|
// secret sector already in buffer?
|
||||||
@ -613,34 +560,36 @@ u32 GetNandCid(void* cid) {
|
|||||||
bool CheckMultiEmuNand(void)
|
bool CheckMultiEmuNand(void)
|
||||||
{
|
{
|
||||||
// this only checks for the theoretical possibility
|
// 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)
|
u32 InitEmuNandBase(bool reset)
|
||||||
{
|
{
|
||||||
if (!reset) {
|
if (!reset) {
|
||||||
u32 last_valid = emunand_base_sector;
|
u32 last_valid = emunand_base_sector;
|
||||||
|
u32 emunand_min_sectors = GetNandMinSizeSectors(NAND_EMUNAND);
|
||||||
|
|
||||||
// legacy type multiNAND
|
// legacy type multiNAND
|
||||||
u32 legacy_sectors = (getMMCDevice(0)->total_size > 0x200000) ? 0x400000 : 0x200000;
|
u32 legacy_sectors = (getMMCDevice(0)->total_size > 0x200000) ? 0x400000 : 0x200000;
|
||||||
emunand_base_sector += legacy_sectors - (emunand_base_sector % legacy_sectors);
|
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
|
return emunand_base_sector; // GW type EmuNAND
|
||||||
emunand_base_sector++;
|
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
|
return emunand_base_sector; // RedNAND type EmuNAND
|
||||||
|
|
||||||
// compact type multiNAND
|
// compact type multiNAND
|
||||||
if (last_valid % 0x2000 <= 1) {
|
if (emunand_min_sectors && (last_valid % 0x2000 <= 1)) {
|
||||||
u32 compact_sectors = align(NAND_MIN_SECTORS + 1, 0x2000);
|
u32 compact_sectors = align(emunand_min_sectors + 1, 0x2000);
|
||||||
emunand_base_sector = last_valid + compact_sectors;
|
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;
|
return emunand_base_sector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emunand_base_sector = 0x000000; // GW type EmuNAND
|
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
|
emunand_base_sector = 0x000001; // RedNAND type EmuNAND
|
||||||
|
|
||||||
return emunand_base_sector;
|
return emunand_base_sector;
|
||||||
|
@ -6,35 +6,10 @@
|
|||||||
#define NAND_EMUNAND (1UL<<1)
|
#define NAND_EMUNAND (1UL<<1)
|
||||||
#define NAND_IMGNAND (1UL<<2)
|
#define NAND_IMGNAND (1UL<<2)
|
||||||
#define NAND_ZERONAND (1UL<<3)
|
#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
|
// hardcoded start sectors
|
||||||
#define NAND_MIN_SECTORS_O3DS 0x1D7800
|
|
||||||
#define NAND_MIN_SECTORS_N3DS 0x26C000
|
|
||||||
|
|
||||||
// start sectors of partitions
|
|
||||||
#define SECTOR_TWL 0x000000
|
|
||||||
#define SECTOR_D0K3 0x000001
|
#define SECTOR_D0K3 0x000001
|
||||||
#define SECTOR_SECRET 0x000096
|
#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
|
// filenames for sector 0x96
|
||||||
#define SECTOR_NAME "sector0x96.bin"
|
#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 ReadNandSectors(u8* buffer, u32 sector, u32 count, u32 keyslot, u32 src);
|
||||||
int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 dest);
|
int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 dest);
|
||||||
|
|
||||||
|
u32 ValidateNandNcsdHeader(NandNcsdHeader* header);
|
||||||
u32 GetNandNcsdMinSizeSectors(NandNcsdHeader* ncsd);
|
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 GetNandNcsdPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, NandNcsdHeader* ncsd);
|
||||||
u32 GetNandPartitionInfo(NandPartitionInfo* info, u32 type, u32 subtype, u32 index, u32 nand_src);
|
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 GetLegitSector0x96(u8* sector);
|
||||||
u32 GetOtpHash(void* hash);
|
u32 GetOtpHash(void* hash);
|
||||||
|
@ -62,11 +62,6 @@ u32 BuildEssentialBackup(const char* path, EssentialBackup* essential) {
|
|||||||
// mount original file
|
// mount original file
|
||||||
InitImgFS(path_bak);
|
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
|
// fill nand cid / otp hash
|
||||||
if (GetNandCid(&(essential->nand_cid)) != 0) return 1;
|
if (GetNandCid(&(essential->nand_cid)) != 0) return 1;
|
||||||
if (!IS_UNLOCKED) memset(&(filelist[5]), 0, sizeof(ExeFsFileHeader));
|
if (!IS_UNLOCKED) memset(&(filelist[5]), 0, sizeof(ExeFsFileHeader));
|
||||||
@ -86,7 +81,7 @@ u32 CheckEmbeddedBackup(const char* path) {
|
|||||||
EssentialBackup* embedded = (EssentialBackup*) (TEMP_BUFFER + sizeof(EssentialBackup));
|
EssentialBackup* embedded = (EssentialBackup*) (TEMP_BUFFER + sizeof(EssentialBackup));
|
||||||
UINT btr;
|
UINT btr;
|
||||||
if ((BuildEssentialBackup(path, essential) != 0) ||
|
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))
|
(memcmp(embedded, essential, sizeof(EssentialBackup)) != 0))
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
@ -97,20 +92,15 @@ u32 EmbedEssentialBackup(const char* path) {
|
|||||||
UINT btw;
|
UINT btw;
|
||||||
// leaving out the write permissions check here, it's okay
|
// leaving out the write permissions check here, it's okay
|
||||||
if ((BuildEssentialBackup(path, essential) != 0) ||
|
if ((BuildEssentialBackup(path, essential) != 0) ||
|
||||||
(CheckNandHeader(essential->nand_hdr) == 0) || // 0 -> header not recognized
|
(ValidateNandNcsdHeader((NandNcsdHeader*) essential->nand_hdr) != 0) ||
|
||||||
(fvx_qwrite(path, essential, 0x200, sizeof(EssentialBackup), &btw) != FR_OK) ||
|
(fvx_qwrite(path, essential, SECTOR_D0K3 * 0x200, sizeof(EssentialBackup), &btw) != FR_OK) ||
|
||||||
(btw != sizeof(EssentialBackup)))
|
(btw != sizeof(EssentialBackup)))
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 ValidateNandDump(const char* path) {
|
u32 ValidateNandDump(const char* path) {
|
||||||
const u32 mbr_sectors[] = { SECTOR_TWL, SECTOR_CTR };
|
NandPartitionInfo info;
|
||||||
const u32 firm_sectors[] = { SECTOR_FIRM0, SECTOR_FIRM1 };
|
|
||||||
u8 buffer[0x200];
|
|
||||||
FirmHeader firm;
|
|
||||||
MbrHeader mbr;
|
|
||||||
u32 nand_type;
|
|
||||||
FIL file;
|
FIL file;
|
||||||
|
|
||||||
// truncated path string
|
// truncated path string
|
||||||
@ -122,25 +112,29 @@ u32 ValidateNandDump(const char* path) {
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// check NAND header
|
// check NAND header
|
||||||
if ((ReadNandFile(&file, buffer, 0, 1, 0xFF) != 0) ||
|
NandNcsdHeader ncsd;
|
||||||
((nand_type = CheckNandHeader(buffer)) == 0)) { // zero means header not recognized
|
if ((ReadNandFile(&file, &ncsd, 0, 1, 0xFF) != 0) || (ValidateNandNcsdHeader(&ncsd) != 0)) {
|
||||||
ShowPrompt(false, "%s\nHeader does not belong to device", pathstr);
|
ShowPrompt(false, "%s\nNCSD header is not valid", pathstr);
|
||||||
fvx_close(&file);
|
fvx_close(&file);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check size
|
// 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);
|
ShowPrompt(false, "%s\nNAND dump misses data", pathstr);
|
||||||
fvx_close(&file);
|
fvx_close(&file);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check MBRs (TWL & CTR)
|
// check TWL & CTR FAT partitions
|
||||||
for (u32 i = 0; i < sizeof(mbr_sectors) / sizeof(u32); i++) {
|
for (u32 i = 0; i < 2; i++) {
|
||||||
u32 keyslot = (i == 0) ? 0x03 : (nand_type == NAND_TYPE_O3DS) ? 0x04 : 0x05;
|
|
||||||
char* section_type = (i) ? "CTR" : "MBR";
|
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)) {
|
(ValidateMbrHeader(&mbr) != 0)) {
|
||||||
ShowPrompt(false, "%s\nError: %s MBR is corrupt", pathstr, section_type);
|
ShowPrompt(false, "%s\nError: %s MBR is corrupt", pathstr, section_type);
|
||||||
fvx_close(&file);
|
fvx_close(&file);
|
||||||
@ -148,9 +142,10 @@ u32 ValidateNandDump(const char* path) {
|
|||||||
}
|
}
|
||||||
for (u32 p = 0; p < 4; p++) {
|
for (u32 p = 0; p < 4; p++) {
|
||||||
u32 p_sector = mbr.partitions[p].sector;
|
u32 p_sector = mbr.partitions[p].sector;
|
||||||
|
u8 fat[0x200];
|
||||||
if (!p_sector) continue;
|
if (!p_sector) continue;
|
||||||
if ((ReadNandFile(&file, buffer, mbr_sectors[i] + p_sector, 1, keyslot) != 0) ||
|
if ((ReadNandFile(&file, fat, info.sector + p_sector, 1, info.keyslot) != 0) ||
|
||||||
(ValidateFatHeader(buffer) != 0)) {
|
(ValidateFatHeader(fat) != 0)) {
|
||||||
ShowPrompt(false, "%s\nError: %s partition%u is corrupt", pathstr, section_type, p);
|
ShowPrompt(false, "%s\nError: %s partition%u is corrupt", pathstr, section_type, p);
|
||||||
fvx_close(&file);
|
fvx_close(&file);
|
||||||
return 1;
|
return 1;
|
||||||
@ -158,56 +153,54 @@ u32 ValidateNandDump(const char* path) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check FIRMs (FIRM1 must be valid)
|
// check FIRMs (at least one FIRM must be valid)
|
||||||
for (u32 i = 0; i < sizeof(firm_sectors) / sizeof(u32); i++) {
|
// check all 8 firms, also check if ARM9 & ARM11 entrypoints are available
|
||||||
u32 keyslot = 0x06;
|
for (u32 f = 0; f <= 8; f++) {
|
||||||
if ((ReadNandFile(&file, &firm, firm_sectors[i], 1, keyslot) != 0) ||
|
FirmHeader firm;
|
||||||
(ValidateFirmHeader(&firm, 0) != 0) ||
|
if (GetNandNcsdPartitionInfo(&info, NP_TYPE_FIRM, NP_SUBTYPE_CTR, f, &ncsd) != 0) {
|
||||||
(getbe32(firm.dec_magic) != 0)) { // decrypted firms are not allowed
|
ShowPrompt(false, "%s\nNo valid FIRM found", pathstr);
|
||||||
ShowPrompt(false, "%s\nError: FIRM%u header is corrupt", pathstr, i);
|
|
||||||
fvx_close(&file);
|
fvx_close(&file);
|
||||||
return 1;
|
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
|
// hash verify all available sections
|
||||||
if (i == 0) continue; // no hash checks for FIRM0 (might be A9LH)
|
u32 s;
|
||||||
for (u32 s = 0; s < 4; s++) {
|
for (s = 0; s < 4; s++) {
|
||||||
FirmSectionHeader* section = firm.sections + 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;
|
u32 count = section->size / 0x200;
|
||||||
if (!count) continue;
|
if (!count) continue;
|
||||||
sha_init(SHA256_MODE);
|
sha_init(SHA256_MODE);
|
||||||
// relies on sections being aligned to sectors
|
// relies on sections being aligned to sectors
|
||||||
for (u32 c = 0; c < count; c += MAIN_BUFFER_SIZE / 0x200) {
|
for (u32 c = 0; c < count; c += MAIN_BUFFER_SIZE / 0x200) {
|
||||||
u32 read_sectors = min(MAIN_BUFFER_SIZE / 0x200, (count - c));
|
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);
|
sha_update(MAIN_BUFFER, read_sectors * 0x200);
|
||||||
}
|
}
|
||||||
u8 hash[0x20];
|
u8 hash[0x20];
|
||||||
sha_get(hash);
|
sha_get(hash);
|
||||||
if (memcmp(hash, section->hash, 0x20) != 0) {
|
if (memcmp(hash, section->hash, 0x20) != 0) break;
|
||||||
ShowPrompt(false, "%s\nFIRM%u/%u hash mismatch", pathstr, i, s);
|
|
||||||
fvx_close(&file);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (s >= 4) break; // valid FIRM found
|
||||||
}
|
}
|
||||||
|
fvx_close(&file);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 SafeRestoreNandDump(const char* path) {
|
u32 SafeRestoreNandDump(const char* path) {
|
||||||
u32 safe_sectors[] = { SAFE_SECTORS };
|
|
||||||
FIL file;
|
|
||||||
|
|
||||||
if ((ValidateNandDump(path) != 0) && // NAND dump validation
|
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;
|
return 1;
|
||||||
if (!IS_A9LH) {
|
if (!IS_A9LH) {
|
||||||
ShowPrompt(false, "Error: A9LH/B9S not detected.");
|
ShowPrompt(false, "Error: B9S/A9LH not detected.");
|
||||||
return 1;
|
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;
|
return 1;
|
||||||
if (!SetWritePermissions(PERM_SYS_LVL1, true)) return 1;
|
if (!SetWritePermissions(PERM_SYS_LVL1, true)) return 1;
|
||||||
|
|
||||||
@ -217,39 +210,81 @@ u32 SafeRestoreNandDump(const char* path) {
|
|||||||
memset(essential, 0, sizeof(EssentialBackup));
|
memset(essential, 0, sizeof(EssentialBackup));
|
||||||
|
|
||||||
// open file, get size
|
// open file, get size
|
||||||
|
FIL file;
|
||||||
if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
||||||
return 1;
|
return 1;
|
||||||
u32 fsize = fvx_size(&file);
|
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
|
// main processing loop
|
||||||
u32 ret = 0;
|
u32 ret = 0;
|
||||||
|
u32 sector0 = 1; // start at the sector after NCSD
|
||||||
if (!ShowProgress(0, 0, path)) ret = 1;
|
if (!ShowProgress(0, 0, path)) ret = 1;
|
||||||
for (u32 p = 0; p < sizeof(safe_sectors) / sizeof(u32); p += 2) {
|
for (int p = -1; p < 8; p++) {
|
||||||
u32 sector0 = safe_sectors[p];
|
NandPartitionInfo np_info;
|
||||||
u32 sector1 = safe_sectors[p+1];
|
u32 idx = (p < 0) ? 0 : p;
|
||||||
fvx_lseek(&file, sector0 * 0x200);
|
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) {
|
for (u32 s = sector0; (s < sector1) && (ret == 0); s += MAIN_BUFFER_SIZE / 0x200) {
|
||||||
UINT btr;
|
|
||||||
u32 count = min(MAIN_BUFFER_SIZE / 0x200, (sector1 - s));
|
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 (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 (!ShowProgress(s + count, fsize / 0x200, path)) ret = 1;
|
||||||
}
|
}
|
||||||
}
|
if (sector1 == fsize / 0x200) break; // at file end
|
||||||
if (!IS_O3DS && (CheckNandType(NAND_SYSNAND) != NAND_TYPE_N3DS) && (ret == 0)) {
|
sector0 = np_info.sector + np_info.count;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
fvx_close(&file);
|
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
|
// inject essential backup to NAND
|
||||||
WriteNandSectors((u8*) essential, ESSENTIAL_SECTOR, (sizeof(EssentialBackup) + 0x1FF) / 0x200, 0xFF, NAND_SYSNAND);
|
WriteNandSectors((u8*) essential, ESSENTIAL_SECTOR, (sizeof(EssentialBackup) + 0x1FF) / 0x200, 0xFF, NAND_SYSNAND);
|
||||||
|
|
||||||
|
@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#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 CheckEmbeddedBackup(const char* path);
|
||||||
u32 EmbedEssentialBackup(const char* path);
|
u32 EmbedEssentialBackup(const char* path);
|
||||||
u32 ValidateNandDump(const char* path);
|
u32 ValidateNandDump(const char* path);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user