mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 05:32:47 +00:00
Allow NAND safe restore and validation
This commit is contained in:
parent
f600402cee
commit
9e40ce3e86
32
source/fs/fatmbr.c
Normal file
32
source/fs/fatmbr.c
Normal 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
90
source/fs/fatmbr.h
Normal 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);
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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
152
source/nand/nandutil.c
Normal 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
12
source/nand/nandutil.h
Normal 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);
|
Loading…
x
Reference in New Issue
Block a user