Enable restore of original compression for NCSD & NCCH

This commit is contained in:
d0k3 2025-10-20 16:53:42 +02:00
parent ceabad8b3f
commit 34ca0fc3a7
9 changed files with 75 additions and 15 deletions

View File

@ -54,6 +54,7 @@
#define FTYPE_VERIFICABLE(tp) (tp&(IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_CDNTMD|GAME_TWLTMD|GAME_TIE|GAME_TAD|GAME_TICKET|GAME_BOSS|SYS_FIRM))
#define FTYPE_DECRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|GAME_NUSCDN|SYS_FIRM|BIN_KEYDB))
#define FTYPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|BIN_KEYDB))
#define FTYPE_CRYPTOFIXABLE(tp) (tp&(GAME_NCSD|GAME_NCCH))
#define FTYPE_CIABUILD(tp) ((tp&(GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_CDNTMD|GAME_TWLTMD|GAME_TIE|GAME_TAD)) || ((tp&GAME_NDS)&&(tp&(FLAG_DSIW))))
#define FTYPE_CIABUILD_L(tp) (tp&(GAME_TMD|GAME_CDNTMD|GAME_TIE|GAME_TAD))
#define FTYPE_CIAINSTALL(tp) ((tp&(GAME_NCSD|GAME_NCCH|GAME_CIA|GAME_CDNTMD|GAME_TWLTMD)) || ((tp&GAME_NDS)&&(tp&(FLAG_DSIW))))

View File

@ -228,14 +228,58 @@ u32 CryptNcch(void* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* e
return 0;
}
u32 BruteForceNcchCrypto(void* data, u16* crypto) {
// data must contain NCCH header and (if available) ExtHeader (0xA00 byte total)
NcchHeader ncch;
NcchExtHeader exthdr;
memcpy(&ncch, data, sizeof(NcchHeader));
if (ValidateNcchHeader(&ncch))
return 1; // not an NCCH header
if (ncch.size_exthdr) {
memcpy(&exthdr, ((u8*)data + NCCH_EXTHDR_OFFSET), sizeof(NcchExtHeader));
if ((NCCH_ENCRYPTED(&ncch)) &&
(DecryptNcch((u8*) &exthdr, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), &ncch, NULL) != 0))
return 1; // could not decrypt the extHeader
}
// the brute forcing part..
for (u32 i = 0x00; i < 0x20; i++) {
const u8 flag3_settings[4] = { 0x00, 0x01, 0x0A, 0x0B };
const u8 flag7_mask = 0b11011010;
ncch.flags[3] = flag3_settings[i & 0b11];
ncch.flags[7] =
(ncch.flags[7] & flag7_mask) |
(((i>>2)&0b1)<<5) | // seed crypto flag
(((i>>3)&0b1)<<0) | // fixed crypto flag
(((i>>4)&0b1)<<2); // no crypto flag
if (ValidateNcchSignature(&ncch, (ncch.size_exthdr) ? &exthdr : NULL) == 0) {
*crypto = NCCH_GET_CRYPTO(&ncch);
return 0;
}
}
// if we get here, we didn't find a the crypto flags
return 1;
}
// on the fly de- / encryptor for NCCH - sequential
u32 CryptNcchSequential(void* data, u32 offset, u32 size, u16 crypt_to) {
u32 CryptNcchSequential(void* data, u32 offset, u32 size, u16 crypto) {
// warning: this will only work for sequential processing
// unexpected results otherwise
static NcchHeader ncch = { 0 };
static ExeFsHeader exefs = { 0 };
static NcchHeader* ncchptr = NULL;
static ExeFsHeader* exefsptr = NULL;
static u16 crypt_to = NCCH_NOCRYPTO;
// brute force original crypto
if (crypto == NCCH_BFCRYPTO) {
if ((offset == 0) &&
((size < 0xA00) || (BruteForceNcchCrypto(data, &crypt_to) != 0)))
return 1;
} else crypt_to = crypto;
// fetch ncch header from data
if ((offset == 0) && (size >= sizeof(NcchHeader))) {

View File

@ -16,6 +16,7 @@
#define NCCH_NOCRYPTO 0x0004
#define NCCH_STDCRYPTO 0x0000
#define NCCH_BFCRYPTO 0xFFFF
#define NCCH_GET_CRYPTO(ncch) (!NCCH_ENCRYPTED(ncch) ? NCCH_NOCRYPTO : (((ncch)->flags[3] << 8) | ((ncch)->flags[7]&(0x01|0x20))))
// wrapper defines
@ -87,7 +88,7 @@ u32 ValidateNcchHeader(NcchHeader* header);
u32 ValidateNcchSignature(NcchHeader* header, NcchExtHeader* exthdr);
u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid);
u32 SetupNcchCrypto(NcchHeader* ncch, u16 crypt_to);
u32 CryptNcch(void* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypto);
u32 CryptNcch(void* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypt_to);
u32 CryptNcchSequential(void* data, u32 offset, u32 size, u16 crypto);
u32 SetNcchSdFlag(void* data);
u32 SetupSystemForNcch(NcchHeader* ncch, bool to_emunand);

View File

@ -1213,6 +1213,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
bool verificable = (FTYPE_VERIFICABLE(filetype));
bool decryptable = (FTYPE_DECRYPTABLE(filetype));
bool encryptable = (FTYPE_ENCRYPTABLE(filetype));
bool crypto_fixable = (FTYPE_CRYPTOFIXABLE(filetype));
bool cryptable_inplace = ((encryptable||decryptable) && !in_output_path && (*current_path == '0'));
bool cia_buildable = (FTYPE_CIABUILD(filetype));
bool cia_buildable_legit = (FTYPE_CIABUILD_L(filetype));
@ -1626,7 +1627,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
continue;
}
DrawDirContents(current_dir, (*cursor = i), scroll);
if (!(filetype & BIN_KEYDB) && (CryptGameFile(path, inplace, false) == 0)) n_success++;
if (!(filetype & BIN_KEYDB) && (CryptGameFile(path, inplace, false, false) == 0)) n_success++;
else if ((filetype & BIN_KEYDB) && (CryptAesKeyDb(path, inplace, false) == 0)) n_success++;
else { // on failure: show error, continue
char lpathstr[UTF_BUFFER_BYTESIZE(32)];
@ -1646,7 +1647,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
ShowPrompt(false, "%s\n%s", pathstr, STR_FILE_NOT_ENCRYPTED);
} else {
u32 ret = (filetype & BIN_KEYDB) ? CryptAesKeyDb(file_path, inplace, false) :
CryptGameFile(file_path, inplace, false);
CryptGameFile(file_path, inplace, false, false);
if (inplace || (ret != 0)) ShowPrompt(false, "%s\n%s", pathstr, (ret == 0) ? STR_DECRYPTION_SUCCESS : STR_DECRYPTION_FAILED);
else ShowPrompt(false, STR_PATH_DECRYPTED_TO_OUT, pathstr, OUTPUT_PATH);
}
@ -1654,7 +1655,13 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
return 0;
}
else if (user_select == encrypt) { // -> encrypt game file
if (cryptable_inplace) {
if (crypto_fixable) {
optionstr[0] = STR_STANDARD_CRYPTO;
optionstr[1] = STR_ORIGINAL_CRYPTO;
user_select = (int) ShowSelectPrompt(2, optionstr, "%s", STR_SELECT_TYPE_OF_ENCRYPTION);
} else (user_select = 1);
bool restore = (user_select == 2);
if (user_select && cryptable_inplace) {
char encryptToOut[UTF_BUFFER_BYTESIZE(64)];
snprintf(encryptToOut, sizeof(encryptToOut), STR_ENCRYPT_TO_OUT, OUTPUT_PATH);
optionstr[0] = encryptToOut;
@ -1662,7 +1669,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
user_select = (int) ((n_marked > 1) ?
ShowSelectPrompt(2, optionstr, STR_PATH_N_FILES_SELECTED, pathstr, n_marked) :
ShowSelectPrompt(2, optionstr, "%s%s", pathstr, tidstr));
} else user_select = 1;
} else if (user_select) user_select = 1;
bool inplace = (user_select == 2);
if (!user_select) { // do nothing when no choice is made
} else if ((n_marked > 1) && ShowPrompt(true, STR_TRY_TO_ENCRYPT_N_SELECTED_FILES, n_marked)) {
@ -1678,7 +1685,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
continue;
}
DrawDirContents(current_dir, (*cursor = i), scroll);
if (!(filetype & BIN_KEYDB) && (CryptGameFile(path, inplace, true) == 0)) n_success++;
if (!(filetype & BIN_KEYDB) && (CryptGameFile(path, inplace, true, restore) == 0)) n_success++;
else if ((filetype & BIN_KEYDB) && (CryptAesKeyDb(path, inplace, true) == 0)) n_success++;
else { // on failure: show error, continue
char lpathstr[UTF_BUFFER_BYTESIZE(32)];
@ -1695,7 +1702,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
if (!inplace && n_success) ShowPrompt(false, STR_N_FILES_WRITTEN_TO_OUT, n_success, OUTPUT_PATH);
} else {
u32 ret = (filetype & BIN_KEYDB) ? CryptAesKeyDb(file_path, inplace, true) :
CryptGameFile(file_path, inplace, true);
CryptGameFile(file_path, inplace, true, restore);
if (inplace || (ret != 0)) ShowPrompt(false, "%s\n%s", pathstr, (ret == 0) ? STR_ENCRYPTION_SUCCESS : STR_ENCRYPTION_FAILED);
else ShowPrompt(false, STR_PATH_ENCRYPTED_TO_OUT, pathstr, OUTPUT_PATH);
}

View File

@ -19,7 +19,7 @@ static int title_decrypt(lua_State* L) {
ret = (CryptAesKeyDb(path, true, false) == 0);
whichfailed = "CryptAesKeyDb";
} else {
ret = (CryptGameFile(path, true, false) == 0);
ret = (CryptGameFile(path, true, false, false) == 0);
whichfailed = "CryptGameFile";
}
@ -41,7 +41,7 @@ static int title_encrypt(lua_State* L) {
ret = (CryptAesKeyDb(path, true, true) == 0);
whichfailed = "CryptAesKeyDb";
} else {
ret = (CryptGameFile(path, true, true) == 0);
ret = (CryptGameFile(path, true, true, false) == 0);
whichfailed = "CryptGameFile";
}

View File

@ -15,6 +15,7 @@
// use NCCH crypto defines for everything
#define CRYPTO_DECRYPT NCCH_NOCRYPTO
#define CRYPTO_ENCRYPT NCCH_STDCRYPTO
#define CRYPTO_RESTORE NCCH_BFCRYPTO
// partitionA path
#define PART_PATH "D:/partitionA.bin"
@ -1462,13 +1463,16 @@ u32 CryptCdnFile(const char* orig, const char* dest, u16 crypto) {
return ret;
}
u32 CryptGameFile(const char* path, bool inplace, bool encrypt) {
u32 CryptGameFile(const char* path, bool inplace, bool encrypt, bool restore) {
u64 filetype = IdentifyFileType(path);
u16 crypto = encrypt ? CRYPTO_ENCRYPT : CRYPTO_DECRYPT;
char dest[256];
char* destptr = (char*) path;
u32 ret = 0;
if (restore && (filetype & (GAME_NCCH|GAME_NCSD)))
crypto = CRYPTO_RESTORE;
if (!inplace) { // build output name
// build output name
snprintf(dest, sizeof(dest), OUTPUT_PATH "/");

View File

@ -4,7 +4,7 @@
u32 VerifyGameFile(const char* path, bool sig_check);
u32 CheckEncryptedGameFile(const char* path);
u32 CryptGameFile(const char* path, bool inplace, bool encrypt);
u32 CryptGameFile(const char* path, bool inplace, bool encrypt, bool restore);
u32 BuildCiaFromGameFile(const char* path, bool force_legit);
u32 InstallGameFile(const char* path, bool to_emunand);
u32 InstallCifinishFile(const char* path, bool to_emunand);

View File

@ -1352,13 +1352,13 @@ bool run_cmd(cmd_id id, u32 flags, char** argv, char* err_str) {
else if (id == CMD_ID_DECRYPT) {
u64 filetype = IdentifyFileType(argv[0]);
if (filetype & BIN_KEYDB) ret = (CryptAesKeyDb(argv[0], true, false) == 0);
else ret = (CryptGameFile(argv[0], true, false) == 0);
else ret = (CryptGameFile(argv[0], true, false, false) == 0);
if (err_str) snprintf(err_str, _ERR_STR_LEN, "%s", STR_SCRIPTERR_DECRYPT_FAILED);
}
else if (id == CMD_ID_ENCRYPT) {
u64 filetype = IdentifyFileType(argv[0]);
if (filetype & BIN_KEYDB) ret = (CryptAesKeyDb(argv[0], true, true) == 0);
else ret = (CryptGameFile(argv[0], true, true) == 0);
else ret = (CryptGameFile(argv[0], true, true, false) == 0);
if (err_str) snprintf(err_str, _ERR_STR_LEN, "%s", STR_SCRIPTERR_ENCRYPT_FAILED);
}
else if (id == CMD_ID_BUILDCIA) {

View File

@ -816,5 +816,8 @@
"ERROR_SIGNATURE_CHECK_FAILED": "Error: Signature check failed",
"USE_SIGNATURE_VERIFICATION": "Use signature verification?",
"IGNORE_SIGNATURES": "Ignore signatures",
"VERIFY_SIGNATURES": "Verify signatures"
"VERIFY_SIGNATURES": "Verify signatures",
"STANDARD_CRYPTO": "Standard encryption",
"ORIGINAL_CRYPTO": "Original encryption",
"SELECT_TYPE_OF_ENCRYPTION": "Select type of encryption"
}