Allow NAND safe restore and validation

This commit is contained in:
d0k3 2017-01-02 17:37:08 +01:00
parent f600402cee
commit 9e40ce3e86
9 changed files with 343 additions and 17 deletions

32
source/fs/fatmbr.c Normal file
View File

@ -0,0 +1,32 @@
#include "fatmbr.h"
u32 ValidateMbrHeader(MbrHeader* mbr) {
if (mbr->magic != FATMBR_MAGIC) return 1; // check magic
u32 sector = 1; // check partitions
for (u32 i = 0; i < 4; i++) {
MbrPartitionInfo* partition = mbr->partitions + i;
if (!partition->count && i) continue;
else if (!partition->count) return 1; // first partition can't be empty
if ((partition->type != 0x1) && (partition->type != 0x4) && (partition->type != 0x6) &&
(partition->type != 0xB) && (partition->type != 0xC) && (partition->type != 0xE))
return 1; // bad / unknown filesystem type
if (partition->sector < sector) return 1; // overlapping partitions
sector = partition->sector + partition->count;
}
return 0;
}
u32 ValidateFatHeader(void* fat) {
if (getle16((u8*) fat + 0x1FE) != FATMBR_MAGIC) return 1; // check magic
Fat32Header* fat32 = (Fat32Header*) fat;
if (strncmp(fat32->fs_type, "FAT32 ", 8) == 0)
return 0; // is FAT32 header
Fat16Header* fat16 = (Fat16Header*) fat;
if ((strncmp(fat16->fs_type, "FAT16 ", 8) == 0) ||
(strncmp(fat16->fs_type, "FAT12 ", 8) == 0) ||
(strncmp(fat16->fs_type, "FAT ", 8) == 0))
return 0; // is FAT16 / FAT12 header
if ((getle64(fat16->fs_type) == 0) && (fat16->sct_size == 0x200))
return 0; // special case for public.sav
return 1; // failed, not a FAT header
}

90
source/fs/fatmbr.h Normal file
View File

@ -0,0 +1,90 @@
#pragma once
#include "common.h"
#define FATMBR_MAGIC 0xAA55 // little endian!
typedef struct {
u8 status; // 0x80
u8 chs_start[3]; // 0x01 0x01 0x00
u8 type; // 0x0C
u8 chs_end[3]; // 0xFE 0xFF 0xFF
u32 sector; // 0x2000 (4MB offset, 512 byte sectors)
u32 count;
} __attribute__((packed)) MbrPartitionInfo;
typedef struct {
char text[446];
MbrPartitionInfo partitions[4];
u16 magic; // 0xAA55
} __attribute__((packed)) MbrHeader;
typedef struct { // unused
u32 signature0; // 0x41615252
u8 reserved0[480];
u32 signature1; // 0x61417272
u32 clr_free; // 0xFFFFFFFF
u32 clr_next; // 0xFFFFFFFF
u8 reserved1[14];
u16 magic; // 0xAA55
} __attribute__((packed)) FileSystemInfo;
typedef struct {
u8 jmp[3]; // 0x90 0x00 0xEB
char oemname[8]; // "anything"
u16 sct_size; // 0x0200
u8 clr_size; // 0x40 -> 32kB clusters with 512byte sectors
u16 sct_reserved; // 0x20
u8 fat_n; // 0x02
u16 reserved0; // root entry count in FAT16
u16 reserved1; // partition size when <= 32MB
u8 mediatype; // 0xF8
u16 reserved2; // FAT size in sectors in FAT16
u16 sct_track; // 0x3F
u16 sct_heads; // 0xFF
u32 sct_hidden; // same as partition offset in MBR
u32 sct_total; // same as partition size in MBR
u32 fat_size; // roundup((((sct_total - sct_reserved) / clr_size) * 4) / sct_size)
u16 flags; // 0x00
u16 version; // 0x00
u32 clr_root; // 0x02
u16 sct_fsinfo; // 0x01
u16 sct_backup; // 0x06
u8 reserved3[12];
u8 ndrive; // 0x80
u8 head_cur; // 0x00
u8 boot_sig; // 0x29
u32 vol_id; // volume id / 0x00
char vol_label[11]; // "anything "
char fs_type[8]; // "FAT32 "
u8 reserved4[420];
u16 magic; // 0xAA55
} __attribute__((packed)) Fat32Header;
typedef struct { // this struct is not tested enough!
u8 jmp[3]; // 0x90 0x00 0xEB
char oemname[8]; // "anything"
u16 sct_size; // 0x0200
u8 clr_size; // 0x20 (???) -> 16kB clusters with 512byte sectors
u16 sct_reserved; // 0x01
u8 fat_n; // 0x02
u16 root_n; // 0x0200
u16 reserved0; // partition size when <= 32MB
u8 mediatype; // 0xF8
u16 fat_size; // roundup((((sct_total - sct_reserved) / clr_size) * 2) / sct_size)
u16 sct_track; // 0x3F
u16 sct_heads; // 0xFF
u32 sct_hidden; // same as partition offset in MBR
u32 sct_total; // same as partition size in MBR
u8 ndrive; // 0x80
u8 head_cur; // 0x00
u8 boot_sig; // 0x29
u32 vol_id; // volume id / 0x00
char vol_label[11]; // "anything "
char fs_type[8]; // "FAT16 "
u8 reserved4[448];
u16 magic; // 0xAA55
} __attribute__((packed)) Fat16Header;
u32 ValidateMbrHeader(MbrHeader* mbr);
u32 ValidateFatHeader(void* fat);

View File

@ -1,5 +1,6 @@
#include "filetype.h" #include "filetype.h"
#include "fsutil.h" #include "fsutil.h"
#include "fatmbr.h"
#include "game.h" #include "game.h"
u32 IdentifyFileType(const char* path) { u32 IdentifyFileType(const char* path) {
@ -12,17 +13,13 @@ u32 IdentifyFileType(const char* path) {
if ((getbe32(header + 0x100) == 0x4E435344) && (getbe64(header + 0x110) == (u64) 0x0104030301000000) && if ((getbe32(header + 0x100) == 0x4E435344) && (getbe64(header + 0x110) == (u64) 0x0104030301000000) &&
(getbe64(header + 0x108) == (u64) 0) && (fsize >= 0x8FC8000)) { (getbe64(header + 0x108) == (u64) 0) && (fsize >= 0x8FC8000)) {
return IMG_NAND; // NAND image return IMG_NAND; // NAND image
} else if (getbe16(header + 0x1FE) == 0x55AA) { // migt be FAT or MBR } else if (ValidateFatHeader(header) == 0) {
if ((strncmp((char*) header + 0x36, "FAT12 ", 8) == 0) || (strncmp((char*) header + 0x36, "FAT16 ", 8) == 0) || return IMG_FAT; // FAT image file
(strncmp((char*) header + 0x36, "FAT ", 8) == 0) || (strncmp((char*) header + 0x52, "FAT32 ", 8) == 0) || } else if (ValidateMbrHeader((MbrHeader*) (void*) header) == 0) {
((getle64(header + 0x36) == 0) && (getle16(header + 0x0B) == 0x200))) { // last one is a special case for public.sav MbrHeader* mbr = (MbrHeader*) (void*) header;
return IMG_FAT; // this is an actual FAT header MbrPartitionInfo* partition0 = mbr->partitions;
} else if (((getle32(header + 0x1BE + 0x8) + getle32(header + 0x1BE + 0xC)) < (fsize / 0x200)) && // check file size if ((partition0->sector + partition0->count) <= (fsize / 0x200)) // size check
(getle32(header + 0x1BE + 0x8) > 0) && (getle32(header + 0x1BE + 0xC) >= 0x800) && // check first partition sanity return IMG_FAT; // possibly an MBR -> also treat as FAT image
((header[0x1BE + 0x4] == 0x1) || (header[0x1BE + 0x4] == 0x4) || (header[0x1BE + 0x4] == 0x6) || // filesystem type
(header[0x1BE + 0x4] == 0xB) || (header[0x1BE + 0x4] == 0xC) || (header[0x1BE + 0x4] == 0xE))) {
return IMG_FAT; // this might be an MBR -> give it the benefit of doubt
}
} else if (ValidateCiaHeader((CiaHeader*) (void*) header) == 0) { } else if (ValidateCiaHeader((CiaHeader*) (void*) header) == 0) {
// this only works because these functions ignore CIA content index // this only works because these functions ignore CIA content index
CiaInfo info; CiaInfo info;

View File

@ -15,9 +15,10 @@
#define SYS_FIRM (1<<8) #define SYS_FIRM (1<<8)
#define FTYPE_MOUNTABLE (IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|SYS_FIRM) #define FTYPE_MOUNTABLE (IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|SYS_FIRM)
#define FYTPE_VERIFICABLE (GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|SYS_FIRM) #define FYTPE_VERIFICABLE (IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|SYS_FIRM)
#define FYTPE_DECRYPTABLE (GAME_CIA|GAME_NCSD|GAME_NCCH|SYS_FIRM) #define FYTPE_DECRYPTABLE (GAME_CIA|GAME_NCSD|GAME_NCCH|SYS_FIRM)
#define FTYPE_BUILDABLE (GAME_NCSD|GAME_NCCH|GAME_TMD) #define FTYPE_BUILDABLE (GAME_NCSD|GAME_NCCH|GAME_TMD)
#define FTYPE_BUILDABLE_L (FTYPE_BUILDABLE&(GAME_TMD)) #define FTYPE_BUILDABLE_L (FTYPE_BUILDABLE&(GAME_TMD))
#define FTYPE_RESTORABLE (IMG_NAND)
u32 IdentifyFileType(const char* path); u32 IdentifyFileType(const char* path);

View File

@ -6,6 +6,7 @@
#include "fsutil.h" #include "fsutil.h"
#include "fsperm.h" #include "fsperm.h"
#include "gameutil.h" #include "gameutil.h"
#include "nandutil.h"
#include "filetype.h" #include "filetype.h"
#include "platform.h" #include "platform.h"
#include "nand.h" #include "nand.h"
@ -576,6 +577,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
bool decryptable_inplace = (decryptable && (drvtype & (DRV_SDCARD|DRV_RAMDRIVE))); bool decryptable_inplace = (decryptable && (drvtype & (DRV_SDCARD|DRV_RAMDRIVE)));
bool buildable = (filetype & FTYPE_BUILDABLE); bool buildable = (filetype & FTYPE_BUILDABLE);
bool buildable_legit = (filetype & FTYPE_BUILDABLE_L); bool buildable_legit = (filetype & FTYPE_BUILDABLE_L);
bool restorable = CheckA9lh() && (filetype & FTYPE_RESTORABLE);
char pathstr[32 + 1]; char pathstr[32 + 1];
TruncateString(pathstr, curr_entry->path, 32, 8); TruncateString(pathstr, curr_entry->path, 32, 8);
@ -592,7 +594,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
(int) ++n_opt : -1; (int) ++n_opt : -1;
int searchdrv = (DriveType(current_path) & DRV_SEARCH) ? ++n_opt : -1; int searchdrv = (DriveType(current_path) & DRV_SEARCH) ? ++n_opt : -1;
if (special > 0) optionstr[special-1] = if (special > 0) optionstr[special-1] =
(filetype == IMG_NAND ) ? "Mount as NAND image" : (filetype == IMG_NAND ) ? "NAND image options..." :
(filetype == IMG_FAT ) ? "Mount as FAT image" : (filetype == IMG_FAT ) ? "Mount as FAT image" :
(filetype == GAME_CIA ) ? "CIA image options..." : (filetype == GAME_CIA ) ? "CIA image options..." :
(filetype == GAME_NCSD ) ? "NCSD image options..." : (filetype == GAME_NCSD ) ? "NCSD image options..." :
@ -640,12 +642,14 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
// stuff for special menu starts here // stuff for special menu starts here
n_opt = 0; n_opt = 0;
int mount = (mountable) ? ++n_opt : -1; int mount = (mountable) ? ++n_opt : -1;
int restore = (restorable) ? ++n_opt : -1;
int decrypt = (decryptable) ? ++n_opt : -1; int decrypt = (decryptable) ? ++n_opt : -1;
int decrypt_inplace = (decryptable_inplace) ? ++n_opt : -1; int decrypt_inplace = (decryptable_inplace) ? ++n_opt : -1;
int build = (buildable) ? ++n_opt : -1; int build = (buildable) ? ++n_opt : -1;
int build_legit = (buildable_legit) ? ++n_opt : -1; int build_legit = (buildable_legit) ? ++n_opt : -1;
int verify = (verificable) ? ++n_opt : -1; int verify = (verificable) ? ++n_opt : -1;
if (mount > 0) optionstr[mount-1] = "Mount image to drive"; if (mount > 0) optionstr[mount-1] = "Mount image to drive";
if (restore > 0) optionstr[restore-1] = "Restore SysNAND (safe)";
if (decrypt > 0) optionstr[decrypt-1] = "Decrypt file (SD output)"; if (decrypt > 0) optionstr[decrypt-1] = "Decrypt file (SD output)";
if (decrypt_inplace > 0) optionstr[decrypt_inplace-1] = "Decrypt file (inplace)"; if (decrypt_inplace > 0) optionstr[decrypt_inplace-1] = "Decrypt file (inplace)";
if (build > 0) optionstr[build-1] = (build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)"; if (build > 0) optionstr[build-1] = (build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)";
@ -752,7 +756,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
else ShowPrompt(false, "%s\nCIA build failed", pathstr); else ShowPrompt(false, "%s\nCIA build failed", pathstr);
} }
return 0; return 0;
} else if (user_select == verify) { // -> verify game file } else if (user_select == verify) { // -> verify game / nand file
if ((n_marked > 1) && ShowPrompt(true, "Try to verify all %lu selected files?", n_marked)) { if ((n_marked > 1) && ShowPrompt(true, "Try to verify all %lu selected files?", n_marked)) {
u32 n_success = 0; u32 n_success = 0;
u32 n_other = 0; u32 n_other = 0;
@ -768,7 +772,9 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
if (!(filetype & (GAME_CIA|GAME_TMD)) && if (!(filetype & (GAME_CIA|GAME_TMD)) &&
!ShowProgress(n_processed++, n_marked, path)) break; !ShowProgress(n_processed++, n_marked, path)) break;
current_dir->entry[i].marked = false; current_dir->entry[i].marked = false;
if (VerifyGameFile(path) == 0) n_success++; if (filetype & IMG_NAND) {
if (ValidateNandDump(path) == 0) n_success++;
} else if (VerifyGameFile(path) == 0) n_success++;
else { // on failure: set *cursor on failed title, break; else { // on failure: set *cursor on failed title, break;
*cursor = i; *cursor = i;
break; break;
@ -779,10 +785,16 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
else ShowPrompt(false, "%lu/%lu files verified ok", n_success, n_marked); else ShowPrompt(false, "%lu/%lu files verified ok", n_success, n_marked);
} else { } else {
ShowString("%s\nVerifying file, please wait...", pathstr); ShowString("%s\nVerifying file, please wait...", pathstr);
ShowPrompt(false, "%s\nVerification %s", pathstr, if (filetype & IMG_NAND) {
ShowPrompt(false, "%s\nNAND validation %s", pathstr,
(ValidateNandDump(curr_entry->path) == 0) ? "success" : "failed");
} else ShowPrompt(false, "%s\nVerification %s", pathstr,
(VerifyGameFile(curr_entry->path) == 0) ? "success" : "failed"); (VerifyGameFile(curr_entry->path) == 0) ? "success" : "failed");
} }
return 0; return 0;
} else if (user_select == restore) { // -> restore SysNAND (A9LH preserving)
ShowPrompt(false, "%s\nNAND restore %s", pathstr,
(SafeRestoreNandDump(curr_entry->path) == 0) ? "success" : "failed");
} }
return 1; return 1;

View File

@ -8,7 +8,7 @@
#include "sdmmc.h" #include "sdmmc.h"
#include "image.h" #include "image.h"
#define NAND_MIN_SECTORS ((GetUnitPlatform() == PLATFORM_N3DS) ? 0x26C000 : 0x1D7800) #define NAND_MIN_SECTORS ((GetUnitPlatform() == PLATFORM_N3DS) ? NAND_MIN_SECTORS_N3DS : NAND_MIN_SECTORS_O3DS)
static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from FIRM0 / external file static u8 slot0x05KeyY[0x10] = { 0x00 }; // need to load this from FIRM0 / external file
static u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY (16 byte) static u8 slot0x05KeyY_sha256[0x20] = { // hash for slot0x05KeyY (16 byte)
@ -34,6 +34,14 @@ static u8 nand_magic_o3ds[0x60] = { // NCSD NAND header O3DS magic
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
}; };
static 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 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 };
@ -338,6 +346,24 @@ int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 n
return 0; 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 (memcmp(header_dec + 0x1BE, twl_mbr, sizeof(twl_mbr)) != 0)
return 0; // header does not belong to console
// header type check
if (memcmp(header + 0x100, nand_magic_n3ds, sizeof(nand_magic_n3ds) == 0) == 0)
return (GetUnitPlatform() == PLATFORM_3DS) ? 0 : NAND_TYPE_N3DS;
else if (memcmp(header + 0x100, nand_magic_o3ds, sizeof(nand_magic_o3ds) == 0) == 0)
return NAND_TYPE_O3DS;
return 0;
}
u32 CheckNandType(u32 nand_src) u32 CheckNandType(u32 nand_src)
{ {
if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF, nand_src) != 0) if (ReadNandSectors(NAND_BUFFER, 0, 1, 0xFF, nand_src) != 0)

View File

@ -10,6 +10,9 @@
#define NAND_TYPE_N3DS (1<<5) #define NAND_TYPE_N3DS (1<<5)
#define NAND_TYPE_NO3DS (1<<6) #define NAND_TYPE_NO3DS (1<<6)
#define NAND_MIN_SECTORS_O3DS 0x1D7800
#define NAND_MIN_SECTORS_N3DS 0x26C000
bool InitNandCrypto(void); bool InitNandCrypto(void);
bool CheckSlot0x05Crypto(void); bool CheckSlot0x05Crypto(void);
bool CheckSector0x96Crypto(void); bool CheckSector0x96Crypto(void);
@ -23,6 +26,7 @@ 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);
u64 GetNandSizeSectors(u32 src); u64 GetNandSizeSectors(u32 src);
u32 CheckNandHeader(u8* header);
u32 CheckNandType(u32 src); u32 CheckNandType(u32 src);
bool InitEmuNandBase(void); bool InitEmuNandBase(void);

152
source/nand/nandutil.c Normal file
View File

@ -0,0 +1,152 @@
#include "nandutil.h"
#include "nand.h"
#include "firm.h"
#include "fatmbr.h"
#include "fsperm.h"
#include "sha.h"
#include "ui.h"
#include "ff.h"
u32 ReadNandFile(FIL* file, void* buffer, u32 sector, u32 count, u32 keyslot) {
u32 offset = sector * 0x200;
u32 size = count * 0x200;
UINT btr;
if ((f_tell(file) != offset) && (f_lseek(file, offset) != FR_OK))
return 1; // seek failed
if ((f_read(file, buffer, size, &btr) != FR_OK) || (btr != size))
return 1; // read failed
if (keyslot < 0x40) CryptNand(buffer, sector, count, keyslot);
return 0;
}
u32 ValidateNandDump(const char* path) {
const u32 mbr_sectors[] = { TWL_OFFSET, CTR_OFFSET };
const u32 firm_sectors[] = { FIRM_OFFSETS };
u8 buffer[0x200];
FirmHeader firm;
MbrHeader mbr;
u32 nand_type;
FIL file;
// truncated path string
char pathstr[32 + 1];
TruncateString(pathstr, path, 32, 8);
// open file
if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
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);
f_close(&file);
return 1;
}
// check size
if (f_size(&file) < ((nand_type == NAND_TYPE_O3DS) ? NAND_MIN_SECTORS_O3DS : NAND_MIN_SECTORS_N3DS)) {
ShowPrompt(false, "%s\nNAND dump misses data", pathstr);
f_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;
char* section_type = (i) ? "CTR" : "MBR";
if ((ReadNandFile(&file, &mbr, mbr_sectors[i], 1, keyslot) != 0) ||
(ValidateMbrHeader(&mbr) != 0)) {
ShowPrompt(false, "%s\nError: %s MBR is corrupt", pathstr, section_type);
f_close(&file);
return 1; // impossible to happen
}
for (u32 p = 0; p < 4; p++) {
u32 p_sector = mbr.partitions[p].sector;
if (!p_sector) continue;
if ((ReadNandFile(&file, buffer, mbr_sectors[i] + p_sector, 1, keyslot) != 0) ||
(ValidateFatHeader(buffer) != 0)) {
ShowPrompt(false, "%s\nError: %s partition%u is corrupt", pathstr, section_type, p);
f_close(&file);
return 1;
}
}
}
// 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) ||
(getbe32(firm.dec_magic) != 0)) { // decrypted firms are not allowed
ShowPrompt(false, "%s\nError: FIRM%u header is corrupt", pathstr, i);
f_close(&file);
return 1;
}
// hash verify all available sections
if (i == 0) continue; // no hash checks for FIRM0 (might be A9LH)
for (u32 s = 0; s < 4; s++) {
FirmSectionHeader* section = firm.sections + s;
u32 sector = firm_sectors[i] + (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);
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);
f_close(&file);
return 1;
}
}
}
return 0;
}
u32 SafeRestoreNandDump(const char* path) {
u32 safe_sectors[] = { SAFE_SECTORS };
FIL file;
/* if (ValidateNandDump(path) != 0) { // NAND dump validation
ShowPrompt(false, "NAND dump corrupt or not from console.\nYou can still try mount and copy.");
return 1;
}*/
if (!CheckA9lh()) {
ShowPrompt(false, "Error: A9LH not detected.");
return 1;
}
if (!SetWritePermissions(PERM_SYSNAND, true)) return 1;
// open file, get size
if (f_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return 1;
u32 fsize = f_size(&file);
safe_sectors[(sizeof(safe_sectors) / sizeof(u32)) - 1] = fsize / 0x200;
// main processing loop
u32 ret = 0;
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];
f_lseek(&file, sector0 * 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 (f_read(&file, MAIN_BUFFER, count * 0x200, &btr) != FR_OK) 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;
}
}
f_close(&file);
return ret;
}

12
source/nand/nandutil.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "common.h"
#define TWL_OFFSET 0x000000
#define CTR_OFFSET 0x05C980
#define FIRM_OFFSETS 0x058980, 0x05A980
#define SAFE_SECTORS 0x000001, 0x000096, 0x000097, 0x058980, 0x05C980, 0x000000 // last one is a placeholder
u32 ValidateNandDump(const char* path);
u32 SafeRestoreNandDump(const char* path);