mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 18:26:39 +00:00
RomFS file entry patching working.
This commit is contained in:
parent
5631046a67
commit
18531961ca
4 changed files with 322 additions and 74 deletions
179
source/main.c
179
source/main.c
|
@ -113,6 +113,7 @@ int main(int argc, char *argv[])
|
||||||
u64 romfs_size = 0;
|
u64 romfs_size = 0;
|
||||||
RomFileSystemFileEntry *romfs_file_entry = NULL;
|
RomFileSystemFileEntry *romfs_file_entry = NULL;
|
||||||
RomFileSystemContext romfs_ctx = {0};
|
RomFileSystemContext romfs_ctx = {0};
|
||||||
|
RomFileSystemFileEntryPatch romfs_patch = {0};
|
||||||
|
|
||||||
buf = malloc(0x400000);
|
buf = malloc(0x400000);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
|
@ -172,15 +173,6 @@ int main(int argc, char *argv[])
|
||||||
printf("romfs initialize ctx succeeded\n");
|
printf("romfs initialize ctx succeeded\n");
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
if (romfsGetTotalDataSize(&romfs_ctx, &romfs_size))
|
|
||||||
{
|
|
||||||
printf("romfs size succeeded: 0x%lX\n", romfs_size);
|
|
||||||
} else {
|
|
||||||
printf("romfs size failed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
|
||||||
|
|
||||||
tmp_file = fopen("sdmc:/nxdt_test/romfs_ctx.bin", "wb");
|
tmp_file = fopen("sdmc:/nxdt_test/romfs_ctx.bin", "wb");
|
||||||
if (tmp_file)
|
if (tmp_file)
|
||||||
{
|
{
|
||||||
|
@ -200,9 +192,9 @@ int main(int argc, char *argv[])
|
||||||
fwrite(romfs_ctx.dir_table, 1, romfs_ctx.dir_table_size, tmp_file);
|
fwrite(romfs_ctx.dir_table, 1, romfs_ctx.dir_table_size, tmp_file);
|
||||||
fclose(tmp_file);
|
fclose(tmp_file);
|
||||||
tmp_file = NULL;
|
tmp_file = NULL;
|
||||||
printf("dir table saved\n");
|
printf("romfs dir table saved\n");
|
||||||
} else {
|
} else {
|
||||||
printf("dir table not saved\n");
|
printf("romfs dir table not saved\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
|
@ -213,40 +205,9 @@ int main(int argc, char *argv[])
|
||||||
fwrite(romfs_ctx.file_table, 1, romfs_ctx.file_table_size, tmp_file);
|
fwrite(romfs_ctx.file_table, 1, romfs_ctx.file_table_size, tmp_file);
|
||||||
fclose(tmp_file);
|
fclose(tmp_file);
|
||||||
tmp_file = NULL;
|
tmp_file = NULL;
|
||||||
printf("file table saved\n");
|
printf("romfs file table saved\n");
|
||||||
} else {
|
} else {
|
||||||
printf("file table not saved\n");
|
printf("romfs file table not saved\n");
|
||||||
}
|
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
|
||||||
|
|
||||||
romfs_file_entry = romfsGetFileEntryByPath(&romfs_ctx, "/control.nacp");
|
|
||||||
if (!romfs_file_entry)
|
|
||||||
{
|
|
||||||
printf("romfs get file entry by path failed\n");
|
|
||||||
goto out2;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("romfs get file entry by path success: %s | %p\n", romfs_file_entry->name, romfs_file_entry);
|
|
||||||
consoleUpdate(NULL);
|
|
||||||
|
|
||||||
if (romfsReadFileEntryData(&romfs_ctx, romfs_file_entry, buf, romfs_file_entry->size, 0))
|
|
||||||
{
|
|
||||||
printf("romfs read file entry success\n");
|
|
||||||
consoleUpdate(NULL);
|
|
||||||
|
|
||||||
tmp_file = fopen("sdmc:/nxdt_test/control.nacp", "wb");
|
|
||||||
if (tmp_file)
|
|
||||||
{
|
|
||||||
fwrite(buf, 1, romfs_file_entry->size, tmp_file);
|
|
||||||
fclose(tmp_file);
|
|
||||||
tmp_file = NULL;
|
|
||||||
printf("romfs file entry data saved\n");
|
|
||||||
} else {
|
|
||||||
printf("romfs file entry data not saved\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
printf("romfs read file entry failed\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
consoleUpdate(NULL);
|
consoleUpdate(NULL);
|
||||||
|
@ -270,6 +231,134 @@ int main(int argc, char *argv[])
|
||||||
printf("romfs read fs data failed\n");
|
printf("romfs read fs data failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (romfsGetTotalDataSize(&romfs_ctx, &romfs_size))
|
||||||
|
{
|
||||||
|
printf("romfs size succeeded: 0x%lX\n", romfs_size);
|
||||||
|
} else {
|
||||||
|
printf("romfs size failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
|
romfs_file_entry = romfsGetFileEntryByPath(&romfs_ctx, "/control.nacp");
|
||||||
|
if (!romfs_file_entry)
|
||||||
|
{
|
||||||
|
printf("romfs get file entry by path failed\n");
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("romfs get file entry by path success: %s | %p\n", romfs_file_entry->name, romfs_file_entry);
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
|
if (!romfsReadFileEntryData(&romfs_ctx, romfs_file_entry, buf, romfs_file_entry->size, 0))
|
||||||
|
{
|
||||||
|
printf("romfs read file entry failed\n");
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("romfs read file entry success\n");
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
|
tmp_file = fopen("sdmc:/nxdt_test/control.nacp", "wb");
|
||||||
|
if (tmp_file)
|
||||||
|
{
|
||||||
|
fwrite(buf, 1, romfs_file_entry->size, tmp_file);
|
||||||
|
fclose(tmp_file);
|
||||||
|
tmp_file = NULL;
|
||||||
|
printf("romfs file entry data saved\n");
|
||||||
|
} else {
|
||||||
|
printf("romfs file entry data not saved\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
|
NacpStruct *nacp_data = (NacpStruct*)buf;
|
||||||
|
memset(nacp_data->lang, 0, MEMBER_SIZE(NacpStruct, lang));
|
||||||
|
for(u8 i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
sprintf(nacp_data->lang[i].name, "nxdumptool");
|
||||||
|
sprintf(nacp_data->lang[i].author, "DarkMatterCore");
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp_file = fopen("sdmc:/nxdt_test/control_mod.nacp", "wb");
|
||||||
|
if (tmp_file)
|
||||||
|
{
|
||||||
|
fwrite(buf, 1, romfs_file_entry->size, tmp_file);
|
||||||
|
fclose(tmp_file);
|
||||||
|
tmp_file = NULL;
|
||||||
|
printf("romfs file entry mod data saved\n");
|
||||||
|
} else {
|
||||||
|
printf("romfs file entry mod data not saved\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
|
if (!romfsGenerateFileEntryPatch(&romfs_ctx, romfs_file_entry, buf, MEMBER_SIZE(NacpStruct, lang), 0, &romfs_patch))
|
||||||
|
{
|
||||||
|
printf("romfs file entry patch failed\n");
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("romfs file entry patch success\n");
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
|
tmp_file = fopen("sdmc:/nxdt_test/romfs_patch.bin", "wb");
|
||||||
|
if (tmp_file)
|
||||||
|
{
|
||||||
|
fwrite(&romfs_patch, 1, sizeof(RomFileSystemFileEntryPatch), tmp_file);
|
||||||
|
fclose(tmp_file);
|
||||||
|
tmp_file = NULL;
|
||||||
|
printf("romfs patch saved\n");
|
||||||
|
} else {
|
||||||
|
printf("romfs patch not saved\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(u8 i = 0; i < (NCA_IVFC_HASH_DATA_LAYER_COUNT + 1); i++)
|
||||||
|
{
|
||||||
|
NcaHashInfoLayerPatch *layer_patch = (i < NCA_IVFC_HASH_DATA_LAYER_COUNT ? &(romfs_patch.cur_format_patch.hash_data_layer_patch[i]) : &(romfs_patch.cur_format_patch.hash_target_layer_patch));
|
||||||
|
if (!layer_patch->size || !layer_patch->data) continue;
|
||||||
|
|
||||||
|
char path[64];
|
||||||
|
sprintf(path, "sdmc:/nxdt_test/romfs_patch_l%u.bin", i);
|
||||||
|
|
||||||
|
tmp_file = fopen(path, "wb");
|
||||||
|
if (tmp_file)
|
||||||
|
{
|
||||||
|
fwrite(layer_patch->data, 1, layer_patch->size, tmp_file);
|
||||||
|
fclose(tmp_file);
|
||||||
|
tmp_file = NULL;
|
||||||
|
printf("romfs patch #%u saved\n", i);
|
||||||
|
} else {
|
||||||
|
printf("romfs patch #%u not saved\n", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ncaEncryptHeader(nca_ctx))
|
||||||
|
{
|
||||||
|
printf("nca header mod not encrypted\n");
|
||||||
|
goto out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("nca header mod encrypted\n");
|
||||||
|
consoleUpdate(NULL);
|
||||||
|
|
||||||
|
tmp_file = fopen("sdmc:/nxdt_test/nca_header_mod.bin", "wb");
|
||||||
|
if (tmp_file)
|
||||||
|
{
|
||||||
|
fwrite(&(nca_ctx->header), 1, sizeof(NcaHeader), tmp_file);
|
||||||
|
fclose(tmp_file);
|
||||||
|
tmp_file = NULL;
|
||||||
|
printf("nca header mod saved\n");
|
||||||
|
} else {
|
||||||
|
printf("nca header mod not saved\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -287,6 +376,8 @@ out2:
|
||||||
|
|
||||||
if (tmp_file) fclose(tmp_file);
|
if (tmp_file) fclose(tmp_file);
|
||||||
|
|
||||||
|
romfsFreeFileEntryPatch(&romfs_patch);
|
||||||
|
|
||||||
romfsFreeContext(&romfs_ctx);
|
romfsFreeContext(&romfs_ctx);
|
||||||
|
|
||||||
if (serviceIsActive(&(ncm_storage.s))) ncmContentStorageClose(&ncm_storage);
|
if (serviceIsActive(&(ncm_storage.s))) ncmContentStorageClose(&ncm_storage);
|
||||||
|
|
189
source/nca.c
189
source/nca.c
|
@ -137,7 +137,7 @@ bool ncaEncryptHeader(NcaContext *ctx)
|
||||||
{
|
{
|
||||||
case NcaVersion_Nca3:
|
case NcaVersion_Nca3:
|
||||||
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, ctx->header.fs_headers, ctx->header.fs_headers, NCA_FULL_HEADER_LENGTH - NCA_HEADER_LENGTH, 2, NCA_AES_XTS_SECTOR_SIZE, true);
|
crypt_res = aes128XtsNintendoCrypt(&hdr_aes_ctx, ctx->header.fs_headers, ctx->header.fs_headers, NCA_FULL_HEADER_LENGTH - NCA_HEADER_LENGTH, 2, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||||
if (crypt_res != NCA_FULL_HEADER_LENGTH)
|
if (crypt_res != (NCA_FULL_HEADER_LENGTH - NCA_HEADER_LENGTH))
|
||||||
{
|
{
|
||||||
LOGFILE("Error encrypting NCA3 \"%s\" FS section headers!", ctx->content_id_str);
|
LOGFILE("Error encrypting NCA3 \"%s\" FS section headers!", ctx->content_id_str);
|
||||||
return false;
|
return false;
|
||||||
|
@ -485,8 +485,8 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reencrypt hash target layer block */
|
/* Reencrypt hash target layer block */
|
||||||
out->hash_target_layer_patch.data = _ncaGenerateEncryptedFsSectionBlock(ctx, hash_target_block, hash_target_size, hash_target_start_offset, &(out->hash_target_layer_patch.size), \
|
out->hash_target_layer_patch.data = _ncaGenerateEncryptedFsSectionBlock(ctx, hash_target_block + hash_target_data_offset, data_size, hash_target_layer_offset + data_offset, \
|
||||||
&(out->hash_target_layer_patch.offset), false);
|
&(out->hash_target_layer_patch.size), &(out->hash_target_layer_patch.offset), false);
|
||||||
if (!out->hash_target_layer_patch.data)
|
if (!out->hash_target_layer_patch.data)
|
||||||
{
|
{
|
||||||
LOGFILE("Failed to generate encrypted HierarchicalSha256 hash target layer block!");
|
LOGFILE("Failed to generate encrypted HierarchicalSha256 hash target layer block!");
|
||||||
|
@ -505,22 +505,183 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
|
||||||
success = true;
|
success = true;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mutexUnlock(&g_ncaCryptoBufferMutex);
|
|
||||||
|
|
||||||
if (hash_target_block) free(hash_target_block);
|
if (hash_target_block) free(hash_target_block);
|
||||||
|
|
||||||
if (hash_data_layer) free(hash_data_layer);
|
if (hash_data_layer) free(hash_data_layer);
|
||||||
|
|
||||||
|
if (!success) ncaFreeHierarchicalSha256Patch(out);
|
||||||
|
|
||||||
|
mutexUnlock(&g_ncaCryptoBufferMutex);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out)
|
||||||
|
{
|
||||||
|
mutexLock(&g_ncaCryptoBufferMutex);
|
||||||
|
|
||||||
|
NcaContext *nca_ctx = NULL;
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
u8 *cur_data = NULL;
|
||||||
|
u64 cur_data_offset = data_offset;
|
||||||
|
u64 cur_data_size = data_size;
|
||||||
|
|
||||||
|
u8 *hash_data_block = NULL, *hash_target_block = NULL;
|
||||||
|
|
||||||
|
if (!ctx || !(nca_ctx = (NcaContext*)ctx->nca_ctx) || !ctx->header || ctx->header->hash_type != NcaHashType_HierarchicalIntegrity || !data || !data_size || !out || \
|
||||||
|
data_offset >= ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size || \
|
||||||
|
(data_offset + data_size) > ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size)
|
||||||
|
{
|
||||||
|
LOGFILE("Invalid parameters!");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process each IVFC layer */
|
||||||
|
for(u8 i = (NCA_IVFC_HASH_DATA_LAYER_COUNT + 1); i > 0; i--)
|
||||||
|
{
|
||||||
|
NcaHierarchicalIntegrityLayerInfo *cur_layer_info = (i > NCA_IVFC_HASH_DATA_LAYER_COUNT ? &(ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info) : \
|
||||||
|
&(ctx->header->hash_info.hierarchical_integrity.hash_data_layer_info[i - 1]));
|
||||||
|
|
||||||
|
NcaHierarchicalIntegrityLayerInfo *parent_layer_info = (i > 1 ? &(ctx->header->hash_info.hierarchical_integrity.hash_data_layer_info[i - 2]) : NULL);
|
||||||
|
|
||||||
|
NcaHashInfoLayerPatch *cur_layer_patch = (i > NCA_IVFC_HASH_DATA_LAYER_COUNT ? &(out->hash_target_layer_patch) : &(out->hash_data_layer_patch[i - 1]));
|
||||||
|
|
||||||
|
if (!cur_layer_info->size || !cur_layer_info->block_size || (parent_layer_info && (!parent_layer_info->size || !parent_layer_info->block_size)))
|
||||||
|
{
|
||||||
|
LOGFILE("Invalid HierarchicalIntegrity parent/child layer!");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate required offsets and sizes */
|
||||||
|
u64 hash_block_size = NCA_IVFC_BLOCK_SIZE(cur_layer_info->block_size);
|
||||||
|
|
||||||
|
u64 hash_data_layer_offset = 0;
|
||||||
|
u64 hash_data_start_offset = 0, hash_data_end_offset = 0, hash_data_size = 0;
|
||||||
|
|
||||||
|
u64 hash_target_layer_offset = cur_layer_info->offset, hash_target_layer_size = cur_layer_info->size;
|
||||||
|
u64 hash_target_start_offset = 0, hash_target_end_offset = 0, hash_target_size = 0, hash_target_data_offset = 0;
|
||||||
|
|
||||||
|
if (parent_layer_info)
|
||||||
|
{
|
||||||
|
/* HierarchicalIntegrity layer from L1 to L5 */
|
||||||
|
hash_data_layer_offset = parent_layer_info->offset;
|
||||||
|
|
||||||
|
hash_data_start_offset = ((cur_data_offset / hash_block_size) * SHA256_HASH_SIZE);
|
||||||
|
hash_data_end_offset = (((cur_data_offset + cur_data_size) / hash_block_size) * SHA256_HASH_SIZE);
|
||||||
|
hash_data_size = (hash_data_end_offset != hash_data_start_offset ? (hash_data_end_offset - hash_data_start_offset) : SHA256_HASH_SIZE);
|
||||||
|
|
||||||
|
hash_target_start_offset = (hash_target_layer_offset + ALIGN_DOWN(cur_data_offset, hash_block_size));
|
||||||
|
hash_target_end_offset = (hash_target_layer_offset + ALIGN_UP(cur_data_offset + cur_data_size, hash_block_size));
|
||||||
|
hash_target_size = (hash_target_end_offset - hash_target_start_offset);
|
||||||
|
} else {
|
||||||
|
/* HierarchicalIntegrity master layer */
|
||||||
|
/* The master hash is calculated over the whole layer and saved to the NCA FS header */
|
||||||
|
hash_target_start_offset = hash_target_layer_offset;
|
||||||
|
hash_target_end_offset = (hash_target_layer_offset + hash_target_layer_size);
|
||||||
|
hash_target_size = hash_target_layer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_target_data_offset = (cur_data_offset - ALIGN_DOWN(cur_data_offset, hash_block_size));
|
||||||
|
|
||||||
|
/* Allocate memory for our hash target layer block */
|
||||||
|
hash_target_block = calloc(hash_target_size, sizeof(u8));
|
||||||
|
if (!hash_target_block)
|
||||||
|
{
|
||||||
|
LOGFILE("Unable to allocate 0x%lX bytes for the HierarchicalIntegrity hash target layer block!");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust hash target layer end offset and size if needed to avoid read errors */
|
||||||
|
if (hash_target_end_offset > (hash_target_layer_offset + hash_target_layer_size))
|
||||||
|
{
|
||||||
|
hash_target_end_offset = (hash_target_layer_offset + hash_target_layer_size);
|
||||||
|
hash_target_size = (hash_target_end_offset - hash_target_start_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read hash target layer block */
|
||||||
|
if (!_ncaReadFsSection(ctx, hash_target_block, hash_target_size, hash_target_start_offset, false))
|
||||||
|
{
|
||||||
|
LOGFILE("Failed to read HierarchicalIntegrity hash target layer block!");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replace hash target layer block data */
|
||||||
|
memcpy(hash_target_block + hash_target_data_offset, (i > NCA_IVFC_HASH_DATA_LAYER_COUNT ? data : cur_data), cur_data_size);
|
||||||
|
|
||||||
|
if (parent_layer_info)
|
||||||
|
{
|
||||||
|
/* Allocate memory for our hash data layer block */
|
||||||
|
hash_data_block = calloc(hash_data_size, sizeof(u8));
|
||||||
|
if (!hash_data_block)
|
||||||
|
{
|
||||||
|
LOGFILE("Unable to allocate 0x%lX bytes for the HierarchicalIntegrity hash data layer block!");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read hash target layer block */
|
||||||
|
if (!_ncaReadFsSection(ctx, hash_data_block, hash_data_size, hash_data_layer_offset + hash_data_start_offset, false))
|
||||||
|
{
|
||||||
|
LOGFILE("Failed to read HierarchicalIntegrity hash data layer block!");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recalculate hashes */
|
||||||
|
/* Size isn't truncated for blocks smaller than the hash block size, unlike HierarchicalSha256, so we just keep using the same hash block size throughout the loop */
|
||||||
|
/* For these specific cases, the rest of the block should be filled with zeroes (already taken care of by using calloc()) */
|
||||||
|
for(u64 i = 0, j = 0; i < hash_target_size; i += hash_block_size, j++) sha256CalculateHash(hash_data_block + (j * SHA256_HASH_SIZE), hash_target_block + i, hash_block_size);
|
||||||
|
} else {
|
||||||
|
/* Recalculate master hash from hash info block */
|
||||||
|
sha256CalculateHash(ctx->header->hash_info.hierarchical_integrity.master_hash, hash_target_block, hash_target_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reencrypt hash target layer block */
|
||||||
|
cur_layer_patch->data = _ncaGenerateEncryptedFsSectionBlock(ctx, hash_target_block + hash_target_data_offset, cur_data_size, hash_target_layer_offset + cur_data_offset, \
|
||||||
|
&(cur_layer_patch->size), &(cur_layer_patch->offset), false);
|
||||||
|
if (!cur_layer_patch->data)
|
||||||
|
{
|
||||||
|
LOGFILE("Failed to generate encrypted HierarchicalIntegrity hash target layer block!");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free hash target layer block */
|
||||||
|
free(hash_target_block);
|
||||||
|
hash_target_block = NULL;
|
||||||
|
|
||||||
|
if (parent_layer_info)
|
||||||
|
{
|
||||||
|
/* Free previous layer data if necessary */
|
||||||
|
if (cur_data) free(cur_data);
|
||||||
|
|
||||||
|
/* Prepare data for the next target layer */
|
||||||
|
cur_data = hash_data_block;
|
||||||
|
cur_data_offset = hash_data_start_offset;
|
||||||
|
cur_data_size = hash_data_size;
|
||||||
|
hash_data_block = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recalculate FS header hash */
|
||||||
|
sha256CalculateHash(nca_ctx->header.fs_hashes[ctx->section_num].hash, ctx->header, sizeof(NcaFsHeader));
|
||||||
|
|
||||||
|
/* Enable the 'dirty_header' flag */
|
||||||
|
nca_ctx->dirty_header = true;
|
||||||
|
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (hash_data_block) free(hash_data_block);
|
||||||
|
|
||||||
|
if (hash_target_block) free(hash_target_block);
|
||||||
|
|
||||||
|
if (cur_data) free(cur_data);
|
||||||
|
|
||||||
|
if (!success) ncaFreeHierarchicalIntegrityPatch(out);
|
||||||
|
|
||||||
|
mutexUnlock(&g_ncaCryptoBufferMutex);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
static size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt)
|
static size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt)
|
||||||
{
|
{
|
||||||
|
@ -1016,13 +1177,13 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
||||||
success = true;
|
success = true;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (lock) mutexUnlock(&g_ncaCryptoBufferMutex);
|
|
||||||
|
|
||||||
if (!success && out)
|
if (!success && out)
|
||||||
{
|
{
|
||||||
free(out);
|
free(out);
|
||||||
out = NULL;
|
out = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lock) mutexUnlock(&g_ncaCryptoBufferMutex);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
22
source/nca.h
22
source/nca.h
|
@ -328,13 +328,13 @@ bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 of
|
||||||
/// Returns a pointer to a heap-allocated buffer used to encrypt the input plaintext data, based on the encryption type used by the input NCA FS section, as well as its offset and size.
|
/// Returns a pointer to a heap-allocated buffer used to encrypt the input plaintext data, based on the encryption type used by the input NCA FS section, as well as its offset and size.
|
||||||
/// Input offset must be relative to the start of the NCA FS section.
|
/// Input offset must be relative to the start of the NCA FS section.
|
||||||
/// Output size and offset are guaranteed to be aligned to the AES sector size used by the encryption type from the FS section.
|
/// Output size and offset are guaranteed to be aligned to the AES sector size used by the encryption type from the FS section.
|
||||||
/// Output offset is relative to the start of the NCA content file, making it easier to use the output encrypted block to replace data in-place while writing a NCA.
|
/// Output offset is relative to the start of the NCA content file, making it easier to use the output encrypted block to seamlessly replace data while dumping a NCA.
|
||||||
void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset);
|
void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset);
|
||||||
|
|
||||||
/// Generates HierarchicalSha256 FS section patch data, which can be used to replace NCA data in content dumping operations.
|
/// Generates HierarchicalSha256 FS section patch data, which can be used to replace NCA data in content dumping operations.
|
||||||
/// Input offset must be relative to the start of the HierarchicalSha256 hash target layer (actual underlying FS).
|
/// Input offset must be relative to the start of the HierarchicalSha256 hash target layer (actual underlying FS).
|
||||||
/// Bear in mind that this function recalculates both the NcaHashInfo block master hash and the NCA FS header hash from the NCA header, and enables the 'dirty_header' flag from the NCA context.
|
/// Bear in mind that this function recalculates both the NcaHashInfo block master hash and the NCA FS header hash from the NCA header, and enables the 'dirty_header' flag from the NCA context.
|
||||||
/// As such, this function is not capable of generating more than one patch per HierarchicalSha256 FS section.
|
/// As such, this function is not designed to generate more than one patch per HierarchicalSha256 FS section.
|
||||||
bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out);
|
bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out);
|
||||||
|
|
||||||
/// Cleanups a previously generated NcaHierarchicalSha256Patch.
|
/// Cleanups a previously generated NcaHierarchicalSha256Patch.
|
||||||
|
@ -346,23 +346,23 @@ NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
|
||||||
memset(patch, 0, sizeof(NcaHierarchicalSha256Patch));
|
memset(patch, 0, sizeof(NcaHierarchicalSha256Patch));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates HierarchicalIntegrity FS section patch data, which can be used to replace NCA data in content dumping operations.
|
||||||
|
/// Input offset must be relative to the start of the HierarchicalIntegrity hash target layer (actual underlying FS).
|
||||||
|
/// Bear in mind that this function recalculates both the NcaHashInfo block master hash and the NCA FS header hash from the NCA header, and enables the 'dirty_header' flag from the NCA context.
|
||||||
|
/// As such, this function is not designed to generate more than one patch per HierarchicalIntegrity FS section.
|
||||||
|
bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out);
|
||||||
|
|
||||||
/// Cleanups a previously generated NcaHierarchicalIntegrityPatch.
|
/// Cleanups a previously generated NcaHierarchicalIntegrityPatch.
|
||||||
NX_INLINE void ncaFreeHierarchicalIntegrityPatch(NcaHierarchicalIntegrityPatch *patch)
|
NX_INLINE void ncaFreeHierarchicalIntegrityPatch(NcaHierarchicalIntegrityPatch *patch)
|
||||||
{
|
{
|
||||||
if (!patch) return;
|
if (!patch) return;
|
||||||
|
|
||||||
for(u8 i = 0; i < NCA_IVFC_HASH_DATA_LAYER_COUNT; i++)
|
for(u8 i = 0; i < (NCA_IVFC_HASH_DATA_LAYER_COUNT + 1); i++)
|
||||||
{
|
{
|
||||||
if (patch->hash_data_layer_patch[i].data) free(patch->hash_data_layer_patch[i].data);
|
NcaHashInfoLayerPatch *layer_patch = (i < NCA_IVFC_HASH_DATA_LAYER_COUNT ? &(patch->hash_data_layer_patch[i]) : &(patch->hash_target_layer_patch));
|
||||||
|
if (layer_patch->data) free(layer_patch->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (patch->hash_target_layer_patch.data) free(patch->hash_target_layer_patch.data);
|
|
||||||
memset(patch, 0, sizeof(NcaHierarchicalIntegrityPatch));
|
memset(patch, 0, sizeof(NcaHierarchicalIntegrityPatch));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,7 +413,6 @@ NX_INLINE bool ncaValidateHierarchicalSha256Offsets(NcaHierarchicalSha256 *hiera
|
||||||
{
|
{
|
||||||
if (!hierarchical_sha256 || !section_size || !hierarchical_sha256->hash_block_size || hierarchical_sha256->layer_count != NCA_HIERARCHICAL_SHA256_LAYER_COUNT) return false;
|
if (!hierarchical_sha256 || !section_size || !hierarchical_sha256->hash_block_size || hierarchical_sha256->layer_count != NCA_HIERARCHICAL_SHA256_LAYER_COUNT) return false;
|
||||||
|
|
||||||
/* Validate layer offsets and sizes */
|
|
||||||
for(u8 i = 0; i < NCA_HIERARCHICAL_SHA256_LAYER_COUNT; i++)
|
for(u8 i = 0; i < NCA_HIERARCHICAL_SHA256_LAYER_COUNT; i++)
|
||||||
{
|
{
|
||||||
NcaHierarchicalSha256LayerInfo *layer_info = (i == 0 ? &(hierarchical_sha256->hash_data_layer_info) : &(hierarchical_sha256->hash_target_layer_info));
|
NcaHierarchicalSha256LayerInfo *layer_info = (i == 0 ? &(hierarchical_sha256->hash_data_layer_info) : &(hierarchical_sha256->hash_target_layer_info));
|
||||||
|
@ -428,7 +427,6 @@ NX_INLINE bool ncaValidateHierarchicalIntegrityOffsets(NcaHierarchicalIntegrity
|
||||||
if (!hierarchical_integrity || !section_size || __builtin_bswap32(hierarchical_integrity->magic) != NCA_IVFC_MAGIC || !hierarchical_integrity->master_hash_size || \
|
if (!hierarchical_integrity || !section_size || __builtin_bswap32(hierarchical_integrity->magic) != NCA_IVFC_MAGIC || !hierarchical_integrity->master_hash_size || \
|
||||||
hierarchical_integrity->layer_count != NCA_IVFC_LAYER_COUNT) return false;
|
hierarchical_integrity->layer_count != NCA_IVFC_LAYER_COUNT) return false;
|
||||||
|
|
||||||
/* Validate layer offsets and sizes */
|
|
||||||
for(u8 i = 0; i < (NCA_IVFC_HASH_DATA_LAYER_COUNT + 1); i++)
|
for(u8 i = 0; i < (NCA_IVFC_HASH_DATA_LAYER_COUNT + 1); i++)
|
||||||
{
|
{
|
||||||
NcaHierarchicalIntegrityLayerInfo *layer_info = (i < NCA_IVFC_HASH_DATA_LAYER_COUNT ? &(hierarchical_integrity->hash_data_layer_info[i]) : &(hierarchical_integrity->hash_target_layer_info));
|
NcaHierarchicalIntegrityLayerInfo *layer_info = (i < NCA_IVFC_HASH_DATA_LAYER_COUNT ? &(hierarchical_integrity->hash_data_layer_info[i]) : &(hierarchical_integrity->hash_target_layer_info));
|
||||||
|
|
|
@ -490,14 +490,12 @@ bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEnt
|
||||||
if (ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs)
|
if (ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs)
|
||||||
{
|
{
|
||||||
out->use_old_format_patch = true;
|
out->use_old_format_patch = true;
|
||||||
|
|
||||||
success = ncaGenerateHierarchicalSha256Patch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->old_format_patch));
|
success = ncaGenerateHierarchicalSha256Patch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->old_format_patch));
|
||||||
if (!success) LOGFILE("Failed to generate 0x%lX bytes HierarchicalSha256 patch at offset 0x%lX for RomFS file entry!", data_size, fs_offset);
|
if (!success) LOGFILE("Failed to generate 0x%lX bytes HierarchicalSha256 patch at offset 0x%lX for RomFS file entry!", data_size, fs_offset);
|
||||||
} else {
|
} else {
|
||||||
out->use_old_format_patch = false;
|
out->use_old_format_patch = false;
|
||||||
|
success = ncaGenerateHierarchicalIntegrityPatch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->cur_format_patch));
|
||||||
success = true;
|
if (!success) LOGFILE("Failed to generate 0x%lX bytes HierarchicalIntegrity patch at offset 0x%lX for RomFS file entry!", data_size, fs_offset);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
|
Loading…
Reference in a new issue