From c8c062e7fa63b6cb56e3309525e3e1eb72c27d6f Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Wed, 22 Jul 2020 20:37:02 -0400 Subject: [PATCH] Test unified NCA hash layer write function (works gucci). --- source/main.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++- source/nca.c | 115 ++++++++++++++++++++++++------------------------- source/nca.h | 5 ++- source/romfs.c | 18 ++++---- source/romfs.h | 2 +- 5 files changed, 182 insertions(+), 71 deletions(-) diff --git a/source/main.c b/source/main.c index 3a9e833..e26315c 100644 --- a/source/main.c +++ b/source/main.c @@ -216,7 +216,7 @@ int main(int argc, char *argv[]) u8 *buf = NULL; - u64 base_tid = (u64)0x01006A800016E000; // ACNH 0x01006F8002326000 | Smash 0x01006A800016E000 | Dark Souls 0x01004AB00A260000 | BotW 0x01007EF00011E000 + u64 base_tid = (u64)0x010082400BCC6000; // ACNH 0x01006F8002326000 | Smash 0x01006A800016E000 | Dark Souls 0x01004AB00A260000 | BotW 0x01007EF00011E000 | Untitled Goose Game 0x010082400BCC6000 u64 update_tid = (base_tid | 0x800); Ticket base_tik = {0}, update_tik = {0}; @@ -371,6 +371,117 @@ int main(int argc, char *argv[]) consolePrint("bktr initialize ctx succeeded\n"); + + + + + + + + + + + + + + + + + FILE *tmp_file = NULL; + RomFileSystemFileEntry *romfs_file_entry = NULL; + RomFileSystemFileEntryPatch romfs_patch = {0}; + + romfs_file_entry = romfsGetFileEntryByPath(&(bktr_ctx.base_romfs_ctx), "/Data/rawsettings"); + if (!romfs_file_entry) + { + consolePrint("romfs get file entry by path failed\n"); + goto out2; + } + + consolePrint("romfs get file entry by path success: %s | 0x%lX | %p\n", romfs_file_entry->name, romfs_file_entry->size, romfs_file_entry); + + if (!romfsReadFileEntryData(&(bktr_ctx.base_romfs_ctx), romfs_file_entry, buf, romfs_file_entry->size, 0)) + { + consolePrint("romfs read file entry failed\n"); + goto out2; + } + + consolePrint("romfs read file entry success\n"); + + memset(buf, 0xAA, romfs_file_entry->size); + + if (!romfsGenerateFileEntryPatch(&(bktr_ctx.base_romfs_ctx), romfs_file_entry, buf, romfs_file_entry->size, 0, &romfs_patch)) + { + consolePrint("romfs file entry patch failed\n"); + goto out2; + } + + consolePrint("romfs file entry patch success\n"); + + if (!ncaEncryptHeader(base_nca_ctx)) + { + consolePrint("nca header mod not encrypted\n"); + romfsFreeFileEntryPatch(&romfs_patch); + goto out2; + } + + consolePrint("nca header mod encrypted\n"); + + tmp_file = fopen("sdmc:/program_nca_mod.bin", "wb"); + if (!tmp_file) + { + consolePrint("program nca mod not saved\n"); + romfsFreeFileEntryPatch(&romfs_patch); + goto out2; + } + + u64 block_size = TEST_BUF_SIZE; + for(u64 i = 0; i < base_nca_ctx->content_size; i += block_size) + { + if (block_size > (base_nca_ctx->content_size - i)) block_size = (base_nca_ctx->content_size - i); + + if (!ncaReadContentFile(base_nca_ctx, buf, block_size, i)) + { + consolePrint("failed to read 0x%lX chunk from offset 0x%lX\n", block_size, i); + fclose(tmp_file); + romfsFreeFileEntryPatch(&romfs_patch); + goto out2; + } + + if (i == 0) + { + memcpy(buf, &(base_nca_ctx->header), sizeof(NcaHeader)); + for(u64 j = 0; j < 4; j++) memcpy(buf + sizeof(NcaHeader) + (j * sizeof(NcaFsHeader)), &(base_nca_ctx->fs_contexts[j].header), sizeof(NcaFsHeader)); + } + + romfsWriteFileEntryPatchToMemoryBuffer(&(bktr_ctx.base_romfs_ctx), &romfs_patch, buf, block_size, i); + + fwrite(buf, 1, block_size, tmp_file); + fflush(tmp_file); + + consolePrint("wrote 0x%lX bytes to offset 0x%lX\n", block_size, i); + } + + fclose(tmp_file); + romfsFreeFileEntryPatch(&romfs_patch); + + goto out2; + + + + + + + + + + + + + + + + shared_data.bktr_ctx = &bktr_ctx; shared_data.data = buf; shared_data.data_size = 0; diff --git a/source/nca.c b/source/nca.c index 42fa637..d40a2c2 100644 --- a/source/nca.c +++ b/source/nca.c @@ -44,7 +44,6 @@ NX_INLINE bool ncaIsFsInfoEntryValid(NcaFsInfo *fs_info); static bool ncaDecryptHeader(NcaContext *ctx); static bool ncaDecryptKeyArea(NcaContext *ctx); -static bool ncaEncryptHeader(NcaContext *ctx); static bool ncaEncryptKeyArea(NcaContext *ctx); NX_INLINE bool ncaIsVersion0KeyAreaEncrypted(NcaContext *ctx); @@ -341,6 +340,63 @@ void ncaRemoveTitlekeyCrypto(NcaContext *ctx) ctx->dirty_header = true; } +bool ncaEncryptHeader(NcaContext *ctx) +{ + if (!ctx || !strlen(ctx->content_id_str)) + { + LOGFILE("Invalid NCA context!"); + return false; + } + + size_t crypt_res = 0; + const u8 *header_key = keysGetNcaHeaderKey(); + Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0}; + + /* Encrypt NCA key area. */ + if (!ncaEncryptKeyArea(ctx)) + { + LOGFILE("Error encrypting NCA \"%s\" key area!", ctx->content_id_str); + return false; + } + + /* Prepare AES-128-XTS contexts. */ + aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + AES_128_KEY_SIZE, true); + if (ctx->format_version == NcaVersion_Nca0) aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_key_area.aes_xts_1, ctx->decrypted_key_area.aes_xts_2, true); + + /* Encrypt NCA header. */ + crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), sizeof(NcaHeader), 0, NCA_AES_XTS_SECTOR_SIZE, true); + if (crypt_res != sizeof(NcaHeader)) + { + LOGFILE("Error encrypting NCA \"%s\" header!", ctx->content_id_str); + return false; + } + + /* Encrypt NCA FS section headers. */ + /* Both NCA2 and NCA3 place the NCA FS section headers right after the NCA header. However, NCA0 places them at the start sector from each NCA FS section. */ + /* NCA0 FS section headers will be encrypted in-place, but they need to be written to their proper offsets. */ + for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) + { + /* Don't proceed if this NCA FS section isn't populated. */ + if (ctx->format_version != NcaVersion_Nca3 && !ncaIsFsInfoEntryValid(&(ctx->header.fs_info[i]))) continue; + + /* The AES-XTS sector number for each NCA FS header varies depending on the NCA format version. */ + /* NCA3 uses sector number 0 for the NCA header, then increases it with each new sector (e.g. making the first NCA FS section header use sector number 2, and so on). */ + /* NCA2 uses sector number 0 for each NCA FS section header. */ + /* NCA0 uses sector number 0 for the NCA header, then uses sector number 0 for the rest of the data and increases it with each new sector. */ + Aes128XtsContext *aes_xts_ctx = (ctx->format_version != NcaVersion_Nca0 ? &hdr_aes_ctx : &nca0_fs_header_ctx); + u64 sector = (ctx->format_version == NcaVersion_Nca3 ? (2U + i) : (ctx->format_version == NcaVersion_Nca2 ? 0 : (ctx->header.fs_info[i].start_sector - 2))); + + crypt_res = aes128XtsNintendoCrypt(aes_xts_ctx, &(ctx->fs_contexts[i].header), &(ctx->fs_contexts[i].header), sizeof(NcaFsHeader), sector, NCA_AES_XTS_SECTOR_SIZE, true); + if (crypt_res != sizeof(NcaFsHeader)) + { + LOGFILE("Error encrypting NCA%u \"%s\" FS section header #%u!", ctx->format_version, ctx->content_id_str, i); + return false; + } + } + + return true; +} + NX_INLINE bool ncaIsFsInfoEntryValid(NcaFsInfo *fs_info) { if (!fs_info) return false; @@ -470,63 +526,6 @@ static bool ncaDecryptKeyArea(NcaContext *ctx) return true; } -static bool ncaEncryptHeader(NcaContext *ctx) -{ - if (!ctx || !strlen(ctx->content_id_str)) - { - LOGFILE("Invalid NCA context!"); - return false; - } - - size_t crypt_res = 0; - const u8 *header_key = keysGetNcaHeaderKey(); - Aes128XtsContext hdr_aes_ctx = {0}, nca0_fs_header_ctx = {0}; - - /* Encrypt NCA key area. */ - if (!ncaEncryptKeyArea(ctx)) - { - LOGFILE("Error encrypting NCA \"%s\" key area!", ctx->content_id_str); - return false; - } - - /* Prepare AES-128-XTS contexts. */ - aes128XtsContextCreate(&hdr_aes_ctx, header_key, header_key + AES_128_KEY_SIZE, true); - if (ctx->format_version == NcaVersion_Nca0) aes128XtsContextCreate(&nca0_fs_header_ctx, ctx->decrypted_key_area.aes_xts_1, ctx->decrypted_key_area.aes_xts_2, true); - - /* Encrypt NCA header. */ - crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, &(ctx->header), &(ctx->header), sizeof(NcaHeader), 0, NCA_AES_XTS_SECTOR_SIZE, true); - if (crypt_res != sizeof(NcaHeader)) - { - LOGFILE("Error encrypting NCA \"%s\" header!", ctx->content_id_str); - return false; - } - - /* Encrypt NCA FS section headers. */ - /* Both NCA2 and NCA3 place the NCA FS section headers right after the NCA header. However, NCA0 places them at the start sector from each NCA FS section. */ - /* NCA0 FS section headers will be encrypted in-place, but they need to be written to their proper offsets. */ - for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++) - { - /* Don't proceed if this NCA FS section isn't populated. */ - if (ctx->format_version != NcaVersion_Nca3 && !ncaIsFsInfoEntryValid(&(ctx->header.fs_info[i]))) continue; - - /* The AES-XTS sector number for each NCA FS header varies depending on the NCA format version. */ - /* NCA3 uses sector number 0 for the NCA header, then increases it with each new sector (e.g. making the first NCA FS section header use sector number 2, and so on). */ - /* NCA2 uses sector number 0 for each NCA FS section header. */ - /* NCA0 uses sector number 0 for the NCA header, then uses sector number 0 for the rest of the data and increases it with each new sector. */ - Aes128XtsContext *aes_xts_ctx = (ctx->format_version != NcaVersion_Nca0 ? &hdr_aes_ctx : &nca0_fs_header_ctx); - u64 sector = (ctx->format_version == NcaVersion_Nca3 ? (2U + i) : (ctx->format_version == NcaVersion_Nca2 ? 0 : (ctx->header.fs_info[i].start_sector - 2))); - - crypt_res = aes128XtsNintendoCrypt(aes_xts_ctx, &(ctx->fs_contexts[i].header), &(ctx->fs_contexts[i].header), sizeof(NcaFsHeader), sector, NCA_AES_XTS_SECTOR_SIZE, true); - if (crypt_res != sizeof(NcaFsHeader)) - { - LOGFILE("Error encrypting NCA%u \"%s\" FS section header #%u!", ctx->format_version, ctx->content_id_str, i); - return false; - } - } - - return true; -} - static bool ncaEncryptKeyArea(NcaContext *ctx) { if (!ctx) diff --git a/source/nca.h b/source/nca.h index c00917d..7ccccdd 100644 --- a/source/nca.h +++ b/source/nca.h @@ -305,7 +305,7 @@ typedef struct { typedef struct { u8 storage_id; ///< NcmStorageId. - NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data. + NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data from eMMC/SD. u64 gamecard_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard. NcmContentId content_id; ///< Also used to read NCA data. char content_id_str[0x21]; @@ -407,7 +407,8 @@ void ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(NcaContext *ctx, NcaHierar /// Removes titlekey crypto dependency from a NCA context by wiping the Rights ID from the underlying NCA header and copying the decrypted titlekey to the NCA key area. void ncaRemoveTitlekeyCrypto(NcaContext *ctx); - +/// Encrypts NCA header and NCA FS headers from a NCA context. +bool ncaEncryptHeader(NcaContext *ctx); diff --git a/source/romfs.c b/source/romfs.c index efd8902..c103c78 100644 --- a/source/romfs.c +++ b/source/romfs.c @@ -282,14 +282,14 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext * /* Duplicate path to avoid problems with strtok(). */ if (!(path_dup = strdup(path))) { - LOGFILE("Unable to duplicate input path!"); + LOGFILE("Unable to duplicate input path! (\"%s\").", path); return NULL; } pch = strtok(path_dup, "/"); if (!pch) { - LOGFILE("Failed to tokenize input path!"); + LOGFILE("Failed to tokenize input path! (\"%s\").", path); dir_entry = NULL; goto end; } @@ -298,7 +298,7 @@ RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext * { if (!(dir_entry = romfsGetChildDirectoryEntryByName(ctx, dir_entry, pch))) { - LOGFILE("Failed to retrieve directory entry by name!"); + LOGFILE("Failed to retrieve directory entry by name for \"%s\"! (\"%s\").", pch, path); break; } @@ -327,7 +327,7 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const /* Duplicate path. */ if (!(path_dup = strdup(path))) { - LOGFILE("Unable to duplicate input path!"); + LOGFILE("Unable to duplicate input path! (\"%s\").", path); return NULL; } @@ -341,7 +341,7 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const /* Safety check. */ if (!path_len || !(filename = strrchr(path_dup, '/'))) { - LOGFILE("Invalid input path!"); + LOGFILE("Invalid input path! (\"%s\").", path); goto end; } @@ -352,12 +352,12 @@ RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const /* If the first character is NULL, then just retrieve the root directory entry. */ if (!(dir_entry = (*path_dup ? romfsGetDirectoryEntryByPath(ctx, path_dup) : romfsGetDirectoryEntryByOffset(ctx, 0)))) { - LOGFILE("Failed to retrieve directory entry!"); + LOGFILE("Failed to retrieve directory entry for \"%s\"! (\"%s\").", *path_dup ? path_dup : "/", path); goto end; } /* Retrieve file entry. */ - if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename))) LOGFILE("Failed to retrieve file entry by name!"); + if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename))) LOGFILE("Failed to retrieve file entry by name for \"%s\"! (\"%s\").", filename, path); end: if (path_dup) free(path_dup); @@ -522,7 +522,7 @@ static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSys while(dir_offset != ROMFS_VOID_ENTRY) { if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !child_dir_entry->name_length) return NULL; - if (child_dir_entry->name_length == name_len && !strcmp(child_dir_entry->name, name)) return child_dir_entry; + if (child_dir_entry->name_length == name_len && !strncmp(child_dir_entry->name, name, name_len)) return child_dir_entry; dir_offset = child_dir_entry->next_offset; } @@ -541,7 +541,7 @@ static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext while(file_offset != ROMFS_VOID_ENTRY) { if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset)) || !child_file_entry->name_length) return NULL; - if (child_file_entry->name_length == name_len && !strcmp(child_file_entry->name, name)) return child_file_entry; + if (child_file_entry->name_length == name_len && !strncmp(child_file_entry->name, name, name_len)) return child_file_entry; file_offset = child_file_entry->next_offset; } diff --git a/source/romfs.h b/source/romfs.h index 43fe177..9d4806e 100644 --- a/source/romfs.h +++ b/source/romfs.h @@ -175,7 +175,7 @@ NX_INLINE RomFileSystemFileEntry *romfsGetFileEntryByOffset(RomFileSystemContext return (RomFileSystemFileEntry*)((u8*)ctx->file_table + file_entry_offset); } -NX_INLINE void romfsWriteFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntryPatch *patch, void *buf, u64 buf_size, u64 buf_offset) +NX_INLINE void romfsWriteFileEntryPatchToMemoryBuffer(RomFileSystemContext *ctx, RomFileSystemFileEntryPatch *patch, void *buf, u64 buf_size, u64 buf_offset) { if (!ctx || !ctx->nca_fs_ctx || !patch || (!patch->use_old_format_patch && ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs) || \ (patch->use_old_format_patch && ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)) return;