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

Functions and wrappers to write generated NCA hash layer patches.

This commit is contained in:
Pablo Curiel 2020-07-22 16:35:23 -04:00
parent 90e0f057bc
commit b8d80bf260
6 changed files with 140 additions and 65 deletions

View file

@ -59,6 +59,8 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val, bool lock);
static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, void *out, bool is_integrity_patch);
static void ncaWriteHashDataPatchToMemoryBuffer(NcaContext *ctx, NcaHashDataPatch *layer_patch, void *buf, u64 buf_size, u64 buf_offset);
static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset, bool lock);
bool ncaAllocateCryptoBuffer(void)
@ -296,11 +298,27 @@ bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *da
return ncaGenerateHashDataPatch(ctx, data, data_size, data_offset, out, false);
}
void ncaWriteHierarchicalSha256PatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset)
{
if (!ctx || !strlen(ctx->content_id_str) || ctx->content_size < NCA_FULL_HEADER_LENGTH || !patch || memcmp(patch->content_id.c, ctx->content_id.c, 0x10) != 0 || !patch->hash_region_count || \
patch->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT || !buf || !buf_size || buf_offset >= ctx->content_size || (buf_offset + buf_size) > ctx->content_size) return;
for(u32 i = 0; i < patch->hash_region_count; i++) ncaWriteHashDataPatchToMemoryBuffer(ctx, &(patch->hash_region_patch[i]), buf, buf_size, buf_offset);
}
bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out)
{
return ncaGenerateHashDataPatch(ctx, data, data_size, data_offset, out, true);
}
void ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalIntegrityPatch *patch, void *buf, u64 buf_size, u64 buf_offset)
{
if (!ctx || !strlen(ctx->content_id_str) || ctx->content_size < NCA_FULL_HEADER_LENGTH || !patch || memcmp(patch->content_id.c, ctx->content_id.c, 0x10) != 0 || !buf || !buf_size || \
buf_offset >= ctx->content_size || (buf_offset + buf_size) > ctx->content_size) return;
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++) ncaWriteHashDataPatchToMemoryBuffer(ctx, &(patch->hash_level_patch[i]), buf, buf_size, buf_offset);
}
void ncaRemoveTitlekeyCrypto(NcaContext *ctx)
{
if (!ctx || !ctx->rights_id_available || !ctx->titlekey_retrieved) return;
@ -630,7 +648,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
bool ret = false;
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrEx || !out || !read_size || \
offset >= ctx->section_size || (offset + read_size) > ctx->section_size)
{
@ -752,7 +770,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
bool ret = false;
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
ctx->section_type != NcaFsSectionType_PatchRomFs || ctx->encryption_type != NcaEncryptionType_AesCtrEx || !out || !read_size || offset >= ctx->section_size || \
(offset + read_size) > ctx->section_size)
{
@ -1014,6 +1032,9 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data,
/* Enable the 'dirty_header' flag. */
nca_ctx->dirty_header = true;
/* Copy content ID. */
memcpy(!is_integrity_patch ? &(hierarchical_sha256_patch->content_id) : &(hierarchical_integrity_patch->content_id), &(nca_ctx->content_id), sizeof(NcmContentId));
/* Set hash region count (if needed). */
if (!is_integrity_patch) hierarchical_sha256_patch->hash_region_count = layer_count;
@ -1039,6 +1060,24 @@ end:
return success;
}
static void ncaWriteHashDataPatchToMemoryBuffer(NcaContext *ctx, NcaHashDataPatch *layer_patch, void *buf, u64 buf_size, u64 buf_offset)
{
/* Return right away if we're dealing with invalid parameters, or if the buffer data is not part of the range covered by the patch (last two conditions). */
if (!ctx || !layer_patch || layer_patch->offset < sizeof(NcaHeader) || layer_patch->offset >= ctx->content_size || !layer_patch->size || !layer_patch->data || \
(layer_patch->offset + layer_patch->size) > ctx->content_size || !buf || (buf_offset + buf_size) <= layer_patch->offset || (layer_patch->offset + layer_patch->size) <= buf_offset) return;
/* Overwrite buffer data using patch data. */
u64 patch_block_offset = (buf_offset > layer_patch->offset ? (buf_offset - layer_patch->offset) : 0);
u64 patch_block_size = (layer_patch->size - patch_block_offset);
u64 buf_block_offset = (buf_offset > layer_patch->offset ? 0 : (layer_patch->offset - buf_offset));
u64 buf_block_size = ((buf_size - buf_block_offset) > patch_block_size ? patch_block_size : (buf_size - buf_block_offset));
memcpy((u8*)buf + buf_block_offset, layer_patch->data + patch_block_offset, buf_block_size);
LOGFILE("Overwrote 0x%lX bytes block at offset 0x%lX from raw NCA \"%s\" buffer (size 0x%lX, NCA offset 0x%lX).", buf_block_size, buf_block_offset, ctx->content_id_str, buf_size, buf_offset);
}
static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset, bool lock)
{
if (lock) mutexLock(&g_ncaCryptoBufferMutex);
@ -1046,7 +1085,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
u8 *out = NULL;
bool success = false;
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < NCA_FULL_HEADER_LENGTH || \
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type >= NcaEncryptionType_AesCtrEx || !data || !data_size || \
data_offset >= ctx->section_size || (data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset)
{

View file

@ -332,11 +332,13 @@ typedef struct {
} NcaHashDataPatch;
typedef struct {
NcmContentId content_id;
u32 hash_region_count;
NcaHashDataPatch hash_region_patch[NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT];
} NcaHierarchicalSha256Patch;
typedef struct {
NcmContentId content_id;
NcaHashDataPatch hash_level_patch[NCA_IVFC_LEVEL_COUNT];
} NcaHierarchicalIntegrityPatch;
@ -378,18 +380,9 @@ void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *d
/// 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.
NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
{
if (!patch || !patch->hash_region_count || patch->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT) return;
for(u8 i = 0; i < patch->hash_region_count; i++)
{
if (patch->hash_region_patch[i].data) free(patch->hash_region_patch[i].data);
}
memset(patch, 0, sizeof(NcaHierarchicalSha256Patch));
}
/// Overwrites block(s) from a buffer holding raw NCA data using previously initialized NcaContext and NcaHierarchicalSha256Patch.
/// 'buf_offset' must hold the raw NCA offset where the data stored in 'buf' was read from.
void ncaWriteHierarchicalSha256PatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset);
/// Generates HierarchicalIntegrity FS section patch data, which can be used to seamlessly replace NCA data.
/// Input offset must be relative to the start of the last HierarchicalIntegrity hash level (actual underlying FS).
@ -397,20 +390,21 @@ NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
/// 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_LEVEL_COUNT; i++)
{
if (patch->hash_level_patch[i].data) free(patch->hash_level_patch[i].data);
}
memset(patch, 0, sizeof(NcaHierarchicalIntegrityPatch));
}
/// Overwrites block(s) from a buffer holding raw NCA data using a previously initialized NcaContext and NcaHierarchicalIntegrityPatch.
/// 'buf_offset' must hold the raw NCA offset where the data stored in 'buf' was read from.
void ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalIntegrityPatch *patch, void *buf, u64 buf_size, u64 buf_offset);
/// Removes titlekey crypto dependency from a NCA context by wiping the Rights ID from the underlying NCA header copy and copying the decrypted titlekey to the NCA key area.
/// 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);
@ -457,7 +451,7 @@ NX_INLINE bool ncaValidateHierarchicalSha256Offsets(NcaHierarchicalSha256Data *h
if (!hierarchical_sha256_data || !section_size || !hierarchical_sha256_data->hash_block_size || !hierarchical_sha256_data->hash_region_count || \
hierarchical_sha256_data->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT) return false;
for(u8 i = 0; i < hierarchical_sha256_data->hash_region_count; i++)
for(u32 i = 0; i < hierarchical_sha256_data->hash_region_count; i++)
{
if (hierarchical_sha256_data->hash_region[i].offset >= section_size || !hierarchical_sha256_data->hash_region[i].size || \
(hierarchical_sha256_data->hash_region[i].offset + hierarchical_sha256_data->hash_region[i].size) > section_size) return false;
@ -471,7 +465,7 @@ NX_INLINE bool ncaValidateHierarchicalIntegrityOffsets(NcaIntegrityMetaInfo *int
if (!integrity_meta_info || !section_size || __builtin_bswap32(integrity_meta_info->magic) != NCA_IVFC_MAGIC || integrity_meta_info->master_hash_size != SHA256_HASH_SIZE || \
integrity_meta_info->info_level_hash.max_level_count != NCA_IVFC_MAX_LEVEL_COUNT) return false;
for(u8 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
{
if (integrity_meta_info->info_level_hash.level_information[i].offset >= section_size || !integrity_meta_info->info_level_hash.level_information[i].size || \
!integrity_meta_info->info_level_hash.level_information[i].block_order || \
@ -481,4 +475,28 @@ NX_INLINE bool ncaValidateHierarchicalIntegrityOffsets(NcaIntegrityMetaInfo *int
return true;
}
NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
{
if (!patch) return;
for(u32 i = 0; i < NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT; i++)
{
if (patch->hash_region_patch[i].data) free(patch->hash_region_patch[i].data);
}
memset(patch, 0, sizeof(NcaHierarchicalSha256Patch));
}
NX_INLINE void ncaFreeHierarchicalIntegrityPatch(NcaHierarchicalIntegrityPatch *patch)
{
if (!patch) return;
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
{
if (patch->hash_level_patch[i].data) free(patch->hash_level_patch[i].data);
}
memset(patch, 0, sizeof(NcaHierarchicalIntegrityPatch));
}
#endif /* __NCA_H__ */

View file

@ -53,14 +53,6 @@ typedef struct {
/// Initializes a partition FS context.
bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx);
/// Cleanups a previously initialized partition FS context.
NX_INLINE void pfsFreeContext(PartitionFileSystemContext *ctx)
{
if (!ctx) return;
if (ctx->header) free(ctx->header);
memset(ctx, 0, sizeof(PartitionFileSystemContext));
}
/// Reads raw partition data using a partition FS context.
/// Input offset must be relative to the start of the partition FS.
bool pfsReadPartitionData(PartitionFileSystemContext *ctx, void *out, u64 read_size, u64 offset);
@ -75,13 +67,21 @@ bool pfsGetEntryIndexByName(PartitionFileSystemContext *ctx, const char *name, u
/// Calculates the extracted partition FS size.
bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size);
/// Generates HierarchicalSha256 FS section patch data using a partition FS context + entry, which can be used to replace NCA data in content dumping operations.
/// Generates HierarchicalSha256 FS section patch data using a partition FS context + entry, which can be used to seamlessly replace NCA data.
/// Input offset must be relative to the start of the partition FS entry data.
/// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch().
/// Use the pfsWriteEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function.
bool pfsGenerateEntryPatch(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out);
/// Miscellaneous functions.
NX_INLINE void pfsFreeContext(PartitionFileSystemContext *ctx)
{
if (!ctx) return;
if (ctx->header) free(ctx->header);
memset(ctx, 0, sizeof(PartitionFileSystemContext));
}
NX_INLINE u32 pfsGetEntryCount(PartitionFileSystemContext *ctx)
{
if (!ctx || !ctx->header_size || !ctx->header) return 0;
@ -116,4 +116,15 @@ NX_INLINE PartitionFileSystemEntry *pfsGetEntryByName(PartitionFileSystemContext
return pfsGetEntryByIndex(ctx, idx);
}
NX_INLINE void pfsWriteEntryPatchToMemoryBuffer(PartitionFileSystemContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset)
{
if (!ctx || !ctx->nca_fs_ctx) return;
ncaWriteHierarchicalSha256PatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, patch, buf, buf_size, buf_offset);
}
NX_INLINE void pfsFreeEntryPatch(NcaHierarchicalSha256Patch *patch)
{
ncaFreeHierarchicalSha256Patch(patch);
}
#endif /* __PFS_H__ */

View file

@ -496,20 +496,18 @@ bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEnt
bool success = false;
u64 fs_offset = (ctx->body_offset + file_entry->offset + data_offset);
memset(&(out->old_format_patch), 0, sizeof(NcaHierarchicalSha256Patch));
memset(&(out->cur_format_patch), 0, sizeof(NcaHierarchicalIntegrityPatch));
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 = 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);
}
if (!success) LOGFILE("Failed to generate 0x%lX bytes Hierarchical%s patch at offset 0x%lX for RomFS file entry!", data_size, \
ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? "Sha256" : "Integrity", fs_offset);
return success;
}

View file

@ -119,15 +119,6 @@ typedef enum {
/// Initializes a RomFS context.
bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx);
/// Cleanups a previously initialized RomFS context.
NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx)
{
if (!ctx) return;
if (ctx->dir_table) free(ctx->dir_table);
if (ctx->file_table) free(ctx->file_table);
memset(ctx, 0, sizeof(RomFileSystemContext));
}
/// Reads raw filesystem data using a RomFS context.
/// Input offset must be relative to the start of the RomFS.
bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset);
@ -159,19 +150,19 @@ bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFile
/// Generates HierarchicalSha256 (NCA0) / HierarchicalIntegrity (NCA2/NCA3) FS section patch data using a RomFS context + file entry, which can be used to seamlessly replace NCA data.
/// Input offset must be relative to the start of the RomFS file entry data.
/// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch() / ncaGenerateHierarchicalIntegrityPatch().
/// Use the romfsWriteFileEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function.
bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out);
/// Cleanups a previously generated RomFS file entry patch.
NX_INLINE void romfsFreeFileEntryPatch(RomFileSystemFileEntryPatch *patch)
{
if (!patch) return;
ncaFreeHierarchicalSha256Patch(&(patch->old_format_patch));
ncaFreeHierarchicalIntegrityPatch(&(patch->cur_format_patch));
memset(patch, 0, sizeof(RomFileSystemFileEntryPatch));
}
/// Miscellaneous functions.
NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx)
{
if (!ctx) return;
if (ctx->dir_table) free(ctx->dir_table);
if (ctx->file_table) free(ctx->file_table);
memset(ctx, 0, sizeof(RomFileSystemContext));
}
NX_INLINE RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByOffset(RomFileSystemContext *ctx, u32 dir_entry_offset)
{
if (!ctx || !ctx->dir_table || (dir_entry_offset + sizeof(RomFileSystemDirectoryEntry)) > ctx->dir_table_size) return NULL;
@ -184,4 +175,25 @@ 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)
{
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;
if (patch->use_old_format_patch)
{
ncaWriteHierarchicalSha256PatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, &(patch->old_format_patch), buf, buf_size, buf_offset);
} else {
ncaWriteHierarchicalIntegrityPatchToMemoryBuffer((NcaContext*)ctx->nca_fs_ctx->nca_ctx, &(patch->cur_format_patch), buf, buf_size, buf_offset);
}
}
NX_INLINE void romfsFreeFileEntryPatch(RomFileSystemFileEntryPatch *patch)
{
if (!patch) return;
patch->use_old_format_patch = false;
ncaFreeHierarchicalSha256Patch(&(patch->old_format_patch));
ncaFreeHierarchicalIntegrityPatch(&(patch->cur_format_patch));
}
#endif /* __ROMFS_H__ */

View file

@ -7,14 +7,11 @@ todo:
tik: use dumped tickets when the original ones can't be found in the ES savefile?
nca: function to write encrypted nca headers / nca fs headers (don't forget nca0 please)
nca: function to write hashdata patches
pfs0: filelist generation methods
pfs0: full header aligned to 0x20 (nsp)
pfs0: function to write patches
romfs: filelist generation methods
romfs: function to write patches
bktr: filelist generation methods (wrappers for romfs functions)