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

RomFS file entry patching working.

This commit is contained in:
Pablo Curiel 2020-04-29 05:54:40 -04:00
parent 5631046a67
commit 18531961ca
4 changed files with 322 additions and 74 deletions

View file

@ -113,6 +113,7 @@ int main(int argc, char *argv[])
u64 romfs_size = 0;
RomFileSystemFileEntry *romfs_file_entry = NULL;
RomFileSystemContext romfs_ctx = {0};
RomFileSystemFileEntryPatch romfs_patch = {0};
buf = malloc(0x400000);
if (!buf)
@ -172,15 +173,6 @@ int main(int argc, char *argv[])
printf("romfs initialize ctx succeeded\n");
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");
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);
fclose(tmp_file);
tmp_file = NULL;
printf("dir table saved\n");
printf("romfs dir table saved\n");
} else {
printf("dir table not saved\n");
printf("romfs dir table not saved\n");
}
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);
fclose(tmp_file);
tmp_file = NULL;
printf("file table saved\n");
printf("romfs file table saved\n");
} else {
printf("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");
printf("romfs file table not saved\n");
}
consoleUpdate(NULL);
@ -270,6 +231,134 @@ int main(int argc, char *argv[])
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);
romfsFreeFileEntryPatch(&romfs_patch);
romfsFreeContext(&romfs_ctx);
if (serviceIsActive(&(ncm_storage.s))) ncmContentStorageClose(&ncm_storage);

View file

@ -137,7 +137,7 @@ bool ncaEncryptHeader(NcaContext *ctx)
{
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);
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);
return false;
@ -485,8 +485,8 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
}
/* 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.offset), false);
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.size), &(out->hash_target_layer_patch.offset), false);
if (!out->hash_target_layer_patch.data)
{
LOGFILE("Failed to generate encrypted HierarchicalSha256 hash target layer block!");
@ -505,22 +505,183 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
success = true;
exit:
mutexUnlock(&g_ncaCryptoBufferMutex);
if (hash_target_block) free(hash_target_block);
if (hash_data_layer) free(hash_data_layer);
if (!success) ncaFreeHierarchicalSha256Patch(out);
mutexUnlock(&g_ncaCryptoBufferMutex);
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)
{
@ -1016,13 +1177,13 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
success = true;
exit:
if (lock) mutexUnlock(&g_ncaCryptoBufferMutex);
if (!success && out)
{
free(out);
out = NULL;
}
if (lock) mutexUnlock(&g_ncaCryptoBufferMutex);
return out;
}

View file

@ -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.
/// 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 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);
/// 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).
/// 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);
/// Cleanups a previously generated NcaHierarchicalSha256Patch.
@ -346,23 +346,23 @@ NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
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.
NX_INLINE void ncaFreeHierarchicalIntegrityPatch(NcaHierarchicalIntegrityPatch *patch)
{
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));
}
@ -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;
/* Validate layer offsets and sizes */
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));
@ -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 || \
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++)
{
NcaHierarchicalIntegrityLayerInfo *layer_info = (i < NCA_IVFC_HASH_DATA_LAYER_COUNT ? &(hierarchical_integrity->hash_data_layer_info[i]) : &(hierarchical_integrity->hash_target_layer_info));

View file

@ -490,14 +490,12 @@ bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEnt
if (ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs)
{
out->use_old_format_patch = true;
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);
} else {
out->use_old_format_patch = false;
success = true;
success = ncaGenerateHierarchicalIntegrityPatch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->cur_format_patch));
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;