1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 18:26:39 +00:00

Test unified NCA hash layer write function (works gucci).

This commit is contained in:
Pablo Curiel 2020-07-22 20:37:02 -04:00
parent 62cc25805d
commit c8c062e7fa
5 changed files with 182 additions and 71 deletions

View file

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

View file

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

View file

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

View file

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

View file

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