Provide the tools for CTR transfers (A9LH only)

This commit is contained in:
d0k3 2017-03-02 22:40:58 +01:00
parent 55e3da2e3c
commit e502ad1d99
9 changed files with 129 additions and 5 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

76
source/nand/ctrtransfer.c Normal file
View File

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

View File

@ -0,0 +1,5 @@
#pragma once
#include "common.h"
u32 AdaptCtrNandImage(const char* path);

View File

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

View File

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