From 89b987b1c07c29d8ca54213b79737de6cb4c4604 Mon Sep 17 00:00:00 2001 From: d0k3 Date: Fri, 17 Feb 2017 03:28:53 +0100 Subject: [PATCH] Enabled embedded backups for NAND images --- source/common/common.h | 2 +- source/fs/filetype.h | 1 + source/godmode.c | 16 +++++++- source/nand/nandutil.c | 93 ++++++++++++++++++++++++++++++++++++++++-- source/nand/nandutil.h | 2 + source/nand/sysfiles.h | 31 ++++++++++++++ 6 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 source/nand/sysfiles.h diff --git a/source/common/common.h b/source/common/common.h index b869373..f78be21 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -38,7 +38,7 @@ (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v)) // GodMode9 version -#define VERSION "0.9.9.1" +#define VERSION "0.9.9.2" // input / output paths #define INPUT_PATHS "0:", "0:/files9", "1:/rw/files9" diff --git a/source/fs/filetype.h b/source/fs/filetype.h index ad73f8f..bf024bc 100644 --- a/source/fs/filetype.h +++ b/source/fs/filetype.h @@ -31,6 +31,7 @@ #define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD))) #define FTYPE_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI)) #define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND)) +#define FTYPE_EBACKUP(tp) (tp&(IMG_NAND)) #define FTYPE_XORPAD(tp) (tp&(BIN_NCCHNFO)) #define FTYPE_PAYLOAD(tp) (tp&(BIN_LAUNCH)) diff --git a/source/godmode.c b/source/godmode.c index 158613c..5245a9a 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -616,10 +616,11 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur bool buildable_legit = (FTYPE_BUILDABLE_L(filetype)); bool hsinjectable = (FTYPE_HSINJECTABLE(filetype)); bool restorable = (FTYPE_RESTORABLE(filetype) && CheckA9lh() && !(drvtype & DRV_SYSNAND)); + bool ebackupable = (FTYPE_EBACKUP(filetype)); bool xorpadable = (FTYPE_XORPAD(filetype)); bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT)); bool special_opt = mountable || verificable || decryptable || encryptable || - buildable || buildable_legit || hsinjectable || restorable || xorpadable || launchable; + buildable || buildable_legit || hsinjectable || restorable || xorpadable || launchable || ebackupable; char pathstr[48]; TruncateString(pathstr, curr_entry->path, 32, 8); @@ -770,6 +771,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur n_opt = 0; int mount = (mountable) ? ++n_opt : -1; int restore = (restorable) ? ++n_opt : -1; + int ebackup = (ebackupable) ? ++n_opt : -1; int decrypt = (decryptable) ? ++n_opt : -1; int encrypt = (encryptable) ? ++n_opt : -1; int build = (buildable) ? ++n_opt : -1; @@ -781,6 +783,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur int launch = (launchable) ? ++n_opt : -1; if (mount > 0) optionstr[mount-1] = "Mount image to drive"; if (restore > 0) optionstr[restore-1] = "Restore SysNAND (safe)"; + if (ebackup > 0) optionstr[ebackup-1] = "Update embedded backup"; if (decrypt > 0) optionstr[decrypt-1] = (cryptable_inplace) ? "Decrypt file (...)" : "Decrypt file (" OUTPUT_PATH ")"; if (encrypt > 0) optionstr[encrypt-1] = (cryptable_inplace) ? "Encrypt file (...)" : "Encrypt file (" OUTPUT_PATH ")"; if (build > 0) optionstr[build-1] = (build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)"; @@ -987,9 +990,11 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur ShowPrompt(false, "%s\nH&S inject %s", pathstr, (InjectHealthAndSafety(curr_entry->path, destdrv[user_select-1]) == 0) ? "success" : "failed"); } + 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 0; } else if ((user_select == xorpad) || (user_select == xorpad_inplace)) { bool inplace = (user_select == xorpad_inplace); bool success = (BuildNcchInfoXorpads((inplace) ? current_path : OUTPUT_PATH, curr_entry->path) == 0); @@ -1005,6 +1010,14 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur *scroll = 0; *cursor = 1; } + return 0; + } else if (user_select == ebackup) { + ShowString("%s\nUpdating embedded backup...", pathstr); + bool required = (CheckEmbeddedBackup(curr_entry->path) != 0); + bool success = (required && (EmbedEssentialBackup(curr_entry->path) == 0)); + ShowPrompt(false, "%s\nBackup update: %s", pathstr, (!required) ? "not required" : + (success) ? "completed" : "failed!"); + return 0; } else if ((user_select == launch)) { size_t payload_size = FileGetSize(curr_entry->path); if (ShowUnlockSequence(3, "%s (%dkB)\nLaunch as arm9 payload?", pathstr, payload_size / 1024)) { @@ -1013,6 +1026,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur while(1); } // failed load is basically impossible here } + return 0; } return FileHandlerMenu(current_path, cursor, scroll, current_dir, clipboard); diff --git a/source/nand/nandutil.c b/source/nand/nandutil.c index 70a164a..b8dd813 100644 --- a/source/nand/nandutil.c +++ b/source/nand/nandutil.c @@ -2,11 +2,26 @@ #include "nand.h" #include "firm.h" #include "fatmbr.h" +#include "sysfiles.h" // for essential backup struct +#include "exefs.h" // for essential backup struct +#include "image.h" +#include "fsinit.h" #include "fsperm.h" #include "sha.h" #include "ui.h" #include "vff.h" +typedef struct { + ExeFsHeader header; + u8 nand_hdr[0x200]; + SecureInfo secinfo; + u8 padding_secinfo[0x200 - sizeof(SecureInfo)]; + MovableSed movable; + u8 padding_movable[0x200 - sizeof(MovableSed)]; + LocalFriendCodeSeed frndseed; + u8 padding_frndseed[0x200 - sizeof(LocalFriendCodeSeed)]; +} __attribute__((packed)) EssentialBackup; + u32 ReadNandFile(FIL* file, void* buffer, u32 sector, u32 count, u32 keyslot) { u32 offset = sector * 0x200; u32 size = count * 0x200; @@ -19,6 +34,79 @@ u32 ReadNandFile(FIL* file, void* buffer, u32 sector, u32 count, u32 keyslot) { return 0; } +u32 BuildEssentialBackup(const char* path, EssentialBackup* essential) { + // prepare essential backup struct + const ExeFsFileHeader filelist[] = { + { "nand_hdr", 0x000, 0x200 }, + { "secinfo" , 0x200, 0x111 }, + { "movable" , 0x400, 0x140 }, + { "frndseed", 0x600, 0x110 } + }; + memset(essential, 0, sizeof(EssentialBackup)); + memcpy(essential, filelist, sizeof(filelist)); + + // backup current mount path, mount new path + char path_store[256] = { 0 }; + char* path_bak = NULL; + strncpy(path_store, GetMountPath(), 256); + if (*path_store) path_bak = path_store; + if (!InitImgFS(path)) { + InitImgFS(path_bak); + return 1; + } + + // read four files + ExeFsFileHeader* files = essential->header.files; + if ((fvx_qread("I:/nand_hdr.bin", &(essential->nand_hdr), 0, 0x200, (UINT*) &(files[0].size)) != FR_OK) || + ((fvx_qread("7:/rw/sys/SecureInfo_A", &(essential->secinfo), 0, 0x200, (UINT*) &(files[1].size)) != FR_OK) && + (fvx_qread("7:/rw/sys/SecureInfo_B", &(essential->secinfo), 0, 0x200, (UINT*) &(files[1].size)) != FR_OK)) || + (fvx_qread("7:/private/movable.sed", &(essential->movable), 0, 0x200, (UINT*) &(files[2].size)) != FR_OK) || + ((fvx_qread("7:/rw/sys/LocalFriendCodeSeed_B", &(essential->frndseed), 0, 0x200, (UINT*) &(files[3].size)) != FR_OK) && + (fvx_qread("7:/rw/sys/LocalFriendCodeSeed_A", &(essential->frndseed), 0, 0x200, (UINT*) &(files[3].size)) != FR_OK))) { + InitImgFS(path_bak); + return 1; + } + + // mount original file + InitImgFS(path_bak); + + // check sizes + if ((files[0].size != 0x200) || (files[1].size != 0x111) || + ((files[2].size != 0x120) && (files[2].size != 0x140)) || (files[3].size != 0x110)) + return 1; + + // calculate hashes + for (u32 i = 0; i < 4; i++) + sha_quick(essential->header.hashes[9-i], + ((u8*) essential) + files[i].offset + sizeof(ExeFsHeader), + files[i].size, SHA256_MODE); + + return 0; +} + +u32 CheckEmbeddedBackup(const char* path) { + EssentialBackup* essential = (EssentialBackup*) TEMP_BUFFER; + EssentialBackup* embedded = (EssentialBackup*) (TEMP_BUFFER + sizeof(EssentialBackup)); + UINT btr; + if ((BuildEssentialBackup(path, essential) != 0) || + (fvx_qread(path, embedded, 0x200, sizeof(EssentialBackup), &btr) != FR_OK) || + (memcmp(embedded, essential, sizeof(EssentialBackup)) != 0)) + return 1; + return 0; +} + +u32 EmbedEssentialBackup(const char* path) { + EssentialBackup* essential = (EssentialBackup*) TEMP_BUFFER; + UINT btw; + // leaving out the write permissions check here, it's okay + if ((BuildEssentialBackup(path, essential) != 0) || + (CheckNandHeader(essential->nand_hdr) == 0) || // 0 -> header not recognized + (fvx_qwrite(path, essential, 0x200, sizeof(EssentialBackup), &btw) != FR_OK) || + (btw != sizeof(EssentialBackup))) + return 1; + return 0; +} + u32 ValidateNandDump(const char* path) { const u32 mbr_sectors[] = { SECTOR_TWL, SECTOR_CTR }; const u32 firm_sectors[] = { SECTOR_FIRM0, SECTOR_FIRM1 }; @@ -114,10 +202,9 @@ 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."); + if ((ValidateNandDump(path) != 0) && // NAND dump validation + !ShowPrompt(true, "NAND dump corrupt or not from console.\nStill continue?")) return 1; - }*/ if (!CheckA9lh()) { ShowPrompt(false, "Error: A9LH not detected."); return 1; diff --git a/source/nand/nandutil.h b/source/nand/nandutil.h index edf630f..6c81ae5 100644 --- a/source/nand/nandutil.h +++ b/source/nand/nandutil.h @@ -5,5 +5,7 @@ #define SAFE_SECTORS 0x000000 + 0x1, SECTOR_SECRET, SECTOR_SECRET + 0x1, \ SECTOR_FIRM0, SECTOR_CTR, 0x000000 // last one is a placeholder +u32 CheckEmbeddedBackup(const char* path); +u32 EmbedEssentialBackup(const char* path); u32 ValidateNandDump(const char* path); u32 SafeRestoreNandDump(const char* path); diff --git a/source/nand/sysfiles.h b/source/nand/sysfiles.h new file mode 100644 index 0000000..164502f --- /dev/null +++ b/source/nand/sysfiles.h @@ -0,0 +1,31 @@ +#pragma once + +#include "common.h" + +// /rw/sys/LocalFriendCodeSeed_B (/_A) file +// see: http://3dbrew.org/wiki/Nandrw/sys/LocalFriendCodeSeed_B +typedef struct { + u8 signature[0x100]; + u8 unknown[0x8]; // normally zero + u8 codeseed[0x8]; // the actual data +} __attribute__((packed)) LocalFriendCodeSeed; + +// /private/movable.sed file +// see: http://3dbrew.org/wiki/Nand/private/movable.sed +typedef struct { + u8 magic[0x4]; // "SEED" + u8 indicator[0x4]; // uninitialized all zero, otherwise u8[1] nonzero + LocalFriendCodeSeed codeseed_data; + u8 keyy_high[8]; + u8 unknown[0x10]; + u8 cmac[0x10]; +} __attribute__((packed)) MovableSed; + +// /rw/sys/Secure_A (/_B) file +// see: http://3dbrew.org/wiki/Nandrw/sys/SecureInfo_A +typedef struct { + u8 signature[0x100]; + u8 region; + u8 unknown; + char serial[0xF]; +} __attribute__((packed)) SecureInfo;