From 1a9cad856da46ce726af0c61f471f26ce4297aad Mon Sep 17 00:00:00 2001 From: d0k3 Date: Mon, 30 Jan 2017 22:06:26 +0100 Subject: [PATCH] Improved NCCH crypto routines ... now allow on-the-fly reencryption. H&S inject is now also possible for encrypted CXIs --- source/fs/filetype.c | 2 +- source/fs/filetype.h | 5 ++-- source/game/gameutil.c | 19 ++++++++------- source/game/ncch.c | 55 +++++++++++++++++++++++++----------------- source/game/ncch.h | 6 ++--- source/game/ncchinfo.c | 2 +- source/virtual/vgame.c | 2 +- 7 files changed, 51 insertions(+), 40 deletions(-) diff --git a/source/fs/filetype.c b/source/fs/filetype.c index d67a958..292530d 100644 --- a/source/fs/filetype.c +++ b/source/fs/filetype.c @@ -37,7 +37,7 @@ 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); + u32 type = GAME_NCCH | (NCCH_IS_CXI(ncch) ? FLAG_CXI : 0); if (fsize >= (ncch->size * NCCH_MEDIA_UNIT)) return type; // NCCH (".APP") file } else if (ValidateExeFsHeader((ExeFsHeader*) (void*) header, fsize) == 0) { diff --git a/source/fs/filetype.h b/source/fs/filetype.h index 353e7ee..2df2f0d 100644 --- a/source/fs/filetype.h +++ b/source/fs/filetype.h @@ -15,15 +15,14 @@ #define BIN_NCCHNFO (1<<10) #define BIN_LAUNCH (1<<11) -#define FLAG_CXI (1<<30) -#define FLAG_ENCRYPTED (1<<31) +#define FLAG_CXI (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|GAME_BOSS|SYS_FIRM)) #define FYTPE_DECRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|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_HSINJECTABLE(tp) ((tp&(GAME_NCCH|FLAG_CXI)) == (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)) diff --git a/source/game/gameutil.c b/source/game/gameutil.c index c19a1dd..a9c4972 100644 --- a/source/game/gameutil.c +++ b/source/game/gameutil.c @@ -309,7 +309,7 @@ u32 VerifyNcchFile(const char* path, u32 offset, u32 size) { } // check / setup crypto - if (SetupNcchCrypto(&ncch) != 0) { + if (SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 0) { if (!offset) ShowPrompt(false, "%s\nError: Crypto not set up", pathstr); fvx_close(&file); return 1; @@ -722,12 +722,12 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 UINT bytes_read, bytes_written; u8 ctr[16]; + NcchHeader* ncch = (NcchHeader*) (void*) MAIN_BUFFER; GetTmdCtr(ctr, chunk); // NCCH crypto? if (fvx_read(ofp, MAIN_BUFFER, sizeof(NcchHeader), &bytes_read) != FR_OK) ret = 1; if (cia_crypto) DecryptCiaContentSequential(MAIN_BUFFER, sizeof(NcchHeader), ctr, titlekey); - ncch_crypto = ((ValidateNcchHeader((NcchHeader*) (void*) MAIN_BUFFER) == 0) && - NCCH_ENCRYPTED((NcchHeader*) (void*) MAIN_BUFFER)); - if (ncch_crypto && (SetupNcchCrypto((NcchHeader*) (void*) MAIN_BUFFER) != 0)) + ncch_crypto = ((ValidateNcchHeader(ncch) == 0) && NCCH_ENCRYPTED(ncch)); + if (ncch_crypto && (SetupNcchCrypto(ncch, NCCH_NOCRYPTO) != 0)) ret = 1; GetTmdCtr(ctr, chunk); @@ -912,7 +912,7 @@ u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset, NcchHeader ncch; if ((fvx_read(&ofile, &ncch, sizeof(NcchHeader), &bytes_read) != FR_OK) || (ValidateNcchHeader(&ncch) != 0) || - (SetupNcchCrypto(&ncch) != 0)) + (SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 0)) ncch_crypto = false; fvx_lseek(&ofile, offset); } @@ -1243,9 +1243,9 @@ u32 InjectHealthAndSafety(const char* path, const char* destdrv) { const u32 tidlow_hs_n3ds[] = { 0x20020300, 0x20021300, 0x20022300, 0, 0, 0x00027300, 0 }; NcchHeader ncch; - // check input file + // check input file / crypto if ((LoadNcchHeaders(&ncch, NULL, NULL, path, 0) != 0) || - (NCCH_ENCRYPTED(&ncch)) || !(NCCH_IS_CXI(&ncch))) + !(NCCH_IS_CXI(&ncch)) || (SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 0)) return 1; // write permissions @@ -1288,8 +1288,9 @@ u32 InjectHealthAndSafety(const char* path, const char* destdrv) { 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)) + if ((LoadNcchHeaders(&ncch, NULL, NULL, path_cxi, 0) != 0) || + (SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 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); diff --git a/source/game/ncch.c b/source/game/ncch.c index 1555fdf..75c8789 100644 --- a/source/game/ncch.c +++ b/source/game/ncch.c @@ -148,9 +148,9 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) { return 1; } -u32 SetNcchKey(NcchHeader* ncch, u16 crypt_to, u32 keyid) { - u8 flags3 = (crypt_to & 0x04) ? ncch->flags[3] : (crypt_to >> 8) & 0xFF; - u8 flags7 = (crypt_to & 0x04) ? ncch->flags[7] : crypt_to & 0xFF; +u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid) { + u8 flags3 = (crypto >> 8) & 0xFF; + u8 flags7 = crypto & 0xFF; u32 keyslot = (!keyid || !flags3) ? 0x2C : // standard / secure3 / secure4 / 7.x crypto (flags3 == 0x0A) ? 0x18 : (flags3 == 0x0B) ? 0x1B : 0x25; @@ -195,13 +195,20 @@ u32 SetNcchKey(NcchHeader* ncch, u16 crypt_to, u32 keyid) { return 0; } -u32 SetupNcchCrypto(NcchHeader* ncch) { - return (!NCCH_ENCRYPTED(ncch) || - ((SetNcchKey(ncch, NCCH_NOCRYPTO, 0) == 0) && (SetNcchKey(ncch, NCCH_NOCRYPTO, 1) == 0))) ? 0 : 1; +// this is used to force and check crypto setup +// (also prevents SHA register usage later on) +u32 SetupNcchCrypto(NcchHeader* ncch, u16 crypt_to) { + u16 crypt_from = NCCH_GET_CRYPTO(ncch); + u32 res_from = ((crypt_from & NCCH_NOCRYPTO) || + ((SetNcchKey(ncch, crypt_from, 0) == 0) && (SetNcchKey(ncch, crypt_from, 1) == 0))) ? 0 : 1; + u32 res_to = ((crypt_to & NCCH_NOCRYPTO) || + ((SetNcchKey(ncch, crypt_to, 0) == 0) && (SetNcchKey(ncch, crypt_to, 1) == 0))) ? 0 : 1; + return res_from | res_to; } u32 CryptNcchSection(u8* data, u32 offset_data, u32 size_data, u32 offset_section, u32 size_section, - u32 offset_ctr, NcchHeader* ncch, u32 snum, u16 crypto, u32 keyid) { + u32 offset_ctr, NcchHeader* ncch, u32 snum, u16 crypt_to, u32 keyid) { + u16 crypt_from = NCCH_GET_CRYPTO(ncch); const u32 mode = AES_CNT_CTRNAND_MODE; // check if section in data @@ -225,30 +232,34 @@ u32 CryptNcchSection(u8* data, u32 offset_data, u32 size_data, u32 offset_sectio // actual decryption stuff u8 ctr[16]; GetNcchCtr(ctr, ncch, snum); - if (SetNcchKey(ncch, crypto, keyid) != 0) return 1; - ctr_decrypt_byte(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr); + if (!(crypt_from & NCCH_NOCRYPTO)) { + if (SetNcchKey(ncch, crypt_from, keyid) != 0) return 1; + ctr_decrypt_byte(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr); + } + if (!(crypt_to & NCCH_NOCRYPTO)) { + if (SetNcchKey(ncch, crypt_to, keyid) != 0) return 1; + ctr_decrypt_byte(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr); + } return 0; } // on the fly de-/encryptor for NCCH -u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypto) { +u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypt_to) { const u32 offset_flag3 = 0x188 + 3; const u32 offset_flag7 = 0x188 + 7; + u16 crypt_from = NCCH_GET_CRYPTO(ncch); // check for encryption - if (((!NCCH_ENCRYPTED(ncch)) && (crypto & 0x04)) || - (crypto == NCCH_GET_CRYPTO(ncch))) + if ((crypt_to & crypt_from & NCCH_NOCRYPTO) || (crypt_to == crypt_from)) return 0; // desired end result already met - else if ((NCCH_ENCRYPTED(ncch)) && !(crypto & 0x04)) - return 1; // encrypted differently // ncch flags handling if ((offset <= offset_flag3) && (offset + size > offset_flag3)) - data[offset_flag3 - offset] = (crypto >> 8); + data[offset_flag3 - offset] = (crypt_to >> 8); if ((offset <= offset_flag7) && (offset + size > offset_flag7)) { data[offset_flag7 - offset] &= ~(0x01|0x20|0x04); - data[offset_flag7 - offset] |= (crypto & (0x01|0x20|0x04)); + data[offset_flag7 - offset] |= (crypt_to & (0x01|0x20|0x04)); } // exthdr handling @@ -256,7 +267,7 @@ u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exe if (CryptNcchSection(data, offset, size, NCCH_EXTHDR_OFFSET, NCCH_EXTHDR_SIZE, - 0, ncch, 1, crypto, 0) != 0) return 1; + 0, ncch, 1, crypt_to, 0) != 0) return 1; } // exefs handling @@ -264,7 +275,7 @@ u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exe // exefs header handling if (CryptNcchSection(data, offset, size, ncch->offset_exefs * NCCH_MEDIA_UNIT, - 0x200, 0, ncch, 2, crypto, 0) != 0) return 1; + 0x200, 0, ncch, 2, crypt_to, 0) != 0) return 1; // exefs file handling if (exefs) for (u32 i = 0; i < 10; i++) { @@ -272,7 +283,7 @@ u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exe if (CryptNcchSection(data, offset, size, (ncch->offset_exefs * NCCH_MEDIA_UNIT) + 0x200 + file->offset, file->size, 0x200 + file->offset, - ncch, 2, crypto, EXEFS_KEYID(file->name)) != 0) return 1; + ncch, 2, crypt_to, EXEFS_KEYID(file->name)) != 0) return 1; } } @@ -281,14 +292,14 @@ u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exe if (CryptNcchSection(data, offset, size, ncch->offset_romfs * NCCH_MEDIA_UNIT, ncch->size_romfs * NCCH_MEDIA_UNIT, - 0, ncch, 3, crypto, 1) != 0) return 1; + 0, ncch, 3, crypt_to, 1) != 0) return 1; } return 0; } // on the fly de- / encryptor for NCCH - sequential -u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypto) { +u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypt_to) { // warning: this will only work for sequential processing // unexpected results otherwise static NcchHeader ncch = { 0 }; @@ -320,7 +331,7 @@ u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypto) { } } - return CryptNcch(data, offset, size, ncchptr, exefsptr, crypto); + return CryptNcch(data, offset, size, ncchptr, exefsptr, crypt_to); } u32 SetNcchSdFlag(u8* data) { // data must be at least 0x600 byte and start with NCCH header diff --git a/source/game/ncch.h b/source/game/ncch.h index 3a91db6..791519a 100644 --- a/source/game/ncch.h +++ b/source/game/ncch.h @@ -11,8 +11,8 @@ #define NCCH_ENCRYPTED(ncch) (!((ncch)->flags[7] & 0x04)) #define NCCH_IS_CXI(ncch) ((ncch)->flags[5] & 0x02) -#define NCCH_GET_CRYPTO(ncch) (((ncch)->flags[3] << 8) | (ncch)->flags[7]) #define NCCH_NOCRYPTO 0x0004 +#define NCCH_GET_CRYPTO(ncch) (!NCCH_ENCRYPTED(ncch) ? NCCH_NOCRYPTO : (((ncch)->flags[3] << 8) | ((ncch)->flags[7]&(0x01|0x20)))) // wrapper defines #define DecryptNcch(data, offset, size, ncch, exefs) CryptNcch(data, offset, size, ncch, exefs, NCCH_NOCRYPTO) @@ -70,8 +70,8 @@ typedef struct { } __attribute__((packed, aligned(16))) NcchHeader; u32 ValidateNcchHeader(NcchHeader* header); -u32 SetNcchKey(NcchHeader* ncch, u16 crypt_to, u32 keyid); -u32 SetupNcchCrypto(NcchHeader* ncch); +u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid); +u32 SetupNcchCrypto(NcchHeader* ncch, u16 crypt_to); u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypto); u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypto); u32 SetNcchSdFlag(u8* data); diff --git a/source/game/ncchinfo.c b/source/game/ncchinfo.c index 99a2840..c7e6999 100644 --- a/source/game/ncchinfo.c +++ b/source/game/ncchinfo.c @@ -48,7 +48,7 @@ u32 BuildNcchInfoXorpad(u8* buffer, NcchInfoEntry* entry, u32 size, u32 offset) ncch.flags[3] = (u8) entry->ncchFlag3; ncch.flags[7] = (u8) (entry->ncchFlag7 & ~0x04); ncch.programId = ncch.partitionId = entry->titleId; - if (SetNcchKey(&ncch, NCCH_NOCRYPTO, 1) != 0) + if (SetNcchKey(&ncch, NCCH_GET_CRYPTO(&ncch), 1) != 0) return 1; // write xorpad diff --git a/source/virtual/vgame.c b/source/virtual/vgame.c index 8c91a38..9f21830 100644 --- a/source/virtual/vgame.c +++ b/source/virtual/vgame.c @@ -121,7 +121,7 @@ bool BuildVGameNcchDir(void) { u32 n = 0; // NCCH crypto - bool ncch_crypto = (NCCH_ENCRYPTED(ncch)) && (SetupNcchCrypto(ncch) == 0); + bool ncch_crypto = (NCCH_ENCRYPTED(ncch)) && (SetupNcchCrypto(ncch, NCCH_NOCRYPTO) == 0); // header strncpy(templates[n].name, NAME_NCCH_HEADER, 32);