mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Add H&S injection for unencrypted CXIs
This commit is contained in:
parent
7fb0f2df0f
commit
0c696416c7
@ -37,8 +37,9 @@ u32 IdentifyFileType(const char* path) {
|
||||
return GAME_NCSD; // NCSD (".3DS") file
|
||||
} else if (ValidateNcchHeader((NcchHeader*) (void*) header) == 0) {
|
||||
NcchHeader* ncch = (NcchHeader*) (void*) header;
|
||||
u32 type = GAME_NCCH | (NCCH_IS_CXI(ncch) ? FLAG_CXI : 0) | (NCCH_ENCRYPTED(ncch) ? FLAG_ENCRYPTED : 0);
|
||||
if (fsize >= (ncch->size * NCCH_MEDIA_UNIT))
|
||||
return GAME_NCCH; // NCCH (".APP") file
|
||||
return type; // NCCH (".APP") file
|
||||
} else if (ValidateExeFsHeader((ExeFsHeader*) (void*) header, fsize) == 0) {
|
||||
return GAME_EXEFS; // ExeFS file (false positives possible)
|
||||
} else if (memcmp(header, romfs_magic, sizeof(romfs_magic)) == 0) {
|
||||
|
@ -14,13 +14,17 @@
|
||||
#define BIN_NCCHNFO (1<<9)
|
||||
#define BIN_LAUNCH (1<<10)
|
||||
|
||||
#define FTYPE_MOUNTABLE (IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|SYS_FIRM)
|
||||
#define FYTPE_VERIFICABLE (IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|SYS_FIRM)
|
||||
#define FYTPE_DECRYPTABLE (GAME_CIA|GAME_NCSD|GAME_NCCH|SYS_FIRM)
|
||||
#define FTYPE_BUILDABLE (GAME_NCSD|GAME_NCCH|GAME_TMD)
|
||||
#define FTYPE_BUILDABLE_L (FTYPE_BUILDABLE&(GAME_TMD))
|
||||
#define FTYPE_RESTORABLE (IMG_NAND)
|
||||
#define FTYPE_XORPAD (BIN_NCCHNFO)
|
||||
#define FTYPE_PAYLOAD (BIN_LAUNCH)
|
||||
#define FLAG_CXI (1<<30)
|
||||
#define FLAG_ENCRYPTED (1<<31)
|
||||
|
||||
#define FTYPE_MOUNTABLE(tp) (tp&(IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|SYS_FIRM))
|
||||
#define FYTPE_VERIFICABLE(tp) (tp&(IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|SYS_FIRM))
|
||||
#define FYTPE_DECRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|SYS_FIRM))
|
||||
#define FTYPE_BUILDABLE(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD))
|
||||
#define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD)))
|
||||
#define FTYPE_HSINJECTABLE(tp) ((tp&(GAME_NCCH|FLAG_CXI|FLAG_ENCRYPTED)) == (GAME_NCCH|FLAG_CXI))
|
||||
#define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND))
|
||||
#define FTYPE_XORPAD(tp) (tp&(BIN_NCCHNFO))
|
||||
#define FTYPE_PAYLOAD(tp) (tp&(BIN_LAUNCH))
|
||||
|
||||
u32 IdentifyFileType(const char* path);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "ui.h"
|
||||
#include "fsperm.h"
|
||||
#include "filetype.h"
|
||||
#include "platform.h"
|
||||
#include "aes.h"
|
||||
#include "sha.h"
|
||||
#include "vff.h"
|
||||
@ -610,7 +611,7 @@ u32 CheckEncryptedGameFile(const char* path) {
|
||||
else return 1;
|
||||
}
|
||||
|
||||
u32 DecryptNcchNcsdFirmFile(const char* orig, const char* dest, u32 mode,
|
||||
u32 CryptNcchNcsdFirmFile(const char* orig, const char* dest, u32 mode, u16 crypto, // crypto only relevant for NCCH
|
||||
u32 offset, u32 size, TmdContentChunk* chunk, const u8* titlekey) { // this line only for CIA contents
|
||||
// this will do a simple copy for unencrypted files
|
||||
bool inplace = (strncmp(orig, dest, 256) == 0);
|
||||
@ -646,7 +647,7 @@ u32 DecryptNcchNcsdFirmFile(const char* orig, const char* dest, u32 mode,
|
||||
u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i));
|
||||
UINT bytes_read, bytes_written;
|
||||
if (fvx_read(ofp, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1;
|
||||
if (((mode & GAME_NCCH) && (DecryptNcchSequential(MAIN_BUFFER, i, read_bytes) != 0)) ||
|
||||
if (((mode & GAME_NCCH) && (CryptNcchSequential(MAIN_BUFFER, i, read_bytes, crypto) != 0)) ||
|
||||
((mode & GAME_NCSD) && (DecryptNcsdSequential(MAIN_BUFFER, i, read_bytes) != 0)) ||
|
||||
((mode & SYS_FIRM) && (DecryptFirmSequential(MAIN_BUFFER, i, read_bytes) != 0)))
|
||||
ret = 1;
|
||||
@ -718,7 +719,7 @@ u32 DecryptCiaFile(const char* orig, const char* dest) {
|
||||
for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) {
|
||||
TmdContentChunk* chunk = &(cia->content_list[i]);
|
||||
u64 size = getbe64(chunk->size);
|
||||
if (DecryptNcchNcsdFirmFile(orig, dest, GAME_CIA, next_offset, size, chunk, titlekey) != 0)
|
||||
if (CryptNcchNcsdFirmFile(orig, dest, GAME_CIA, NCCH_NOCRYPTO, next_offset, size, chunk, titlekey) != 0)
|
||||
return 1;
|
||||
next_offset += size;
|
||||
}
|
||||
@ -737,7 +738,7 @@ u32 DecryptFirmFile(const char* orig, const char* dest) {
|
||||
UINT btr;
|
||||
|
||||
// actual decryption
|
||||
if (DecryptNcchNcsdFirmFile(orig, dest, SYS_FIRM, 0, 0, NULL, NULL) != 0)
|
||||
if (CryptNcchNcsdFirmFile(orig, dest, SYS_FIRM, NCCH_NOCRYPTO, 0, 0, NULL, NULL) != 0)
|
||||
return 1;
|
||||
|
||||
// open destination file, get FIRM header
|
||||
@ -814,7 +815,7 @@ u32 DecryptGameFile(const char* path, bool inplace) {
|
||||
else if (filetype & SYS_FIRM)
|
||||
ret = DecryptFirmFile(path, destptr);
|
||||
else if (filetype & (GAME_NCCH|GAME_NCSD))
|
||||
ret = DecryptNcchNcsdFirmFile(path, destptr, filetype, 0, 0, NULL, NULL);
|
||||
ret = CryptNcchNcsdFirmFile(path, destptr, filetype, NCCH_NOCRYPTO, 0, 0, NULL, NULL);
|
||||
else ret = 1;
|
||||
|
||||
if (!inplace && (ret != 0))
|
||||
@ -1176,3 +1177,92 @@ u32 BuildNcchInfoXorpads(const char* destdir, const char* path) {
|
||||
fvx_close(&fp_info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 InjectHealthAndSafety(const char* path, const char* destdrv) {
|
||||
const u32 tidlow_hs_o3ds[] = { 0x00020300, 0x00021300, 0x00022300, 0, 0x00026300, 0x00027300, 0x00028300 };
|
||||
const u32 tidlow_hs_n3ds[] = { 0x20020300, 0x20021300, 0x20022300, 0, 0, 0x00027300, 0 };
|
||||
NcchHeader ncch;
|
||||
|
||||
// check input file
|
||||
if ((LoadNcchHeaders(&ncch, NULL, NULL, path, 0) != 0) ||
|
||||
(NCCH_ENCRYPTED(&ncch)) || !(NCCH_IS_CXI(&ncch)))
|
||||
return 1;
|
||||
|
||||
// write permissions
|
||||
if (!CheckWritePermissions(destdrv))
|
||||
return 1;
|
||||
|
||||
// get H&S title id low
|
||||
u32 tidlow_hs = 0;
|
||||
for (char secchar = 'C'; secchar >= 'A'; secchar--) {
|
||||
char path_secinfo[32];
|
||||
u8 secinfo[0x111];
|
||||
u32 region = 0xFF;
|
||||
UINT br;
|
||||
snprintf(path_secinfo, 32, "%s/rw/sys/SecureInfo_%c", destdrv, secchar);
|
||||
if ((fvx_qread(path_secinfo, secinfo, 0, 0x111, &br) != FR_OK) ||
|
||||
(br != 0x111))
|
||||
continue;
|
||||
region = secinfo[0x100];
|
||||
if (region >= sizeof(tidlow_hs_o3ds) / sizeof(u32)) continue;
|
||||
tidlow_hs = (GetUnitPlatform() == PLATFORM_3DS) ?
|
||||
tidlow_hs_o3ds[region] : tidlow_hs_n3ds[region];
|
||||
break;
|
||||
}
|
||||
if (!tidlow_hs) return 1;
|
||||
|
||||
// build paths
|
||||
char path_tmd[64];
|
||||
char path_cxi[64];
|
||||
char path_bak[64];
|
||||
snprintf(path_tmd, 64, "%s/title/00040010/%08lx/content/00000000.tmd", destdrv, tidlow_hs);
|
||||
|
||||
TitleMetaData* tmd = (TitleMetaData*) TEMP_BUFFER;
|
||||
TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1);
|
||||
if (LoadTmdFile(tmd, path_tmd) != 0) return 1;
|
||||
if (!getbe16(tmd->content_count)) return 1;
|
||||
snprintf(path_cxi, 64, "%s/title/00040010/%08lx/content/%08lX.app", destdrv, tidlow_hs, getbe32(chunk->id));
|
||||
snprintf(path_bak, 64, "%s/title/00040010/%08lx/content/%08lX.bak", destdrv, tidlow_hs, getbe32(chunk->id));
|
||||
|
||||
// check crypto, get sig
|
||||
u64 tid_hs = ((u64) 0x00040010 << 32) | tidlow_hs;
|
||||
u16 crypto = NCCH_NOCRYPTO;
|
||||
u8 sig[0x100];
|
||||
if ((LoadNcchHeaders(&ncch, NULL, NULL, path_cxi, 0) != 0) || (SetupNcchCrypto(&ncch) != 0) ||
|
||||
!(NCCH_IS_CXI(&ncch)) || (ncch.programId != tid_hs) || (ncch.partitionId != tid_hs))
|
||||
return 1;
|
||||
crypto = NCCH_GET_CRYPTO(&ncch);
|
||||
memcpy(sig, ncch.signature, 0x100);
|
||||
|
||||
// make a backup copy if there is not already one (point of no return)
|
||||
if (f_stat(path_bak, NULL) != FR_OK) {
|
||||
if (f_rename(path_cxi, path_bak) != FR_OK) return 1;
|
||||
} else f_unlink(path_cxi);
|
||||
|
||||
// copy the source CXI
|
||||
u32 ret = 0;
|
||||
if (CryptNcchNcsdFirmFile(path, path_cxi, GAME_NCCH, NCCH_NOCRYPTO, 0, 0, NULL, NULL) != 0)
|
||||
ret = 1;
|
||||
|
||||
// fix up the injected H&S NCCH header (copy H&S signature, title ID)
|
||||
if ((ret == 0) && (LoadNcchHeaders(&ncch, NULL, NULL, path_cxi, 0) == 0)) {
|
||||
UINT bw;
|
||||
ncch.programId = tid_hs;
|
||||
ncch.partitionId = tid_hs;
|
||||
memcpy(ncch.signature, sig, 0x100);
|
||||
if ((fvx_qwrite(path_cxi, &ncch, 0, sizeof(NcchHeader), &bw) != FR_OK) ||
|
||||
(bw != sizeof(NcchHeader)))
|
||||
ret = 1;
|
||||
} else ret = 1;
|
||||
|
||||
// encrypt the CXI in place
|
||||
if (CryptNcchNcsdFirmFile(path_cxi, path_cxi, GAME_NCCH, crypto, 0, 0, NULL, NULL) != 0)
|
||||
ret = 1;
|
||||
|
||||
if (ret != 0) { // try recover
|
||||
f_unlink(path_cxi);
|
||||
f_rename(path_bak, path_cxi);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -7,3 +7,4 @@ u32 CheckEncryptedGameFile(const char* path);
|
||||
u32 DecryptGameFile(const char* path, bool inplace);
|
||||
u32 BuildCiaFromGameFile(const char* path, bool force_legit);
|
||||
u32 BuildNcchInfoXorpads(const char* destdir, const char* path);
|
||||
u32 InjectHealthAndSafety(const char* path, const char* destdrv);
|
||||
|
@ -582,17 +582,18 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
bool in_output_path = (strncmp(current_path, OUTPUT_PATH, 256) == 0);
|
||||
|
||||
// special stuff, only available for known filetypes (see int special below)
|
||||
bool mountable = ((filetype & FTYPE_MOUNTABLE) && !(drvtype & DRV_IMAGE));
|
||||
bool verificable = (filetype & FYTPE_VERIFICABLE);
|
||||
bool decryptable = (filetype & FYTPE_DECRYPTABLE);
|
||||
bool mountable = (FTYPE_MOUNTABLE(filetype) && !(drvtype & DRV_IMAGE));
|
||||
bool verificable = (FYTPE_VERIFICABLE(filetype));
|
||||
bool decryptable = (FYTPE_DECRYPTABLE(filetype));
|
||||
bool decryptable_inplace = (decryptable && (drvtype & (DRV_SDCARD|DRV_RAMDRIVE)));
|
||||
bool buildable = (filetype & FTYPE_BUILDABLE);
|
||||
bool buildable_legit = (filetype & FTYPE_BUILDABLE_L);
|
||||
bool restorable = (CheckA9lh() && (filetype & FTYPE_RESTORABLE) && !(drvtype & DRV_SYSNAND));
|
||||
bool xorpadable = (filetype & FTYPE_XORPAD);
|
||||
bool launchable = ((filetype & FTYPE_PAYLOAD) && (drvtype & DRV_FAT));
|
||||
bool buildable = (FTYPE_BUILDABLE(filetype));
|
||||
bool buildable_legit = (FTYPE_BUILDABLE_L(filetype));
|
||||
bool hsinjectable = (FTYPE_HSINJECTABLE(filetype));
|
||||
bool restorable = (FTYPE_RESTORABLE(filetype) && CheckA9lh() && !(drvtype & DRV_SYSNAND));
|
||||
bool xorpadable = (FTYPE_XORPAD(filetype));
|
||||
bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT));
|
||||
bool special_opt = mountable || verificable || decryptable || decryptable_inplace ||
|
||||
buildable || buildable_legit || restorable || xorpadable || launchable;
|
||||
buildable || buildable_legit || hsinjectable || restorable || xorpadable || launchable;
|
||||
|
||||
char pathstr[32 + 1];
|
||||
TruncateString(pathstr, curr_entry->path, 32, 8);
|
||||
@ -699,6 +700,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 hsinject = (hsinjectable) ? ++n_opt : -1;
|
||||
int xorpad = (xorpadable) ? ++n_opt : -1;
|
||||
int xorpad_inplace = (xorpadable) ? ++n_opt : -1;
|
||||
int launch = (launchable) ? ++n_opt : -1;
|
||||
@ -709,6 +711,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 (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)";
|
||||
if (launch > 0) optionstr[launch-1] = "Launch as ARM9 payload";
|
||||
@ -845,6 +848,22 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
(VerifyGameFile(curr_entry->path) == 0) ? "success" : "failed");
|
||||
}
|
||||
return 0;
|
||||
} else if (user_select == hsinject) { // -> Inject to Health & Safety
|
||||
char* destdrv[2] = { NULL };
|
||||
n_opt = 0;
|
||||
if (DriveType("1:")) {
|
||||
optionstr[n_opt] = "SysNAND H&S inject";
|
||||
destdrv[n_opt++] = "1:";
|
||||
}
|
||||
if (DriveType("4:")) {
|
||||
optionstr[n_opt] = "EmuNAND H&S inject";
|
||||
destdrv[n_opt++] = "4:";
|
||||
}
|
||||
user_select = (n_opt > 1) ? (int) ShowSelectPrompt(n_opt, optionstr, pathstr) : n_opt;
|
||||
if (user_select) {
|
||||
ShowPrompt(false, "%s\nH&S inject %s", pathstr,
|
||||
(InjectHealthAndSafety(curr_entry->path, destdrv[user_select-1]) == 0) ? "success" : "failed");
|
||||
}
|
||||
} else if (user_select == restore) { // -> restore SysNAND (A9LH preserving)
|
||||
ShowPrompt(false, "%s\nNAND restore %s", pathstr,
|
||||
(SafeRestoreNandDump(curr_entry->path) == 0) ? "success" : "failed");
|
||||
|
Loading…
x
Reference in New Issue
Block a user