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

nca: parse CompressionInfo struct.

This commit is contained in:
Pablo Curiel 2022-06-30 23:04:54 +02:00
parent 8d81528619
commit e372b97131
3 changed files with 94 additions and 49 deletions

2
.gitignore vendored
View file

@ -18,4 +18,4 @@ host/nxdumptool
*.exe *.exe
main.cpp main.cpp
/code_templates/tmp/* /code_templates/tmp/*
/nmh3 /galgun

View file

@ -389,6 +389,15 @@ typedef struct {
Aes128CtrContext sparse_ctr_ctx; ///< AES-128-CTR context used for sparse table decryption. Aes128CtrContext sparse_ctr_ctx; ///< AES-128-CTR context used for sparse table decryption.
u64 cur_sparse_virtual_offset; ///< Current sparse layer virtual offset. Used for content decryption if a sparse layer is available. u64 cur_sparse_virtual_offset; ///< Current sparse layer virtual offset. Used for content decryption if a sparse layer is available.
///< CompressionInfo-related fields.
bool has_compression_layer; ///< Set to true if this NCA FS section has a compression layer.
u64 compression_table_offset; ///< section_offset + hash_target_offset + header.compression_info.bucket.offset. Relative to the start of the NCA content file. Placed here for convenience.
u64 compression_table_size; ///< header.compression_info.bucket.size. Placed here for convenience.
///< NSP-related fields. ///< NSP-related fields.
bool header_written; ///< Set to true after this FS section header has been written to an output dump. bool header_written; ///< Set to true after this FS section header has been written to an output dump.
} NcaFsSectionContext; } NcaFsSectionContext;
@ -481,6 +490,11 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type,
/// Input offset must be relative to the start of the NCA content file. /// Input offset must be relative to the start of the NCA content file.
bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset); bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset);
/// Retrieves the offset and/or size from the FS section hierarchical hash target layer.
/// Output offset is relative to the start of the FS section.
/// Either 'out_offset' or 'out_size' can be NULL, but at least one of them must be a valid pointer.
bool ncaGetFsSectionHashTargetProperties(NcaFsSectionContext *ctx, u64 *out_offset, u64 *out_size);
/// Reads decrypted data from a NCA FS section using an input context. /// Reads decrypted data from a NCA FS section using an input context.
/// 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.
/// If dealing with Patch RomFS sections, this function should only be used when *not* reading BKTR AesCtrEx storage data. Use ncaReadAesCtrExStorageFromBktrSection() for that. /// If dealing with Patch RomFS sections, this function should only be used when *not* reading BKTR AesCtrEx storage data. Use ncaReadAesCtrExStorageFromBktrSection() for that.
@ -546,43 +560,6 @@ NX_INLINE bool ncaIsHeaderDirty(NcaContext *ctx)
return (memcmp(tmp_hash, ctx->header_hash, SHA256_HASH_SIZE) != 0); return (memcmp(tmp_hash, ctx->header_hash, SHA256_HASH_SIZE) != 0);
} }
NX_INLINE bool ncaGetFsSectionHashTargetProperties(NcaFsSectionContext *ctx, u64 *out_offset, u64 *out_size)
{
if (!ctx || (!out_offset && !out_size)) return false;
bool success = true;
switch(ctx->hash_type)
{
case NcaHashType_None:
if (out_offset) *out_offset = 0;
if (out_size) *out_size = ctx->section_size;
break;
case NcaHashType_HierarchicalSha256:
case NcaHashType_HierarchicalSha3256:
{
u32 layer_count = ctx->header.hash_data.hierarchical_sha256_data.hash_region_count;
NcaRegion *hash_region = &(ctx->header.hash_data.hierarchical_sha256_data.hash_region[layer_count - 1]);
if (out_offset) *out_offset = hash_region->offset;
if (out_size) *out_size = hash_region->size;
}
break;
case NcaHashType_HierarchicalIntegrity:
case NcaHashType_HierarchicalIntegritySha3:
{
NcaHierarchicalIntegrityVerificationLevelInformation *lvl_info = &(ctx->header.hash_data.integrity_meta_info.info_level_hash.level_information[NCA_IVFC_LEVEL_COUNT - 1]);
if (out_offset) *out_offset = lvl_info->offset;
if (out_size) *out_size = lvl_info->size;
}
break;
default:
success = false;
break;
}
return success;
}
NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch) NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
{ {
if (!patch) return; if (!patch) return;

View file

@ -209,6 +209,47 @@ bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset)
return ret; return ret;
} }
bool ncaGetFsSectionHashTargetProperties(NcaFsSectionContext *ctx, u64 *out_offset, u64 *out_size)
{
if (!ctx || (!out_offset && !out_size))
{
LOG_MSG("Invalid parameters!");
return false;
}
bool success = true;
switch(ctx->hash_type)
{
case NcaHashType_None:
if (out_offset) *out_offset = 0;
if (out_size) *out_size = ctx->section_size;
break;
case NcaHashType_HierarchicalSha256:
case NcaHashType_HierarchicalSha3256:
{
u32 layer_count = ctx->header.hash_data.hierarchical_sha256_data.hash_region_count;
NcaRegion *hash_region = &(ctx->header.hash_data.hierarchical_sha256_data.hash_region[layer_count - 1]);
if (out_offset) *out_offset = hash_region->offset;
if (out_size) *out_size = hash_region->size;
}
break;
case NcaHashType_HierarchicalIntegrity:
case NcaHashType_HierarchicalIntegritySha3:
{
NcaHierarchicalIntegrityVerificationLevelInformation *lvl_info = &(ctx->header.hash_data.integrity_meta_info.info_level_hash.level_information[NCA_IVFC_LEVEL_COUNT - 1]);
if (out_offset) *out_offset = lvl_info->offset;
if (out_size) *out_size = lvl_info->size;
}
break;
default:
success = false;
break;
}
return success;
}
bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset) bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset)
{ {
bool ret = false; bool ret = false;
@ -700,14 +741,18 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
NcaSparseInfo *sparse_info = &(fs_ctx->header.sparse_info); NcaSparseInfo *sparse_info = &(fs_ctx->header.sparse_info);
NcaBucketInfo *sparse_bucket = &(sparse_info->bucket); NcaBucketInfo *sparse_bucket = &(sparse_info->bucket);
NcaBucketInfo *compression_bucket = &(fs_ctx->header.compression_info.bucket);
bool success = false; bool success = false;
/* Fill section context. */ /* Fill section context. */
fs_ctx->enabled = false;
fs_ctx->nca_ctx = nca_ctx; fs_ctx->nca_ctx = nca_ctx;
fs_ctx->section_idx = section_idx; fs_ctx->section_idx = section_idx;
fs_ctx->section_type = NcaFsSectionType_Invalid; /* Placeholder. */ fs_ctx->section_type = NcaFsSectionType_Invalid; /* Placeholder. */
fs_ctx->has_sparse_layer = (sparse_info->generation != 0); fs_ctx->has_sparse_layer = (sparse_info->generation != 0);
fs_ctx->enabled = false; fs_ctx->has_compression_layer = (compression_bucket->offset != 0 && compression_bucket->size != 0);
fs_ctx->cur_sparse_virtual_offset = 0;
/* Don't proceed if this NCA FS section isn't populated. */ /* Don't proceed if this NCA FS section isn't populated. */
if (!ncaIsFsInfoEntryValid(fs_info)) if (!ncaIsFsInfoEntryValid(fs_info))
@ -784,7 +829,7 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
goto end; goto end;
} }
/* Check if we're dealing with a sparse storage. */ /* Check if we're dealing with a sparse layer. */
if (fs_ctx->has_sparse_layer) if (fs_ctx->has_sparse_layer)
{ {
/* Check if the sparse bucket is valid. */ /* Check if the sparse bucket is valid. */
@ -792,7 +837,7 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
u64 raw_storage_size = (sparse_bucket->offset + sparse_bucket->size); u64 raw_storage_size = (sparse_bucket->offset + sparse_bucket->size);
if (__builtin_bswap32(sparse_bucket->header.magic) != NCA_BKTR_MAGIC || sparse_bucket->header.version != NCA_BKTR_VERSION || raw_storage_offset < sizeof(NcaHeader) || \ if (__builtin_bswap32(sparse_bucket->header.magic) != NCA_BKTR_MAGIC || sparse_bucket->header.version != NCA_BKTR_VERSION || raw_storage_offset < sizeof(NcaHeader) || \
((raw_storage_offset + raw_storage_size) > nca_ctx->content_size)) (raw_storage_offset + raw_storage_size) > nca_ctx->content_size)
{ {
LOG_DATA(sparse_info, sizeof(NcaSparseInfo), "Invalid SparseInfo data for FS section #%u in \"%s\" (0x%lX). Skipping FS section. SparseInfo dump:", section_idx, \ LOG_DATA(sparse_info, sizeof(NcaSparseInfo), "Invalid SparseInfo data for FS section #%u in \"%s\" (0x%lX). Skipping FS section. SparseInfo dump:", section_idx, \
nca_ctx->content_id_str, nca_ctx->content_size); nca_ctx->content_id_str, nca_ctx->content_size);
@ -811,16 +856,39 @@ static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
fs_ctx->sparse_table_offset = (sparse_info->physical_offset + sparse_bucket->offset); fs_ctx->sparse_table_offset = (sparse_info->physical_offset + sparse_bucket->offset);
fs_ctx->sparse_table_size = sparse_bucket->size; fs_ctx->sparse_table_size = sparse_bucket->size;
/* Check if we're within boundaries. */ /* Update section size. */
if ((fs_ctx->sparse_table_offset + fs_ctx->sparse_table_size) > nca_ctx->content_size) fs_ctx->section_size = raw_storage_size;
}
/* Check if we're dealing with a compression layer. */
if (fs_ctx->has_compression_layer)
{
u64 raw_storage_offset = 0;
u64 raw_storage_size = compression_bucket->size;
/* Get target hash layer offset. */
if (!ncaGetFsSectionHashTargetProperties(fs_ctx, &raw_storage_offset, NULL))
{ {
LOG_DATA(sparse_info, sizeof(NcaSparseInfo), "SparseInfo table for FS section #%u in \"%s\" is out of NCA boundaries (0x%lX). Skipping FS section. SparseInfo dump:", \ LOG_MSG("Invalid hash type for FS section #%u in \"%s\" (0x%02X). Skipping FS section.", fs_ctx->section_idx, nca_ctx->content_id_str, fs_ctx->hash_type);
section_idx, nca_ctx->content_id_str, nca_ctx->content_size);
goto end; goto end;
} }
/* Update section size. */ /* Update compression layer offset. */
fs_ctx->section_size = raw_storage_size; raw_storage_offset += compression_bucket->offset;
/* Check if the compression bucket is valid. */
if (__builtin_bswap32(compression_bucket->header.magic) != NCA_BKTR_MAGIC || compression_bucket->header.version != NCA_BKTR_VERSION || !sparse_bucket->header.entry_count || \
raw_storage_offset < sizeof(NcaHeader) || (raw_storage_offset + raw_storage_size) > fs_ctx->section_size || \
(fs_ctx->section_offset + raw_storage_offset + raw_storage_size) > nca_ctx->content_size)
{
LOG_DATA(sparse_info, sizeof(NcaSparseInfo), "Invalid CompressionInfo data for FS section #%u in \"%s\" (0x%lX). Skipping FS section. CompressionInfo dump:", section_idx, \
nca_ctx->content_id_str, nca_ctx->content_size);
goto end;
}
/* Set sparse table properties. */
fs_ctx->compression_table_offset = (fs_ctx->section_offset + raw_storage_offset);
fs_ctx->compression_table_size = raw_storage_size;
} }
/* Check if we're within boundaries. */ /* Check if we're within boundaries. */
@ -1223,8 +1291,8 @@ static bool ncaFsSectionCheckHashRegionAccess(NcaFsSectionContext *ctx, u64 offs
static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val) static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val)
{ {
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_idx >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \ if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_idx >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
ctx->section_type != NcaFsSectionType_PatchRomFs || (ctx->encryption_type != NcaEncryptionType_AesCtrEx && ctx->encryption_type != NcaEncryptionType_AesCtrExSkipLayerHash) || \ ctx->section_type != NcaFsSectionType_PatchRomFs || (ctx->encryption_type != NcaEncryptionType_None && ctx->encryption_type != NcaEncryptionType_AesCtrEx && \
!out || !read_size || (offset + read_size) > ctx->section_size) ctx->encryption_type != NcaEncryptionType_AesCtrExSkipLayerHash) || !out || !read_size || (offset + read_size) > ctx->section_size)
{ {
LOG_MSG("Invalid NCA FS section header parameters!"); LOG_MSG("Invalid NCA FS section header parameters!");
return false; return false;