Enabled embedded backups for NAND images

This commit is contained in:
d0k3 2017-02-17 03:28:53 +01:00
parent 1764a1b271
commit 89b987b1c0
6 changed files with 140 additions and 5 deletions

View File

@ -38,7 +38,7 @@
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v)) (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
// GodMode9 version // GodMode9 version
#define VERSION "0.9.9.1" #define VERSION "0.9.9.2"
// input / output paths // input / output paths
#define INPUT_PATHS "0:", "0:/files9", "1:/rw/files9" #define INPUT_PATHS "0:", "0:/files9", "1:/rw/files9"

View File

@ -31,6 +31,7 @@
#define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD))) #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_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI))
#define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND)) #define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND))
#define FTYPE_EBACKUP(tp) (tp&(IMG_NAND))
#define FTYPE_XORPAD(tp) (tp&(BIN_NCCHNFO)) #define FTYPE_XORPAD(tp) (tp&(BIN_NCCHNFO))
#define FTYPE_PAYLOAD(tp) (tp&(BIN_LAUNCH)) #define FTYPE_PAYLOAD(tp) (tp&(BIN_LAUNCH))

View File

@ -616,10 +616,11 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
bool buildable_legit = (FTYPE_BUILDABLE_L(filetype)); bool buildable_legit = (FTYPE_BUILDABLE_L(filetype));
bool hsinjectable = (FTYPE_HSINJECTABLE(filetype)); bool hsinjectable = (FTYPE_HSINJECTABLE(filetype));
bool restorable = (FTYPE_RESTORABLE(filetype) && CheckA9lh() && !(drvtype & DRV_SYSNAND)); bool restorable = (FTYPE_RESTORABLE(filetype) && CheckA9lh() && !(drvtype & DRV_SYSNAND));
bool ebackupable = (FTYPE_EBACKUP(filetype));
bool xorpadable = (FTYPE_XORPAD(filetype)); bool xorpadable = (FTYPE_XORPAD(filetype));
bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT)); bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT));
bool special_opt = mountable || verificable || decryptable || encryptable || 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]; char pathstr[48];
TruncateString(pathstr, curr_entry->path, 32, 8); 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; n_opt = 0;
int mount = (mountable) ? ++n_opt : -1; int mount = (mountable) ? ++n_opt : -1;
int restore = (restorable) ? ++n_opt : -1; int restore = (restorable) ? ++n_opt : -1;
int ebackup = (ebackupable) ? ++n_opt : -1;
int decrypt = (decryptable) ? ++n_opt : -1; int decrypt = (decryptable) ? ++n_opt : -1;
int encrypt = (encryptable) ? ++n_opt : -1; int encrypt = (encryptable) ? ++n_opt : -1;
int build = (buildable) ? ++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; int launch = (launchable) ? ++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 (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 (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 (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)"; 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, ShowPrompt(false, "%s\nH&S inject %s", pathstr,
(InjectHealthAndSafety(curr_entry->path, destdrv[user_select-1]) == 0) ? "success" : "failed"); (InjectHealthAndSafety(curr_entry->path, destdrv[user_select-1]) == 0) ? "success" : "failed");
} }
return 0;
} else if (user_select == restore) { // -> restore SysNAND (A9LH preserving) } else if (user_select == restore) { // -> restore SysNAND (A9LH preserving)
ShowPrompt(false, "%s\nNAND restore %s", pathstr, ShowPrompt(false, "%s\nNAND restore %s", pathstr,
(SafeRestoreNandDump(curr_entry->path) == 0) ? "success" : "failed"); (SafeRestoreNandDump(curr_entry->path) == 0) ? "success" : "failed");
return 0;
} else if ((user_select == xorpad) || (user_select == xorpad_inplace)) { } else if ((user_select == xorpad) || (user_select == xorpad_inplace)) {
bool inplace = (user_select == xorpad_inplace); bool inplace = (user_select == xorpad_inplace);
bool success = (BuildNcchInfoXorpads((inplace) ? current_path : OUTPUT_PATH, curr_entry->path) == 0); 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; *scroll = 0;
*cursor = 1; *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)) { } else if ((user_select == launch)) {
size_t payload_size = FileGetSize(curr_entry->path); size_t payload_size = FileGetSize(curr_entry->path);
if (ShowUnlockSequence(3, "%s (%dkB)\nLaunch as arm9 payload?", pathstr, payload_size / 1024)) { 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); while(1);
} // failed load is basically impossible here } // failed load is basically impossible here
} }
return 0;
} }
return FileHandlerMenu(current_path, cursor, scroll, current_dir, clipboard); return FileHandlerMenu(current_path, cursor, scroll, current_dir, clipboard);

View File

@ -2,11 +2,26 @@
#include "nand.h" #include "nand.h"
#include "firm.h" #include "firm.h"
#include "fatmbr.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 "fsperm.h"
#include "sha.h" #include "sha.h"
#include "ui.h" #include "ui.h"
#include "vff.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 ReadNandFile(FIL* file, void* buffer, u32 sector, u32 count, u32 keyslot) {
u32 offset = sector * 0x200; u32 offset = sector * 0x200;
u32 size = count * 0x200; u32 size = count * 0x200;
@ -19,6 +34,79 @@ u32 ReadNandFile(FIL* file, void* buffer, u32 sector, u32 count, u32 keyslot) {
return 0; 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) { u32 ValidateNandDump(const char* path) {
const u32 mbr_sectors[] = { SECTOR_TWL, SECTOR_CTR }; const u32 mbr_sectors[] = { SECTOR_TWL, SECTOR_CTR };
const u32 firm_sectors[] = { SECTOR_FIRM0, SECTOR_FIRM1 }; const u32 firm_sectors[] = { SECTOR_FIRM0, SECTOR_FIRM1 };
@ -114,10 +202,9 @@ u32 SafeRestoreNandDump(const char* path) {
u32 safe_sectors[] = { SAFE_SECTORS }; u32 safe_sectors[] = { SAFE_SECTORS };
FIL file; FIL file;
/* if (ValidateNandDump(path) != 0) { // NAND dump validation if ((ValidateNandDump(path) != 0) && // NAND dump validation
ShowPrompt(false, "NAND dump corrupt or not from console.\nYou can still try mount and copy."); !ShowPrompt(true, "NAND dump corrupt or not from console.\nStill continue?"))
return 1; return 1;
}*/
if (!CheckA9lh()) { if (!CheckA9lh()) {
ShowPrompt(false, "Error: A9LH not detected."); ShowPrompt(false, "Error: A9LH not detected.");
return 1; return 1;

View File

@ -5,5 +5,7 @@
#define SAFE_SECTORS 0x000000 + 0x1, SECTOR_SECRET, SECTOR_SECRET + 0x1, \ #define SAFE_SECTORS 0x000000 + 0x1, SECTOR_SECRET, SECTOR_SECRET + 0x1, \
SECTOR_FIRM0, SECTOR_CTR, 0x000000 // last one is a placeholder 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 ValidateNandDump(const char* path);
u32 SafeRestoreNandDump(const char* path); u32 SafeRestoreNandDump(const char* path);

31
source/nand/sysfiles.h Normal file
View File

@ -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;