From e502ad1d99e07451af952a8af922b7e9bc85ad6b Mon Sep 17 00:00:00 2001 From: d0k3 Date: Thu, 2 Mar 2017 22:40:58 +0100 Subject: [PATCH] Provide the tools for CTR transfers (A9LH only) --- source/common/common.h | 2 +- source/fs/filetype.c | 4 ++- source/fs/filetype.h | 2 ++ source/fs/fsutil.c | 2 +- source/godmode.c | 12 ++++++- source/nand/ctrtransfer.c | 76 +++++++++++++++++++++++++++++++++++++++ source/nand/ctrtransfer.h | 5 +++ source/nand/nand.c | 29 ++++++++++++++- source/nand/nand.h | 2 ++ 9 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 source/nand/ctrtransfer.c create mode 100644 source/nand/ctrtransfer.h diff --git a/source/common/common.h b/source/common/common.h index 56b73b7..d7ee865 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -48,7 +48,7 @@ #endif // GodMode9 version -#define VERSION "1.0.3" +#define VERSION "1.0.4" // Maximum payload size (arbitrary value!) #define SELF_MAX_SIZE (320 * 1024) // 320kB diff --git a/source/fs/filetype.c b/source/fs/filetype.c index cc8578f..657ec8a 100644 --- a/source/fs/filetype.c +++ b/source/fs/filetype.c @@ -1,6 +1,7 @@ #include "filetype.h" #include "fsutil.h" #include "fatmbr.h" +#include "nand.h" #include "game.h" #include "chainload.h" @@ -26,8 +27,9 @@ u32 IdentifyFileType(const char* path) { } else if (ValidateMbrHeader((MbrHeader*) data) == 0) { MbrHeader* mbr = (MbrHeader*) data; MbrPartitionInfo* partition0 = mbr->partitions; + bool ctr = (CheckNandMbr(data) & (NAND_TYPE_O3DS|NAND_TYPE_N3DS)); // is this a CTRNAND MBR? if ((partition0->sector + partition0->count) <= (fsize / 0x200)) // size check - return IMG_FAT; // possibly an MBR -> also treat as FAT image + return IMG_FAT | (ctr ? FLAG_CTR : 0); // possibly an MBR -> also treat as FAT image } else if (ValidateCiaHeader((CiaHeader*) data) == 0) { // this only works because these functions ignore CIA content index CiaInfo info; diff --git a/source/fs/filetype.h b/source/fs/filetype.h index 2e0e605..46b0ac6 100644 --- a/source/fs/filetype.h +++ b/source/fs/filetype.h @@ -20,6 +20,7 @@ #define BIN_SUPPORT (1UL<<15) #define TYPE_BASE 0x00FFFFFF // 24 bit reserved for base types +#define FLAG_CTR (1UL<<29) #define FLAG_NUSCDN (1UL<<30) #define FLAG_CXI (1UL<<31) @@ -29,6 +30,7 @@ #define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS)) #define FTYPE_BUILDABLE(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD)) #define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD))) +#define FTYPE_TRANSFERABLE(tp) ((u32) (tp&(IMG_FAT|FLAG_CTR)) == (u32) (IMG_FAT|FLAG_CTR)) #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)) diff --git a/source/fs/fsutil.c b/source/fs/fsutil.c index 00a657d..092b899 100644 --- a/source/fs/fsutil.c +++ b/source/fs/fsutil.c @@ -866,7 +866,7 @@ bool PathDelete(const char* path) { char fpath[256]; // 256 is the maximum length of a full path if (!CheckDirWritePermissions(path)) return false; strncpy(fpath, path, 256); - ShowString("Deleting files, please wait..."); + // ShowString("Deleting files, please wait..."); // handled elsewhere return PathDeleteWorker(fpath); } diff --git a/source/godmode.c b/source/godmode.c index 388e035..bfce2fb 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -13,6 +13,7 @@ #include "virtual.h" #include "vcart.h" #include "nandcmac.h" +#include "ctrtransfer.h" #include "ncchinfo.h" #include "image.h" #include "chainload.h" @@ -611,6 +612,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur bool cryptable_inplace = ((encryptable||decryptable) && !in_output_path && (drvtype & DRV_FAT)); bool buildable = (FTYPE_BUILDABLE(filetype)); bool buildable_legit = (FTYPE_BUILDABLE_L(filetype)); + bool transferable = (FTYPE_TRANSFERABLE(filetype) && (drvtype & DRV_FAT)); bool hsinjectable = (FTYPE_HSINJECTABLE(filetype)); bool restorable = (FTYPE_RESTORABLE(filetype) && IS_A9LH && !(drvtype & DRV_SYSNAND)); bool ebackupable = (FTYPE_EBACKUP(filetype)); @@ -643,7 +645,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur int searchdrv = (DriveType(current_path) & DRV_SEARCH) ? ++n_opt : -1; if (special > 0) optionstr[special-1] = (filetype & IMG_NAND ) ? "NAND image options..." : - (filetype & IMG_FAT ) ? "Mount as FAT image" : + (filetype & IMG_FAT ) ? (transferable) ? "CTRNAND options..." : "Mount as FAT image" : (filetype & GAME_CIA ) ? "CIA image options..." : (filetype & GAME_NCSD ) ? "NCSD image options..." : (filetype & GAME_NCCH ) ? "NCCH image options..." : @@ -774,6 +776,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur int build = (buildable) ? ++n_opt : -1; int build_legit = (buildable_legit) ? ++n_opt : -1; int verify = (verificable) ? ++n_opt : -1; + int ctradapt = (transferable) ? ++n_opt : -1; int hsinject = (hsinjectable) ? ++n_opt : -1; int xorpad = (xorpadable) ? ++n_opt : -1; int xorpad_inplace = (xorpadable) ? ++n_opt : -1; @@ -786,6 +789,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur if (build > 0) optionstr[build-1] = (build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)"; if (build_legit > 0) optionstr[build_legit-1] = "Build CIA (legit)"; if (verify > 0) optionstr[verify-1] = "Verify file"; + if (ctradapt > 0) optionstr[ctradapt-1] = "Adapt image to SysNAND"; if (hsinject > 0) optionstr[hsinject-1] = "Inject to H&S"; if (xorpad > 0) optionstr[xorpad-1] = "Build XORpads (SD output)"; if (xorpad_inplace > 0) optionstr[xorpad_inplace-1] = "Build XORpads (inplace)"; @@ -988,6 +992,10 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur (InjectHealthAndSafety(curr_entry->path, destdrv[user_select-1]) == 0) ? "success" : "failed"); } return 0; + } else if (user_select == ctradapt) { // -> adapt CTRNAND image to SysNAND + ShowPrompt(false, "%s\n%s", pathstr, + (AdaptCtrNandImage(curr_entry->path) == 0) ? "Image adapted to SysNAND" : "Image modification 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"); @@ -1334,6 +1342,7 @@ u32 GodMode() { if (n_marked) { if (ShowPrompt(true, "Delete %u path(s)?", n_marked)) { u32 n_errors = 0; + ShowString("Deleting files, please wait..."); for (u32 c = 0; c < current_dir->n_entries; c++) if (current_dir->entry[c].marked && !PathDelete(current_dir->entry[c].path)) n_errors++; @@ -1344,6 +1353,7 @@ u32 GodMode() { char namestr[36+1]; TruncateString(namestr, curr_entry->name, 28, 12); if (ShowPrompt(true, "Delete \"%s\"?", namestr)) { + ShowString("Deleting files, please wait..."); if (!PathDelete(curr_entry->path)) ShowPrompt(false, "Failed deleting:\n%s", namestr); ClearScreenF(true, false, COLOR_STD_BG); diff --git a/source/nand/ctrtransfer.c b/source/nand/ctrtransfer.c new file mode 100644 index 0000000..08ecc6a --- /dev/null +++ b/source/nand/ctrtransfer.c @@ -0,0 +1,76 @@ +#include "ctrtransfer.h" +#include "nandcmac.h" +#include "fsutil.h" +#include "fsinit.h" +#include "fsperm.h" +#include "image.h" +#include "essentials.h" +#include "ui.h" + +u32 AdaptCtrNandImage(const char* path) { + if (!CheckWritePermissions(path)) return 1; + + // a little warning... + if (!ShowPrompt(true, "This will alter the provided image\nto make it transferable to your NAND.")) + return 1; + + // 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; + } + + // fixing CMACs + ShowString("Fixing .db CMACs, please wait..."); + if ((FixFileCmac("7:/dbs/ticket.db") != 0) || + (FixFileCmac("7:/dbs/certs.db") != 0) || + (FixFileCmac("7:/dbs/title.db") != 0)) { + ShowPrompt(false, "Error: .db file missing."); + InitImgFS(path_bak); + return 1; + } + FixFileCmac("7:/dbs/import.db"); + FixFileCmac("7:/dbs/tmp_t.db"); + FixFileCmac("7:/dbs/tmp_i.db"); + + // cleanup this image + ShowString("Cleaning up image, please wait..."); + // special handling for out of region images + SecureInfo* secnfo_img = (SecureInfo*) TEMP_BUFFER; + SecureInfo* secnfo_loc = (SecureInfo*) (TEMP_BUFFER + 0x200); + if (((FileGetData("7:/rw/sys/SecureInfo_A", (u8*) secnfo_img, sizeof(SecureInfo), 0) == sizeof(SecureInfo)) || + (FileGetData("7:/rw/sys/SecureInfo_B", (u8*) secnfo_img, sizeof(SecureInfo), 0) == sizeof(SecureInfo))) && + ((FileGetData("1:/rw/sys/SecureInfo_A", (u8*) secnfo_loc, sizeof(SecureInfo), 0) == sizeof(SecureInfo)) || + (FileGetData("1:/rw/sys/SecureInfo_B", (u8*) secnfo_loc, sizeof(SecureInfo), 0) == sizeof(SecureInfo))) && + (secnfo_img->region != secnfo_loc->region)) { + secnfo_loc->region = secnfo_img->region; + FileSetData("1:/rw/sys/SecureInfo_C", (u8*) secnfo_loc, sizeof(SecureInfo), 0, true); + } + // actual cleanup + PathDelete("7:/private/movable.sed"); + PathDelete("7:/rw/sys/LocalFriendCodeSeed_B"); + PathDelete("7:/rw/sys/LocalFriendCodeSeed_A"); + PathDelete("7:/rw/sys/SecureInfo_A"); + PathDelete("7:/rw/sys/SecureInfo_B"); + PathDelete("7:/data"); + + // inject all required files to image + u32 flags = OVERWRITE_ALL; + if (!PathCopy("7:/private", "1:/private/movable.sed", &flags) || + !(PathCopy("7:/rw/sys", "1:/rw/sys/LocalFriendCodeSeed_B", &flags) || + PathCopy("7:/rw/sys", "1:/rw/sys/LocalFriendCodeSeed_A", &flags)) || + !(PathCopy("7:/rw/sys", "1:/rw/sys/SecureInfo_A", &flags) || + PathCopy("7:/rw/sys", "1:/rw/sys/SecureInfo_B", &flags))) { + ShowPrompt(false, "Error: Failed transfering data."); + InitImgFS(path_bak); + return 1; + } + PathCopy("7:", "1:/data", &flags); + + InitImgFS(path_bak); + return 0; +} diff --git a/source/nand/ctrtransfer.h b/source/nand/ctrtransfer.h new file mode 100644 index 0000000..597f128 --- /dev/null +++ b/source/nand/ctrtransfer.h @@ -0,0 +1,5 @@ +#pragma once + +#include "common.h" + +u32 AdaptCtrNandImage(const char* path); diff --git a/source/nand/nand.c b/source/nand/nand.c index 133dc75..392d332 100644 --- a/source/nand/nand.c +++ b/source/nand/nand.c @@ -51,6 +51,22 @@ static const u8 twl_mbr[0x42] = { // encrypted version inside the NCSD NAND head 0x55, 0xAA }; +static const u8 ctr_mbr_o3ds[0x42] = { // found at the beginning of the CTRNAND partition (O3DS) + 0x00, 0x05, 0x2B, 0x00, 0x06, 0x02, 0x42, 0x80, 0x65, 0x01, 0x00, 0x00, 0x1B, 0x9F, 0x17, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0xAA +}; + +static const u8 ctr_mbr_n3ds[0x42] = { // found at the beginning of the CTRNAND partition (N3DS) + 0x00, 0x05, 0x1D, 0x00, 0x06, 0x02, 0x82, 0x17, 0x57, 0x01, 0x00, 0x00, 0x69, 0xE9, 0x20, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0xAA +}; + static u8 CtrNandCtr[16]; static u8 TwlNandCtr[16]; static u8 OtpSha256[32] = { 0 }; @@ -352,13 +368,24 @@ int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 n return 0; } +u32 CheckNandMbr(u8* mbr) +{ + if (memcmp(mbr + (0x200 - sizeof(twl_mbr)), twl_mbr, sizeof(twl_mbr)) == 0) + return NAND_TYPE_TWL; // TWLNAND MBR (included in NAND header) + else if (memcmp(mbr + (0x200 - sizeof(ctr_mbr_o3ds)), ctr_mbr_o3ds, sizeof(ctr_mbr_o3ds)) == 0) + return NAND_TYPE_O3DS; // CTRNAND MBR (@0x05C980) + else if (memcmp(mbr + (0x200 - sizeof(ctr_mbr_n3ds)), ctr_mbr_n3ds, sizeof(ctr_mbr_n3ds)) == 0) + return NAND_TYPE_N3DS; // CTRNAND MBR (@0x05C980) + else 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) + if (CheckNandMbr(header_dec) != NAND_TYPE_TWL) return 0; // header does not belong to console // header type check diff --git a/source/nand/nand.h b/source/nand/nand.h index 3b59103..f0d6735 100644 --- a/source/nand/nand.h +++ b/source/nand/nand.h @@ -9,6 +9,7 @@ #define NAND_TYPE_O3DS (1UL<<4) #define NAND_TYPE_N3DS (1UL<<5) #define NAND_TYPE_NO3DS (1UL<<6) +#define NAND_TYPE_TWL (1UL<<7) // minimum size of NAND memory #define NAND_MIN_SECTORS_O3DS 0x1D7800 @@ -47,6 +48,7 @@ int WriteNandSectors(const u8* buffer, u32 sector, u32 count, u32 keyslot, u32 d u64 GetNandSizeSectors(u32 src); u64 GetNandUnusedSectors(u32 src); +u32 CheckNandMbr(u8* mbr); u32 CheckNandHeader(u8* header); u32 CheckNandType(u32 src);