mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Enabled embedded backups for NAND images
This commit is contained in:
parent
1764a1b271
commit
89b987b1c0
@ -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"
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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
31
source/nand/sysfiles.h
Normal 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;
|
Loading…
x
Reference in New Issue
Block a user